]> git.ipfire.org Git - thirdparty/squid.git/blob - src/client_side.cc
Import IPv6 support from squid3-ipv6 branch to 3-HEAD.
[thirdparty/squid.git] / src / client_side.cc
1
2 /*
3 * $Id: client_side.cc,v 1.771 2007/12/14 23:11:46 amosjeffries Exp $
4 *
5 * DEBUG: section 33 Client-side Routines
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 /* Errors and client side
37 *
38 * Problem the first: the store entry is no longer authoritative on the
39 * reply status. EBITTEST (E_ABORT) is no longer a valid test outside
40 * of client_side_reply.c.
41 * Problem the second: resources are wasted if we delay in cleaning up.
42 * Problem the third we can't depend on a connection close to clean up.
43 *
44 * Nice thing the first: Any step in the stream can callback with data
45 * representing an error.
46 * Nice thing the second: once you stop requesting reads from upstream,
47 * upstream can be stopped too.
48 *
49 * Solution #1: Error has a callback mechanism to hand over a membuf
50 * with the error content. The failing node pushes that back as the
51 * reply. Can this be generalised to reduce duplicate efforts?
52 * A: Possibly. For now, only one location uses this.
53 * How to deal with pre-stream errors?
54 * Tell client_side_reply that we *want* an error page before any
55 * stream calls occur. Then we simply read as normal.
56 */
57
58 #include "squid.h"
59 #include "client_side.h"
60 #include "clientStream.h"
61 #include "IPInterception.h"
62 #include "AuthUserRequest.h"
63 #include "Store.h"
64 #include "comm.h"
65 #include "HttpHdrContRange.h"
66 #include "HttpReply.h"
67 #include "HttpRequest.h"
68 #include "MemObject.h"
69 #include "fde.h"
70 #include "client_side_request.h"
71 #include "ACLChecklist.h"
72 #include "ConnectionDetail.h"
73 #include "client_side_reply.h"
74 #include "ClientRequestContext.h"
75 #include "MemBuf.h"
76 #include "SquidTime.h"
77
78 #if LINGERING_CLOSE
79 #define comm_close comm_lingering_close
80 #endif
81
82 /* Persistent connection logic:
83 *
84 * requests (httpClientRequest structs) get added to the connection
85 * list, with the current one being chr
86 *
87 * The request is *immediately* kicked off, and data flows through
88 * to clientSocketRecipient.
89 *
90 * If the data that arrives at clientSocketRecipient is not for the current
91 * request, clientSocketRecipient simply returns, without requesting more
92 * data, or sending it.
93 *
94 * ClientKeepAliveNextRequest will then detect the presence of data in
95 * the next ClientHttpRequest, and will send it, restablishing the
96 * data flow.
97 */
98
99 /* our socket-related context */
100
101
102 CBDATA_CLASS_INIT(ClientSocketContext);
103
104 void *
105 ClientSocketContext::operator new (size_t byteCount)
106 {
107 /* derived classes with different sizes must implement their own new */
108 assert (byteCount == sizeof (ClientSocketContext));
109 CBDATA_INIT_TYPE(ClientSocketContext);
110 return cbdataAlloc(ClientSocketContext);
111 }
112
113 void
114 ClientSocketContext::operator delete (void *address)
115 {
116 cbdataFree (address);
117 }
118
119 /* Local functions */
120 /* ClientSocketContext */
121 static ClientSocketContext *ClientSocketContextNew(ClientHttpRequest *);
122 /* other */
123 static IOCB clientWriteComplete;
124 static IOCB clientWriteBodyComplete;
125 static IOCB clientReadRequest;
126 static bool clientParseRequest(ConnStateData::Pointer conn, bool &do_next_read);
127 static void clientAfterReadingRequests(int fd, ConnStateData::Pointer &conn, int do_next_read);
128 static PF connStateClosed;
129 static PF requestTimeout;
130 static PF clientLifetimeTimeout;
131 static ClientSocketContext *parseHttpRequestAbort(ConnStateData::Pointer & conn,
132 const char *uri);
133 static ClientSocketContext *parseHttpRequest(ConnStateData::Pointer &, HttpParser *, method_t *, HttpVersion *);
134 #if USE_IDENT
135 static IDCB clientIdentDone;
136 #endif
137 static CSCB clientSocketRecipient;
138 static CSD clientSocketDetach;
139 static void clientSetKeepaliveFlag(ClientHttpRequest *);
140 static int clientIsContentLengthValid(HttpRequest * r);
141 static bool okToAccept();
142 static int clientIsRequestBodyValid(int64_t bodyLength);
143 static int clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength);
144
145 static void clientUpdateStatHistCounters(log_type logType, int svc_time);
146 static void clientUpdateStatCounters(log_type logType);
147 static void clientUpdateHierCounters(HierarchyLogEntry *);
148 static bool clientPingHasFinished(ping_data const *aPing);
149 static void clientPrepareLogWithRequestDetails(HttpRequest *, AccessLogEntry *);
150 #ifndef PURIFY
151 static int connIsUsable(ConnStateData::Pointer conn);
152 #endif
153 static int responseFinishedOrFailed(HttpReply * rep, StoreIOBuffer const &receivedData);
154 static void ClientSocketContextPushDeferredIfNeeded(ClientSocketContext::Pointer deferredRequest, ConnStateData::Pointer & conn);
155 static void clientUpdateSocketStats(log_type logType, size_t size);
156
157 char *skipLeadingSpace(char *aString);
158 static int connReadWasError(ConnStateData::Pointer& conn, comm_err_t, int size, int xerrno);
159 static int connFinishedWithConn(ConnStateData::Pointer& conn, int size);
160 static void connNoteUseOfBuffer(ConnStateData* conn, size_t byteCount);
161 static int connKeepReadingIncompleteRequest(ConnStateData::Pointer & conn);
162 static void connCancelIncompleteRequests(ConnStateData::Pointer & conn);
163
164 static ConnStateData *connStateCreate(const IPAddress &peer, const IPAddress &me, int fd, http_port_list *port);
165
166 int
167 ClientSocketContext::fd() const
168 {
169 assert (http);
170 assert (http->getConn() != NULL);
171 return http->getConn()->fd;
172 }
173
174 clientStreamNode *
175 ClientSocketContext::getTail() const
176 {
177 if (http->client_stream.tail)
178 return (clientStreamNode *)http->client_stream.tail->data;
179
180 return NULL;
181 }
182
183 clientStreamNode *
184 ClientSocketContext::getClientReplyContext() const
185 {
186 return (clientStreamNode *)http->client_stream.tail->prev->data;
187 }
188
189 /*
190 * This routine should be called to grow the inbuf and then
191 * call comm_read().
192 */
193 void
194 ConnStateData::readSomeData()
195 {
196 if (reading())
197 return;
198
199 reading(true);
200
201 debugs(33, 4, "clientReadSomeData: FD " << fd << ": reading request...");
202
203 makeSpaceAvailable();
204
205 /* Make sure we are not still reading from the client side! */
206 /* XXX this could take a bit of CPU time! aiee! -- adrian */
207 assert(!comm_has_pending_read(fd));
208
209 comm_read(fd, in.addressToReadInto(), getAvailableBufferLength(), clientReadRequest, this);
210 }
211
212
213 void
214 ClientSocketContext::removeFromConnectionList(ConnStateData::Pointer conn)
215 {
216 ClientSocketContext::Pointer *tempContextPointer;
217 assert(conn != NULL);
218 assert(conn->getCurrentContext() != NULL);
219 /* Unlink us from the connection request list */
220 tempContextPointer = & conn->currentobject;
221
222 while (tempContextPointer->getRaw()) {
223 if (*tempContextPointer == this)
224 break;
225
226 tempContextPointer = &(*tempContextPointer)->next;
227 }
228
229 assert(tempContextPointer->getRaw() != NULL);
230 *tempContextPointer = next;
231 next = NULL;
232 }
233
234 ClientSocketContext::~ClientSocketContext()
235 {
236 clientStreamNode *node = getTail();
237
238 if (node) {
239 ClientSocketContext *streamContext = dynamic_cast<ClientSocketContext *> (node->data.getRaw());
240
241 if (streamContext) {
242 /* We are *always* the tail - prevent recursive free */
243 assert(this == streamContext);
244 node->data = NULL;
245 }
246 }
247
248 if (connRegistered_)
249 deRegisterWithConn();
250
251 httpRequestFree(http);
252
253 /* clean up connection links to us */
254 assert(this != next.getRaw());
255 }
256
257 void
258 ClientSocketContext::registerWithConn()
259 {
260 assert (!connRegistered_);
261 assert (http);
262 assert (http->getConn() != NULL);
263 connRegistered_ = true;
264 http->getConn()->addContextToQueue(this);
265 }
266
267 void
268 ClientSocketContext::deRegisterWithConn()
269 {
270 assert (connRegistered_);
271 removeFromConnectionList(http->getConn());
272 connRegistered_ = false;
273 }
274
275 void
276 ClientSocketContext::connIsFinished()
277 {
278 assert (http);
279 assert (http->getConn() != NULL);
280 deRegisterWithConn();
281 /* we can't handle any more stream data - detach */
282 clientStreamDetach(getTail(), http);
283 }
284
285 ClientSocketContext::ClientSocketContext() : http(NULL), reply(NULL), next(NULL),
286 writtenToSocket(0),
287 mayUseConnection_ (false),
288 connRegistered_ (false)
289 {
290 memset (reqbuf, '\0', sizeof (reqbuf));
291 flags.deferred = 0;
292 flags.parsed_ok = 0;
293 deferredparams.node = NULL;
294 deferredparams.rep = NULL;
295 }
296
297 ClientSocketContext *
298 ClientSocketContextNew(ClientHttpRequest * http)
299 {
300 ClientSocketContext *newContext;
301 assert(http != NULL);
302 newContext = new ClientSocketContext;
303 newContext->http = http;
304 return newContext;
305 }
306
307 #if USE_IDENT
308 static void
309 clientIdentDone(const char *ident, void *data)
310 {
311 ConnStateData *conn = (ConnStateData *)data;
312 xstrncpy(conn->rfc931, ident ? ident : dash_str, USER_IDENT_SZ);
313 }
314
315 #endif
316
317 void
318 clientUpdateStatCounters(log_type logType)
319 {
320 statCounter.client_http.requests++;
321
322 if (logTypeIsATcpHit(logType))
323 statCounter.client_http.hits++;
324
325 if (logType == LOG_TCP_HIT)
326 statCounter.client_http.disk_hits++;
327 else if (logType == LOG_TCP_MEM_HIT)
328 statCounter.client_http.mem_hits++;
329 }
330
331 void
332 clientUpdateStatHistCounters(log_type logType, int svc_time)
333 {
334 statHistCount(&statCounter.client_http.all_svc_time, svc_time);
335 /*
336 * The idea here is not to be complete, but to get service times
337 * for only well-defined types. For example, we don't include
338 * LOG_TCP_REFRESH_FAIL because its not really a cache hit
339 * (we *tried* to validate it, but failed).
340 */
341
342 switch (logType) {
343
344 case LOG_TCP_REFRESH_UNMODIFIED:
345 statHistCount(&statCounter.client_http.nh_svc_time, svc_time);
346 break;
347
348 case LOG_TCP_IMS_HIT:
349 statHistCount(&statCounter.client_http.nm_svc_time, svc_time);
350 break;
351
352 case LOG_TCP_HIT:
353
354 case LOG_TCP_MEM_HIT:
355
356 case LOG_TCP_OFFLINE_HIT:
357 statHistCount(&statCounter.client_http.hit_svc_time, svc_time);
358 break;
359
360 case LOG_TCP_MISS:
361
362 case LOG_TCP_CLIENT_REFRESH_MISS:
363 statHistCount(&statCounter.client_http.miss_svc_time, svc_time);
364 break;
365
366 default:
367 /* make compiler warnings go away */
368 break;
369 }
370 }
371
372 bool
373 clientPingHasFinished(ping_data const *aPing)
374 {
375 if (0 != aPing->stop.tv_sec && 0 != aPing->start.tv_sec)
376 return true;
377
378 return false;
379 }
380
381 void
382 clientUpdateHierCounters(HierarchyLogEntry * someEntry)
383 {
384 ping_data *i;
385
386 switch (someEntry->code) {
387 #if USE_CACHE_DIGESTS
388
389 case CD_PARENT_HIT:
390
391 case CD_SIBLING_HIT:
392 statCounter.cd.times_used++;
393 break;
394 #endif
395
396 case SIBLING_HIT:
397
398 case PARENT_HIT:
399
400 case FIRST_PARENT_MISS:
401
402 case CLOSEST_PARENT_MISS:
403 statCounter.icp.times_used++;
404 i = &someEntry->ping;
405
406 if (clientPingHasFinished(i))
407 statHistCount(&statCounter.icp.query_svc_time,
408 tvSubUsec(i->start, i->stop));
409
410 if (i->timeout)
411 statCounter.icp.query_timeouts++;
412
413 break;
414
415 case CLOSEST_PARENT:
416
417 case CLOSEST_DIRECT:
418 statCounter.netdb.times_used++;
419
420 break;
421
422 default:
423 break;
424 }
425 }
426
427 void
428 ClientHttpRequest::updateCounters()
429 {
430 clientUpdateStatCounters(logType);
431
432 if (request->errType != ERR_NONE)
433 statCounter.client_http.errors++;
434
435 clientUpdateStatHistCounters(logType,
436 tvSubMsec(start, current_time));
437
438 clientUpdateHierCounters(&request->hier);
439 }
440
441 void
442 clientPrepareLogWithRequestDetails(HttpRequest * request, AccessLogEntry * aLogEntry)
443 {
444 assert(request);
445 assert(aLogEntry);
446
447 if (Config.onoff.log_mime_hdrs) {
448 Packer p;
449 MemBuf mb;
450 mb.init();
451 packerToMemInit(&p, &mb);
452 request->header.packInto(&p);
453 aLogEntry->headers.request = xstrdup(mb.buf);
454 packerClean(&p);
455 mb.clean();
456 }
457
458 aLogEntry->http.method = request->method;
459 aLogEntry->http.version = request->http_ver;
460 aLogEntry->hier = request->hier;
461
462 aLogEntry->cache.extuser = request->extacl_user.buf();
463
464 if (request->auth_user_request) {
465
466 if (request->auth_user_request->username())
467 aLogEntry->cache.authuser =
468 xstrdup(request->auth_user_request->username());
469
470 AUTHUSERREQUESTUNLOCK(request->auth_user_request, "request via clientPrepareLogWithRequestDetails");
471 }
472 }
473
474 void
475 ClientHttpRequest::logRequest()
476 {
477 if (out.size || logType) {
478 al.icp.opcode = ICP_INVALID;
479 al.url = log_uri;
480 debugs(33, 9, "clientLogRequest: al.url='" << al.url << "'");
481
482 if (al.reply) {
483 al.http.code = al.reply->sline.status;
484 al.http.content_type = al.reply->content_type.buf();
485 } else if (loggingEntry() && loggingEntry()->mem_obj) {
486 al.http.code = loggingEntry()->mem_obj->getReply()->sline.status;
487 al.http.content_type = loggingEntry()->mem_obj->getReply()->content_type.buf();
488 }
489
490 debugs(33, 9, "clientLogRequest: http.code='" << al.http.code << "'");
491
492 if (loggingEntry() && loggingEntry()->mem_obj)
493 al.cache.objectSize = loggingEntry()->contentLen();
494
495 al.cache.caddr.SetNoAddr();
496
497 if(getConn() != NULL) al.cache.caddr = getConn()->log_addr;
498
499 al.cache.size = out.size;
500
501 al.cache.highOffset = out.offset;
502
503 al.cache.code = logType;
504
505 al.cache.msec = tvSubMsec(start, current_time);
506
507 if (request)
508 clientPrepareLogWithRequestDetails(request, &al);
509
510 if (getConn() != NULL && getConn()->rfc931[0])
511 al.cache.rfc931 = getConn()->rfc931;
512
513 #if USE_SSL && 0
514
515 /* This is broken. Fails if the connection has been closed. Needs
516 * to snarf the ssl details some place earlier..
517 */
518 if (getConn() != NULL)
519 al.cache.ssluser = sslGetUserEmail(fd_table[getConn()->fd].ssl);
520
521 #endif
522
523 ACLChecklist *checklist = clientAclChecklistCreate(Config.accessList.log, this);
524
525 if (al.reply)
526 checklist->reply = HTTPMSGLOCK(al.reply);
527
528 if (!Config.accessList.log || checklist->fastCheck()) {
529 al.request = HTTPMSGLOCK(request);
530 accessLogLog(&al, checklist);
531 updateCounters();
532
533 if (getConn() != NULL)
534 clientdbUpdate(getConn()->peer, logType, PROTO_HTTP, out.size);
535 }
536
537 delete checklist;
538
539 accessLogFreeMemory(&al);
540 }
541 }
542
543 void
544 ClientHttpRequest::freeResources()
545 {
546 safe_free(uri);
547 safe_free(log_uri);
548 safe_free(redirect.location);
549 range_iter.boundary.clean();
550 HTTPMSGUNLOCK(request);
551
552 if (client_stream.tail)
553 clientStreamAbort((clientStreamNode *)client_stream.tail->data, this);
554 }
555
556 void
557 httpRequestFree(void *data)
558 {
559 ClientHttpRequest *http = (ClientHttpRequest *)data;
560 assert(http != NULL);
561 delete http;
562 }
563
564 bool
565 ConnStateData::areAllContextsForThisConnection() const
566 {
567 assert(this != NULL);
568 ClientSocketContext::Pointer context = getCurrentContext();
569
570 while (context.getRaw()) {
571 if (context->http->getConn() != this)
572 return false;
573
574 context = context->next;
575 }
576
577 return true;
578 }
579
580 void
581 ConnStateData::freeAllContexts()
582 {
583 ClientSocketContext::Pointer context;
584
585 while ((context = getCurrentContext()).getRaw() != NULL) {
586 assert(getCurrentContext() !=
587 getCurrentContext()->next);
588 context->connIsFinished();
589 assert (context != currentobject);
590 }
591 }
592
593 /* This is a handler normally called by comm_close() */
594 static void
595 connStateClosed(int fd, void *data)
596 {
597 ConnStateData *connState = (ConnStateData *)data;
598 assert (fd == connState->fd);
599 connState->close();
600 }
601
602 void
603 ConnStateData::close()
604 {
605 debugs(33, 3, "ConnStateData::close: FD " << fd);
606 openReference = NULL;
607 fd = -1;
608 flags.readMoreRequests = false;
609 clientdbEstablished(peer, -1); /* decrement */
610 assert(areAllContextsForThisConnection());
611 freeAllContexts();
612
613 if (auth_user_request != NULL) {
614 debugs(33, 4, "ConnStateData::close: freeing auth_user_request '" << auth_user_request << "' (this is '" << this << "')");
615 auth_user_request->onConnectionClose(this);
616 }
617 }
618
619 bool
620 ConnStateData::isOpen() const
621 {
622 return openReference != NULL;
623 }
624
625 ConnStateData::~ConnStateData()
626 {
627 assert(this != NULL);
628 debugs(33, 3, "ConnStateData::~ConnStateData: FD " << fd);
629
630 if (isOpen())
631 close();
632
633 AUTHUSERREQUESTUNLOCK(auth_user_request, "~conn");
634
635 cbdataReferenceDone(port);
636
637 if (bodyPipe != NULL) {
638 bodyPipe->clearProducer(false);
639 bodyPipe = NULL; // refcounted
640 }
641 }
642
643 /*
644 * clientSetKeepaliveFlag() sets request->flags.proxy_keepalive.
645 * This is the client-side persistent connection flag. We need
646 * to set this relatively early in the request processing
647 * to handle hacks for broken servers and clients.
648 */
649 static void
650 clientSetKeepaliveFlag(ClientHttpRequest * http)
651 {
652 HttpRequest *request = http->request;
653 const HttpHeader *req_hdr = &request->header;
654
655 debugs(33, 3, "clientSetKeepaliveFlag: http_ver = " <<
656 request->http_ver.major << "." << request->http_ver.minor);
657 debugs(33, 3, "clientSetKeepaliveFlag: method = " <<
658 RequestMethodStr[request->method]);
659
660 HttpVersion http_ver(1,0);
661 /* we are HTTP/1.0, no matter what the client requests... */
662
663 if (httpMsgIsPersistent(http_ver, req_hdr))
664 request->flags.proxy_keepalive = 1;
665 }
666
667 static int
668 clientIsContentLengthValid(HttpRequest * r)
669 {
670 switch (r->method) {
671
672 case METHOD_PUT:
673
674 case METHOD_POST:
675 /* PUT/POST requires a request entity */
676 return (r->content_length >= 0);
677
678 case METHOD_GET:
679
680 case METHOD_HEAD:
681 /* We do not want to see a request entity on GET/HEAD requests */
682 return (r->content_length <= 0 || Config.onoff.request_entities);
683
684 default:
685 /* For other types of requests we don't care */
686 return 1;
687 }
688
689 /* NOT REACHED */
690 }
691
692 int
693 clientIsRequestBodyValid(int64_t bodyLength)
694 {
695 if (bodyLength >= 0)
696 return 1;
697
698 return 0;
699 }
700
701 int
702 clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength)
703 {
704 if (Config.maxRequestBodySize &&
705 bodyLength > Config.maxRequestBodySize)
706 return 1; /* too large */
707
708 return 0;
709 }
710
711 #ifndef PURIFY
712 int
713 connIsUsable(ConnStateData::Pointer conn)
714 {
715 if (conn == NULL || conn->fd == -1)
716 return 0;
717
718 return 1;
719 }
720
721 #endif
722
723 ClientSocketContext::Pointer
724 ConnStateData::getCurrentContext() const
725 {
726 assert(this);
727 return currentobject;
728 }
729
730 void
731 ClientSocketContext::deferRecipientForLater(clientStreamNode * node, HttpReply * rep, StoreIOBuffer receivedData)
732 {
733 debugs(33, 2, "clientSocketRecipient: Deferring request " << http->uri);
734 assert(flags.deferred == 0);
735 flags.deferred = 1;
736 deferredparams.node = node;
737 deferredparams.rep = rep;
738 deferredparams.queuedBuffer = receivedData;
739 return;
740 }
741
742 int
743 responseFinishedOrFailed(HttpReply * rep, StoreIOBuffer const & receivedData)
744 {
745 if (rep == NULL && receivedData.data == NULL && receivedData.length == 0)
746 return 1;
747
748 return 0;
749 }
750
751 bool
752 ClientSocketContext::startOfOutput() const
753 {
754 return http->out.size == 0;
755 }
756
757 size_t
758 ClientSocketContext::lengthToSend(Range<int64_t> const &available)
759 {
760 /*the size of available range can always fit in a size_t type*/
761 size_t maximum = (size_t)available.size();
762
763 if (!http->request->range)
764 return maximum;
765
766 assert (canPackMoreRanges());
767
768 if (http->range_iter.debt() == -1)
769 return maximum;
770
771 assert (http->range_iter.debt() > 0);
772
773 /* TODO this + the last line could be a range intersection calculation */
774 if (available.start < http->range_iter.currentSpec()->offset)
775 return 0;
776
777 return XMIN(http->range_iter.debt(), (int64_t)maximum);
778 }
779
780 void
781 ClientSocketContext::noteSentBodyBytes(size_t bytes)
782 {
783 http->out.offset += bytes;
784
785 if (!http->request->range)
786 return;
787
788 if (http->range_iter.debt() != -1) {
789 http->range_iter.debt(http->range_iter.debt() - bytes);
790 assert (http->range_iter.debt() >= 0);
791 }
792
793 /* debt() always stops at -1, below that is a bug */
794 assert (http->range_iter.debt() >= -1);
795 }
796
797 bool
798 ClientHttpRequest::multipartRangeRequest() const
799 {
800 return request->multipartRangeRequest();
801 }
802
803 bool
804 ClientSocketContext::multipartRangeRequest() const
805 {
806 return http->multipartRangeRequest();
807 }
808
809 void
810 ClientSocketContext::sendBody(HttpReply * rep, StoreIOBuffer bodyData)
811 {
812 assert(rep == NULL);
813
814 if (!multipartRangeRequest()) {
815 size_t length = lengthToSend(bodyData.range());
816 noteSentBodyBytes (length);
817 comm_write(fd(), bodyData.data, length, clientWriteBodyComplete, this, NULL);
818 return;
819 }
820
821 MemBuf mb;
822 mb.init();
823 packRange(bodyData, &mb);
824
825 if (mb.contentSize())
826 /* write */
827 comm_write_mbuf(fd(), &mb, clientWriteComplete, this);
828 else
829 writeComplete(fd(), NULL, 0, COMM_OK);
830 }
831
832 /* put terminating boundary for multiparts */
833 static void
834 clientPackTermBound(String boundary, MemBuf * mb)
835 {
836 mb->Printf("\r\n--%s--\r\n", boundary.buf());
837 debugs(33, 6, "clientPackTermBound: buf offset: " << mb->size);
838 }
839
840 /* appends a "part" HTTP header (as in a multi-part/range reply) to the buffer */
841 static void
842 clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb)
843 {
844 HttpHeader hdr(hoReply);
845 Packer p;
846 assert(rep);
847 assert(spec);
848
849 /* put boundary */
850 debugs(33, 5, "clientPackRangeHdr: appending boundary: " <<
851 boundary.buf());
852 /* rfc2046 requires to _prepend_ boundary with <crlf>! */
853 mb->Printf("\r\n--%s\r\n", boundary.buf());
854
855 /* stuff the header with required entries and pack it */
856
857 if (rep->header.has(HDR_CONTENT_TYPE))
858 hdr.putStr(HDR_CONTENT_TYPE, rep->header.getStr(HDR_CONTENT_TYPE));
859
860 httpHeaderAddContRange(&hdr, *spec, rep->content_length);
861
862 packerToMemInit(&p, mb);
863
864 hdr.packInto(&p);
865
866 packerClean(&p);
867
868 hdr.clean();
869
870 /* append <crlf> (we packed a header, not a reply) */
871 mb->Printf("\r\n");
872 }
873
874 /*
875 * extracts a "range" from *buf and appends them to mb, updating
876 * all offsets and such.
877 */
878 void
879 ClientSocketContext::packRange(StoreIOBuffer const &source, MemBuf * mb)
880 {
881 HttpHdrRangeIter * i = &http->range_iter;
882 Range<int64_t> available (source.range());
883 char const *buf = source.data;
884
885 while (i->currentSpec() && available.size()) {
886 const size_t copy_sz = lengthToSend(available);
887
888 if (copy_sz) {
889 /*
890 * intersection of "have" and "need" ranges must not be empty
891 */
892 assert(http->out.offset < i->currentSpec()->offset + i->currentSpec()->length);
893 assert(http->out.offset + available.size() > i->currentSpec()->offset);
894
895 /*
896 * put boundary and headers at the beginning of a range in a
897 * multi-range
898 */
899
900 if (http->multipartRangeRequest() && i->debt() == i->currentSpec()->length) {
901 assert(http->memObject());
902 clientPackRangeHdr(
903 http->memObject()->getReply(), /* original reply */
904 i->currentSpec(), /* current range */
905 i->boundary, /* boundary, the same for all */
906 mb);
907 }
908
909 /*
910 * append content
911 */
912 debugs(33, 3, "clientPackRange: appending " << copy_sz << " bytes");
913
914 noteSentBodyBytes (copy_sz);
915
916 mb->append(buf, copy_sz);
917
918 /*
919 * update offsets
920 */
921 available.start += copy_sz;
922
923 buf += copy_sz;
924
925 }
926
927 /*
928 * paranoid check
929 */
930 assert((available.size() >= 0 && i->debt() >= 0) || i->debt() == -1);
931
932 if (!canPackMoreRanges()) {
933 debugs(33, 3, "clientPackRange: Returning because !canPackMoreRanges.");
934
935 if (i->debt() == 0)
936 /* put terminating boundary for multiparts */
937 clientPackTermBound(i->boundary, mb);
938
939 return;
940 }
941
942 int64_t next = getNextRangeOffset();
943
944 assert (next >= http->out.offset);
945
946 int64_t skip = next - http->out.offset;
947
948 /* adjust for not to be transmitted bytes */
949 http->out.offset = next;
950
951 if (available.size() <= skip)
952 return;
953
954 available.start += skip;
955
956 buf += skip;
957
958 if (copy_sz == 0)
959 return;
960 }
961 }
962
963 /* returns expected content length for multi-range replies
964 * note: assumes that httpHdrRangeCanonize has already been called
965 * warning: assumes that HTTP headers for individual ranges at the
966 * time of the actuall assembly will be exactly the same as
967 * the headers when clientMRangeCLen() is called */
968 int
969 ClientHttpRequest::mRangeCLen()
970 {
971 int64_t clen = 0;
972 MemBuf mb;
973
974 assert(memObject());
975
976 mb.init();
977 HttpHdrRange::iterator pos = request->range->begin();
978
979 while (pos != request->range->end()) {
980 /* account for headers for this range */
981 mb.reset();
982 clientPackRangeHdr(memObject()->getReply(),
983 *pos, range_iter.boundary, &mb);
984 clen += mb.size;
985
986 /* account for range content */
987 clen += (*pos)->length;
988
989 debugs(33, 6, "clientMRangeCLen: (clen += " << mb.size << " + " << (*pos)->length << ") == " << clen);
990 ++pos;
991 }
992
993 /* account for the terminating boundary */
994 mb.reset();
995
996 clientPackTermBound(range_iter.boundary, &mb);
997
998 clen += mb.size;
999
1000 mb.clean();
1001
1002 return clen;
1003 }
1004
1005 /*
1006 * returns true if If-Range specs match reply, false otherwise
1007 */
1008 static int
1009 clientIfRangeMatch(ClientHttpRequest * http, HttpReply * rep)
1010 {
1011 const TimeOrTag spec = http->request->header.getTimeOrTag(HDR_IF_RANGE);
1012 /* check for parsing falure */
1013
1014 if (!spec.valid)
1015 return 0;
1016
1017 /* got an ETag? */
1018 if (spec.tag.str) {
1019 ETag rep_tag = rep->header.getETag(HDR_ETAG);
1020 debugs(33, 3, "clientIfRangeMatch: ETags: " << spec.tag.str << " and " <<
1021 (rep_tag.str ? rep_tag.str : "<none>"));
1022
1023 if (!rep_tag.str)
1024 return 0; /* entity has no etag to compare with! */
1025
1026 if (spec.tag.weak || rep_tag.weak) {
1027 debugs(33, 1, "clientIfRangeMatch: Weak ETags are not allowed in If-Range: " << spec.tag.str << " ? " << rep_tag.str);
1028 return 0; /* must use strong validator for sub-range requests */
1029 }
1030
1031 return etagIsEqual(&rep_tag, &spec.tag);
1032 }
1033
1034 /* got modification time? */
1035 if (spec.time >= 0) {
1036 return http->storeEntry()->lastmod <= spec.time;
1037 }
1038
1039 assert(0); /* should not happen */
1040 return 0;
1041 }
1042
1043 /* generates a "unique" boundary string for multipart responses
1044 * the caller is responsible for cleaning the string */
1045 String
1046 ClientHttpRequest::rangeBoundaryStr() const
1047 {
1048 assert(this);
1049 const char *key;
1050 String b (full_appname_string);
1051 b.append (":",1);
1052 key = storeEntry()->getMD5Text();
1053 b.append(key, strlen(key));
1054 return b;
1055 }
1056
1057 /* adds appropriate Range headers if needed */
1058 void
1059 ClientSocketContext::buildRangeHeader(HttpReply * rep)
1060 {
1061 HttpHeader *hdr = rep ? &rep->header : 0;
1062 const char *range_err = NULL;
1063 HttpRequest *request = http->request;
1064 assert(request->range);
1065 /* check if we still want to do ranges */
1066
1067 if (!rep)
1068 range_err = "no [parse-able] reply";
1069 else if ((rep->sline.status != HTTP_OK) && (rep->sline.status != HTTP_PARTIAL_CONTENT))
1070 range_err = "wrong status code";
1071 else if (hdr->has(HDR_CONTENT_RANGE))
1072 range_err = "origin server does ranges";
1073 else if (rep->content_length < 0)
1074 range_err = "unknown length";
1075 else if (rep->content_length != http->memObject()->getReply()->content_length)
1076 range_err = "INCONSISTENT length"; /* a bug? */
1077
1078 /* hits only - upstream peer determines correct behaviour on misses, and client_side_reply determines
1079 * hits candidates
1080 */
1081 else if (logTypeIsATcpHit(http->logType) && http->request->header.has(HDR_IF_RANGE) && !clientIfRangeMatch(http, rep))
1082 range_err = "If-Range match failed";
1083 else if (!http->request->range->canonize(rep))
1084 range_err = "canonization failed";
1085 else if (http->request->range->isComplex())
1086 range_err = "too complex range header";
1087 else if (!logTypeIsATcpHit(http->logType) && http->request->range->offsetLimitExceeded())
1088 range_err = "range outside range_offset_limit";
1089
1090 /* get rid of our range specs on error */
1091 if (range_err) {
1092 /* XXX We do this here because we need canonisation etc. However, this current
1093 * code will lead to incorrect store offset requests - the store will have the
1094 * offset data, but we won't be requesting it.
1095 * So, we can either re-request, or generate an error
1096 */
1097 debugs(33, 3, "clientBuildRangeHeader: will not do ranges: " << range_err << ".");
1098 delete http->request->range;
1099 http->request->range = NULL;
1100 } else {
1101 /* XXX: TODO: Review, this unconditional set may be wrong. - TODO: review. */
1102 httpStatusLineSet(&rep->sline, rep->sline.version,
1103 HTTP_PARTIAL_CONTENT, NULL);
1104 // web server responded with a valid, but unexpected range.
1105 // will (try-to) forward as-is.
1106 //TODO: we should cope with multirange request/responses
1107 bool replyMatchRequest = rep->content_range != NULL ?
1108 request->range->contains(rep->content_range->spec) :
1109 true;
1110 const int spec_count = http->request->range->specs.count;
1111 int64_t actual_clen = -1;
1112
1113 debugs(33, 3, "clientBuildRangeHeader: range spec count: " <<
1114 spec_count << " virgin clen: " << rep->content_length);
1115 assert(spec_count > 0);
1116 /* ETags should not be returned with Partial Content replies? */
1117 hdr->delById(HDR_ETAG);
1118 /* append appropriate header(s) */
1119
1120 if (spec_count == 1) {
1121 if (!replyMatchRequest) {
1122 hdr->delById(HDR_CONTENT_RANGE);
1123 hdr->putContRange(rep->content_range);
1124 actual_clen = rep->content_length;
1125 //http->range_iter.pos = rep->content_range->spec.begin();
1126 (*http->range_iter.pos)->offset = rep->content_range->spec.offset;
1127 (*http->range_iter.pos)->length = rep->content_range->spec.length;
1128
1129 } else {
1130 HttpHdrRange::iterator pos = http->request->range->begin();
1131 assert(*pos);
1132 /* append Content-Range */
1133
1134 if (!hdr->has(HDR_CONTENT_RANGE)) {
1135 /* No content range, so this was a full object we are
1136 * sending parts of.
1137 */
1138 httpHeaderAddContRange(hdr, **pos, rep->content_length);
1139 }
1140
1141 /* set new Content-Length to the actual number of bytes
1142 * transmitted in the message-body */
1143 actual_clen = (*pos)->length;
1144 }
1145 } else {
1146 /* multipart! */
1147 /* generate boundary string */
1148 http->range_iter.boundary = http->rangeBoundaryStr();
1149 /* delete old Content-Type, add ours */
1150 hdr->delById(HDR_CONTENT_TYPE);
1151 httpHeaderPutStrf(hdr, HDR_CONTENT_TYPE,
1152 "multipart/byteranges; boundary=\"%s\"",
1153 http->range_iter.boundary.buf());
1154 /* Content-Length is not required in multipart responses
1155 * but it is always nice to have one */
1156 actual_clen = http->mRangeCLen();
1157 /* http->out needs to start where we want data at */
1158 http->out.offset = http->range_iter.currentSpec()->offset;
1159 }
1160
1161 /* replace Content-Length header */
1162 assert(actual_clen >= 0);
1163
1164 hdr->delById(HDR_CONTENT_LENGTH);
1165
1166 hdr->putInt64(HDR_CONTENT_LENGTH, actual_clen);
1167
1168 debugs(33, 3, "clientBuildRangeHeader: actual content length: " << actual_clen);
1169
1170 /* And start the range iter off */
1171 http->range_iter.updateSpec();
1172 }
1173 }
1174
1175 void
1176 ClientSocketContext::prepareReply(HttpReply * rep)
1177 {
1178 reply = rep;
1179
1180 if (http->request->range)
1181 buildRangeHeader(rep);
1182 }
1183
1184 void
1185 ClientSocketContext::sendStartOfMessage(HttpReply * rep, StoreIOBuffer bodyData)
1186 {
1187 prepareReply(rep);
1188 assert (rep);
1189 MemBuf *mb = rep->pack();
1190 /* Save length of headers for persistent conn checks */
1191 http->out.headers_sz = mb->contentSize();
1192 #if HEADERS_LOG
1193
1194 headersLog(0, 0, http->request->method, rep);
1195 #endif
1196
1197 if (bodyData.data && bodyData.length) {
1198 if (!multipartRangeRequest()) {
1199 size_t length = lengthToSend(bodyData.range());
1200 noteSentBodyBytes (length);
1201
1202 mb->append(bodyData.data, length);
1203 } else {
1204 packRange(bodyData, mb);
1205 }
1206 }
1207
1208 /* write */
1209 debugs(33,7, HERE << "sendStartOfMessage schedules clientWriteComplete");
1210 comm_write_mbuf(fd(), mb, clientWriteComplete, this);
1211
1212 delete mb;
1213 }
1214
1215 /*
1216 * Write a chunk of data to a client socket. If the reply is present,
1217 * send the reply headers down the wire too, and clean them up when
1218 * finished.
1219 * Pre-condition:
1220 * The request is one backed by a connection, not an internal request.
1221 * data context is not NULL
1222 * There are no more entries in the stream chain.
1223 */
1224 static void
1225 clientSocketRecipient(clientStreamNode * node, ClientHttpRequest * http,
1226 HttpReply * rep, StoreIOBuffer receivedData)
1227 {
1228 int fd;
1229 /* Test preconditions */
1230 assert(node != NULL);
1231 PROF_start(clientSocketRecipient);
1232 /* TODO: handle this rather than asserting
1233 * - it should only ever happen if we cause an abort and
1234 * the callback chain loops back to here, so we can simply return.
1235 * However, that itself shouldn't happen, so it stays as an assert for now.
1236 */
1237 assert(cbdataReferenceValid(node));
1238 assert(node->node.next == NULL);
1239 ClientSocketContext::Pointer context = dynamic_cast<ClientSocketContext *>(node->data.getRaw());
1240 assert(context != NULL);
1241 assert(connIsUsable(http->getConn()));
1242 fd = http->getConn()->fd;
1243 /* TODO: check offset is what we asked for */
1244
1245 if (context != http->getConn()->getCurrentContext()) {
1246 context->deferRecipientForLater(node, rep, receivedData);
1247 PROF_stop(clientSocketRecipient);
1248 return;
1249 }
1250
1251 if (responseFinishedOrFailed(rep, receivedData)) {
1252 context->writeComplete(fd, NULL, 0, COMM_OK);
1253 PROF_stop(clientSocketRecipient);
1254 return;
1255 }
1256
1257 if (!context->startOfOutput())
1258 context->sendBody(rep, receivedData);
1259 else {
1260 assert(rep);
1261 http->al.reply = HTTPMSGLOCK(rep);
1262 context->sendStartOfMessage(rep, receivedData);
1263 }
1264
1265 PROF_stop(clientSocketRecipient);
1266 }
1267
1268 /* Called when a downstream node is no longer interested in
1269 * our data. As we are a terminal node, this means on aborts
1270 * only
1271 */
1272 void
1273 clientSocketDetach(clientStreamNode * node, ClientHttpRequest * http)
1274 {
1275 /* Test preconditions */
1276 assert(node != NULL);
1277 /* TODO: handle this rather than asserting
1278 * - it should only ever happen if we cause an abort and
1279 * the callback chain loops back to here, so we can simply return.
1280 * However, that itself shouldn't happen, so it stays as an assert for now.
1281 */
1282 assert(cbdataReferenceValid(node));
1283 /* Set null by ContextFree */
1284 assert(node->node.next == NULL);
1285 /* this is the assert discussed above */
1286 assert(NULL == dynamic_cast<ClientSocketContext *>(node->data.getRaw()));
1287 /* We are only called when the client socket shutsdown.
1288 * Tell the prev pipeline member we're finished
1289 */
1290 clientStreamDetach(node, http);
1291 }
1292
1293 static void
1294 clientWriteBodyComplete(int fd, char *buf, size_t size, comm_err_t errflag, int xerrno, void *data)
1295 {
1296 debugs(33,7, HERE << "clientWriteBodyComplete schedules clientWriteComplete");
1297 clientWriteComplete(fd, NULL, size, errflag, xerrno, data);
1298 }
1299
1300 void
1301 ConnStateData::readNextRequest()
1302 {
1303 debugs(33, 5, "ConnStateData::readNextRequest: FD " << fd << " reading next req");
1304
1305 fd_note(fd, "Waiting for next request");
1306 /*
1307 * Set the timeout BEFORE calling clientReadRequest().
1308 */
1309 commSetTimeout(fd, Config.Timeout.persistent_request,
1310 requestTimeout, this);
1311 readSomeData();
1312 /* Please don't do anything with the FD past here! */
1313 }
1314
1315 void
1316 ClientSocketContextPushDeferredIfNeeded(ClientSocketContext::Pointer deferredRequest, ConnStateData::Pointer & conn)
1317 {
1318 debugs(33, 2, "ClientSocketContextPushDeferredIfNeeded: FD " << conn->fd << " Sending next");
1319
1320 /* If the client stream is waiting on a socket write to occur, then */
1321
1322 if (deferredRequest->flags.deferred) {
1323 /* NO data is allowed to have been sent */
1324 assert(deferredRequest->http->out.size == 0);
1325 clientSocketRecipient(deferredRequest->deferredparams.node,
1326 deferredRequest->http,
1327 deferredRequest->deferredparams.rep,
1328 deferredRequest->deferredparams.queuedBuffer);
1329 }
1330
1331 /* otherwise, the request is still active in a callbacksomewhere,
1332 * and we are done
1333 */
1334 }
1335
1336 void
1337 ClientSocketContext::keepaliveNextRequest()
1338 {
1339 ConnStateData::Pointer conn = http->getConn();
1340 bool do_next_read = false;
1341
1342 debugs(33, 3, "ClientSocketContext::keepaliveNextRequest: FD " << conn->fd);
1343 connIsFinished();
1344
1345 /*
1346 * Attempt to parse a request from the request buffer.
1347 * If we've been fed a pipelined request it may already
1348 * be in our read buffer.
1349 *
1350 * This needs to fall through - if we're unlucky and parse the _last_ request
1351 * from our read buffer we may never re-register for another client read.
1352 */
1353
1354 if (clientParseRequest(conn, do_next_read)) {
1355 debugs(33, 3, "clientSocketContext::keepaliveNextRequest: FD " << conn->fd << ": parsed next request from buffer");
1356 }
1357
1358 /*
1359 * Either we need to kick-start another read or, if we have
1360 * a half-closed connection, kill it after the last request.
1361 * This saves waiting for half-closed connections to finished being
1362 * half-closed _AND_ then, sometimes, spending "Timeout" time in
1363 * the keepalive "Waiting for next request" state.
1364 */
1365 if (commIsHalfClosed(conn->fd) && (conn->getConcurrentRequestCount() == 0)) {
1366 debugs(33, 3, "ClientSocketContext::keepaliveNextRequest: half-closed client with no pending requests, closing");
1367 comm_close(conn->fd);
1368 return;
1369 }
1370
1371 ClientSocketContext::Pointer deferredRequest;
1372
1373 /*
1374 * At this point we either have a parsed request (which we've
1375 * kicked off the processing for) or not. If we have a deferred
1376 * request (parsed but deferred for pipeling processing reasons)
1377 * then look at processing it. If not, simply kickstart
1378 * another read.
1379 */
1380
1381 if ((deferredRequest = conn->getCurrentContext()).getRaw()) {
1382 debugs(33, 3, "ClientSocketContext:: FD " << conn->fd << ": calling PushDeferredIfNeeded");
1383 ClientSocketContextPushDeferredIfNeeded(deferredRequest, conn);
1384 } else {
1385 debugs(33, 3, "ClientSocketContext:: FD " << conn->fd << ": calling conn->readNextRequest()");
1386 conn->readNextRequest();
1387 }
1388 }
1389
1390 void
1391 clientUpdateSocketStats(log_type logType, size_t size)
1392 {
1393 if (size == 0)
1394 return;
1395
1396 kb_incr(&statCounter.client_http.kbytes_out, size);
1397
1398 if (logTypeIsATcpHit(logType))
1399 kb_incr(&statCounter.client_http.hit_kbytes_out, size);
1400 }
1401
1402 /* returns true if there is still data available to pack more ranges
1403 * increments iterator "i"
1404 * used by clientPackMoreRanges */
1405 bool
1406 ClientSocketContext::canPackMoreRanges() const
1407 {
1408 /* first update "i" if needed */
1409
1410 if (!http->range_iter.debt()) {
1411 debugs(33, 5, "ClientSocketContext::canPackMoreRanges: At end of current range spec for FD " <<
1412 fd());
1413
1414 if (http->range_iter.pos.incrementable())
1415 ++http->range_iter.pos;
1416
1417 http->range_iter.updateSpec();
1418 }
1419
1420 assert(!http->range_iter.debt() == !http->range_iter.currentSpec());
1421 /* paranoid sync condition */
1422 /* continue condition: need_more_data */
1423 debugs(33, 5, "ClientSocketContext::canPackMoreRanges: returning " << (http->range_iter.currentSpec() ? true : false));
1424 return http->range_iter.currentSpec() ? true : false;
1425 }
1426
1427 int64_t
1428 ClientSocketContext::getNextRangeOffset() const
1429 {
1430 if (http->request->range) {
1431 /* offset in range specs does not count the prefix of an http msg */
1432 debugs (33, 5, "ClientSocketContext::getNextRangeOffset: http offset " << http->out.offset);
1433 /* check: reply was parsed and range iterator was initialized */
1434 assert(http->range_iter.valid);
1435 /* filter out data according to range specs */
1436 assert (canPackMoreRanges());
1437 {
1438 int64_t start; /* offset of still missing data */
1439 assert(http->range_iter.currentSpec());
1440 start = http->range_iter.currentSpec()->offset + http->range_iter.currentSpec()->length - http->range_iter.debt();
1441 debugs(33, 3, "clientPackMoreRanges: in: offset: " << http->out.offset);
1442 debugs(33, 3, "clientPackMoreRanges: out:"
1443 " start: " << start <<
1444 " spec[" << http->range_iter.pos - http->request->range->begin() << "]:" <<
1445 " [" << http->range_iter.currentSpec()->offset <<
1446 ", " << http->range_iter.currentSpec()->offset + http->range_iter.currentSpec()->length << "),"
1447 " len: " << http->range_iter.currentSpec()->length <<
1448 " debt: " << http->range_iter.debt());
1449 if (http->range_iter.currentSpec()->length != -1)
1450 assert(http->out.offset <= start); /* we did not miss it */
1451
1452 return start;
1453 }
1454
1455 } else if (reply && reply->content_range) {
1456 /* request does not have ranges, but reply does */
1457 //FIXME: should use range_iter_pos on reply, as soon as reply->content_range
1458 // becomes HttpHdrRange rather than HttpHdrRangeSpec.
1459 return http->out.offset + reply->content_range->spec.offset;
1460 }
1461
1462 return http->out.offset;
1463 }
1464
1465 void
1466 ClientSocketContext::pullData()
1467 {
1468 debugs(33, 5, "ClientSocketContext::pullData: FD " << fd() << " attempting to pull upstream data");
1469
1470 /* More data will be coming from the stream. */
1471 StoreIOBuffer readBuffer;
1472 /* XXX: Next requested byte in the range sequence */
1473 /* XXX: length = getmaximumrangelenfgth */
1474 readBuffer.offset = getNextRangeOffset();
1475 readBuffer.length = HTTP_REQBUF_SZ;
1476 readBuffer.data = reqbuf;
1477 /* we may note we have reached the end of the wanted ranges */
1478 clientStreamRead(getTail(), http, readBuffer);
1479 }
1480
1481 clientStream_status_t
1482 ClientSocketContext::socketState()
1483 {
1484 switch (clientStreamStatus(getTail(), http)) {
1485
1486 case STREAM_NONE:
1487 /* check for range support ending */
1488
1489 if (http->request->range) {
1490 /* check: reply was parsed and range iterator was initialized */
1491 assert(http->range_iter.valid);
1492 /* filter out data according to range specs */
1493
1494 if (!canPackMoreRanges()) {
1495 debugs(33, 5, "ClientSocketContext::socketState: Range request has hit end of returnable range sequence on FD " <<
1496 fd());
1497
1498 if (http->request->flags.proxy_keepalive)
1499 return STREAM_COMPLETE;
1500 else
1501 return STREAM_UNPLANNED_COMPLETE;
1502 }
1503 } else if (reply && reply->content_range) {
1504 /* reply has content-range, but Squid is not managing ranges */
1505 const int64_t &bytesSent = http->out.offset;
1506 const int64_t &bytesExpected = reply->content_range->spec.length;
1507
1508 debugs(33, 7, HERE << "body bytes sent vs. expected: " <<
1509 bytesSent << " ? " << bytesExpected << " (+" <<
1510 reply->content_range->spec.offset << ")");
1511
1512 // did we get at least what we expected, based on range specs?
1513
1514 if (bytesSent == bytesExpected) // got everything
1515 return STREAM_COMPLETE;
1516
1517 // The logic below is not clear: If we got more than we
1518 // expected why would persistency matter? Should not this
1519 // always be an error?
1520 if (bytesSent > bytesExpected) { // got extra
1521 if (http->request->flags.proxy_keepalive)
1522 return STREAM_COMPLETE;
1523 else
1524 return STREAM_UNPLANNED_COMPLETE;
1525 }
1526
1527 // did not get enough yet, expecting more
1528 }
1529
1530 return STREAM_NONE;
1531
1532 case STREAM_COMPLETE:
1533 return STREAM_COMPLETE;
1534
1535 case STREAM_UNPLANNED_COMPLETE:
1536 return STREAM_UNPLANNED_COMPLETE;
1537
1538 case STREAM_FAILED:
1539 return STREAM_FAILED;
1540 }
1541
1542 fatal ("unreachable code\n");
1543 return STREAM_NONE;
1544 }
1545
1546 /* A write has just completed to the client, or we have just realised there is
1547 * no more data to send.
1548 */
1549 void
1550 clientWriteComplete(int fd, char *bufnotused, size_t size, comm_err_t errflag, int xerrno, void *data)
1551 {
1552 ClientSocketContext *context = (ClientSocketContext *)data;
1553 context->writeComplete (fd, bufnotused, size, errflag);
1554 }
1555
1556 void
1557 ClientSocketContext::doClose()
1558 {
1559 comm_close(fd());
1560 }
1561
1562 void
1563 ClientSocketContext::initiateClose(const char *reason)
1564 {
1565 debugs(33, 5, HERE << "initiateClose: closing for " << reason);
1566
1567 if (http != NULL) {
1568 ConnStateData::Pointer conn = http->getConn();
1569
1570 if (conn != NULL) {
1571 if (const int64_t expecting = conn->bodySizeLeft()) {
1572 debugs(33, 5, HERE << "ClientSocketContext::initiateClose: " <<
1573 "closing, but first " << conn << " needs to read " <<
1574 expecting << " request body bytes with " <<
1575 conn->in.notYetUsed << " notYetUsed");
1576
1577 if (conn->closing()) {
1578 debugs(33, 2, HERE << "avoiding double-closing " << conn);
1579 return;
1580 }
1581
1582 /*
1583 * XXX We assume the reply fits in the TCP transmit
1584 * window. If not the connection may stall while sending
1585 * the reply (before reaching here) if the client does not
1586 * try to read the response while sending the request body.
1587 * As of yet we have not received any complaints indicating
1588 * this may be an issue.
1589 */
1590 conn->startClosing(reason);
1591
1592 return;
1593 }
1594 }
1595 }
1596
1597 doClose();
1598 }
1599
1600 void
1601 ClientSocketContext::writeComplete(int fd, char *bufnotused, size_t size, comm_err_t errflag)
1602 {
1603 StoreEntry *entry = http->storeEntry();
1604 http->out.size += size;
1605 assert(fd > -1);
1606 debugs(33, 5, "clientWriteComplete: FD " << fd << ", sz " << size <<
1607 ", err " << errflag << ", off " << http->out.size << ", len " <<
1608 entry ? entry->objectLen() : 0);
1609 clientUpdateSocketStats(http->logType, size);
1610 assert (this->fd() == fd);
1611
1612 /* Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up */
1613
1614 if (errflag == COMM_ERR_CLOSING)
1615 return;
1616
1617 if (errflag || clientHttpRequestStatus(fd, http)) {
1618 initiateClose("failure or true request status");
1619 /* Do we leak here ? */
1620 return;
1621 }
1622
1623 switch (socketState()) {
1624
1625 case STREAM_NONE:
1626 pullData();
1627 break;
1628
1629 case STREAM_COMPLETE:
1630 debugs(33, 5, "clientWriteComplete: FD " << fd << " Keeping Alive");
1631 keepaliveNextRequest();
1632 return;
1633
1634 case STREAM_UNPLANNED_COMPLETE:
1635 /* fallthrough */
1636
1637 case STREAM_FAILED:
1638 initiateClose("STREAM_UNPLANNED_COMPLETE|STREAM_FAILED");
1639 return;
1640
1641 default:
1642 fatal("Hit unreachable code in clientWriteComplete\n");
1643 }
1644 }
1645
1646 extern "C" CSR clientGetMoreData;
1647 extern "C" CSS clientReplyStatus;
1648 extern "C" CSD clientReplyDetach;
1649
1650 static ClientSocketContext *
1651 parseHttpRequestAbort(ConnStateData::Pointer & conn, const char *uri)
1652 {
1653 ClientHttpRequest *http;
1654 ClientSocketContext *context;
1655 StoreIOBuffer tempBuffer;
1656 http = new ClientHttpRequest(conn);
1657 http->req_sz = conn->in.notYetUsed;
1658 http->uri = xstrdup(uri);
1659 setLogUri (http, uri);
1660 context = ClientSocketContextNew(http);
1661 tempBuffer.data = context->reqbuf;
1662 tempBuffer.length = HTTP_REQBUF_SZ;
1663 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
1664 clientReplyStatus, new clientReplyContext(http), clientSocketRecipient,
1665 clientSocketDetach, context, tempBuffer);
1666 return context;
1667 }
1668
1669 char *
1670 skipLeadingSpace(char *aString)
1671 {
1672 char *result = aString;
1673
1674 while (xisspace(*aString))
1675 ++aString;
1676
1677 return result;
1678 }
1679
1680 /*
1681 * 'end' defaults to NULL for backwards compatibility
1682 * remove default value if we ever get rid of NULL-terminated
1683 * request buffers.
1684 */
1685 const char *
1686 findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char *end)
1687 {
1688 if (NULL == end) {
1689 end = uriAndHTTPVersion + strcspn(uriAndHTTPVersion, "\r\n");
1690 assert(end);
1691 }
1692
1693 for (; end > uriAndHTTPVersion; end--) {
1694 if (*end == '\n' || *end == '\r')
1695 continue;
1696
1697 if (xisspace(*end)) {
1698 if (strncasecmp(end + 1, "HTTP/", 5) == 0)
1699 return end + 1;
1700 else
1701 break;
1702 }
1703 }
1704
1705 return NULL;
1706 }
1707
1708 void
1709 setLogUri(ClientHttpRequest * http, char const *uri)
1710 {
1711 safe_free(http->log_uri);
1712
1713 if (!stringHasCntl(uri))
1714 http->log_uri = xstrndup(uri, MAX_URL);
1715 else
1716 http->log_uri = xstrndup(rfc1738_escape_unescaped(uri), MAX_URL);
1717 }
1718
1719 static void
1720 prepareAcceleratedURL(ConnStateData::Pointer & conn, ClientHttpRequest *http, char *url, const char *req_hdr)
1721 {
1722 int vhost = conn->port->vhost;
1723 int vport = conn->port->vport;
1724 char *host;
1725 char ntoabuf[MAX_IPSTRLEN];
1726
1727 http->flags.accel = 1;
1728
1729 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
1730
1731 if (strncasecmp(url, "cache_object://", 15) == 0)
1732 return; /* already in good shape */
1733
1734 if (*url != '/') {
1735 if (conn->port->vhost)
1736 return; /* already in good shape */
1737
1738 /* else we need to ignore the host name */
1739 url = strstr(url, "//");
1740
1741 #if SHOULD_REJECT_UNKNOWN_URLS
1742
1743 if (!url)
1744 return parseHttpRequestAbort(conn, "error:invalid-request");
1745
1746 #endif
1747
1748 if (url)
1749 url = strchr(url + 2, '/');
1750
1751 if (!url)
1752 url = (char *) "/";
1753 }
1754
1755 if (internalCheck(url)) {
1756 /* prepend our name & port */
1757 http->uri = xstrdup(internalLocalUri(NULL, url));
1758 } else if (vhost && (host = mime_get_header(req_hdr, "Host")) != NULL) {
1759 int url_sz = strlen(url) + 32 + Config.appendDomainLen +
1760 strlen(host);
1761 http->uri = (char *)xcalloc(url_sz, 1);
1762 snprintf(http->uri, url_sz, "%s://%s%s",
1763 conn->port->protocol, host, url);
1764 debugs(33, 5, "ACCEL VHOST REWRITE: '" << http->uri << "'");
1765 } else if (conn->port->defaultsite) {
1766 int url_sz = strlen(url) + 32 + Config.appendDomainLen +
1767 strlen(conn->port->defaultsite);
1768 http->uri = (char *)xcalloc(url_sz, 1);
1769 snprintf(http->uri, url_sz, "%s://%s%s",
1770 conn->port->protocol, conn->port->defaultsite, url);
1771 debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: '" << http->uri <<"'");
1772 } else if (vport == -1) {
1773 /* Put the local socket IP address as the hostname. */
1774 int url_sz = strlen(url) + 32 + Config.appendDomainLen;
1775 http->uri = (char *)xcalloc(url_sz, 1);
1776 snprintf(http->uri, url_sz, "%s://%s:%d%s",
1777 http->getConn()->port->protocol,
1778 http->getConn()->me.NtoA(ntoabuf,MAX_IPSTRLEN),
1779 http->getConn()->me.GetPort(), url);
1780 debugs(33, 5, "ACCEL VPORT REWRITE: '" << http->uri << "'");
1781 } else if (vport > 0) {
1782 /* Put the local socket IP address as the hostname, but static port */
1783 int url_sz = strlen(url) + 32 + Config.appendDomainLen;
1784 http->uri = (char *)xcalloc(url_sz, 1);
1785 snprintf(http->uri, url_sz, "%s://%s:%d%s",
1786 http->getConn()->port->protocol,
1787 http->getConn()->me.NtoA(ntoabuf,MAX_IPSTRLEN),
1788 vport, url);
1789 debugs(33, 5, "ACCEL VPORT REWRITE: '" << http->uri << "'");
1790 }
1791 }
1792
1793 static void
1794 prepareTransparentURL(ConnStateData::Pointer & conn, ClientHttpRequest *http, char *url, const char *req_hdr)
1795 {
1796 char *host;
1797 char ntoabuf[MAX_IPSTRLEN];
1798
1799 http->flags.transparent = 1;
1800
1801 if (*url != '/')
1802 return; /* already in good shape */
1803
1804 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
1805
1806 if ((host = mime_get_header(req_hdr, "Host")) != NULL) {
1807 int url_sz = strlen(url) + 32 + Config.appendDomainLen +
1808 strlen(host);
1809 http->uri = (char *)xcalloc(url_sz, 1);
1810 snprintf(http->uri, url_sz, "%s://%s%s",
1811 conn->port->protocol, host, url);
1812 debugs(33, 5, "TRANSPARENT HOST REWRITE: '" << http->uri <<"'");
1813 } else {
1814 /* Put the local socket IP address as the hostname. */
1815 int url_sz = strlen(url) + 32 + Config.appendDomainLen;
1816 http->uri = (char *)xcalloc(url_sz, 1);
1817 snprintf(http->uri, url_sz, "%s://%s:%d%s",
1818 http->getConn()->port->protocol,
1819 http->getConn()->me.NtoA(ntoabuf,MAX_IPSTRLEN),
1820 http->getConn()->me.GetPort(), url);
1821 debugs(33, 5, "TRANSPARENT REWRITE: '" << http->uri << "'");
1822 }
1823 }
1824
1825 /*
1826 * parseHttpRequest()
1827 *
1828 * Returns
1829 * NULL on incomplete requests
1830 * a ClientSocketContext structure on success or failure.
1831 * Sets result->flags.parsed_ok to 0 if failed to parse the request.
1832 * Sets result->flags.parsed_ok to 1 if we have a good request.
1833 */
1834 static ClientSocketContext *
1835 parseHttpRequest(ConnStateData::Pointer & conn, HttpParser *hp, method_t * method_p, HttpVersion *http_ver)
1836 {
1837 char *url = NULL;
1838 char *req_hdr = NULL;
1839 char *end;
1840 size_t req_sz;
1841 ClientHttpRequest *http;
1842 ClientSocketContext *result;
1843 StoreIOBuffer tempBuffer;
1844 int r;
1845
1846 /* pre-set these values to make aborting simpler */
1847 *method_p = METHOD_NONE;
1848
1849 /* Attempt to parse the first line; this'll define the method, url, version and header begin */
1850 r = HttpParserParseReqLine(hp);
1851
1852 if (r == 0) {
1853 debugs(33, 5, "Incomplete request, waiting for end of request line");
1854 return NULL;
1855 }
1856
1857 if (r == -1) {
1858 return parseHttpRequestAbort(conn, "error:invalid-request");
1859 }
1860
1861 /* Request line is valid here .. */
1862 *http_ver = HttpVersion(hp->v_maj, hp->v_min);
1863
1864 /* This call scans the entire request, not just the headers */
1865 if (hp->v_maj > 0) {
1866 if ((req_sz = headersEnd(hp->buf, hp->bufsiz)) == 0) {
1867 debugs(33, 5, "Incomplete request, waiting for end of headers");
1868 return NULL;
1869 }
1870 } else {
1871 debugs(33, 3, "parseHttpRequest: Missing HTTP identifier");
1872 req_sz = HttpParserReqSz(hp);
1873 }
1874
1875 /* We know the whole request is in hp->buf now */
1876
1877 assert(req_sz <= (size_t) hp->bufsiz);
1878
1879 /* Will the following be true with HTTP/0.9 requests? probably not .. */
1880 /* So the rest of the code will need to deal with '0'-byte headers (ie, none, so don't try parsing em) */
1881 assert(req_sz > 0);
1882
1883 hp->hdr_end = req_sz - 1;
1884
1885 hp->hdr_start = hp->req_end + 1;
1886
1887 /* Enforce max_request_size */
1888 if (req_sz >= Config.maxRequestHeaderSize) {
1889 debugs(33, 5, "parseHttpRequest: Too large request");
1890 return parseHttpRequestAbort(conn, "error:request-too-large");
1891 }
1892
1893 /* Set method_p */
1894 *method_p = HttpRequestMethod(&hp->buf[hp->m_start], &hp->buf[hp->m_end]);
1895
1896 if (*method_p == METHOD_NONE) {
1897 /* XXX need a way to say "this many character length string" */
1898 debugs(33, 1, "clientParseRequestMethod: Unsupported method in request '" << hp->buf << "'");
1899
1900 /* XXX where's the method set for this error? */
1901 return parseHttpRequestAbort(conn, "error:unsupported-request-method");
1902 }
1903
1904 /* set url */
1905 /*
1906 * XXX this should eventually not use a malloc'ed buffer; the transformation code
1907 * below needs to be modified to not expect a mutable nul-terminated string.
1908 */
1909 url = (char *)xmalloc(hp->u_end - hp->u_start + 16);
1910
1911 memcpy(url, hp->buf + hp->u_start, hp->u_end - hp->u_start + 1);
1912
1913 url[hp->u_end - hp->u_start + 1] = '\0';
1914
1915 /*
1916 * Process headers after request line
1917 * TODO: Use httpRequestParse here.
1918 */
1919 /* XXX this code should be modified to take a const char * later! */
1920 req_hdr = (char *) hp->buf + hp->req_end + 1;
1921
1922 debugs(33, 3, "parseHttpRequest: req_hdr = {" << req_hdr << "}");
1923
1924 end = (char *) hp->buf + hp->hdr_end;
1925
1926 debugs(33, 3, "parseHttpRequest: end = {" << end << "}");
1927
1928 if (strstr(req_hdr, "\r\r\n")) {
1929 debugs(33, 1, "WARNING: suspicious HTTP request contains double CR");
1930 xfree(url);
1931 return parseHttpRequestAbort(conn, "error:double-CR");
1932 }
1933
1934 debugs(33, 3, "parseHttpRequest: prefix_sz = " <<
1935 (int) HttpParserRequestLen(hp) << ", req_line_sz = " <<
1936 HttpParserReqSz(hp));
1937
1938 /* Ok, all headers are received */
1939 http = new ClientHttpRequest(conn);
1940
1941 http->req_sz = HttpParserRequestLen(hp);
1942 result = ClientSocketContextNew(http);
1943 tempBuffer.data = result->reqbuf;
1944 tempBuffer.length = HTTP_REQBUF_SZ;
1945
1946 ClientStreamData newServer = new clientReplyContext(http);
1947 ClientStreamData newClient = result;
1948 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
1949 clientReplyStatus, newServer, clientSocketRecipient,
1950 clientSocketDetach, newClient, tempBuffer);
1951
1952 debugs(33, 5, "parseHttpRequest: Request Header is\n" <<(hp->buf) + hp->hdr_start);
1953
1954 #if THIS_VIOLATES_HTTP_SPECS_ON_URL_TRANSFORMATION
1955
1956 if ((t = strchr(url, '#'))) /* remove HTML anchors */
1957 *t = '\0';
1958
1959 #endif
1960
1961 /* Rewrite the URL in transparent or accelerator mode */
1962 if (conn->transparent()) {
1963 prepareTransparentURL(conn, http, url, req_hdr);
1964 } else if (conn->port->accel) {
1965 prepareAcceleratedURL(conn, http, url, req_hdr);
1966 } else if (internalCheck(url)) {
1967 /* prepend our name & port */
1968 http->uri = xstrdup(internalLocalUri(NULL, url));
1969 http->flags.accel = 1;
1970 }
1971
1972 if (!http->uri) {
1973 /* No special rewrites have been applied above, use the
1974 * requested url. may be rewritten later, so make extra room */
1975 int url_sz = strlen(url) + Config.appendDomainLen + 5;
1976 http->uri = (char *)xcalloc(url_sz, 1);
1977 strcpy(http->uri, url);
1978 }
1979
1980 setLogUri(http, http->uri);
1981 debugs(33, 5, "parseHttpRequest: Complete request received");
1982 result->flags.parsed_ok = 1;
1983 xfree(url);
1984 return result;
1985 }
1986
1987 int
1988 ConnStateData::getAvailableBufferLength() const
1989 {
1990 int result = in.allocatedSize - in.notYetUsed - 1;
1991 assert (result >= 0);
1992 return result;
1993 }
1994
1995 void
1996 ConnStateData::makeSpaceAvailable()
1997 {
1998 if (getAvailableBufferLength() < 2) {
1999 in.buf = (char *)memReallocBuf(in.buf, in.allocatedSize * 2, &in.allocatedSize);
2000 debugs(33, 2, "growing request buffer: notYetUsed=" << in.notYetUsed << " size=" << in.allocatedSize);
2001 }
2002 }
2003
2004 void
2005 ConnStateData::addContextToQueue(ClientSocketContext * context)
2006 {
2007 ClientSocketContext::Pointer *S;
2008
2009 for (S = (ClientSocketContext::Pointer *) & currentobject; S->getRaw();
2010 S = &(*S)->next)
2011
2012 ;
2013 *S = context;
2014
2015 ++nrequests;
2016 }
2017
2018 int
2019 ConnStateData::getConcurrentRequestCount() const
2020 {
2021 int result = 0;
2022 ClientSocketContext::Pointer *T;
2023
2024 for (T = (ClientSocketContext::Pointer *) &currentobject;
2025 T->getRaw(); T = &(*T)->next, ++result)
2026
2027 ;
2028 return result;
2029 }
2030
2031 int
2032 connReadWasError(ConnStateData::Pointer & conn, comm_err_t flag, int size, int xerrno)
2033 {
2034 if (flag != COMM_OK) {
2035 debugs(33, 2, "connReadWasError: FD " << conn->fd << ": got flag " << flag);
2036 return 1;
2037 }
2038
2039 if (size < 0) {
2040 if (!ignoreErrno(xerrno)) {
2041 debugs(33, 2, "connReadWasError: FD " << conn->fd << ": " << xstrerr(xerrno));
2042 return 1;
2043 } else if (conn->in.notYetUsed == 0) {
2044 debugs(33, 2, "connReadWasError: FD " << conn->fd << ": no data to process (" << xstrerr(xerrno) << ")");
2045 }
2046 }
2047
2048 return 0;
2049 }
2050
2051 int
2052 connFinishedWithConn(ConnStateData::Pointer & conn, int size)
2053 {
2054 if (size == 0) {
2055 if (conn->getConcurrentRequestCount() == 0 && conn->in.notYetUsed == 0) {
2056 /* no current or pending requests */
2057 debugs(33, 4, "connFinishedWithConn: FD " << conn->fd << " closed");
2058 return 1;
2059 } else if (!Config.onoff.half_closed_clients) {
2060 /* admin doesn't want to support half-closed client sockets */
2061 debugs(33, 3, "connFinishedWithConn: FD " << conn->fd << " aborted (half_closed_clients disabled)");
2062 return 1;
2063 }
2064 }
2065
2066 return 0;
2067 }
2068
2069 void
2070 connNoteUseOfBuffer(ConnStateData* conn, size_t byteCount)
2071 {
2072 assert(byteCount > 0 && byteCount <= conn->in.notYetUsed);
2073 conn->in.notYetUsed -= byteCount;
2074 debugs(33, 5, HERE << "conn->in.notYetUsed = " << conn->in.notYetUsed);
2075 /*
2076 * If there is still data that will be used,
2077 * move it to the beginning.
2078 */
2079
2080 if (conn->in.notYetUsed > 0)
2081 xmemmove(conn->in.buf, conn->in.buf + byteCount,
2082 conn->in.notYetUsed);
2083 }
2084
2085 int
2086 connKeepReadingIncompleteRequest(ConnStateData::Pointer & conn)
2087 {
2088 return conn->in.notYetUsed >= Config.maxRequestHeaderSize ? 0 : 1;
2089 }
2090
2091 void
2092 connCancelIncompleteRequests(ConnStateData::Pointer & conn)
2093 {
2094 ClientSocketContext *context = parseHttpRequestAbort(conn, "error:request-too-large");
2095 clientStreamNode *node = context->getClientReplyContext();
2096 assert(!connKeepReadingIncompleteRequest(conn));
2097 debugs(33, 1, "Request header is too large (" << conn->in.notYetUsed << " bytes)");
2098 debugs(33, 1, "Config 'request_header_max_size'= " << Config.maxRequestHeaderSize << " bytes.");
2099 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2100 assert (repContext);
2101 repContext->setReplyToError(ERR_TOO_BIG,
2102 HTTP_REQUEST_ENTITY_TOO_LARGE, METHOD_NONE, NULL,
2103 conn->peer, NULL, NULL, NULL);
2104 context->registerWithConn();
2105 context->pullData();
2106 }
2107
2108 static void
2109 clientMaybeReadData(ConnStateData::Pointer &conn, int do_next_read)
2110 {
2111 if (do_next_read) {
2112 conn->flags.readMoreRequests = true;
2113 conn->readSomeData();
2114 }
2115 }
2116
2117 static void
2118 clientAfterReadingRequests(int fd, ConnStateData::Pointer &conn, int do_next_read)
2119 {
2120 /*
2121 * If (1) we are reading a message body, (2) and the connection
2122 * is half-closed, and (3) we didn't get the entire HTTP request
2123 * yet, then close this connection.
2124 */
2125
2126 if (fd_table[fd].flags.socket_eof) {
2127 if ((int64_t)conn->in.notYetUsed < conn->bodySizeLeft()) {
2128 /* Partial request received. Abort client connection! */
2129 debugs(33, 3, "clientAfterReadingRequests: FD " << fd << " aborted, partial request");
2130 comm_close(fd);
2131 return;
2132 }
2133 }
2134
2135 clientMaybeReadData (conn, do_next_read);
2136 }
2137
2138 static void
2139 clientProcessRequest(ConnStateData::Pointer &conn, HttpParser *hp, ClientSocketContext *context, method_t method, HttpVersion http_ver)
2140 {
2141 ClientHttpRequest *http = context->http;
2142 HttpRequest *request = NULL;
2143 bool notedUseOfBuffer = false;
2144
2145 /* We have an initial client stream in place should it be needed */
2146 /* setup our private context */
2147 context->registerWithConn();
2148
2149 if (context->flags.parsed_ok == 0) {
2150 clientStreamNode *node = context->getClientReplyContext();
2151 debugs(33, 1, "clientProcessRequest: Invalid Request");
2152 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2153 assert (repContext);
2154 repContext->setReplyToError(ERR_INVALID_REQ, HTTP_BAD_REQUEST, method, NULL, conn->peer, NULL, conn->in.buf, NULL);
2155 assert(context->http->out.offset == 0);
2156 context->pullData();
2157 conn->flags.readMoreRequests = false;
2158 goto finish;
2159 }
2160
2161 if ((request = HttpRequest::CreateFromUrlAndMethod(http->uri, method)) == NULL) {
2162 clientStreamNode *node = context->getClientReplyContext();
2163 debugs(33, 5, "Invalid URL: " << http->uri);
2164 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2165 assert (repContext);
2166 repContext->setReplyToError(ERR_INVALID_URL, HTTP_BAD_REQUEST, method, http->uri, conn->peer, NULL, NULL, NULL);
2167 assert(context->http->out.offset == 0);
2168 context->pullData();
2169 conn->flags.readMoreRequests = false;
2170 goto finish;
2171 }
2172
2173 /* compile headers */
2174 /* we should skip request line! */
2175 /* XXX should actually know the damned buffer size here */
2176 if (!request->parseHeader(HttpParserHdrBuf(hp), HttpParserHdrSz(hp))) {
2177 clientStreamNode *node = context->getClientReplyContext();
2178 debugs(33, 5, "Failed to parse request headers:\n" << HttpParserHdrBuf(hp));
2179 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2180 assert (repContext);
2181 repContext->setReplyToError(ERR_INVALID_URL, HTTP_BAD_REQUEST, method, http->uri, conn->peer, NULL, NULL, NULL);
2182 assert(context->http->out.offset == 0);
2183 context->pullData();
2184 conn->flags.readMoreRequests = false;
2185 goto finish;
2186 }
2187
2188 request->flags.accelerated = http->flags.accel;
2189
2190 request->flags.transparent = http->flags.transparent;
2191
2192 #if LINUX_TPROXY
2193
2194 request->flags.tproxy = conn->port->tproxy && need_linux_tproxy;
2195 #endif
2196
2197 if (internalCheck(request->urlpath.buf())) {
2198 if (internalHostnameIs(request->GetHost()) &&
2199 request->port == getMyPort()) {
2200 http->flags.internal = 1;
2201 } else if (Config.onoff.global_internal_static && internalStaticCheck(request->urlpath.buf())) {
2202 request->SetHost(internalHostname());
2203 request->port = getMyPort();
2204 http->flags.internal = 1;
2205 }
2206 }
2207
2208 if (http->flags.internal) {
2209 request->protocol = PROTO_HTTP;
2210 request->login[0] = '\0';
2211 }
2212
2213 request->flags.internal = http->flags.internal;
2214 setLogUri (http, urlCanonicalClean(request));
2215 request->client_addr = conn->peer;
2216 request->my_addr = conn->me;
2217 request->http_ver = http_ver;
2218
2219 if (!urlCheckRequest(request) ||
2220 request->header.has(HDR_TRANSFER_ENCODING)) {
2221 clientStreamNode *node = context->getClientReplyContext();
2222 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2223 assert (repContext);
2224 repContext->setReplyToError(ERR_UNSUP_REQ,
2225 HTTP_NOT_IMPLEMENTED, request->method, NULL,
2226 conn->peer, request, NULL, NULL);
2227 assert(context->http->out.offset == 0);
2228 context->pullData();
2229 conn->flags.readMoreRequests = false;
2230 goto finish;
2231 }
2232
2233
2234 if (!clientIsContentLengthValid(request)) {
2235 clientStreamNode *node = context->getClientReplyContext();
2236 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2237 assert (repContext);
2238 repContext->setReplyToError(ERR_INVALID_REQ,
2239 HTTP_LENGTH_REQUIRED, request->method, NULL,
2240 conn->peer, request, NULL, NULL);
2241 assert(context->http->out.offset == 0);
2242 context->pullData();
2243 conn->flags.readMoreRequests = false;
2244 goto finish;
2245 }
2246
2247 http->request = HTTPMSGLOCK(request);
2248 clientSetKeepaliveFlag(http);
2249
2250 /* If this is a CONNECT, don't schedule a read - ssl.c will handle it */
2251 if (http->request->method == METHOD_CONNECT)
2252 context->mayUseConnection(true);
2253
2254 /* Do we expect a request-body? */
2255 if (!context->mayUseConnection() && request->content_length > 0) {
2256 request->body_pipe = conn->expectRequestBody(request->content_length);
2257
2258 // consume header early so that body pipe gets just the body
2259 connNoteUseOfBuffer(conn.getRaw(), http->req_sz);
2260 notedUseOfBuffer = true;
2261
2262 conn->handleRequestBodyData();
2263
2264 if (!request->body_pipe->exhausted())
2265 conn->readSomeData();
2266
2267 /* Is it too large? */
2268
2269 if (!clientIsRequestBodyValid(request->content_length) ||
2270 clientIsRequestBodyTooLargeForPolicy(request->content_length)) {
2271 clientStreamNode *node = context->getClientReplyContext();
2272 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2273 assert (repContext);
2274 repContext->setReplyToError(ERR_TOO_BIG,
2275 HTTP_REQUEST_ENTITY_TOO_LARGE, METHOD_NONE, NULL,
2276 conn->peer, http->request, NULL, NULL);
2277 assert(context->http->out.offset == 0);
2278 context->pullData();
2279 goto finish;
2280 }
2281
2282 context->mayUseConnection(true);
2283 }
2284
2285 http->calloutContext = new ClientRequestContext(http);
2286
2287 http->doCallouts();
2288
2289 finish:
2290 if (!notedUseOfBuffer)
2291 connNoteUseOfBuffer(conn.getRaw(), http->req_sz);
2292
2293 /*
2294 * DPW 2007-05-18
2295 * Moved the TCP_RESET feature from clientReplyContext::sendMoreData
2296 * to here because calling comm_reset_close() causes http to
2297 * be freed and the above connNoteUseOfBuffer() would hit an
2298 * assertion, not to mention that we were accessing freed memory.
2299 */
2300 if (http->request->flags.resetTCP() && conn->fd > -1) {
2301 debugs(33, 3, HERE << "Sending TCP RST on FD " << conn->fd);
2302 conn->flags.readMoreRequests = false;
2303 comm_reset_close(conn->fd);
2304 return;
2305 }
2306 }
2307
2308 static void
2309 connStripBufferWhitespace (ConnStateData::Pointer &conn)
2310 {
2311 while (conn->in.notYetUsed > 0 && xisspace(conn->in.buf[0])) {
2312 xmemmove(conn->in.buf, conn->in.buf + 1, conn->in.notYetUsed - 1);
2313 --conn->in.notYetUsed;
2314 }
2315 }
2316
2317 static int
2318 connOkToAddRequest(ConnStateData::Pointer &conn)
2319 {
2320 int result = conn->getConcurrentRequestCount() < (Config.onoff.pipeline_prefetch ? 2 : 1);
2321
2322 if (!result) {
2323 debugs(33, 3, "connOkToAddRequest: FD " << conn->fd <<
2324 " max concurrent requests reached");
2325 debugs(33, 5, "connOkToAddRequest: FD " << conn->fd <<
2326 " defering new request until one is done");
2327 }
2328
2329 return result;
2330 }
2331
2332 /*
2333 * bodySizeLeft
2334 *
2335 * Report on the number of bytes of body content that we
2336 * know are yet to be read on this connection.
2337 */
2338 int64_t
2339 ConnStateData::bodySizeLeft()
2340 {
2341 // XXX: this logic will not work for chunked requests with unknown sizes
2342
2343 if (bodyPipe != NULL)
2344 return bodyPipe->unproducedSize();
2345
2346 return 0;
2347 }
2348
2349 /*
2350 * Attempt to parse one or more requests from the input buffer.
2351 * If a request is successfully parsed, even if the next request
2352 * is only partially parsed, it will return TRUE.
2353 * do_next_read is updated to indicate whether a read should be
2354 * scheduled.
2355 */
2356 static bool
2357 clientParseRequest(ConnStateData::Pointer conn, bool &do_next_read)
2358 {
2359 method_t method;
2360 ClientSocketContext *context;
2361 bool parsed_req = false;
2362 HttpVersion http_ver;
2363 HttpParser hp;
2364
2365 debugs(33, 5, "clientParseRequest: FD " << conn->fd << ": attempting to parse");
2366
2367 while (conn->in.notYetUsed > 0 && conn->bodySizeLeft() == 0) {
2368 connStripBufferWhitespace (conn);
2369
2370 /* Don't try to parse if the buffer is empty */
2371
2372 if (conn->in.notYetUsed == 0)
2373 break;
2374
2375 /* Limit the number of concurrent requests to 2 */
2376
2377 if (!connOkToAddRequest(conn)) {
2378 break;
2379 }
2380
2381 /* Should not be needed anymore */
2382 /* Terminate the string */
2383 conn->in.buf[conn->in.notYetUsed] = '\0';
2384
2385 /* Begin the parsing */
2386 HttpParserInit(&hp, conn->in.buf, conn->in.notYetUsed);
2387
2388 /* Process request */
2389 PROF_start(parseHttpRequest);
2390
2391 context = parseHttpRequest(conn, &hp, &method, &http_ver);
2392
2393 PROF_stop(parseHttpRequest);
2394
2395 /* partial or incomplete request */
2396 if (!context) {
2397
2398 if (!connKeepReadingIncompleteRequest(conn))
2399 connCancelIncompleteRequests(conn);
2400
2401 break;
2402 }
2403
2404 /* status -1 or 1 */
2405 if (context) {
2406 debugs(33, 5, "clientParseRequest: FD " << conn->fd << ": parsed a request");
2407 commSetTimeout(conn->fd, Config.Timeout.lifetime, clientLifetimeTimeout,
2408 context->http);
2409
2410 clientProcessRequest(conn, &hp, context, method, http_ver);
2411
2412 parsed_req = true;
2413
2414 if (context->mayUseConnection()) {
2415 debugs(33, 3, "clientParseRequest: Not reading, as this request may need the connection");
2416 do_next_read = 0;
2417 break;
2418 }
2419
2420 if (!conn->flags.readMoreRequests) {
2421 conn->flags.readMoreRequests = true;
2422 break;
2423 }
2424
2425 continue; /* while offset > 0 && conn->bodySizeLeft() == 0 */
2426 }
2427 } /* while offset > 0 && conn->bodySizeLeft() == 0 */
2428
2429 /* XXX where to 'finish' the parsing pass? */
2430
2431 return parsed_req;
2432 }
2433
2434 static void
2435 clientReadRequest(int fd, char *buf, size_t size, comm_err_t flag, int xerrno,
2436 void *data)
2437 {
2438 debugs(33,5,HERE << "clientReadRequest FD " << fd << " size " << size);
2439 ConnStateData::Pointer conn ((ConnStateData *)data);
2440 conn->reading(false);
2441 bool do_next_read = 1; /* the default _is_ to read data! - adrian */
2442
2443 assert (fd == conn->fd);
2444
2445 /* Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up */
2446
2447 if (flag == COMM_ERR_CLOSING) {
2448 debugs(33,5, HERE << " FD " << fd << " closing Bailout.");
2449 return;
2450 }
2451
2452 /*
2453 * Don't reset the timeout value here. The timeout value will be
2454 * set to Config.Timeout.request by httpAccept() and
2455 * clientWriteComplete(), and should apply to the request as a
2456 * whole, not individual read() calls. Plus, it breaks our
2457 * lame half-close detection
2458 */
2459 if (connReadWasError(conn, flag, size, xerrno)) {
2460 comm_close(fd);
2461 return;
2462 }
2463
2464 if (flag == COMM_OK) {
2465 if (size > 0) {
2466 kb_incr(&statCounter.client_http.kbytes_in, size);
2467
2468 conn->handleReadData(buf, size);
2469
2470 /* The above may close the connection under our feets */
2471 if (!conn->isOpen())
2472 return;
2473
2474 } else if (size == 0) {
2475 debugs(33, 5, "clientReadRequest: FD " << fd << " closed?");
2476
2477 if (connFinishedWithConn(conn, size)) {
2478 comm_close(fd);
2479 return;
2480 }
2481
2482 /* It might be half-closed, we can't tell */
2483 fd_table[fd].flags.socket_eof = 1;
2484
2485 commMarkHalfClosed(fd);
2486
2487 do_next_read = 0;
2488
2489 fd_note(fd, "half-closed");
2490
2491 /* There is one more close check at the end, to detect aborted
2492 * (partial) requests. At this point we can't tell if the request
2493 * is partial.
2494 */
2495 /* Continue to process previously read data */
2496 }
2497 }
2498
2499 /* Process next request */
2500 if (conn->getConcurrentRequestCount() == 0)
2501 fd_note(conn->fd, "Reading next request");
2502
2503 if (! clientParseRequest(conn, do_next_read)) {
2504 if (!conn->isOpen())
2505 return;
2506 /*
2507 * If the client here is half closed and we failed
2508 * to parse a request, close the connection.
2509 * The above check with connFinishedWithConn() only
2510 * succeeds _if_ the buffer is empty which it won't
2511 * be if we have an incomplete request.
2512 */
2513 if (conn->getConcurrentRequestCount() == 0 && commIsHalfClosed(fd)) {
2514 debugs(33, 5, "clientReadRequest: FD " << fd << ": half-closed connection, no completed request parsed, connection closing.");
2515 comm_close(fd);
2516 return;
2517 }
2518 }
2519
2520 if (!conn->isOpen())
2521 return;
2522
2523 clientAfterReadingRequests(fd, conn, do_next_read);
2524 }
2525
2526 // called when new request data has been read from the socket
2527 void
2528 ConnStateData::handleReadData(char *buf, size_t size)
2529 {
2530 char *current_buf = in.addressToReadInto();
2531
2532 if (buf != current_buf)
2533 xmemmove(current_buf, buf, size);
2534
2535 in.notYetUsed += size;
2536
2537 in.buf[in.notYetUsed] = '\0'; /* Terminate the string */
2538
2539 // if we are reading a body, stuff data into the body pipe
2540 if (bodyPipe != NULL)
2541 handleRequestBodyData();
2542 }
2543
2544 // called when new request body data has been buffered in in.buf
2545 // may close the connection if we were closing and piped everything out
2546 void
2547 ConnStateData::handleRequestBodyData()
2548 {
2549 assert(bodyPipe != NULL);
2550
2551 if (const size_t putSize = bodyPipe->putMoreData(in.buf, in.notYetUsed))
2552 connNoteUseOfBuffer(this, putSize);
2553
2554 if (!bodyPipe->mayNeedMoreData()) {
2555 // BodyPipe will clear us automagically when we produced everything
2556 bodyPipe = NULL;
2557
2558 debugs(33,5, HERE << "produced entire request body for FD " << fd);
2559
2560 if (closing()) {
2561 /* we've finished reading like good clients,
2562 * now do the close that initiateClose initiated.
2563 *
2564 * XXX: do we have to close? why not check keepalive et.
2565 *
2566 * XXX: To support chunked requests safely, we need to handle
2567 * the case of an endless request. This if-statement does not,
2568 * because mayNeedMoreData is true if request size is not known.
2569 */
2570 comm_close(fd);
2571 }
2572 }
2573 }
2574
2575 void
2576 ConnStateData::noteMoreBodySpaceAvailable(BodyPipe &)
2577 {
2578 handleRequestBodyData();
2579 }
2580
2581 void
2582 ConnStateData::noteBodyConsumerAborted(BodyPipe &)
2583 {
2584 if (!closing())
2585 startClosing("body consumer aborted");
2586 }
2587
2588 /* general lifetime handler for HTTP requests */
2589 static void
2590 requestTimeout(int fd, void *data)
2591 {
2592 #if THIS_CONFUSES_PERSISTENT_CONNECTION_AWARE_BROWSERS_AND_USERS
2593 ConnStateData *conn = data;
2594 debugs(33, 3, "requestTimeout: FD " << fd << ": lifetime is expired.");
2595
2596 if (COMMIO_FD_WRITECB(fd)->active) {
2597 /* FIXME: If this code is reinstated, check the conn counters,
2598 * not the fd table state
2599 */
2600 /*
2601 * Some data has been sent to the client, just close the FD
2602 */
2603 comm_close(fd);
2604 } else if (conn->nrequests) {
2605 /*
2606 * assume its a persistent connection; just close it
2607 */
2608 comm_close(fd);
2609 } else {
2610 /*
2611 * Generate an error
2612 */
2613 ClientHttpRequest **H;
2614 clientStreamNode *node;
2615 ClientHttpRequest *http =
2616 parseHttpRequestAbort(conn, "error:Connection%20lifetime%20expired");
2617 node = http->client_stream.tail->prev->data;
2618 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2619 assert (repContext);
2620 repContext->setReplyToError(ERR_LIFETIME_EXP,
2621 HTTP_REQUEST_TIMEOUT, METHOD_NONE, "N/A", &conn->peer.sin_addr,
2622 NULL, NULL, NULL);
2623 /* No requests can be outstanded */
2624 assert(conn->chr == NULL);
2625 /* add to the client request queue */
2626
2627 for (H = &conn->chr; *H; H = &(*H)->next)
2628
2629 ;
2630 *H = http;
2631
2632 clientStreamRead(http->client_stream.tail->data, http, 0,
2633 HTTP_REQBUF_SZ, context->reqbuf);
2634
2635 /*
2636 * if we don't close() here, we still need a timeout handler!
2637 */
2638 commSetTimeout(fd, 30, requestTimeout, conn);
2639
2640 /*
2641 * Aha, but we don't want a read handler!
2642 */
2643 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
2644 }
2645
2646 #else
2647 /*
2648 * Just close the connection to not confuse browsers
2649 * using persistent connections. Some browsers opens
2650 * an connection and then does not use it until much
2651 * later (presumeably because the request triggering
2652 * the open has already been completed on another
2653 * connection)
2654 */
2655 debugs(33, 3, "requestTimeout: FD " << fd << ": lifetime is expired.");
2656
2657 comm_close(fd);
2658
2659 #endif
2660 }
2661
2662 static void
2663 clientLifetimeTimeout(int fd, void *data)
2664 {
2665 ClientHttpRequest *http = (ClientHttpRequest *)data;
2666 debugs(33, 1, "WARNING: Closing client " << http->getConn()->peer << " connection due to lifetime timeout");
2667 debugs(33, 1, "\t" << http->uri);
2668 comm_close(fd);
2669 }
2670
2671 static bool
2672 okToAccept()
2673 {
2674 static time_t last_warn = 0;
2675
2676 if (fdNFree() >= RESERVED_FD)
2677 return true;
2678
2679 if (last_warn + 15 < squid_curtime) {
2680 debugs(33, 0, HERE << "WARNING! Your cache is running out of filedescriptors");
2681 last_warn = squid_curtime;
2682 }
2683
2684 return false;
2685 }
2686
2687 ConnStateData *
2688
2689 connStateCreate(const IPAddress &peer, const IPAddress &me, int fd, http_port_list *port)
2690 {
2691 ConnStateData *result = new ConnStateData;
2692 result->peer = peer;
2693 result->log_addr = peer;
2694 result->log_addr.ApplyMask(Config.Addrs.client_netmask.GetCIDR());
2695 result->me = me;
2696 result->fd = fd;
2697 result->in.buf = (char *)memAllocBuf(CLIENT_REQ_BUF_SZ, &result->in.allocatedSize);
2698 result->port = cbdataReference(port);
2699
2700 if (port->transparent)
2701 {
2702
2703 IPAddress dst;
2704
2705 if (clientNatLookup(fd, me, peer, dst) == 0) {
2706 result-> me = dst; /* XXX This should be moved to another field */
2707 result->transparent(true);
2708 }
2709 }
2710
2711 if (port->disable_pmtu_discovery != DISABLE_PMTU_OFF &&
2712 (result->transparent() || port->disable_pmtu_discovery == DISABLE_PMTU_ALWAYS))
2713 {
2714 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
2715 int i = IP_PMTUDISC_DONT;
2716 setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &i, sizeof i);
2717
2718 #else
2719
2720 static int reported = 0;
2721
2722 if (!reported) {
2723 debugs(33, 1, "Notice: httpd_accel_no_pmtu_disc not supported on your platform");
2724 reported = 1;
2725 }
2726
2727 #endif
2728
2729 }
2730
2731 result->flags.readMoreRequests = true;
2732 return result;
2733 }
2734
2735 /* Handle a new connection on HTTP socket. */
2736 void
2737 httpAccept(int sock, int newfd, ConnectionDetail *details,
2738 comm_err_t flag, int xerrno, void *data)
2739 {
2740 http_port_list *s = (http_port_list *)data;
2741 ConnStateData *connState = NULL;
2742
2743 if (flag == COMM_ERR_CLOSING) {
2744 return;
2745 }
2746
2747 if (!okToAccept())
2748 AcceptLimiter::Instance().defer (sock, httpAccept, data);
2749 else
2750 /* kick off another one for later */
2751 comm_accept(sock, httpAccept, data);
2752
2753 if (flag != COMM_OK) {
2754 debugs(33, 1, "httpAccept: FD " << sock << ": accept failure: " << xstrerr(xerrno));
2755 return;
2756 }
2757
2758 debugs(33, 4, "httpAccept: FD " << newfd << ": accepted");
2759 fd_note(newfd, "client http connect");
2760 connState = connStateCreate(details->peer, details->me, newfd, s);
2761 comm_add_close_handler(newfd, connStateClosed, connState);
2762
2763 if (Config.onoff.log_fqdn)
2764 fqdncache_gethostbyaddr(details->peer, FQDN_LOOKUP_IF_MISS);
2765
2766 commSetTimeout(newfd, Config.Timeout.request, requestTimeout, connState);
2767
2768 #if USE_IDENT
2769
2770 ACLChecklist identChecklist;
2771
2772 identChecklist.src_addr = details->peer;
2773
2774 identChecklist.my_addr = details->me;
2775
2776 identChecklist.accessList = cbdataReference(Config.accessList.identLookup);
2777
2778 /* cbdataReferenceDone() happens in either fastCheck() or ~ACLCheckList */
2779
2780 if (identChecklist.fastCheck())
2781 identStart(details->me, details->peer, clientIdentDone, connState);
2782
2783 #endif
2784
2785 connState->readSomeData();
2786
2787 clientdbEstablished(details->peer, 1);
2788
2789 incoming_sockets_accepted++;
2790 }
2791
2792 #if USE_SSL
2793
2794 /* negotiate an SSL connection */
2795 static void
2796 clientNegotiateSSL(int fd, void *data)
2797 {
2798 ConnStateData *conn = (ConnStateData *)data;
2799 X509 *client_cert;
2800 SSL *ssl = fd_table[fd].ssl;
2801 int ret;
2802
2803 if ((ret = SSL_accept(ssl)) <= 0) {
2804 int ssl_error = SSL_get_error(ssl, ret);
2805
2806 switch (ssl_error) {
2807
2808 case SSL_ERROR_WANT_READ:
2809 commSetSelect(fd, COMM_SELECT_READ, clientNegotiateSSL, conn, 0);
2810 return;
2811
2812 case SSL_ERROR_WANT_WRITE:
2813 commSetSelect(fd, COMM_SELECT_WRITE, clientNegotiateSSL, conn, 0);
2814 return;
2815
2816 case SSL_ERROR_SYSCALL:
2817
2818 if (ret == 0) {
2819 debugs(83, 2, "clientNegotiateSSL: Error negotiating SSL connection on FD " << fd << ": Aborted by client");
2820 comm_close(fd);
2821 return;
2822 } else {
2823 int hard = 1;
2824
2825 if (errno == ECONNRESET)
2826 hard = 0;
2827
2828 debugs(83, hard ? 1 : 2, "clientNegotiateSSL: Error negotiating SSL connection on FD " <<
2829 fd << ": " << strerror(errno) << " (" << errno << ")");
2830
2831 comm_close(fd);
2832
2833 return;
2834 }
2835
2836 case SSL_ERROR_ZERO_RETURN:
2837 debugs(83, 1, "clientNegotiateSSL: Error negotiating SSL connection on FD " << fd << ": Closed by client");
2838 comm_close(fd);
2839 return;
2840
2841 default:
2842 debugs(83, 1, "clientNegotiateSSL: Error negotiating SSL connection on FD " <<
2843 fd << ": " << ERR_error_string(ERR_get_error(), NULL) <<
2844 " (" << ssl_error << "/" << ret << ")");
2845 comm_close(fd);
2846 return;
2847 }
2848
2849 /* NOTREACHED */
2850 }
2851
2852 if (SSL_session_reused(ssl)) {
2853 debugs(83, 2, "clientNegotiateSSL: Session " << SSL_get_session(ssl) <<
2854 " reused on FD " << fd << " (" << fd_table[fd].ipaddr << ":" << (int)fd_table[fd].remote_port << ")");
2855 } else {
2856 if (do_debug(83, 4)) {
2857 /* Write out the SSL session details.. actually the call below, but
2858 * OpenSSL headers do strange typecasts confusing GCC.. */
2859 /* PEM_write_SSL_SESSION(debug_log, SSL_get_session(ssl)); */
2860 #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00908000L
2861 PEM_ASN1_write((i2d_of_void *)i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL);
2862
2863 #elif (ALLOW_ALWAYS_SSL_SESSION_DETAIL == 1)
2864
2865 /* When using gcc 3.3.x and OpenSSL 0.9.7x sometimes a compile error can occur here.
2866 * This is caused by an unpredicatble gcc behaviour on a cast of the first argument
2867 * of PEM_ASN1_write(). For this reason this code section is disabled. To enable it,
2868 * define ALLOW_ALWAYS_SSL_SESSION_DETAIL=1.
2869 * Because there are two possible usable cast, if you get an error here, try the other
2870 * commented line. */
2871
2872 PEM_ASN1_write((int(*)())i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL);
2873 /* PEM_ASN1_write((int(*)(...))i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL); */
2874
2875 #else
2876
2877 debugs(83, 4, "With " OPENSSL_VERSION_TEXT ", session details are available only defining ALLOW_ALWAYS_SSL_SESSION_DETAIL=1 in the source." );
2878
2879 #endif
2880 /* Note: This does not automatically fflush the log file.. */
2881 }
2882
2883 debugs(83, 2, "clientNegotiateSSL: New session " <<
2884 SSL_get_session(ssl) << " on FD " << fd << " (" <<
2885 fd_table[fd].ipaddr << ":" << (int)fd_table[fd].remote_port <<
2886 ")");
2887 }
2888
2889 debugs(83, 3, "clientNegotiateSSL: FD " << fd << " negotiated cipher " <<
2890 SSL_get_cipher(ssl));
2891
2892 client_cert = SSL_get_peer_certificate(ssl);
2893
2894 if (client_cert != NULL) {
2895 debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
2896 " client certificate: subject: " <<
2897 X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0));
2898
2899 debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
2900 " client certificate: issuer: " <<
2901 X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0));
2902
2903
2904 X509_free(client_cert);
2905 } else {
2906 debugs(83, 5, "clientNegotiateSSL: FD " << fd <<
2907 " has no certificate.");
2908 }
2909
2910 conn->readSomeData();
2911 }
2912
2913 /* handle a new HTTPS connection */
2914 static void
2915 httpsAccept(int sock, int newfd, ConnectionDetail *details,
2916 comm_err_t flag, int xerrno, void *data)
2917 {
2918 https_port_list *s = (https_port_list *)data;
2919 SSL_CTX *sslContext = s->sslContext;
2920 ConnStateData *connState = NULL;
2921 SSL *ssl;
2922 int ssl_error;
2923
2924 if (flag == COMM_ERR_CLOSING) {
2925 return;
2926 }
2927
2928 if (!okToAccept())
2929 AcceptLimiter::Instance().defer (sock, httpsAccept, data);
2930 else
2931 /* kick off another one for later */
2932 comm_accept(sock, httpsAccept, data);
2933
2934 if (flag != COMM_OK) {
2935 errno = xerrno;
2936 debugs(33, 1, "httpsAccept: FD " << sock << ": accept failure: " << xstrerr(xerrno));
2937 return;
2938 }
2939
2940 if ((ssl = SSL_new(sslContext)) == NULL) {
2941 ssl_error = ERR_get_error();
2942 debugs(83, 1, "httpsAccept: Error allocating handle: " << ERR_error_string(ssl_error, NULL) );
2943 comm_close(newfd);
2944 return;
2945 }
2946
2947 SSL_set_fd(ssl, newfd);
2948 fd_table[newfd].ssl = ssl;
2949 fd_table[newfd].read_method = &ssl_read_method;
2950 fd_table[newfd].write_method = &ssl_write_method;
2951
2952 debugs(33, 5, "httpsAccept: FD " << newfd << " accepted, starting SSL negotiation.");
2953 fd_note(newfd, "client https connect");
2954 connState = connStateCreate(details->peer, details->me, newfd, (http_port_list *)s);
2955 comm_add_close_handler(newfd, connStateClosed, connState);
2956
2957 if (Config.onoff.log_fqdn)
2958 fqdncache_gethostbyaddr(details->peer, FQDN_LOOKUP_IF_MISS);
2959
2960 commSetTimeout(newfd, Config.Timeout.request, requestTimeout, connState);
2961
2962 #if USE_IDENT
2963
2964 ACLChecklist identChecklist;
2965
2966 identChecklist.src_addr = details->peer;
2967
2968 identChecklist.my_addr = details->me;
2969
2970 identChecklist.accessList = cbdataReference(Config.accessList.identLookup);
2971
2972 /* cbdataReferenceDone() happens in either fastCheck() or ~ACLCheckList */
2973
2974 if (identChecklist.fastCheck())
2975 identStart(details->me, details->peer, clientIdentDone, connState);
2976
2977 #endif
2978
2979 commSetSelect(newfd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
2980
2981 clientdbEstablished(details->peer, 1);
2982
2983 incoming_sockets_accepted++;
2984 }
2985
2986 #endif /* USE_SSL */
2987
2988
2989 static void
2990 clientHttpConnectionsOpen(void)
2991 {
2992 http_port_list *s = NULL;
2993 int fd = -1;
2994
2995 for (s = Config.Sockaddr.http; s; s = s->next) {
2996 if (MAXHTTPPORTS == NHttpSockets) {
2997 debugs(1, 1, "WARNING: You have too many 'http_port' lines.");
2998 debugs(1, 1, " The limit is " << MAXHTTPPORTS);
2999 continue;
3000 }
3001
3002 enter_suid();
3003 fd = comm_open(SOCK_STREAM,
3004 IPPROTO_TCP,
3005 s->s,
3006 COMM_NONBLOCKING, "HTTP Socket");
3007 leave_suid();
3008
3009 if (fd < 0)
3010 continue;
3011
3012 comm_listen(fd);
3013
3014 comm_accept(fd, httpAccept, s);
3015
3016 debugs(1, 1, "Accepting " <<
3017 (s->transparent ? "transparently proxied" :
3018 s->accel ? "accelerated" : "" )
3019 << " HTTP connections at " << s->s
3020 << ", FD " << fd << "." );
3021
3022 HttpSockets[NHttpSockets++] = fd;
3023 }
3024 }
3025
3026 #if USE_SSL
3027 static void
3028 clientHttpsConnectionsOpen(void)
3029 {
3030 https_port_list *s;
3031 int fd;
3032
3033 for (s = Config.Sockaddr.https; s; s = (https_port_list *)s->http.next) {
3034 if (MAXHTTPPORTS == NHttpSockets) {
3035 debugs(1, 1, "WARNING: You have too many 'https_port' lines.");
3036 debugs(1, 1, " The limit is " << MAXHTTPPORTS);
3037 continue;
3038 }
3039
3040 if (s->sslContext == NULL) {
3041 debugs(1, 1, "Can not accept HTTPS connections at " << s->http.s);
3042 }
3043
3044 enter_suid();
3045 fd = comm_open(SOCK_STREAM,
3046 IPPROTO_TCP,
3047 s->http.s,
3048 COMM_NONBLOCKING, "HTTPS Socket");
3049 leave_suid();
3050
3051 if (fd < 0)
3052 continue;
3053
3054 comm_listen(fd);
3055
3056 comm_accept(fd, httpsAccept, s);
3057
3058 debugs(1, 1, "Accepting HTTPS connections at " << s->http.s << ", FD " << fd << ".");
3059
3060 HttpSockets[NHttpSockets++] = fd;
3061 }
3062 }
3063
3064 #endif
3065
3066 void
3067 clientOpenListenSockets(void)
3068 {
3069 clientHttpConnectionsOpen();
3070 #if USE_SSL
3071
3072 clientHttpsConnectionsOpen();
3073 #endif
3074
3075 if (NHttpSockets < 1)
3076 fatal("Cannot open HTTP Port");
3077 }
3078
3079 void
3080 clientHttpConnectionsClose(void)
3081 {
3082 int i;
3083
3084 for (i = 0; i < NHttpSockets; i++) {
3085 if (HttpSockets[i] >= 0) {
3086 debugs(1, 1, "FD " << HttpSockets[i] <<
3087 " Closing HTTP connection");
3088 comm_close(HttpSockets[i]);
3089 HttpSockets[i] = -1;
3090 }
3091 }
3092
3093 NHttpSockets = 0;
3094 }
3095
3096 int
3097 varyEvaluateMatch(StoreEntry * entry, HttpRequest * request)
3098 {
3099 const char *vary = request->vary_headers;
3100 int has_vary = entry->getReply()->header.has(HDR_VARY);
3101 #if X_ACCELERATOR_VARY
3102
3103 has_vary |=
3104 entry->getReply()->header.has(HDR_X_ACCELERATOR_VARY);
3105 #endif
3106
3107 if (!has_vary || !entry->mem_obj->vary_headers) {
3108 if (vary) {
3109 /* Oops... something odd is going on here.. */
3110 debugs(33, 1, "varyEvaluateMatch: Oops. Not a Vary object on second attempt, '" <<
3111 entry->mem_obj->url << "' '" << vary << "'");
3112 safe_free(request->vary_headers);
3113 return VARY_CANCEL;
3114 }
3115
3116 if (!has_vary) {
3117 /* This is not a varying object */
3118 return VARY_NONE;
3119 }
3120
3121 /* virtual "vary" object found. Calculate the vary key and
3122 * continue the search
3123 */
3124 vary = httpMakeVaryMark(request, entry->getReply());
3125
3126 if (vary) {
3127 request->vary_headers = xstrdup(vary);
3128 return VARY_OTHER;
3129 } else {
3130 /* Ouch.. we cannot handle this kind of variance */
3131 /* XXX This cannot really happen, but just to be complete */
3132 return VARY_CANCEL;
3133 }
3134 } else {
3135 if (!vary) {
3136 vary = httpMakeVaryMark(request, entry->getReply());
3137
3138 if (vary)
3139 request->vary_headers = xstrdup(vary);
3140 }
3141
3142 if (!vary) {
3143 /* Ouch.. we cannot handle this kind of variance */
3144 /* XXX This cannot really happen, but just to be complete */
3145 return VARY_CANCEL;
3146 } else if (strcmp(vary, entry->mem_obj->vary_headers) == 0) {
3147 return VARY_MATCH;
3148 } else {
3149 /* Oops.. we have already been here and still haven't
3150 * found the requested variant. Bail out
3151 */
3152 debugs(33, 1, "varyEvaluateMatch: Oops. Not a Vary match on second attempt, '" <<
3153 entry->mem_obj->url << "' '" << vary << "'");
3154 return VARY_CANCEL;
3155 }
3156 }
3157 }
3158
3159 ACLChecklist *
3160 clientAclChecklistCreate(const acl_access * acl, ClientHttpRequest * http)
3161 {
3162 ACLChecklist *ch;
3163 ConnStateData::Pointer conn = http->getConn();
3164 ch = aclChecklistCreate(acl, http->request, conn.getRaw() != NULL ? conn->rfc931 : dash_str);
3165
3166 /*
3167 * hack for ident ACL. It needs to get full addresses, and a place to store
3168 * the ident result on persistent connections...
3169 */
3170 /* connection oriented auth also needs these two lines for it's operation. */
3171 /*
3172 * Internal requests do not have a connection reference, because: A) their
3173 * byte count may be transformed before being applied to an outbound
3174 * connection B) they are internal - any limiting on them should be done on
3175 * the server end.
3176 */
3177
3178 if (conn.getRaw() != NULL)
3179 ch->conn(conn); /* unreferenced in acl.cc */
3180
3181 return ch;
3182 }
3183
3184 CBDATA_CLASS_INIT(ConnStateData);
3185
3186 ConnStateData::ConnStateData() : transparent_ (false), reading_ (false), closing_ (false)
3187 {
3188 openReference = this;
3189 }
3190
3191 bool
3192 ConnStateData::transparent() const
3193 {
3194 return transparent_;
3195 }
3196
3197 void
3198 ConnStateData::transparent(bool const anInt)
3199 {
3200 transparent_ = anInt;
3201 }
3202
3203 bool
3204 ConnStateData::reading() const
3205 {
3206 return reading_;
3207 }
3208
3209 void
3210 ConnStateData::reading(bool const newBool)
3211 {
3212 assert (reading() != newBool);
3213 reading_ = newBool;
3214 }
3215
3216
3217 BodyPipe::Pointer
3218 ConnStateData::expectRequestBody(int64_t size)
3219 {
3220 bodyPipe = new BodyPipe(this);
3221 bodyPipe->setBodySize(size);
3222 return bodyPipe;
3223 }
3224
3225 bool
3226 ConnStateData::closing() const
3227 {
3228 return closing_;
3229 }
3230
3231 // Called by ClientSocketContext to give the connection a chance to read
3232 // the entire body before closing the socket.
3233 void
3234 ConnStateData::startClosing(const char *reason)
3235 {
3236 debugs(33, 5, HERE << "startClosing " << this << " for " << reason);
3237 assert(!closing());
3238 closing_ = true;
3239
3240 assert(bodyPipe != NULL);
3241 assert(bodySizeLeft() > 0);
3242
3243 // We do not have to abort the body pipeline because we are going to
3244 // read the entire body anyway.
3245 // Perhaps an ICAP server wants to log the complete request.
3246
3247 // If a consumer abort have caused this closing, we may get stuck
3248 // as nobody is consuming our data. Allow auto-consumption.
3249 bodyPipe->enableAutoConsumption();
3250 }
3251
3252 char *
3253 ConnStateData::In::addressToReadInto() const
3254 {
3255 return buf + notYetUsed;
3256 }
3257
3258 ConnStateData::In::In() : buf (NULL), notYetUsed (0), allocatedSize (0)
3259 {}
3260
3261 ConnStateData::In::~In()
3262 {
3263 if (allocatedSize)
3264 memFreeBuf(allocatedSize, buf);
3265 }