]> git.ipfire.org Git - thirdparty/squid.git/blob - src/client_side.cc
2b9d35adbf2dbc68427c73cdb34e7691c199b486
[thirdparty/squid.git] / src / client_side.cc
1 /*
2 * $Id$
3 *
4 * DEBUG: section 33 Client-side Routines
5 * AUTHOR: Duane Wessels
6 *
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
9 *
10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
32 *
33 */
34
35 /**
36 \defgroup ClientSide Client-Side Logics
37 *
38 \section cserrors Errors and client side
39 *
40 \par Problem the first:
41 * the store entry is no longer authoritative on the
42 * reply status. EBITTEST (E_ABORT) is no longer a valid test outside
43 * of client_side_reply.c.
44 * Problem the second: resources are wasted if we delay in cleaning up.
45 * Problem the third we can't depend on a connection close to clean up.
46 *
47 \par Nice thing the first:
48 * Any step in the stream can callback with data
49 * representing an error.
50 * Nice thing the second: once you stop requesting reads from upstream,
51 * upstream can be stopped too.
52 *
53 \par Solution #1:
54 * Error has a callback mechanism to hand over a membuf
55 * with the error content. The failing node pushes that back as the
56 * reply. Can this be generalised to reduce duplicate efforts?
57 * A: Possibly. For now, only one location uses this.
58 * How to deal with pre-stream errors?
59 * Tell client_side_reply that we *want* an error page before any
60 * stream calls occur. Then we simply read as normal.
61 *
62 *
63 \section pconn_logic Persistent connection logic:
64 *
65 \par
66 * requests (httpClientRequest structs) get added to the connection
67 * list, with the current one being chr
68 *
69 \par
70 * The request is *immediately* kicked off, and data flows through
71 * to clientSocketRecipient.
72 *
73 \par
74 * If the data that arrives at clientSocketRecipient is not for the current
75 * request, clientSocketRecipient simply returns, without requesting more
76 * data, or sending it.
77 *
78 \par
79 * ClientKeepAliveNextRequest will then detect the presence of data in
80 * the next ClientHttpRequest, and will send it, restablishing the
81 * data flow.
82 */
83
84 #include "squid-old.h"
85
86 #include "acl/FilledChecklist.h"
87 #if USE_AUTH
88 #include "auth/UserRequest.h"
89 #endif
90 #include "base/Subscription.h"
91 #include "base/TextException.h"
92 #include "ChunkedCodingParser.h"
93 #include "client_side.h"
94 #include "client_side_reply.h"
95 #include "client_side_request.h"
96 #if USE_DELAY_POOLS
97 #include "ClientInfo.h"
98 #endif
99 #include "ClientRequestContext.h"
100 #include "clientStream.h"
101 #include "comm.h"
102 #include "comm/Connection.h"
103 #include "CommCalls.h"
104 #include "comm/Loops.h"
105 #include "comm/Write.h"
106 #include "comm/TcpAcceptor.h"
107 #include "eui/Config.h"
108 #include "fde.h"
109 #include "HttpHdrContRange.h"
110 #include "HttpReply.h"
111 #include "HttpRequest.h"
112 #include "ident/Config.h"
113 #include "ident/Ident.h"
114 #include "ipc/FdNotes.h"
115 #include "ipc/StartListening.h"
116 #include "MemBuf.h"
117 #include "MemObject.h"
118 #include "ProtoPort.h"
119 #include "rfc1738.h"
120 #include "StatCounters.h"
121 #include "StatHist.h"
122 #include "SquidTime.h"
123 #if USE_SSL
124 #include "ssl/context_storage.h"
125 #include "ssl/helper.h"
126 #include "ssl/support.h"
127 #include "ssl/gadgets.h"
128 #endif
129 #if USE_SSL_CRTD
130 #include "ssl/crtd_message.h"
131 #include "ssl/certificate_db.h"
132 #endif
133 #include "Store.h"
134 #include "TimeOrTag.h"
135
136 #if HAVE_LIMITS
137 #include <limits>
138 #endif
139
140 #if LINGERING_CLOSE
141 #define comm_close comm_lingering_close
142 #endif
143
144 /// dials clientListenerConnectionOpened call
145 class ListeningStartedDialer: public CallDialer, public Ipc::StartListeningCb
146 {
147 public:
148 typedef void (*Handler)(http_port_list *portCfg, const Ipc::FdNoteId note, const Subscription::Pointer &sub);
149 ListeningStartedDialer(Handler aHandler, http_port_list *aPortCfg, const Ipc::FdNoteId note, const Subscription::Pointer &aSub):
150 handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}
151
152 virtual void print(std::ostream &os) const {
153 startPrint(os) <<
154 ", " << FdNote(portTypeNote) << " port=" << (void*)portCfg << ')';
155 }
156
157 virtual bool canDial(AsyncCall &) const { return true; }
158 virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }
159
160 public:
161 Handler handler;
162
163 private:
164 http_port_list *portCfg; ///< from Config.Sockaddr.http
165 Ipc::FdNoteId portTypeNote; ///< Type of IPC socket being opened
166 Subscription::Pointer sub; ///< The handler to be subscribed for this connetion listener
167 };
168
169 static void clientListenerConnectionOpened(http_port_list *s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub);
170
171 /* our socket-related context */
172
173
174 CBDATA_CLASS_INIT(ClientSocketContext);
175
176 void *
177 ClientSocketContext::operator new (size_t byteCount)
178 {
179 /* derived classes with different sizes must implement their own new */
180 assert (byteCount == sizeof (ClientSocketContext));
181 CBDATA_INIT_TYPE(ClientSocketContext);
182 return cbdataAlloc(ClientSocketContext);
183 }
184
185 void
186 ClientSocketContext::operator delete (void *address)
187 {
188 cbdataFree (address);
189 }
190
191 /* Local functions */
192 /* ClientSocketContext */
193 static ClientSocketContext *ClientSocketContextNew(const Comm::ConnectionPointer &clientConn, ClientHttpRequest *);
194 /* other */
195 static IOCB clientWriteComplete;
196 static IOCB clientWriteBodyComplete;
197 static IOACB httpAccept;
198 #if USE_SSL
199 static IOACB httpsAccept;
200 #endif
201 static CTCB clientLifetimeTimeout;
202 static ClientSocketContext *parseHttpRequestAbort(ConnStateData * conn, const char *uri);
203 static ClientSocketContext *parseHttpRequest(ConnStateData *, HttpParser *, HttpRequestMethod *, HttpVersion *);
204 #if USE_IDENT
205 static IDCB clientIdentDone;
206 #endif
207 static CSCB clientSocketRecipient;
208 static CSD clientSocketDetach;
209 static void clientSetKeepaliveFlag(ClientHttpRequest *);
210 static int clientIsContentLengthValid(HttpRequest * r);
211 static int clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength);
212
213 static void clientUpdateStatHistCounters(log_type logType, int svc_time);
214 static void clientUpdateStatCounters(log_type logType);
215 static void clientUpdateHierCounters(HierarchyLogEntry *);
216 static bool clientPingHasFinished(ping_data const *aPing);
217 void prepareLogWithRequestDetails(HttpRequest *, AccessLogEntry *);
218 #ifndef PURIFY
219 static bool connIsUsable(ConnStateData * conn);
220 #endif
221 static int responseFinishedOrFailed(HttpReply * rep, StoreIOBuffer const &receivedData);
222 static void ClientSocketContextPushDeferredIfNeeded(ClientSocketContext::Pointer deferredRequest, ConnStateData * conn);
223 static void clientUpdateSocketStats(log_type logType, size_t size);
224
225 char *skipLeadingSpace(char *aString);
226 static void connNoteUseOfBuffer(ConnStateData* conn, size_t byteCount);
227
228 static ConnStateData *connStateCreate(const Comm::ConnectionPointer &client, http_port_list *port);
229
230
231 clientStreamNode *
232 ClientSocketContext::getTail() const
233 {
234 if (http->client_stream.tail)
235 return (clientStreamNode *)http->client_stream.tail->data;
236
237 return NULL;
238 }
239
240 clientStreamNode *
241 ClientSocketContext::getClientReplyContext() const
242 {
243 return (clientStreamNode *)http->client_stream.tail->prev->data;
244 }
245
246 /**
247 * This routine should be called to grow the inbuf and then
248 * call comm_read().
249 */
250 void
251 ConnStateData::readSomeData()
252 {
253 if (reading())
254 return;
255
256 debugs(33, 4, HERE << clientConnection << ": reading request...");
257
258 if (!maybeMakeSpaceAvailable())
259 return;
260
261 typedef CommCbMemFunT<ConnStateData, CommIoCbParams> Dialer;
262 reader = JobCallback(33, 5, Dialer, this, ConnStateData::clientReadRequest);
263 comm_read(clientConnection, in.addressToReadInto(), getAvailableBufferLength(), reader);
264 }
265
266
267 void
268 ClientSocketContext::removeFromConnectionList(ConnStateData * conn)
269 {
270 ClientSocketContext::Pointer *tempContextPointer;
271 assert(conn != NULL && cbdataReferenceValid(conn));
272 assert(conn->getCurrentContext() != NULL);
273 /* Unlink us from the connection request list */
274 tempContextPointer = & conn->currentobject;
275
276 while (tempContextPointer->getRaw()) {
277 if (*tempContextPointer == this)
278 break;
279
280 tempContextPointer = &(*tempContextPointer)->next;
281 }
282
283 assert(tempContextPointer->getRaw() != NULL);
284 *tempContextPointer = next;
285 next = NULL;
286 }
287
288 ClientSocketContext::~ClientSocketContext()
289 {
290 clientStreamNode *node = getTail();
291
292 if (node) {
293 ClientSocketContext *streamContext = dynamic_cast<ClientSocketContext *> (node->data.getRaw());
294
295 if (streamContext) {
296 /* We are *always* the tail - prevent recursive free */
297 assert(this == streamContext);
298 node->data = NULL;
299 }
300 }
301
302 if (connRegistered_)
303 deRegisterWithConn();
304
305 httpRequestFree(http);
306
307 /* clean up connection links to us */
308 assert(this != next.getRaw());
309 }
310
311 void
312 ClientSocketContext::registerWithConn()
313 {
314 assert (!connRegistered_);
315 assert (http);
316 assert (http->getConn() != NULL);
317 connRegistered_ = true;
318 http->getConn()->addContextToQueue(this);
319 }
320
321 void
322 ClientSocketContext::deRegisterWithConn()
323 {
324 assert (connRegistered_);
325 removeFromConnectionList(http->getConn());
326 connRegistered_ = false;
327 }
328
329 void
330 ClientSocketContext::connIsFinished()
331 {
332 assert (http);
333 assert (http->getConn() != NULL);
334 deRegisterWithConn();
335 /* we can't handle any more stream data - detach */
336 clientStreamDetach(getTail(), http);
337 }
338
339 ClientSocketContext::ClientSocketContext() : http(NULL), reply(NULL), next(NULL),
340 writtenToSocket(0),
341 mayUseConnection_ (false),
342 connRegistered_ (false)
343 {
344 memset (reqbuf, '\0', sizeof (reqbuf));
345 flags.deferred = 0;
346 flags.parsed_ok = 0;
347 deferredparams.node = NULL;
348 deferredparams.rep = NULL;
349 }
350
351 ClientSocketContext *
352 ClientSocketContextNew(const Comm::ConnectionPointer &client, ClientHttpRequest * http)
353 {
354 ClientSocketContext *newContext;
355 assert(http != NULL);
356 newContext = new ClientSocketContext;
357 newContext->http = http;
358 newContext->clientConnection = client;
359 return newContext;
360 }
361
362 void
363 ClientSocketContext::writeControlMsg(HttpControlMsg &msg)
364 {
365 HttpReply *rep = msg.reply;
366 Must(rep);
367
368 // apply selected clientReplyContext::buildReplyHeader() mods
369 // it is not clear what headers are required for control messages
370 rep->header.removeHopByHopEntries();
371 rep->header.putStr(HDR_CONNECTION, "keep-alive");
372 httpHdrMangleList(&rep->header, http->request, ROR_REPLY);
373
374 // remember the callback
375 cbControlMsgSent = msg.cbSuccess;
376
377 MemBuf *mb = rep->pack();
378
379 debugs(11, 2, "HTTP Client " << clientConnection);
380 debugs(11, 2, "HTTP Client CONTROL MSG:\n---------\n" << mb->buf << "\n----------");
381
382 AsyncCall::Pointer call = commCbCall(33, 5, "ClientSocketContext::wroteControlMsg",
383 CommIoCbPtrFun(&WroteControlMsg, this));
384 Comm::Write(clientConnection, mb, call);
385
386 delete mb;
387 }
388
389 /// called when we wrote the 1xx response
390 void
391 ClientSocketContext::wroteControlMsg(const Comm::ConnectionPointer &conn, char *, size_t, comm_err_t errflag, int xerrno)
392 {
393 if (errflag == COMM_ERR_CLOSING)
394 return;
395
396 if (errflag == COMM_OK) {
397 ScheduleCallHere(cbControlMsgSent);
398 return;
399 }
400
401 debugs(33, 3, HERE << "1xx writing failed: " << xstrerr(xerrno));
402 // no error notification: see HttpControlMsg.h for rationale and
403 // note that some errors are detected elsewhere (e.g., close handler)
404
405 // close on 1xx errors to be conservative and to simplify the code
406 // (if we do not close, we must notify the source of a failure!)
407 conn->close();
408 }
409
410 /// wroteControlMsg() wrapper: ClientSocketContext is not an AsyncJob
411 void
412 ClientSocketContext::WroteControlMsg(const Comm::ConnectionPointer &conn, char *bufnotused, size_t size, comm_err_t errflag, int xerrno, void *data)
413 {
414 ClientSocketContext *context = static_cast<ClientSocketContext*>(data);
415 context->wroteControlMsg(conn, bufnotused, size, errflag, xerrno);
416 }
417
418 #if USE_IDENT
419 static void
420 clientIdentDone(const char *ident, void *data)
421 {
422 ConnStateData *conn = (ConnStateData *)data;
423 xstrncpy(conn->clientConnection->rfc931, ident ? ident : dash_str, USER_IDENT_SZ);
424 }
425 #endif
426
427 void
428 clientUpdateStatCounters(log_type logType)
429 {
430 ++statCounter.client_http.requests;
431
432 if (logTypeIsATcpHit(logType))
433 ++statCounter.client_http.hits;
434
435 if (logType == LOG_TCP_HIT)
436 ++statCounter.client_http.disk_hits;
437 else if (logType == LOG_TCP_MEM_HIT)
438 ++statCounter.client_http.mem_hits;
439 }
440
441 void
442 clientUpdateStatHistCounters(log_type logType, int svc_time)
443 {
444 statCounter.client_http.allSvcTime.count(svc_time);
445 /**
446 * The idea here is not to be complete, but to get service times
447 * for only well-defined types. For example, we don't include
448 * LOG_TCP_REFRESH_FAIL because its not really a cache hit
449 * (we *tried* to validate it, but failed).
450 */
451
452 switch (logType) {
453
454 case LOG_TCP_REFRESH_UNMODIFIED:
455 statCounter.client_http.nearHitSvcTime.count(svc_time);
456 break;
457
458 case LOG_TCP_IMS_HIT:
459 statCounter.client_http.nearMissSvcTime.count(svc_time);
460 break;
461
462 case LOG_TCP_HIT:
463
464 case LOG_TCP_MEM_HIT:
465
466 case LOG_TCP_OFFLINE_HIT:
467 statCounter.client_http.hitSvcTime.count(svc_time);
468 break;
469
470 case LOG_TCP_MISS:
471
472 case LOG_TCP_CLIENT_REFRESH_MISS:
473 statCounter.client_http.missSvcTime.count(svc_time);
474 break;
475
476 default:
477 /* make compiler warnings go away */
478 break;
479 }
480 }
481
482 bool
483 clientPingHasFinished(ping_data const *aPing)
484 {
485 if (0 != aPing->stop.tv_sec && 0 != aPing->start.tv_sec)
486 return true;
487
488 return false;
489 }
490
491 void
492 clientUpdateHierCounters(HierarchyLogEntry * someEntry)
493 {
494 ping_data *i;
495
496 switch (someEntry->code) {
497 #if USE_CACHE_DIGESTS
498
499 case CD_PARENT_HIT:
500
501 case CD_SIBLING_HIT:
502 statCounter.cd.times_used++;
503 break;
504 #endif
505
506 case SIBLING_HIT:
507
508 case PARENT_HIT:
509
510 case FIRST_PARENT_MISS:
511
512 case CLOSEST_PARENT_MISS:
513 statCounter.icp.times_used++;
514 i = &someEntry->ping;
515
516 if (clientPingHasFinished(i))
517 statCounter.icp.querySvcTime.count(tvSubUsec(i->start, i->stop));
518
519 if (i->timeout)
520 statCounter.icp.query_timeouts++;
521
522 break;
523
524 case CLOSEST_PARENT:
525
526 case CLOSEST_DIRECT:
527 statCounter.netdb.times_used++;
528
529 break;
530
531 default:
532 break;
533 }
534 }
535
536 void
537 ClientHttpRequest::updateCounters()
538 {
539 clientUpdateStatCounters(logType);
540
541 if (request->errType != ERR_NONE)
542 statCounter.client_http.errors++;
543
544 clientUpdateStatHistCounters(logType,
545 tvSubMsec(start_time, current_time));
546
547 clientUpdateHierCounters(&request->hier);
548 }
549
550 void
551 prepareLogWithRequestDetails(HttpRequest * request, AccessLogEntry * aLogEntry)
552 {
553 assert(request);
554 assert(aLogEntry);
555
556 if (Config.onoff.log_mime_hdrs) {
557 Packer p;
558 MemBuf mb;
559 mb.init();
560 packerToMemInit(&p, &mb);
561 request->header.packInto(&p);
562 //This is the request after adaptation or redirection
563 aLogEntry->headers.adapted_request = xstrdup(mb.buf);
564
565 // the virgin request is saved to aLogEntry->request
566 if (aLogEntry->request) {
567 packerClean(&p);
568 mb.reset();
569 packerToMemInit(&p, &mb);
570 aLogEntry->request->header.packInto(&p);
571 aLogEntry->headers.request = xstrdup(mb.buf);
572 }
573
574 #if USE_ADAPTATION
575 const Adaptation::History::Pointer ah = request->adaptLogHistory();
576 if (ah != NULL) {
577 packerClean(&p);
578 mb.reset();
579 packerToMemInit(&p, &mb);
580 ah->lastMeta.packInto(&p);
581 aLogEntry->adapt.last_meta = xstrdup(mb.buf);
582 }
583 #endif
584
585 packerClean(&p);
586 mb.clean();
587 }
588
589 #if ICAP_CLIENT
590 const Adaptation::Icap::History::Pointer ih = request->icapHistory();
591 if (ih != NULL)
592 aLogEntry->icap.processingTime = ih->processingTime();
593 #endif
594
595 aLogEntry->http.method = request->method;
596 aLogEntry->http.version = request->http_ver;
597 aLogEntry->hier = request->hier;
598 if (request->content_length > 0) // negative when no body or unknown length
599 aLogEntry->cache.requestSize += request->content_length;
600 aLogEntry->cache.extuser = request->extacl_user.termedBuf();
601
602 #if USE_AUTH
603 if (request->auth_user_request != NULL) {
604 if (request->auth_user_request->username())
605 aLogEntry->cache.authuser = xstrdup(request->auth_user_request->username());
606 }
607 #endif
608
609 // Adapted request, if any, inherits and then collects all the stats, but
610 // the virgin request gets logged instead; copy the stats to log them.
611 // TODO: avoid losses by keeping these stats in a shared history object?
612 if (aLogEntry->request) {
613 aLogEntry->request->dnsWait = request->dnsWait;
614 aLogEntry->request->errType = request->errType;
615 aLogEntry->request->errDetail = request->errDetail;
616 }
617 }
618
619 void
620 ClientHttpRequest::logRequest()
621 {
622 if (!out.size && !logType)
623 debugs(33, 5, HERE << "logging half-baked transaction: " << log_uri);
624
625 al.icp.opcode = ICP_INVALID;
626 al.url = log_uri;
627 debugs(33, 9, "clientLogRequest: al.url='" << al.url << "'");
628
629 if (al.reply) {
630 al.http.code = al.reply->sline.status;
631 al.http.content_type = al.reply->content_type.termedBuf();
632 } else if (loggingEntry() && loggingEntry()->mem_obj) {
633 al.http.code = loggingEntry()->mem_obj->getReply()->sline.status;
634 al.http.content_type = loggingEntry()->mem_obj->getReply()->content_type.termedBuf();
635 }
636
637 debugs(33, 9, "clientLogRequest: http.code='" << al.http.code << "'");
638
639 if (loggingEntry() && loggingEntry()->mem_obj)
640 al.cache.objectSize = loggingEntry()->contentLen();
641
642 al.cache.caddr.SetNoAddr();
643
644 if (getConn() != NULL) {
645 al.cache.caddr = getConn()->log_addr;
646 al.cache.port = cbdataReference(getConn()->port);
647 }
648
649 al.cache.requestSize = req_sz;
650 al.cache.requestHeadersSize = req_sz;
651
652 al.cache.replySize = out.size;
653 al.cache.replyHeadersSize = out.headers_sz;
654
655 al.cache.highOffset = out.offset;
656
657 al.cache.code = logType;
658
659 al.cache.msec = tvSubMsec(start_time, current_time);
660
661 if (request)
662 prepareLogWithRequestDetails(request, &al);
663
664 if (getConn() != NULL && getConn()->clientConnection != NULL && getConn()->clientConnection->rfc931[0])
665 al.cache.rfc931 = getConn()->clientConnection->rfc931;
666
667 #if USE_SSL && 0
668
669 /* This is broken. Fails if the connection has been closed. Needs
670 * to snarf the ssl details some place earlier..
671 */
672 if (getConn() != NULL)
673 al.cache.ssluser = sslGetUserEmail(fd_table[getConn()->fd].ssl);
674
675 #endif
676
677 ACLFilledChecklist *checklist = clientAclChecklistCreate(Config.accessList.log, this);
678
679 if (al.reply)
680 checklist->reply = HTTPMSGLOCK(al.reply);
681
682 if (!Config.accessList.log || checklist->fastCheck() == ACCESS_ALLOWED) {
683 if (request)
684 al.adapted_request = HTTPMSGLOCK(request);
685 accessLogLog(&al, checklist);
686 updateCounters();
687
688 if (getConn() != NULL && getConn()->clientConnection != NULL)
689 clientdbUpdate(getConn()->clientConnection->remote, logType, AnyP::PROTO_HTTP, out.size);
690 }
691
692 delete checklist;
693
694 accessLogFreeMemory(&al);
695 }
696
697 void
698 ClientHttpRequest::freeResources()
699 {
700 safe_free(uri);
701 safe_free(log_uri);
702 safe_free(redirect.location);
703 range_iter.boundary.clean();
704 HTTPMSGUNLOCK(request);
705
706 if (client_stream.tail)
707 clientStreamAbort((clientStreamNode *)client_stream.tail->data, this);
708 }
709
710 void
711 httpRequestFree(void *data)
712 {
713 ClientHttpRequest *http = (ClientHttpRequest *)data;
714 assert(http != NULL);
715 delete http;
716 }
717
718 bool
719 ConnStateData::areAllContextsForThisConnection() const
720 {
721 assert(this != NULL);
722 ClientSocketContext::Pointer context = getCurrentContext();
723
724 while (context.getRaw()) {
725 if (context->http->getConn() != this)
726 return false;
727
728 context = context->next;
729 }
730
731 return true;
732 }
733
734 void
735 ConnStateData::freeAllContexts()
736 {
737 ClientSocketContext::Pointer context;
738
739 while ((context = getCurrentContext()).getRaw() != NULL) {
740 assert(getCurrentContext() !=
741 getCurrentContext()->next);
742 context->connIsFinished();
743 assert (context != currentobject);
744 }
745 }
746
747 /// propagates abort event to all contexts
748 void
749 ConnStateData::notifyAllContexts(int xerrno)
750 {
751 typedef ClientSocketContext::Pointer CSCP;
752 for (CSCP c = getCurrentContext(); c.getRaw(); c = c->next)
753 c->noteIoError(xerrno);
754 }
755
756 /* This is a handler normally called by comm_close() */
757 void ConnStateData::connStateClosed(const CommCloseCbParams &io)
758 {
759 deleteThis("ConnStateData::connStateClosed");
760 }
761
762 // cleans up before destructor is called
763 void
764 ConnStateData::swanSong()
765 {
766 debugs(33, 2, HERE << clientConnection);
767 flags.readMore = false;
768 clientdbEstablished(clientConnection->remote, -1); /* decrement */
769 assert(areAllContextsForThisConnection());
770 freeAllContexts();
771 #if USE_AUTH
772 if (auth_user_request != NULL) {
773 debugs(33, 4, "ConnStateData::swanSong: freeing auth_user_request '" << auth_user_request << "' (this is '" << this << "')");
774 auth_user_request->onConnectionClose(this);
775 }
776 #endif
777
778 if (Comm::IsConnOpen(pinning.serverConnection))
779 pinning.serverConnection->close();
780 pinning.serverConnection = NULL;
781
782 if (Comm::IsConnOpen(clientConnection))
783 clientConnection->close();
784 clientConnection = NULL;
785
786 BodyProducer::swanSong();
787 flags.swanSang = true;
788 }
789
790 bool
791 ConnStateData::isOpen() const
792 {
793 return cbdataReferenceValid(this) && // XXX: checking "this" in a method
794 Comm::IsConnOpen(clientConnection) &&
795 !fd_table[clientConnection->fd].closing();
796 }
797
798 ConnStateData::~ConnStateData()
799 {
800 assert(this != NULL);
801 debugs(33, 3, HERE << clientConnection);
802
803 if (isOpen())
804 debugs(33, 1, "BUG: ConnStateData did not close " << clientConnection);
805
806 if (!flags.swanSang)
807 debugs(33, 1, "BUG: ConnStateData was not destroyed properly; " << clientConnection);
808
809 cbdataReferenceDone(port);
810
811 if (bodyPipe != NULL)
812 stopProducingFor(bodyPipe, false);
813 }
814
815 /**
816 * clientSetKeepaliveFlag() sets request->flags.proxy_keepalive.
817 * This is the client-side persistent connection flag. We need
818 * to set this relatively early in the request processing
819 * to handle hacks for broken servers and clients.
820 */
821 static void
822 clientSetKeepaliveFlag(ClientHttpRequest * http)
823 {
824 HttpRequest *request = http->request;
825
826 debugs(33, 3, "clientSetKeepaliveFlag: http_ver = " <<
827 request->http_ver.major << "." << request->http_ver.minor);
828 debugs(33, 3, "clientSetKeepaliveFlag: method = " <<
829 RequestMethodStr(request->method));
830
831 // TODO: move to HttpRequest::hdrCacheInit, just like HttpReply.
832 request->flags.proxy_keepalive = request->persistent() ? 1 : 0;
833 }
834
835 static int
836 clientIsContentLengthValid(HttpRequest * r)
837 {
838 switch (r->method.id()) {
839
840 case METHOD_GET:
841
842 case METHOD_HEAD:
843 /* We do not want to see a request entity on GET/HEAD requests */
844 return (r->content_length <= 0 || Config.onoff.request_entities);
845
846 default:
847 /* For other types of requests we don't care */
848 return 1;
849 }
850
851 /* NOT REACHED */
852 }
853
854 int
855 clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength)
856 {
857 if (Config.maxRequestBodySize &&
858 bodyLength > Config.maxRequestBodySize)
859 return 1; /* too large */
860
861 return 0;
862 }
863
864 #ifndef PURIFY
865 bool
866 connIsUsable(ConnStateData * conn)
867 {
868 if (conn == NULL || !cbdataReferenceValid(conn) || !Comm::IsConnOpen(conn->clientConnection))
869 return false;
870
871 return true;
872 }
873
874 #endif
875
876 // careful: the "current" context may be gone if we wrote an early response
877 ClientSocketContext::Pointer
878 ConnStateData::getCurrentContext() const
879 {
880 assert(this);
881 return currentobject;
882 }
883
884 void
885 ClientSocketContext::deferRecipientForLater(clientStreamNode * node, HttpReply * rep, StoreIOBuffer receivedData)
886 {
887 debugs(33, 2, "clientSocketRecipient: Deferring request " << http->uri);
888 assert(flags.deferred == 0);
889 flags.deferred = 1;
890 deferredparams.node = node;
891 deferredparams.rep = rep;
892 deferredparams.queuedBuffer = receivedData;
893 return;
894 }
895
896 int
897 responseFinishedOrFailed(HttpReply * rep, StoreIOBuffer const & receivedData)
898 {
899 if (rep == NULL && receivedData.data == NULL && receivedData.length == 0)
900 return 1;
901
902 return 0;
903 }
904
905 bool
906 ClientSocketContext::startOfOutput() const
907 {
908 return http->out.size == 0;
909 }
910
911 size_t
912 ClientSocketContext::lengthToSend(Range<int64_t> const &available)
913 {
914 /*the size of available range can always fit in a size_t type*/
915 size_t maximum = (size_t)available.size();
916
917 if (!http->request->range)
918 return maximum;
919
920 assert (canPackMoreRanges());
921
922 if (http->range_iter.debt() == -1)
923 return maximum;
924
925 assert (http->range_iter.debt() > 0);
926
927 /* TODO this + the last line could be a range intersection calculation */
928 if (available.start < http->range_iter.currentSpec()->offset)
929 return 0;
930
931 return min(http->range_iter.debt(), (int64_t)maximum);
932 }
933
934 void
935 ClientSocketContext::noteSentBodyBytes(size_t bytes)
936 {
937 http->out.offset += bytes;
938
939 if (!http->request->range)
940 return;
941
942 if (http->range_iter.debt() != -1) {
943 http->range_iter.debt(http->range_iter.debt() - bytes);
944 assert (http->range_iter.debt() >= 0);
945 }
946
947 /* debt() always stops at -1, below that is a bug */
948 assert (http->range_iter.debt() >= -1);
949 }
950
951 bool
952 ClientHttpRequest::multipartRangeRequest() const
953 {
954 return request->multipartRangeRequest();
955 }
956
957 bool
958 ClientSocketContext::multipartRangeRequest() const
959 {
960 return http->multipartRangeRequest();
961 }
962
963 void
964 ClientSocketContext::sendBody(HttpReply * rep, StoreIOBuffer bodyData)
965 {
966 assert(rep == NULL);
967
968 if (!multipartRangeRequest() && !http->request->flags.chunked_reply) {
969 size_t length = lengthToSend(bodyData.range());
970 noteSentBodyBytes (length);
971 AsyncCall::Pointer call = commCbCall(33, 5, "clientWriteBodyComplete",
972 CommIoCbPtrFun(clientWriteBodyComplete, this));
973 Comm::Write(clientConnection, bodyData.data, length, call, NULL);
974 return;
975 }
976
977 MemBuf mb;
978 mb.init();
979 if (multipartRangeRequest())
980 packRange(bodyData, &mb);
981 else
982 packChunk(bodyData, mb);
983
984 if (mb.contentSize()) {
985 /* write */
986 AsyncCall::Pointer call = commCbCall(33, 5, "clientWriteComplete",
987 CommIoCbPtrFun(clientWriteComplete, this));
988 Comm::Write(clientConnection, &mb, call);
989 } else
990 writeComplete(clientConnection, NULL, 0, COMM_OK);
991 }
992
993 /**
994 * Packs bodyData into mb using chunked encoding. Packs the last-chunk
995 * if bodyData is empty.
996 */
997 void
998 ClientSocketContext::packChunk(const StoreIOBuffer &bodyData, MemBuf &mb)
999 {
1000 const uint64_t length =
1001 static_cast<uint64_t>(lengthToSend(bodyData.range()));
1002 noteSentBodyBytes(length);
1003
1004 mb.Printf("%"PRIX64"\r\n", length);
1005 mb.append(bodyData.data, length);
1006 mb.Printf("\r\n");
1007 }
1008
1009 /** put terminating boundary for multiparts */
1010 static void
1011 clientPackTermBound(String boundary, MemBuf * mb)
1012 {
1013 mb->Printf("\r\n--" SQUIDSTRINGPH "--\r\n", SQUIDSTRINGPRINT(boundary));
1014 debugs(33, 6, "clientPackTermBound: buf offset: " << mb->size);
1015 }
1016
1017 /** appends a "part" HTTP header (as in a multi-part/range reply) to the buffer */
1018 static void
1019 clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb)
1020 {
1021 HttpHeader hdr(hoReply);
1022 Packer p;
1023 assert(rep);
1024 assert(spec);
1025
1026 /* put boundary */
1027 debugs(33, 5, "clientPackRangeHdr: appending boundary: " << boundary);
1028 /* rfc2046 requires to _prepend_ boundary with <crlf>! */
1029 mb->Printf("\r\n--" SQUIDSTRINGPH "\r\n", SQUIDSTRINGPRINT(boundary));
1030
1031 /* stuff the header with required entries and pack it */
1032
1033 if (rep->header.has(HDR_CONTENT_TYPE))
1034 hdr.putStr(HDR_CONTENT_TYPE, rep->header.getStr(HDR_CONTENT_TYPE));
1035
1036 httpHeaderAddContRange(&hdr, *spec, rep->content_length);
1037
1038 packerToMemInit(&p, mb);
1039
1040 hdr.packInto(&p);
1041
1042 packerClean(&p);
1043
1044 hdr.clean();
1045
1046 /* append <crlf> (we packed a header, not a reply) */
1047 mb->Printf("\r\n");
1048 }
1049
1050 /**
1051 * extracts a "range" from *buf and appends them to mb, updating
1052 * all offsets and such.
1053 */
1054 void
1055 ClientSocketContext::packRange(StoreIOBuffer const &source, MemBuf * mb)
1056 {
1057 HttpHdrRangeIter * i = &http->range_iter;
1058 Range<int64_t> available (source.range());
1059 char const *buf = source.data;
1060
1061 while (i->currentSpec() && available.size()) {
1062 const size_t copy_sz = lengthToSend(available);
1063
1064 if (copy_sz) {
1065 /*
1066 * intersection of "have" and "need" ranges must not be empty
1067 */
1068 assert(http->out.offset < i->currentSpec()->offset + i->currentSpec()->length);
1069 assert(http->out.offset + (int64_t)available.size() > i->currentSpec()->offset);
1070
1071 /*
1072 * put boundary and headers at the beginning of a range in a
1073 * multi-range
1074 */
1075
1076 if (http->multipartRangeRequest() && i->debt() == i->currentSpec()->length) {
1077 assert(http->memObject());
1078 clientPackRangeHdr(
1079 http->memObject()->getReply(), /* original reply */
1080 i->currentSpec(), /* current range */
1081 i->boundary, /* boundary, the same for all */
1082 mb);
1083 }
1084
1085 /*
1086 * append content
1087 */
1088 debugs(33, 3, "clientPackRange: appending " << copy_sz << " bytes");
1089
1090 noteSentBodyBytes (copy_sz);
1091
1092 mb->append(buf, copy_sz);
1093
1094 /*
1095 * update offsets
1096 */
1097 available.start += copy_sz;
1098
1099 buf += copy_sz;
1100
1101 }
1102
1103 if (!canPackMoreRanges()) {
1104 debugs(33, 3, "clientPackRange: Returning because !canPackMoreRanges.");
1105
1106 if (i->debt() == 0)
1107 /* put terminating boundary for multiparts */
1108 clientPackTermBound(i->boundary, mb);
1109
1110 return;
1111 }
1112
1113 int64_t nextOffset = getNextRangeOffset();
1114
1115 assert (nextOffset >= http->out.offset);
1116
1117 int64_t skip = nextOffset - http->out.offset;
1118
1119 /* adjust for not to be transmitted bytes */
1120 http->out.offset = nextOffset;
1121
1122 if (available.size() <= (uint64_t)skip)
1123 return;
1124
1125 available.start += skip;
1126
1127 buf += skip;
1128
1129 if (copy_sz == 0)
1130 return;
1131 }
1132 }
1133
1134 /** returns expected content length for multi-range replies
1135 * note: assumes that httpHdrRangeCanonize has already been called
1136 * warning: assumes that HTTP headers for individual ranges at the
1137 * time of the actuall assembly will be exactly the same as
1138 * the headers when clientMRangeCLen() is called */
1139 int
1140 ClientHttpRequest::mRangeCLen()
1141 {
1142 int64_t clen = 0;
1143 MemBuf mb;
1144
1145 assert(memObject());
1146
1147 mb.init();
1148 HttpHdrRange::iterator pos = request->range->begin();
1149
1150 while (pos != request->range->end()) {
1151 /* account for headers for this range */
1152 mb.reset();
1153 clientPackRangeHdr(memObject()->getReply(),
1154 *pos, range_iter.boundary, &mb);
1155 clen += mb.size;
1156
1157 /* account for range content */
1158 clen += (*pos)->length;
1159
1160 debugs(33, 6, "clientMRangeCLen: (clen += " << mb.size << " + " << (*pos)->length << ") == " << clen);
1161 ++pos;
1162 }
1163
1164 /* account for the terminating boundary */
1165 mb.reset();
1166
1167 clientPackTermBound(range_iter.boundary, &mb);
1168
1169 clen += mb.size;
1170
1171 mb.clean();
1172
1173 return clen;
1174 }
1175
1176 /**
1177 * returns true if If-Range specs match reply, false otherwise
1178 */
1179 static int
1180 clientIfRangeMatch(ClientHttpRequest * http, HttpReply * rep)
1181 {
1182 const TimeOrTag spec = http->request->header.getTimeOrTag(HDR_IF_RANGE);
1183 /* check for parsing falure */
1184
1185 if (!spec.valid)
1186 return 0;
1187
1188 /* got an ETag? */
1189 if (spec.tag.str) {
1190 ETag rep_tag = rep->header.getETag(HDR_ETAG);
1191 debugs(33, 3, "clientIfRangeMatch: ETags: " << spec.tag.str << " and " <<
1192 (rep_tag.str ? rep_tag.str : "<none>"));
1193
1194 if (!rep_tag.str)
1195 return 0; /* entity has no etag to compare with! */
1196
1197 if (spec.tag.weak || rep_tag.weak) {
1198 debugs(33, 1, "clientIfRangeMatch: Weak ETags are not allowed in If-Range: " << spec.tag.str << " ? " << rep_tag.str);
1199 return 0; /* must use strong validator for sub-range requests */
1200 }
1201
1202 return etagIsStrongEqual(rep_tag, spec.tag);
1203 }
1204
1205 /* got modification time? */
1206 if (spec.time >= 0) {
1207 return http->storeEntry()->lastmod <= spec.time;
1208 }
1209
1210 assert(0); /* should not happen */
1211 return 0;
1212 }
1213
1214 /**
1215 * generates a "unique" boundary string for multipart responses
1216 * the caller is responsible for cleaning the string */
1217 String
1218 ClientHttpRequest::rangeBoundaryStr() const
1219 {
1220 assert(this);
1221 const char *key;
1222 String b(APP_FULLNAME);
1223 b.append(":",1);
1224 key = storeEntry()->getMD5Text();
1225 b.append(key, strlen(key));
1226 return b;
1227 }
1228
1229 /** adds appropriate Range headers if needed */
1230 void
1231 ClientSocketContext::buildRangeHeader(HttpReply * rep)
1232 {
1233 HttpHeader *hdr = rep ? &rep->header : 0;
1234 const char *range_err = NULL;
1235 HttpRequest *request = http->request;
1236 assert(request->range);
1237 /* check if we still want to do ranges */
1238
1239 int64_t roffLimit = request->getRangeOffsetLimit();
1240
1241 if (!rep)
1242 range_err = "no [parse-able] reply";
1243 else if ((rep->sline.status != HTTP_OK) && (rep->sline.status != HTTP_PARTIAL_CONTENT))
1244 range_err = "wrong status code";
1245 else if (hdr->has(HDR_CONTENT_RANGE))
1246 range_err = "origin server does ranges";
1247 else if (rep->content_length < 0)
1248 range_err = "unknown length";
1249 else if (rep->content_length != http->memObject()->getReply()->content_length)
1250 range_err = "INCONSISTENT length"; /* a bug? */
1251
1252 /* hits only - upstream peer determines correct behaviour on misses, and client_side_reply determines
1253 * hits candidates
1254 */
1255 else if (logTypeIsATcpHit(http->logType) && http->request->header.has(HDR_IF_RANGE) && !clientIfRangeMatch(http, rep))
1256 range_err = "If-Range match failed";
1257 else if (!http->request->range->canonize(rep))
1258 range_err = "canonization failed";
1259 else if (http->request->range->isComplex())
1260 range_err = "too complex range header";
1261 else if (!logTypeIsATcpHit(http->logType) && http->request->range->offsetLimitExceeded(roffLimit))
1262 range_err = "range outside range_offset_limit";
1263
1264 /* get rid of our range specs on error */
1265 if (range_err) {
1266 /* XXX We do this here because we need canonisation etc. However, this current
1267 * code will lead to incorrect store offset requests - the store will have the
1268 * offset data, but we won't be requesting it.
1269 * So, we can either re-request, or generate an error
1270 */
1271 debugs(33, 3, "clientBuildRangeHeader: will not do ranges: " << range_err << ".");
1272 delete http->request->range;
1273 http->request->range = NULL;
1274 } else {
1275 /* XXX: TODO: Review, this unconditional set may be wrong. - TODO: review. */
1276 httpStatusLineSet(&rep->sline, rep->sline.version,
1277 HTTP_PARTIAL_CONTENT, NULL);
1278 // web server responded with a valid, but unexpected range.
1279 // will (try-to) forward as-is.
1280 //TODO: we should cope with multirange request/responses
1281 bool replyMatchRequest = rep->content_range != NULL ?
1282 request->range->contains(rep->content_range->spec) :
1283 true;
1284 const int spec_count = http->request->range->specs.count;
1285 int64_t actual_clen = -1;
1286
1287 debugs(33, 3, "clientBuildRangeHeader: range spec count: " <<
1288 spec_count << " virgin clen: " << rep->content_length);
1289 assert(spec_count > 0);
1290 /* append appropriate header(s) */
1291
1292 if (spec_count == 1) {
1293 if (!replyMatchRequest) {
1294 hdr->delById(HDR_CONTENT_RANGE);
1295 hdr->putContRange(rep->content_range);
1296 actual_clen = rep->content_length;
1297 //http->range_iter.pos = rep->content_range->spec.begin();
1298 (*http->range_iter.pos)->offset = rep->content_range->spec.offset;
1299 (*http->range_iter.pos)->length = rep->content_range->spec.length;
1300
1301 } else {
1302 HttpHdrRange::iterator pos = http->request->range->begin();
1303 assert(*pos);
1304 /* append Content-Range */
1305
1306 if (!hdr->has(HDR_CONTENT_RANGE)) {
1307 /* No content range, so this was a full object we are
1308 * sending parts of.
1309 */
1310 httpHeaderAddContRange(hdr, **pos, rep->content_length);
1311 }
1312
1313 /* set new Content-Length to the actual number of bytes
1314 * transmitted in the message-body */
1315 actual_clen = (*pos)->length;
1316 }
1317 } else {
1318 /* multipart! */
1319 /* generate boundary string */
1320 http->range_iter.boundary = http->rangeBoundaryStr();
1321 /* delete old Content-Type, add ours */
1322 hdr->delById(HDR_CONTENT_TYPE);
1323 httpHeaderPutStrf(hdr, HDR_CONTENT_TYPE,
1324 "multipart/byteranges; boundary=\"" SQUIDSTRINGPH "\"",
1325 SQUIDSTRINGPRINT(http->range_iter.boundary));
1326 /* Content-Length is not required in multipart responses
1327 * but it is always nice to have one */
1328 actual_clen = http->mRangeCLen();
1329 /* http->out needs to start where we want data at */
1330 http->out.offset = http->range_iter.currentSpec()->offset;
1331 }
1332
1333 /* replace Content-Length header */
1334 assert(actual_clen >= 0);
1335
1336 hdr->delById(HDR_CONTENT_LENGTH);
1337
1338 hdr->putInt64(HDR_CONTENT_LENGTH, actual_clen);
1339
1340 debugs(33, 3, "clientBuildRangeHeader: actual content length: " << actual_clen);
1341
1342 /* And start the range iter off */
1343 http->range_iter.updateSpec();
1344 }
1345 }
1346
1347 void
1348 ClientSocketContext::prepareReply(HttpReply * rep)
1349 {
1350 reply = rep;
1351
1352 if (http->request->range)
1353 buildRangeHeader(rep);
1354 }
1355
1356 void
1357 ClientSocketContext::sendStartOfMessage(HttpReply * rep, StoreIOBuffer bodyData)
1358 {
1359 prepareReply(rep);
1360 assert (rep);
1361 MemBuf *mb = rep->pack();
1362
1363 // dump now, so we dont output any body.
1364 debugs(11, 2, "HTTP Client " << clientConnection);
1365 debugs(11, 2, "HTTP Client REPLY:\n---------\n" << mb->buf << "\n----------");
1366
1367 /* Save length of headers for persistent conn checks */
1368 http->out.headers_sz = mb->contentSize();
1369 #if HEADERS_LOG
1370
1371 headersLog(0, 0, http->request->method, rep);
1372 #endif
1373
1374 if (bodyData.data && bodyData.length) {
1375 if (multipartRangeRequest())
1376 packRange(bodyData, mb);
1377 else if (http->request->flags.chunked_reply) {
1378 packChunk(bodyData, *mb);
1379 } else {
1380 size_t length = lengthToSend(bodyData.range());
1381 noteSentBodyBytes (length);
1382
1383 mb->append(bodyData.data, length);
1384 }
1385 }
1386
1387 /* write */
1388 debugs(33,7, HERE << "sendStartOfMessage schedules clientWriteComplete");
1389 AsyncCall::Pointer call = commCbCall(33, 5, "clientWriteComplete",
1390 CommIoCbPtrFun(clientWriteComplete, this));
1391 Comm::Write(clientConnection, mb, call);
1392 delete mb;
1393 }
1394
1395 /**
1396 * Write a chunk of data to a client socket. If the reply is present,
1397 * send the reply headers down the wire too, and clean them up when
1398 * finished.
1399 * Pre-condition:
1400 * The request is one backed by a connection, not an internal request.
1401 * data context is not NULL
1402 * There are no more entries in the stream chain.
1403 */
1404 static void
1405 clientSocketRecipient(clientStreamNode * node, ClientHttpRequest * http,
1406 HttpReply * rep, StoreIOBuffer receivedData)
1407 {
1408 /* Test preconditions */
1409 assert(node != NULL);
1410 PROF_start(clientSocketRecipient);
1411 /* TODO: handle this rather than asserting
1412 * - it should only ever happen if we cause an abort and
1413 * the callback chain loops back to here, so we can simply return.
1414 * However, that itself shouldn't happen, so it stays as an assert for now.
1415 */
1416 assert(cbdataReferenceValid(node));
1417 assert(node->node.next == NULL);
1418 ClientSocketContext::Pointer context = dynamic_cast<ClientSocketContext *>(node->data.getRaw());
1419 assert(context != NULL);
1420 assert(connIsUsable(http->getConn()));
1421
1422 /* TODO: check offset is what we asked for */
1423
1424 if (context != http->getConn()->getCurrentContext()) {
1425 context->deferRecipientForLater(node, rep, receivedData);
1426 PROF_stop(clientSocketRecipient);
1427 return;
1428 }
1429
1430 // After sending Transfer-Encoding: chunked (at least), always send
1431 // the last-chunk if there was no error, ignoring responseFinishedOrFailed.
1432 const bool mustSendLastChunk = http->request->flags.chunked_reply &&
1433 !http->request->flags.stream_error && !context->startOfOutput();
1434 if (responseFinishedOrFailed(rep, receivedData) && !mustSendLastChunk) {
1435 context->writeComplete(context->clientConnection, NULL, 0, COMM_OK);
1436 PROF_stop(clientSocketRecipient);
1437 return;
1438 }
1439
1440 if (!context->startOfOutput())
1441 context->sendBody(rep, receivedData);
1442 else {
1443 assert(rep);
1444 http->al.reply = HTTPMSGLOCK(rep);
1445 context->sendStartOfMessage(rep, receivedData);
1446 }
1447
1448 PROF_stop(clientSocketRecipient);
1449 }
1450
1451 /**
1452 * Called when a downstream node is no longer interested in
1453 * our data. As we are a terminal node, this means on aborts
1454 * only
1455 */
1456 void
1457 clientSocketDetach(clientStreamNode * node, ClientHttpRequest * http)
1458 {
1459 /* Test preconditions */
1460 assert(node != NULL);
1461 /* TODO: handle this rather than asserting
1462 * - it should only ever happen if we cause an abort and
1463 * the callback chain loops back to here, so we can simply return.
1464 * However, that itself shouldn't happen, so it stays as an assert for now.
1465 */
1466 assert(cbdataReferenceValid(node));
1467 /* Set null by ContextFree */
1468 assert(node->node.next == NULL);
1469 /* this is the assert discussed above */
1470 assert(NULL == dynamic_cast<ClientSocketContext *>(node->data.getRaw()));
1471 /* We are only called when the client socket shutsdown.
1472 * Tell the prev pipeline member we're finished
1473 */
1474 clientStreamDetach(node, http);
1475 }
1476
1477 static void
1478 clientWriteBodyComplete(const Comm::ConnectionPointer &conn, char *buf, size_t size, comm_err_t errflag, int xerrno, void *data)
1479 {
1480 debugs(33,7, HERE << "clientWriteBodyComplete schedules clientWriteComplete");
1481 clientWriteComplete(conn, NULL, size, errflag, xerrno, data);
1482 }
1483
1484 void
1485 ConnStateData::readNextRequest()
1486 {
1487 debugs(33, 5, HERE << clientConnection << " reading next req");
1488
1489 fd_note(clientConnection->fd, "Idle client: Waiting for next request");
1490 /**
1491 * Set the timeout BEFORE calling clientReadRequest().
1492 */
1493 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
1494 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
1495 TimeoutDialer, this, ConnStateData::requestTimeout);
1496 commSetConnTimeout(clientConnection, Config.Timeout.clientIdlePconn, timeoutCall);
1497
1498 readSomeData();
1499 /** Please don't do anything with the FD past here! */
1500 }
1501
1502 static void
1503 ClientSocketContextPushDeferredIfNeeded(ClientSocketContext::Pointer deferredRequest, ConnStateData * conn)
1504 {
1505 debugs(33, 2, HERE << conn->clientConnection << " Sending next");
1506
1507 /** If the client stream is waiting on a socket write to occur, then */
1508
1509 if (deferredRequest->flags.deferred) {
1510 /** NO data is allowed to have been sent. */
1511 assert(deferredRequest->http->out.size == 0);
1512 /** defer now. */
1513 clientSocketRecipient(deferredRequest->deferredparams.node,
1514 deferredRequest->http,
1515 deferredRequest->deferredparams.rep,
1516 deferredRequest->deferredparams.queuedBuffer);
1517 }
1518
1519 /** otherwise, the request is still active in a callbacksomewhere,
1520 * and we are done
1521 */
1522 }
1523
1524 /// called when we have successfully finished writing the response
1525 void
1526 ClientSocketContext::keepaliveNextRequest()
1527 {
1528 ConnStateData * conn = http->getConn();
1529
1530 debugs(33, 3, HERE << "ConnnStateData(" << conn->clientConnection << "), Context(" << clientConnection << ")");
1531 connIsFinished();
1532
1533 if (conn->pinning.pinned && !Comm::IsConnOpen(conn->pinning.serverConnection)) {
1534 debugs(33, 2, HERE << conn->clientConnection << " Connection was pinned but server side gone. Terminating client connection");
1535 conn->clientConnection->close();
1536 return;
1537 }
1538
1539 /** \par
1540 * We are done with the response, and we are either still receiving request
1541 * body (early response!) or have already stopped receiving anything.
1542 *
1543 * If we are still receiving, then clientParseRequest() below will fail.
1544 * (XXX: but then we will call readNextRequest() which may succeed and
1545 * execute a smuggled request as we are not done with the current request).
1546 *
1547 * If we stopped because we got everything, then try the next request.
1548 *
1549 * If we stopped receiving because of an error, then close now to avoid
1550 * getting stuck and to prevent accidental request smuggling.
1551 */
1552
1553 if (const char *reason = conn->stoppedReceiving()) {
1554 debugs(33, 3, HERE << "closing for earlier request error: " << reason);
1555 conn->clientConnection->close();
1556 return;
1557 }
1558
1559 /** \par
1560 * Attempt to parse a request from the request buffer.
1561 * If we've been fed a pipelined request it may already
1562 * be in our read buffer.
1563 *
1564 \par
1565 * This needs to fall through - if we're unlucky and parse the _last_ request
1566 * from our read buffer we may never re-register for another client read.
1567 */
1568
1569 if (conn->clientParseRequests()) {
1570 debugs(33, 3, HERE << conn->clientConnection << ": parsed next request from buffer");
1571 }
1572
1573 /** \par
1574 * Either we need to kick-start another read or, if we have
1575 * a half-closed connection, kill it after the last request.
1576 * This saves waiting for half-closed connections to finished being
1577 * half-closed _AND_ then, sometimes, spending "Timeout" time in
1578 * the keepalive "Waiting for next request" state.
1579 */
1580 if (commIsHalfClosed(conn->clientConnection->fd) && (conn->getConcurrentRequestCount() == 0)) {
1581 debugs(33, 3, "ClientSocketContext::keepaliveNextRequest: half-closed client with no pending requests, closing");
1582 conn->clientConnection->close();
1583 return;
1584 }
1585
1586 ClientSocketContext::Pointer deferredRequest;
1587
1588 /** \par
1589 * At this point we either have a parsed request (which we've
1590 * kicked off the processing for) or not. If we have a deferred
1591 * request (parsed but deferred for pipeling processing reasons)
1592 * then look at processing it. If not, simply kickstart
1593 * another read.
1594 */
1595
1596 if ((deferredRequest = conn->getCurrentContext()).getRaw()) {
1597 debugs(33, 3, HERE << conn->clientConnection << ": calling PushDeferredIfNeeded");
1598 ClientSocketContextPushDeferredIfNeeded(deferredRequest, conn);
1599 } else if (conn->flags.readMore) {
1600 debugs(33, 3, HERE << conn->clientConnection << ": calling conn->readNextRequest()");
1601 conn->readNextRequest();
1602 } else {
1603 // XXX: Can this happen? CONNECT tunnels have deferredRequest set.
1604 debugs(33, DBG_IMPORTANT, HERE << "abandoning " << conn->clientConnection);
1605 }
1606 }
1607
1608 void
1609 clientUpdateSocketStats(log_type logType, size_t size)
1610 {
1611 if (size == 0)
1612 return;
1613
1614 kb_incr(&statCounter.client_http.kbytes_out, size);
1615
1616 if (logTypeIsATcpHit(logType))
1617 kb_incr(&statCounter.client_http.hit_kbytes_out, size);
1618 }
1619
1620 /**
1621 * increments iterator "i"
1622 * used by clientPackMoreRanges
1623 *
1624 \retval true there is still data available to pack more ranges
1625 \retval false
1626 */
1627 bool
1628 ClientSocketContext::canPackMoreRanges() const
1629 {
1630 /** first update iterator "i" if needed */
1631
1632 if (!http->range_iter.debt()) {
1633 debugs(33, 5, HERE << "At end of current range spec for " << clientConnection);
1634
1635 if (http->range_iter.pos.incrementable())
1636 ++http->range_iter.pos;
1637
1638 http->range_iter.updateSpec();
1639 }
1640
1641 assert(!http->range_iter.debt() == !http->range_iter.currentSpec());
1642
1643 /* paranoid sync condition */
1644 /* continue condition: need_more_data */
1645 debugs(33, 5, "ClientSocketContext::canPackMoreRanges: returning " << (http->range_iter.currentSpec() ? true : false));
1646 return http->range_iter.currentSpec() ? true : false;
1647 }
1648
1649 int64_t
1650 ClientSocketContext::getNextRangeOffset() const
1651 {
1652 if (http->request->range) {
1653 /* offset in range specs does not count the prefix of an http msg */
1654 debugs (33, 5, "ClientSocketContext::getNextRangeOffset: http offset " << http->out.offset);
1655 /* check: reply was parsed and range iterator was initialized */
1656 assert(http->range_iter.valid);
1657 /* filter out data according to range specs */
1658 assert (canPackMoreRanges());
1659 {
1660 int64_t start; /* offset of still missing data */
1661 assert(http->range_iter.currentSpec());
1662 start = http->range_iter.currentSpec()->offset + http->range_iter.currentSpec()->length - http->range_iter.debt();
1663 debugs(33, 3, "clientPackMoreRanges: in: offset: " << http->out.offset);
1664 debugs(33, 3, "clientPackMoreRanges: out:"
1665 " start: " << start <<
1666 " spec[" << http->range_iter.pos - http->request->range->begin() << "]:" <<
1667 " [" << http->range_iter.currentSpec()->offset <<
1668 ", " << http->range_iter.currentSpec()->offset + http->range_iter.currentSpec()->length << "),"
1669 " len: " << http->range_iter.currentSpec()->length <<
1670 " debt: " << http->range_iter.debt());
1671 if (http->range_iter.currentSpec()->length != -1)
1672 assert(http->out.offset <= start); /* we did not miss it */
1673
1674 return start;
1675 }
1676
1677 } else if (reply && reply->content_range) {
1678 /* request does not have ranges, but reply does */
1679 /** \todo FIXME: should use range_iter_pos on reply, as soon as reply->content_range
1680 * becomes HttpHdrRange rather than HttpHdrRangeSpec.
1681 */
1682 return http->out.offset + reply->content_range->spec.offset;
1683 }
1684
1685 return http->out.offset;
1686 }
1687
1688 void
1689 ClientSocketContext::pullData()
1690 {
1691 debugs(33, 5, HERE << clientConnection << " attempting to pull upstream data");
1692
1693 /* More data will be coming from the stream. */
1694 StoreIOBuffer readBuffer;
1695 /* XXX: Next requested byte in the range sequence */
1696 /* XXX: length = getmaximumrangelenfgth */
1697 readBuffer.offset = getNextRangeOffset();
1698 readBuffer.length = HTTP_REQBUF_SZ;
1699 readBuffer.data = reqbuf;
1700 /* we may note we have reached the end of the wanted ranges */
1701 clientStreamRead(getTail(), http, readBuffer);
1702 }
1703
1704 clientStream_status_t
1705 ClientSocketContext::socketState()
1706 {
1707 switch (clientStreamStatus(getTail(), http)) {
1708
1709 case STREAM_NONE:
1710 /* check for range support ending */
1711
1712 if (http->request->range) {
1713 /* check: reply was parsed and range iterator was initialized */
1714 assert(http->range_iter.valid);
1715 /* filter out data according to range specs */
1716
1717 if (!canPackMoreRanges()) {
1718 debugs(33, 5, HERE << "Range request at end of returnable " <<
1719 "range sequence on " << clientConnection);
1720
1721 if (http->request->flags.proxy_keepalive)
1722 return STREAM_COMPLETE;
1723 else
1724 return STREAM_UNPLANNED_COMPLETE;
1725 }
1726 } else if (reply && reply->content_range) {
1727 /* reply has content-range, but Squid is not managing ranges */
1728 const int64_t &bytesSent = http->out.offset;
1729 const int64_t &bytesExpected = reply->content_range->spec.length;
1730
1731 debugs(33, 7, HERE << "body bytes sent vs. expected: " <<
1732 bytesSent << " ? " << bytesExpected << " (+" <<
1733 reply->content_range->spec.offset << ")");
1734
1735 // did we get at least what we expected, based on range specs?
1736
1737 if (bytesSent == bytesExpected) { // got everything
1738 if (http->request->flags.proxy_keepalive)
1739 return STREAM_COMPLETE;
1740 else
1741 return STREAM_UNPLANNED_COMPLETE;
1742 }
1743
1744 // The logic below is not clear: If we got more than we
1745 // expected why would persistency matter? Should not this
1746 // always be an error?
1747 if (bytesSent > bytesExpected) { // got extra
1748 if (http->request->flags.proxy_keepalive)
1749 return STREAM_COMPLETE;
1750 else
1751 return STREAM_UNPLANNED_COMPLETE;
1752 }
1753
1754 // did not get enough yet, expecting more
1755 }
1756
1757 return STREAM_NONE;
1758
1759 case STREAM_COMPLETE:
1760 return STREAM_COMPLETE;
1761
1762 case STREAM_UNPLANNED_COMPLETE:
1763 return STREAM_UNPLANNED_COMPLETE;
1764
1765 case STREAM_FAILED:
1766 return STREAM_FAILED;
1767 }
1768
1769 fatal ("unreachable code\n");
1770 return STREAM_NONE;
1771 }
1772
1773 /**
1774 * A write has just completed to the client, or we have just realised there is
1775 * no more data to send.
1776 */
1777 void
1778 clientWriteComplete(const Comm::ConnectionPointer &conn, char *bufnotused, size_t size, comm_err_t errflag, int xerrno, void *data)
1779 {
1780 ClientSocketContext *context = (ClientSocketContext *)data;
1781 context->writeComplete(conn, bufnotused, size, errflag);
1782 }
1783
1784 /// remembers the abnormal connection termination for logging purposes
1785 void
1786 ClientSocketContext::noteIoError(const int xerrno)
1787 {
1788 if (http) {
1789 if (xerrno == ETIMEDOUT)
1790 http->al.http.timedout = true;
1791 else // even if xerrno is zero (which means read abort/eof)
1792 http->al.http.aborted = true;
1793 }
1794 }
1795
1796
1797 void
1798 ClientSocketContext::doClose()
1799 {
1800 clientConnection->close();
1801 }
1802
1803 /// called when we encounter a response-related error
1804 void
1805 ClientSocketContext::initiateClose(const char *reason)
1806 {
1807 http->getConn()->stopSending(reason); // closes ASAP
1808 }
1809
1810 void
1811 ConnStateData::stopSending(const char *error)
1812 {
1813 debugs(33, 4, HERE << "sending error (" << clientConnection << "): " << error <<
1814 "; old receiving error: " <<
1815 (stoppedReceiving() ? stoppedReceiving_ : "none"));
1816
1817 if (const char *oldError = stoppedSending()) {
1818 debugs(33, 3, HERE << "already stopped sending: " << oldError);
1819 return; // nothing has changed as far as this connection is concerned
1820 }
1821 stoppedSending_ = error;
1822
1823 if (!stoppedReceiving()) {
1824 if (const int64_t expecting = mayNeedToReadMoreBody()) {
1825 debugs(33, 5, HERE << "must still read " << expecting <<
1826 " request body bytes with " << in.notYetUsed << " unused");
1827 return; // wait for the request receiver to finish reading
1828 }
1829 }
1830
1831 clientConnection->close();
1832 }
1833
1834 void
1835 ClientSocketContext::writeComplete(const Comm::ConnectionPointer &conn, char *bufnotused, size_t size, comm_err_t errflag)
1836 {
1837 const StoreEntry *entry = http->storeEntry();
1838 http->out.size += size;
1839 debugs(33, 5, HERE << conn << ", sz " << size <<
1840 ", err " << errflag << ", off " << http->out.size << ", len " <<
1841 (entry ? entry->objectLen() : 0));
1842 clientUpdateSocketStats(http->logType, size);
1843
1844 /* Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up */
1845
1846 if (errflag == COMM_ERR_CLOSING || !Comm::IsConnOpen(conn))
1847 return;
1848
1849 if (errflag || clientHttpRequestStatus(conn->fd, http)) {
1850 initiateClose("failure or true request status");
1851 /* Do we leak here ? */
1852 return;
1853 }
1854
1855 switch (socketState()) {
1856
1857 case STREAM_NONE:
1858 pullData();
1859 break;
1860
1861 case STREAM_COMPLETE:
1862 debugs(33, 5, HERE << conn << " Keeping Alive");
1863 keepaliveNextRequest();
1864 return;
1865
1866 case STREAM_UNPLANNED_COMPLETE:
1867 initiateClose("STREAM_UNPLANNED_COMPLETE");
1868 return;
1869
1870 case STREAM_FAILED:
1871 initiateClose("STREAM_FAILED");
1872 return;
1873
1874 default:
1875 fatal("Hit unreachable code in clientWriteComplete\n");
1876 }
1877 }
1878
1879 extern "C" CSR clientGetMoreData;
1880 extern "C" CSS clientReplyStatus;
1881 extern "C" CSD clientReplyDetach;
1882
1883 static ClientSocketContext *
1884 parseHttpRequestAbort(ConnStateData * csd, const char *uri)
1885 {
1886 ClientHttpRequest *http;
1887 ClientSocketContext *context;
1888 StoreIOBuffer tempBuffer;
1889 http = new ClientHttpRequest(csd);
1890 http->req_sz = csd->in.notYetUsed;
1891 http->uri = xstrdup(uri);
1892 setLogUri (http, uri);
1893 context = ClientSocketContextNew(csd->clientConnection, http);
1894 tempBuffer.data = context->reqbuf;
1895 tempBuffer.length = HTTP_REQBUF_SZ;
1896 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
1897 clientReplyStatus, new clientReplyContext(http), clientSocketRecipient,
1898 clientSocketDetach, context, tempBuffer);
1899 return context;
1900 }
1901
1902 char *
1903 skipLeadingSpace(char *aString)
1904 {
1905 char *result = aString;
1906
1907 while (xisspace(*aString))
1908 ++aString;
1909
1910 return result;
1911 }
1912
1913 /**
1914 * 'end' defaults to NULL for backwards compatibility
1915 * remove default value if we ever get rid of NULL-terminated
1916 * request buffers.
1917 */
1918 const char *
1919 findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char *end)
1920 {
1921 if (NULL == end) {
1922 end = uriAndHTTPVersion + strcspn(uriAndHTTPVersion, "\r\n");
1923 assert(end);
1924 }
1925
1926 for (; end > uriAndHTTPVersion; end--) {
1927 if (*end == '\n' || *end == '\r')
1928 continue;
1929
1930 if (xisspace(*end)) {
1931 if (strncasecmp(end + 1, "HTTP/", 5) == 0)
1932 return end + 1;
1933 else
1934 break;
1935 }
1936 }
1937
1938 return NULL;
1939 }
1940
1941 void
1942 setLogUri(ClientHttpRequest * http, char const *uri, bool cleanUrl)
1943 {
1944 safe_free(http->log_uri);
1945
1946 if (!cleanUrl)
1947 // The uri is already clean just dump it.
1948 http->log_uri = xstrndup(uri, MAX_URL);
1949 else {
1950 int flags = 0;
1951 switch (Config.uri_whitespace) {
1952 case URI_WHITESPACE_ALLOW:
1953 flags |= RFC1738_ESCAPE_NOSPACE;
1954
1955 case URI_WHITESPACE_ENCODE:
1956 flags |= RFC1738_ESCAPE_UNESCAPED;
1957 http->log_uri = xstrndup(rfc1738_do_escape(uri, flags), MAX_URL);
1958 break;
1959
1960 case URI_WHITESPACE_CHOP: {
1961 flags |= RFC1738_ESCAPE_NOSPACE;
1962 flags |= RFC1738_ESCAPE_UNESCAPED;
1963 http->log_uri = xstrndup(rfc1738_do_escape(uri, flags), MAX_URL);
1964 int pos = strcspn(http->log_uri, w_space);
1965 http->log_uri[pos] = '\0';
1966 }
1967 break;
1968
1969 case URI_WHITESPACE_DENY:
1970 case URI_WHITESPACE_STRIP:
1971 default: {
1972 const char *t;
1973 char *tmp_uri = static_cast<char*>(xmalloc(strlen(uri) + 1));
1974 char *q = tmp_uri;
1975 t = uri;
1976 while (*t) {
1977 if (!xisspace(*t))
1978 *q++ = *t;
1979 t++;
1980 }
1981 *q = '\0';
1982 http->log_uri = xstrndup(rfc1738_escape_unescaped(tmp_uri), MAX_URL);
1983 xfree(tmp_uri);
1984 }
1985 break;
1986 }
1987 }
1988 }
1989
1990 static void
1991 prepareAcceleratedURL(ConnStateData * conn, ClientHttpRequest *http, char *url, const char *req_hdr)
1992 {
1993 int vhost = conn->port->vhost;
1994 int vport = conn->port->vport;
1995 char *host;
1996 char ipbuf[MAX_IPSTRLEN];
1997
1998 http->flags.accel = 1;
1999
2000 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
2001
2002 if (strncasecmp(url, "cache_object://", 15) == 0)
2003 return; /* already in good shape */
2004
2005 if (*url != '/') {
2006 if (conn->port->vhost)
2007 return; /* already in good shape */
2008
2009 /* else we need to ignore the host name */
2010 url = strstr(url, "//");
2011
2012 #if SHOULD_REJECT_UNKNOWN_URLS
2013
2014 if (!url) {
2015 hp->request_parse_status = HTTP_BAD_REQUEST;
2016 return parseHttpRequestAbort(conn, "error:invalid-request");
2017 }
2018 #endif
2019
2020 if (url)
2021 url = strchr(url + 2, '/');
2022
2023 if (!url)
2024 url = (char *) "/";
2025 }
2026
2027 if (vport < 0)
2028 vport = http->getConn()->clientConnection->local.GetPort();
2029
2030 const bool switchedToHttps = conn->switchedToHttps();
2031 const bool tryHostHeader = vhost || switchedToHttps;
2032 if (tryHostHeader && (host = mime_get_header(req_hdr, "Host")) != NULL) {
2033 debugs(33, 5, "ACCEL VHOST REWRITE: vhost=" << host << " + vport=" << vport);
2034 char thost[256];
2035 if (vport > 0) {
2036 thost[0] = '\0';
2037 char *t = NULL;
2038 if (host[strlen(host)] != ']' && (t = strrchr(host,':')) != NULL) {
2039 strncpy(thost, host, (t-host));
2040 snprintf(thost+(t-host), sizeof(thost)-(t-host), ":%d", vport);
2041 host = thost;
2042 } else if (!t) {
2043 snprintf(thost, sizeof(thost), "%s:%d",host, vport);
2044 host = thost;
2045 }
2046 } // else nothing to alter port-wise.
2047 int url_sz = strlen(url) + 32 + Config.appendDomainLen +
2048 strlen(host);
2049 http->uri = (char *)xcalloc(url_sz, 1);
2050 const char *protocol = switchedToHttps ?
2051 "https" : conn->port->protocol;
2052 snprintf(http->uri, url_sz, "%s://%s%s", protocol, host, url);
2053 debugs(33, 5, "ACCEL VHOST REWRITE: '" << http->uri << "'");
2054 } else if (conn->port->defaultsite /* && !vhost */) {
2055 debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: defaultsite=" << conn->port->defaultsite << " + vport=" << vport);
2056 int url_sz = strlen(url) + 32 + Config.appendDomainLen +
2057 strlen(conn->port->defaultsite);
2058 http->uri = (char *)xcalloc(url_sz, 1);
2059 char vportStr[32];
2060 vportStr[0] = '\0';
2061 if (vport > 0) {
2062 snprintf(vportStr, sizeof(vportStr),":%d",vport);
2063 }
2064 snprintf(http->uri, url_sz, "%s://%s%s%s",
2065 conn->port->protocol, conn->port->defaultsite, vportStr, url);
2066 debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: '" << http->uri <<"'");
2067 } else if (vport > 0 /* && (!vhost || no Host:) */) {
2068 debugs(33, 5, "ACCEL VPORT REWRITE: http_port IP + vport=" << vport);
2069 /* Put the local socket IP address as the hostname, with whatever vport we found */
2070 int url_sz = strlen(url) + 32 + Config.appendDomainLen;
2071 http->uri = (char *)xcalloc(url_sz, 1);
2072 http->getConn()->clientConnection->local.ToHostname(ipbuf,MAX_IPSTRLEN);
2073 snprintf(http->uri, url_sz, "%s://%s:%d%s",
2074 http->getConn()->port->protocol,
2075 ipbuf, vport, url);
2076 debugs(33, 5, "ACCEL VPORT REWRITE: '" << http->uri << "'");
2077 }
2078 }
2079
2080 static void
2081 prepareTransparentURL(ConnStateData * conn, ClientHttpRequest *http, char *url, const char *req_hdr)
2082 {
2083 char *host;
2084 char ipbuf[MAX_IPSTRLEN];
2085
2086 if (*url != '/')
2087 return; /* already in good shape */
2088
2089 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
2090 // BUG 2976: Squid only accepts intercepted HTTP.
2091
2092 if ((host = mime_get_header(req_hdr, "Host")) != NULL) {
2093 int url_sz = strlen(url) + 32 + Config.appendDomainLen +
2094 strlen(host);
2095 http->uri = (char *)xcalloc(url_sz, 1);
2096 snprintf(http->uri, url_sz, "http://%s%s", /*conn->port->protocol,*/ host, url);
2097 debugs(33, 5, "TRANSPARENT HOST REWRITE: '" << http->uri <<"'");
2098 } else {
2099 /* Put the local socket IP address as the hostname. */
2100 int url_sz = strlen(url) + 32 + Config.appendDomainLen;
2101 http->uri = (char *)xcalloc(url_sz, 1);
2102 http->getConn()->clientConnection->local.ToHostname(ipbuf,MAX_IPSTRLEN),
2103 snprintf(http->uri, url_sz, "http://%s:%d%s",
2104 // http->getConn()->port->protocol,
2105 ipbuf, http->getConn()->clientConnection->local.GetPort(), url);
2106 debugs(33, 5, "TRANSPARENT REWRITE: '" << http->uri << "'");
2107 }
2108 }
2109
2110 /**
2111 * parseHttpRequest()
2112 *
2113 * Returns
2114 * NULL on incomplete requests
2115 * a ClientSocketContext structure on success or failure.
2116 * Sets result->flags.parsed_ok to 0 if failed to parse the request.
2117 * Sets result->flags.parsed_ok to 1 if we have a good request.
2118 */
2119 static ClientSocketContext *
2120 parseHttpRequest(ConnStateData *csd, HttpParser *hp, HttpRequestMethod * method_p, HttpVersion *http_ver)
2121 {
2122 char *req_hdr = NULL;
2123 char *end;
2124 size_t req_sz;
2125 ClientHttpRequest *http;
2126 ClientSocketContext *result;
2127 StoreIOBuffer tempBuffer;
2128 int r;
2129
2130 /* pre-set these values to make aborting simpler */
2131 *method_p = METHOD_NONE;
2132
2133 /* NP: don't be tempted to move this down or remove again.
2134 * It's the only DDoS protection old-String has against long URL */
2135 if ( hp->bufsiz <= 0) {
2136 debugs(33, 5, "Incomplete request, waiting for end of request line");
2137 return NULL;
2138 } else if ( (size_t)hp->bufsiz >= Config.maxRequestHeaderSize && headersEnd(hp->buf, Config.maxRequestHeaderSize) == 0) {
2139 debugs(33, 5, "parseHttpRequest: Too large request");
2140 hp->request_parse_status = HTTP_HEADER_TOO_LARGE;
2141 return parseHttpRequestAbort(csd, "error:request-too-large");
2142 }
2143
2144 /* Attempt to parse the first line; this'll define the method, url, version and header begin */
2145 r = HttpParserParseReqLine(hp);
2146
2147 if (r == 0) {
2148 debugs(33, 5, "Incomplete request, waiting for end of request line");
2149 return NULL;
2150 }
2151
2152 if (r == -1) {
2153 return parseHttpRequestAbort(csd, "error:invalid-request");
2154 }
2155
2156 /* Request line is valid here .. */
2157 *http_ver = HttpVersion(hp->req.v_maj, hp->req.v_min);
2158
2159 /* This call scans the entire request, not just the headers */
2160 if (hp->req.v_maj > 0) {
2161 if ((req_sz = headersEnd(hp->buf, hp->bufsiz)) == 0) {
2162 debugs(33, 5, "Incomplete request, waiting for end of headers");
2163 return NULL;
2164 }
2165 } else {
2166 debugs(33, 3, "parseHttpRequest: Missing HTTP identifier");
2167 req_sz = HttpParserReqSz(hp);
2168 }
2169
2170 /* We know the whole request is in hp->buf now */
2171
2172 assert(req_sz <= (size_t) hp->bufsiz);
2173
2174 /* Will the following be true with HTTP/0.9 requests? probably not .. */
2175 /* So the rest of the code will need to deal with '0'-byte headers (ie, none, so don't try parsing em) */
2176 assert(req_sz > 0);
2177
2178 hp->hdr_end = req_sz - 1;
2179
2180 hp->hdr_start = hp->req.end + 1;
2181
2182 /* Enforce max_request_size */
2183 if (req_sz >= Config.maxRequestHeaderSize) {
2184 debugs(33, 5, "parseHttpRequest: Too large request");
2185 hp->request_parse_status = HTTP_HEADER_TOO_LARGE;
2186 return parseHttpRequestAbort(csd, "error:request-too-large");
2187 }
2188
2189 /* Set method_p */
2190 *method_p = HttpRequestMethod(&hp->buf[hp->req.m_start], &hp->buf[hp->req.m_end]+1);
2191
2192 /* deny CONNECT via accelerated ports */
2193 if (*method_p == METHOD_CONNECT && csd && csd->port && csd->port->accel) {
2194 debugs(33, DBG_IMPORTANT, "WARNING: CONNECT method received on " << csd->port->protocol << " Accelerator port " << csd->port->s.GetPort() );
2195 /* XXX need a way to say "this many character length string" */
2196 debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->buf);
2197 hp->request_parse_status = HTTP_METHOD_NOT_ALLOWED;
2198 return parseHttpRequestAbort(csd, "error:method-not-allowed");
2199 }
2200
2201 if (*method_p == METHOD_NONE) {
2202 /* XXX need a way to say "this many character length string" */
2203 debugs(33, 1, "clientParseRequestMethod: Unsupported method in request '" << hp->buf << "'");
2204 hp->request_parse_status = HTTP_METHOD_NOT_ALLOWED;
2205 return parseHttpRequestAbort(csd, "error:unsupported-request-method");
2206 }
2207
2208 /*
2209 * Process headers after request line
2210 * TODO: Use httpRequestParse here.
2211 */
2212 /* XXX this code should be modified to take a const char * later! */
2213 req_hdr = (char *) hp->buf + hp->req.end + 1;
2214
2215 debugs(33, 3, "parseHttpRequest: req_hdr = {" << req_hdr << "}");
2216
2217 end = (char *) hp->buf + hp->hdr_end;
2218
2219 debugs(33, 3, "parseHttpRequest: end = {" << end << "}");
2220
2221 debugs(33, 3, "parseHttpRequest: prefix_sz = " <<
2222 (int) HttpParserRequestLen(hp) << ", req_line_sz = " <<
2223 HttpParserReqSz(hp));
2224
2225 /* Ok, all headers are received */
2226 http = new ClientHttpRequest(csd);
2227
2228 http->req_sz = HttpParserRequestLen(hp);
2229 result = ClientSocketContextNew(csd->clientConnection, http);
2230 tempBuffer.data = result->reqbuf;
2231 tempBuffer.length = HTTP_REQBUF_SZ;
2232
2233 ClientStreamData newServer = new clientReplyContext(http);
2234 ClientStreamData newClient = result;
2235 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
2236 clientReplyStatus, newServer, clientSocketRecipient,
2237 clientSocketDetach, newClient, tempBuffer);
2238
2239 debugs(33, 5, "parseHttpRequest: Request Header is\n" <<(hp->buf) + hp->hdr_start);
2240
2241 /* set url */
2242 /*
2243 * XXX this should eventually not use a malloc'ed buffer; the transformation code
2244 * below needs to be modified to not expect a mutable nul-terminated string.
2245 */
2246 char *url = (char *)xmalloc(hp->req.u_end - hp->req.u_start + 16);
2247
2248 memcpy(url, hp->buf + hp->req.u_start, hp->req.u_end - hp->req.u_start + 1);
2249
2250 url[hp->req.u_end - hp->req.u_start + 1] = '\0';
2251
2252 #if THIS_VIOLATES_HTTP_SPECS_ON_URL_TRANSFORMATION
2253
2254 if ((t = strchr(url, '#'))) /* remove HTML anchors */
2255 *t = '\0';
2256
2257 #endif
2258
2259 debugs(33,5, HERE << "repare absolute URL from " << (csd->transparent()?"intercept":(csd->port->accel ? "accel":"")));
2260 /* Rewrite the URL in transparent or accelerator mode */
2261 /* NP: there are several cases to traverse here:
2262 * - standard mode (forward proxy)
2263 * - transparent mode (TPROXY)
2264 * - transparent mode with failures
2265 * - intercept mode (NAT)
2266 * - intercept mode with failures
2267 * - accelerator mode (reverse proxy)
2268 * - internal URL
2269 * - mixed combos of the above with internal URL
2270 */
2271 if (csd->transparent()) {
2272 /* intercept or transparent mode, properly working with no failures */
2273 prepareTransparentURL(csd, http, url, req_hdr);
2274
2275 } else if (internalCheck(url)) {
2276 /* internal URL mode */
2277 /* prepend our name & port */
2278 http->uri = xstrdup(internalLocalUri(NULL, url));
2279 // We just re-wrote the URL. Must replace the Host: header.
2280 // But have not parsed there yet!! flag for local-only handling.
2281 http->flags.internal = 1;
2282
2283 } else if (csd->port->accel || csd->switchedToHttps()) {
2284 /* accelerator mode */
2285 prepareAcceleratedURL(csd, http, url, req_hdr);
2286 }
2287
2288 if (!http->uri) {
2289 /* No special rewrites have been applied above, use the
2290 * requested url. may be rewritten later, so make extra room */
2291 int url_sz = strlen(url) + Config.appendDomainLen + 5;
2292 http->uri = (char *)xcalloc(url_sz, 1);
2293 strcpy(http->uri, url);
2294 }
2295
2296 debugs(33, 5, "parseHttpRequest: Complete request received");
2297
2298 // XXX: crop this dump at the end of headers. No need for extras
2299 debugs(11, 2, "HTTP Client " << csd->clientConnection);
2300 debugs(11, 2, "HTTP Client REQUEST:\n---------\n" << (hp->buf) + hp->req.m_start << "\n----------");
2301
2302 result->flags.parsed_ok = 1;
2303 xfree(url);
2304 return result;
2305 }
2306
2307 int
2308 ConnStateData::getAvailableBufferLength() const
2309 {
2310 assert (in.allocatedSize > in.notYetUsed); // allocated more than used
2311 const size_t result = in.allocatedSize - in.notYetUsed - 1;
2312 // huge request_header_max_size may lead to more than INT_MAX unused space
2313 assert (static_cast<ssize_t>(result) <= INT_MAX);
2314 return result;
2315 }
2316
2317 bool
2318 ConnStateData::maybeMakeSpaceAvailable()
2319 {
2320 if (getAvailableBufferLength() < 2) {
2321 size_t newSize;
2322 if (in.allocatedSize >= Config.maxRequestBufferSize) {
2323 debugs(33, 4, "request buffer full: client_request_buffer_max_size=" << Config.maxRequestBufferSize);
2324 return false;
2325 }
2326 if ((newSize=in.allocatedSize * 2) > Config.maxRequestBufferSize) {
2327 newSize=Config.maxRequestBufferSize;
2328 }
2329 in.buf = (char *)memReallocBuf(in.buf, newSize, &in.allocatedSize);
2330 debugs(33, 2, "growing request buffer: notYetUsed=" << in.notYetUsed << " size=" << in.allocatedSize);
2331 }
2332 return true;
2333 }
2334
2335 void
2336 ConnStateData::addContextToQueue(ClientSocketContext * context)
2337 {
2338 ClientSocketContext::Pointer *S;
2339
2340 for (S = (ClientSocketContext::Pointer *) & currentobject; S->getRaw();
2341 S = &(*S)->next);
2342 *S = context;
2343
2344 ++nrequests;
2345 }
2346
2347 int
2348 ConnStateData::getConcurrentRequestCount() const
2349 {
2350 int result = 0;
2351 ClientSocketContext::Pointer *T;
2352
2353 for (T = (ClientSocketContext::Pointer *) &currentobject;
2354 T->getRaw(); T = &(*T)->next, ++result);
2355 return result;
2356 }
2357
2358 int
2359 ConnStateData::connReadWasError(comm_err_t flag, int size, int xerrno)
2360 {
2361 if (flag != COMM_OK) {
2362 debugs(33, 2, "connReadWasError: FD " << clientConnection << ": got flag " << flag);
2363 return 1;
2364 }
2365
2366 if (size < 0) {
2367 if (!ignoreErrno(xerrno)) {
2368 debugs(33, 2, "connReadWasError: FD " << clientConnection << ": " << xstrerr(xerrno));
2369 return 1;
2370 } else if (in.notYetUsed == 0) {
2371 debugs(33, 2, "connReadWasError: FD " << clientConnection << ": no data to process (" << xstrerr(xerrno) << ")");
2372 }
2373 }
2374
2375 return 0;
2376 }
2377
2378 int
2379 ConnStateData::connFinishedWithConn(int size)
2380 {
2381 if (size == 0) {
2382 if (getConcurrentRequestCount() == 0 && in.notYetUsed == 0) {
2383 /* no current or pending requests */
2384 debugs(33, 4, HERE << clientConnection << " closed");
2385 return 1;
2386 } else if (!Config.onoff.half_closed_clients) {
2387 /* admin doesn't want to support half-closed client sockets */
2388 debugs(33, 3, HERE << clientConnection << " aborted (half_closed_clients disabled)");
2389 notifyAllContexts(0); // no specific error implies abort
2390 return 1;
2391 }
2392 }
2393
2394 return 0;
2395 }
2396
2397 void
2398 connNoteUseOfBuffer(ConnStateData* conn, size_t byteCount)
2399 {
2400 assert(byteCount > 0 && byteCount <= conn->in.notYetUsed);
2401 conn->in.notYetUsed -= byteCount;
2402 debugs(33, 5, HERE << "conn->in.notYetUsed = " << conn->in.notYetUsed);
2403 /*
2404 * If there is still data that will be used,
2405 * move it to the beginning.
2406 */
2407
2408 if (conn->in.notYetUsed > 0)
2409 memmove(conn->in.buf, conn->in.buf + byteCount, conn->in.notYetUsed);
2410 }
2411
2412 /// respond with ERR_TOO_BIG if request header exceeds request_header_max_size
2413 void
2414 ConnStateData::checkHeaderLimits()
2415 {
2416 if (in.notYetUsed < Config.maxRequestHeaderSize)
2417 return; // can accumulte more header data
2418
2419 debugs(33, 3, "Request header is too large (" << in.notYetUsed << " > " <<
2420 Config.maxRequestHeaderSize << " bytes)");
2421
2422 ClientSocketContext *context = parseHttpRequestAbort(this, "error:request-too-large");
2423 clientStreamNode *node = context->getClientReplyContext();
2424 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2425 assert (repContext);
2426 repContext->setReplyToError(ERR_TOO_BIG,
2427 HTTP_BAD_REQUEST, METHOD_NONE, NULL,
2428 clientConnection->remote, NULL, NULL, NULL);
2429 context->registerWithConn();
2430 context->pullData();
2431 }
2432
2433 void
2434 ConnStateData::clientAfterReadingRequests()
2435 {
2436 // Were we expecting to read more request body from half-closed connection?
2437 if (mayNeedToReadMoreBody() && commIsHalfClosed(clientConnection->fd)) {
2438 debugs(33, 3, HERE << "truncated body: closing half-closed " << clientConnection);
2439 clientConnection->close();
2440 return;
2441 }
2442
2443 if (flags.readMore)
2444 readSomeData();
2445 }
2446
2447 static void
2448 clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *context, const HttpRequestMethod& method, HttpVersion http_ver)
2449 {
2450 ClientHttpRequest *http = context->http;
2451 HttpRequest *request = NULL;
2452 bool notedUseOfBuffer = false;
2453 bool chunked = false;
2454 bool mustReplyToOptions = false;
2455 bool unsupportedTe = false;
2456 bool expectBody = false;
2457
2458 /* We have an initial client stream in place should it be needed */
2459 /* setup our private context */
2460 context->registerWithConn();
2461
2462 if (context->flags.parsed_ok == 0) {
2463 clientStreamNode *node = context->getClientReplyContext();
2464 debugs(33, 2, "clientProcessRequest: Invalid Request");
2465 // setLogUri should called before repContext->setReplyToError
2466 setLogUri(http, http->uri, true);
2467 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2468 assert (repContext);
2469 switch (hp->request_parse_status) {
2470 case HTTP_HEADER_TOO_LARGE:
2471 repContext->setReplyToError(ERR_TOO_BIG, HTTP_BAD_REQUEST, method, http->uri, conn->clientConnection->remote, NULL, conn->in.buf, NULL);
2472 break;
2473 case HTTP_METHOD_NOT_ALLOWED:
2474 repContext->setReplyToError(ERR_UNSUP_REQ, HTTP_METHOD_NOT_ALLOWED, method, http->uri,
2475 conn->clientConnection->remote, NULL, conn->in.buf, NULL);
2476 break;
2477 default:
2478 repContext->setReplyToError(ERR_INVALID_REQ, hp->request_parse_status, method, http->uri,
2479 conn->clientConnection->remote, NULL, conn->in.buf, NULL);
2480 }
2481 assert(context->http->out.offset == 0);
2482 context->pullData();
2483 conn->flags.readMore = false;
2484 goto finish;
2485 }
2486
2487 if ((request = HttpRequest::CreateFromUrlAndMethod(http->uri, method)) == NULL) {
2488 clientStreamNode *node = context->getClientReplyContext();
2489 debugs(33, 5, "Invalid URL: " << http->uri);
2490 // setLogUri should called before repContext->setReplyToError
2491 setLogUri(http, http->uri, true);
2492 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2493 assert (repContext);
2494 repContext->setReplyToError(ERR_INVALID_URL, HTTP_BAD_REQUEST, method, http->uri, conn->clientConnection->remote, NULL, NULL, NULL);
2495 assert(context->http->out.offset == 0);
2496 context->pullData();
2497 conn->flags.readMore = false;
2498 goto finish;
2499 }
2500
2501 /* RFC 2616 section 10.5.6 : handle unsupported HTTP versions cleanly. */
2502 /* We currently only accept 0.9, 1.0, 1.1 */
2503 if ( (http_ver.major == 0 && http_ver.minor != 9) ||
2504 (http_ver.major == 1 && http_ver.minor > 1 ) ||
2505 (http_ver.major > 1) ) {
2506
2507 clientStreamNode *node = context->getClientReplyContext();
2508 debugs(33, 5, "Unsupported HTTP version discovered. :\n" << HttpParserHdrBuf(hp));
2509 // setLogUri should called before repContext->setReplyToError
2510 setLogUri(http, http->uri, true);
2511 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2512 assert (repContext);
2513 repContext->setReplyToError(ERR_UNSUP_HTTPVERSION, HTTP_HTTP_VERSION_NOT_SUPPORTED, method, http->uri,
2514 conn->clientConnection->remote, NULL, HttpParserHdrBuf(hp), NULL);
2515 assert(context->http->out.offset == 0);
2516 context->pullData();
2517 conn->flags.readMore = false;
2518 goto finish;
2519 }
2520
2521 /* compile headers */
2522 /* we should skip request line! */
2523 /* XXX should actually know the damned buffer size here */
2524 if (http_ver.major >= 1 && !request->parseHeader(HttpParserHdrBuf(hp), HttpParserHdrSz(hp))) {
2525 clientStreamNode *node = context->getClientReplyContext();
2526 debugs(33, 5, "Failed to parse request headers:\n" << HttpParserHdrBuf(hp));
2527 // setLogUri should called before repContext->setReplyToError
2528 setLogUri(http, http->uri, true);
2529 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2530 assert (repContext);
2531 repContext->setReplyToError(ERR_INVALID_REQ, HTTP_BAD_REQUEST, method, http->uri, conn->clientConnection->remote, NULL, NULL, NULL);
2532 assert(context->http->out.offset == 0);
2533 context->pullData();
2534 conn->flags.readMore = false;
2535 goto finish;
2536 }
2537
2538 request->clientConnectionManager = conn;
2539
2540 request->flags.accelerated = http->flags.accel;
2541 request->flags.sslBumped = conn->switchedToHttps();
2542 request->flags.ignore_cc = conn->port->ignore_cc;
2543 request->flags.no_direct = request->flags.accelerated ? !conn->port->allow_direct : 0;
2544 #if USE_AUTH
2545 if (request->flags.sslBumped) {
2546 if (conn->auth_user_request != NULL)
2547 request->auth_user_request = conn->auth_user_request;
2548 }
2549 #endif
2550
2551 /** \par
2552 * If transparent or interception mode is working clone the transparent and interception flags
2553 * from the port settings to the request.
2554 */
2555 if (http->clientConnection != NULL) {
2556 request->flags.intercepted = ((http->clientConnection->flags & COMM_INTERCEPTION) != 0);
2557 request->flags.spoof_client_ip = ((http->clientConnection->flags & COMM_TRANSPARENT) != 0 ) ;
2558 }
2559
2560 if (internalCheck(request->urlpath.termedBuf())) {
2561 if (internalHostnameIs(request->GetHost()) &&
2562 request->port == getMyPort()) {
2563 http->flags.internal = 1;
2564 } else if (Config.onoff.global_internal_static && internalStaticCheck(request->urlpath.termedBuf())) {
2565 request->SetHost(internalHostname());
2566 request->port = getMyPort();
2567 http->flags.internal = 1;
2568 }
2569 }
2570
2571 if (http->flags.internal) {
2572 request->protocol = AnyP::PROTO_HTTP;
2573 request->login[0] = '\0';
2574 }
2575
2576 request->flags.internal = http->flags.internal;
2577 setLogUri (http, urlCanonicalClean(request));
2578 request->client_addr = conn->clientConnection->remote; // XXX: remove reuest->client_addr member.
2579 #if FOLLOW_X_FORWARDED_FOR
2580 // indirect client gets stored here because it is an HTTP header result (from X-Forwarded-For:)
2581 // not a details about teh TCP connection itself
2582 request->indirect_client_addr = conn->clientConnection->remote;
2583 #endif /* FOLLOW_X_FORWARDED_FOR */
2584 request->my_addr = conn->clientConnection->local;
2585 request->myportname = conn->port->name;
2586 request->http_ver = http_ver;
2587
2588 // Link this HttpRequest to ConnStateData relatively early so the following complex handling can use it
2589 // TODO: this effectively obsoletes a lot of conn->FOO copying. That needs cleaning up later.
2590 request->clientConnectionManager = conn;
2591
2592 if (request->header.chunked()) {
2593 chunked = true;
2594 } else if (request->header.has(HDR_TRANSFER_ENCODING)) {
2595 const String te = request->header.getList(HDR_TRANSFER_ENCODING);
2596 // HTTP/1.1 requires chunking to be the last encoding if there is one
2597 unsupportedTe = te.size() && te != "identity";
2598 } // else implied identity coding
2599
2600 mustReplyToOptions = (method == METHOD_OPTIONS) &&
2601 (request->header.getInt64(HDR_MAX_FORWARDS) == 0);
2602 if (!urlCheckRequest(request) || mustReplyToOptions || unsupportedTe) {
2603 clientStreamNode *node = context->getClientReplyContext();
2604 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2605 assert (repContext);
2606 repContext->setReplyToError(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED, request->method, NULL,
2607 conn->clientConnection->remote, request, NULL, NULL);
2608 assert(context->http->out.offset == 0);
2609 context->pullData();
2610 conn->flags.readMore = false;
2611 goto finish;
2612 }
2613
2614
2615 if (!chunked && !clientIsContentLengthValid(request)) {
2616 clientStreamNode *node = context->getClientReplyContext();
2617 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2618 assert (repContext);
2619 repContext->setReplyToError(ERR_INVALID_REQ,
2620 HTTP_LENGTH_REQUIRED, request->method, NULL,
2621 conn->clientConnection->remote, request, NULL, NULL);
2622 assert(context->http->out.offset == 0);
2623 context->pullData();
2624 conn->flags.readMore = false;
2625 goto finish;
2626 }
2627
2628 if (request->header.has(HDR_EXPECT)) {
2629 const String expect = request->header.getList(HDR_EXPECT);
2630 const bool supportedExpect = (expect.caseCmp("100-continue") == 0);
2631 if (!supportedExpect) {
2632 clientStreamNode *node = context->getClientReplyContext();
2633 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2634 assert (repContext);
2635 repContext->setReplyToError(ERR_INVALID_REQ, HTTP_EXPECTATION_FAILED, request->method, http->uri,
2636 conn->clientConnection->remote, request, NULL, NULL);
2637 assert(context->http->out.offset == 0);
2638 context->pullData();
2639 conn->flags.readMore = false;
2640 goto finish;
2641 }
2642 }
2643
2644 http->request = HTTPMSGLOCK(request);
2645 clientSetKeepaliveFlag(http);
2646
2647 // Let tunneling code be fully responsible for CONNECT requests
2648 if (http->request->method == METHOD_CONNECT) {
2649 context->mayUseConnection(true);
2650 conn->flags.readMore = false;
2651 }
2652
2653 /* Do we expect a request-body? */
2654 expectBody = chunked || request->content_length > 0;
2655 if (!context->mayUseConnection() && expectBody) {
2656 request->body_pipe = conn->expectRequestBody(
2657 chunked ? -1 : request->content_length);
2658
2659 // consume header early so that body pipe gets just the body
2660 connNoteUseOfBuffer(conn, http->req_sz);
2661 notedUseOfBuffer = true;
2662
2663 /* Is it too large? */
2664 if (!chunked && // if chunked, we will check as we accumulate
2665 clientIsRequestBodyTooLargeForPolicy(request->content_length)) {
2666 clientStreamNode *node = context->getClientReplyContext();
2667 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2668 assert (repContext);
2669 repContext->setReplyToError(ERR_TOO_BIG,
2670 HTTP_REQUEST_ENTITY_TOO_LARGE, METHOD_NONE, NULL,
2671 conn->clientConnection->remote, http->request, NULL, NULL);
2672 assert(context->http->out.offset == 0);
2673 context->pullData();
2674 conn->flags.readMore = false;
2675 goto finish;
2676 }
2677
2678 // We may stop producing, comm_close, and/or call setReplyToError()
2679 // below, so quit on errors to avoid http->doCallouts()
2680 if (!conn->handleRequestBodyData())
2681 goto finish;
2682
2683 if (!request->body_pipe->productionEnded()) {
2684 debugs(33, 5, HERE << "need more request body");
2685 context->mayUseConnection(true);
2686 assert(conn->flags.readMore);
2687 }
2688 }
2689
2690 http->calloutContext = new ClientRequestContext(http);
2691
2692 http->doCallouts();
2693
2694 finish:
2695 if (!notedUseOfBuffer)
2696 connNoteUseOfBuffer(conn, http->req_sz);
2697
2698 /*
2699 * DPW 2007-05-18
2700 * Moved the TCP_RESET feature from clientReplyContext::sendMoreData
2701 * to here because calling comm_reset_close() causes http to
2702 * be freed and the above connNoteUseOfBuffer() would hit an
2703 * assertion, not to mention that we were accessing freed memory.
2704 */
2705 if (request && request->flags.resetTCP() && Comm::IsConnOpen(conn->clientConnection)) {
2706 debugs(33, 3, HERE << "Sending TCP RST on " << conn->clientConnection);
2707 conn->flags.readMore = false;
2708 comm_reset_close(conn->clientConnection);
2709 }
2710 }
2711
2712 static void
2713 connStripBufferWhitespace (ConnStateData * conn)
2714 {
2715 while (conn->in.notYetUsed > 0 && xisspace(conn->in.buf[0])) {
2716 memmove(conn->in.buf, conn->in.buf + 1, conn->in.notYetUsed - 1);
2717 --conn->in.notYetUsed;
2718 }
2719 }
2720
2721 static int
2722 connOkToAddRequest(ConnStateData * conn)
2723 {
2724 int result = conn->getConcurrentRequestCount() < (Config.onoff.pipeline_prefetch ? 2 : 1);
2725
2726 if (!result) {
2727 debugs(33, 3, HERE << conn->clientConnection << " max concurrent requests reached");
2728 debugs(33, 5, HERE << conn->clientConnection << " defering new request until one is done");
2729 }
2730
2731 return result;
2732 }
2733
2734 /**
2735 * Attempt to parse one or more requests from the input buffer.
2736 * If a request is successfully parsed, even if the next request
2737 * is only partially parsed, it will return TRUE.
2738 */
2739 bool
2740 ConnStateData::clientParseRequests()
2741 {
2742 HttpRequestMethod method;
2743 bool parsed_req = false;
2744 HttpVersion http_ver;
2745
2746 debugs(33, 5, HERE << clientConnection << ": attempting to parse");
2747
2748 // Loop while we have read bytes that are not needed for producing the body
2749 // On errors, bodyPipe may become nil, but readMore will be cleared
2750 while (in.notYetUsed > 0 && !bodyPipe && flags.readMore) {
2751 connStripBufferWhitespace(this);
2752
2753 /* Don't try to parse if the buffer is empty */
2754 if (in.notYetUsed == 0)
2755 break;
2756
2757 /* Limit the number of concurrent requests to 2 */
2758 if (!connOkToAddRequest(this)) {
2759 break;
2760 }
2761
2762 /* Should not be needed anymore */
2763 /* Terminate the string */
2764 in.buf[in.notYetUsed] = '\0';
2765
2766 /* Begin the parsing */
2767 PROF_start(parseHttpRequest);
2768 HttpParserInit(&parser_, in.buf, in.notYetUsed);
2769
2770 /* Process request */
2771 ClientSocketContext *context = parseHttpRequest(this, &parser_, &method, &http_ver);
2772 PROF_stop(parseHttpRequest);
2773
2774 /* partial or incomplete request */
2775 if (!context) {
2776 // TODO: why parseHttpRequest can just return parseHttpRequestAbort
2777 // (which becomes context) but checkHeaderLimits cannot?
2778 checkHeaderLimits();
2779 break;
2780 }
2781
2782 /* status -1 or 1 */
2783 if (context) {
2784 debugs(33, 5, HERE << clientConnection << ": parsed a request");
2785 AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "clientLifetimeTimeout",
2786 CommTimeoutCbPtrFun(clientLifetimeTimeout, context->http));
2787 commSetConnTimeout(clientConnection, Config.Timeout.lifetime, timeoutCall);
2788
2789 clientProcessRequest(this, &parser_, context, method, http_ver);
2790
2791 parsed_req = true; // XXX: do we really need to parse everything right NOW ?
2792
2793 if (context->mayUseConnection()) {
2794 debugs(33, 3, HERE << "Not parsing new requests, as this request may need the connection");
2795 break;
2796 }
2797 }
2798 }
2799
2800 /* XXX where to 'finish' the parsing pass? */
2801 return parsed_req;
2802 }
2803
2804 void
2805 ConnStateData::clientReadRequest(const CommIoCbParams &io)
2806 {
2807 debugs(33,5,HERE << io.conn << " size " << io.size);
2808 Must(reading());
2809 reader = NULL;
2810
2811 /* Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up */
2812
2813 if (io.flag == COMM_ERR_CLOSING) {
2814 debugs(33,5, HERE << io.conn << " closing Bailout.");
2815 return;
2816 }
2817
2818 assert(Comm::IsConnOpen(clientConnection));
2819 assert(io.conn->fd == clientConnection->fd);
2820
2821 /*
2822 * Don't reset the timeout value here. The timeout value will be
2823 * set to Config.Timeout.request by httpAccept() and
2824 * clientWriteComplete(), and should apply to the request as a
2825 * whole, not individual read() calls. Plus, it breaks our
2826 * lame half-close detection
2827 */
2828 if (connReadWasError(io.flag, io.size, io.xerrno)) {
2829 notifyAllContexts(io.xerrno);
2830 io.conn->close();
2831 return;
2832 }
2833
2834 if (io.flag == COMM_OK) {
2835 if (io.size > 0) {
2836 kb_incr(&(statCounter.client_http.kbytes_in), io.size);
2837
2838 // may comm_close or setReplyToError
2839 if (!handleReadData(io.buf, io.size))
2840 return;
2841
2842 } else if (io.size == 0) {
2843 debugs(33, 5, HERE << io.conn << " closed?");
2844
2845 if (connFinishedWithConn(io.size)) {
2846 clientConnection->close();
2847 return;
2848 }
2849
2850 /* It might be half-closed, we can't tell */
2851 fd_table[io.conn->fd].flags.socket_eof = 1;
2852
2853 commMarkHalfClosed(io.conn->fd);
2854
2855 fd_note(io.conn->fd, "half-closed");
2856
2857 /* There is one more close check at the end, to detect aborted
2858 * (partial) requests. At this point we can't tell if the request
2859 * is partial.
2860 */
2861 /* Continue to process previously read data */
2862 }
2863 }
2864
2865 /* Process next request */
2866 if (getConcurrentRequestCount() == 0)
2867 fd_note(io.fd, "Reading next request");
2868
2869 if (!clientParseRequests()) {
2870 if (!isOpen())
2871 return;
2872 /*
2873 * If the client here is half closed and we failed
2874 * to parse a request, close the connection.
2875 * The above check with connFinishedWithConn() only
2876 * succeeds _if_ the buffer is empty which it won't
2877 * be if we have an incomplete request.
2878 * XXX: This duplicates ClientSocketContext::keepaliveNextRequest
2879 */
2880 if (getConcurrentRequestCount() == 0 && commIsHalfClosed(io.fd)) {
2881 debugs(33, 5, HERE << io.conn << ": half-closed connection, no completed request parsed, connection closing.");
2882 clientConnection->close();
2883 return;
2884 }
2885 }
2886
2887 if (!isOpen())
2888 return;
2889
2890 clientAfterReadingRequests();
2891 }
2892
2893 /**
2894 * called when new request data has been read from the socket
2895 *
2896 * \retval false called comm_close or setReplyToError (the caller should bail)
2897 * \retval true we did not call comm_close or setReplyToError
2898 */
2899 bool
2900 ConnStateData::handleReadData(char *buf, size_t size)
2901 {
2902 char *current_buf = in.addressToReadInto();
2903
2904 if (buf != current_buf)
2905 memmove(current_buf, buf, size);
2906
2907 in.notYetUsed += size;
2908
2909 in.buf[in.notYetUsed] = '\0'; /* Terminate the string */
2910
2911 // if we are reading a body, stuff data into the body pipe
2912 if (bodyPipe != NULL)
2913 return handleRequestBodyData();
2914 return true;
2915 }
2916
2917 /**
2918 * called when new request body data has been buffered in in.buf
2919 * may close the connection if we were closing and piped everything out
2920 *
2921 * \retval false called comm_close or setReplyToError (the caller should bail)
2922 * \retval true we did not call comm_close or setReplyToError
2923 */
2924 bool
2925 ConnStateData::handleRequestBodyData()
2926 {
2927 assert(bodyPipe != NULL);
2928
2929 size_t putSize = 0;
2930
2931 if (in.bodyParser) { // chunked encoding
2932 if (const err_type error = handleChunkedRequestBody(putSize)) {
2933 abortChunkedRequestBody(error);
2934 return false;
2935 }
2936 } else { // identity encoding
2937 debugs(33,5, HERE << "handling plain request body for " << clientConnection);
2938 putSize = bodyPipe->putMoreData(in.buf, in.notYetUsed);
2939 if (!bodyPipe->mayNeedMoreData()) {
2940 // BodyPipe will clear us automagically when we produced everything
2941 bodyPipe = NULL;
2942 }
2943 }
2944
2945 if (putSize > 0)
2946 connNoteUseOfBuffer(this, putSize);
2947
2948 if (!bodyPipe) {
2949 debugs(33,5, HERE << "produced entire request body for " << clientConnection);
2950
2951 if (const char *reason = stoppedSending()) {
2952 /* we've finished reading like good clients,
2953 * now do the close that initiateClose initiated.
2954 */
2955 debugs(33, 3, HERE << "closing for earlier sending error: " << reason);
2956 clientConnection->close();
2957 return false;
2958 }
2959 }
2960
2961 return true;
2962 }
2963
2964 /// parses available chunked encoded body bytes, checks size, returns errors
2965 err_type
2966 ConnStateData::handleChunkedRequestBody(size_t &putSize)
2967 {
2968 debugs(33,7, HERE << "chunked from " << clientConnection << ": " << in.notYetUsed);
2969
2970 try { // the parser will throw on errors
2971
2972 if (!in.notYetUsed) // nothing to do (MemBuf::init requires this check)
2973 return ERR_NONE;
2974
2975 MemBuf raw; // ChunkedCodingParser only works with MemBufs
2976 // add one because MemBuf will assert if it cannot 0-terminate
2977 raw.init(in.notYetUsed, in.notYetUsed+1);
2978 raw.append(in.buf, in.notYetUsed);
2979
2980 const mb_size_t wasContentSize = raw.contentSize();
2981 BodyPipeCheckout bpc(*bodyPipe);
2982 const bool parsed = in.bodyParser->parse(&raw, &bpc.buf);
2983 bpc.checkIn();
2984 putSize = wasContentSize - raw.contentSize();
2985
2986 // dechunk then check: the size limit applies to _dechunked_ content
2987 if (clientIsRequestBodyTooLargeForPolicy(bodyPipe->producedSize()))
2988 return ERR_TOO_BIG;
2989
2990 if (parsed) {
2991 finishDechunkingRequest(true);
2992 Must(!bodyPipe);
2993 return ERR_NONE; // nil bodyPipe implies body end for the caller
2994 }
2995
2996 // if chunk parser needs data, then the body pipe must need it too
2997 Must(!in.bodyParser->needsMoreData() || bodyPipe->mayNeedMoreData());
2998
2999 // if parser needs more space and we can consume nothing, we will stall
3000 Must(!in.bodyParser->needsMoreSpace() || bodyPipe->buf().hasContent());
3001 } catch (...) { // TODO: be more specific
3002 debugs(33, 3, HERE << "malformed chunks" << bodyPipe->status());
3003 return ERR_INVALID_REQ;
3004 }
3005
3006 debugs(33, 7, HERE << "need more chunked data" << *bodyPipe->status());
3007 return ERR_NONE;
3008 }
3009
3010 /// quit on errors related to chunked request body handling
3011 void
3012 ConnStateData::abortChunkedRequestBody(const err_type error)
3013 {
3014 finishDechunkingRequest(false);
3015
3016 // XXX: The code below works if we fail during initial request parsing,
3017 // but if we fail when the server-side works already, the server may send
3018 // us its response too, causing various assertions. How to prevent that?
3019 #if WE_KNOW_HOW_TO_SEND_ERRORS
3020 ClientSocketContext::Pointer context = getCurrentContext();
3021 if (context != NULL && !context->http->out.offset) { // output nothing yet
3022 clientStreamNode *node = context->getClientReplyContext();
3023 clientReplyContext *repContext = dynamic_cast<clientReplyContext*>(node->data.getRaw());
3024 assert(repContext);
3025 const http_status scode = (error == ERR_TOO_BIG) ?
3026 HTTP_REQUEST_ENTITY_TOO_LARGE : HTTP_BAD_REQUEST;
3027 repContext->setReplyToError(error, scode,
3028 repContext->http->request->method,
3029 repContext->http->uri,
3030 peer,
3031 repContext->http->request,
3032 in.buf, NULL);
3033 context->pullData();
3034 } else {
3035 // close or otherwise we may get stuck as nobody will notice the error?
3036 comm_reset_close(clientConnection);
3037 }
3038 #else
3039 debugs(33, 3, HERE << "aborting chunked request without error " << error);
3040 comm_reset_close(clientConnection);
3041 #endif
3042 flags.readMore = false;
3043 }
3044
3045 void
3046 ConnStateData::noteMoreBodySpaceAvailable(BodyPipe::Pointer )
3047 {
3048 if (!handleRequestBodyData())
3049 return;
3050
3051 // too late to read more body
3052 if (!isOpen() || stoppedReceiving())
3053 return;
3054
3055 readSomeData();
3056 }
3057
3058 void
3059 ConnStateData::noteBodyConsumerAborted(BodyPipe::Pointer )
3060 {
3061 // request reader may get stuck waiting for space if nobody consumes body
3062 if (bodyPipe != NULL)
3063 bodyPipe->enableAutoConsumption();
3064
3065 stopReceiving("virgin request body consumer aborted"); // closes ASAP
3066 }
3067
3068 /** general lifetime handler for HTTP requests */
3069 void
3070 ConnStateData::requestTimeout(const CommTimeoutCbParams &io)
3071 {
3072 #if THIS_CONFUSES_PERSISTENT_CONNECTION_AWARE_BROWSERS_AND_USERS
3073 debugs(33, 3, "requestTimeout: FD " << io.fd << ": lifetime is expired.");
3074
3075 if (COMMIO_FD_WRITECB(io.fd)->active) {
3076 /* FIXME: If this code is reinstated, check the conn counters,
3077 * not the fd table state
3078 */
3079 /*
3080 * Some data has been sent to the client, just close the FD
3081 */
3082 clientConnection->close();
3083 } else if (nrequests) {
3084 /*
3085 * assume its a persistent connection; just close it
3086 */
3087 clientConnection->close();
3088 } else {
3089 /*
3090 * Generate an error
3091 */
3092 ClientHttpRequest **H;
3093 clientStreamNode *node;
3094 ClientHttpRequest *http = parseHttpRequestAbort(this, "error:Connection%20lifetime%20expired");
3095 node = http->client_stream.tail->prev->data;
3096 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
3097 assert (repContext);
3098 repContext->setReplyToError(ERR_LIFETIME_EXP,
3099 HTTP_REQUEST_TIMEOUT, METHOD_NONE, "N/A", &peer.sin_addr,
3100 NULL, NULL, NULL);
3101 /* No requests can be outstanded */
3102 assert(chr == NULL);
3103 /* add to the client request queue */
3104
3105 for (H = &chr; *H; H = &(*H)->next);
3106 *H = http;
3107
3108 clientStreamRead(http->client_stream.tail->data, http, 0,
3109 HTTP_REQBUF_SZ, context->reqbuf);
3110
3111 /*
3112 * if we don't close() here, we still need a timeout handler!
3113 */
3114 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
3115 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
3116 TimeoutDialer, this, ConnStateData::requestTimeout);
3117 commSetConnTimeout(io.conn, 30, timeoutCall);
3118
3119 /*
3120 * Aha, but we don't want a read handler!
3121 */
3122 Comm::SetSelect(io.fd, COMM_SELECT_READ, NULL, NULL, 0);
3123 }
3124
3125 #else
3126 /*
3127 * Just close the connection to not confuse browsers
3128 * using persistent connections. Some browsers opens
3129 * an connection and then does not use it until much
3130 * later (presumeably because the request triggering
3131 * the open has already been completed on another
3132 * connection)
3133 */
3134 debugs(33, 3, "requestTimeout: FD " << io.fd << ": lifetime is expired.");
3135 io.conn->close();
3136 #endif
3137 }
3138
3139 static void
3140 clientLifetimeTimeout(const CommTimeoutCbParams &io)
3141 {
3142 ClientHttpRequest *http = static_cast<ClientHttpRequest *>(io.data);
3143 debugs(33, DBG_IMPORTANT, "WARNING: Closing client connection due to lifetime timeout");
3144 debugs(33, DBG_IMPORTANT, "\t" << http->uri);
3145 http->al.http.timedout = true;
3146 if (Comm::IsConnOpen(io.conn))
3147 io.conn->close();
3148 }
3149
3150 ConnStateData *
3151 connStateCreate(const Comm::ConnectionPointer &client, http_port_list *port)
3152 {
3153 ConnStateData *result = new ConnStateData;
3154
3155 result->clientConnection = client;
3156 result->log_addr = client->remote;
3157 result->log_addr.ApplyMask(Config.Addrs.client_netmask);
3158 result->in.buf = (char *)memAllocBuf(CLIENT_REQ_BUF_SZ, &result->in.allocatedSize);
3159 result->port = cbdataReference(port);
3160
3161 if (port->disable_pmtu_discovery != DISABLE_PMTU_OFF &&
3162 (result->transparent() || port->disable_pmtu_discovery == DISABLE_PMTU_ALWAYS)) {
3163 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
3164 int i = IP_PMTUDISC_DONT;
3165 setsockopt(client->fd, SOL_IP, IP_MTU_DISCOVER, &i, sizeof i);
3166 #else
3167 static int reported = 0;
3168
3169 if (!reported) {
3170 debugs(33, 1, "Notice: httpd_accel_no_pmtu_disc not supported on your platform");
3171 reported = 1;
3172 }
3173 #endif
3174 }
3175
3176 typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
3177 AsyncCall::Pointer call = JobCallback(33, 5, Dialer, result, ConnStateData::connStateClosed);
3178 comm_add_close_handler(client->fd, call);
3179
3180 if (Config.onoff.log_fqdn)
3181 fqdncache_gethostbyaddr(client->remote, FQDN_LOOKUP_IF_MISS);
3182
3183 #if USE_IDENT
3184 if (Ident::TheConfig.identLookup) {
3185 ACLFilledChecklist identChecklist(Ident::TheConfig.identLookup, NULL, NULL);
3186 identChecklist.src_addr = client->remote;
3187 identChecklist.my_addr = client->local;
3188 if (identChecklist.fastCheck() == ACCESS_ALLOWED)
3189 Ident::Start(client, clientIdentDone, result);
3190 }
3191 #endif
3192
3193 #if USE_SQUID_EUI
3194 if (Eui::TheConfig.euiLookup) {
3195 if (client->remote.IsIPv4()) {
3196 result->clientConnection->remoteEui48.lookup(client->remote);
3197 } else if (client->remote.IsIPv6()) {
3198 result->clientConnection->remoteEui64.lookup(client->remote);
3199 }
3200 }
3201 #endif
3202
3203 clientdbEstablished(client->remote, 1);
3204
3205 result->flags.readMore = true;
3206 return result;
3207 }
3208
3209 /** Handle a new connection on HTTP socket. */
3210 void
3211 httpAccept(const CommAcceptCbParams &params)
3212 {
3213 http_port_list *s = (http_port_list *)params.data;
3214
3215 if (params.flag != COMM_OK) {
3216 // Its possible the call was still queued when the client disconnected
3217 debugs(33, 2, "httpAccept: " << s->listenConn << ": accept failure: " << xstrerr(params.xerrno));
3218 return;
3219 }
3220
3221 debugs(33, 4, HERE << params.conn << ": accepted");
3222 fd_note(params.conn->fd, "client http connect");
3223
3224 if (s->tcp_keepalive.enabled) {
3225 commSetTcpKeepalive(params.conn->fd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
3226 }
3227
3228 incoming_sockets_accepted++;
3229
3230 // Socket is ready, setup the connection manager to start using it
3231 ConnStateData *connState = connStateCreate(params.conn, s);
3232
3233 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
3234 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
3235 TimeoutDialer, connState, ConnStateData::requestTimeout);
3236 commSetConnTimeout(params.conn, Config.Timeout.read, timeoutCall);
3237
3238 connState->readSomeData();
3239
3240 #if USE_DELAY_POOLS
3241 fd_table[params.conn->fd].clientInfo = NULL;
3242
3243 if (Config.onoff.client_db) {
3244 /* it was said several times that client write limiter does not work if client_db is disabled */
3245
3246 ClientDelayPools& pools(Config.ClientDelay.pools);
3247 ACLFilledChecklist ch(NULL, NULL, NULL);
3248
3249 // TODO: we check early to limit error response bandwith but we
3250 // should recheck when we can honor delay_pool_uses_indirect
3251 // TODO: we should also pass the port details for myportname here.
3252 ch.src_addr = params.conn->remote;
3253 ch.my_addr = params.conn->local;
3254
3255 for (unsigned int pool = 0; pool < pools.size(); pool++) {
3256
3257 /* pools require explicit 'allow' to assign a client into them */
3258 if (pools[pool].access) {
3259 ch.accessList = pools[pool].access;
3260 allow_t answer = ch.fastCheck();
3261 if (answer == ACCESS_ALLOWED) {
3262
3263 /* request client information from db after we did all checks
3264 this will save hash lookup if client failed checks */
3265 ClientInfo * cli = clientdbGetInfo(params.conn->remote);
3266 assert(cli);
3267
3268 /* put client info in FDE */
3269 fd_table[params.conn->fd].clientInfo = cli;
3270
3271 /* setup write limiter for this request */
3272 const double burst = floor(0.5 +
3273 (pools[pool].highwatermark * Config.ClientDelay.initial)/100.0);
3274 cli->setWriteLimiter(pools[pool].rate, burst, pools[pool].highwatermark);
3275 break;
3276 } else {
3277 debugs(83, 4, HERE << "Delay pool " << pool << " skipped because ACL " << answer);
3278 }
3279 }
3280 }
3281 }
3282 #endif
3283 }
3284
3285 #if USE_SSL
3286
3287 /** Create SSL connection structure and update fd_table */
3288 static SSL *
3289 httpsCreate(const Comm::ConnectionPointer &conn, SSL_CTX *sslContext)
3290 {
3291 SSL *ssl = SSL_new(sslContext);
3292
3293 if (!ssl) {
3294 const int ssl_error = ERR_get_error();
3295 debugs(83, DBG_IMPORTANT, "ERROR: httpsAccept: Error allocating handle: " << ERR_error_string(ssl_error, NULL) );
3296 conn->close();
3297 return NULL;
3298 }
3299
3300 SSL_set_fd(ssl, conn->fd);
3301 fd_table[conn->fd].ssl = ssl;
3302 fd_table[conn->fd].read_method = &ssl_read_method;
3303 fd_table[conn->fd].write_method = &ssl_write_method;
3304
3305 debugs(33, 5, "httpsCreate: will negotate SSL on " << conn);
3306 fd_note(conn->fd, "client https start");
3307
3308 return ssl;
3309 }
3310
3311 /** negotiate an SSL connection */
3312 static void
3313 clientNegotiateSSL(int fd, void *data)
3314 {
3315 ConnStateData *conn = (ConnStateData *)data;
3316 X509 *client_cert;
3317 SSL *ssl = fd_table[fd].ssl;
3318 int ret;
3319
3320 if ((ret = SSL_accept(ssl)) <= 0) {
3321 int ssl_error = SSL_get_error(ssl, ret);
3322
3323 switch (ssl_error) {
3324
3325 case SSL_ERROR_WANT_READ:
3326 Comm::SetSelect(fd, COMM_SELECT_READ, clientNegotiateSSL, conn, 0);
3327 return;
3328
3329 case SSL_ERROR_WANT_WRITE:
3330 Comm::SetSelect(fd, COMM_SELECT_WRITE, clientNegotiateSSL, conn, 0);
3331 return;
3332
3333 case SSL_ERROR_SYSCALL:
3334
3335 if (ret == 0) {
3336 debugs(83, 2, "clientNegotiateSSL: Error negotiating SSL connection on FD " << fd << ": Aborted by client");
3337 comm_close(fd);
3338 return;
3339 } else {
3340 int hard = 1;
3341
3342 if (errno == ECONNRESET)
3343 hard = 0;
3344
3345 debugs(83, hard ? 1 : 2, "clientNegotiateSSL: Error negotiating SSL connection on FD " <<
3346 fd << ": " << strerror(errno) << " (" << errno << ")");
3347
3348 comm_close(fd);
3349
3350 return;
3351 }
3352
3353 case SSL_ERROR_ZERO_RETURN:
3354 debugs(83, 1, "clientNegotiateSSL: Error negotiating SSL connection on FD " << fd << ": Closed by client");
3355 comm_close(fd);
3356 return;
3357
3358 default:
3359 debugs(83, 1, "clientNegotiateSSL: Error negotiating SSL connection on FD " <<
3360 fd << ": " << ERR_error_string(ERR_get_error(), NULL) <<
3361 " (" << ssl_error << "/" << ret << ")");
3362 comm_close(fd);
3363 return;
3364 }
3365
3366 /* NOTREACHED */
3367 }
3368
3369 if (SSL_session_reused(ssl)) {
3370 debugs(83, 2, "clientNegotiateSSL: Session " << SSL_get_session(ssl) <<
3371 " reused on FD " << fd << " (" << fd_table[fd].ipaddr << ":" << (int)fd_table[fd].remote_port << ")");
3372 } else {
3373 if (do_debug(83, 4)) {
3374 /* Write out the SSL session details.. actually the call below, but
3375 * OpenSSL headers do strange typecasts confusing GCC.. */
3376 /* PEM_write_SSL_SESSION(debug_log, SSL_get_session(ssl)); */
3377 #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00908000L
3378 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);
3379
3380 #elif (ALLOW_ALWAYS_SSL_SESSION_DETAIL == 1)
3381
3382 /* When using gcc 3.3.x and OpenSSL 0.9.7x sometimes a compile error can occur here.
3383 * This is caused by an unpredicatble gcc behaviour on a cast of the first argument
3384 * of PEM_ASN1_write(). For this reason this code section is disabled. To enable it,
3385 * define ALLOW_ALWAYS_SSL_SESSION_DETAIL=1.
3386 * Because there are two possible usable cast, if you get an error here, try the other
3387 * commented line. */
3388
3389 PEM_ASN1_write((int(*)())i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL);
3390 /* PEM_ASN1_write((int(*)(...))i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL); */
3391
3392 #else
3393
3394 debugs(83, 4, "With " OPENSSL_VERSION_TEXT ", session details are available only defining ALLOW_ALWAYS_SSL_SESSION_DETAIL=1 in the source." );
3395
3396 #endif
3397 /* Note: This does not automatically fflush the log file.. */
3398 }
3399
3400 debugs(83, 2, "clientNegotiateSSL: New session " <<
3401 SSL_get_session(ssl) << " on FD " << fd << " (" <<
3402 fd_table[fd].ipaddr << ":" << (int)fd_table[fd].remote_port <<
3403 ")");
3404 }
3405
3406 debugs(83, 3, "clientNegotiateSSL: FD " << fd << " negotiated cipher " <<
3407 SSL_get_cipher(ssl));
3408
3409 client_cert = SSL_get_peer_certificate(ssl);
3410
3411 if (client_cert != NULL) {
3412 debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
3413 " client certificate: subject: " <<
3414 X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0));
3415
3416 debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
3417 " client certificate: issuer: " <<
3418 X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0));
3419
3420
3421 X509_free(client_cert);
3422 } else {
3423 debugs(83, 5, "clientNegotiateSSL: FD " << fd <<
3424 " has no certificate.");
3425 }
3426
3427 conn->readSomeData();
3428 }
3429
3430 /** handle a new HTTPS connection */
3431 static void
3432 httpsAccept(const CommAcceptCbParams &params)
3433 {
3434 http_port_list *s = (http_port_list *)params.data;
3435
3436 if (params.flag != COMM_OK) {
3437 // Its possible the call was still queued when the client disconnected
3438 debugs(33, 2, "httpsAccept: " << s->listenConn << ": accept failure: " << xstrerr(params.xerrno));
3439 return;
3440 }
3441
3442 SSL_CTX *sslContext = s->staticSslContext.get();
3443 SSL *ssl = NULL;
3444 if (!(ssl = httpsCreate(params.conn, sslContext)))
3445 return;
3446
3447 debugs(33, 4, HERE << params.conn << " accepted, starting SSL negotiation.");
3448 fd_note(params.conn->fd, "client https connect");
3449
3450 if (s->tcp_keepalive.enabled) {
3451 commSetTcpKeepalive(params.conn->fd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
3452 }
3453
3454 incoming_sockets_accepted++;
3455
3456 // Socket is ready, setup the connection manager to start using it
3457 ConnStateData *connState = connStateCreate(params.conn, s);
3458
3459 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
3460 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
3461 TimeoutDialer, connState, ConnStateData::requestTimeout);
3462 commSetConnTimeout(params.conn, Config.Timeout.request, timeoutCall);
3463
3464 Comm::SetSelect(params.conn->fd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
3465 }
3466
3467 void
3468 ConnStateData::sslCrtdHandleReplyWrapper(void *data, char *reply)
3469 {
3470 ConnStateData * state_data = (ConnStateData *)(data);
3471 state_data->sslCrtdHandleReply(reply);
3472 }
3473
3474 void
3475 ConnStateData::sslCrtdHandleReply(const char * reply)
3476 {
3477 if (!reply) {
3478 debugs(1, 1, HERE << "\"ssl_crtd\" helper return <NULL> reply");
3479 } else {
3480 Ssl::CrtdMessage reply_message;
3481 if (reply_message.parse(reply, strlen(reply)) != Ssl::CrtdMessage::OK) {
3482 debugs(33, 5, HERE << "Reply from ssl_crtd for " << sslHostName << " is incorrect");
3483 } else {
3484 if (reply_message.getCode() != "OK") {
3485 debugs(33, 5, HERE << "Certificate for " << sslHostName << " cannot be generated. ssl_crtd response: " << reply_message.getBody());
3486 } else {
3487 debugs(33, 5, HERE << "Certificate for " << sslHostName << " was successfully recieved from ssl_crtd");
3488 SSL_CTX *ctx = Ssl::generateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str());
3489 getSslContextDone(ctx, true);
3490 return;
3491 }
3492 }
3493 }
3494 getSslContextDone(NULL);
3495 }
3496
3497 bool
3498 ConnStateData::getSslContextStart()
3499 {
3500 char const * host = sslHostName.termedBuf();
3501 if (port->generateHostCertificates && host && strcmp(host, "") != 0) {
3502 debugs(33, 5, HERE << "Finding SSL certificate for " << host << " in cache");
3503 Ssl::LocalContextStorage & ssl_ctx_cache(Ssl::TheGlobalContextStorage.getLocalStorage(port->s));
3504 SSL_CTX * dynCtx = ssl_ctx_cache.find(host);
3505 if (dynCtx) {
3506 debugs(33, 5, HERE << "SSL certificate for " << host << " have found in cache");
3507 if (Ssl::verifySslCertificateDate(dynCtx)) {
3508 debugs(33, 5, HERE << "Cached SSL certificate for " << host << " is valid");
3509 return getSslContextDone(dynCtx);
3510 } else {
3511 debugs(33, 5, HERE << "Cached SSL certificate for " << host << " is out of date. Delete this certificate from cache");
3512 ssl_ctx_cache.remove(host);
3513 }
3514 } else {
3515 debugs(33, 5, HERE << "SSL certificate for " << host << " haven't found in cache");
3516 }
3517
3518 #if USE_SSL_CRTD
3519 debugs(33, 5, HERE << "Generating SSL certificate for " << host << " using ssl_crtd.");
3520 Ssl::CrtdMessage request_message;
3521 request_message.setCode(Ssl::CrtdMessage::code_new_certificate);
3522 Ssl::CrtdMessage::BodyParams map;
3523 map.insert(std::make_pair(Ssl::CrtdMessage::param_host, host));
3524 std::string bufferToWrite;
3525 Ssl::writeCertAndPrivateKeyToMemory(port->signingCert, port->signPkey, bufferToWrite);
3526 request_message.composeBody(map, bufferToWrite);
3527 Ssl::Helper::GetInstance()->sslSubmit(request_message, sslCrtdHandleReplyWrapper, this);
3528 return true;
3529 #else
3530 debugs(33, 5, HERE << "Generating SSL certificate for " << host);
3531 dynCtx = Ssl::generateSslContext(host, port->signingCert, port->signPkey);
3532 return getSslContextDone(dynCtx, true);
3533 #endif //USE_SSL_CRTD
3534 }
3535 return getSslContextDone(NULL);
3536 }
3537
3538 bool
3539 ConnStateData::getSslContextDone(SSL_CTX * sslContext, bool isNew)
3540 {
3541 // Try to add generated ssl context to storage.
3542 if (port->generateHostCertificates && isNew) {
3543
3544 Ssl::addChainToSslContext(sslContext, port->certsToChain.get());
3545
3546 Ssl::LocalContextStorage & ssl_ctx_cache(Ssl::TheGlobalContextStorage.getLocalStorage(port->s));
3547 if (sslContext && sslHostName != "") {
3548 if (!ssl_ctx_cache.add(sslHostName.termedBuf(), sslContext)) {
3549 // If it is not in storage delete after using. Else storage deleted it.
3550 fd_table[clientConnection->fd].dynamicSslContext = sslContext;
3551 }
3552 } else {
3553 debugs(33, 2, HERE << "Failed to generate SSL cert for " << sslHostName);
3554 }
3555 }
3556
3557 // If generated ssl context = NULL, try to use static ssl context.
3558 if (!sslContext) {
3559 if (!port->staticSslContext) {
3560 debugs(83, 1, "Closing SSL " << clientConnection->remote << " as lacking SSL context");
3561 clientConnection->close();
3562 return false;
3563 } else {
3564 debugs(33, 5, HERE << "Using static ssl context.");
3565 sslContext = port->staticSslContext.get();
3566 }
3567 }
3568
3569 SSL *ssl = NULL;
3570 if (!(ssl = httpsCreate(clientConnection, sslContext)))
3571 return false;
3572
3573 // commSetConnTimeout() was called for this request before we switched.
3574
3575 // Disable the client read handler until peer selection is complete
3576 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
3577 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, clientNegotiateSSL, this, 0);
3578 switchedToHttps_ = true;
3579 return true;
3580 }
3581
3582 bool
3583 ConnStateData::switchToHttps(const char *host)
3584 {
3585 assert(!switchedToHttps_);
3586
3587 sslHostName = host;
3588
3589 //HTTPMSGLOCK(currentobject->http->request);
3590 assert(areAllContextsForThisConnection());
3591 freeAllContexts();
3592 //currentobject->connIsFinished();
3593
3594 // We are going to read new request
3595 flags.readMore = true;
3596 debugs(33, 5, HERE << "converting " << clientConnection << " to SSL");
3597
3598 return getSslContextStart();
3599 }
3600
3601 #endif /* USE_SSL */
3602
3603 /// check FD after clientHttp[s]ConnectionOpened, adjust HttpSockets as needed
3604 static bool
3605 OpenedHttpSocket(const Comm::ConnectionPointer &c, const Ipc::FdNoteId portType)
3606 {
3607 if (!Comm::IsConnOpen(c)) {
3608 Must(NHttpSockets > 0); // we tried to open some
3609 --NHttpSockets; // there will be fewer sockets than planned
3610 Must(HttpSockets[NHttpSockets] < 0); // no extra fds received
3611
3612 if (!NHttpSockets) // we could not open any listen sockets at all
3613 fatalf("Unable to open %s",FdNote(portType));
3614
3615 return false;
3616 }
3617 return true;
3618 }
3619
3620 /// find any unused HttpSockets[] slot and store fd there or return false
3621 static bool
3622 AddOpenedHttpSocket(const Comm::ConnectionPointer &conn)
3623 {
3624 bool found = false;
3625 for (int i = 0; i < NHttpSockets && !found; i++) {
3626 if ((found = HttpSockets[i] < 0))
3627 HttpSockets[i] = conn->fd;
3628 }
3629 return found;
3630 }
3631
3632 static void
3633 clientHttpConnectionsOpen(void)
3634 {
3635 http_port_list *s = NULL;
3636
3637 for (s = Config.Sockaddr.http; s; s = s->next) {
3638 if (MAXHTTPPORTS == NHttpSockets) {
3639 debugs(1, 1, "WARNING: You have too many 'http_port' lines.");
3640 debugs(1, 1, " The limit is " << MAXHTTPPORTS);
3641 continue;
3642 }
3643
3644 #if USE_SSL
3645 if (s->sslBump && !Config.accessList.ssl_bump) {
3646 debugs(33, DBG_IMPORTANT, "WARNING: No ssl_bump configured. Disabling ssl-bump on " << s->protocol << "_port " << s->s);
3647 s->sslBump = 0;
3648 }
3649
3650 if (s->sslBump && !s->staticSslContext && !s->generateHostCertificates) {
3651 debugs(1, DBG_IMPORTANT, "Will not bump SSL at http_port " << s->s << " due to SSL initialization failure.");
3652 s->sslBump = 0;
3653 }
3654 if (s->sslBump) {
3655 // Create ssl_ctx cache for this port.
3656 Ssl::TheGlobalContextStorage.addLocalStorage(s->s, s->dynamicCertMemCacheSize == std::numeric_limits<size_t>::max() ? 4194304 : s->dynamicCertMemCacheSize);
3657 }
3658 #endif
3659
3660 // Fill out a Comm::Connection which IPC will open as a listener for us
3661 // then pass back when active so we can start a TcpAcceptor subscription.
3662 s->listenConn = new Comm::Connection;
3663 s->listenConn->local = s->s;
3664 s->listenConn->flags = COMM_NONBLOCKING | (s->spoof_client_ip ? COMM_TRANSPARENT : 0) | (s->intercepted ? COMM_INTERCEPTION : 0);
3665
3666 // setup the subscriptions such that new connections accepted by listenConn are handled by HTTP
3667 typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
3668 RefCount<AcceptCall> subCall = commCbCall(5, 5, "httpAccept", CommAcceptCbPtrFun(httpAccept, s));
3669 Subscription::Pointer sub = new CallSubscription<AcceptCall>(subCall);
3670
3671 AsyncCall::Pointer listenCall = asyncCall(33,2, "clientListenerConnectionOpened",
3672 ListeningStartedDialer(&clientListenerConnectionOpened, s, Ipc::fdnHttpSocket, sub));
3673 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->listenConn, Ipc::fdnHttpSocket, listenCall);
3674
3675 HttpSockets[NHttpSockets++] = -1; // set in clientListenerConnectionOpened
3676 }
3677 }
3678
3679 #if USE_SSL
3680 static void
3681 clientHttpsConnectionsOpen(void)
3682 {
3683 http_port_list *s;
3684
3685 for (s = Config.Sockaddr.https; s; s = s->next) {
3686 if (MAXHTTPPORTS == NHttpSockets) {
3687 debugs(1, 1, "Ignoring 'https_port' lines exceeding the limit.");
3688 debugs(1, 1, "The limit is " << MAXHTTPPORTS << " HTTPS ports.");
3689 continue;
3690 }
3691
3692 if (!s->staticSslContext) {
3693 debugs(1, 1, "Ignoring https_port " << s->s <<
3694 " due to SSL initialization failure.");
3695 continue;
3696 }
3697
3698 // Fill out a Comm::Connection which IPC will open as a listener for us
3699 s->listenConn = new Comm::Connection;
3700 s->listenConn->local = s->s;
3701 s->listenConn->flags = COMM_NONBLOCKING | (s->spoof_client_ip ? COMM_TRANSPARENT : 0) |
3702 (s->intercepted ? COMM_INTERCEPTION : 0);
3703
3704 // setup the subscriptions such that new connections accepted by listenConn are handled by HTTPS
3705 typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
3706 RefCount<AcceptCall> subCall = commCbCall(5, 5, "httpsAccept", CommAcceptCbPtrFun(httpsAccept, s));
3707 Subscription::Pointer sub = new CallSubscription<AcceptCall>(subCall);
3708
3709 AsyncCall::Pointer listenCall = asyncCall(33, 2, "clientListenerConnectionOpened",
3710 ListeningStartedDialer(&clientListenerConnectionOpened,
3711 s, Ipc::fdnHttpsSocket, sub));
3712 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->listenConn, Ipc::fdnHttpsSocket, listenCall);
3713 HttpSockets[NHttpSockets++] = -1;
3714 }
3715 }
3716 #endif
3717
3718 /// process clientHttpConnectionsOpen result
3719 static void
3720 clientListenerConnectionOpened(http_port_list *s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub)
3721 {
3722 if (!OpenedHttpSocket(s->listenConn, portTypeNote))
3723 return;
3724
3725 Must(s);
3726 Must(Comm::IsConnOpen(s->listenConn));
3727
3728 // TCP: setup a job to handle accept() with subscribed handler
3729 AsyncJob::Start(new Comm::TcpAcceptor(s->listenConn, FdNote(portTypeNote), sub));
3730
3731 debugs(1, 1, "Accepting " <<
3732 (s->intercepted ? "NAT intercepted " : "") <<
3733 (s->spoof_client_ip ? "TPROXY spoofing " : "") <<
3734 (s->sslBump ? "SSL bumped " : "") <<
3735 (s->accel ? "reverse-proxy " : "")
3736 << FdNote(portTypeNote) << " connections at "
3737 << s->listenConn);
3738
3739 Must(AddOpenedHttpSocket(s->listenConn)); // otherwise, we have received a fd we did not ask for
3740 }
3741
3742 void
3743 clientOpenListenSockets(void)
3744 {
3745 clientHttpConnectionsOpen();
3746 #if USE_SSL
3747 clientHttpsConnectionsOpen();
3748 #endif
3749
3750 if (NHttpSockets < 1)
3751 fatal("No HTTP or HTTPS ports configured");
3752 }
3753
3754 void
3755 clientHttpConnectionsClose(void)
3756 {
3757 for (http_port_list *s = Config.Sockaddr.http; s; s = s->next) {
3758 if (s->listenConn != NULL) {
3759 debugs(1, 1, "Closing HTTP port " << s->listenConn->local);
3760 s->listenConn->close();
3761 s->listenConn = NULL;
3762 }
3763 }
3764
3765 #if USE_SSL
3766 for (http_port_list *s = Config.Sockaddr.https; s; s = s->next) {
3767 if (s->listenConn != NULL) {
3768 debugs(1, 1, "Closing HTTPS port " << s->listenConn->local);
3769 s->listenConn->close();
3770 s->listenConn = NULL;
3771 }
3772 }
3773 #endif
3774
3775 // TODO see if we can drop HttpSockets array entirely */
3776 for (int i = 0; i < NHttpSockets; i++) {
3777 HttpSockets[i] = -1;
3778 }
3779
3780 NHttpSockets = 0;
3781 }
3782
3783 int
3784 varyEvaluateMatch(StoreEntry * entry, HttpRequest * request)
3785 {
3786 const char *vary = request->vary_headers;
3787 int has_vary = entry->getReply()->header.has(HDR_VARY);
3788 #if X_ACCELERATOR_VARY
3789
3790 has_vary |=
3791 entry->getReply()->header.has(HDR_X_ACCELERATOR_VARY);
3792 #endif
3793
3794 if (!has_vary || !entry->mem_obj->vary_headers) {
3795 if (vary) {
3796 /* Oops... something odd is going on here.. */
3797 debugs(33, 1, "varyEvaluateMatch: Oops. Not a Vary object on second attempt, '" <<
3798 entry->mem_obj->url << "' '" << vary << "'");
3799 safe_free(request->vary_headers);
3800 return VARY_CANCEL;
3801 }
3802
3803 if (!has_vary) {
3804 /* This is not a varying object */
3805 return VARY_NONE;
3806 }
3807
3808 /* virtual "vary" object found. Calculate the vary key and
3809 * continue the search
3810 */
3811 vary = httpMakeVaryMark(request, entry->getReply());
3812
3813 if (vary) {
3814 request->vary_headers = xstrdup(vary);
3815 return VARY_OTHER;
3816 } else {
3817 /* Ouch.. we cannot handle this kind of variance */
3818 /* XXX This cannot really happen, but just to be complete */
3819 return VARY_CANCEL;
3820 }
3821 } else {
3822 if (!vary) {
3823 vary = httpMakeVaryMark(request, entry->getReply());
3824
3825 if (vary)
3826 request->vary_headers = xstrdup(vary);
3827 }
3828
3829 if (!vary) {
3830 /* Ouch.. we cannot handle this kind of variance */
3831 /* XXX This cannot really happen, but just to be complete */
3832 return VARY_CANCEL;
3833 } else if (strcmp(vary, entry->mem_obj->vary_headers) == 0) {
3834 return VARY_MATCH;
3835 } else {
3836 /* Oops.. we have already been here and still haven't
3837 * found the requested variant. Bail out
3838 */
3839 debugs(33, 1, "varyEvaluateMatch: Oops. Not a Vary match on second attempt, '" <<
3840 entry->mem_obj->url << "' '" << vary << "'");
3841 return VARY_CANCEL;
3842 }
3843 }
3844 }
3845
3846 ACLFilledChecklist *
3847 clientAclChecklistCreate(const acl_access * acl, ClientHttpRequest * http)
3848 {
3849 ConnStateData * conn = http->getConn();
3850 ACLFilledChecklist *ch = new ACLFilledChecklist(acl, http->request,
3851 cbdataReferenceValid(conn) && conn != NULL && conn->clientConnection != NULL ? conn->clientConnection->rfc931 : dash_str);
3852
3853 /*
3854 * hack for ident ACL. It needs to get full addresses, and a place to store
3855 * the ident result on persistent connections...
3856 */
3857 /* connection oriented auth also needs these two lines for it's operation. */
3858 /*
3859 * Internal requests do not have a connection reference, because: A) their
3860 * byte count may be transformed before being applied to an outbound
3861 * connection B) they are internal - any limiting on them should be done on
3862 * the server end.
3863 */
3864
3865 if (conn != NULL)
3866 ch->conn(conn); /* unreferenced in FilledCheckList.cc */
3867
3868 return ch;
3869 }
3870
3871 CBDATA_CLASS_INIT(ConnStateData);
3872
3873 ConnStateData::ConnStateData() :
3874 AsyncJob("ConnStateData"),
3875 switchedToHttps_(false),
3876 stoppedSending_(NULL),
3877 stoppedReceiving_(NULL)
3878 {
3879 pinning.pinned = false;
3880 pinning.auth = false;
3881 }
3882
3883 bool
3884 ConnStateData::transparent() const
3885 {
3886 return clientConnection != NULL && (clientConnection->flags & (COMM_TRANSPARENT|COMM_INTERCEPTION));
3887 }
3888
3889 bool
3890 ConnStateData::reading() const
3891 {
3892 return reader != NULL;
3893 }
3894
3895 void
3896 ConnStateData::stopReading()
3897 {
3898 if (reading()) {
3899 comm_read_cancel(clientConnection->fd, reader);
3900 reader = NULL;
3901 }
3902 }
3903
3904
3905 BodyPipe::Pointer
3906 ConnStateData::expectRequestBody(int64_t size)
3907 {
3908 bodyPipe = new BodyPipe(this);
3909 if (size >= 0)
3910 bodyPipe->setBodySize(size);
3911 else
3912 startDechunkingRequest();
3913 return bodyPipe;
3914 }
3915
3916 int64_t
3917 ConnStateData::mayNeedToReadMoreBody() const
3918 {
3919 if (!bodyPipe)
3920 return 0; // request without a body or read/produced all body bytes
3921
3922 if (!bodyPipe->bodySizeKnown())
3923 return -1; // probably need to read more, but we cannot be sure
3924
3925 const int64_t needToProduce = bodyPipe->unproducedSize();
3926 const int64_t haveAvailable = static_cast<int64_t>(in.notYetUsed);
3927
3928 if (needToProduce <= haveAvailable)
3929 return 0; // we have read what we need (but are waiting for pipe space)
3930
3931 return needToProduce - haveAvailable;
3932 }
3933
3934 void
3935 ConnStateData::stopReceiving(const char *error)
3936 {
3937 debugs(33, 4, HERE << "receiving error (" << clientConnection << "): " << error <<
3938 "; old sending error: " <<
3939 (stoppedSending() ? stoppedSending_ : "none"));
3940
3941 if (const char *oldError = stoppedReceiving()) {
3942 debugs(33, 3, HERE << "already stopped receiving: " << oldError);
3943 return; // nothing has changed as far as this connection is concerned
3944 }
3945
3946 stoppedReceiving_ = error;
3947
3948 if (const char *sendError = stoppedSending()) {
3949 debugs(33, 3, HERE << "closing because also stopped sending: " << sendError);
3950 clientConnection->close();
3951 }
3952 }
3953
3954 void
3955 ConnStateData::expectNoForwarding()
3956 {
3957 if (bodyPipe != NULL) {
3958 debugs(33, 4, HERE << "no consumer for virgin body " << bodyPipe->status());
3959 bodyPipe->expectNoConsumption();
3960 }
3961 }
3962
3963 /// initialize dechunking state
3964 void
3965 ConnStateData::startDechunkingRequest()
3966 {
3967 Must(bodyPipe != NULL);
3968 debugs(33, 5, HERE << "start dechunking" << bodyPipe->status());
3969 assert(!in.bodyParser);
3970 in.bodyParser = new ChunkedCodingParser;
3971 }
3972
3973 /// put parsed content into input buffer and clean up
3974 void
3975 ConnStateData::finishDechunkingRequest(bool withSuccess)
3976 {
3977 debugs(33, 5, HERE << "finish dechunking: " << withSuccess);
3978
3979 if (bodyPipe != NULL) {
3980 debugs(33, 7, HERE << "dechunked tail: " << bodyPipe->status());
3981 BodyPipe::Pointer myPipe = bodyPipe;
3982 stopProducingFor(bodyPipe, withSuccess); // sets bodyPipe->bodySize()
3983 Must(!bodyPipe); // we rely on it being nil after we are done with body
3984 if (withSuccess) {
3985 Must(myPipe->bodySizeKnown());
3986 ClientSocketContext::Pointer context = getCurrentContext();
3987 if (context != NULL && context->http && context->http->request)
3988 context->http->request->setContentLength(myPipe->bodySize());
3989 }
3990 }
3991
3992 delete in.bodyParser;
3993 in.bodyParser = NULL;
3994 }
3995
3996 char *
3997 ConnStateData::In::addressToReadInto() const
3998 {
3999 return buf + notYetUsed;
4000 }
4001
4002 ConnStateData::In::In() : bodyParser(NULL),
4003 buf (NULL), notYetUsed (0), allocatedSize (0)
4004 {}
4005
4006 ConnStateData::In::~In()
4007 {
4008 if (allocatedSize)
4009 memFreeBuf(allocatedSize, buf);
4010 delete bodyParser; // TODO: pool
4011 }
4012
4013 void
4014 ConnStateData::sendControlMsg(HttpControlMsg msg)
4015 {
4016 if (!isOpen()) {
4017 debugs(33, 3, HERE << "ignoring 1xx due to earlier closure");
4018 return;
4019 }
4020
4021 ClientSocketContext::Pointer context = getCurrentContext();
4022 if (context != NULL) {
4023 context->writeControlMsg(msg); // will call msg.cbSuccess
4024 return;
4025 }
4026
4027 debugs(33, 3, HERE << " closing due to missing context for 1xx");
4028 clientConnection->close();
4029 }
4030
4031 /* This is a comm call normally scheduled by comm_close() */
4032 void
4033 ConnStateData::clientPinnedConnectionClosed(const CommCloseCbParams &io)
4034 {
4035 unpinConnection();
4036 }
4037
4038 void
4039 ConnStateData::pinConnection(const Comm::ConnectionPointer &pinServer, HttpRequest *request, struct peer *aPeer, bool auth)
4040 {
4041 char desc[FD_DESC_SZ];
4042
4043 if (Comm::IsConnOpen(pinning.serverConnection)) {
4044 if (pinning.serverConnection->fd == pinServer->fd)
4045 return;
4046
4047 unpinConnection(); // clears fields ready for re-use. Prevent close() scheduling our close handler.
4048 pinning.serverConnection->close();
4049 } else
4050 unpinConnection(); // clears fields ready for re-use.
4051
4052 pinning.serverConnection = pinServer;
4053 pinning.host = xstrdup(request->GetHost());
4054 pinning.port = request->port;
4055 pinning.pinned = true;
4056 if (aPeer)
4057 pinning.peer = cbdataReference(aPeer);
4058 pinning.auth = auth;
4059 char stmp[MAX_IPSTRLEN];
4060 snprintf(desc, FD_DESC_SZ, "%s pinned connection for %s (%d)",
4061 (auth || !aPeer) ? request->GetHost() : aPeer->name, clientConnection->remote.ToURL(stmp,MAX_IPSTRLEN), clientConnection->fd);
4062 fd_note(pinning.serverConnection->fd, desc);
4063
4064 typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
4065 pinning.closeHandler = JobCallback(33, 5,
4066 Dialer, this, ConnStateData::clientPinnedConnectionClosed);
4067 comm_add_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
4068 }
4069
4070 const Comm::ConnectionPointer
4071 ConnStateData::validatePinnedConnection(HttpRequest *request, const struct peer *aPeer)
4072 {
4073 bool valid = true;
4074 if (!Comm::IsConnOpen(pinning.serverConnection))
4075 valid = false;
4076 if (pinning.auth && request && strcasecmp(pinning.host, request->GetHost()) != 0) {
4077 valid = false;
4078 }
4079 if (request && pinning.port != request->port) {
4080 valid = false;
4081 }
4082 if (pinning.peer && !cbdataReferenceValid(pinning.peer)) {
4083 valid = false;
4084 }
4085 if (aPeer != pinning.peer) {
4086 valid = false;
4087 }
4088
4089 if (!valid) {
4090 /* The pinning info is not safe, remove any pinning info */
4091 unpinConnection();
4092 }
4093
4094 return pinning.serverConnection;
4095 }
4096
4097 void
4098 ConnStateData::unpinConnection()
4099 {
4100 if (pinning.peer)
4101 cbdataReferenceDone(pinning.peer);
4102
4103 if (pinning.closeHandler != NULL) {
4104 comm_remove_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
4105 pinning.closeHandler = NULL;
4106 }
4107 /// also close the server side socket, we should not use it for any future requests...
4108 pinning.serverConnection->close();
4109 safe_free(pinning.host);
4110
4111 /* NOTE: pinning.pinned should be kept. This combined with fd == -1 at the end of a request indicates that the host
4112 * connection has gone away */
4113 }