]> git.ipfire.org Git - thirdparty/squid.git/blame - src/Server.cc
- ICAP-unrelated improvements from the squid3-icap branch on SF
[thirdparty/squid.git] / src / Server.cc
CommitLineData
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 43ServerStateData::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
53ServerStateData::~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
71void
72ServerStateData::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.
99void 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
117void
118ServerStateData::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
125bool
126ServerStateData::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
137void
138ServerStateData::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
150void
151ServerStateData::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
163void
164ServerStateData::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
177void
178ServerStateData::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
187void
188ServerStateData::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
197void
198ServerStateData::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
207void
208ServerStateData::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
218void
219ServerStateData::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
226void
227ServerStateData::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 270void
271ServerStateData::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
287void
288ServerStateData::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 300bool
5f8252d2 301ServerStateData::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
332void 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
349bool
350ServerStateData::doneWithIcap() const {
351 return !virginBodyDestination && !adaptedHeadSource && !adaptedBodySource;
352}
353
354// can supply more virgin response body data
355void
356ServerStateData::noteMoreBodySpaceAvailable(BodyPipe &)
357{
358 maybeReadVirginBody();
359}
360
361// the consumer of our virgin response body aborted, we should too
362void
363ServerStateData::noteBodyConsumerAborted(BodyPipe &bp)
364{
365 stopProducingFor(virginBodyDestination, false);
366 handleIcapAborted();
367}
368
369// received adapted response headers (body may follow)
370void
371ServerStateData::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)
405void
406ServerStateData::noteIcapHeadersAborted()
407{
408 adaptedHeadSource = NULL;
409 handleIcapAborted();
410}
411
412// more adapted response body is available
413void
414ServerStateData::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
433void
434ServerStateData::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
445void ServerStateData::handleAdaptedBodyProducerAborted()
446{
447 stopConsumingFrom(adaptedBodySource);
448 handleIcapAborted();
449}
450
451// common part of noteIcapHeadersAdapted and handleAdaptedBodyProductionEnded
452void
453ServerStateData::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
462void
463ServerStateData::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