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