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