]> git.ipfire.org Git - thirdparty/squid.git/blob - src/client_side.cc
fix headers to allow inclusion into C++ source
[thirdparty/squid.git] / src / client_side.cc
1
2 /*
3 * $Id: client_side.cc,v 1.592 2002/09/15 06:40:57 robertc Exp $
4 *
5 * DEBUG: section 33 Client-side Routines
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 /* Errors and client side
37 *
38 * Problem the first: the store entry is no longer authoritative on the
39 * reply status. EBITTEST (E_ABORT) is no longer a valid test outside
40 * of client_side_reply.c.
41 * Problem the second: resources are wasted if we delay in cleaning up.
42 * Problem the third we can't depend on a connection close to clean up.
43 *
44 * Nice thing the first: Any step in the stream can callback with data
45 * representing an error.
46 * Nice thing the second: once you stop requesting reads from upstream,
47 * upstream can be stopped too.
48 *
49 * Solution #1: Error has a callback mechanism to hand over a membuf
50 * with the error content. The failing node pushes that back as the
51 * reply. Can this be generalised to reduce duplicate efforts?
52 * A: Possibly. For now, only one location uses this.
53 * How to deal with pre-stream errors?
54 * Tell client_side_reply that we *want* an error page before any
55 * stream calls occur. Then we simply read as normal.
56 */
57
58 #include "squid.h"
59
60 #if IPF_TRANSPARENT
61 #if HAVE_SYS_IOCTL_H
62 #include <sys/ioctl.h>
63 #endif
64 #include <netinet/tcp.h>
65 #include <net/if.h>
66 #if HAVE_IP_FIL_COMPAT_H
67 #include <ip_fil_compat.h>
68 #elif HAVE_NETINET_IP_FIL_COMPAT_H
69 #include <netinet/ip_fil_compat.h>
70 #elif HAVE_IP_COMPAT_H
71 #include <ip_compat.h>
72 #elif HAVE_NETINET_IP_COMPAT_H
73 #include <netinet/ip_compat.h>
74 #endif
75 #if HAVE_IP_FIL_H
76 #include <ip_fil.h>
77 #elif HAVE_NETINET_IP_FIL_H
78 #include <netinet/ip_fil.h>
79 #endif
80 #if HAVE_IP_NAT_H
81 #include <ip_nat.h>
82 #elif HAVE_NETINET_IP_NAT_H
83 #include <netinet/ip_nat.h>
84 #endif
85 #endif
86
87 #if PF_TRANSPARENT
88 #include <sys/types.h>
89 #include <sys/socket.h>
90 #include <sys/ioctl.h>
91 #include <sys/fcntl.h>
92 #include <net/if.h>
93 #include <netinet/in.h>
94 #include <net/pfvar.h>
95 #endif
96
97 #if LINUX_NETFILTER
98 #include <linux/netfilter_ipv4.h>
99 #endif
100
101
102 #if LINGERING_CLOSE
103 #define comm_close comm_lingering_close
104 #endif
105
106 static const char *const crlf = "\r\n";
107
108 #define FAILURE_MODE_TIME 300
109
110 /* Persistent connection logic:
111 *
112 * requests (httpClientRequest structs) get added to the connection
113 * list, with the current one being chr
114 *
115 * The request is *immediately* kicked off, and data flows through
116 * to clientSocketRecipient.
117 *
118 * If the data that arrives at clientSocketRecipient is not for the current
119 * request, clientSocketRecipient simply returns, without requesting more
120 * data, or sending it.
121 *
122 * ClientKeepAliveNextRequest will then detect the presence of data in
123 * the next clientHttpRequest, and will send it, restablishing the
124 * data flow.
125 */
126
127 /* our socket-related context */
128 typedef struct _clientSocketContext {
129 clientHttpRequest *http; /* we own this */
130 char reqbuf[HTTP_REQBUF_SZ];
131 struct _clientSocketContext *next;
132 struct {
133 int deferred:1; /* This is a pipelined request waiting for the
134 * current object to complete */
135 } flags;
136 struct {
137 clientStreamNode *node;
138 HttpReply *rep;
139 const char *body_data;
140 ssize_t body_size;
141 } deferredparams;
142 } clientSocketContext;
143
144 CBDATA_TYPE(clientSocketContext);
145
146 /* Local functions */
147 /* clientSocketContext */
148 static FREE clientSocketContextFree;
149 static clientSocketContext *clientSocketContextNew(clientHttpRequest *);
150 /* other */
151 static CWCB clientWriteComplete;
152 static CWCB clientWriteBodyComplete;
153 static PF clientReadRequest;
154 static PF connStateFree;
155 static PF requestTimeout;
156 static PF clientLifetimeTimeout;
157 static void checkFailureRatio(err_type, hier_code);
158 static clientSocketContext *parseHttpRequestAbort(ConnStateData * conn,
159 const char *uri);
160 static clientSocketContext *parseHttpRequest(ConnStateData *, method_t *, int *,
161 char **, size_t *);
162 #if USE_IDENT
163 static IDCB clientIdentDone;
164 #endif
165 static CSCB clientSocketRecipient;
166 static CSD clientSocketDetach;
167 static void clientSetKeepaliveFlag(clientHttpRequest *);
168 static int clientCheckContentLength(request_t * r);
169 static DEFER httpAcceptDefer;
170 static int clientRequestBodyTooLarge(int clen);
171 static void clientProcessBody(ConnStateData * conn);
172
173 void
174 clientSocketContextFree(void *data)
175 {
176 clientSocketContext *context = data;
177 ConnStateData *conn = context->http->conn;
178 clientStreamNode *node = context->http->client_stream.tail->data;
179 /* We are *always* the tail - prevent recursive free */
180 assert(context == node->data);
181 node->data = NULL;
182 httpRequestFree(context->http);
183 /* clean up connection links to us */
184 assert(context != context->next);
185 if (conn) {
186 void **p;
187 clientSocketContext **S;
188 assert(conn->currentobject != NULL);
189 /* Unlink us from the connection request list */
190 p = &conn->currentobject;
191 S = (clientSocketContext **) p;
192 while (*S) {
193 if (*S == context)
194 break;
195 S = &(*S)->next;
196 }
197 assert(*S != NULL);
198 *S = context->next;
199 context->next = NULL;
200 }
201 }
202
203 clientSocketContext *
204 clientSocketContextNew(clientHttpRequest * http)
205 {
206 clientSocketContext *rv;
207 assert(http != NULL);
208 CBDATA_INIT_TYPE_FREECB(clientSocketContext, clientSocketContextFree);
209 rv = cbdataAlloc(clientSocketContext);
210 rv->http = http;
211 return rv;
212 }
213
214 #if USE_IDENT
215 static void
216 clientIdentDone(const char *ident, void *data)
217 {
218 ConnStateData *conn = data;
219 xstrncpy(conn->rfc931, ident ? ident : dash_str, USER_IDENT_SZ);
220 }
221
222 #endif
223
224 static void
225 clientUpdateCounters(clientHttpRequest * http)
226 {
227 int svc_time = tvSubMsec(http->start, current_time);
228 ping_data *i;
229 HierarchyLogEntry *H;
230 statCounter.client_http.requests++;
231 if (isTcpHit(http->logType))
232 statCounter.client_http.hits++;
233 if (http->logType == LOG_TCP_HIT)
234 statCounter.client_http.disk_hits++;
235 else if (http->logType == LOG_TCP_MEM_HIT)
236 statCounter.client_http.mem_hits++;
237 if (http->request->errType != ERR_NONE)
238 statCounter.client_http.errors++;
239 statHistCount(&statCounter.client_http.all_svc_time, svc_time);
240 /*
241 * The idea here is not to be complete, but to get service times
242 * for only well-defined types. For example, we don't include
243 * LOG_TCP_REFRESH_FAIL_HIT because its not really a cache hit
244 * (we *tried* to validate it, but failed).
245 */
246 switch (http->logType) {
247 case LOG_TCP_REFRESH_HIT:
248 statHistCount(&statCounter.client_http.nh_svc_time, svc_time);
249 break;
250 case LOG_TCP_IMS_HIT:
251 statHistCount(&statCounter.client_http.nm_svc_time, svc_time);
252 break;
253 case LOG_TCP_HIT:
254 case LOG_TCP_MEM_HIT:
255 case LOG_TCP_OFFLINE_HIT:
256 statHistCount(&statCounter.client_http.hit_svc_time, svc_time);
257 break;
258 case LOG_TCP_MISS:
259 case LOG_TCP_CLIENT_REFRESH_MISS:
260 statHistCount(&statCounter.client_http.miss_svc_time, svc_time);
261 break;
262 default:
263 /* make compiler warnings go away */
264 break;
265 }
266 H = &http->request->hier;
267 switch (H->alg) {
268 case PEER_SA_DIGEST:
269 statCounter.cd.times_used++;
270 break;
271 case PEER_SA_ICP:
272 statCounter.icp.times_used++;
273 i = &H->ping;
274 if (0 != i->stop.tv_sec && 0 != i->start.tv_sec)
275 statHistCount(&statCounter.icp.query_svc_time,
276 tvSubUsec(i->start, i->stop));
277 if (i->timeout)
278 statCounter.icp.query_timeouts++;
279 break;
280 case PEER_SA_NETDB:
281 statCounter.netdb.times_used++;
282 break;
283 default:
284 break;
285 }
286 }
287
288 void
289 httpRequestFree(void *data)
290 {
291 clientHttpRequest *http = data;
292 ConnStateData *conn;
293 request_t *request = NULL;
294 MemObject *mem = NULL;
295 assert(http != NULL);
296 conn = http->conn;
297 request = http->request;
298 debug(33, 3) ("httpRequestFree: %s\n", http->uri);
299 /* FIXME: This needs to use the stream */
300 if (!clientCheckTransferDone(http)) {
301 if (request && request->body_connection)
302 clientAbortBody(request); /* abort body transter */
303 /* the ICP check here was erroneous - storeReleaseRequest was always called if entry was valid
304 */
305 }
306 assert(http->logType < LOG_TYPE_MAX);
307 if (http->entry)
308 mem = http->entry->mem_obj;
309 if (http->out.size || http->logType) {
310 http->al.icp.opcode = ICP_INVALID;
311 http->al.url = http->log_uri;
312 debug(33, 9) ("httpRequestFree: al.url='%s'\n", http->al.url);
313 if (mem) {
314 http->al.http.code = mem->reply->sline.status;
315 http->al.http.content_type = strBuf(mem->reply->content_type);
316 }
317 http->al.cache.caddr = conn ? conn->log_addr : no_addr;
318 http->al.cache.size = http->out.size;
319 http->al.cache.code = http->logType;
320 http->al.cache.msec = tvSubMsec(http->start, current_time);
321 if (request) {
322 Packer p;
323 MemBuf mb;
324 memBufDefInit(&mb);
325 packerToMemInit(&p, &mb);
326 httpHeaderPackInto(&request->header, &p);
327 http->al.http.method = request->method;
328 http->al.http.version = request->http_ver;
329 http->al.headers.request = xstrdup(mb.buf);
330 http->al.hier = request->hier;
331 if (request->auth_user_request) {
332 http->al.cache.authuser =
333 xstrdup(authenticateUserRequestUsername(request->
334 auth_user_request));
335 authenticateAuthUserRequestUnlock(request->auth_user_request);
336 request->auth_user_request = NULL;
337 }
338 if (conn && conn->rfc931[0])
339 http->al.cache.rfc931 = conn->rfc931;
340 packerClean(&p);
341 memBufClean(&mb);
342 }
343 accessLogLog(&http->al);
344 clientUpdateCounters(http);
345 if (conn)
346 clientdbUpdate(conn->peer.sin_addr, http->logType, PROTO_HTTP,
347 http->out.size);
348 }
349 if (request)
350 checkFailureRatio(request->errType, http->al.hier.code);
351 safe_free(http->uri);
352 safe_free(http->log_uri);
353 safe_free(http->al.headers.request);
354 safe_free(http->al.headers.reply);
355 safe_free(http->al.cache.authuser);
356 safe_free(http->redirect.location);
357 requestUnlink(http->request);
358 if (http->client_stream.tail)
359 clientStreamAbort(http->client_stream.tail->data, http);
360 /* moving to the next connection is handled by the context free */
361 dlinkDelete(&http->active, &ClientActiveRequests);
362 cbdataFree(http);
363 }
364
365 /* This is a handler normally called by comm_close() */
366 static void
367 connStateFree(int fd, void *data)
368 {
369 ConnStateData *connState = data;
370 clientSocketContext *context;
371 debug(33, 3) ("connStateFree: FD %d\n", fd);
372 assert(connState != NULL);
373 clientdbEstablished(connState->peer.sin_addr, -1); /* decrement */
374 while ((context = connState->currentobject) != NULL) {
375 assert(context->http->conn == connState);
376 assert(connState->currentobject !=
377 ((clientSocketContext *) connState->currentobject)->next);
378 cbdataFree(context);
379 }
380 if (connState->auth_user_request)
381 authenticateAuthUserRequestUnlock(connState->auth_user_request);
382 connState->auth_user_request = NULL;
383 authenticateOnCloseConnection(connState);
384 memFreeBuf(connState->in.size, connState->in.buf);
385 pconnHistCount(0, connState->nrequests);
386 cbdataFree(connState);
387 #ifdef _SQUID_LINUX_
388 /* prevent those nasty RST packets */
389 {
390 char buf[SQUID_TCP_SO_RCVBUF];
391 while (FD_READ_METHOD(fd, buf, SQUID_TCP_SO_RCVBUF) > 0);
392 }
393 #endif
394 }
395
396 /*
397 * clientSetKeepaliveFlag() sets request->flags.proxy_keepalive.
398 * This is the client-side persistent connection flag. We need
399 * to set this relatively early in the request processing
400 * to handle hacks for broken servers and clients.
401 */
402 static void
403 clientSetKeepaliveFlag(clientHttpRequest * http)
404 {
405 request_t *request = http->request;
406 const HttpHeader *req_hdr = &request->header;
407
408 debug(33, 3) ("clientSetKeepaliveFlag: http_ver = %d.%d\n",
409 request->http_ver.major, request->http_ver.minor);
410 debug(33, 3) ("clientSetKeepaliveFlag: method = %s\n",
411 RequestMethodStr[request->method]);
412 if (!Config.onoff.client_pconns)
413 request->flags.proxy_keepalive = 0;
414 else {
415 http_version_t http_ver;
416 httpBuildVersion(&http_ver, 1, 0);
417 /* we are HTTP/1.0, no matter what the client requests... */
418 if (httpMsgIsPersistent(http_ver, req_hdr))
419 request->flags.proxy_keepalive = 1;
420 }
421 }
422
423 static int
424 clientCheckContentLength(request_t * r)
425 {
426 switch (r->method) {
427 case METHOD_PUT:
428 case METHOD_POST:
429 /* PUT/POST requires a request entity */
430 return (r->content_length >= 0);
431 case METHOD_GET:
432 case METHOD_HEAD:
433 /* We do not want to see a request entity on GET/HEAD requests */
434 return (r->content_length <= 0);
435 default:
436 /* For other types of requests we don't care */
437 return 1;
438 }
439 /* NOT REACHED */
440 }
441
442 int
443 isTcpHit(log_type code)
444 {
445 /* this should be a bitmap for better optimization */
446 if (code == LOG_TCP_HIT)
447 return 1;
448 if (code == LOG_TCP_IMS_HIT)
449 return 1;
450 if (code == LOG_TCP_REFRESH_FAIL_HIT)
451 return 1;
452 if (code == LOG_TCP_REFRESH_HIT)
453 return 1;
454 if (code == LOG_TCP_NEGATIVE_HIT)
455 return 1;
456 if (code == LOG_TCP_MEM_HIT)
457 return 1;
458 if (code == LOG_TCP_OFFLINE_HIT)
459 return 1;
460 return 0;
461 }
462
463 static int
464 clientRequestBodyTooLarge(int clen)
465 {
466 if (0 == Config.maxRequestBodySize)
467 return 0; /* disabled */
468 if (clen < 0)
469 return 0; /* unknown, bug? */
470 if (clen > Config.maxRequestBodySize)
471 return 1; /* too large */
472 return 0;
473 }
474
475 /*
476 * Write a chunk of data to a client socket. If the reply is present, send the reply headers down the wire too,
477 * and clean them up when finished.
478 * Pre-condition:
479 * The request is one backed by a connection, not an internal request.
480 * data context is not NULL
481 * There are no more entries in the stream chain.
482 */
483 static void
484 clientSocketRecipient(clientStreamNode * node, clientHttpRequest * http,
485 HttpReply * rep, const char *body_data, ssize_t body_size)
486 {
487 int fd;
488 clientSocketContext *context;
489 /* Test preconditions */
490 assert(node != NULL);
491 /* TODO: handle this rather than asserting - it should only ever happen if we cause an abort and
492 * the callback chain loops back to here, so we can simply return.
493 * However, that itself shouldn't happen, so it stays as an assert for now.
494 */
495 assert(cbdataReferenceValid(node));
496 assert(node->data != NULL);
497 assert(node->node.next == NULL);
498 context = node->data;
499 assert(http->conn && http->conn->fd != -1);
500 fd = http->conn->fd;
501 if (http->conn->currentobject != context) {
502 /* there is another object in progress, defer this one */
503 debug(33, 2) ("clientSocketRecipient: Deferring %s\n", http->uri);
504 context->flags.deferred = 1;
505 context->deferredparams.node = node;
506 context->deferredparams.rep = rep;
507 context->deferredparams.body_data = body_data;
508 context->deferredparams.body_size = body_size;
509 return;
510 }
511 /* EOF / Read error / aborted entry */
512 if (rep == NULL && body_data == NULL && body_size == 0) {
513 clientWriteComplete(fd, NULL, 0, COMM_OK, context);
514 return;
515 }
516 /* trivial case */
517 if (http->out.offset != 0) {
518 assert(rep == NULL);
519 /* Avoid copying to MemBuf if we know "rep" is NULL, and we only have a body */
520 http->out.offset += body_size;
521 comm_write(fd, body_data, body_size, clientWriteBodyComplete, context,
522 NULL);
523 /* NULL because its a static buffer */
524 return;
525 } else {
526 MemBuf mb;
527 /* write headers and/or body if any */
528 assert(rep || (body_data && body_size));
529 /* init mb; put status line and headers if any */
530 if (rep) {
531 mb = httpReplyPack(rep);
532 /* http->out.offset += rep->hdr_sz; */
533 #if HEADERS_LOG
534 headersLog(0, 0, http->request->method, rep);
535 #endif
536 httpReplyDestroy(rep);
537 rep = NULL;
538 } else {
539 memBufDefInit(&mb);
540 }
541 if (body_data && body_size) {
542 http->out.offset += body_size;
543 memBufAppend(&mb, body_data, body_size);
544 }
545 /* write */
546 comm_write_mbuf(fd, mb, clientWriteComplete, context);
547 /* if we don't do it, who will? */
548 }
549 }
550
551 /* Called when a downstream node is no longer interested in
552 * our data. As we are a terminal node, this means on aborts
553 * only
554 */
555 void
556 clientSocketDetach(clientStreamNode * node, clientHttpRequest * http)
557 {
558 clientSocketContext *context;
559 /* Test preconditions */
560 assert(node != NULL);
561 /* TODO: handle this rather than asserting - it should only ever happen if we cause an abort and
562 * the callback chain loops back to here, so we can simply return.
563 * However, that itself shouldn't happen, so it stays as an assert for now.
564 */
565 assert(cbdataReferenceValid(node));
566 /* Set null by ContextFree */
567 assert(node->data == NULL);
568 assert(node->node.next == NULL);
569 context = node->data;
570 /* We are only called when the client socket shutsdown.
571 * Tell the prev pipeline member we're finished
572 */
573 clientStreamDetach(node, http);
574 }
575
576 /*
577 * clientWriteBodyComplete is called for MEM_CLIENT_SOCK_BUF's
578 * written directly to the client socket, versus copying to a MemBuf
579 * and going through comm_write_mbuf. Most non-range responses after
580 * the headers probably go through here.
581 */
582 static void
583 clientWriteBodyComplete(int fd, char *buf, size_t size, int errflag, void *data)
584 {
585 /*
586 * NOTE: clientWriteComplete doesn't currently use its "buf"
587 * (second) argument, so we pass in NULL.
588 */
589 clientWriteComplete(fd, NULL, size, errflag, data);
590 }
591
592 static void
593 clientKeepaliveNextRequest(clientSocketContext * context)
594 {
595 clientHttpRequest *http = context->http;
596 ConnStateData *conn = http->conn;
597
598 debug(33, 3) ("clientKeepaliveNextRequest: FD %d\n", conn->fd);
599 conn->defer.until = 0; /* Kick it to read a new request */
600 cbdataFree(context);
601 if ((context = conn->currentobject) == NULL) {
602 debug(33, 5) ("clientKeepaliveNextRequest: FD %d reading next req\n",
603 conn->fd);
604 fd_note(conn->fd, "Waiting for next request");
605 /*
606 * Set the timeout BEFORE calling clientReadRequest().
607 */
608 commSetTimeout(conn->fd, Config.Timeout.persistent_request,
609 requestTimeout, conn);
610 /*
611 * CYGWIN has a problem and is blocking on read() requests when there
612 * is no data present.
613 * This hack may hit performance a little, but it's better than
614 * blocking!.
615 */
616 #ifdef _SQUID_CYGWIN_
617 commSetSelect(conn->fd, COMM_SELECT_READ, clientReadRequest, conn, 0);
618 #else
619 clientReadRequest(conn->fd, conn); /* Read next request */
620 #endif
621 /*
622 * Note, the FD may be closed at this point.
623 */
624 } else {
625 debug(33, 2) ("clientKeepaliveNextRequest: FD %d Sending next\n",
626 conn->fd);
627 /* If the client stream is waiting on a socket write to occur, then */
628 if (context->flags.deferred) {
629 /* NO data is allowed to have been sent */
630 assert(http->out.size == 0);
631 clientSocketRecipient(context->deferredparams.node, http,
632 context->deferredparams.rep,
633 context->deferredparams.body_data,
634 context->deferredparams.body_size);
635 }
636 /* otherwise, the request is still active in a callbacksomewhere,
637 * and we are done
638 */
639 }
640 }
641
642
643 /* A write has just completed to the client, or we have just realised there is
644 * no more data to send.
645 */
646 static void
647 clientWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data)
648 {
649 clientSocketContext *context = data;
650 clientHttpRequest *http = context->http;
651 StoreEntry *entry = http->entry;
652 /* cheating: we are always the tail */
653 clientStreamNode *node = http->client_stream.tail->data;
654 http->out.size += size;
655 debug(33, 5) ("clientWriteComplete: FD %d, sz %ld, err %d, off %ld, len %d\n",
656 fd, (long int) size, errflag, (long int) http->out.size, entry ? objectLen(entry) : 0);
657 if (size > 0 && fd > -1) {
658 kb_incr(&statCounter.client_http.kbytes_out, size);
659 if (isTcpHit(http->logType))
660 kb_incr(&statCounter.client_http.hit_kbytes_out, size);
661 }
662 if (errflag) {
663 /*
664 * just close the socket, httpRequestFree will abort if needed.
665 * errflag is only EVER set by the comms callbacks
666 */
667 assert(fd != -1);
668 comm_close(fd);
669 return;
670 }
671 if (clientHttpRequestStatus(fd, http)) {
672 if (fd != -1)
673 comm_close(fd);
674 /* Do we leak here ? */
675 return;
676 }
677 switch (clientStreamStatus(node, http)) {
678 case STREAM_NONE:
679 /* More data will be coming from the stream. */
680 clientStreamRead(http->client_stream.tail->data, http, http->out.offset,
681 HTTP_REQBUF_SZ, context->reqbuf);
682 break;
683 case STREAM_COMPLETE:
684 debug(33, 5) ("clientWriteComplete: FD %d Keeping Alive\n", fd);
685 clientKeepaliveNextRequest(context);
686 return;
687 case STREAM_UNPLANNED_COMPLETE:
688 /* fallthrough */
689 case STREAM_FAILED:
690 if (fd != -1)
691 comm_close(fd);
692 return;
693 default:
694 fatal("Hit unreachable code in clientWriteComplete\n");
695 }
696 }
697
698 extern CSR clientGetMoreData;
699 extern CSS clientReplyStatus;
700 extern CSD clientReplyDetach;
701
702 static clientSocketContext *
703 parseHttpRequestAbort(ConnStateData * conn, const char *uri)
704 {
705 clientHttpRequest *http;
706 clientSocketContext *context;
707 http = cbdataAlloc(clientHttpRequest);
708 http->conn = conn;
709 http->start = current_time;
710 http->req_sz = conn->in.offset;
711 http->uri = xstrdup(uri);
712 http->log_uri = xstrndup(uri, MAX_URL);
713 context = clientSocketContextNew(http);
714 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
715 clientReplyStatus, clientReplyNewContext(http), clientSocketRecipient,
716 clientSocketDetach, context, context->reqbuf, HTTP_REQBUF_SZ);
717 dlinkAdd(http, &http->active, &ClientActiveRequests);
718 return context;
719 }
720
721 /* Utility function to perform part of request parsing */
722 static clientSocketContext *
723 clientParseHttpRequestLine(char *inbuf, size_t req_sz, ConnStateData * conn,
724 method_t * method_p, char **url_p, http_version_t * http_ver_p)
725 {
726 char *mstr = NULL;
727 char *url = NULL;
728 char *token = NULL;
729 char *t;
730 /* Barf on NULL characters in the headers */
731 if (strlen(inbuf) != req_sz) {
732 debug(33, 1) ("parseHttpRequest: Requestheader contains NULL characters\n");
733 return parseHttpRequestAbort(conn, "error:invalid-request");
734 }
735 /* Look for request method */
736 if ((mstr = strtok(inbuf, "\t ")) == NULL) {
737 debug(33, 1) ("parseHttpRequest: Can't get request method\n");
738 return parseHttpRequestAbort(conn, "error:invalid-request-method");
739 }
740 *method_p = urlParseMethod(mstr);
741 if (*method_p == METHOD_NONE) {
742 debug(33, 1) ("parseHttpRequest: Unsupported method '%s'\n", mstr);
743 return parseHttpRequestAbort(conn, "error:unsupported-request-method");
744 }
745 debug(33, 5) ("parseHttpRequest: Method is '%s'\n", mstr);
746
747 /* look for URL+HTTP/x.x */
748 if ((url = strtok(NULL, "\n")) == NULL) {
749 debug(33, 1) ("parseHttpRequest: Missing URL\n");
750 return parseHttpRequestAbort(conn, "error:missing-url");
751 }
752 while (xisspace(*url))
753 url++;
754 t = url + strlen(url);
755 assert(*t == '\0');
756 while (t > url) {
757 t--;
758 if (xisspace(*t) && !strncmp(t + 1, "HTTP/", 5)) {
759 token = t + 1;
760 break;
761 }
762 }
763 while (t > url && xisspace(*t))
764 *(t--) = '\0';
765 debug(33, 5) ("parseHttpRequest: URI is '%s'\n", url);
766 *url_p = url;
767 if (token == NULL) {
768 debug(33, 3) ("parseHttpRequest: Missing HTTP identifier\n");
769 #if RELAXED_HTTP_PARSER
770 httpBuildVersion(http_ver_p, 0, 9); /* wild guess */
771 #else
772 return parseHttpRequestAbort(conn, "error:missing-http-ident");
773 #endif
774 } else {
775 if (sscanf(token + 5, "%d.%d", &http_ver_p->major,
776 &http_ver_p->minor) != 2) {
777 debug(33, 3) ("parseHttpRequest: Invalid HTTP identifier.\n");
778 return parseHttpRequestAbort(conn, "error: invalid HTTP-ident");
779 }
780 debug(33, 6) ("parseHttpRequest: Client HTTP version %d.%d.\n",
781 http_ver_p->major, http_ver_p->minor);
782 }
783
784 /* everything was ok */
785 return NULL;
786 }
787
788 /*
789 * parseHttpRequest()
790 *
791 * Returns
792 * NULL on error or incomplete request
793 * a clientHttpRequest structure on success
794 */
795 static clientSocketContext *
796 parseHttpRequest(ConnStateData * conn, method_t * method_p, int *status,
797 char **prefix_p, size_t * req_line_sz_p)
798 {
799 char *inbuf = NULL;
800 char *url = NULL;
801 char *req_hdr = NULL;
802 http_version_t http_ver;
803 char *t = NULL;
804 char *end;
805 size_t header_sz; /* size of headers, not including first line */
806 size_t prefix_sz; /* size of whole request (req-line + headers) */
807 size_t url_sz;
808 size_t req_sz;
809 clientHttpRequest *http;
810 clientSocketContext *context;
811 #if IPF_TRANSPARENT
812 struct natlookup natLookup;
813 static int natfd = -1;
814 static int siocgnatl_cmd = SIOCGNATL & 0xff;
815 int x;
816 #endif
817 #if PF_TRANSPARENT
818 struct pfioc_natlook nl;
819 static int pffd = -1;
820 #endif
821 #if LINUX_NETFILTER
822 size_t sock_sz = sizeof(conn->me);
823 #endif
824
825 /* pre-set these values to make aborting simpler */
826 *prefix_p = NULL;
827 *method_p = METHOD_NONE;
828 *status = -1;
829
830 if ((req_sz = headersEnd(conn->in.buf, conn->in.offset)) == 0) {
831 debug(33, 5) ("Incomplete request, waiting for end of headers\n");
832 *status = 0;
833 return NULL;
834 }
835 assert(req_sz <= conn->in.offset);
836 /* Use memcpy, not strdup! */
837 inbuf = xmalloc(req_sz + 1);
838 xmemcpy(inbuf, conn->in.buf, req_sz);
839 *(inbuf + req_sz) = '\0';
840
841 /* Is there a legitimate first line to the headers ? */
842 if ((context =
843 clientParseHttpRequestLine(inbuf, req_sz, conn, method_p, &url,
844 &http_ver))) {
845 /* something wrong, abort */
846 xfree(inbuf);
847 return context;
848 }
849 /*
850 * Process headers after request line
851 */
852 req_hdr = strtok(NULL, null_string);
853 header_sz = req_sz - (req_hdr - inbuf);
854 if (0 == header_sz) {
855 debug(33, 3) ("parseHttpRequest: header_sz == 0\n");
856 *status = 0;
857 xfree(inbuf);
858 return NULL;
859 }
860 assert(header_sz > 0);
861 debug(33, 3) ("parseHttpRequest: req_hdr = {%s}\n", req_hdr);
862 end = req_hdr + header_sz;
863 debug(33, 3) ("parseHttpRequest: end = {%s}\n", end);
864
865 prefix_sz = end - inbuf;
866 *req_line_sz_p = req_hdr - inbuf;
867 debug(33, 3) ("parseHttpRequest: prefix_sz = %d, req_line_sz = %d\n",
868 (int) prefix_sz, (int) *req_line_sz_p);
869 assert(prefix_sz <= conn->in.offset);
870
871 /* Ok, all headers are received */
872 http = cbdataAlloc(clientHttpRequest);
873 http->http_ver = http_ver;
874 http->conn = conn;
875 http->start = current_time;
876 http->req_sz = prefix_sz;
877 context = clientSocketContextNew(http);
878 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
879 clientReplyStatus, clientReplyNewContext(http), clientSocketRecipient,
880 clientSocketDetach, context, context->reqbuf, HTTP_REQBUF_SZ);
881 *prefix_p = xmalloc(prefix_sz + 1);
882 xmemcpy(*prefix_p, conn->in.buf, prefix_sz);
883 *(*prefix_p + prefix_sz) = '\0';
884 dlinkAdd(http, &http->active, &ClientActiveRequests);
885
886 /* XXX this function is still way to long. here is a natural point for further simplification */
887
888 debug(33, 5) ("parseHttpRequest: Request Header is\n%s\n",
889 (*prefix_p) + *req_line_sz_p);
890 #if THIS_VIOLATES_HTTP_SPECS_ON_URL_TRANSFORMATION
891 if ((t = strchr(url, '#'))) /* remove HTML anchors */
892 *t = '\0';
893 #endif
894
895 /* handle internal objects */
896 if (internalCheck(url)) {
897 /* prepend our name & port */
898 http->uri = xstrdup(internalLocalUri(NULL, url));
899 http->flags.internal = 1;
900 http->flags.accel = 1;
901 }
902 /* see if we running in Config2.Accel.on, if so got to convert it to URL */
903 else if (Config2.Accel.on && *url == '/') {
904 /* prepend the accel prefix */
905 if (opt_accel_uses_host && (t = mime_get_header(req_hdr, "Host"))) {
906 int vport;
907 char *q;
908 const char *protocol_name = "http";
909 if (vport_mode)
910 vport = (int) ntohs(http->conn->me.sin_port);
911 else
912 vport = (int) Config.Accel.port;
913 /* If a Host: header was specified, use it to build the URL
914 * instead of the one in the Config file. */
915 /*
916 * XXX Use of the Host: header here opens a potential
917 * security hole. There are no checks that the Host: value
918 * corresponds to one of your servers. It might, for example,
919 * refer to www.playboy.com. The 'dst' and/or 'dst_domain' ACL
920 * types should be used to prevent httpd-accelerators
921 * handling requests for non-local servers */
922 strtok(t, " /;@");
923 if ((q = strchr(t, ':'))) {
924 *q++ = '\0';
925 if (vport_mode)
926 vport = atoi(q);
927 }
928 url_sz = strlen(url) + 32 + Config.appendDomainLen + strlen(t);
929 http->uri = xcalloc(url_sz, 1);
930
931 #if SSL_FORWARDING_NOT_YET_DONE
932 if (Config.Sockaddr.https->s.sin_port == http->conn->me.sin_port) {
933 protocol_name = "https";
934 vport = ntohs(http->conn->me.sin_port);
935 }
936 #endif
937 snprintf(http->uri, url_sz, "%s://%s:%d%s",
938 protocol_name, t, vport, url);
939 } else if (vhost_mode) {
940 int vport;
941 /* Put the local socket IP address as the hostname */
942 url_sz = strlen(url) + 32 + Config.appendDomainLen;
943 http->uri = xcalloc(url_sz, 1);
944 if (vport_mode)
945 vport = (int) ntohs(http->conn->me.sin_port);
946 else
947 vport = (int) Config.Accel.port;
948 #if IPF_TRANSPARENT
949 natLookup.nl_inport = http->conn->me.sin_port;
950 natLookup.nl_outport = http->conn->peer.sin_port;
951 natLookup.nl_inip = http->conn->me.sin_addr;
952 natLookup.nl_outip = http->conn->peer.sin_addr;
953 natLookup.nl_flags = IPN_TCP;
954 if (natfd < 0) {
955 int save_errno;
956 enter_suid();
957 natfd = open(IPL_NAT, O_RDONLY, 0);
958 save_errno = errno;
959 leave_suid();
960 errno = save_errno;
961 }
962 if (natfd < 0) {
963 debug(50, 1) ("parseHttpRequest: NAT open failed: %s\n",
964 xstrerror());
965 cbdataFree(context);
966 xfree(inbuf);
967 return parseHttpRequestAbort(conn, "error:nat-open-failed");
968 }
969 /*
970 * IP-Filter changed the type for SIOCGNATL between
971 * 3.3 and 3.4. It also changed the cmd value for
972 * SIOCGNATL, so at least we can detect it. We could
973 * put something in configure and use ifdefs here, but
974 * this seems simpler.
975 */
976 if (63 == siocgnatl_cmd) {
977 struct natlookup *nlp = &natLookup;
978 x = ioctl(natfd, SIOCGNATL, &nlp);
979 } else {
980 x = ioctl(natfd, SIOCGNATL, &natLookup);
981 }
982 if (x < 0) {
983 if (errno != ESRCH) {
984 debug(50, 1) ("parseHttpRequest: NAT lookup failed: ioctl(SIOCGNATL)\n");
985 close(natfd);
986 natfd = -1;
987 cbdataFree(context);
988 xfree(inbuf);
989 return parseHttpRequestAbort(conn,
990 "error:nat-lookup-failed");
991 } else
992 snprintf(http->uri, url_sz, "http://%s:%d%s",
993 inet_ntoa(http->conn->me.sin_addr), vport, url);
994 } else {
995 if (vport_mode)
996 vport = ntohs(natLookup.nl_realport);
997 snprintf(http->uri, url_sz, "http://%s:%d%s",
998 inet_ntoa(natLookup.nl_realip), vport, url);
999 }
1000 #elif PF_TRANSPARENT
1001 if (pffd < 0)
1002 pffd = open("/dev/pf", O_RDWR);
1003 if (pffd < 0) {
1004 debug(50, 1) ("parseHttpRequest: PF open failed: %s\n",
1005 xstrerror());
1006 cbdataFree(context);
1007 xfree(inbuf);
1008 return parseHttpRequestAbort(conn, "error:pf-open-failed");
1009 }
1010 memset(&nl, 0, sizeof(struct pfioc_natlook));
1011 nl.saddr.v4.s_addr = http->conn->peer.sin_addr.s_addr;
1012 nl.sport = http->conn->peer.sin_port;
1013 nl.daddr.v4.s_addr = http->conn->me.sin_addr.s_addr;
1014 nl.dport = http->conn->me.sin_port;
1015 nl.af = AF_INET;
1016 nl.proto = IPPROTO_TCP;
1017 nl.direction = PF_OUT;
1018 if (ioctl(pffd, DIOCNATLOOK, &nl)) {
1019 if (errno != ENOENT) {
1020 debug(50, 1) ("parseHttpRequest: PF lookup failed: ioctl(DIOCNATLOOK)\n");
1021 close(pffd);
1022 pffd = -1;
1023 cbdataFree(context);
1024 xfree(inbuf);
1025 return parseHttpRequestAbort(conn,
1026 "error:pf-lookup-failed");
1027 } else
1028 snprintf(http->uri, url_sz, "http://%s:%d%s",
1029 inet_ntoa(http->conn->me.sin_addr), vport, url);
1030 } else
1031 snprintf(http->uri, url_sz, "http://%s:%d%s",
1032 inet_ntoa(nl.rdaddr.v4), ntohs(nl.rdport), url);
1033 #else
1034 #if LINUX_NETFILTER
1035 /* If the call fails the address structure will be unchanged */
1036 getsockopt(conn->fd, SOL_IP, SO_ORIGINAL_DST, &conn->me, &sock_sz);
1037 debug(33, 5) ("parseHttpRequest: addr = %s",
1038 inet_ntoa(conn->me.sin_addr));
1039 if (vport_mode)
1040 vport = (int) ntohs(http->conn->me.sin_port);
1041 #endif
1042 snprintf(http->uri, url_sz, "http://%s:%d%s",
1043 inet_ntoa(http->conn->me.sin_addr), vport, url);
1044 #endif
1045 debug(33, 5) ("VHOST REWRITE: '%s'\n", http->uri);
1046 } else {
1047 url_sz = strlen(Config2.Accel.prefix) + strlen(url) +
1048 Config.appendDomainLen + 1;
1049 http->uri = xcalloc(url_sz, 1);
1050 snprintf(http->uri, url_sz, "%s%s", Config2.Accel.prefix, url);
1051 }
1052 http->flags.accel = 1;
1053 } else {
1054 /* URL may be rewritten later, so make extra room */
1055 url_sz = strlen(url) + Config.appendDomainLen + 5;
1056 http->uri = xcalloc(url_sz, 1);
1057 strcpy(http->uri, url);
1058 http->flags.accel = 0;
1059 }
1060 if (!stringHasCntl(http->uri))
1061 http->log_uri = xstrndup(http->uri, MAX_URL);
1062 else
1063 http->log_uri = xstrndup(rfc1738_escape_unescaped(http->uri), MAX_URL);
1064 debug(33, 5) ("parseHttpRequest: Complete request received\n");
1065 xfree(inbuf);
1066 *status = 1;
1067 return context;
1068 }
1069
1070 static int
1071 clientReadDefer(int fdnotused, void *data)
1072 {
1073 ConnStateData *conn = data;
1074 if (conn->body.size_left)
1075 return conn->in.offset >= conn->in.size - 1;
1076 else
1077 return conn->defer.until > squid_curtime;
1078 }
1079
1080 static void
1081 clientReadRequest(int fd, void *data)
1082 {
1083 ConnStateData *conn = data;
1084 int parser_return_code = 0;
1085 request_t *request = NULL;
1086 int size;
1087 method_t method;
1088 char *prefix = NULL;
1089 fde *F = &fd_table[fd];
1090 int len = conn->in.size - conn->in.offset - 1;
1091 clientSocketContext *context;
1092 debug(33, 4) ("clientReadRequest: FD %d: reading request...\n", fd);
1093 commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, conn, 0);
1094 if (len == 0) {
1095 /* Grow the request memory area to accomodate for a large request */
1096 conn->in.buf =
1097 memReallocBuf(conn->in.buf, conn->in.size * 2, &conn->in.size);
1098 debug(33, 2) ("growing request buffer: offset=%ld size=%ld\n",
1099 (long) conn->in.offset, (long) conn->in.size);
1100 len = conn->in.size - conn->in.offset - 1;
1101 }
1102 statCounter.syscalls.sock.reads++;
1103 size = FD_READ_METHOD(fd, conn->in.buf + conn->in.offset, len);
1104 if (size > 0) {
1105 fd_bytes(fd, size, FD_READ);
1106 kb_incr(&statCounter.client_http.kbytes_in, size);
1107 }
1108 /*
1109 * Don't reset the timeout value here. The timeout value will be
1110 * set to Config.Timeout.request by httpAccept() and
1111 * clientWriteComplete(), and should apply to the request as a
1112 * whole, not individual read() calls. Plus, it breaks our
1113 * lame half-close detection
1114 */
1115 if (size > 0) {
1116 conn->in.offset += size;
1117 conn->in.buf[conn->in.offset] = '\0'; /* Terminate the string */
1118 } else if (size == 0) {
1119 if (conn->currentobject == NULL && conn->in.offset == 0) {
1120 /* no current or pending requests */
1121 debug(33, 4) ("clientReadRequest: FD %d closed\n", fd);
1122 comm_close(fd);
1123 return;
1124 } else if (!Config.onoff.half_closed_clients) {
1125 /* admin doesn't want to support half-closed client sockets */
1126 debug(33, 3) ("clientReadRequest: FD %d aborted (half_closed_clients disabled)\n",
1127 fd);
1128 comm_close(fd);
1129 return;
1130 }
1131 /* It might be half-closed, we can't tell */
1132 debug(33, 5) ("clientReadRequest: FD %d closed?\n", fd);
1133 F->flags.socket_eof = 1;
1134 conn->defer.until = squid_curtime + 1;
1135 conn->defer.n++;
1136 fd_note(fd, "half-closed");
1137 /* There is one more close check at the end, to detect aborted
1138 * (partial) requests. At this point we can't tell if the request
1139 * is partial.
1140 */
1141 /* Continue to process previously read data */
1142 } else if (size < 0) {
1143 if (!ignoreErrno(errno)) {
1144 debug(50, 2) ("clientReadRequest: FD %d: %s\n", fd, xstrerror());
1145 comm_close(fd);
1146 return;
1147 } else if (conn->in.offset == 0) {
1148 debug(50, 2) ("clientReadRequest: FD %d: no data to process (%s)\n",
1149 fd, xstrerror());
1150 }
1151 /* Continue to process previously read data */
1152 }
1153 /* Process request body if any */
1154 if (conn->in.offset > 0 && conn->body.callback != NULL)
1155 clientProcessBody(conn);
1156 /* Process next request */
1157 while (conn->in.offset > 0 && conn->body.size_left == 0) {
1158 clientSocketContext **S;
1159 int nrequests;
1160 size_t req_line_sz;
1161 /* Skip leading (and trailing) whitespace */
1162 while (conn->in.offset > 0 && xisspace(conn->in.buf[0])) {
1163 xmemmove(conn->in.buf, conn->in.buf + 1, conn->in.offset - 1);
1164 conn->in.offset--;
1165 }
1166 conn->in.buf[conn->in.offset] = '\0'; /* Terminate the string */
1167 if (conn->in.offset == 0)
1168 break;
1169 /* Limit the number of concurrent requests to 2 */
1170 for (S = (clientSocketContext **) & conn->currentobject, nrequests = 0;
1171 *S; S = &(*S)->next, nrequests++);
1172 if (nrequests >= (Config.onoff.pipeline_prefetch ? 2 : 1)) {
1173 debug(33, 3) ("clientReadRequest: FD %d max concurrent requests reached\n",
1174 fd);
1175 debug(33, 5) ("clientReadRequest: FD %d defering new request until one is done\n",
1176 fd);
1177 conn->defer.until = squid_curtime + 100; /* Reset when a request is complete */
1178 conn->defer.n++;
1179 return;
1180 }
1181 conn->in.buf[conn->in.offset] = '\0'; /* Terminate the string */
1182 if (nrequests == 0)
1183 fd_note(conn->fd, "Reading next request");
1184 /* Process request */
1185 context = parseHttpRequest(conn,
1186 &method, &parser_return_code, &prefix, &req_line_sz);
1187 if (!context)
1188 safe_free(prefix);
1189 if (context) {
1190 clientHttpRequest *http = context->http;
1191 /* We have an initial client stream in place should it be needed */
1192 /* setup our private context */
1193 assert(http->req_sz > 0);
1194 conn->in.offset -= http->req_sz;
1195 assert(conn->in.offset >= 0);
1196 debug(33, 5) ("conn->in.offset = %d\n", (int) conn->in.offset);
1197 /*
1198 * If we read past the end of this request, move the remaining
1199 * data to the beginning
1200 */
1201 if (conn->in.offset > 0)
1202 xmemmove(conn->in.buf, conn->in.buf + http->req_sz,
1203 conn->in.offset);
1204 /* add to the client request queue */
1205 for (S = (clientSocketContext **) & conn->currentobject; *S;
1206 S = &(*S)->next);
1207 *S = context;
1208 conn->nrequests++;
1209 commSetTimeout(fd, Config.Timeout.lifetime, clientLifetimeTimeout,
1210 http);
1211 if (parser_return_code < 0) {
1212 clientStreamNode *node = http->client_stream.tail->prev->data;
1213 debug(33, 1) ("clientReadRequest: FD %d Invalid Request\n", fd);
1214 clientSetReplyToError(node->data,
1215 ERR_INVALID_REQ, HTTP_BAD_REQUEST, method, NULL,
1216 &conn->peer.sin_addr, NULL, conn->in.buf, NULL);
1217 clientStreamRead(http->client_stream.tail->data, http, 0,
1218 HTTP_REQBUF_SZ, context->reqbuf);
1219 safe_free(prefix);
1220 break;
1221 }
1222 if ((request = urlParse(method, http->uri)) == NULL) {
1223 clientStreamNode *node = http->client_stream.tail->prev->data;
1224 debug(33, 5) ("Invalid URL: %s\n", http->uri);
1225 clientSetReplyToError(node->data,
1226 ERR_INVALID_URL, HTTP_BAD_REQUEST, method, http->uri,
1227 &conn->peer.sin_addr, NULL, NULL, NULL);
1228 clientStreamRead(http->client_stream.tail->data, http, 0,
1229 HTTP_REQBUF_SZ, context->reqbuf);
1230 safe_free(prefix);
1231 break;
1232 } else {
1233 /* compile headers */
1234 /* we should skip request line! */
1235 if (!httpRequestParseHeader(request, prefix + req_line_sz))
1236 debug(33, 1) ("Failed to parse request headers: %s\n%s\n",
1237 http->uri, prefix);
1238 /* continue anyway? */
1239 }
1240 request->flags.accelerated = http->flags.accel;
1241 if (!http->flags.internal) {
1242 if (internalCheck(strBuf(request->urlpath))) {
1243 if (internalHostnameIs(request->host) &&
1244 request->port == getMyPort()) {
1245 http->flags.internal = 1;
1246 } else if (internalStaticCheck(strBuf(request->urlpath))) {
1247 xstrncpy(request->host, internalHostname(),
1248 SQUIDHOSTNAMELEN);
1249 request->port = getMyPort();
1250 http->flags.internal = 1;
1251 }
1252 }
1253 }
1254 /*
1255 * cache the Content-length value in request_t.
1256 */
1257 request->content_length = httpHeaderGetInt(&request->header,
1258 HDR_CONTENT_LENGTH);
1259 request->flags.internal = http->flags.internal;
1260 safe_free(prefix);
1261 safe_free(http->log_uri);
1262 http->log_uri = xstrdup(urlCanonicalClean(request));
1263 request->client_addr = conn->peer.sin_addr;
1264 request->my_addr = conn->me.sin_addr;
1265 request->my_port = ntohs(conn->me.sin_port);
1266 request->http_ver = http->http_ver;
1267 if (!urlCheckRequest(request) ||
1268 httpHeaderHas(&request->header, HDR_TRANSFER_ENCODING)) {
1269 clientStreamNode *node = http->client_stream.tail->prev->data;
1270 clientSetReplyToError(node->data, ERR_UNSUP_REQ,
1271 HTTP_NOT_IMPLEMENTED, request->method, NULL,
1272 &conn->peer.sin_addr, request, NULL, NULL);
1273 clientStreamRead(http->client_stream.tail->data, http, 0,
1274 HTTP_REQBUF_SZ, context->reqbuf);
1275 break;
1276 }
1277 if (!clientCheckContentLength(request)) {
1278 clientStreamNode *node = http->client_stream.tail->prev->data;
1279 clientSetReplyToError(node->data, ERR_INVALID_REQ,
1280 HTTP_LENGTH_REQUIRED, request->method, NULL,
1281 &conn->peer.sin_addr, request, NULL, NULL);
1282 clientStreamRead(http->client_stream.tail->data, http, 0,
1283 HTTP_REQBUF_SZ, context->reqbuf);
1284 break;
1285 }
1286 http->request = requestLink(request);
1287 clientSetKeepaliveFlag(http);
1288 /* Do we expect a request-body? */
1289 if (request->content_length > 0) {
1290 conn->body.size_left = request->content_length;
1291 request->body_connection = conn;
1292 /* Is it too large? */
1293 if (clientRequestBodyTooLarge(request->content_length)) {
1294 clientStreamNode *node =
1295 http->client_stream.tail->prev->data;
1296 clientSetReplyToError(node->data, ERR_TOO_BIG,
1297 HTTP_REQUEST_ENTITY_TOO_LARGE, METHOD_NONE, NULL,
1298 &conn->peer.sin_addr, http->request, NULL, NULL);
1299 clientStreamRead(http->client_stream.tail->data, http, 0,
1300 HTTP_REQBUF_SZ, context->reqbuf);
1301 break;
1302 }
1303 }
1304 clientAccessCheck(http);
1305 continue; /* while offset > 0 && body.size_left == 0 */
1306 } else if (parser_return_code == 0) {
1307 /*
1308 * Partial request received; reschedule until parseHttpRequest()
1309 * is happy with the input
1310 */
1311 if (conn->in.offset >= Config.maxRequestHeaderSize) {
1312 /* The request is too large to handle */
1313 clientStreamNode *node;
1314 context =
1315 parseHttpRequestAbort(conn, "error:request-too-large");
1316 node = context->http->client_stream.tail->prev->data;
1317 debug(33, 1) ("Request header is too large (%d bytes)\n",
1318 (int) conn->in.offset);
1319 debug(33, 1) ("Config 'request_header_max_size'= %ld bytes.\n",
1320 (long int) Config.maxRequestHeaderSize);
1321 clientSetReplyToError(node->data, ERR_TOO_BIG,
1322 HTTP_REQUEST_ENTITY_TOO_LARGE, METHOD_NONE, NULL,
1323 &conn->peer.sin_addr, NULL, NULL, NULL);
1324 /* add to the client request queue */
1325 for (S = (clientSocketContext **) & conn->currentobject; *S;
1326 S = &(*S)->next);
1327 *S = context;
1328 clientStreamRead(context->http->client_stream.tail->data,
1329 context->http, 0, HTTP_REQBUF_SZ, context->reqbuf);
1330 return;
1331 }
1332 break;
1333 }
1334 } /* while offset > 0 && conn->body.size_left == 0 */
1335 /* Check if a half-closed connection was aborted in the middle */
1336 if (F->flags.socket_eof) {
1337 if (conn->in.offset != conn->body.size_left) { /* != 0 when no request body */
1338 /* Partial request received. Abort client connection! */
1339 debug(33, 3) ("clientReadRequest: FD %d aborted, partial request\n",
1340 fd);
1341 comm_close(fd);
1342 return;
1343 }
1344 }
1345 }
1346
1347 /* file_read like function, for reading body content */
1348 void
1349 clientReadBody(request_t * request, char *buf, size_t size, CBCB * callback,
1350 void *cbdata)
1351 {
1352 ConnStateData *conn = request->body_connection;
1353 if (!conn) {
1354 debug(33, 5) ("clientReadBody: no body to read, request=%p\n", request);
1355 callback(buf, 0, cbdata); /* Signal end of body */
1356 return;
1357 }
1358 debug(33, 2) ("clientReadBody: start fd=%d body_size=%lu in.offset=%ld cb=%p req=%p\n",
1359 conn->fd, (unsigned long int) conn->body.size_left,
1360 (long int) conn->in.offset, callback, request);
1361 conn->body.callback = callback;
1362 conn->body.cbdata = cbdata;
1363 conn->body.buf = buf;
1364 conn->body.bufsize = size;
1365 conn->body.request = requestLink(request);
1366 clientProcessBody(conn);
1367 }
1368
1369 /* Called by clientReadRequest to process body content */
1370 static void
1371 clientProcessBody(ConnStateData * conn)
1372 {
1373 int size;
1374 char *buf = conn->body.buf;
1375 void *cbdata = conn->body.cbdata;
1376 CBCB *callback = conn->body.callback;
1377 request_t *request = conn->body.request;
1378 /* Note: request is null while eating "aborted" transfers */
1379 debug(33, 2) ("clientProcessBody: start fd=%d body_size=%lu in.offset=%ld cb=%p req=%p\n",
1380 conn->fd, (unsigned long int) conn->body.size_left,
1381 (long int) conn->in.offset, callback, request);
1382 if (conn->in.offset) {
1383 /* Some sanity checks... */
1384 assert(conn->body.size_left > 0);
1385 assert(conn->in.offset > 0);
1386 assert(callback != NULL);
1387 assert(buf != NULL);
1388 /* How much do we have to process? */
1389 size = conn->in.offset;
1390 if (size > conn->body.size_left) /* only process the body part */
1391 size = conn->body.size_left;
1392 if (size > conn->body.bufsize) /* don't copy more than requested */
1393 size = conn->body.bufsize;
1394 xmemcpy(buf, conn->in.buf, size);
1395 conn->body.size_left -= size;
1396 /* Move any remaining data */
1397 conn->in.offset -= size;
1398 if (conn->in.offset > 0)
1399 xmemmove(conn->in.buf, conn->in.buf + size, conn->in.offset);
1400 /* Remove request link if this is the last part of the body, as
1401 * clientReadRequest automatically continues to process next request */
1402 if (conn->body.size_left <= 0 && request != NULL)
1403 request->body_connection = NULL;
1404 /* Remove clientReadBody arguments (the call is completed) */
1405 conn->body.request = NULL;
1406 conn->body.callback = NULL;
1407 conn->body.buf = NULL;
1408 conn->body.bufsize = 0;
1409 /* Remember that we have touched the body, not restartable */
1410 if (request != NULL)
1411 request->flags.body_sent = 1;
1412 /* Invoke callback function */
1413 callback(buf, size, cbdata);
1414 if (request != NULL)
1415 requestUnlink(request); /* Linked in clientReadBody */
1416 debug(33, 2) ("clientProcessBody: end fd=%d size=%d body_size=%lu in.offset=%ld cb=%p req=%p\n",
1417 conn->fd, size, (unsigned long int) conn->body.size_left,
1418 (long int) conn->in.offset, callback, request);
1419 }
1420 }
1421
1422 /* A dummy handler that throws away a request-body */
1423 static char bodyAbortBuf[SQUID_TCP_SO_RCVBUF];
1424 static void
1425 clientReadBodyAbortHandler(char *buf, size_t size, void *data)
1426 {
1427 ConnStateData *conn = (ConnStateData *) data;
1428 debug(33, 2) ("clientReadBodyAbortHandler: fd=%d body_size=%lu in.offset=%ld\n",
1429 conn->fd, (unsigned long int) conn->body.size_left,
1430 (long int) conn->in.offset);
1431 if (size != 0 && conn->body.size_left != 0) {
1432 debug(33, 3) ("clientReadBodyAbortHandler: fd=%d shedule next read\n",
1433 conn->fd);
1434 conn->body.callback = clientReadBodyAbortHandler;
1435 conn->body.buf = bodyAbortBuf;
1436 conn->body.bufsize = sizeof(bodyAbortBuf);
1437 conn->body.cbdata = data;
1438 }
1439 }
1440
1441 /* Abort a body request */
1442 int
1443 clientAbortBody(request_t * request)
1444 {
1445 ConnStateData *conn = request->body_connection;
1446 char *buf;
1447 CBCB *callback;
1448 void *cbdata;
1449 request->body_connection = NULL;
1450 if (!conn || conn->body.size_left <= 0)
1451 return 0; /* No body to abort */
1452 if (conn->body.callback != NULL) {
1453 buf = conn->body.buf;
1454 callback = conn->body.callback;
1455 cbdata = conn->body.cbdata;
1456 assert(request == conn->body.request);
1457 conn->body.buf = NULL;
1458 conn->body.callback = NULL;
1459 conn->body.cbdata = NULL;
1460 conn->body.request = NULL;
1461 callback(buf, -1, cbdata); /* Signal abort to clientReadBody caller */
1462 requestUnlink(request);
1463 }
1464 clientReadBodyAbortHandler(NULL, -1, conn); /* Install abort handler */
1465 /* clientProcessBody() */
1466 return 1; /* Aborted */
1467 }
1468
1469 /* general lifetime handler for HTTP requests */
1470 static void
1471 requestTimeout(int fd, void *data)
1472 {
1473 #if THIS_CONFUSES_PERSISTENT_CONNECTION_AWARE_BROWSERS_AND_USERS
1474 ConnStateData *conn = data;
1475 debug(33, 3) ("requestTimeout: FD %d: lifetime is expired.\n", fd);
1476 if (fd_table[fd].rwstate) {
1477 /*
1478 * Some data has been sent to the client, just close the FD
1479 */
1480 comm_close(fd);
1481 } else if (conn->nrequests) {
1482 /*
1483 * assume its a persistent connection; just close it
1484 */
1485 comm_close(fd);
1486 } else {
1487 /*
1488 * Generate an error
1489 */
1490 clientHttpRequest **H;
1491 clientStreamNode *node;
1492 clientHttpRequest *http =
1493 parseHttpRequestAbort(conn,
1494 "error:Connection%20lifetime%20expired");
1495 node = http->client_stream.tail->prev->data;
1496 clientSetReplyToError(node->data, ERR_LIFETIME_EXP,
1497 HTTP_REQUEST_TIMEOUT, METHOD_NONE, "N/A", &conn->peer.sin_addr,
1498 NULL, NULL, NULL);
1499 /* No requests can be outstanded */
1500 assert(conn->chr == NULL);
1501 /* add to the client request queue */
1502 for (H = &conn->chr; *H; H = &(*H)->next);
1503 *H = http;
1504 clientStreamRead(http->client_stream.tail->data, http, 0,
1505 HTTP_REQBUF_SZ, context->reqbuf);
1506 /*
1507 * if we don't close() here, we still need a timeout handler!
1508 */
1509 commSetTimeout(fd, 30, requestTimeout, conn);
1510 /*
1511 * Aha, but we don't want a read handler!
1512 */
1513 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
1514 }
1515 #else
1516 /*
1517 * Just close the connection to not confuse browsers
1518 * using persistent connections. Some browsers opens
1519 * an connection and then does not use it until much
1520 * later (presumeably because the request triggering
1521 * the open has already been completed on another
1522 * connection)
1523 */
1524 debug(33, 3) ("requestTimeout: FD %d: lifetime is expired.\n", fd);
1525 comm_close(fd);
1526 #endif
1527 }
1528
1529 static void
1530 clientLifetimeTimeout(int fd, void *data)
1531 {
1532 clientHttpRequest *http = data;
1533 ConnStateData *conn = http->conn;
1534 debug(33,
1535 1) ("WARNING: Closing client %s connection due to lifetime timeout\n",
1536 inet_ntoa(conn->peer.sin_addr));
1537 debug(33, 1) ("\t%s\n", http->uri);
1538 comm_close(fd);
1539 }
1540
1541 static int
1542 httpAcceptDefer(int fdunused, void *dataunused)
1543 {
1544 static time_t last_warn = 0;
1545 if (fdNFree() >= RESERVED_FD)
1546 return 0;
1547 if (last_warn + 15 < squid_curtime) {
1548 debug(33, 0) ("WARNING! Your cache is running out of filedescriptors\n");
1549 last_warn = squid_curtime;
1550 }
1551 return 1;
1552 }
1553
1554 /* Handle a new connection on HTTP socket. */
1555 void
1556 httpAccept(int sock, void *data)
1557 {
1558 int *N = &incoming_sockets_accepted;
1559 int fd = -1;
1560 ConnStateData *connState = NULL;
1561 struct sockaddr_in peer;
1562 struct sockaddr_in me;
1563 int max = INCOMING_HTTP_MAX;
1564 #if USE_IDENT
1565 static aclCheck_t identChecklist;
1566 #endif
1567 commSetSelect(sock, COMM_SELECT_READ, httpAccept, NULL, 0);
1568 while (max-- && !httpAcceptDefer(sock, NULL)) {
1569 memset(&peer, '\0', sizeof(struct sockaddr_in));
1570 memset(&me, '\0', sizeof(struct sockaddr_in));
1571 if ((fd = comm_accept(sock, &peer, &me)) < 0) {
1572 if (!ignoreErrno(errno))
1573 debug(50, 1) ("httpAccept: FD %d: accept failure: %s\n",
1574 sock, xstrerror());
1575 break;
1576 }
1577 debug(33, 4) ("httpAccept: FD %d: accepted\n", fd);
1578 connState = cbdataAlloc(ConnStateData);
1579 connState->peer = peer;
1580 connState->log_addr = peer.sin_addr;
1581 connState->log_addr.s_addr &= Config.Addrs.client_netmask.s_addr;
1582 connState->me = me;
1583 connState->fd = fd;
1584 connState->in.buf = memAllocBuf(CLIENT_REQ_BUF_SZ, &connState->in.size);
1585 comm_add_close_handler(fd, connStateFree, connState);
1586 if (Config.onoff.log_fqdn)
1587 fqdncache_gethostbyaddr(peer.sin_addr, FQDN_LOOKUP_IF_MISS);
1588 commSetTimeout(fd, Config.Timeout.request, requestTimeout, connState);
1589 #if USE_IDENT
1590 identChecklist.src_addr = peer.sin_addr;
1591 identChecklist.my_addr = me.sin_addr;
1592 identChecklist.my_port = ntohs(me.sin_port);
1593 if (aclCheckFast(Config.accessList.identLookup, &identChecklist))
1594 identStart(&me, &peer, clientIdentDone, connState);
1595 #endif
1596 commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, connState, 0);
1597 commSetDefer(fd, clientReadDefer, connState);
1598 clientdbEstablished(peer.sin_addr, 1);
1599 assert(N);
1600 (*N)++;
1601 }
1602 }
1603
1604 #if USE_SSL
1605
1606 /* negotiate an SSL connection */
1607 static void
1608 clientNegotiateSSL(int fd, void *data)
1609 {
1610 ConnStateData *conn = data;
1611 X509 *client_cert;
1612 int ret;
1613
1614 if ((ret = SSL_accept(fd_table[fd].ssl)) <= 0) {
1615 if (BIO_sock_should_retry(ret)) {
1616 commSetSelect(fd, COMM_SELECT_READ, clientNegotiateSSL, conn, 0);
1617 return;
1618 }
1619 ret = ERR_get_error();
1620 if (ret) {
1621 debug(83, 1)
1622 ("clientNegotiateSSL: Error negotiating SSL connection on FD %d: %s\n",
1623 fd, ERR_error_string(ret, NULL));
1624 }
1625 comm_close(fd);
1626 return;
1627 }
1628 debug(83, 5) ("clientNegotiateSSL: FD %d negotiated cipher %s\n", fd,
1629 SSL_get_cipher(fd_table[fd].ssl));
1630
1631 client_cert = SSL_get_peer_certificate(fd_table[fd].ssl);
1632 if (client_cert != NULL) {
1633 debug(83, 5) ("clientNegotiateSSL: FD %d client certificate: subject: %s\n",
1634 fd, X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0));
1635
1636 debug(83, 5) ("clientNegotiateSSL: FD %d client certificate: issuer: %s\n",
1637 fd, X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0));
1638
1639 X509_free(client_cert);
1640 } else {
1641 debug(83, 5) ("clientNegotiateSSL: FD %d has no certificate.\n", fd);
1642 }
1643
1644 commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, conn, 0);
1645 }
1646
1647 struct _https_port_data {
1648 SSL_CTX *sslContext;
1649 };
1650 typedef struct _https_port_data https_port_data;
1651 CBDATA_TYPE(https_port_data);
1652
1653 /* handle a new HTTPS connection */
1654 static void
1655 httpsAccept(int sock, void *data)
1656 {
1657 int *N = &incoming_sockets_accepted;
1658 https_port_data *https_port = data;
1659 SSL_CTX *sslContext = https_port->sslContext;
1660 int fd = -1;
1661 ConnStateData *connState = NULL;
1662 struct sockaddr_in peer;
1663 struct sockaddr_in me;
1664 int max = INCOMING_HTTP_MAX;
1665 SSL *ssl;
1666 int ssl_error;
1667 #if USE_IDENT
1668 static aclCheck_t identChecklist;
1669 #endif
1670 commSetSelect(sock, COMM_SELECT_READ, httpsAccept, https_port, 0);
1671 while (max-- && !httpAcceptDefer(sock, NULL)) {
1672 memset(&peer, '\0', sizeof(struct sockaddr_in));
1673 memset(&me, '\0', sizeof(struct sockaddr_in));
1674 if ((fd = comm_accept(sock, &peer, &me)) < 0) {
1675 if (!ignoreErrno(errno))
1676 debug(50, 1) ("httpsAccept: FD %d: accept failure: %s\n",
1677 sock, xstrerror());
1678 break;
1679 }
1680 if ((ssl = SSL_new(sslContext)) == NULL) {
1681 ssl_error = ERR_get_error();
1682 debug(83, 1) ("httpsAccept: Error allocating handle: %s\n",
1683 ERR_error_string(ssl_error, NULL));
1684 break;
1685 }
1686 SSL_set_fd(ssl, fd);
1687 fd_table[fd].ssl = ssl;
1688 fd_table[fd].read_method = &ssl_read_method;
1689 fd_table[fd].write_method = &ssl_write_method;
1690 debug(50, 5) ("httpsAccept: FD %d accepted, starting SSL negotiation.\n", fd);
1691
1692 connState = cbdataAlloc(ConnStateData);
1693 connState->peer = peer;
1694 connState->log_addr = peer.sin_addr;
1695 connState->log_addr.s_addr &= Config.Addrs.client_netmask.s_addr;
1696 connState->me = me;
1697 connState->fd = fd;
1698 connState->in.buf = memAllocBuf(CLIENT_REQ_BUF_SZ, &connState->in.size);
1699 /* XXX account connState->in.buf */
1700 comm_add_close_handler(fd, connStateFree, connState);
1701 if (Config.onoff.log_fqdn)
1702 fqdncache_gethostbyaddr(peer.sin_addr, FQDN_LOOKUP_IF_MISS);
1703 commSetTimeout(fd, Config.Timeout.request, requestTimeout, connState);
1704 #if USE_IDENT
1705 identChecklist.src_addr = peer.sin_addr;
1706 identChecklist.my_addr = me.sin_addr;
1707 identChecklist.my_port = ntohs(me.sin_port);
1708 if (aclCheckFast(Config.accessList.identLookup, &identChecklist))
1709 identStart(&me, &peer, clientIdentDone, connState);
1710 #endif
1711 commSetSelect(fd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
1712 commSetDefer(fd, clientReadDefer, connState);
1713 clientdbEstablished(peer.sin_addr, 1);
1714 (*N)++;
1715 }
1716 }
1717
1718 #endif /* USE_SSL */
1719
1720 /*
1721 * This function is designed to serve a fairly specific purpose.
1722 * Occasionally our vBNS-connected caches can talk to each other, but not
1723 * the rest of the world. Here we try to detect frequent failures which
1724 * make the cache unusable (e.g. DNS lookup and connect() failures). If
1725 * the failure:success ratio goes above 1.0 then we go into "hit only"
1726 * mode where we only return UDP_HIT or UDP_MISS_NOFETCH. Neighbors
1727 * will only fetch HITs from us if they are using the ICP protocol. We
1728 * stay in this mode for 5 minutes.
1729 *
1730 * Duane W., Sept 16, 1996
1731 */
1732
1733 static void
1734 checkFailureRatio(err_type etype, hier_code hcode)
1735 {
1736 static double magic_factor = 100.0;
1737 double n_good;
1738 double n_bad;
1739 if (hcode == HIER_NONE)
1740 return;
1741 n_good = magic_factor / (1.0 + request_failure_ratio);
1742 n_bad = magic_factor - n_good;
1743 switch (etype) {
1744 case ERR_DNS_FAIL:
1745 case ERR_CONNECT_FAIL:
1746 case ERR_READ_ERROR:
1747 n_bad++;
1748 break;
1749 default:
1750 n_good++;
1751 }
1752 request_failure_ratio = n_bad / n_good;
1753 if (hit_only_mode_until > squid_curtime)
1754 return;
1755 if (request_failure_ratio < 1.0)
1756 return;
1757 debug(33, 0) ("Failure Ratio at %4.2f\n", request_failure_ratio);
1758 debug(33, 0) ("Going into hit-only-mode for %d minutes...\n",
1759 FAILURE_MODE_TIME / 60);
1760 hit_only_mode_until = squid_curtime + FAILURE_MODE_TIME;
1761 request_failure_ratio = 0.8; /* reset to something less than 1.0 */
1762 }
1763
1764 static void
1765 clientHttpConnectionsOpen(void)
1766 {
1767 sockaddr_in_list *s;
1768 int fd;
1769 for (s = Config.Sockaddr.http; s; s = s->next) {
1770 if (MAXHTTPPORTS == NHttpSockets) {
1771 debug(1, 1) ("WARNING: You have too many 'http_port' lines.\n");
1772 debug(1, 1) (" The limit is %d\n", MAXHTTPPORTS);
1773 continue;
1774 }
1775 enter_suid();
1776 fd = comm_open(SOCK_STREAM,
1777 0,
1778 s->s.sin_addr,
1779 ntohs(s->s.sin_port), COMM_NONBLOCKING, "HTTP Socket");
1780 leave_suid();
1781 if (fd < 0)
1782 continue;
1783 comm_listen(fd);
1784 commSetSelect(fd, COMM_SELECT_READ, httpAccept, NULL, 0);
1785 /*
1786 * We need to set a defer handler here so that we don't
1787 * peg the CPU with select() when we hit the FD limit.
1788 */
1789 commSetDefer(fd, httpAcceptDefer, NULL);
1790 debug(1, 1) ("Accepting HTTP connections at %s, port %d, FD %d.\n",
1791 inet_ntoa(s->s.sin_addr), (int) ntohs(s->s.sin_port), fd);
1792 HttpSockets[NHttpSockets++] = fd;
1793 }
1794 }
1795
1796 #if USE_SSL
1797 static void
1798 clientHttpsConnectionsOpen(void)
1799 {
1800 https_port_list *s;
1801 https_port_data *https_port;
1802 int fd;
1803 for (s = Config.Sockaddr.https; s; s = s->next) {
1804 if (MAXHTTPPORTS == NHttpSockets) {
1805 debug(1, 1) ("WARNING: You have too many 'https_port' lines.\n");
1806 debug(1, 1) (" The limit is %d\n", MAXHTTPPORTS);
1807 continue;
1808 }
1809 enter_suid();
1810 fd = comm_open(SOCK_STREAM,
1811 0,
1812 s->s.sin_addr,
1813 ntohs(s->s.sin_port), COMM_NONBLOCKING, "HTTPS Socket");
1814 leave_suid();
1815 if (fd < 0)
1816 continue;
1817 CBDATA_INIT_TYPE(https_port_data);
1818 https_port = cbdataAlloc(https_port_data);
1819 https_port->sslContext =
1820 sslCreateContext(s->cert, s->key, s->version, s->cipher,
1821 s->options);
1822 comm_listen(fd);
1823 commSetSelect(fd, COMM_SELECT_READ, httpsAccept, https_port, 0);
1824 commSetDefer(fd, httpAcceptDefer, NULL);
1825 debug(1, 1) ("Accepting HTTPS connections at %s, port %d, FD %d.\n",
1826 inet_ntoa(s->s.sin_addr), (int) ntohs(s->s.sin_port), fd);
1827 HttpSockets[NHttpSockets++] = fd;
1828 }
1829 }
1830
1831 #endif
1832
1833 void
1834 clientOpenListenSockets(void)
1835 {
1836 clientHttpConnectionsOpen();
1837 #if USE_SSL
1838 clientHttpsConnectionsOpen();
1839 #endif
1840 if (NHttpSockets < 1)
1841 fatal("Cannot open HTTP Port");
1842 }
1843
1844 void
1845 clientHttpConnectionsClose(void)
1846 {
1847 int i;
1848 for (i = 0; i < NHttpSockets; i++) {
1849 if (HttpSockets[i] >= 0) {
1850 debug(1, 1) ("FD %d Closing HTTP connection\n", HttpSockets[i]);
1851 comm_close(HttpSockets[i]);
1852 HttpSockets[i] = -1;
1853 }
1854 }
1855 NHttpSockets = 0;
1856 }
1857
1858 int
1859 varyEvaluateMatch(StoreEntry * entry, request_t * request)
1860 {
1861 const char *vary = request->vary_headers;
1862 int has_vary = httpHeaderHas(&entry->mem_obj->reply->header, HDR_VARY);
1863 #if X_ACCELERATOR_VARY
1864 has_vary |=
1865 httpHeaderHas(&entry->mem_obj->reply->header, HDR_X_ACCELERATOR_VARY);
1866 #endif
1867 if (!has_vary || !entry->mem_obj->vary_headers) {
1868 if (vary) {
1869 /* Oops... something odd is going on here.. */
1870 debug(33,
1871 1)
1872 ("varyEvaluateMatch: Oops. Not a Vary object on second attempt, '%s' '%s'\n",
1873 entry->mem_obj->url, vary);
1874 safe_free(request->vary_headers);
1875 return VARY_CANCEL;
1876 }
1877 if (!has_vary) {
1878 /* This is not a varying object */
1879 return VARY_NONE;
1880 }
1881 /* virtual "vary" object found. Calculate the vary key and
1882 * continue the search
1883 */
1884 vary = httpMakeVaryMark(request, entry->mem_obj->reply);
1885 if (vary) {
1886 request->vary_headers = xstrdup(vary);
1887 return VARY_OTHER;
1888 } else {
1889 /* Ouch.. we cannot handle this kind of variance */
1890 /* XXX This cannot really happen, but just to be complete */
1891 return VARY_CANCEL;
1892 }
1893 } else {
1894 if (!vary) {
1895 vary = httpMakeVaryMark(request, entry->mem_obj->reply);
1896 if (vary)
1897 request->vary_headers = xstrdup(vary);
1898 }
1899 if (!vary) {
1900 /* Ouch.. we cannot handle this kind of variance */
1901 /* XXX This cannot really happen, but just to be complete */
1902 return VARY_CANCEL;
1903 } else if (strcmp(vary, entry->mem_obj->vary_headers) == 0) {
1904 return VARY_MATCH;
1905 } else {
1906 /* Oops.. we have already been here and still haven't
1907 * found the requested variant. Bail out
1908 */
1909 debug(33, 1) ("varyEvaluateMatch: Oops. Not a Vary match on second attempt, '%s' '%s'\n",
1910 entry->mem_obj->url, vary);
1911 return VARY_CANCEL;
1912 }
1913 }
1914 }