]>
Commit | Line | Data |
---|---|---|
cd304fc2 | 1 | /* |
5f8252d2 | 2 | * $Id: Server.cc,v 1.8 2007/04/06 04:50:05 rousskov Exp $ |
cd304fc2 | 3 | * |
4 | * DEBUG: | |
5 | * AUTHOR: Duane Wessels | |
6 | * | |
7 | * SQUID Web Proxy Cache http://www.squid-cache.org/ | |
8 | * ---------------------------------------------------------- | |
9 | * | |
10 | * Squid is the result of efforts by numerous individuals from | |
11 | * the Internet community; see the CONTRIBUTORS file for full | |
12 | * details. Many organizations have provided support for Squid's | |
13 | * development; see the SPONSORS file for full details. Squid is | |
14 | * Copyrighted (C) 2001 by the Regents of the University of | |
15 | * California; see the COPYRIGHT file for full details. Squid | |
16 | * incorporates software developed and/or copyrighted by other | |
17 | * sources; see the CREDITS file for full details. | |
18 | * | |
19 | * This program is free software; you can redistribute it and/or modify | |
20 | * it under the terms of the GNU General Public License as published by | |
21 | * the Free Software Foundation; either version 2 of the License, or | |
22 | * (at your option) any later version. | |
23 | * | |
24 | * This program is distributed in the hope that it will be useful, | |
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
27 | * GNU General Public License for more details. | |
28 | * | |
29 | * You should have received a copy of the GNU General Public License | |
30 | * along with this program; if not, write to the Free Software | |
31 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
32 | * | |
33 | */ | |
34 | ||
35 | #include "squid.h" | |
36 | #include "Server.h" | |
37 | #include "Store.h" | |
38 | #include "HttpRequest.h" | |
39 | #include "HttpReply.h" | |
5f8252d2 | 40 | #include "errorpage.h" |
41 | ||
cd304fc2 | 42 | |
5f8252d2 | 43 | ServerStateData::ServerStateData(FwdState *theFwdState): requestSender(NULL) |
cd304fc2 | 44 | { |
45 | fwd = theFwdState; | |
46 | entry = fwd->entry; | |
34266cde | 47 | |
5f8252d2 | 48 | entry->lock(); |
34266cde | 49 | |
6dd9f4bd | 50 | request = HTTPMSGLOCK(fwd->request); |
cd304fc2 | 51 | } |
52 | ||
53 | ServerStateData::~ServerStateData() | |
54 | { | |
97b5e68f | 55 | entry->unlock(); |
cd304fc2 | 56 | |
6dd9f4bd | 57 | HTTPMSGUNLOCK(request); |
58 | HTTPMSGUNLOCK(reply); | |
cd304fc2 | 59 | |
60 | fwd = NULL; // refcounted | |
61 | ||
5f8252d2 | 62 | if (requestBodySource != NULL) |
63 | requestBodySource->clearConsumer(); | |
64 | ||
65 | #if ICAP_CLIENT | |
66 | cleanIcap(); | |
67 | #endif | |
68 | } | |
69 | ||
70 | // called when no more server communication is expected; may quit | |
71 | void | |
72 | ServerStateData::serverComplete() | |
73 | { | |
74 | debugs(11,5,HERE << "serverComplete " << this); | |
75 | ||
76 | if (!doneWithServer()) { | |
77 | closeServer(); | |
78 | assert(doneWithServer()); | |
79 | } | |
80 | ||
81 | if (requestBodySource != NULL) | |
82 | stopConsumingFrom(requestBodySource); | |
83 | ||
84 | #if ICAP_CLIENT | |
85 | if (virginBodyDestination != NULL) | |
86 | stopProducingFor(virginBodyDestination, true); | |
87 | ||
88 | if (!doneWithIcap()) | |
89 | return; | |
90 | #endif | |
91 | ||
92 | completeForwarding(); | |
93 | quitIfAllDone(); | |
94 | } | |
95 | ||
96 | // When we are done talking to the primary server, we may be still talking | |
97 | // to the ICAP service. And vice versa. Here, we quit only if we are done | |
98 | // talking to both. | |
99 | void ServerStateData::quitIfAllDone() { | |
100 | #if ICAP_CLIENT | |
101 | if (!doneWithIcap()) { | |
102 | debugs(11,5, HERE << "transaction not done: still talking to ICAP"); | |
103 | return; | |
104 | } | |
105 | #endif | |
106 | ||
107 | if (!doneWithServer()) { | |
108 | debugs(11,5, HERE << "transaction not done: still talking to server"); | |
109 | return; | |
110 | } | |
111 | ||
112 | debugs(11,3, HERE << "transaction done"); | |
113 | delete this; | |
114 | } | |
115 | ||
116 | // FTP side overloads this to work around multiple calls to fwd->complete | |
117 | void | |
118 | ServerStateData::completeForwarding() { | |
119 | debugs(11,5, HERE << "completing forwarding for " << fwd); | |
120 | assert(fwd != NULL); | |
121 | fwd->complete(); | |
122 | } | |
123 | ||
124 | // Entry-dependent callbacks use this check to quit if the entry went bad | |
125 | bool | |
126 | ServerStateData::abortOnBadEntry(const char *abortReason) | |
127 | { | |
128 | if (entry->isAccepting()) | |
129 | return false; | |
130 | ||
131 | debugs(11,5, HERE << "entry is not Accepting!"); | |
132 | abortTransaction(abortReason); | |
133 | return true; | |
134 | } | |
135 | ||
136 | // more request or adapted response body is available | |
137 | void | |
138 | ServerStateData::noteMoreBodyDataAvailable(BodyPipe &bp) | |
139 | { | |
140 | #if ICAP_CLIENT | |
141 | if (adaptedBodySource == &bp) { | |
142 | handleMoreAdaptedBodyAvailable(); | |
143 | return; | |
144 | } | |
145 | #endif | |
146 | handleMoreRequestBodyAvailable(); | |
147 | } | |
148 | ||
149 | // the entire request or adapted response body was provided, successfully | |
150 | void | |
151 | ServerStateData::noteBodyProductionEnded(BodyPipe &bp) | |
152 | { | |
cd304fc2 | 153 | #if ICAP_CLIENT |
5f8252d2 | 154 | if (adaptedBodySource == &bp) { |
155 | handleAdaptedBodyProductionEnded(); | |
156 | return; | |
c99de607 | 157 | } |
cd304fc2 | 158 | #endif |
5f8252d2 | 159 | handleRequestBodyProductionEnded(); |
160 | } | |
161 | ||
162 | // premature end of the request or adapted response body production | |
163 | void | |
164 | ServerStateData::noteBodyProducerAborted(BodyPipe &bp) | |
165 | { | |
166 | #if ICAP_CLIENT | |
167 | if (adaptedBodySource == &bp) { | |
168 | handleAdaptedBodyProducerAborted(); | |
169 | return; | |
170 | } | |
171 | #endif | |
172 | handleRequestBodyProducerAborted(); | |
173 | } | |
174 | ||
175 | ||
176 | // more origin request body data is available | |
177 | void | |
178 | ServerStateData::handleMoreRequestBodyAvailable() | |
179 | { | |
180 | if (!requestSender) | |
181 | sendMoreRequestBody(); | |
182 | else | |
183 | debugs(9,3, HERE << "waiting for request body write to complete"); | |
184 | } | |
185 | ||
186 | // there will be no more handleMoreRequestBodyAvailable calls | |
187 | void | |
188 | ServerStateData::handleRequestBodyProductionEnded() | |
189 | { | |
190 | if (!requestSender) | |
191 | doneSendingRequestBody(); | |
192 | else | |
193 | debugs(9,3, HERE << "waiting for request body write to complete"); | |
194 | } | |
195 | ||
196 | // called when we are done sending request body; kids extend this | |
197 | void | |
198 | ServerStateData::doneSendingRequestBody() { | |
199 | debugs(9,3, HERE << "done sending request body"); | |
200 | assert(requestBodySource != NULL); | |
201 | stopConsumingFrom(requestBodySource); | |
202 | ||
203 | // kids extend this | |
204 | } | |
205 | ||
206 | // called when body producers aborts; kids extend this | |
207 | void | |
208 | ServerStateData::handleRequestBodyProducerAborted() | |
209 | { | |
210 | if (requestSender != NULL) | |
211 | debugs(9,3, HERE << "fyi: request body aborted while we were sending"); | |
212 | ||
213 | stopConsumingFrom(requestBodySource); // requestSender, if any, will notice | |
214 | ||
215 | // kids extend this | |
216 | } | |
217 | ||
218 | void | |
219 | ServerStateData::sentRequestBodyWrapper(int fd, char *bufnotused, size_t size, comm_err_t errflag, int xerrno, void *data) | |
220 | { | |
221 | ServerStateData *server = static_cast<ServerStateData *>(data); | |
222 | server->sentRequestBody(fd, size, errflag); | |
223 | } | |
224 | ||
225 | // called when we wrote request headers(!) or a part of the body | |
226 | void | |
227 | ServerStateData::sentRequestBody(int fd, size_t size, comm_err_t errflag) | |
228 | { | |
229 | debug(11, 5) ("sentRequestBody: FD %d: size %d: errflag %d.\n", | |
230 | fd, (int) size, errflag); | |
231 | debugs(32,3,HERE << "sentRequestBody called"); | |
232 | ||
233 | requestSender = NULL; | |
234 | ||
235 | if (size > 0) { | |
236 | fd_bytes(fd, size, FD_WRITE); | |
237 | kb_incr(&statCounter.server.all.kbytes_out, size); | |
238 | // kids should increment their counters | |
239 | } | |
240 | ||
241 | if (errflag == COMM_ERR_CLOSING) | |
242 | return; | |
243 | ||
244 | if (!requestBodySource) { | |
245 | debugs(9,3, HERE << "detected while-we-were-sending abort"); | |
246 | return; // do nothing; | |
247 | } | |
248 | ||
249 | if (errflag) { | |
250 | debug(11, 1) ("sentRequestBody error: FD %d: %s\n", fd, xstrerr(errno)); | |
251 | ErrorState *err; | |
252 | err = errorCon(ERR_WRITE_ERROR, HTTP_BAD_GATEWAY, fwd->request); | |
253 | err->xerrno = errno; | |
254 | fwd->fail(err); | |
255 | abortTransaction("I/O error while sending request body"); | |
256 | return; | |
257 | } | |
258 | ||
259 | if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { | |
260 | abortTransaction("store entry aborted while sending request body"); | |
261 | return; | |
262 | } | |
263 | ||
264 | if (requestBodySource->exhausted()) | |
265 | doneSendingRequestBody(); | |
266 | else | |
267 | sendMoreRequestBody(); | |
cd304fc2 | 268 | } |
269 | ||
5f8252d2 | 270 | void |
271 | ServerStateData::sendMoreRequestBody() | |
272 | { | |
273 | assert(requestBodySource != NULL); | |
274 | assert(!requestSender); | |
275 | MemBuf buf; | |
276 | if (requestBodySource->getMoreData(buf)) { | |
277 | debugs(9,3, HERE << "will write " << buf.contentSize() << " request body bytes"); | |
278 | requestSender = &ServerStateData::sentRequestBodyWrapper; | |
279 | comm_write_mbuf(dataDescriptor(), &buf, requestSender, this); | |
280 | } else { | |
281 | debugs(9,3, HERE << "will wait for more request body bytes or eof"); | |
282 | requestSender = NULL; | |
283 | } | |
284 | } | |
285 | ||
286 | // called by noteIcapHeadersAdapted(), HTTP server overwrites this | |
287 | void | |
288 | ServerStateData::haveParsedReplyHeaders() | |
289 | { | |
290 | // default does nothing | |
291 | } | |
292 | ||
293 | ||
0c25e715 | 294 | #if ICAP_CLIENT |
cd304fc2 | 295 | /* |
c99de607 | 296 | * Initiate an ICAP transaction. Return true on success. |
cd304fc2 | 297 | * Caller will handle error condition by generating a Squid error message |
298 | * or take other action. | |
299 | */ | |
c99de607 | 300 | bool |
5f8252d2 | 301 | ServerStateData::startIcap(ICAPServiceRep::Pointer service, HttpRequest *cause) |
cd304fc2 | 302 | { |
c99de607 | 303 | debug(11,5)("ServerStateData::startIcap() called\n"); |
304 | if (!service) { | |
305 | debug(11,3)("ServerStateData::startIcap fails: lack of service\n"); | |
306 | return false; | |
307 | } | |
308 | if (service->broken()) { | |
309 | debug(11,3)("ServerStateData::startIcap fails: broken service\n"); | |
310 | return false; | |
311 | } | |
5f8252d2 | 312 | |
313 | // check whether we should be sending a body as well | |
314 | assert(!virginBodyDestination); | |
315 | assert(!reply->body_pipe); | |
316 | // start body pipe to feed ICAP transaction if needed | |
317 | ssize_t size = 0; | |
318 | if (reply->expectingBody(cause->method, size) && size) { | |
319 | virginBodyDestination = new BodyPipe(this); | |
320 | reply->body_pipe = virginBodyDestination; | |
321 | debugs(93, 6, HERE << "will send virgin reply body to " << | |
322 | virginBodyDestination << "; size: " << size); | |
323 | } | |
324 | ||
325 | adaptedHeadSource = new ICAPModXact(this, reply, cause, service); | |
326 | ICAPModXact::AsyncStart(adaptedHeadSource.getRaw()); | |
c99de607 | 327 | return true; |
cd304fc2 | 328 | } |
0c25e715 | 329 | |
5f8252d2 | 330 | // properly cleans up ICAP-related state |
331 | // may be called multiple times | |
332 | void ServerStateData::cleanIcap() { | |
333 | debugs(11,5, HERE << "cleaning ICAP"); | |
334 | ||
335 | if (virginBodyDestination != NULL) | |
336 | stopProducingFor(virginBodyDestination, false); | |
337 | ||
338 | if (adaptedHeadSource != NULL) { | |
339 | AsyncCall(11,5, adaptedHeadSource.getRaw(), ICAPModXact::noteInitiatorAborted); | |
340 | adaptedHeadSource = NULL; | |
341 | } | |
342 | ||
343 | if (adaptedBodySource != NULL) | |
344 | stopConsumingFrom(adaptedBodySource); | |
345 | ||
346 | assert(doneWithIcap()); // make sure the two methods are in sync | |
347 | } | |
348 | ||
349 | bool | |
350 | ServerStateData::doneWithIcap() const { | |
351 | return !virginBodyDestination && !adaptedHeadSource && !adaptedBodySource; | |
352 | } | |
353 | ||
354 | // can supply more virgin response body data | |
355 | void | |
356 | ServerStateData::noteMoreBodySpaceAvailable(BodyPipe &) | |
357 | { | |
358 | maybeReadVirginBody(); | |
359 | } | |
360 | ||
361 | // the consumer of our virgin response body aborted, we should too | |
362 | void | |
363 | ServerStateData::noteBodyConsumerAborted(BodyPipe &bp) | |
364 | { | |
365 | stopProducingFor(virginBodyDestination, false); | |
366 | handleIcapAborted(); | |
367 | } | |
368 | ||
369 | // received adapted response headers (body may follow) | |
370 | void | |
371 | ServerStateData::noteIcapHeadersAdapted() | |
372 | { | |
373 | // extract and lock reply before (adaptedHeadSource = NULL) can destroy it | |
374 | HttpReply *rep = dynamic_cast<HttpReply*>(adaptedHeadSource->adapted.header); | |
375 | HTTPMSGLOCK(rep); | |
376 | adaptedHeadSource = NULL; // we do not expect any more messages from it | |
377 | ||
378 | if (abortOnBadEntry("entry went bad while waiting for adapted headers")) { | |
379 | HTTPMSGUNLOCK(rep); // hopefully still safe, even if "this" is deleted | |
380 | return; | |
381 | } | |
382 | ||
383 | assert(rep); | |
384 | entry->replaceHttpReply(rep); | |
385 | HTTPMSGUNLOCK(reply); | |
386 | ||
387 | reply = rep; // already HTTPMSGLOCKed above | |
388 | ||
389 | haveParsedReplyHeaders(); | |
390 | ||
391 | assert(!adaptedBodySource); | |
392 | if (reply->body_pipe != NULL) { | |
393 | // subscribe to receive adapted body | |
394 | adaptedBodySource = reply->body_pipe; | |
395 | // assume that ICAP does not auto-consume on failures | |
396 | assert(adaptedBodySource->setConsumerIfNotLate(this)); | |
397 | } else { | |
398 | // no body | |
399 | handleIcapCompleted(); | |
400 | } | |
401 | ||
402 | } | |
403 | ||
404 | // will not receive adapted response headers (and, hence, body) | |
405 | void | |
406 | ServerStateData::noteIcapHeadersAborted() | |
407 | { | |
408 | adaptedHeadSource = NULL; | |
409 | handleIcapAborted(); | |
410 | } | |
411 | ||
412 | // more adapted response body is available | |
413 | void | |
414 | ServerStateData::handleMoreAdaptedBodyAvailable() | |
415 | { | |
416 | const size_t contentSize = adaptedBodySource->buf().contentSize(); | |
417 | ||
418 | debugs(11,5, HERE << "consuming " << contentSize << " bytes of adapted " << | |
419 | "response body at offset " << adaptedBodySource->consumedSize()); | |
420 | ||
421 | if (abortOnBadEntry("entry refuses adapted body")) | |
422 | return; | |
423 | ||
424 | assert(entry); | |
425 | BodyPipeCheckout bpc(*adaptedBodySource); | |
426 | const StoreIOBuffer ioBuf(&bpc.buf, bpc.offset); | |
427 | entry->write(ioBuf); | |
428 | bpc.buf.consume(contentSize); | |
429 | bpc.checkIn(); | |
430 | } | |
431 | ||
432 | // the entire adapted response body was produced, successfully | |
433 | void | |
434 | ServerStateData::handleAdaptedBodyProductionEnded() | |
435 | { | |
436 | stopConsumingFrom(adaptedBodySource); | |
437 | ||
438 | if (abortOnBadEntry("entry went bad while waiting for adapted body eof")) | |
439 | return; | |
440 | ||
441 | handleIcapCompleted(); | |
442 | } | |
443 | ||
444 | // premature end of the adapted response body | |
445 | void ServerStateData::handleAdaptedBodyProducerAborted() | |
446 | { | |
447 | stopConsumingFrom(adaptedBodySource); | |
448 | handleIcapAborted(); | |
449 | } | |
450 | ||
451 | // common part of noteIcapHeadersAdapted and handleAdaptedBodyProductionEnded | |
452 | void | |
453 | ServerStateData::handleIcapCompleted() | |
454 | { | |
455 | debugs(11,5, HERE << "handleIcapCompleted"); | |
456 | cleanIcap(); | |
457 | completeForwarding(); | |
458 | quitIfAllDone(); | |
459 | } | |
460 | ||
461 | // common part of noteIcap*Aborted and noteBodyConsumerAborted methods | |
462 | void | |
463 | ServerStateData::handleIcapAborted() | |
464 | { | |
465 | debugs(11,5, HERE << "handleIcapAborted; entry empty: " << entry->isEmpty()); | |
466 | ||
467 | if (abortOnBadEntry("entry went bad while ICAP aborted")) | |
468 | return; | |
469 | ||
470 | if (entry->isEmpty()) { | |
471 | debugs(11,9, HERE << "creating ICAP error entry after ICAP failure"); | |
472 | ErrorState *err = | |
473 | errorCon(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request); | |
474 | err->xerrno = errno; | |
475 | fwd->fail(err); | |
476 | fwd->dontRetry(true); | |
477 | } | |
478 | ||
479 | debugs(11,5, HERE << "bailing after ICAP failure"); | |
480 | ||
481 | cleanIcap(); | |
482 | closeServer(); | |
483 | quitIfAllDone(); | |
484 | } | |
485 | ||
0c25e715 | 486 | #endif |