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