]>
Commit | Line | Data |
---|---|---|
41462d93 | 1 | /* |
41462d93 | 2 | * DEBUG: section 17 Request Forwarding |
3 | * AUTHOR: Duane Wessels | |
4 | * | |
2b6662ba | 5 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 6 | * ---------------------------------------------------------- |
41462d93 | 7 | * |
2b6662ba | 8 | * Squid is the result of efforts by numerous individuals from |
9 | * the Internet community; see the CONTRIBUTORS file for full | |
10 | * details. Many organizations have provided support for Squid's | |
11 | * development; see the SPONSORS file for full details. Squid is | |
12 | * Copyrighted (C) 2001 by the Regents of the University of | |
13 | * California; see the COPYRIGHT file for full details. Squid | |
14 | * incorporates software developed and/or copyrighted by other | |
15 | * sources; see the CREDITS file for full details. | |
41462d93 | 16 | * |
17 | * This program is free software; you can redistribute it and/or modify | |
18 | * it under the terms of the GNU General Public License as published by | |
19 | * the Free Software Foundation; either version 2 of the License, or | |
20 | * (at your option) any later version. | |
26ac0430 | 21 | * |
41462d93 | 22 | * This program is distributed in the hope that it will be useful, |
23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
25 | * GNU General Public License for more details. | |
26ac0430 | 26 | * |
41462d93 | 27 | * You should have received a copy of the GNU General Public License |
28 | * along with this program; if not, write to the Free Software | |
cbdec147 | 29 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 30 | * |
41462d93 | 31 | */ |
32 | ||
33 | ||
34 | #include "squid.h" | |
b6b6f466 | 35 | #include "forward.h" |
c0941a6a AR |
36 | #include "acl/FilledChecklist.h" |
37 | #include "acl/Gadgets.h" | |
aa839030 | 38 | #include "CacheManager.h" |
39 | #include "event.h" | |
40 | #include "errorpage.h" | |
41 | #include "fde.h" | |
bbaf2685 | 42 | #include "hier_code.h" |
924f73bc | 43 | #include "HttpReply.h" |
aa839030 | 44 | #include "HttpRequest.h" |
425de4c8 | 45 | #include "ip/QosConfig.h" |
aa839030 | 46 | #include "MemObject.h" |
781ce8ff | 47 | #include "pconn.h" |
aa839030 | 48 | #include "SquidTime.h" |
49 | #include "Store.h" | |
9b5c4a9a | 50 | #include "icmp/net_db.h" |
96d89ea0 | 51 | #include "ip/Intercept.h" |
407b9bb1 | 52 | #include "ip/tools.h" |
8822ebee | 53 | #include "mgr/Registration.h" |
4db984be CT |
54 | #if USE_SSL |
55 | #include "ssl/support.h" | |
56 | #endif | |
fc68f6b1 | 57 | |
b6b6f466 | 58 | static PSC fwdStartCompleteWrapper; |
59 | static PF fwdServerClosedWrapper; | |
60 | #if USE_SSL | |
61 | static PF fwdNegotiateSSLWrapper; | |
62 | #endif | |
63 | static PF fwdConnectTimeoutWrapper; | |
64 | static EVH fwdConnectStartWrapper; | |
65 | static CNCB fwdConnectDoneWrapper; | |
66 | ||
8ddcc35d | 67 | static OBJH fwdStats; |
b6b6f466 | 68 | static void fwdServerFree(FwdServer * fs); |
8ddcc35d | 69 | |
70 | #define MAX_FWD_STATS_IDX 9 | |
9977e14b | 71 | static int FwdReplyCodes[MAX_FWD_STATS_IDX + 1][HTTP_INVALID_HEADER + 1]; |
db1cd23c | 72 | |
781ce8ff | 73 | static PconnPool *fwdPconnPool = new PconnPool("server-side"); |
b6b6f466 | 74 | CBDATA_CLASS_INIT(FwdState); |
781ce8ff | 75 | |
429871db | 76 | void |
77 | FwdState::abort(void* d) | |
78 | { | |
79 | FwdState* fwd = (FwdState*)d; | |
6ecaf21a | 80 | Pointer tmp = fwd; // Grab a temporary pointer to keep the object alive during our scope. |
429871db | 81 | |
82 | if (fwd->server_fd >= 0) { | |
83 | comm_close(fwd->server_fd); | |
84 | fwd->server_fd = -1; | |
85 | } | |
86 | ||
87 | fwd->self = NULL; | |
88 | } | |
89 | ||
b6b6f466 | 90 | /**** PUBLIC INTERFACE ********************************************************/ |
c7f9eb6d | 91 | |
b6b6f466 | 92 | FwdState::FwdState(int fd, StoreEntry * e, HttpRequest * r) |
db1cd23c | 93 | { |
b6b6f466 | 94 | entry = e; |
95 | client_fd = fd; | |
96 | server_fd = -1; | |
6dd9f4bd | 97 | request = HTTPMSGLOCK(r); |
b6b6f466 | 98 | start_t = squid_curtime; |
34266cde | 99 | |
3d0ac046 | 100 | e->lock(); |
b6b6f466 | 101 | EBIT_SET(e->flags, ENTRY_FWD_HDR_WAIT); |
7a0fb323 | 102 | } |
103 | ||
104 | // Called once, right after object creation, when it is safe to set self | |
fc68f6b1 | 105 | void FwdState::start(Pointer aSelf) |
106 | { | |
7a0fb323 | 107 | // Protect ourselves from being destroyed when the only Server pointing |
108 | // to us is gone (while we expect to talk to more Servers later). | |
109 | // Once we set self, we are responsible for clearing it when we do not | |
110 | // expect to talk to any servers. | |
111 | self = aSelf; // refcounted | |
112 | ||
113 | // We hope that either the store entry aborts or peer is selected. | |
114 | // Otherwise we are going to leak our object. | |
34266cde | 115 | |
3900307b | 116 | entry->registerAbort(FwdState::abort, this); |
7a0fb323 | 117 | peerSelect(request, entry, fwdStartCompleteWrapper, this); |
429871db | 118 | |
fc68f6b1 | 119 | // TODO: set self _after_ the peer is selected because we do not need |
7a0fb323 | 120 | // self until we start talking to some Server. |
db1cd23c | 121 | } |
122 | ||
802a8c1d | 123 | void |
124 | FwdState::completed() | |
41462d93 | 125 | { |
fc68f6b1 | 126 | if (flags.forward_completed == 1) { |
127 | debugs(17, 1, HERE << "FwdState::completed called on a completed request! Bad!"); | |
128 | return; | |
129 | } | |
130 | ||
131 | flags.forward_completed = 1; | |
802a8c1d | 132 | |
bc87dc25 | 133 | #if URL_CHECKSUM_DEBUG |
62e76326 | 134 | |
b6b6f466 | 135 | entry->mem_obj->checkUrlChecksum(); |
225644d7 | 136 | #endif |
62e76326 | 137 | |
b6b6f466 | 138 | if (entry->store_status == STORE_PENDING) { |
139 | if (entry->isEmpty()) { | |
140 | assert(err); | |
141 | errorAppendEntry(entry, err); | |
142 | err = NULL; | |
62e76326 | 143 | } else { |
b6b6f466 | 144 | EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); |
145 | entry->complete(); | |
d88e3c49 | 146 | entry->releaseRequest(); |
62e76326 | 147 | } |
f563eea9 | 148 | } |
62e76326 | 149 | |
b6b6f466 | 150 | if (storePendingNClients(entry) > 0) |
151 | assert(!EBIT_TEST(entry->flags, ENTRY_FWD_HDR_WAIT)); | |
62e76326 | 152 | |
802a8c1d | 153 | } |
154 | ||
155 | FwdState::~FwdState() | |
156 | { | |
157 | debugs(17, 3, HERE << "FwdState destructor starting"); | |
fc68f6b1 | 158 | |
802a8c1d | 159 | if (! flags.forward_completed) |
fc68f6b1 | 160 | completed(); |
802a8c1d | 161 | |
b6b6f466 | 162 | serversFree(&servers); |
62e76326 | 163 | |
9d2760b6 AR |
164 | doneWithRetries(); |
165 | ||
6dd9f4bd | 166 | HTTPMSGUNLOCK(request); |
62e76326 | 167 | |
b6b6f466 | 168 | if (err) |
169 | errorStateFree(err); | |
62e76326 | 170 | |
3900307b | 171 | entry->unregisterAbort(); |
429871db | 172 | |
97b5e68f | 173 | entry->unlock(); |
62e76326 | 174 | |
b6b6f466 | 175 | entry = NULL; |
62e76326 | 176 | |
c2f45110 | 177 | int fd = server_fd; |
178 | ||
179 | if (fd > -1) { | |
b6b6f466 | 180 | server_fd = -1; |
c2f45110 | 181 | comm_remove_close_handler(fd, fwdServerClosedWrapper, this); |
bf8fe701 | 182 | debugs(17, 3, "fwdStateFree: closing FD " << fd); |
c2f45110 | 183 | comm_close(fd); |
b6b6f466 | 184 | } |
fc68f6b1 | 185 | |
b6b6f466 | 186 | debugs(17, 3, HERE << "FwdState destructor done"); |
187 | } | |
62e76326 | 188 | |
38413773 | 189 | /** |
b6b6f466 | 190 | * This is the entry point for client-side to start forwarding |
191 | * a transaction. It is a static method that may or may not | |
192 | * allocate a FwdState. | |
193 | */ | |
be0c6690 | 194 | void |
b6b6f466 | 195 | FwdState::fwdStart(int client_fd, StoreEntry *entry, HttpRequest *request) |
196 | { | |
b50e327b | 197 | /** \note |
b6b6f466 | 198 | * client_addr == no_addr indicates this is an "internal" request |
199 | * from peer_digest.c, asn.c, netdb.c, etc and should always | |
200 | * be allowed. yuck, I know. | |
201 | */ | |
62e76326 | 202 | |
b50e327b | 203 | if ( Config.accessList.miss && !request->client_addr.IsNoAddr() && |
af6a12ee | 204 | request->protocol != PROTO_INTERNAL && request->protocol != PROTO_CACHEOBJ) { |
b50e327b | 205 | /** |
b6b6f466 | 206 | * Check if this host is allowed to fetch MISSES from us (miss_access) |
207 | */ | |
c0941a6a | 208 | ACLFilledChecklist ch(Config.accessList.miss, request, NULL); |
b6b6f466 | 209 | ch.src_addr = request->client_addr; |
210 | ch.my_addr = request->my_addr; | |
b6b6f466 | 211 | int answer = ch.fastCheck(); |
212 | ||
213 | if (answer == 0) { | |
214 | err_type page_id; | |
9ce7856a | 215 | page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName, 1); |
b6b6f466 | 216 | |
217 | if (page_id == ERR_NONE) | |
218 | page_id = ERR_FORWARDING_DENIED; | |
219 | ||
2cc81f1f | 220 | ErrorState *anErr = errorCon(page_id, HTTP_FORBIDDEN, request); |
b6b6f466 | 221 | |
222 | errorAppendEntry(entry, anErr); // frees anErr | |
223 | ||
be0c6690 | 224 | return; |
b6b6f466 | 225 | } |
226 | } | |
227 | ||
bf8fe701 | 228 | debugs(17, 3, "FwdState::start() '" << entry->url() << "'"); |
f4ef658f | 229 | /* |
230 | * This seems like an odd place to bind mem_obj and request. | |
231 | * Might want to assert that request is NULL at this point | |
232 | */ | |
6dd9f4bd | 233 | entry->mem_obj->request = HTTPMSGLOCK(request); |
b6b6f466 | 234 | #if URL_CHECKSUM_DEBUG |
235 | ||
236 | entry->mem_obj->checkUrlChecksum(); | |
237 | #endif | |
238 | ||
239 | if (shutting_down) { | |
240 | /* more yuck */ | |
2cc81f1f | 241 | ErrorState *anErr = errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE, request); |
b6b6f466 | 242 | errorAppendEntry(entry, anErr); // frees anErr |
be0c6690 | 243 | return; |
6801f8a8 | 244 | } |
62e76326 | 245 | |
b6b6f466 | 246 | switch (request->protocol) { |
247 | ||
248 | case PROTO_INTERNAL: | |
249 | internalStart(request, entry); | |
be0c6690 | 250 | return; |
b6b6f466 | 251 | |
252 | case PROTO_CACHEOBJ: | |
c83f0bd5 | 253 | CacheManager::GetInstance()->Start(client_fd, request, entry); |
be0c6690 | 254 | return; |
b6b6f466 | 255 | |
256 | case PROTO_URN: | |
257 | urnStart(request, entry); | |
be0c6690 | 258 | return; |
b6b6f466 | 259 | |
260 | default: | |
7a0fb323 | 261 | FwdState::Pointer fwd = new FwdState(client_fd, entry, request); |
2ad20b4f | 262 | |
fc68f6b1 | 263 | /* If we need to transparently proxy the request |
cc192b50 | 264 | * then we need the client source protocol, address and port */ |
26ac0430 | 265 | if (request->flags.spoof_client_ip) { |
2ad20b4f | 266 | fwd->src = request->client_addr; |
2ad20b4f | 267 | } |
fc68f6b1 | 268 | |
7a0fb323 | 269 | fwd->start(fwd); |
be0c6690 | 270 | return; |
b6b6f466 | 271 | } |
272 | ||
273 | /* NOTREACHED */ | |
41462d93 | 274 | } |
275 | ||
b6b6f466 | 276 | void |
277 | FwdState::fail(ErrorState * errorState) | |
278 | { | |
38413773 | 279 | debugs(17, 3, HERE << err_type_str[errorState->type] << " \"" << httpStatusString(errorState->httpStatus) << "\"\n\t" << entry->url() ); |
b6b6f466 | 280 | |
281 | if (err) | |
282 | errorStateFree(err); | |
283 | ||
284 | err = errorState; | |
285 | ||
286 | if (!errorState->request) | |
6dd9f4bd | 287 | errorState->request = HTTPMSGLOCK(request); |
64b66b76 CT |
288 | |
289 | request->detailError(errorState->type, errorState->xerrno); | |
b6b6f466 | 290 | } |
291 | ||
38413773 | 292 | /** |
b6b6f466 | 293 | * Frees fwdState without closing FD or generating an abort |
294 | */ | |
295 | void | |
296 | FwdState::unregister(int fd) | |
297 | { | |
38413773 | 298 | debugs(17, 3, HERE << entry->url() ); |
b6b6f466 | 299 | assert(fd == server_fd); |
300 | assert(fd > -1); | |
301 | comm_remove_close_handler(fd, fwdServerClosedWrapper, this); | |
302 | server_fd = -1; | |
303 | } | |
304 | ||
38413773 | 305 | /** |
b6b6f466 | 306 | * server-side modules call fwdComplete() when they are done |
307 | * downloading an object. Then, we either 1) re-forward the | |
308 | * request somewhere else if needed, or 2) call storeComplete() | |
309 | * to finish it off | |
310 | */ | |
311 | void | |
312 | FwdState::complete() | |
313 | { | |
314 | StoreEntry *e = entry; | |
315 | assert(entry->store_status == STORE_PENDING); | |
38413773 | 316 | debugs(17, 3, HERE << e->url() << "\n\tstatus " << entry->getReply()->sline.status ); |
b6b6f466 | 317 | #if URL_CHECKSUM_DEBUG |
318 | ||
319 | entry->mem_obj->checkUrlChecksum(); | |
320 | #endif | |
321 | ||
322 | logReplyStatus(n_tries, entry->getReply()->sline.status); | |
323 | ||
324 | if (reforward()) { | |
bf8fe701 | 325 | debugs(17, 3, "fwdComplete: re-forwarding " << entry->getReply()->sline.status << " " << e->url()); |
b6b6f466 | 326 | |
327 | if (server_fd > -1) | |
328 | unregister(server_fd); | |
329 | ||
3900307b | 330 | e->reset(); |
b6b6f466 | 331 | |
332 | startComplete(servers); | |
333 | } else { | |
4d6c56a6 | 334 | debugs(17, 3, "fwdComplete: server FD " << server_fd << " not re-forwarding status " << entry->getReply()->sline.status); |
9e5c22cf AJ |
335 | EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); |
336 | entry->complete(); | |
fc68f6b1 | 337 | |
338 | if (server_fd < 0) | |
339 | completed(); | |
340 | ||
7a0fb323 | 341 | self = NULL; // refcounted |
b6b6f466 | 342 | } |
343 | } | |
344 | ||
345 | ||
346 | /**** CALLBACK WRAPPERS ************************************************************/ | |
347 | ||
348 | static void | |
349 | fwdStartCompleteWrapper(FwdServer * servers, void *data) | |
350 | { | |
351 | FwdState *fwd = (FwdState *) data; | |
352 | fwd->startComplete(servers); | |
353 | } | |
354 | ||
355 | static void | |
356 | fwdServerClosedWrapper(int fd, void *data) | |
357 | { | |
358 | FwdState *fwd = (FwdState *) data; | |
359 | fwd->serverClosed(fd); | |
360 | } | |
361 | ||
362 | static void | |
363 | fwdConnectStartWrapper(void *data) | |
364 | { | |
365 | FwdState *fwd = (FwdState *) data; | |
366 | fwd->connectStart(); | |
367 | } | |
368 | ||
369 | #if USE_SSL | |
370 | static void | |
371 | fwdNegotiateSSLWrapper(int fd, void *data) | |
372 | { | |
373 | FwdState *fwd = (FwdState *) data; | |
374 | fwd->negotiateSSL(fd); | |
375 | } | |
376 | ||
377 | #endif | |
378 | ||
379 | static void | |
3ff65596 | 380 | fwdConnectDoneWrapper(int server_fd, const DnsLookupDetails &dns, comm_err_t status, int xerrno, void *data) |
b6b6f466 | 381 | { |
382 | FwdState *fwd = (FwdState *) data; | |
3ff65596 | 383 | fwd->connectDone(server_fd, dns, status, xerrno); |
b6b6f466 | 384 | } |
385 | ||
386 | static void | |
387 | fwdConnectTimeoutWrapper(int fd, void *data) | |
388 | { | |
389 | FwdState *fwd = (FwdState *) data; | |
390 | fwd->connectTimeout(fd); | |
391 | } | |
392 | ||
393 | /* | |
394 | * Accounts for closed persistent connections | |
395 | */ | |
396 | static void | |
397 | fwdPeerClosed(int fd, void *data) | |
398 | { | |
399 | peer *p = (peer *)data; | |
400 | p->stats.conn_open--; | |
401 | } | |
402 | ||
403 | /**** PRIVATE *****************************************************************/ | |
404 | ||
545782b8 | 405 | /* |
406 | * FwdState::checkRetry | |
26ac0430 | 407 | * |
545782b8 | 408 | * Return TRUE if the request SHOULD be retried. This method is |
409 | * called when the HTTP connection fails, or when the connection | |
410 | * is closed before server-side read the end of HTTP headers. | |
411 | */ | |
b6b6f466 | 412 | bool |
413 | FwdState::checkRetry() | |
68bd6892 | 414 | { |
d8fd0f18 | 415 | if (shutting_down) |
b6b6f466 | 416 | return false; |
62e76326 | 417 | |
9d2760b6 AR |
418 | if (!self) { // we have aborted before the server called us back |
419 | debugs(17, 5, HERE << "not retrying because of earlier abort"); | |
420 | // we will be destroyed when the server clears its Pointer to us | |
421 | return false; | |
422 | } | |
423 | ||
b6b6f466 | 424 | if (entry->store_status != STORE_PENDING) |
425 | return false; | |
62e76326 | 426 | |
b6b6f466 | 427 | if (!entry->isEmpty()) |
428 | return false; | |
62e76326 | 429 | |
b6b6f466 | 430 | if (n_tries > 10) |
431 | return false; | |
62e76326 | 432 | |
b6b6f466 | 433 | if (origin_tries > 2) |
434 | return false; | |
4ed0e075 | 435 | |
b6b6f466 | 436 | if (squid_curtime - start_t > Config.Timeout.forward) |
437 | return false; | |
62e76326 | 438 | |
b6b6f466 | 439 | if (flags.dont_retry) |
440 | return false; | |
62e76326 | 441 | |
5d4989a8 | 442 | if (!checkRetriable()) |
443 | return false; | |
444 | ||
58217e94 | 445 | if (request->bodyNibbled()) |
b6b6f466 | 446 | return false; |
62e76326 | 447 | |
b6b6f466 | 448 | return true; |
68bd6892 | 449 | } |
450 | ||
545782b8 | 451 | /* |
452 | * FwdState::checkRetriable | |
453 | * | |
454 | * Return TRUE if this is the kind of request that can be retried | |
455 | * after a failure. If the request is not retriable then we don't | |
456 | * want to risk sending it on a persistent connection. Instead we'll | |
457 | * force it to go on a new HTTP connection. | |
458 | */ | |
b6b6f466 | 459 | bool |
460 | FwdState::checkRetriable() | |
cb928909 | 461 | { |
462 | /* If there is a request body then Squid can only try once | |
463 | * even if the method is indempotent | |
464 | */ | |
465 | ||
5f8252d2 | 466 | if (request->body_pipe != NULL) |
b6b6f466 | 467 | return false; |
cb928909 | 468 | |
469 | /* RFC2616 9.1 Safe and Idempotent Methods */ | |
914b89a2 | 470 | switch (request->method.id()) { |
cb928909 | 471 | /* 9.1.1 Safe Methods */ |
472 | ||
473 | case METHOD_GET: | |
474 | ||
475 | case METHOD_HEAD: | |
914b89a2 | 476 | /* 9.1.2 Idempotent Methods */ |
cb928909 | 477 | |
478 | case METHOD_PUT: | |
479 | ||
480 | case METHOD_DELETE: | |
481 | ||
482 | case METHOD_OPTIONS: | |
483 | ||
484 | case METHOD_TRACE: | |
485 | break; | |
486 | ||
487 | default: | |
b6b6f466 | 488 | return false; |
cb928909 | 489 | } |
490 | ||
b6b6f466 | 491 | return true; |
cb928909 | 492 | } |
493 | ||
b6b6f466 | 494 | void |
495 | FwdState::serverClosed(int fd) | |
910169e5 | 496 | { |
bf8fe701 | 497 | debugs(17, 2, "fwdServerClosed: FD " << fd << " " << entry->url()); |
b6b6f466 | 498 | assert(server_fd == fd); |
499 | server_fd = -1; | |
62e76326 | 500 | |
3e8c047e | 501 | retryOrBail(); |
502 | } | |
503 | ||
504 | void | |
26ac0430 AJ |
505 | FwdState::retryOrBail() |
506 | { | |
b6b6f466 | 507 | if (checkRetry()) { |
508 | int originserver = (servers->_peer == NULL); | |
4a7a3d56 | 509 | debugs(17, 3, "fwdServerClosed: re-forwarding (" << n_tries << " tries, " << (squid_curtime - start_t) << " secs)"); |
62e76326 | 510 | |
b6b6f466 | 511 | if (servers->next) { |
62e76326 | 512 | /* use next, or cycle if origin server isn't last */ |
b6b6f466 | 513 | FwdServer *fs = servers; |
62e76326 | 514 | FwdServer **T, *T2 = NULL; |
b6b6f466 | 515 | servers = fs->next; |
62e76326 | 516 | |
3d0ac046 | 517 | for (T = &servers; *T; T2 = *T, T = &(*T)->next); |
62e76326 | 518 | if (T2 && T2->_peer) { |
519 | /* cycle */ | |
520 | *T = fs; | |
521 | fs->next = NULL; | |
522 | } else { | |
523 | /* Use next. The last "direct" entry is retried multiple times */ | |
b6b6f466 | 524 | servers = fs->next; |
62e76326 | 525 | fwdServerFree(fs); |
4ed0e075 | 526 | originserver = 0; |
62e76326 | 527 | } |
528 | } | |
529 | ||
5eee4ac6 AJ |
530 | /* Ditch error page if it was created before. |
531 | * A new one will be created if there's another problem */ | |
80cd008f AR |
532 | if (err) { |
533 | errorStateFree(err); | |
534 | err = NULL; | |
535 | } | |
5eee4ac6 | 536 | |
4ed0e075 | 537 | /* use eventAdd to break potential call sequence loops and to slow things down a little */ |
b6b6f466 | 538 | eventAdd("fwdConnectStart", fwdConnectStartWrapper, this, originserver ? 0.05 : 0.005, 0); |
62e76326 | 539 | |
540 | return; | |
d8fd0f18 | 541 | } |
62e76326 | 542 | |
9d2760b6 AR |
543 | // TODO: should we call completed() here and move doneWithRetries there? |
544 | doneWithRetries(); | |
545 | ||
546 | if (self != NULL && !err && shutting_down) { | |
2cc81f1f | 547 | errorCon(ERR_SHUTTING_DOWN, HTTP_SERVICE_UNAVAILABLE, request); |
f563eea9 | 548 | } |
62e76326 | 549 | |
b6b6f466 | 550 | self = NULL; // refcounted |
910169e5 | 551 | } |
552 | ||
9d2760b6 AR |
553 | // If the Server quits before nibbling at the request body, the body sender |
554 | // will not know (so that we can retry). Call this if we will not retry. We | |
555 | // will notify the sender so that it does not get stuck waiting for space. | |
556 | void | |
557 | FwdState::doneWithRetries() | |
558 | { | |
559 | if (request && request->body_pipe != NULL) | |
560 | request->body_pipe->expectNoConsumption(); | |
561 | } | |
562 | ||
3e8c047e | 563 | // called by the server that failed after calling unregister() |
564 | void | |
565 | FwdState::handleUnregisteredServerEnd() | |
566 | { | |
567 | debugs(17, 2, "handleUnregisteredServerEnd: self=" << self << | |
26ac0430 | 568 | " err=" << err << ' ' << entry->url()); |
3e8c047e | 569 | assert(server_fd < 0); |
570 | retryOrBail(); | |
571 | } | |
572 | ||
a7ad6e4e | 573 | #if USE_SSL |
b6b6f466 | 574 | void |
575 | FwdState::negotiateSSL(int fd) | |
a7ad6e4e | 576 | { |
b6b6f466 | 577 | FwdServer *fs = servers; |
a7ad6e4e | 578 | SSL *ssl = fd_table[fd].ssl; |
579 | int ret; | |
62e76326 | 580 | |
a7ad6e4e | 581 | if ((ret = SSL_connect(ssl)) <= 0) { |
62e76326 | 582 | int ssl_error = SSL_get_error(ssl, ret); |
583 | ||
584 | switch (ssl_error) { | |
585 | ||
586 | case SSL_ERROR_WANT_READ: | |
b6b6f466 | 587 | commSetSelect(fd, COMM_SELECT_READ, fwdNegotiateSSLWrapper, this, 0); |
62e76326 | 588 | return; |
589 | ||
590 | case SSL_ERROR_WANT_WRITE: | |
b6b6f466 | 591 | commSetSelect(fd, COMM_SELECT_WRITE, fwdNegotiateSSLWrapper, this, 0); |
62e76326 | 592 | return; |
593 | ||
594 | default: | |
26ac0430 AJ |
595 | debugs(81, 1, "fwdNegotiateSSL: Error negotiating SSL connection on FD " << fd << |
596 | ": " << ERR_error_string(ERR_get_error(), NULL) << " (" << ssl_error << | |
597 | "/" << ret << "/" << errno << ")"); | |
2ac4f6b5 | 598 | ErrorState *const anErr = makeConnectingError(ERR_SECURE_CONNECT_FAIL); |
a7ad6e4e | 599 | #ifdef EPROTO |
62e76326 | 600 | |
b6b6f466 | 601 | anErr->xerrno = EPROTO; |
a7ad6e4e | 602 | #else |
62e76326 | 603 | |
b6b6f466 | 604 | anErr->xerrno = EACCES; |
a7ad6e4e | 605 | #endif |
62e76326 | 606 | |
b6b6f466 | 607 | fail(anErr); |
62e76326 | 608 | |
609 | if (fs->_peer) { | |
610 | peerConnectFailed(fs->_peer); | |
611 | fs->_peer->stats.conn_open--; | |
612 | } | |
613 | ||
614 | comm_close(fd); | |
615 | return; | |
616 | } | |
a7ad6e4e | 617 | } |
62e76326 | 618 | |
f38c5e43 | 619 | if (fs->_peer && !SSL_session_reused(ssl)) { |
620 | if (fs->_peer->sslSession) | |
621 | SSL_SESSION_free(fs->_peer->sslSession); | |
622 | ||
623 | fs->_peer->sslSession = SSL_get1_session(ssl); | |
624 | } | |
625 | ||
b6b6f466 | 626 | dispatch(); |
a7ad6e4e | 627 | } |
628 | ||
b6b6f466 | 629 | void |
630 | FwdState::initiateSSL() | |
a7ad6e4e | 631 | { |
b6b6f466 | 632 | FwdServer *fs = servers; |
633 | int fd = server_fd; | |
a7ad6e4e | 634 | SSL *ssl; |
635 | SSL_CTX *sslContext = NULL; | |
636 | peer *peer = fs->_peer; | |
62e76326 | 637 | |
a7ad6e4e | 638 | if (peer) { |
62e76326 | 639 | assert(peer->use_ssl); |
640 | sslContext = peer->sslContext; | |
a7ad6e4e | 641 | } else { |
62e76326 | 642 | sslContext = Config.ssl_client.sslContext; |
a7ad6e4e | 643 | } |
62e76326 | 644 | |
a7ad6e4e | 645 | assert(sslContext); |
62e76326 | 646 | |
a7ad6e4e | 647 | if ((ssl = SSL_new(sslContext)) == NULL) { |
bf8fe701 | 648 | debugs(83, 1, "fwdInitiateSSL: Error allocating handle: " << ERR_error_string(ERR_get_error(), NULL) ); |
2cc81f1f | 649 | ErrorState *anErr = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request); |
b6b6f466 | 650 | anErr->xerrno = errno; |
b6b6f466 | 651 | fail(anErr); |
652 | self = NULL; // refcounted | |
62e76326 | 653 | return; |
a7ad6e4e | 654 | } |
62e76326 | 655 | |
a7ad6e4e | 656 | SSL_set_fd(ssl, fd); |
62e76326 | 657 | |
a7ad6e4e | 658 | if (peer) { |
62e76326 | 659 | if (peer->ssldomain) |
660 | SSL_set_ex_data(ssl, ssl_ex_index_server, peer->ssldomain); | |
661 | ||
a7ad6e4e | 662 | #if NOT_YET |
62e76326 | 663 | |
664 | else if (peer->name) | |
665 | SSL_set_ex_data(ssl, ssl_ex_index_server, peer->name); | |
666 | ||
a7ad6e4e | 667 | #endif |
62e76326 | 668 | |
669 | else | |
670 | SSL_set_ex_data(ssl, ssl_ex_index_server, peer->host); | |
f38c5e43 | 671 | |
672 | if (peer->sslSession) | |
673 | SSL_set_session(ssl, peer->sslSession); | |
674 | ||
a7ad6e4e | 675 | } else { |
cc192b50 | 676 | SSL_set_ex_data(ssl, ssl_ex_index_server, (void*)request->GetHost()); |
a7ad6e4e | 677 | } |
62e76326 | 678 | |
4d5a6db8 | 679 | // Create the ACL check list now, while we have access to more info. |
680 | // The list is used in ssl_verify_cb() and is freed in ssl_free(). | |
681 | if (acl_access *acl = Config.ssl_client.cert_error) { | |
c0941a6a | 682 | ACLFilledChecklist *check = new ACLFilledChecklist(acl, request, dash_str); |
4d5a6db8 | 683 | check->fd(fd); |
684 | SSL_set_ex_data(ssl, ssl_ex_index_cert_error_check, check); | |
685 | } | |
686 | ||
a7ad6e4e | 687 | fd_table[fd].ssl = ssl; |
688 | fd_table[fd].read_method = &ssl_read_method; | |
689 | fd_table[fd].write_method = &ssl_write_method; | |
b6b6f466 | 690 | negotiateSSL(fd); |
a7ad6e4e | 691 | } |
62e76326 | 692 | |
a7ad6e4e | 693 | #endif |
694 | ||
b6b6f466 | 695 | void |
3ff65596 | 696 | FwdState::connectDone(int aServerFD, const DnsLookupDetails &dns, comm_err_t status, int xerrno) |
41462d93 | 697 | { |
b6b6f466 | 698 | FwdServer *fs = servers; |
699 | assert(server_fd == aServerFD); | |
62e76326 | 700 | |
3ff65596 AR |
701 | request->recordLookup(dns); |
702 | ||
beed27a2 | 703 | if (Config.onoff.log_ip_on_direct && status != COMM_ERR_DNS && fs->code == HIER_DIRECT) |
74780d33 | 704 | updateHierarchyInfo(); |
beed27a2 | 705 | |
41462d93 | 706 | if (status == COMM_ERR_DNS) { |
62e76326 | 707 | /* |
708 | * Only set the dont_retry flag if the DNS lookup fails on | |
709 | * a direct connection. If DNS lookup fails when trying | |
710 | * a neighbor cache, we may want to retry another option. | |
711 | */ | |
712 | ||
713 | if (NULL == fs->_peer) | |
b6b6f466 | 714 | flags.dont_retry = 1; |
62e76326 | 715 | |
cc192b50 | 716 | debugs(17, 4, "fwdConnectDone: Unknown host: " << request->GetHost()); |
62e76326 | 717 | |
2ac4f6b5 | 718 | ErrorState *const anErr = makeConnectingError(ERR_DNS_FAIL); |
62e76326 | 719 | |
3ff65596 | 720 | anErr->dnsError = dns.error; |
62e76326 | 721 | |
b6b6f466 | 722 | fail(anErr); |
62e76326 | 723 | |
62e76326 | 724 | comm_close(server_fd); |
41462d93 | 725 | } else if (status != COMM_OK) { |
62e76326 | 726 | assert(fs); |
2ac4f6b5 | 727 | ErrorState *const anErr = makeConnectingError(ERR_CONNECT_FAIL); |
b6b6f466 | 728 | anErr->xerrno = xerrno; |
62e76326 | 729 | |
b6b6f466 | 730 | fail(anErr); |
62e76326 | 731 | |
a4bd7d1f | 732 | if (fs->_peer) |
62e76326 | 733 | peerConnectFailed(fs->_peer); |
62e76326 | 734 | |
735 | comm_close(server_fd); | |
41462d93 | 736 | } else { |
bf8fe701 | 737 | debugs(17, 3, "fwdConnectDone: FD " << server_fd << ": '" << entry->url() << "'" ); |
62e76326 | 738 | |
739 | if (fs->_peer) | |
740 | peerConnectSucceded(fs->_peer); | |
741 | ||
a7ad6e4e | 742 | #if USE_SSL |
62e76326 | 743 | |
744 | if ((fs->_peer && fs->_peer->use_ssl) || | |
745 | (!fs->_peer && request->protocol == PROTO_HTTPS)) { | |
b6b6f466 | 746 | initiateSSL(); |
62e76326 | 747 | return; |
748 | } | |
749 | ||
a7ad6e4e | 750 | #endif |
b6b6f466 | 751 | dispatch(); |
41462d93 | 752 | } |
41462d93 | 753 | } |
754 | ||
b6b6f466 | 755 | void |
756 | FwdState::connectTimeout(int fd) | |
41462d93 | 757 | { |
beed27a2 | 758 | FwdServer *fs = servers; |
759 | ||
bf8fe701 | 760 | debugs(17, 2, "fwdConnectTimeout: FD " << fd << ": '" << entry->url() << "'" ); |
b6b6f466 | 761 | assert(fd == server_fd); |
62e76326 | 762 | |
beed27a2 | 763 | if (Config.onoff.log_ip_on_direct && fs->code == HIER_DIRECT && fd_table[fd].ipaddr[0]) |
74780d33 | 764 | updateHierarchyInfo(); |
beed27a2 | 765 | |
528b2c61 | 766 | if (entry->isEmpty()) { |
2cc81f1f | 767 | ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_GATEWAY_TIMEOUT, request); |
b6b6f466 | 768 | anErr->xerrno = ETIMEDOUT; |
769 | fail(anErr); | |
62e76326 | 770 | /* |
26ac0430 | 771 | * This marks the peer DOWN ... |
62e76326 | 772 | */ |
773 | ||
b6b6f466 | 774 | if (servers) |
775 | if (servers->_peer) | |
776 | peerConnectFailed(servers->_peer); | |
41462d93 | 777 | } |
62e76326 | 778 | |
41462d93 | 779 | comm_close(fd); |
780 | } | |
781 | ||
b6b6f466 | 782 | void |
783 | FwdState::connectStart() | |
41462d93 | 784 | { |
3900307b | 785 | const char *url = entry->url(); |
cb928909 | 786 | int fd = -1; |
b6b6f466 | 787 | FwdServer *fs = servers; |
db1cd23c | 788 | const char *host; |
789 | unsigned short port; | |
777831e0 | 790 | int ctimeout; |
b6b6f466 | 791 | int ftimeout = Config.Timeout.forward - (squid_curtime - start_t); |
62e76326 | 792 | |
b7ac5457 | 793 | Ip::Address outgoing; |
b7ac5457 | 794 | Ip::Address client_addr; |
db1cd23c | 795 | assert(fs); |
b6b6f466 | 796 | assert(server_fd == -1); |
bf8fe701 | 797 | debugs(17, 3, "fwdConnectStart: " << url); |
62e76326 | 798 | |
3ff65596 AR |
799 | if (n_tries == 0) // first attempt |
800 | request->hier.first_conn_start = current_time; | |
801 | ||
29b8d8d6 | 802 | if (fs->_peer) { |
62e76326 | 803 | ctimeout = fs->_peer->connect_timeout > 0 ? fs->_peer->connect_timeout |
804 | : Config.Timeout.peer_connect; | |
db1cd23c | 805 | } else { |
62e76326 | 806 | ctimeout = Config.Timeout.connect; |
db1cd23c | 807 | } |
62e76326 | 808 | |
b0758e04 AJ |
809 | if (request->flags.spoof_client_ip) { |
810 | if (!fs->_peer || !fs->_peer->options.no_tproxy) | |
811 | client_addr = request->client_addr; | |
812 | // else no tproxy today ... | |
813 | } | |
fc68f6b1 | 814 | |
777831e0 | 815 | if (ftimeout < 0) |
816 | ftimeout = 5; | |
817 | ||
818 | if (ftimeout < ctimeout) | |
819 | ctimeout = ftimeout; | |
820 | ||
d67acb4e AJ |
821 | |
822 | request->flags.pinned = 0; | |
823 | if (fs->code == PINNED) { | |
26ac0430 AJ |
824 | ConnStateData *pinned_connection = request->pinnedConnection(); |
825 | assert(pinned_connection); | |
d67acb4e AJ |
826 | fd = pinned_connection->validatePinnedConnection(request, fs->_peer); |
827 | if (fd >= 0) { | |
26ac0430 | 828 | pinned_connection->unpinConnection(); |
d67acb4e AJ |
829 | #if 0 |
830 | if (!fs->_peer) | |
831 | fs->code = HIER_DIRECT; | |
832 | #endif | |
833 | server_fd = fd; | |
834 | n_tries++; | |
835 | request->flags.pinned = 1; | |
836 | if (pinned_connection->pinnedAuth()) | |
837 | request->flags.auth = 1; | |
838 | comm_add_close_handler(fd, fwdServerClosedWrapper, this); | |
60b8c73c | 839 | updateHierarchyInfo(); |
3ff65596 | 840 | connectDone(fd, DnsLookupDetails(), COMM_OK, 0); |
d67acb4e AJ |
841 | return; |
842 | } | |
26ac0430 | 843 | /* Failure. Fall back on next path */ |
60b8c73c | 844 | debugs(17,2,HERE << " Pinned connection " << pinned_connection << " not valid. Releasing."); |
d67acb4e AJ |
845 | request->releasePinnedConnection(); |
846 | servers = fs->next; | |
847 | fwdServerFree(fs); | |
848 | connectStart(); | |
849 | return; | |
26ac0430 | 850 | } |
d67acb4e | 851 | |
af6a12ee | 852 | if (fs->_peer) { |
06093389 AJ |
853 | host = fs->_peer->host; |
854 | port = fs->_peer->http_port; | |
855 | fd = fwdPconnPool->pop(fs->_peer->name, fs->_peer->http_port, request->GetHost(), client_addr, checkRetriable()); | |
af6a12ee | 856 | } else { |
06093389 AJ |
857 | host = request->GetHost(); |
858 | port = request->port; | |
859 | fd = fwdPconnPool->pop(host, port, NULL, client_addr, checkRetriable()); | |
860 | } | |
c8ceec27 | 861 | if (fd >= 0) { |
862 | debugs(17, 3, "fwdConnectStart: reusing pconn FD " << fd); | |
863 | server_fd = fd; | |
864 | n_tries++; | |
4ed0e075 | 865 | |
74780d33 | 866 | if (!fs->_peer) |
c8ceec27 | 867 | origin_tries++; |
74780d33 AJ |
868 | |
869 | updateHierarchyInfo(); | |
4ed0e075 | 870 | |
c8ceec27 | 871 | comm_add_close_handler(fd, fwdServerClosedWrapper, this); |
4ed0e075 | 872 | |
152e24b3 HN |
873 | // TODO: Avoid this if %<lp is not used? F->local_port is often cached. |
874 | request->hier.peer_local_port = comm_local_port(fd); | |
875 | ||
c8ceec27 | 876 | dispatch(); |
4ed0e075 | 877 | |
c8ceec27 | 878 | return; |
41462d93 | 879 | } |
62e76326 | 880 | |
bc87dc25 | 881 | #if URL_CHECKSUM_DEBUG |
b6b6f466 | 882 | entry->mem_obj->checkUrlChecksum(); |
bc87dc25 | 883 | #endif |
62e76326 | 884 | |
6db78a1a | 885 | outgoing = getOutgoingAddr(request, fs->_peer); |
62e76326 | 886 | |
407b9bb1 AJ |
887 | // if IPv6 is disabled try to force IPv4-only outgoing. |
888 | if (!Ip::EnableIpv6 && !outgoing.SetIPv4()) { | |
715b5def | 889 | debugs(50, 4, "fwdConnectStart: IPv6 is Disabled. Cannot connect from " << outgoing); |
407b9bb1 | 890 | ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request); |
715b5def | 891 | anErr->xerrno = EAFNOSUPPORT; |
407b9bb1 AJ |
892 | fail(anErr); |
893 | self = NULL; // refcounted | |
894 | return; | |
895 | } | |
896 | ||
897 | // if IPv6 is split-stack, prefer IPv4 | |
898 | if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK) { | |
899 | // NP: This is not a great choice of default, | |
900 | // but with the current Internet being IPv4-majority has a higher success rate. | |
901 | // if setting to IPv4 fails we dont care, that just means to use IPv6 outgoing. | |
902 | outgoing.SetIPv4(); | |
903 | } | |
904 | ||
425de4c8 | 905 | tos_t tos = GetTosToServer(request); |
d6827718 | 906 | |
425de4c8 AJ |
907 | #if SO_MARK |
908 | nfmark_t mark = GetNfmarkToServer(request); | |
909 | debugs(17, 3, "fwdConnectStart: got outgoing addr " << outgoing << ", tos " << int(tos) | |
ab745b44 | 910 | << ", netfilter mark " << mark); |
425de4c8 AJ |
911 | #else |
912 | nfmark_t mark = 0; | |
913 | debugs(17, 3, "fwdConnectStart: got outgoing addr " << outgoing << ", tos " << int(tos)); | |
914 | #endif | |
62e76326 | 915 | |
56832054 | 916 | int commFlags = COMM_NONBLOCKING; |
f165d2fb | 917 | if (request->flags.spoof_client_ip) { |
b0758e04 | 918 | if (!fs->_peer || !fs->_peer->options.no_tproxy) |
56832054 | 919 | commFlags |= COMM_TRANSPARENT; |
b0758e04 | 920 | // else no tproxy today ... |
c303f6e3 | 921 | } |
62e76326 | 922 | |
425de4c8 | 923 | fd = comm_openex(SOCK_STREAM, IPPROTO_TCP, outgoing, commFlags, tos, mark, url); |
232820e2 | 924 | |
cc192b50 | 925 | debugs(17, 3, "fwdConnectStart: got TCP FD " << fd); |
926 | ||
41462d93 | 927 | if (fd < 0) { |
bf8fe701 | 928 | debugs(50, 4, "fwdConnectStart: " << xstrerror()); |
2cc81f1f | 929 | ErrorState *anErr = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request); |
b6b6f466 | 930 | anErr->xerrno = errno; |
931 | fail(anErr); | |
932 | self = NULL; // refcounted | |
62e76326 | 933 | return; |
41462d93 | 934 | } |
62e76326 | 935 | |
b6b6f466 | 936 | server_fd = fd; |
937 | n_tries++; | |
4ed0e075 | 938 | |
939 | if (!fs->_peer) | |
b6b6f466 | 940 | origin_tries++; |
4ed0e075 | 941 | |
152e24b3 HN |
942 | request->hier.peer_local_port = comm_local_port(fd); |
943 | ||
c7f9eb6d | 944 | /* |
945 | * stats.conn_open is used to account for the number of | |
946 | * connections that we have open to the peer, so we can limit | |
947 | * based on the max-conn option. We need to increment here, | |
948 | * even if the connection may fail. | |
949 | */ | |
62e76326 | 950 | |
a4bd7d1f | 951 | if (fs->_peer) { |
62e76326 | 952 | fs->_peer->stats.conn_open++; |
a4bd7d1f | 953 | comm_add_close_handler(fd, fwdPeerClosed, fs->_peer); |
954 | } | |
62e76326 | 955 | |
b6b6f466 | 956 | comm_add_close_handler(fd, fwdServerClosedWrapper, this); |
62e76326 | 957 | |
b6b6f466 | 958 | commSetTimeout(fd, ctimeout, fwdConnectTimeoutWrapper, this); |
62e76326 | 959 | |
74780d33 | 960 | updateHierarchyInfo(); |
b6b6f466 | 961 | commConnectStart(fd, host, port, fwdConnectDoneWrapper, this); |
41462d93 | 962 | } |
963 | ||
b6b6f466 | 964 | void |
965 | FwdState::startComplete(FwdServer * theServers) | |
41462d93 | 966 | { |
bf8fe701 | 967 | debugs(17, 3, "fwdStartComplete: " << entry->url() ); |
62e76326 | 968 | |
b6b6f466 | 969 | if (theServers != NULL) { |
970 | servers = theServers; | |
971 | connectStart(); | |
41462d93 | 972 | } else { |
b6b6f466 | 973 | startFail(); |
41462d93 | 974 | } |
41462d93 | 975 | } |
976 | ||
b6b6f466 | 977 | void |
978 | FwdState::startFail() | |
41462d93 | 979 | { |
bf8fe701 | 980 | debugs(17, 3, "fwdStartFail: " << entry->url() ); |
2cc81f1f | 981 | ErrorState *anErr = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE, request); |
b6b6f466 | 982 | anErr->xerrno = errno; |
983 | fail(anErr); | |
984 | self = NULL; // refcounted | |
41462d93 | 985 | } |
910169e5 | 986 | |
b6b6f466 | 987 | void |
988 | FwdState::dispatch() | |
41462d93 | 989 | { |
c7f9eb6d | 990 | peer *p = NULL; |
60745f24 | 991 | debugs(17, 3, "fwdDispatch: FD " << client_fd << ": Fetching '" << RequestMethodStr(request->method) << " " << entry->url() << "'" ); |
e0ebe27c | 992 | /* |
993 | * Assert that server_fd is set. This is to guarantee that fwdState | |
994 | * is attached to something and will be deallocated when server_fd | |
995 | * is closed. | |
996 | */ | |
a7ad6e4e | 997 | assert(server_fd > -1); |
62e76326 | 998 | |
3900307b | 999 | fd_note(server_fd, entry->url()); |
62e76326 | 1000 | |
781ce8ff | 1001 | fd_table[server_fd].noteUse(fwdPconnPool); |
62e76326 | 1002 | |
a7ad6e4e | 1003 | /*assert(!EBIT_TEST(entry->flags, ENTRY_DISPATCHED)); */ |
1004 | assert(entry->ping_status != PING_WAITING); | |
62e76326 | 1005 | |
a7ad6e4e | 1006 | assert(entry->lock_count); |
62e76326 | 1007 | |
a7ad6e4e | 1008 | EBIT_SET(entry->flags, ENTRY_DISPATCHED); |
62e76326 | 1009 | |
cc192b50 | 1010 | netdbPingSite(request->GetHost()); |
62e76326 | 1011 | |
425de4c8 AJ |
1012 | /* Update server side TOS and Netfilter mark if using persistent connections. */ |
1013 | if (Config.onoff.server_pconns) { | |
1014 | if (Ip::Qos::TheConfig.isAclTosActive()) { | |
1015 | tos_t tos = GetTosToServer(request); | |
1016 | Ip::Qos::setSockTos(server_fd, tos); | |
1017 | } | |
1018 | #if SO_MARK | |
1019 | if (Ip::Qos::TheConfig.isAclNfmarkActive()) { | |
1020 | nfmark_t mark = GetNfmarkToServer(request); | |
1021 | Ip::Qos::setSockNfmark(server_fd, mark); | |
1022 | } | |
1023 | #endif | |
1024 | } | |
2201bec6 | 1025 | |
425de4c8 | 1026 | /* Retrieves remote server TOS or MARK value, and stores it as part of the |
7172612f | 1027 | * original client request FD object. It is later used to forward |
425de4c8 | 1028 | * remote server's TOS/MARK in the response to the client in case of a MISS. |
7172612f | 1029 | */ |
425de4c8 AJ |
1030 | if (Ip::Qos::TheConfig.isHitNfmarkActive()) { |
1031 | fde * clientFde = &fd_table[client_fd]; | |
1032 | fde * servFde = &fd_table[server_fd]; | |
1033 | if (clientFde && servFde) { | |
1034 | /* Get the netfilter mark for the connection */ | |
1035 | Ip::Qos::getNfmarkFromServer(server_fd, servFde, clientFde); | |
1036 | } | |
1037 | } | |
1038 | ||
1039 | #if _SQUID_LINUX_ | |
1040 | /* Bug 2537: The TOS forward part of QOS only applies to patched Linux kernels. */ | |
1041 | if (Ip::Qos::TheConfig.isHitTosActive()) { | |
1042 | fde * clientFde = &fd_table[client_fd]; | |
1043 | if (clientFde) { | |
1044 | /* Get the TOS value for the packet */ | |
1045 | Ip::Qos::getTosFromServer(server_fd, clientFde); | |
7172612f | 1046 | } |
26ac0430 | 1047 | } |
7172612f AJ |
1048 | #endif |
1049 | ||
b6b6f466 | 1050 | if (servers && (p = servers->_peer)) { |
62e76326 | 1051 | p->stats.fetches++; |
b6b6f466 | 1052 | request->peer_login = p->login; |
1053 | request->peer_domain = p->domain; | |
1054 | httpStart(this); | |
41462d93 | 1055 | } else { |
b6b6f466 | 1056 | request->peer_login = NULL; |
1057 | request->peer_domain = NULL; | |
62e76326 | 1058 | |
1059 | switch (request->protocol) { | |
a7ad6e4e | 1060 | #if USE_SSL |
62e76326 | 1061 | |
1062 | case PROTO_HTTPS: | |
b6b6f466 | 1063 | httpStart(this); |
62e76326 | 1064 | break; |
a7ad6e4e | 1065 | #endif |
62e76326 | 1066 | |
1067 | case PROTO_HTTP: | |
b6b6f466 | 1068 | httpStart(this); |
62e76326 | 1069 | break; |
1070 | ||
1071 | case PROTO_GOPHER: | |
b6b6f466 | 1072 | gopherStart(this); |
62e76326 | 1073 | break; |
1074 | ||
1075 | case PROTO_FTP: | |
b6b6f466 | 1076 | ftpStart(this); |
62e76326 | 1077 | break; |
1078 | ||
62e76326 | 1079 | case PROTO_CACHEOBJ: |
1080 | ||
1081 | case PROTO_INTERNAL: | |
1082 | ||
1083 | case PROTO_URN: | |
1084 | fatal_dump("Should never get here"); | |
1085 | break; | |
1086 | ||
1087 | case PROTO_WHOIS: | |
b6b6f466 | 1088 | whoisStart(this); |
62e76326 | 1089 | break; |
1090 | ||
db80e881 | 1091 | case PROTO_WAIS: /* Not implemented */ |
fc68f6b1 | 1092 | |
62e76326 | 1093 | default: |
bf8fe701 | 1094 | debugs(17, 1, "fwdDispatch: Cannot retrieve '" << entry->url() << "'" ); |
2cc81f1f | 1095 | ErrorState *anErr = errorCon(ERR_UNSUP_REQ, HTTP_BAD_REQUEST, request); |
b6b6f466 | 1096 | fail(anErr); |
62e76326 | 1097 | /* |
1098 | * Force a persistent connection to be closed because | |
1099 | * some Netscape browsers have a bug that sends CONNECT | |
1100 | * requests as GET's over persistent connections. | |
1101 | */ | |
1102 | request->flags.proxy_keepalive = 0; | |
1103 | /* | |
1104 | * Set the dont_retry flag becuase this is not a | |
1105 | * transient (network) error; its a bug. | |
1106 | */ | |
b6b6f466 | 1107 | flags.dont_retry = 1; |
1108 | comm_close(server_fd); | |
62e76326 | 1109 | break; |
1110 | } | |
41462d93 | 1111 | } |
1112 | } | |
1113 | ||
545782b8 | 1114 | /* |
1115 | * FwdState::reforward | |
1116 | * | |
1117 | * returns TRUE if the transaction SHOULD be re-forwarded to the | |
1118 | * next choice in the FwdServers list. This method is called when | |
1119 | * server-side communication completes normally, or experiences | |
1120 | * some error after receiving the end of HTTP headers. | |
1121 | */ | |
b6b6f466 | 1122 | int |
1123 | FwdState::reforward() | |
db1cd23c | 1124 | { |
b6b6f466 | 1125 | StoreEntry *e = entry; |
1126 | FwdServer *fs = servers; | |
db1cd23c | 1127 | http_status s; |
1128 | assert(e->store_status == STORE_PENDING); | |
1129 | assert(e->mem_obj); | |
bc87dc25 | 1130 | #if URL_CHECKSUM_DEBUG |
62e76326 | 1131 | |
528b2c61 | 1132 | e->mem_obj->checkUrlChecksum(); |
bc87dc25 | 1133 | #endif |
62e76326 | 1134 | |
bf8fe701 | 1135 | debugs(17, 3, "fwdReforward: " << e->url() << "?" ); |
62e76326 | 1136 | |
d6eb18d6 | 1137 | if (!EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) { |
bf8fe701 | 1138 | debugs(17, 3, "fwdReforward: No, ENTRY_FWD_HDR_WAIT isn't set"); |
62e76326 | 1139 | return 0; |
d6eb18d6 | 1140 | } |
62e76326 | 1141 | |
437823b4 | 1142 | if (n_tries > Config.forward_max_tries) |
62e76326 | 1143 | return 0; |
1144 | ||
b6b6f466 | 1145 | if (origin_tries > 1) |
4ed0e075 | 1146 | return 0; |
1147 | ||
58217e94 | 1148 | if (request->bodyNibbled()) |
62e76326 | 1149 | return 0; |
1150 | ||
db1cd23c | 1151 | assert(fs); |
62e76326 | 1152 | |
b6b6f466 | 1153 | servers = fs->next; |
62e76326 | 1154 | |
db1cd23c | 1155 | fwdServerFree(fs); |
62e76326 | 1156 | |
b6b6f466 | 1157 | if (servers == NULL) { |
bf8fe701 | 1158 | debugs(17, 3, "fwdReforward: No forward-servers left"); |
62e76326 | 1159 | return 0; |
db1cd23c | 1160 | } |
62e76326 | 1161 | |
528b2c61 | 1162 | s = e->getReply()->sline.status; |
4a7a3d56 | 1163 | debugs(17, 3, "fwdReforward: status " << s); |
b6b6f466 | 1164 | return reforwardableStatus(s); |
db1cd23c | 1165 | } |
1166 | ||
2ac4f6b5 AR |
1167 | /** |
1168 | * Create "503 Service Unavailable" or "504 Gateway Timeout" error depending | |
1169 | * on whether this is a validation request. RFC 2616 says that we MUST reply | |
1170 | * with "504 Gateway Timeout" if validation fails and cached reply has | |
1171 | * proxy-revalidate, must-revalidate or s-maxage Cache-Control directive. | |
1172 | */ | |
1173 | ErrorState * | |
1174 | FwdState::makeConnectingError(const err_type type) const | |
1175 | { | |
1176 | return errorCon(type, request->flags.need_validation ? | |
1177 | HTTP_GATEWAY_TIMEOUT : HTTP_SERVICE_UNAVAILABLE, request); | |
1178 | } | |
1179 | ||
b6b6f466 | 1180 | static void |
1181 | fwdStats(StoreEntry * s) | |
64d8034e | 1182 | { |
b6b6f466 | 1183 | int i; |
1184 | int j; | |
1185 | storeAppendPrintf(s, "Status"); | |
62e76326 | 1186 | |
b6b6f466 | 1187 | for (j = 0; j <= MAX_FWD_STATS_IDX; j++) { |
1188 | storeAppendPrintf(s, "\ttry#%d", j + 1); | |
64d8034e | 1189 | } |
64d8034e | 1190 | |
b6b6f466 | 1191 | storeAppendPrintf(s, "\n"); |
0185bd6f | 1192 | |
b6b6f466 | 1193 | for (i = 0; i <= (int) HTTP_INVALID_HEADER; i++) { |
1194 | if (FwdReplyCodes[0][i] == 0) | |
1195 | continue; | |
0185bd6f | 1196 | |
b6b6f466 | 1197 | storeAppendPrintf(s, "%3d", i); |
0185bd6f | 1198 | |
b6b6f466 | 1199 | for (j = 0; j <= MAX_FWD_STATS_IDX; j++) { |
1200 | storeAppendPrintf(s, "\t%d", FwdReplyCodes[j][i]); | |
62e76326 | 1201 | } |
62e76326 | 1202 | |
b6b6f466 | 1203 | storeAppendPrintf(s, "\n"); |
e0ebe27c | 1204 | } |
7197b20d | 1205 | } |
1206 | ||
a4bd7d1f | 1207 | |
b6b6f466 | 1208 | /**** STATIC MEMBER FUNCTIONS *************************************************/ |
db1cd23c | 1209 | |
b6b6f466 | 1210 | bool |
1211 | FwdState::reforwardableStatus(http_status s) | |
db1cd23c | 1212 | { |
b6b6f466 | 1213 | switch (s) { |
62e76326 | 1214 | |
b6b6f466 | 1215 | case HTTP_BAD_GATEWAY: |
62e76326 | 1216 | |
b6b6f466 | 1217 | case HTTP_GATEWAY_TIMEOUT: |
1218 | return true; | |
62e76326 | 1219 | |
b6b6f466 | 1220 | case HTTP_FORBIDDEN: |
62e76326 | 1221 | |
b6b6f466 | 1222 | case HTTP_INTERNAL_SERVER_ERROR: |
62e76326 | 1223 | |
b6b6f466 | 1224 | case HTTP_NOT_IMPLEMENTED: |
62e76326 | 1225 | |
b6b6f466 | 1226 | case HTTP_SERVICE_UNAVAILABLE: |
1227 | return Config.retry.onerror; | |
62e76326 | 1228 | |
b6b6f466 | 1229 | default: |
1230 | return false; | |
db1cd23c | 1231 | } |
b6b6f466 | 1232 | |
1233 | /* NOTREACHED */ | |
db1cd23c | 1234 | } |
8ddcc35d | 1235 | |
06093389 AJ |
1236 | /** |
1237 | * Decide where details need to be gathered to correctly describe a persistent connection. | |
1238 | * What is needed: | |
d85b8894 AJ |
1239 | * - host name of server at other end of this link (either peer or requested host) |
1240 | * - port to which we connected the other end of this link (for peer or request) | |
1241 | * - domain for which the connection is supposed to be used | |
1242 | * - address of the client for which we made the connection | |
06093389 | 1243 | */ |
781ce8ff | 1244 | void |
b7ac5457 | 1245 | FwdState::pconnPush(int fd, const peer *_peer, const HttpRequest *req, const char *domain, Ip::Address &client_addr) |
781ce8ff | 1246 | { |
06093389 AJ |
1247 | if (_peer) { |
1248 | fwdPconnPool->push(fd, _peer->name, _peer->http_port, domain, client_addr); | |
1249 | } else { | |
1250 | /* small performance improvement, using NULL for domain instead of listing it twice */ | |
1251 | /* although this will leave a gap open for url-rewritten domains to share a link */ | |
1252 | fwdPconnPool->push(fd, req->GetHost(), req->port, NULL, client_addr); | |
1253 | } | |
781ce8ff | 1254 | } |
1255 | ||
8ddcc35d | 1256 | void |
b6b6f466 | 1257 | FwdState::initModule() |
8ddcc35d | 1258 | { |
b6b6f466 | 1259 | memDataInit(MEM_FWD_SERVER, "FwdServer", sizeof(FwdServer), 0); |
6852be71 | 1260 | RegisterWithCacheManager(); |
8ddcc35d | 1261 | } |
1262 | ||
62ee09ca | 1263 | void |
84f50787 | 1264 | FwdState::RegisterWithCacheManager(void) |
62ee09ca | 1265 | { |
8822ebee | 1266 | Mgr::RegisterAction("forward", "Request Forwarding Statistics", fwdStats, 0, 1); |
62ee09ca | 1267 | } |
1268 | ||
b6b6f466 | 1269 | void |
1270 | FwdState::logReplyStatus(int tries, http_status status) | |
8ddcc35d | 1271 | { |
1272 | if (status > HTTP_INVALID_HEADER) | |
62e76326 | 1273 | return; |
1274 | ||
8ddcc35d | 1275 | assert(tries); |
62e76326 | 1276 | |
8ddcc35d | 1277 | tries--; |
62e76326 | 1278 | |
8ddcc35d | 1279 | if (tries > MAX_FWD_STATS_IDX) |
62e76326 | 1280 | tries = MAX_FWD_STATS_IDX; |
1281 | ||
8ddcc35d | 1282 | FwdReplyCodes[tries][status]++; |
1283 | } | |
1284 | ||
b6b6f466 | 1285 | void |
1286 | FwdState::serversFree(FwdServer ** FSVR) | |
8ddcc35d | 1287 | { |
b6b6f466 | 1288 | FwdServer *fs; |
62e76326 | 1289 | |
b6b6f466 | 1290 | while ((fs = *FSVR)) { |
1291 | *FSVR = fs->next; | |
1292 | fwdServerFree(fs); | |
9977e14b | 1293 | } |
b6b6f466 | 1294 | } |
62e76326 | 1295 | |
93280d3c AR |
1296 | /** From Comment #5 by Henrik Nordstrom made at |
1297 | http://www.squid-cache.org/bugs/show_bug.cgi?id=2391 on 2008-09-19 | |
1298 | ||
1299 | updateHierarchyInfo should be called each time a new path has been | |
1300 | selected or when more information about the path is available (i.e. the | |
1301 | server IP), and when it's called it needs to be given reasonable | |
1302 | arguments describing the now selected path.. | |
1303 | ||
1304 | It does not matter from a functional perspective if it gets called a few | |
1305 | times more than what is really needed, but calling it too often may | |
1306 | obviously hurt performance. | |
93280d3c | 1307 | */ |
74780d33 AJ |
1308 | // updates HierarchyLogEntry, guessing nextHop and its format |
1309 | void | |
1310 | FwdState::updateHierarchyInfo() | |
1311 | { | |
1312 | assert(request); | |
1313 | ||
1314 | FwdServer *fs = servers; | |
1315 | assert(fs); | |
1316 | ||
74780d33 AJ |
1317 | const char *nextHop = NULL; |
1318 | ||
dc029c2f | 1319 | if (fs->_peer) { |
ed9f056a HN |
1320 | // went to peer, log peer host name |
1321 | nextHop = fs->_peer->name; | |
74780d33 AJ |
1322 | } else { |
1323 | // went DIRECT, must honor log_ip_on_direct | |
1324 | ||
1325 | // XXX: or should we use request->host_addr here? how? | |
1326 | assert(server_fd >= 0); | |
1327 | nextHop = fd_table[server_fd].ipaddr; | |
1328 | if (!Config.onoff.log_ip_on_direct || !nextHop[0]) | |
1329 | nextHop = request->GetHost(); // domain name | |
dc029c2f | 1330 | } |
74780d33 AJ |
1331 | |
1332 | assert(nextHop); | |
1333 | hierarchyNote(&request->hier, fs->code, nextHop); | |
1334 | } | |
1335 | ||
1336 | ||
b6b6f466 | 1337 | /**** PRIVATE NON-MEMBER FUNCTIONS ********************************************/ |
62e76326 | 1338 | |
b6b6f466 | 1339 | static void |
1340 | fwdServerFree(FwdServer * fs) | |
1341 | { | |
1342 | cbdataReferenceDone(fs->_peer); | |
1343 | memFree(fs, MEM_FWD_SERVER); | |
1344 | } | |
62e76326 | 1345 | |
b7ac5457 | 1346 | static Ip::Address |
cc192b50 | 1347 | aclMapAddr(acl_address * head, ACLChecklist * ch) |
b6b6f466 | 1348 | { |
1349 | acl_address *l; | |
62e76326 | 1350 | |
b7ac5457 | 1351 | Ip::Address addr; |
62e76326 | 1352 | |
26ac0430 | 1353 | for (l = head; l; l = l->next) { |
b50e327b | 1354 | if (!l->aclList || ch->matchAclListFast(l->aclList)) |
b6b6f466 | 1355 | return l->addr; |
9977e14b | 1356 | } |
b6b6f466 | 1357 | |
cc192b50 | 1358 | addr.SetAnyAddr(); |
b6b6f466 | 1359 | return addr; |
8ddcc35d | 1360 | } |
b6a2f15e | 1361 | |
057f5854 | 1362 | /* |
1363 | * DPW 2007-05-19 | |
1364 | * Formerly static, but now used by client_side_request.cc | |
1365 | */ | |
425de4c8 AJ |
1366 | /// Checks for a TOS value to apply depending on the ACL |
1367 | tos_t | |
b6b6f466 | 1368 | aclMapTOS(acl_tos * head, ACLChecklist * ch) |
b6a2f15e | 1369 | { |
b6b6f466 | 1370 | acl_tos *l; |
62e76326 | 1371 | |
b6b6f466 | 1372 | for (l = head; l; l = l->next) { |
b50e327b | 1373 | if (!l->aclList || ch->matchAclListFast(l->aclList)) |
b6b6f466 | 1374 | return l->tos; |
1375 | } | |
5894ad28 | 1376 | |
b6b6f466 | 1377 | return 0; |
1378 | } | |
5894ad28 | 1379 | |
425de4c8 AJ |
1380 | /// Checks for a netfilter mark value to apply depending on the ACL |
1381 | nfmark_t | |
1382 | aclMapNfmark(acl_nfmark * head, ACLChecklist * ch) | |
1383 | { | |
1384 | acl_nfmark *l; | |
1385 | ||
1386 | for (l = head; l; l = l->next) { | |
1387 | if (!l->aclList || ch->matchAclListFast(l->aclList)) | |
1388 | return l->nfmark; | |
1389 | } | |
1390 | ||
1391 | return 0; | |
1392 | } | |
1393 | ||
b7ac5457 | 1394 | Ip::Address |
6db78a1a | 1395 | getOutgoingAddr(HttpRequest * request, struct peer *dst_peer) |
b6b6f466 | 1396 | { |
b0758e04 | 1397 | if (request && request->flags.spoof_client_ip) { |
96d64448 AJ |
1398 | if (!dst_peer || !dst_peer->options.no_tproxy) { |
1399 | #if FOLLOW_X_FORWARDED_FOR && LINUX_NETFILTER | |
1400 | if (Config.onoff.tproxy_uses_indirect_client) | |
1401 | return request->indirect_client_addr; | |
1402 | else | |
1403 | #endif | |
656a1484 | 1404 | return request->client_addr; |
96d64448 | 1405 | } |
b0758e04 AJ |
1406 | // else no tproxy today ... |
1407 | } | |
c303f6e3 | 1408 | |
b50e327b | 1409 | if (!Config.accessList.outgoing_address) { |
b7ac5457 | 1410 | return Ip::Address(); // anything will do. |
b50e327b AJ |
1411 | } |
1412 | ||
c0941a6a | 1413 | ACLFilledChecklist ch(NULL, request, NULL); |
6db78a1a AJ |
1414 | ch.dst_peer = dst_peer; |
1415 | ||
26ac0430 | 1416 | if (request) { |
9aaf363a AJ |
1417 | #if FOLLOW_X_FORWARDED_FOR |
1418 | if (Config.onoff.acl_uses_indirect_client) | |
1419 | ch.src_addr = request->indirect_client_addr; | |
1420 | else | |
1421 | #endif | |
f892cbd2 | 1422 | ch.src_addr = request->client_addr; |
b6b6f466 | 1423 | ch.my_addr = request->my_addr; |
b6b6f466 | 1424 | } |
62e76326 | 1425 | |
b6b6f466 | 1426 | return aclMapAddr(Config.accessList.outgoing_address, &ch); |
1427 | } | |
62e76326 | 1428 | |
425de4c8 AJ |
1429 | tos_t |
1430 | GetTosToServer(HttpRequest * request) | |
1431 | { | |
1432 | ACLFilledChecklist ch(NULL, request, NULL); | |
1433 | ||
1434 | if (request) { | |
1435 | ch.src_addr = request->client_addr; | |
1436 | ch.my_addr = request->my_addr; | |
1437 | } | |
1438 | ||
1439 | return aclMapTOS(Ip::Qos::TheConfig.tosToServer, &ch); | |
1440 | } | |
1441 | ||
1442 | nfmark_t | |
1443 | GetNfmarkToServer(HttpRequest * request) | |
b6b6f466 | 1444 | { |
c0941a6a | 1445 | ACLFilledChecklist ch(NULL, request, NULL); |
62e76326 | 1446 | |
b6b6f466 | 1447 | if (request) { |
1448 | ch.src_addr = request->client_addr; | |
1449 | ch.my_addr = request->my_addr; | |
b6a2f15e | 1450 | } |
62e76326 | 1451 | |
425de4c8 | 1452 | return aclMapNfmark(Ip::Qos::TheConfig.nfmarkToServer, &ch); |
b6a2f15e | 1453 | } |