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