3 * $Id: client_side.cc,v 1.771 2007/12/14 23:11:46 amosjeffries Exp $
5 * DEBUG: section 33 Client-side Routines
6 * AUTHOR: Duane Wessels
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
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.
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.
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.
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.
36 /* Errors and client side
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.
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.
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.
59 #include "client_side.h"
60 #include "clientStream.h"
61 #include "IPInterception.h"
62 #include "AuthUserRequest.h"
65 #include "HttpHdrContRange.h"
66 #include "HttpReply.h"
67 #include "HttpRequest.h"
68 #include "MemObject.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"
76 #include "SquidTime.h"
79 #define comm_close comm_lingering_close
82 /* Persistent connection logic:
84 * requests (httpClientRequest structs) get added to the connection
85 * list, with the current one being chr
87 * The request is *immediately* kicked off, and data flows through
88 * to clientSocketRecipient.
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.
94 * ClientKeepAliveNextRequest will then detect the presence of data in
95 * the next ClientHttpRequest, and will send it, restablishing the
99 /* our socket-related context */
102 CBDATA_CLASS_INIT(ClientSocketContext
);
105 ClientSocketContext::operator new (size_t byteCount
)
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
);
114 ClientSocketContext::operator delete (void *address
)
116 cbdataFree (address
);
119 /* Local functions */
120 /* ClientSocketContext */
121 static ClientSocketContext
*ClientSocketContextNew(ClientHttpRequest
*);
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
,
133 static ClientSocketContext
*parseHttpRequest(ConnStateData::Pointer
&, HttpParser
*, method_t
*, HttpVersion
*);
135 static IDCB clientIdentDone
;
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
);
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
*);
151 static int connIsUsable(ConnStateData::Pointer conn
);
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
);
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
);
164 static ConnStateData
*connStateCreate(const IPAddress
&peer
, const IPAddress
&me
, int fd
, http_port_list
*port
);
167 ClientSocketContext::fd() const
170 assert (http
->getConn() != NULL
);
171 return http
->getConn()->fd
;
175 ClientSocketContext::getTail() const
177 if (http
->client_stream
.tail
)
178 return (clientStreamNode
*)http
->client_stream
.tail
->data
;
184 ClientSocketContext::getClientReplyContext() const
186 return (clientStreamNode
*)http
->client_stream
.tail
->prev
->data
;
190 * This routine should be called to grow the inbuf and then
194 ConnStateData::readSomeData()
201 debugs(33, 4, "clientReadSomeData: FD " << fd
<< ": reading request...");
203 makeSpaceAvailable();
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
));
209 comm_read(fd
, in
.addressToReadInto(), getAvailableBufferLength(), clientReadRequest
, this);
214 ClientSocketContext::removeFromConnectionList(ConnStateData::Pointer conn
)
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
;
222 while (tempContextPointer
->getRaw()) {
223 if (*tempContextPointer
== this)
226 tempContextPointer
= &(*tempContextPointer
)->next
;
229 assert(tempContextPointer
->getRaw() != NULL
);
230 *tempContextPointer
= next
;
234 ClientSocketContext::~ClientSocketContext()
236 clientStreamNode
*node
= getTail();
239 ClientSocketContext
*streamContext
= dynamic_cast<ClientSocketContext
*> (node
->data
.getRaw());
242 /* We are *always* the tail - prevent recursive free */
243 assert(this == streamContext
);
249 deRegisterWithConn();
251 httpRequestFree(http
);
253 /* clean up connection links to us */
254 assert(this != next
.getRaw());
258 ClientSocketContext::registerWithConn()
260 assert (!connRegistered_
);
262 assert (http
->getConn() != NULL
);
263 connRegistered_
= true;
264 http
->getConn()->addContextToQueue(this);
268 ClientSocketContext::deRegisterWithConn()
270 assert (connRegistered_
);
271 removeFromConnectionList(http
->getConn());
272 connRegistered_
= false;
276 ClientSocketContext::connIsFinished()
279 assert (http
->getConn() != NULL
);
280 deRegisterWithConn();
281 /* we can't handle any more stream data - detach */
282 clientStreamDetach(getTail(), http
);
285 ClientSocketContext::ClientSocketContext() : http(NULL
), reply(NULL
), next(NULL
),
287 mayUseConnection_ (false),
288 connRegistered_ (false)
290 memset (reqbuf
, '\0', sizeof (reqbuf
));
293 deferredparams
.node
= NULL
;
294 deferredparams
.rep
= NULL
;
297 ClientSocketContext
*
298 ClientSocketContextNew(ClientHttpRequest
* http
)
300 ClientSocketContext
*newContext
;
301 assert(http
!= NULL
);
302 newContext
= new ClientSocketContext
;
303 newContext
->http
= http
;
309 clientIdentDone(const char *ident
, void *data
)
311 ConnStateData
*conn
= (ConnStateData
*)data
;
312 xstrncpy(conn
->rfc931
, ident
? ident
: dash_str
, USER_IDENT_SZ
);
318 clientUpdateStatCounters(log_type logType
)
320 statCounter
.client_http
.requests
++;
322 if (logTypeIsATcpHit(logType
))
323 statCounter
.client_http
.hits
++;
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
++;
332 clientUpdateStatHistCounters(log_type logType
, int svc_time
)
334 statHistCount(&statCounter
.client_http
.all_svc_time
, svc_time
);
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).
344 case LOG_TCP_REFRESH_UNMODIFIED
:
345 statHistCount(&statCounter
.client_http
.nh_svc_time
, svc_time
);
348 case LOG_TCP_IMS_HIT
:
349 statHistCount(&statCounter
.client_http
.nm_svc_time
, svc_time
);
354 case LOG_TCP_MEM_HIT
:
356 case LOG_TCP_OFFLINE_HIT
:
357 statHistCount(&statCounter
.client_http
.hit_svc_time
, svc_time
);
362 case LOG_TCP_CLIENT_REFRESH_MISS
:
363 statHistCount(&statCounter
.client_http
.miss_svc_time
, svc_time
);
367 /* make compiler warnings go away */
373 clientPingHasFinished(ping_data
const *aPing
)
375 if (0 != aPing
->stop
.tv_sec
&& 0 != aPing
->start
.tv_sec
)
382 clientUpdateHierCounters(HierarchyLogEntry
* someEntry
)
386 switch (someEntry
->code
) {
387 #if USE_CACHE_DIGESTS
392 statCounter
.cd
.times_used
++;
400 case FIRST_PARENT_MISS
:
402 case CLOSEST_PARENT_MISS
:
403 statCounter
.icp
.times_used
++;
404 i
= &someEntry
->ping
;
406 if (clientPingHasFinished(i
))
407 statHistCount(&statCounter
.icp
.query_svc_time
,
408 tvSubUsec(i
->start
, i
->stop
));
411 statCounter
.icp
.query_timeouts
++;
418 statCounter
.netdb
.times_used
++;
428 ClientHttpRequest::updateCounters()
430 clientUpdateStatCounters(logType
);
432 if (request
->errType
!= ERR_NONE
)
433 statCounter
.client_http
.errors
++;
435 clientUpdateStatHistCounters(logType
,
436 tvSubMsec(start
, current_time
));
438 clientUpdateHierCounters(&request
->hier
);
442 clientPrepareLogWithRequestDetails(HttpRequest
* request
, AccessLogEntry
* aLogEntry
)
447 if (Config
.onoff
.log_mime_hdrs
) {
451 packerToMemInit(&p
, &mb
);
452 request
->header
.packInto(&p
);
453 aLogEntry
->headers
.request
= xstrdup(mb
.buf
);
458 aLogEntry
->http
.method
= request
->method
;
459 aLogEntry
->http
.version
= request
->http_ver
;
460 aLogEntry
->hier
= request
->hier
;
462 aLogEntry
->cache
.extuser
= request
->extacl_user
.buf();
464 if (request
->auth_user_request
) {
466 if (request
->auth_user_request
->username())
467 aLogEntry
->cache
.authuser
=
468 xstrdup(request
->auth_user_request
->username());
470 AUTHUSERREQUESTUNLOCK(request
->auth_user_request
, "request via clientPrepareLogWithRequestDetails");
475 ClientHttpRequest::logRequest()
477 if (out
.size
|| logType
) {
478 al
.icp
.opcode
= ICP_INVALID
;
480 debugs(33, 9, "clientLogRequest: al.url='" << al
.url
<< "'");
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();
490 debugs(33, 9, "clientLogRequest: http.code='" << al
.http
.code
<< "'");
492 if (loggingEntry() && loggingEntry()->mem_obj
)
493 al
.cache
.objectSize
= loggingEntry()->contentLen();
495 al
.cache
.caddr
.SetNoAddr();
497 if(getConn() != NULL
) al
.cache
.caddr
= getConn()->log_addr
;
499 al
.cache
.size
= out
.size
;
501 al
.cache
.highOffset
= out
.offset
;
503 al
.cache
.code
= logType
;
505 al
.cache
.msec
= tvSubMsec(start
, current_time
);
508 clientPrepareLogWithRequestDetails(request
, &al
);
510 if (getConn() != NULL
&& getConn()->rfc931
[0])
511 al
.cache
.rfc931
= getConn()->rfc931
;
515 /* This is broken. Fails if the connection has been closed. Needs
516 * to snarf the ssl details some place earlier..
518 if (getConn() != NULL
)
519 al
.cache
.ssluser
= sslGetUserEmail(fd_table
[getConn()->fd
].ssl
);
523 ACLChecklist
*checklist
= clientAclChecklistCreate(Config
.accessList
.log
, this);
526 checklist
->reply
= HTTPMSGLOCK(al
.reply
);
528 if (!Config
.accessList
.log
|| checklist
->fastCheck()) {
529 al
.request
= HTTPMSGLOCK(request
);
530 accessLogLog(&al
, checklist
);
533 if (getConn() != NULL
)
534 clientdbUpdate(getConn()->peer
, logType
, PROTO_HTTP
, out
.size
);
539 accessLogFreeMemory(&al
);
544 ClientHttpRequest::freeResources()
548 safe_free(redirect
.location
);
549 range_iter
.boundary
.clean();
550 HTTPMSGUNLOCK(request
);
552 if (client_stream
.tail
)
553 clientStreamAbort((clientStreamNode
*)client_stream
.tail
->data
, this);
557 httpRequestFree(void *data
)
559 ClientHttpRequest
*http
= (ClientHttpRequest
*)data
;
560 assert(http
!= NULL
);
565 ConnStateData::areAllContextsForThisConnection() const
567 assert(this != NULL
);
568 ClientSocketContext::Pointer context
= getCurrentContext();
570 while (context
.getRaw()) {
571 if (context
->http
->getConn() != this)
574 context
= context
->next
;
581 ConnStateData::freeAllContexts()
583 ClientSocketContext::Pointer context
;
585 while ((context
= getCurrentContext()).getRaw() != NULL
) {
586 assert(getCurrentContext() !=
587 getCurrentContext()->next
);
588 context
->connIsFinished();
589 assert (context
!= currentobject
);
593 /* This is a handler normally called by comm_close() */
595 connStateClosed(int fd
, void *data
)
597 ConnStateData
*connState
= (ConnStateData
*)data
;
598 assert (fd
== connState
->fd
);
603 ConnStateData::close()
605 debugs(33, 3, "ConnStateData::close: FD " << fd
);
606 openReference
= NULL
;
608 flags
.readMoreRequests
= false;
609 clientdbEstablished(peer
, -1); /* decrement */
610 assert(areAllContextsForThisConnection());
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);
620 ConnStateData::isOpen() const
622 return openReference
!= NULL
;
625 ConnStateData::~ConnStateData()
627 assert(this != NULL
);
628 debugs(33, 3, "ConnStateData::~ConnStateData: FD " << fd
);
633 AUTHUSERREQUESTUNLOCK(auth_user_request
, "~conn");
635 cbdataReferenceDone(port
);
637 if (bodyPipe
!= NULL
) {
638 bodyPipe
->clearProducer(false);
639 bodyPipe
= NULL
; // refcounted
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.
650 clientSetKeepaliveFlag(ClientHttpRequest
* http
)
652 HttpRequest
*request
= http
->request
;
653 const HttpHeader
*req_hdr
= &request
->header
;
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
]);
660 HttpVersion
http_ver(1,0);
661 /* we are HTTP/1.0, no matter what the client requests... */
663 if (httpMsgIsPersistent(http_ver
, req_hdr
))
664 request
->flags
.proxy_keepalive
= 1;
668 clientIsContentLengthValid(HttpRequest
* r
)
675 /* PUT/POST requires a request entity */
676 return (r
->content_length
>= 0);
681 /* We do not want to see a request entity on GET/HEAD requests */
682 return (r
->content_length
<= 0 || Config
.onoff
.request_entities
);
685 /* For other types of requests we don't care */
693 clientIsRequestBodyValid(int64_t bodyLength
)
702 clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength
)
704 if (Config
.maxRequestBodySize
&&
705 bodyLength
> Config
.maxRequestBodySize
)
706 return 1; /* too large */
713 connIsUsable(ConnStateData::Pointer conn
)
715 if (conn
== NULL
|| conn
->fd
== -1)
723 ClientSocketContext::Pointer
724 ConnStateData::getCurrentContext() const
727 return currentobject
;
731 ClientSocketContext::deferRecipientForLater(clientStreamNode
* node
, HttpReply
* rep
, StoreIOBuffer receivedData
)
733 debugs(33, 2, "clientSocketRecipient: Deferring request " << http
->uri
);
734 assert(flags
.deferred
== 0);
736 deferredparams
.node
= node
;
737 deferredparams
.rep
= rep
;
738 deferredparams
.queuedBuffer
= receivedData
;
743 responseFinishedOrFailed(HttpReply
* rep
, StoreIOBuffer
const & receivedData
)
745 if (rep
== NULL
&& receivedData
.data
== NULL
&& receivedData
.length
== 0)
752 ClientSocketContext::startOfOutput() const
754 return http
->out
.size
== 0;
758 ClientSocketContext::lengthToSend(Range
<int64_t> const &available
)
760 /*the size of available range can always fit in a size_t type*/
761 size_t maximum
= (size_t)available
.size();
763 if (!http
->request
->range
)
766 assert (canPackMoreRanges());
768 if (http
->range_iter
.debt() == -1)
771 assert (http
->range_iter
.debt() > 0);
773 /* TODO this + the last line could be a range intersection calculation */
774 if (available
.start
< http
->range_iter
.currentSpec()->offset
)
777 return XMIN(http
->range_iter
.debt(), (int64_t)maximum
);
781 ClientSocketContext::noteSentBodyBytes(size_t bytes
)
783 http
->out
.offset
+= bytes
;
785 if (!http
->request
->range
)
788 if (http
->range_iter
.debt() != -1) {
789 http
->range_iter
.debt(http
->range_iter
.debt() - bytes
);
790 assert (http
->range_iter
.debt() >= 0);
793 /* debt() always stops at -1, below that is a bug */
794 assert (http
->range_iter
.debt() >= -1);
798 ClientHttpRequest::multipartRangeRequest() const
800 return request
->multipartRangeRequest();
804 ClientSocketContext::multipartRangeRequest() const
806 return http
->multipartRangeRequest();
810 ClientSocketContext::sendBody(HttpReply
* rep
, StoreIOBuffer bodyData
)
814 if (!multipartRangeRequest()) {
815 size_t length
= lengthToSend(bodyData
.range());
816 noteSentBodyBytes (length
);
817 comm_write(fd(), bodyData
.data
, length
, clientWriteBodyComplete
, this, NULL
);
823 packRange(bodyData
, &mb
);
825 if (mb
.contentSize())
827 comm_write_mbuf(fd(), &mb
, clientWriteComplete
, this);
829 writeComplete(fd(), NULL
, 0, COMM_OK
);
832 /* put terminating boundary for multiparts */
834 clientPackTermBound(String boundary
, MemBuf
* mb
)
836 mb
->Printf("\r\n--%s--\r\n", boundary
.buf());
837 debugs(33, 6, "clientPackTermBound: buf offset: " << mb
->size
);
840 /* appends a "part" HTTP header (as in a multi-part/range reply) to the buffer */
842 clientPackRangeHdr(const HttpReply
* rep
, const HttpHdrRangeSpec
* spec
, String boundary
, MemBuf
* mb
)
844 HttpHeader
hdr(hoReply
);
850 debugs(33, 5, "clientPackRangeHdr: appending boundary: " <<
852 /* rfc2046 requires to _prepend_ boundary with <crlf>! */
853 mb
->Printf("\r\n--%s\r\n", boundary
.buf());
855 /* stuff the header with required entries and pack it */
857 if (rep
->header
.has(HDR_CONTENT_TYPE
))
858 hdr
.putStr(HDR_CONTENT_TYPE
, rep
->header
.getStr(HDR_CONTENT_TYPE
));
860 httpHeaderAddContRange(&hdr
, *spec
, rep
->content_length
);
862 packerToMemInit(&p
, mb
);
870 /* append <crlf> (we packed a header, not a reply) */
875 * extracts a "range" from *buf and appends them to mb, updating
876 * all offsets and such.
879 ClientSocketContext::packRange(StoreIOBuffer
const &source
, MemBuf
* mb
)
881 HttpHdrRangeIter
* i
= &http
->range_iter
;
882 Range
<int64_t> available (source
.range());
883 char const *buf
= source
.data
;
885 while (i
->currentSpec() && available
.size()) {
886 const size_t copy_sz
= lengthToSend(available
);
890 * intersection of "have" and "need" ranges must not be empty
892 assert(http
->out
.offset
< i
->currentSpec()->offset
+ i
->currentSpec()->length
);
893 assert(http
->out
.offset
+ available
.size() > i
->currentSpec()->offset
);
896 * put boundary and headers at the beginning of a range in a
900 if (http
->multipartRangeRequest() && i
->debt() == i
->currentSpec()->length
) {
901 assert(http
->memObject());
903 http
->memObject()->getReply(), /* original reply */
904 i
->currentSpec(), /* current range */
905 i
->boundary
, /* boundary, the same for all */
912 debugs(33, 3, "clientPackRange: appending " << copy_sz
<< " bytes");
914 noteSentBodyBytes (copy_sz
);
916 mb
->append(buf
, copy_sz
);
921 available
.start
+= copy_sz
;
930 assert((available
.size() >= 0 && i
->debt() >= 0) || i
->debt() == -1);
932 if (!canPackMoreRanges()) {
933 debugs(33, 3, "clientPackRange: Returning because !canPackMoreRanges.");
936 /* put terminating boundary for multiparts */
937 clientPackTermBound(i
->boundary
, mb
);
942 int64_t next
= getNextRangeOffset();
944 assert (next
>= http
->out
.offset
);
946 int64_t skip
= next
- http
->out
.offset
;
948 /* adjust for not to be transmitted bytes */
949 http
->out
.offset
= next
;
951 if (available
.size() <= skip
)
954 available
.start
+= skip
;
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 */
969 ClientHttpRequest::mRangeCLen()
977 HttpHdrRange::iterator pos
= request
->range
->begin();
979 while (pos
!= request
->range
->end()) {
980 /* account for headers for this range */
982 clientPackRangeHdr(memObject()->getReply(),
983 *pos
, range_iter
.boundary
, &mb
);
986 /* account for range content */
987 clen
+= (*pos
)->length
;
989 debugs(33, 6, "clientMRangeCLen: (clen += " << mb
.size
<< " + " << (*pos
)->length
<< ") == " << clen
);
993 /* account for the terminating boundary */
996 clientPackTermBound(range_iter
.boundary
, &mb
);
1006 * returns true if If-Range specs match reply, false otherwise
1009 clientIfRangeMatch(ClientHttpRequest
* http
, HttpReply
* rep
)
1011 const TimeOrTag spec
= http
->request
->header
.getTimeOrTag(HDR_IF_RANGE
);
1012 /* check for parsing falure */
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>"));
1024 return 0; /* entity has no etag to compare with! */
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 */
1031 return etagIsEqual(&rep_tag
, &spec
.tag
);
1034 /* got modification time? */
1035 if (spec
.time
>= 0) {
1036 return http
->storeEntry()->lastmod
<= spec
.time
;
1039 assert(0); /* should not happen */
1043 /* generates a "unique" boundary string for multipart responses
1044 * the caller is responsible for cleaning the string */
1046 ClientHttpRequest::rangeBoundaryStr() const
1050 String
b (full_appname_string
);
1052 key
= storeEntry()->getMD5Text();
1053 b
.append(key
, strlen(key
));
1057 /* adds appropriate Range headers if needed */
1059 ClientSocketContext::buildRangeHeader(HttpReply
* rep
)
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 */
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? */
1078 /* hits only - upstream peer determines correct behaviour on misses, and client_side_reply determines
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";
1090 /* get rid of our range specs on error */
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
1097 debugs(33, 3, "clientBuildRangeHeader: will not do ranges: " << range_err
<< ".");
1098 delete http
->request
->range
;
1099 http
->request
->range
= NULL
;
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
) :
1110 const int spec_count
= http
->request
->range
->specs
.count
;
1111 int64_t actual_clen
= -1;
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) */
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
;
1130 HttpHdrRange::iterator pos
= http
->request
->range
->begin();
1132 /* append Content-Range */
1134 if (!hdr
->has(HDR_CONTENT_RANGE
)) {
1135 /* No content range, so this was a full object we are
1138 httpHeaderAddContRange(hdr
, **pos
, rep
->content_length
);
1141 /* set new Content-Length to the actual number of bytes
1142 * transmitted in the message-body */
1143 actual_clen
= (*pos
)->length
;
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
;
1161 /* replace Content-Length header */
1162 assert(actual_clen
>= 0);
1164 hdr
->delById(HDR_CONTENT_LENGTH
);
1166 hdr
->putInt64(HDR_CONTENT_LENGTH
, actual_clen
);
1168 debugs(33, 3, "clientBuildRangeHeader: actual content length: " << actual_clen
);
1170 /* And start the range iter off */
1171 http
->range_iter
.updateSpec();
1176 ClientSocketContext::prepareReply(HttpReply
* rep
)
1180 if (http
->request
->range
)
1181 buildRangeHeader(rep
);
1185 ClientSocketContext::sendStartOfMessage(HttpReply
* rep
, StoreIOBuffer bodyData
)
1189 MemBuf
*mb
= rep
->pack();
1190 /* Save length of headers for persistent conn checks */
1191 http
->out
.headers_sz
= mb
->contentSize();
1194 headersLog(0, 0, http
->request
->method
, rep
);
1197 if (bodyData
.data
&& bodyData
.length
) {
1198 if (!multipartRangeRequest()) {
1199 size_t length
= lengthToSend(bodyData
.range());
1200 noteSentBodyBytes (length
);
1202 mb
->append(bodyData
.data
, length
);
1204 packRange(bodyData
, mb
);
1209 debugs(33,7, HERE
<< "sendStartOfMessage schedules clientWriteComplete");
1210 comm_write_mbuf(fd(), mb
, clientWriteComplete
, this);
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
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.
1225 clientSocketRecipient(clientStreamNode
* node
, ClientHttpRequest
* http
,
1226 HttpReply
* rep
, StoreIOBuffer receivedData
)
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.
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 */
1245 if (context
!= http
->getConn()->getCurrentContext()) {
1246 context
->deferRecipientForLater(node
, rep
, receivedData
);
1247 PROF_stop(clientSocketRecipient
);
1251 if (responseFinishedOrFailed(rep
, receivedData
)) {
1252 context
->writeComplete(fd
, NULL
, 0, COMM_OK
);
1253 PROF_stop(clientSocketRecipient
);
1257 if (!context
->startOfOutput())
1258 context
->sendBody(rep
, receivedData
);
1261 http
->al
.reply
= HTTPMSGLOCK(rep
);
1262 context
->sendStartOfMessage(rep
, receivedData
);
1265 PROF_stop(clientSocketRecipient
);
1268 /* Called when a downstream node is no longer interested in
1269 * our data. As we are a terminal node, this means on aborts
1273 clientSocketDetach(clientStreamNode
* node
, ClientHttpRequest
* http
)
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.
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
1290 clientStreamDetach(node
, http
);
1294 clientWriteBodyComplete(int fd
, char *buf
, size_t size
, comm_err_t errflag
, int xerrno
, void *data
)
1296 debugs(33,7, HERE
<< "clientWriteBodyComplete schedules clientWriteComplete");
1297 clientWriteComplete(fd
, NULL
, size
, errflag
, xerrno
, data
);
1301 ConnStateData::readNextRequest()
1303 debugs(33, 5, "ConnStateData::readNextRequest: FD " << fd
<< " reading next req");
1305 fd_note(fd
, "Waiting for next request");
1307 * Set the timeout BEFORE calling clientReadRequest().
1309 commSetTimeout(fd
, Config
.Timeout
.persistent_request
,
1310 requestTimeout
, this);
1312 /* Please don't do anything with the FD past here! */
1316 ClientSocketContextPushDeferredIfNeeded(ClientSocketContext::Pointer deferredRequest
, ConnStateData::Pointer
& conn
)
1318 debugs(33, 2, "ClientSocketContextPushDeferredIfNeeded: FD " << conn
->fd
<< " Sending next");
1320 /* If the client stream is waiting on a socket write to occur, then */
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
);
1331 /* otherwise, the request is still active in a callbacksomewhere,
1337 ClientSocketContext::keepaliveNextRequest()
1339 ConnStateData::Pointer conn
= http
->getConn();
1340 bool do_next_read
= false;
1342 debugs(33, 3, "ClientSocketContext::keepaliveNextRequest: FD " << conn
->fd
);
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.
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.
1354 if (clientParseRequest(conn
, do_next_read
)) {
1355 debugs(33, 3, "clientSocketContext::keepaliveNextRequest: FD " << conn
->fd
<< ": parsed next request from buffer");
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.
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
);
1371 ClientSocketContext::Pointer deferredRequest
;
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
1381 if ((deferredRequest
= conn
->getCurrentContext()).getRaw()) {
1382 debugs(33, 3, "ClientSocketContext:: FD " << conn
->fd
<< ": calling PushDeferredIfNeeded");
1383 ClientSocketContextPushDeferredIfNeeded(deferredRequest
, conn
);
1385 debugs(33, 3, "ClientSocketContext:: FD " << conn
->fd
<< ": calling conn->readNextRequest()");
1386 conn
->readNextRequest();
1391 clientUpdateSocketStats(log_type logType
, size_t size
)
1396 kb_incr(&statCounter
.client_http
.kbytes_out
, size
);
1398 if (logTypeIsATcpHit(logType
))
1399 kb_incr(&statCounter
.client_http
.hit_kbytes_out
, size
);
1402 /* returns true if there is still data available to pack more ranges
1403 * increments iterator "i"
1404 * used by clientPackMoreRanges */
1406 ClientSocketContext::canPackMoreRanges() const
1408 /* first update "i" if needed */
1410 if (!http
->range_iter
.debt()) {
1411 debugs(33, 5, "ClientSocketContext::canPackMoreRanges: At end of current range spec for FD " <<
1414 if (http
->range_iter
.pos
.incrementable())
1415 ++http
->range_iter
.pos
;
1417 http
->range_iter
.updateSpec();
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;
1428 ClientSocketContext::getNextRangeOffset() const
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());
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 */
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
;
1462 return http
->out
.offset
;
1466 ClientSocketContext::pullData()
1468 debugs(33, 5, "ClientSocketContext::pullData: FD " << fd() << " attempting to pull upstream data");
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
);
1481 clientStream_status_t
1482 ClientSocketContext::socketState()
1484 switch (clientStreamStatus(getTail(), http
)) {
1487 /* check for range support ending */
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 */
1494 if (!canPackMoreRanges()) {
1495 debugs(33, 5, "ClientSocketContext::socketState: Range request has hit end of returnable range sequence on FD " <<
1498 if (http
->request
->flags
.proxy_keepalive
)
1499 return STREAM_COMPLETE
;
1501 return STREAM_UNPLANNED_COMPLETE
;
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
;
1508 debugs(33, 7, HERE
<< "body bytes sent vs. expected: " <<
1509 bytesSent
<< " ? " << bytesExpected
<< " (+" <<
1510 reply
->content_range
->spec
.offset
<< ")");
1512 // did we get at least what we expected, based on range specs?
1514 if (bytesSent
== bytesExpected
) // got everything
1515 return STREAM_COMPLETE
;
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
;
1524 return STREAM_UNPLANNED_COMPLETE
;
1527 // did not get enough yet, expecting more
1532 case STREAM_COMPLETE
:
1533 return STREAM_COMPLETE
;
1535 case STREAM_UNPLANNED_COMPLETE
:
1536 return STREAM_UNPLANNED_COMPLETE
;
1539 return STREAM_FAILED
;
1542 fatal ("unreachable code\n");
1546 /* A write has just completed to the client, or we have just realised there is
1547 * no more data to send.
1550 clientWriteComplete(int fd
, char *bufnotused
, size_t size
, comm_err_t errflag
, int xerrno
, void *data
)
1552 ClientSocketContext
*context
= (ClientSocketContext
*)data
;
1553 context
->writeComplete (fd
, bufnotused
, size
, errflag
);
1557 ClientSocketContext::doClose()
1563 ClientSocketContext::initiateClose(const char *reason
)
1565 debugs(33, 5, HERE
<< "initiateClose: closing for " << reason
);
1568 ConnStateData::Pointer conn
= http
->getConn();
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");
1577 if (conn
->closing()) {
1578 debugs(33, 2, HERE
<< "avoiding double-closing " << conn
);
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.
1590 conn
->startClosing(reason
);
1601 ClientSocketContext::writeComplete(int fd
, char *bufnotused
, size_t size
, comm_err_t errflag
)
1603 StoreEntry
*entry
= http
->storeEntry();
1604 http
->out
.size
+= size
;
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
);
1612 /* Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up */
1614 if (errflag
== COMM_ERR_CLOSING
)
1617 if (errflag
|| clientHttpRequestStatus(fd
, http
)) {
1618 initiateClose("failure or true request status");
1619 /* Do we leak here ? */
1623 switch (socketState()) {
1629 case STREAM_COMPLETE
:
1630 debugs(33, 5, "clientWriteComplete: FD " << fd
<< " Keeping Alive");
1631 keepaliveNextRequest();
1634 case STREAM_UNPLANNED_COMPLETE
:
1638 initiateClose("STREAM_UNPLANNED_COMPLETE|STREAM_FAILED");
1642 fatal("Hit unreachable code in clientWriteComplete\n");
1646 extern "C" CSR clientGetMoreData
;
1647 extern "C" CSS clientReplyStatus
;
1648 extern "C" CSD clientReplyDetach
;
1650 static ClientSocketContext
*
1651 parseHttpRequestAbort(ConnStateData::Pointer
& conn
, const char *uri
)
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
);
1670 skipLeadingSpace(char *aString
)
1672 char *result
= aString
;
1674 while (xisspace(*aString
))
1681 * 'end' defaults to NULL for backwards compatibility
1682 * remove default value if we ever get rid of NULL-terminated
1686 findTrailingHTTPVersion(const char *uriAndHTTPVersion
, const char *end
)
1689 end
= uriAndHTTPVersion
+ strcspn(uriAndHTTPVersion
, "\r\n");
1693 for (; end
> uriAndHTTPVersion
; end
--) {
1694 if (*end
== '\n' || *end
== '\r')
1697 if (xisspace(*end
)) {
1698 if (strncasecmp(end
+ 1, "HTTP/", 5) == 0)
1709 setLogUri(ClientHttpRequest
* http
, char const *uri
)
1711 safe_free(http
->log_uri
);
1713 if (!stringHasCntl(uri
))
1714 http
->log_uri
= xstrndup(uri
, MAX_URL
);
1716 http
->log_uri
= xstrndup(rfc1738_escape_unescaped(uri
), MAX_URL
);
1720 prepareAcceleratedURL(ConnStateData::Pointer
& conn
, ClientHttpRequest
*http
, char *url
, const char *req_hdr
)
1722 int vhost
= conn
->port
->vhost
;
1723 int vport
= conn
->port
->vport
;
1725 char ntoabuf
[MAX_IPSTRLEN
];
1727 http
->flags
.accel
= 1;
1729 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
1731 if (strncasecmp(url
, "cache_object://", 15) == 0)
1732 return; /* already in good shape */
1735 if (conn
->port
->vhost
)
1736 return; /* already in good shape */
1738 /* else we need to ignore the host name */
1739 url
= strstr(url
, "//");
1741 #if SHOULD_REJECT_UNKNOWN_URLS
1744 return parseHttpRequestAbort(conn
, "error:invalid-request");
1749 url
= strchr(url
+ 2, '/');
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
+
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
),
1789 debugs(33, 5, "ACCEL VPORT REWRITE: '" << http
->uri
<< "'");
1794 prepareTransparentURL(ConnStateData::Pointer
& conn
, ClientHttpRequest
*http
, char *url
, const char *req_hdr
)
1797 char ntoabuf
[MAX_IPSTRLEN
];
1799 http
->flags
.transparent
= 1;
1802 return; /* already in good shape */
1804 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
1806 if ((host
= mime_get_header(req_hdr
, "Host")) != NULL
) {
1807 int url_sz
= strlen(url
) + 32 + Config
.appendDomainLen
+
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
<<"'");
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
<< "'");
1826 * parseHttpRequest()
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.
1834 static ClientSocketContext
*
1835 parseHttpRequest(ConnStateData::Pointer
& conn
, HttpParser
*hp
, method_t
* method_p
, HttpVersion
*http_ver
)
1838 char *req_hdr
= NULL
;
1841 ClientHttpRequest
*http
;
1842 ClientSocketContext
*result
;
1843 StoreIOBuffer tempBuffer
;
1846 /* pre-set these values to make aborting simpler */
1847 *method_p
= METHOD_NONE
;
1849 /* Attempt to parse the first line; this'll define the method, url, version and header begin */
1850 r
= HttpParserParseReqLine(hp
);
1853 debugs(33, 5, "Incomplete request, waiting for end of request line");
1858 return parseHttpRequestAbort(conn
, "error:invalid-request");
1861 /* Request line is valid here .. */
1862 *http_ver
= HttpVersion(hp
->v_maj
, hp
->v_min
);
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");
1871 debugs(33, 3, "parseHttpRequest: Missing HTTP identifier");
1872 req_sz
= HttpParserReqSz(hp
);
1875 /* We know the whole request is in hp->buf now */
1877 assert(req_sz
<= (size_t) hp
->bufsiz
);
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) */
1883 hp
->hdr_end
= req_sz
- 1;
1885 hp
->hdr_start
= hp
->req_end
+ 1;
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");
1894 *method_p
= HttpRequestMethod(&hp
->buf
[hp
->m_start
], &hp
->buf
[hp
->m_end
]);
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
<< "'");
1900 /* XXX where's the method set for this error? */
1901 return parseHttpRequestAbort(conn
, "error:unsupported-request-method");
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.
1909 url
= (char *)xmalloc(hp
->u_end
- hp
->u_start
+ 16);
1911 memcpy(url
, hp
->buf
+ hp
->u_start
, hp
->u_end
- hp
->u_start
+ 1);
1913 url
[hp
->u_end
- hp
->u_start
+ 1] = '\0';
1916 * Process headers after request line
1917 * TODO: Use httpRequestParse here.
1919 /* XXX this code should be modified to take a const char * later! */
1920 req_hdr
= (char *) hp
->buf
+ hp
->req_end
+ 1;
1922 debugs(33, 3, "parseHttpRequest: req_hdr = {" << req_hdr
<< "}");
1924 end
= (char *) hp
->buf
+ hp
->hdr_end
;
1926 debugs(33, 3, "parseHttpRequest: end = {" << end
<< "}");
1928 if (strstr(req_hdr
, "\r\r\n")) {
1929 debugs(33, 1, "WARNING: suspicious HTTP request contains double CR");
1931 return parseHttpRequestAbort(conn
, "error:double-CR");
1934 debugs(33, 3, "parseHttpRequest: prefix_sz = " <<
1935 (int) HttpParserRequestLen(hp
) << ", req_line_sz = " <<
1936 HttpParserReqSz(hp
));
1938 /* Ok, all headers are received */
1939 http
= new ClientHttpRequest(conn
);
1941 http
->req_sz
= HttpParserRequestLen(hp
);
1942 result
= ClientSocketContextNew(http
);
1943 tempBuffer
.data
= result
->reqbuf
;
1944 tempBuffer
.length
= HTTP_REQBUF_SZ
;
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
);
1952 debugs(33, 5, "parseHttpRequest: Request Header is\n" <<(hp
->buf
) + hp
->hdr_start
);
1954 #if THIS_VIOLATES_HTTP_SPECS_ON_URL_TRANSFORMATION
1956 if ((t
= strchr(url
, '#'))) /* remove HTML anchors */
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;
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
);
1980 setLogUri(http
, http
->uri
);
1981 debugs(33, 5, "parseHttpRequest: Complete request received");
1982 result
->flags
.parsed_ok
= 1;
1988 ConnStateData::getAvailableBufferLength() const
1990 int result
= in
.allocatedSize
- in
.notYetUsed
- 1;
1991 assert (result
>= 0);
1996 ConnStateData::makeSpaceAvailable()
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
);
2005 ConnStateData::addContextToQueue(ClientSocketContext
* context
)
2007 ClientSocketContext::Pointer
*S
;
2009 for (S
= (ClientSocketContext::Pointer
*) & currentobject
; S
->getRaw();
2019 ConnStateData::getConcurrentRequestCount() const
2022 ClientSocketContext::Pointer
*T
;
2024 for (T
= (ClientSocketContext::Pointer
*) ¤tobject
;
2025 T
->getRaw(); T
= &(*T
)->next
, ++result
)
2032 connReadWasError(ConnStateData::Pointer
& conn
, comm_err_t flag
, int size
, int xerrno
)
2034 if (flag
!= COMM_OK
) {
2035 debugs(33, 2, "connReadWasError: FD " << conn
->fd
<< ": got flag " << flag
);
2040 if (!ignoreErrno(xerrno
)) {
2041 debugs(33, 2, "connReadWasError: FD " << conn
->fd
<< ": " << xstrerr(xerrno
));
2043 } else if (conn
->in
.notYetUsed
== 0) {
2044 debugs(33, 2, "connReadWasError: FD " << conn
->fd
<< ": no data to process (" << xstrerr(xerrno
) << ")");
2052 connFinishedWithConn(ConnStateData::Pointer
& conn
, int size
)
2055 if (conn
->getConcurrentRequestCount() == 0 && conn
->in
.notYetUsed
== 0) {
2056 /* no current or pending requests */
2057 debugs(33, 4, "connFinishedWithConn: FD " << conn
->fd
<< " closed");
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)");
2070 connNoteUseOfBuffer(ConnStateData
* conn
, size_t byteCount
)
2072 assert(byteCount
> 0 && byteCount
<= conn
->in
.notYetUsed
);
2073 conn
->in
.notYetUsed
-= byteCount
;
2074 debugs(33, 5, HERE
<< "conn->in.notYetUsed = " << conn
->in
.notYetUsed
);
2076 * If there is still data that will be used,
2077 * move it to the beginning.
2080 if (conn
->in
.notYetUsed
> 0)
2081 xmemmove(conn
->in
.buf
, conn
->in
.buf
+ byteCount
,
2082 conn
->in
.notYetUsed
);
2086 connKeepReadingIncompleteRequest(ConnStateData::Pointer
& conn
)
2088 return conn
->in
.notYetUsed
>= Config
.maxRequestHeaderSize
? 0 : 1;
2092 connCancelIncompleteRequests(ConnStateData::Pointer
& conn
)
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();
2109 clientMaybeReadData(ConnStateData::Pointer
&conn
, int do_next_read
)
2112 conn
->flags
.readMoreRequests
= true;
2113 conn
->readSomeData();
2118 clientAfterReadingRequests(int fd
, ConnStateData::Pointer
&conn
, int do_next_read
)
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.
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");
2135 clientMaybeReadData (conn
, do_next_read
);
2139 clientProcessRequest(ConnStateData::Pointer
&conn
, HttpParser
*hp
, ClientSocketContext
*context
, method_t method
, HttpVersion http_ver
)
2141 ClientHttpRequest
*http
= context
->http
;
2142 HttpRequest
*request
= NULL
;
2143 bool notedUseOfBuffer
= false;
2145 /* We have an initial client stream in place should it be needed */
2146 /* setup our private context */
2147 context
->registerWithConn();
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;
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;
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;
2188 request
->flags
.accelerated
= http
->flags
.accel
;
2190 request
->flags
.transparent
= http
->flags
.transparent
;
2194 request
->flags
.tproxy
= conn
->port
->tproxy
&& need_linux_tproxy
;
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;
2208 if (http
->flags
.internal
) {
2209 request
->protocol
= PROTO_HTTP
;
2210 request
->login
[0] = '\0';
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
;
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;
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;
2247 http
->request
= HTTPMSGLOCK(request
);
2248 clientSetKeepaliveFlag(http
);
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);
2254 /* Do we expect a request-body? */
2255 if (!context
->mayUseConnection() && request
->content_length
> 0) {
2256 request
->body_pipe
= conn
->expectRequestBody(request
->content_length
);
2258 // consume header early so that body pipe gets just the body
2259 connNoteUseOfBuffer(conn
.getRaw(), http
->req_sz
);
2260 notedUseOfBuffer
= true;
2262 conn
->handleRequestBodyData();
2264 if (!request
->body_pipe
->exhausted())
2265 conn
->readSomeData();
2267 /* Is it too large? */
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();
2282 context
->mayUseConnection(true);
2285 http
->calloutContext
= new ClientRequestContext(http
);
2290 if (!notedUseOfBuffer
)
2291 connNoteUseOfBuffer(conn
.getRaw(), http
->req_sz
);
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.
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
);
2309 connStripBufferWhitespace (ConnStateData::Pointer
&conn
)
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
;
2318 connOkToAddRequest(ConnStateData::Pointer
&conn
)
2320 int result
= conn
->getConcurrentRequestCount() < (Config
.onoff
.pipeline_prefetch
? 2 : 1);
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");
2335 * Report on the number of bytes of body content that we
2336 * know are yet to be read on this connection.
2339 ConnStateData::bodySizeLeft()
2341 // XXX: this logic will not work for chunked requests with unknown sizes
2343 if (bodyPipe
!= NULL
)
2344 return bodyPipe
->unproducedSize();
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
2357 clientParseRequest(ConnStateData::Pointer conn
, bool &do_next_read
)
2360 ClientSocketContext
*context
;
2361 bool parsed_req
= false;
2362 HttpVersion http_ver
;
2365 debugs(33, 5, "clientParseRequest: FD " << conn
->fd
<< ": attempting to parse");
2367 while (conn
->in
.notYetUsed
> 0 && conn
->bodySizeLeft() == 0) {
2368 connStripBufferWhitespace (conn
);
2370 /* Don't try to parse if the buffer is empty */
2372 if (conn
->in
.notYetUsed
== 0)
2375 /* Limit the number of concurrent requests to 2 */
2377 if (!connOkToAddRequest(conn
)) {
2381 /* Should not be needed anymore */
2382 /* Terminate the string */
2383 conn
->in
.buf
[conn
->in
.notYetUsed
] = '\0';
2385 /* Begin the parsing */
2386 HttpParserInit(&hp
, conn
->in
.buf
, conn
->in
.notYetUsed
);
2388 /* Process request */
2389 PROF_start(parseHttpRequest
);
2391 context
= parseHttpRequest(conn
, &hp
, &method
, &http_ver
);
2393 PROF_stop(parseHttpRequest
);
2395 /* partial or incomplete request */
2398 if (!connKeepReadingIncompleteRequest(conn
))
2399 connCancelIncompleteRequests(conn
);
2404 /* status -1 or 1 */
2406 debugs(33, 5, "clientParseRequest: FD " << conn
->fd
<< ": parsed a request");
2407 commSetTimeout(conn
->fd
, Config
.Timeout
.lifetime
, clientLifetimeTimeout
,
2410 clientProcessRequest(conn
, &hp
, context
, method
, http_ver
);
2414 if (context
->mayUseConnection()) {
2415 debugs(33, 3, "clientParseRequest: Not reading, as this request may need the connection");
2420 if (!conn
->flags
.readMoreRequests
) {
2421 conn
->flags
.readMoreRequests
= true;
2425 continue; /* while offset > 0 && conn->bodySizeLeft() == 0 */
2427 } /* while offset > 0 && conn->bodySizeLeft() == 0 */
2429 /* XXX where to 'finish' the parsing pass? */
2435 clientReadRequest(int fd
, char *buf
, size_t size
, comm_err_t flag
, int xerrno
,
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 */
2443 assert (fd
== conn
->fd
);
2445 /* Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up */
2447 if (flag
== COMM_ERR_CLOSING
) {
2448 debugs(33,5, HERE
<< " FD " << fd
<< " closing Bailout.");
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
2459 if (connReadWasError(conn
, flag
, size
, xerrno
)) {
2464 if (flag
== COMM_OK
) {
2466 kb_incr(&statCounter
.client_http
.kbytes_in
, size
);
2468 conn
->handleReadData(buf
, size
);
2470 /* The above may close the connection under our feets */
2471 if (!conn
->isOpen())
2474 } else if (size
== 0) {
2475 debugs(33, 5, "clientReadRequest: FD " << fd
<< " closed?");
2477 if (connFinishedWithConn(conn
, size
)) {
2482 /* It might be half-closed, we can't tell */
2483 fd_table
[fd
].flags
.socket_eof
= 1;
2485 commMarkHalfClosed(fd
);
2489 fd_note(fd
, "half-closed");
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
2495 /* Continue to process previously read data */
2499 /* Process next request */
2500 if (conn
->getConcurrentRequestCount() == 0)
2501 fd_note(conn
->fd
, "Reading next request");
2503 if (! clientParseRequest(conn
, do_next_read
)) {
2504 if (!conn
->isOpen())
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.
2513 if (conn
->getConcurrentRequestCount() == 0 && commIsHalfClosed(fd
)) {
2514 debugs(33, 5, "clientReadRequest: FD " << fd
<< ": half-closed connection, no completed request parsed, connection closing.");
2520 if (!conn
->isOpen())
2523 clientAfterReadingRequests(fd
, conn
, do_next_read
);
2526 // called when new request data has been read from the socket
2528 ConnStateData::handleReadData(char *buf
, size_t size
)
2530 char *current_buf
= in
.addressToReadInto();
2532 if (buf
!= current_buf
)
2533 xmemmove(current_buf
, buf
, size
);
2535 in
.notYetUsed
+= size
;
2537 in
.buf
[in
.notYetUsed
] = '\0'; /* Terminate the string */
2539 // if we are reading a body, stuff data into the body pipe
2540 if (bodyPipe
!= NULL
)
2541 handleRequestBodyData();
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
2547 ConnStateData::handleRequestBodyData()
2549 assert(bodyPipe
!= NULL
);
2551 if (const size_t putSize
= bodyPipe
->putMoreData(in
.buf
, in
.notYetUsed
))
2552 connNoteUseOfBuffer(this, putSize
);
2554 if (!bodyPipe
->mayNeedMoreData()) {
2555 // BodyPipe will clear us automagically when we produced everything
2558 debugs(33,5, HERE
<< "produced entire request body for FD " << fd
);
2561 /* we've finished reading like good clients,
2562 * now do the close that initiateClose initiated.
2564 * XXX: do we have to close? why not check keepalive et.
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.
2576 ConnStateData::noteMoreBodySpaceAvailable(BodyPipe
&)
2578 handleRequestBodyData();
2582 ConnStateData::noteBodyConsumerAborted(BodyPipe
&)
2585 startClosing("body consumer aborted");
2588 /* general lifetime handler for HTTP requests */
2590 requestTimeout(int fd
, void *data
)
2592 #if THIS_CONFUSES_PERSISTENT_CONNECTION_AWARE_BROWSERS_AND_USERS
2593 ConnStateData
*conn
= data
;
2594 debugs(33, 3, "requestTimeout: FD " << fd
<< ": lifetime is expired.");
2596 if (COMMIO_FD_WRITECB(fd
)->active
) {
2597 /* FIXME: If this code is reinstated, check the conn counters,
2598 * not the fd table state
2601 * Some data has been sent to the client, just close the FD
2604 } else if (conn
->nrequests
) {
2606 * assume its a persistent connection; just close it
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
,
2623 /* No requests can be outstanded */
2624 assert(conn
->chr
== NULL
);
2625 /* add to the client request queue */
2627 for (H
= &conn
->chr
; *H
; H
= &(*H
)->next
)
2632 clientStreamRead(http
->client_stream
.tail
->data
, http
, 0,
2633 HTTP_REQBUF_SZ
, context
->reqbuf
);
2636 * if we don't close() here, we still need a timeout handler!
2638 commSetTimeout(fd
, 30, requestTimeout
, conn
);
2641 * Aha, but we don't want a read handler!
2643 commSetSelect(fd
, COMM_SELECT_READ
, NULL
, NULL
, 0);
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
2655 debugs(33, 3, "requestTimeout: FD " << fd
<< ": lifetime is expired.");
2663 clientLifetimeTimeout(int fd
, void *data
)
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
);
2674 static time_t last_warn
= 0;
2676 if (fdNFree() >= RESERVED_FD
)
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
;
2689 connStateCreate(const IPAddress
&peer
, const IPAddress
&me
, int fd
, http_port_list
*port
)
2691 ConnStateData
*result
= new ConnStateData
;
2692 result
->peer
= peer
;
2693 result
->log_addr
= peer
;
2694 result
->log_addr
.ApplyMask(Config
.Addrs
.client_netmask
.GetCIDR());
2697 result
->in
.buf
= (char *)memAllocBuf(CLIENT_REQ_BUF_SZ
, &result
->in
.allocatedSize
);
2698 result
->port
= cbdataReference(port
);
2700 if (port
->transparent
)
2705 if (clientNatLookup(fd
, me
, peer
, dst
) == 0) {
2706 result
-> me
= dst
; /* XXX This should be moved to another field */
2707 result
->transparent(true);
2711 if (port
->disable_pmtu_discovery
!= DISABLE_PMTU_OFF
&&
2712 (result
->transparent() || port
->disable_pmtu_discovery
== DISABLE_PMTU_ALWAYS
))
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
);
2720 static int reported
= 0;
2723 debugs(33, 1, "Notice: httpd_accel_no_pmtu_disc not supported on your platform");
2731 result
->flags
.readMoreRequests
= true;
2735 /* Handle a new connection on HTTP socket. */
2737 httpAccept(int sock
, int newfd
, ConnectionDetail
*details
,
2738 comm_err_t flag
, int xerrno
, void *data
)
2740 http_port_list
*s
= (http_port_list
*)data
;
2741 ConnStateData
*connState
= NULL
;
2743 if (flag
== COMM_ERR_CLOSING
) {
2748 AcceptLimiter::Instance().defer (sock
, httpAccept
, data
);
2750 /* kick off another one for later */
2751 comm_accept(sock
, httpAccept
, data
);
2753 if (flag
!= COMM_OK
) {
2754 debugs(33, 1, "httpAccept: FD " << sock
<< ": accept failure: " << xstrerr(xerrno
));
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
);
2763 if (Config
.onoff
.log_fqdn
)
2764 fqdncache_gethostbyaddr(details
->peer
, FQDN_LOOKUP_IF_MISS
);
2766 commSetTimeout(newfd
, Config
.Timeout
.request
, requestTimeout
, connState
);
2770 ACLChecklist identChecklist
;
2772 identChecklist
.src_addr
= details
->peer
;
2774 identChecklist
.my_addr
= details
->me
;
2776 identChecklist
.accessList
= cbdataReference(Config
.accessList
.identLookup
);
2778 /* cbdataReferenceDone() happens in either fastCheck() or ~ACLCheckList */
2780 if (identChecklist
.fastCheck())
2781 identStart(details
->me
, details
->peer
, clientIdentDone
, connState
);
2785 connState
->readSomeData();
2787 clientdbEstablished(details
->peer
, 1);
2789 incoming_sockets_accepted
++;
2794 /* negotiate an SSL connection */
2796 clientNegotiateSSL(int fd
, void *data
)
2798 ConnStateData
*conn
= (ConnStateData
*)data
;
2800 SSL
*ssl
= fd_table
[fd
].ssl
;
2803 if ((ret
= SSL_accept(ssl
)) <= 0) {
2804 int ssl_error
= SSL_get_error(ssl
, ret
);
2806 switch (ssl_error
) {
2808 case SSL_ERROR_WANT_READ
:
2809 commSetSelect(fd
, COMM_SELECT_READ
, clientNegotiateSSL
, conn
, 0);
2812 case SSL_ERROR_WANT_WRITE
:
2813 commSetSelect(fd
, COMM_SELECT_WRITE
, clientNegotiateSSL
, conn
, 0);
2816 case SSL_ERROR_SYSCALL
:
2819 debugs(83, 2, "clientNegotiateSSL: Error negotiating SSL connection on FD " << fd
<< ": Aborted by client");
2825 if (errno
== ECONNRESET
)
2828 debugs(83, hard
? 1 : 2, "clientNegotiateSSL: Error negotiating SSL connection on FD " <<
2829 fd
<< ": " << strerror(errno
) << " (" << errno
<< ")");
2836 case SSL_ERROR_ZERO_RETURN
:
2837 debugs(83, 1, "clientNegotiateSSL: Error negotiating SSL connection on FD " << fd
<< ": Closed by client");
2842 debugs(83, 1, "clientNegotiateSSL: Error negotiating SSL connection on FD " <<
2843 fd
<< ": " << ERR_error_string(ERR_get_error(), NULL
) <<
2844 " (" << ssl_error
<< "/" << ret
<< ")");
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
<< ")");
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
);
2863 #elif (ALLOW_ALWAYS_SSL_SESSION_DETAIL == 1)
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. */
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); */
2877 debugs(83, 4, "With " OPENSSL_VERSION_TEXT
", session details are available only defining ALLOW_ALWAYS_SSL_SESSION_DETAIL=1 in the source." );
2880 /* Note: This does not automatically fflush the log file.. */
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
<<
2889 debugs(83, 3, "clientNegotiateSSL: FD " << fd
<< " negotiated cipher " <<
2890 SSL_get_cipher(ssl
));
2892 client_cert
= SSL_get_peer_certificate(ssl
);
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));
2899 debugs(83, 3, "clientNegotiateSSL: FD " << fd
<<
2900 " client certificate: issuer: " <<
2901 X509_NAME_oneline(X509_get_issuer_name(client_cert
), 0, 0));
2904 X509_free(client_cert
);
2906 debugs(83, 5, "clientNegotiateSSL: FD " << fd
<<
2907 " has no certificate.");
2910 conn
->readSomeData();
2913 /* handle a new HTTPS connection */
2915 httpsAccept(int sock
, int newfd
, ConnectionDetail
*details
,
2916 comm_err_t flag
, int xerrno
, void *data
)
2918 https_port_list
*s
= (https_port_list
*)data
;
2919 SSL_CTX
*sslContext
= s
->sslContext
;
2920 ConnStateData
*connState
= NULL
;
2924 if (flag
== COMM_ERR_CLOSING
) {
2929 AcceptLimiter::Instance().defer (sock
, httpsAccept
, data
);
2931 /* kick off another one for later */
2932 comm_accept(sock
, httpsAccept
, data
);
2934 if (flag
!= COMM_OK
) {
2936 debugs(33, 1, "httpsAccept: FD " << sock
<< ": accept failure: " << xstrerr(xerrno
));
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
) );
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
;
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
);
2957 if (Config
.onoff
.log_fqdn
)
2958 fqdncache_gethostbyaddr(details
->peer
, FQDN_LOOKUP_IF_MISS
);
2960 commSetTimeout(newfd
, Config
.Timeout
.request
, requestTimeout
, connState
);
2964 ACLChecklist identChecklist
;
2966 identChecklist
.src_addr
= details
->peer
;
2968 identChecklist
.my_addr
= details
->me
;
2970 identChecklist
.accessList
= cbdataReference(Config
.accessList
.identLookup
);
2972 /* cbdataReferenceDone() happens in either fastCheck() or ~ACLCheckList */
2974 if (identChecklist
.fastCheck())
2975 identStart(details
->me
, details
->peer
, clientIdentDone
, connState
);
2979 commSetSelect(newfd
, COMM_SELECT_READ
, clientNegotiateSSL
, connState
, 0);
2981 clientdbEstablished(details
->peer
, 1);
2983 incoming_sockets_accepted
++;
2986 #endif /* USE_SSL */
2990 clientHttpConnectionsOpen(void)
2992 http_port_list
*s
= NULL
;
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
);
3003 fd
= comm_open(SOCK_STREAM
,
3006 COMM_NONBLOCKING
, "HTTP Socket");
3014 comm_accept(fd
, httpAccept
, s
);
3016 debugs(1, 1, "Accepting " <<
3017 (s
->transparent
? "transparently proxied" :
3018 s
->accel
? "accelerated" : "" )
3019 << " HTTP connections at " << s
->s
3020 << ", FD " << fd
<< "." );
3022 HttpSockets
[NHttpSockets
++] = fd
;
3028 clientHttpsConnectionsOpen(void)
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
);
3040 if (s
->sslContext
== NULL
) {
3041 debugs(1, 1, "Can not accept HTTPS connections at " << s
->http
.s
);
3045 fd
= comm_open(SOCK_STREAM
,
3048 COMM_NONBLOCKING
, "HTTPS Socket");
3056 comm_accept(fd
, httpsAccept
, s
);
3058 debugs(1, 1, "Accepting HTTPS connections at " << s
->http
.s
<< ", FD " << fd
<< ".");
3060 HttpSockets
[NHttpSockets
++] = fd
;
3067 clientOpenListenSockets(void)
3069 clientHttpConnectionsOpen();
3072 clientHttpsConnectionsOpen();
3075 if (NHttpSockets
< 1)
3076 fatal("Cannot open HTTP Port");
3080 clientHttpConnectionsClose(void)
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;
3097 varyEvaluateMatch(StoreEntry
* entry
, HttpRequest
* request
)
3099 const char *vary
= request
->vary_headers
;
3100 int has_vary
= entry
->getReply()->header
.has(HDR_VARY
);
3101 #if X_ACCELERATOR_VARY
3104 entry
->getReply()->header
.has(HDR_X_ACCELERATOR_VARY
);
3107 if (!has_vary
|| !entry
->mem_obj
->vary_headers
) {
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
);
3117 /* This is not a varying object */
3121 /* virtual "vary" object found. Calculate the vary key and
3122 * continue the search
3124 vary
= httpMakeVaryMark(request
, entry
->getReply());
3127 request
->vary_headers
= xstrdup(vary
);
3130 /* Ouch.. we cannot handle this kind of variance */
3131 /* XXX This cannot really happen, but just to be complete */
3136 vary
= httpMakeVaryMark(request
, entry
->getReply());
3139 request
->vary_headers
= xstrdup(vary
);
3143 /* Ouch.. we cannot handle this kind of variance */
3144 /* XXX This cannot really happen, but just to be complete */
3146 } else if (strcmp(vary
, entry
->mem_obj
->vary_headers
) == 0) {
3149 /* Oops.. we have already been here and still haven't
3150 * found the requested variant. Bail out
3152 debugs(33, 1, "varyEvaluateMatch: Oops. Not a Vary match on second attempt, '" <<
3153 entry
->mem_obj
->url
<< "' '" << vary
<< "'");
3160 clientAclChecklistCreate(const acl_access
* acl
, ClientHttpRequest
* http
)
3163 ConnStateData::Pointer conn
= http
->getConn();
3164 ch
= aclChecklistCreate(acl
, http
->request
, conn
.getRaw() != NULL
? conn
->rfc931
: dash_str
);
3167 * hack for ident ACL. It needs to get full addresses, and a place to store
3168 * the ident result on persistent connections...
3170 /* connection oriented auth also needs these two lines for it's operation. */
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
3178 if (conn
.getRaw() != NULL
)
3179 ch
->conn(conn
); /* unreferenced in acl.cc */
3184 CBDATA_CLASS_INIT(ConnStateData
);
3186 ConnStateData::ConnStateData() : transparent_ (false), reading_ (false), closing_ (false)
3188 openReference
= this;
3192 ConnStateData::transparent() const
3194 return transparent_
;
3198 ConnStateData::transparent(bool const anInt
)
3200 transparent_
= anInt
;
3204 ConnStateData::reading() const
3210 ConnStateData::reading(bool const newBool
)
3212 assert (reading() != newBool
);
3218 ConnStateData::expectRequestBody(int64_t size
)
3220 bodyPipe
= new BodyPipe(this);
3221 bodyPipe
->setBodySize(size
);
3226 ConnStateData::closing() const
3231 // Called by ClientSocketContext to give the connection a chance to read
3232 // the entire body before closing the socket.
3234 ConnStateData::startClosing(const char *reason
)
3236 debugs(33, 5, HERE
<< "startClosing " << this << " for " << reason
);
3240 assert(bodyPipe
!= NULL
);
3241 assert(bodySizeLeft() > 0);
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.
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();
3253 ConnStateData::In::addressToReadInto() const
3255 return buf
+ notYetUsed
;
3258 ConnStateData::In::In() : buf (NULL
), notYetUsed (0), allocatedSize (0)
3261 ConnStateData::In::~In()
3264 memFreeBuf(allocatedSize
, buf
);