]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side.cc
fix headers to allow inclusion into C++ source
[thirdparty/squid.git] / src / client_side.cc
CommitLineData
3c66d057 1
dd11e0b7 2/*
29b8d8d6 3 * $Id: client_side.cc,v 1.592 2002/09/15 06:40:57 robertc Exp $
dd11e0b7 4 *
5 * DEBUG: section 33 Client-side Routines
6 * AUTHOR: Duane Wessels
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
dd11e0b7 10 *
2b6662ba 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.
dd11e0b7 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
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
dd11e0b7 34 */
f88bb09c 35
edce4d98 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
f88bb09c 58#include "squid.h"
59
5cafc1d6 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>
42b51993 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
5cafc1d6 71#include <ip_compat.h>
9bc73deb 72#elif HAVE_NETINET_IP_COMPAT_H
73#include <netinet/ip_compat.h>
74#endif
75#if HAVE_IP_FIL_H
5cafc1d6 76#include <ip_fil.h>
9bc73deb 77#elif HAVE_NETINET_IP_FIL_H
78#include <netinet/ip_fil.h>
79#endif
80#if HAVE_IP_NAT_H
5cafc1d6 81#include <ip_nat.h>
9bc73deb 82#elif HAVE_NETINET_IP_NAT_H
83#include <netinet/ip_nat.h>
84#endif
5cafc1d6 85#endif
86
2b0dd4ac 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
d852fbad 97#if LINUX_NETFILTER
98#include <linux/netfilter_ipv4.h>
99#endif
5cafc1d6 100
101
5492ad1d 102#if LINGERING_CLOSE
103#define comm_close comm_lingering_close
104#endif
105
7a2f978b 106static const char *const crlf = "\r\n";
107
7a2f978b 108#define FAILURE_MODE_TIME 300
109
edce4d98 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 */
128typedef 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
144CBDATA_TYPE(clientSocketContext);
7a2f978b 145
edce4d98 146/* Local functions */
147/* clientSocketContext */
148static FREE clientSocketContextFree;
149static clientSocketContext *clientSocketContextNew(clientHttpRequest *);
150/* other */
fc5d6f7f 151static CWCB clientWriteComplete;
f4f278b5 152static CWCB clientWriteBodyComplete;
7a2f978b 153static PF clientReadRequest;
154static PF connStateFree;
155static PF requestTimeout;
b5c39993 156static PF clientLifetimeTimeout;
88aad2e5 157static void checkFailureRatio(err_type, hier_code);
edce4d98 158static clientSocketContext *parseHttpRequestAbort(ConnStateData * conn,
159 const char *uri);
160static clientSocketContext *parseHttpRequest(ConnStateData *, method_t *, int *,
161 char **, size_t *);
3898f57f 162#if USE_IDENT
05832ae1 163static IDCB clientIdentDone;
3898f57f 164#endif
edce4d98 165static CSCB clientSocketRecipient;
166static CSD clientSocketDetach;
c68e9c6b 167static void clientSetKeepaliveFlag(clientHttpRequest *);
efb9218c 168static int clientCheckContentLength(request_t * r);
d20b1cd0 169static DEFER httpAcceptDefer;
efd900cb 170static int clientRequestBodyTooLarge(int clen);
94439e4e 171static void clientProcessBody(ConnStateData * conn);
ea6f43cd 172
b8d8561b 173void
edce4d98 174clientSocketContextFree(void *data)
f88bb09c 175{
edce4d98 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;
1cfdbcf0 196 }
edce4d98 197 assert(*S != NULL);
198 *S = context->next;
199 context->next = NULL;
f88bb09c 200 }
201}
202
edce4d98 203clientSocketContext *
204clientSocketContextNew(clientHttpRequest * http)
f88bb09c 205{
edce4d98 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;
4d55827a 212}
213
edce4d98 214#if USE_IDENT
4d55827a 215static void
edce4d98 216clientIdentDone(const char *ident, void *data)
4d55827a 217{
edce4d98 218 ConnStateData *conn = data;
219 xstrncpy(conn->rfc931, ident ? ident : dash_str, USER_IDENT_SZ);
e81957b7 220}
221
447e176b 222#endif
7a2f978b 223
2d72d4fd 224static void
0e473d70 225clientUpdateCounters(clientHttpRequest * http)
a7c05555 226{
ee1679df 227 int svc_time = tvSubMsec(http->start, current_time);
b4e7f82d 228 ping_data *i;
39edba21 229 HierarchyLogEntry *H;
83704487 230 statCounter.client_http.requests++;
29b8d8d6 231 if (isTcpHit(http->logType))
83704487 232 statCounter.client_http.hits++;
29b8d8d6 233 if (http->logType == LOG_TCP_HIT)
83704487 234 statCounter.client_http.disk_hits++;
29b8d8d6 235 else if (http->logType == LOG_TCP_MEM_HIT)
83704487 236 statCounter.client_http.mem_hits++;
29b8d8d6 237 if (http->request->errType != ERR_NONE)
83704487 238 statCounter.client_http.errors++;
239 statHistCount(&statCounter.client_http.all_svc_time, svc_time);
ee1679df 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 */
29b8d8d6 246 switch (http->logType) {
7c9c84ad 247 case LOG_TCP_REFRESH_HIT:
83704487 248 statHistCount(&statCounter.client_http.nh_svc_time, svc_time);
7c9c84ad 249 break;
ee1679df 250 case LOG_TCP_IMS_HIT:
83704487 251 statHistCount(&statCounter.client_http.nm_svc_time, svc_time);
ee1679df 252 break;
253 case LOG_TCP_HIT:
254 case LOG_TCP_MEM_HIT:
b540e168 255 case LOG_TCP_OFFLINE_HIT:
83704487 256 statHistCount(&statCounter.client_http.hit_svc_time, svc_time);
ee1679df 257 break;
258 case LOG_TCP_MISS:
259 case LOG_TCP_CLIENT_REFRESH_MISS:
83704487 260 statHistCount(&statCounter.client_http.miss_svc_time, svc_time);
ee1679df 261 break;
262 default:
263 /* make compiler warnings go away */
264 break;
265 }
39edba21 266 H = &http->request->hier;
69c95dd3 267 switch (H->alg) {
268 case PEER_SA_DIGEST:
83704487 269 statCounter.cd.times_used++;
69c95dd3 270 break;
271 case PEER_SA_ICP:
83704487 272 statCounter.icp.times_used++;
b4e7f82d 273 i = &H->ping;
69c95dd3 274 if (0 != i->stop.tv_sec && 0 != i->start.tv_sec)
83704487 275 statHistCount(&statCounter.icp.query_svc_time,
69c95dd3 276 tvSubUsec(i->start, i->stop));
277 if (i->timeout)
83704487 278 statCounter.icp.query_timeouts++;
69c95dd3 279 break;
280 case PEER_SA_NETDB:
83704487 281 statCounter.netdb.times_used++;
69c95dd3 282 break;
283 default:
284 break;
17b6e784 285 }
a7c05555 286}
287
edce4d98 288void
7a2f978b 289httpRequestFree(void *data)
290{
291 clientHttpRequest *http = data;
edce4d98 292 ConnStateData *conn;
293 request_t *request = NULL;
7a2f978b 294 MemObject *mem = NULL;
edce4d98 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 */
1b02b5be 300 if (!clientCheckTransferDone(http)) {
94439e4e 301 if (request && request->body_connection)
302 clientAbortBody(request); /* abort body transter */
edce4d98 303 /* the ICP check here was erroneous - storeReleaseRequest was always called if entry was valid
bbf5ef83 304 */
7a2f978b 305 }
29b8d8d6 306 assert(http->logType < LOG_TYPE_MAX);
b6a2f15e 307 if (http->entry)
308 mem = http->entry->mem_obj;
29b8d8d6 309 if (http->out.size || http->logType) {
728da2ee 310 http->al.icp.opcode = ICP_INVALID;
ef65d6ca 311 http->al.url = http->log_uri;
312 debug(33, 9) ("httpRequestFree: al.url='%s'\n", http->al.url);
7a2f978b 313 if (mem) {
cb69b4c7 314 http->al.http.code = mem->reply->sline.status;
038eb4ed 315 http->al.http.content_type = strBuf(mem->reply->content_type);
7a2f978b 316 }
edce4d98 317 http->al.cache.caddr = conn ? conn->log_addr : no_addr;
7a2f978b 318 http->al.cache.size = http->out.size;
29b8d8d6 319 http->al.cache.code = http->logType;
7a2f978b 320 http->al.cache.msec = tvSubMsec(http->start, current_time);
7a2f978b 321 if (request) {
2246b732 322 Packer p;
323 MemBuf mb;
324 memBufDefInit(&mb);
325 packerToMemInit(&p, &mb);
326 httpHeaderPackInto(&request->header, &p);
7a2f978b 327 http->al.http.method = request->method;
c68e9c6b 328 http->al.http.version = request->http_ver;
2246b732 329 http->al.headers.request = xstrdup(mb.buf);
7a2f978b 330 http->al.hier = request->hier;
94439e4e 331 if (request->auth_user_request) {
edce4d98 332 http->al.cache.authuser =
333 xstrdup(authenticateUserRequestUsername(request->
334 auth_user_request));
94439e4e 335 authenticateAuthUserRequestUnlock(request->auth_user_request);
336 request->auth_user_request = NULL;
337 }
edce4d98 338 if (conn && conn->rfc931[0])
94439e4e 339 http->al.cache.rfc931 = conn->rfc931;
2246b732 340 packerClean(&p);
341 memBufClean(&mb);
7a2f978b 342 }
343 accessLogLog(&http->al);
a7c05555 344 clientUpdateCounters(http);
edce4d98 345 if (conn)
29b8d8d6 346 clientdbUpdate(conn->peer.sin_addr, http->logType, PROTO_HTTP,
edce4d98 347 http->out.size);
7a2f978b 348 }
88aad2e5 349 if (request)
29b8d8d6 350 checkFailureRatio(request->errType, http->al.hier.code);
23d92c64 351 safe_free(http->uri);
352 safe_free(http->log_uri);
2246b732 353 safe_free(http->al.headers.request);
7a2f978b 354 safe_free(http->al.headers.reply);
b31ff3d7 355 safe_free(http->al.cache.authuser);
6d38ef86 356 safe_free(http->redirect.location);
7a2f978b 357 requestUnlink(http->request);
edce4d98 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 */
0f1bc304 361 dlinkDelete(&http->active, &ClientActiveRequests);
7a2f978b 362 cbdataFree(http);
363}
364
365/* This is a handler normally called by comm_close() */
366static void
367connStateFree(int fd, void *data)
368{
369 ConnStateData *connState = data;
edce4d98 370 clientSocketContext *context;
93f9abd0 371 debug(33, 3) ("connStateFree: FD %d\n", fd);
7a2f978b 372 assert(connState != NULL);
9bc73deb 373 clientdbEstablished(connState->peer.sin_addr, -1); /* decrement */
edce4d98 374 while ((context = connState->currentobject) != NULL) {
375 assert(context->http->conn == connState);
376 assert(connState->currentobject !=
377 ((clientSocketContext *) connState->currentobject)->next);
378 cbdataFree(context);
7a2f978b 379 }
5d146f7d 380 if (connState->auth_user_request)
381 authenticateAuthUserRequestUnlock(connState->auth_user_request);
382 connState->auth_user_request = NULL;
383 authenticateOnCloseConnection(connState);
1eb41ae8 384 memFreeBuf(connState->in.size, connState->in.buf);
7a2f978b 385 pconnHistCount(0, connState->nrequests);
386 cbdataFree(connState);
403028a9 387#ifdef _SQUID_LINUX_
388 /* prevent those nasty RST packets */
389 {
390 char buf[SQUID_TCP_SO_RCVBUF];
1f7c9178 391 while (FD_READ_METHOD(fd, buf, SQUID_TCP_SO_RCVBUF) > 0);
403028a9 392 }
393#endif
7a2f978b 394}
395
c68e9c6b 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 */
402static void
403clientSetKeepaliveFlag(clientHttpRequest * http)
404{
405 request_t *request = http->request;
406 const HttpHeader *req_hdr = &request->header;
f6329bc3 407
ccf44862 408 debug(33, 3) ("clientSetKeepaliveFlag: http_ver = %d.%d\n",
409 request->http_ver.major, request->http_ver.minor);
c68e9c6b 410 debug(33, 3) ("clientSetKeepaliveFlag: method = %s\n",
411 RequestMethodStr[request->method]);
efd900cb 412 if (!Config.onoff.client_pconns)
413 request->flags.proxy_keepalive = 0;
f6329bc3 414 else {
415 http_version_t http_ver;
edce4d98 416 httpBuildVersion(&http_ver, 1, 0);
417 /* we are HTTP/1.0, no matter what the client requests... */
f6329bc3 418 if (httpMsgIsPersistent(http_ver, req_hdr))
419 request->flags.proxy_keepalive = 1;
420 }
c68e9c6b 421}
422
31be8b80 423static int
efb9218c 424clientCheckContentLength(request_t * r)
31be8b80 425{
ffc128c4 426 switch (r->method) {
427 case METHOD_PUT:
428 case METHOD_POST:
429 /* PUT/POST requires a request entity */
2a6b906c 430 return (r->content_length >= 0);
ffc128c4 431 case METHOD_GET:
432 case METHOD_HEAD:
433 /* We do not want to see a request entity on GET/HEAD requests */
2a6b906c 434 return (r->content_length <= 0);
ffc128c4 435 default:
436 /* For other types of requests we don't care */
0cdcddb9 437 return 1;
ffc128c4 438 }
439 /* NOT REACHED */
31be8b80 440}
441
cf50a0af 442int
7a2f978b 443isTcpHit(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;
b540e168 458 if (code == LOG_TCP_OFFLINE_HIT)
459 return 1;
7a2f978b 460 return 0;
461}
462
efd900cb 463static int
464clientRequestBodyTooLarge(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
2246b732 475/*
edce4d98 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.
2246b732 482 */
edce4d98 483static void
484clientSocketRecipient(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);
b5443c04 519 /* Avoid copying to MemBuf if we know "rep" is NULL, and we only have a body */
520 http->out.offset += body_size;
edce4d98 521 comm_write(fd, body_data, body_size, clientWriteBodyComplete, context,
522 NULL);
523 /* NULL because its a static buffer */
b5443c04 524 return;
edce4d98 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 */
9f086470 530 if (rep) {
edce4d98 531 mb = httpReplyPack(rep);
532/* http->out.offset += rep->hdr_sz; */
c3609322 533#if HEADERS_LOG
edce4d98 534 headersLog(0, 0, http->request->method, rep);
c3609322 535#endif
edce4d98 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? */
2246b732 548 }
edce4d98 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 */
555void
556clientSocketDetach(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);
7a2f978b 574}
575
f4f278b5 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 */
582static void
583clientWriteBodyComplete(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);
f4f278b5 590}
591
21f2031d 592static void
edce4d98 593clientKeepaliveNextRequest(clientSocketContext * context)
1a92a1e2 594{
edce4d98 595 clientHttpRequest *http = context->http;
1a92a1e2 596 ConnStateData *conn = http->conn;
bd4e6ec8 597
ebb6e9a0 598 debug(33, 3) ("clientKeepaliveNextRequest: FD %d\n", conn->fd);
1a92a1e2 599 conn->defer.until = 0; /* Kick it to read a new request */
edce4d98 600 cbdataFree(context);
601 if ((context = conn->currentobject) == NULL) {
21f2031d 602 debug(33, 5) ("clientKeepaliveNextRequest: FD %d reading next req\n",
603 conn->fd);
94439e4e 604 fd_note(conn->fd, "Waiting for next request");
21f2031d 605 /*
606 * Set the timeout BEFORE calling clientReadRequest().
607 */
edce4d98 608 commSetTimeout(conn->fd, Config.Timeout.persistent_request,
609 requestTimeout, conn);
7f6ffd15 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 */
b05490a8 616#ifdef _SQUID_CYGWIN_
7f6ffd15 617 commSetSelect(conn->fd, COMM_SELECT_READ, clientReadRequest, conn, 0);
b05490a8 618#else
21f2031d 619 clientReadRequest(conn->fd, conn); /* Read next request */
b05490a8 620#endif
21f2031d 621 /*
622 * Note, the FD may be closed at this point.
623 */
21f2031d 624 } else {
6711d505 625 debug(33, 2) ("clientKeepaliveNextRequest: FD %d Sending next\n",
1a92a1e2 626 conn->fd);
edce4d98 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);
1a92a1e2 635 }
edce4d98 636 /* otherwise, the request is still active in a callbacksomewhere,
637 * and we are done
638 */
1a92a1e2 639 }
640}
641
edce4d98 642
643/* A write has just completed to the client, or we have just realised there is
644 * no more data to send.
645 */
fc5d6f7f 646static void
79a15e0a 647clientWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data)
7a2f978b 648{
edce4d98 649 clientSocketContext *context = data;
650 clientHttpRequest *http = context->http;
7a2f978b 651 StoreEntry *entry = http->entry;
edce4d98 652 /* cheating: we are always the tail */
653 clientStreamNode *node = http->client_stream.tail->data;
7a2f978b 654 http->out.size += size;
32754419 655 debug(33, 5) ("clientWriteComplete: FD %d, sz %ld, err %d, off %ld, len %d\n",
edce4d98 656 fd, (long int) size, errflag, (long int) http->out.size, entry ? objectLen(entry) : 0);
657 if (size > 0 && fd > -1) {
83704487 658 kb_incr(&statCounter.client_http.kbytes_out, size);
29b8d8d6 659 if (isTcpHit(http->logType))
83704487 660 kb_incr(&statCounter.client_http.hit_kbytes_out, size);
d8b83480 661 }
7a2f978b 662 if (errflag) {
e55650e3 663 /*
edce4d98 664 * just close the socket, httpRequestFree will abort if needed.
665 * errflag is only EVER set by the comms callbacks
e55650e3 666 */
edce4d98 667 assert(fd != -1);
7a2f978b 668 comm_close(fd);
edce4d98 669 return;
670 }
671 if (clientHttpRequestStatus(fd, http)) {
672 if (fd != -1)
7a2f978b 673 comm_close(fd);
edce4d98 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)
7a2f978b 691 comm_close(fd);
edce4d98 692 return;
693 default:
694 fatal("Hit unreachable code in clientWriteComplete\n");
7a2f978b 695 }
696}
697
edce4d98 698extern CSR clientGetMoreData;
699extern CSS clientReplyStatus;
700extern CSD clientReplyDetach;
701
702static clientSocketContext *
703parseHttpRequestAbort(ConnStateData * conn, const char *uri)
038eb4ed 704{
edce4d98 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;
038eb4ed 719}
720
edce4d98 721/* Utility function to perform part of request parsing */
722static clientSocketContext *
723clientParseHttpRequestLine(char *inbuf, size_t req_sz, ConnStateData * conn,
724 method_t * method_p, char **url_p, http_version_t * http_ver_p)
50ddd7a4 725{
edce4d98 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");
1b95ce49 734 }
edce4d98 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");
7ddc902f 739 }
edce4d98 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");
50ddd7a4 744 }
edce4d98 745 debug(33, 5) ("parseHttpRequest: Method is '%s'\n", mstr);
50ddd7a4 746
edce4d98 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");
7a2f978b 751 }
edce4d98 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;
d15acfd8 761 }
edce4d98 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");
2b906e48 773#endif
49d3fcb0 774 } else {
edce4d98 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");
d20b1cd0 779 }
edce4d98 780 debug(33, 6) ("parseHttpRequest: Client HTTP version %d.%d.\n",
781 http_ver_p->major, http_ver_p->minor);
6d38ef86 782 }
7a2f978b 783
edce4d98 784 /* everything was ok */
785 return NULL;
99edd1c3 786}
787
7a2f978b 788/*
789 * parseHttpRequest()
790 *
791 * Returns
792 * NULL on error or incomplete request
793 * a clientHttpRequest structure on success
794 */
edce4d98 795static clientSocketContext *
7a2f978b 796parseHttpRequest(ConnStateData * conn, method_t * method_p, int *status,
ea285003 797 char **prefix_p, size_t * req_line_sz_p)
7a2f978b 798{
799 char *inbuf = NULL;
7a2f978b 800 char *url = NULL;
801 char *req_hdr = NULL;
ccf44862 802 http_version_t http_ver;
7a2f978b 803 char *t = NULL;
2334c194 804 char *end;
7a2f978b 805 size_t header_sz; /* size of headers, not including first line */
ea285003 806 size_t prefix_sz; /* size of whole request (req-line + headers) */
7a2f978b 807 size_t url_sz;
c68e9c6b 808 size_t req_sz;
edce4d98 809 clientHttpRequest *http;
810 clientSocketContext *context;
5cafc1d6 811#if IPF_TRANSPARENT
812 struct natlookup natLookup;
813 static int natfd = -1;
6efce75c 814 static int siocgnatl_cmd = SIOCGNATL & 0xff;
815 int x;
5cafc1d6 816#endif
2b0dd4ac 817#if PF_TRANSPARENT
818 struct pfioc_natlook nl;
819 static int pffd = -1;
820#endif
d852fbad 821#if LINUX_NETFILTER
822 size_t sock_sz = sizeof(conn->me);
823#endif
7a2f978b 824
6792f0d3 825 /* pre-set these values to make aborting simpler */
826 *prefix_p = NULL;
827 *method_p = METHOD_NONE;
828 *status = -1;
829
c68e9c6b 830 if ((req_sz = headersEnd(conn->in.buf, conn->in.offset)) == 0) {
831 debug(33, 5) ("Incomplete request, waiting for end of headers\n");
7a2f978b 832 *status = 0;
833 return NULL;
834 }
c68e9c6b 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';
7a2f978b 840
edce4d98 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 */
c797472e 846 xfree(inbuf);
edce4d98 847 return context;
99edd1c3 848 }
c68e9c6b 849 /*
850 * Process headers after request line
851 */
852 req_hdr = strtok(NULL, null_string);
853 header_sz = req_sz - (req_hdr - inbuf);
2334c194 854 if (0 == header_sz) {
1a92a1e2 855 debug(33, 3) ("parseHttpRequest: header_sz == 0\n");
7a2f978b 856 *status = 0;
c797472e 857 xfree(inbuf);
7a2f978b 858 return NULL;
859 }
754b9ce9 860 assert(header_sz > 0);
861 debug(33, 3) ("parseHttpRequest: req_hdr = {%s}\n", req_hdr);
2334c194 862 end = req_hdr + header_sz;
04990e2d 863 debug(33, 3) ("parseHttpRequest: end = {%s}\n", end);
99edd1c3 864
99edd1c3 865 prefix_sz = end - inbuf;
866 *req_line_sz_p = req_hdr - inbuf;
ea285003 867 debug(33, 3) ("parseHttpRequest: prefix_sz = %d, req_line_sz = %d\n",
99edd1c3 868 (int) prefix_sz, (int) *req_line_sz_p);
869 assert(prefix_sz <= conn->in.offset);
7a2f978b 870
871 /* Ok, all headers are received */
72711e31 872 http = cbdataAlloc(clientHttpRequest);
7a2f978b 873 http->http_ver = http_ver;
874 http->conn = conn;
875 http->start = current_time;
99edd1c3 876 http->req_sz = prefix_sz;
edce4d98 877 context = clientSocketContextNew(http);
878 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
879 clientReplyStatus, clientReplyNewContext(http), clientSocketRecipient,
880 clientSocketDetach, context, context->reqbuf, HTTP_REQBUF_SZ);
99edd1c3 881 *prefix_p = xmalloc(prefix_sz + 1);
882 xmemcpy(*prefix_p, conn->in.buf, prefix_sz);
883 *(*prefix_p + prefix_sz) = '\0';
0f1bc304 884 dlinkAdd(http, &http->active, &ClientActiveRequests);
7a2f978b 885
edce4d98 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);
ba9ebd0a 890#if THIS_VIOLATES_HTTP_SPECS_ON_URL_TRANSFORMATION
7a2f978b 891 if ((t = strchr(url, '#'))) /* remove HTML anchors */
892 *t = '\0';
ba9ebd0a 893#endif
7a2f978b 894
4162ee3b 895 /* handle internal objects */
1da5651f 896 if (internalCheck(url)) {
4162ee3b 897 /* prepend our name & port */
1da5651f 898 http->uri = xstrdup(internalLocalUri(NULL, url));
77ed547a 899 http->flags.internal = 1;
c68e9c6b 900 http->flags.accel = 1;
4162ee3b 901 }
7a2f978b 902 /* see if we running in Config2.Accel.on, if so got to convert it to URL */
4162ee3b 903 else if (Config2.Accel.on && *url == '/') {
7a2f978b 904 /* prepend the accel prefix */
dbfed404 905 if (opt_accel_uses_host && (t = mime_get_header(req_hdr, "Host"))) {
40457767 906 int vport;
42b51993 907 char *q;
a2c963ae 908 const char *protocol_name = "http";
40457767 909 if (vport_mode)
910 vport = (int) ntohs(http->conn->me.sin_port);
911 else
912 vport = (int) Config.Accel.port;
7a2f978b 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 */
42b51993 922 strtok(t, " /;@");
923 if ((q = strchr(t, ':'))) {
924 *q++ = '\0';
40457767 925 if (vport_mode)
926 vport = atoi(q);
42b51993 927 }
edce4d98 928 url_sz = strlen(url) + 32 + Config.appendDomainLen + strlen(t);
23d92c64 929 http->uri = xcalloc(url_sz, 1);
1f7c9178 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);
dbfed404 939 } else if (vhost_mode) {
42b51993 940 int vport;
dbfed404 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);
42b51993 944 if (vport_mode)
945 vport = (int) ntohs(http->conn->me.sin_port);
946 else
947 vport = (int) Config.Accel.port;
5cafc1d6 948#if IPF_TRANSPARENT
135171fe 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;
68108ec5 954 if (natfd < 0) {
955 int save_errno;
956 enter_suid();
135171fe 957 natfd = open(IPL_NAT, O_RDONLY, 0);
68108ec5 958 save_errno = errno;
959 leave_suid();
960 errno = save_errno;
961 }
135171fe 962 if (natfd < 0) {
963 debug(50, 1) ("parseHttpRequest: NAT open failed: %s\n",
964 xstrerror());
edce4d98 965 cbdataFree(context);
c797472e 966 xfree(inbuf);
ac18f51a 967 return parseHttpRequestAbort(conn, "error:nat-open-failed");
5cafc1d6 968 }
6efce75c 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) {
ac18f51a 983 if (errno != ESRCH) {
984 debug(50, 1) ("parseHttpRequest: NAT lookup failed: ioctl(SIOCGNATL)\n");
985 close(natfd);
986 natfd = -1;
edce4d98 987 cbdataFree(context);
c797472e 988 xfree(inbuf);
edce4d98 989 return parseHttpRequestAbort(conn,
990 "error:nat-lookup-failed");
ac18f51a 991 } else
992 snprintf(http->uri, url_sz, "http://%s:%d%s",
edce4d98 993 inet_ntoa(http->conn->me.sin_addr), vport, url);
52ee8bdd 994 } else {
995 if (vport_mode)
add2192d 996 vport = ntohs(natLookup.nl_realport);
52ee8bdd 997 snprintf(http->uri, url_sz, "http://%s:%d%s",
edce4d98 998 inet_ntoa(natLookup.nl_realip), vport, url);
52ee8bdd 999 }
2b0dd4ac 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());
edce4d98 1006 cbdataFree(context);
1007 xfree(inbuf);
2b0dd4ac 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;
edce4d98 1023 cbdataFree(context);
1024 xfree(inbuf);
1025 return parseHttpRequestAbort(conn,
1026 "error:pf-lookup-failed");
2b0dd4ac 1027 } else
1028 snprintf(http->uri, url_sz, "http://%s:%d%s",
edce4d98 1029 inet_ntoa(http->conn->me.sin_addr), vport, url);
2b0dd4ac 1030 } else
1031 snprintf(http->uri, url_sz, "http://%s:%d%s",
edce4d98 1032 inet_ntoa(nl.rdaddr.v4), ntohs(nl.rdport), url);
5cafc1d6 1033#else
d852fbad 1034#if LINUX_NETFILTER
f0debecb 1035 /* If the call fails the address structure will be unchanged */
1036 getsockopt(conn->fd, SOL_IP, SO_ORIGINAL_DST, &conn->me, &sock_sz);
edce4d98 1037 debug(33, 5) ("parseHttpRequest: addr = %s",
1038 inet_ntoa(conn->me.sin_addr));
40457767 1039 if (vport_mode)
1040 vport = (int) ntohs(http->conn->me.sin_port);
d852fbad 1041#endif
dbfed404 1042 snprintf(http->uri, url_sz, "http://%s:%d%s",
edce4d98 1043 inet_ntoa(http->conn->me.sin_addr), vport, url);
5cafc1d6 1044#endif
93f9abd0 1045 debug(33, 5) ("VHOST REWRITE: '%s'\n", http->uri);
7a2f978b 1046 } else {
1047 url_sz = strlen(Config2.Accel.prefix) + strlen(url) +
1048 Config.appendDomainLen + 1;
23d92c64 1049 http->uri = xcalloc(url_sz, 1);
1050 snprintf(http->uri, url_sz, "%s%s", Config2.Accel.prefix, url);
7a2f978b 1051 }
77ed547a 1052 http->flags.accel = 1;
7a2f978b 1053 } else {
1054 /* URL may be rewritten later, so make extra room */
1055 url_sz = strlen(url) + Config.appendDomainLen + 5;
23d92c64 1056 http->uri = xcalloc(url_sz, 1);
1057 strcpy(http->uri, url);
77ed547a 1058 http->flags.accel = 0;
7a2f978b 1059 }
7e3ce7b9 1060 if (!stringHasCntl(http->uri))
c68e9c6b 1061 http->log_uri = xstrndup(http->uri, MAX_URL);
d548ee64 1062 else
9bc73deb 1063 http->log_uri = xstrndup(rfc1738_escape_unescaped(http->uri), MAX_URL);
93f9abd0 1064 debug(33, 5) ("parseHttpRequest: Complete request received\n");
7a2f978b 1065 xfree(inbuf);
7a2f978b 1066 *status = 1;
edce4d98 1067 return context;
7a2f978b 1068}
1069
1070static int
79d39a72 1071clientReadDefer(int fdnotused, void *data)
7a2f978b 1072{
1073 ConnStateData *conn = data;
94439e4e 1074 if (conn->body.size_left)
ec7c5e3e 1075 return conn->in.offset >= conn->in.size - 1;
94439e4e 1076 else
1077 return conn->defer.until > squid_curtime;
7a2f978b 1078}
1079
1080static void
1081clientReadRequest(int fd, void *data)
1082{
1083 ConnStateData *conn = data;
1084 int parser_return_code = 0;
7a2f978b 1085 request_t *request = NULL;
7a2f978b 1086 int size;
7a2f978b 1087 method_t method;
08e70b8f 1088 char *prefix = NULL;
7a2f978b 1089 fde *F = &fd_table[fd];
44db45e8 1090 int len = conn->in.size - conn->in.offset - 1;
edce4d98 1091 clientSocketContext *context;
93f9abd0 1092 debug(33, 4) ("clientReadRequest: FD %d: reading request...\n", fd);
ec7c5e3e 1093 commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, conn, 0);
28ee8ce5 1094 if (len == 0) {
1095 /* Grow the request memory area to accomodate for a large request */
edce4d98 1096 conn->in.buf =
1097 memReallocBuf(conn->in.buf, conn->in.size * 2, &conn->in.size);
28ee8ce5 1098 debug(33, 2) ("growing request buffer: offset=%ld size=%ld\n",
6fbf8998 1099 (long) conn->in.offset, (long) conn->in.size);
28ee8ce5 1100 len = conn->in.size - conn->in.offset - 1;
1101 }
83704487 1102 statCounter.syscalls.sock.reads++;
1f7c9178 1103 size = FD_READ_METHOD(fd, conn->in.buf + conn->in.offset, len);
d8b83480 1104 if (size > 0) {
ea285003 1105 fd_bytes(fd, size, FD_READ);
83704487 1106 kb_incr(&statCounter.client_http.kbytes_in, size);
d8b83480 1107 }
44db45e8 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 */
94439e4e 1115 if (size > 0) {
1116 conn->in.offset += size;
1117 conn->in.buf[conn->in.offset] = '\0'; /* Terminate the string */
28ee8ce5 1118 } else if (size == 0) {
edce4d98 1119 if (conn->currentobject == NULL && conn->in.offset == 0) {
7a2f978b 1120 /* no current or pending requests */
94439e4e 1121 debug(33, 4) ("clientReadRequest: FD %d closed\n", fd);
7a2f978b 1122 comm_close(fd);
1123 return;
ea285003 1124 } else if (!Config.onoff.half_closed_clients) {
1125 /* admin doesn't want to support half-closed client sockets */
edce4d98 1126 debug(33, 3) ("clientReadRequest: FD %d aborted (half_closed_clients disabled)\n",
1127 fd);
ea285003 1128 comm_close(fd);
1129 return;
7a2f978b 1130 }
1131 /* It might be half-closed, we can't tell */
93f9abd0 1132 debug(33, 5) ("clientReadRequest: FD %d closed?\n", fd);
58a6c186 1133 F->flags.socket_eof = 1;
7a2f978b 1134 conn->defer.until = squid_curtime + 1;
1135 conn->defer.n++;
44db45e8 1136 fd_note(fd, "half-closed");
94439e4e 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 */
7a2f978b 1142 } else if (size < 0) {
6d3caf23 1143 if (!ignoreErrno(errno)) {
7a2f978b 1144 debug(50, 2) ("clientReadRequest: FD %d: %s\n", fd, xstrerror());
1145 comm_close(fd);
ebf4efff 1146 return;
47130615 1147 } else if (conn->in.offset == 0) {
edce4d98 1148 debug(50, 2) ("clientReadRequest: FD %d: no data to process (%s)\n",
1149 fd, xstrerror());
7a2f978b 1150 }
ebf4efff 1151 /* Continue to process previously read data */
7a2f978b 1152 }
94439e4e 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) {
edce4d98 1158 clientSocketContext **S;
ebf4efff 1159 int nrequests;
cff0c749 1160 size_t req_line_sz;
94439e4e 1161 /* Skip leading (and trailing) whitespace */
b6a2f15e 1162 while (conn->in.offset > 0 && xisspace(conn->in.buf[0])) {
9a6f98a5 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;
ebf4efff 1169 /* Limit the number of concurrent requests to 2 */
edce4d98 1170 for (S = (clientSocketContext **) & conn->currentobject, nrequests = 0;
1171 *S; S = &(*S)->next, nrequests++);
3d15e2d7 1172 if (nrequests >= (Config.onoff.pipeline_prefetch ? 2 : 1)) {
edce4d98 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);
47130615 1177 conn->defer.until = squid_curtime + 100; /* Reset when a request is complete */
380991a7 1178 conn->defer.n++;
1179 return;
ebf4efff 1180 }
94439e4e 1181 conn->in.buf[conn->in.offset] = '\0'; /* Terminate the string */
1182 if (nrequests == 0)
1183 fd_note(conn->fd, "Reading next request");
ebf4efff 1184 /* Process request */
edce4d98 1185 context = parseHttpRequest(conn,
1186 &method, &parser_return_code, &prefix, &req_line_sz);
1187 if (!context)
13f11a4a 1188 safe_free(prefix);
edce4d98 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 */
7a2f978b 1193 assert(http->req_sz > 0);
1194 conn->in.offset -= http->req_sz;
1195 assert(conn->in.offset >= 0);
ebb6e9a0 1196 debug(33, 5) ("conn->in.offset = %d\n", (int) conn->in.offset);
6fa92aa2 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)
edce4d98 1202 xmemmove(conn->in.buf, conn->in.buf + http->req_sz,
1203 conn->in.offset);
ebf4efff 1204 /* add to the client request queue */
edce4d98 1205 for (S = (clientSocketContext **) & conn->currentobject; *S;
1206 S = &(*S)->next);
1207 *S = context;
7a2f978b 1208 conn->nrequests++;
edce4d98 1209 commSetTimeout(fd, Config.Timeout.lifetime, clientLifetimeTimeout,
1210 http);
3775d53c 1211 if (parser_return_code < 0) {
edce4d98 1212 clientStreamNode *node = http->client_stream.tail->prev->data;
93f9abd0 1213 debug(33, 1) ("clientReadRequest: FD %d Invalid Request\n", fd);
edce4d98 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);
13f11a4a 1219 safe_free(prefix);
3775d53c 1220 break;
1221 }
23d92c64 1222 if ((request = urlParse(method, http->uri)) == NULL) {
edce4d98 1223 clientStreamNode *node = http->client_stream.tail->prev->data;
93f9abd0 1224 debug(33, 5) ("Invalid URL: %s\n", http->uri);
edce4d98 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);
99edd1c3 1230 safe_free(prefix);
7a2f978b 1231 break;
99edd1c3 1232 } else {
1233 /* compile headers */
1234 /* we should skip request line! */
ea285003 1235 if (!httpRequestParseHeader(request, prefix + req_line_sz))
99edd1c3 1236 debug(33, 1) ("Failed to parse request headers: %s\n%s\n",
1237 http->uri, prefix);
1238 /* continue anyway? */
7a2f978b 1239 }
c68e9c6b 1240 request->flags.accelerated = http->flags.accel;
ba26bca4 1241 if (!http->flags.internal) {
1242 if (internalCheck(strBuf(request->urlpath))) {
1f38f50a 1243 if (internalHostnameIs(request->host) &&
52f772de 1244 request->port == getMyPort()) {
b6a2f15e 1245 http->flags.internal = 1;
ba26bca4 1246 } else if (internalStaticCheck(strBuf(request->urlpath))) {
edce4d98 1247 xstrncpy(request->host, internalHostname(),
1248 SQUIDHOSTNAMELEN);
52f772de 1249 request->port = getMyPort();
5999b776 1250 http->flags.internal = 1;
ba26bca4 1251 }
1252 }
1253 }
1f38f50a 1254 /*
1255 * cache the Content-length value in request_t.
1256 */
1257 request->content_length = httpHeaderGetInt(&request->header,
1258 HDR_CONTENT_LENGTH);
c68e9c6b 1259 request->flags.internal = http->flags.internal;
13f11a4a 1260 safe_free(prefix);
23d92c64 1261 safe_free(http->log_uri);
1262 http->log_uri = xstrdup(urlCanonicalClean(request));
7a2f978b 1263 request->client_addr = conn->peer.sin_addr;
3c11d1f5 1264 request->my_addr = conn->me.sin_addr;
7e3ce7b9 1265 request->my_port = ntohs(conn->me.sin_port);
7a2f978b 1266 request->http_ver = http->http_ver;
5affedf3 1267 if (!urlCheckRequest(request) ||
1268 httpHeaderHas(&request->header, HDR_TRANSFER_ENCODING)) {
edce4d98 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);
3775d53c 1275 break;
7a2f978b 1276 }
94439e4e 1277 if (!clientCheckContentLength(request)) {
edce4d98 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);
31be8b80 1284 break;
1285 }
7a2f978b 1286 http->request = requestLink(request);
c68e9c6b 1287 clientSetKeepaliveFlag(http);
94439e4e 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)) {
edce4d98 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);
0483b991 1301 break;
1302 }
7a2f978b 1303 }
2aecf121 1304 clientAccessCheck(http);
94439e4e 1305 continue; /* while offset > 0 && body.size_left == 0 */
7a2f978b 1306 } else if (parser_return_code == 0) {
1307 /*
1308 * Partial request received; reschedule until parseHttpRequest()
1309 * is happy with the input
1310 */
28ee8ce5 1311 if (conn->in.offset >= Config.maxRequestHeaderSize) {
1312 /* The request is too large to handle */
edce4d98 1313 clientStreamNode *node;
1314 context =
1315 parseHttpRequestAbort(conn, "error:request-too-large");
1316 node = context->http->client_stream.tail->prev->data;
28ee8ce5 1317 debug(33, 1) ("Request header is too large (%d bytes)\n",
1318 (int) conn->in.offset);
32754419 1319 debug(33, 1) ("Config 'request_header_max_size'= %ld bytes.\n",
1320 (long int) Config.maxRequestHeaderSize);
edce4d98 1321 clientSetReplyToError(node->data, ERR_TOO_BIG,
1322 HTTP_REQUEST_ENTITY_TOO_LARGE, METHOD_NONE, NULL,
1323 &conn->peer.sin_addr, NULL, NULL, NULL);
28ee8ce5 1324 /* add to the client request queue */
edce4d98 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);
28ee8ce5 1330 return;
7a2f978b 1331 }
7a2f978b 1332 break;
7a2f978b 1333 }
94439e4e 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! */
edce4d98 1339 debug(33, 3) ("clientReadRequest: FD %d aborted, partial request\n",
1340 fd);
94439e4e 1341 comm_close(fd);
1342 return;
1343 }
1344 }
1345}
1346
1347/* file_read like function, for reading body content */
1348void
edce4d98 1349clientReadBody(request_t * request, char *buf, size_t size, CBCB * callback,
1350 void *cbdata)
94439e4e 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;
7a2f978b 1357 }
edce4d98 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);
94439e4e 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);
28ee8ce5 1366 clientProcessBody(conn);
94439e4e 1367}
1368
1369/* Called by clientReadRequest to process body content */
1370static void
1371clientProcessBody(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 */
edce4d98 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);
28ee8ce5 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 */
edce4d98 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);
28ee8ce5 1419 }
94439e4e 1420}
1421
1422/* A dummy handler that throws away a request-body */
1423static char bodyAbortBuf[SQUID_TCP_SO_RCVBUF];
2d72d4fd 1424static void
94439e4e 1425clientReadBodyAbortHandler(char *buf, size_t size, void *data)
1426{
1427 ConnStateData *conn = (ConnStateData *) data;
edce4d98 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);
94439e4e 1431 if (size != 0 && conn->body.size_left != 0) {
edce4d98 1432 debug(33, 3) ("clientReadBodyAbortHandler: fd=%d shedule next read\n",
1433 conn->fd);
94439e4e 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 */
1442int
1443clientAbortBody(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 */
7a2f978b 1467}
1468
1469/* general lifetime handler for HTTP requests */
1470static void
1471requestTimeout(int fd, void *data)
1472{
ad63ceea 1473#if THIS_CONFUSES_PERSISTENT_CONNECTION_AWARE_BROWSERS_AND_USERS
7a2f978b 1474 ConnStateData *conn = data;
badd8ff0 1475 debug(33, 3) ("requestTimeout: FD %d: lifetime is expired.\n", fd);
7a2f978b 1476 if (fd_table[fd].rwstate) {
79e0dc20 1477 /*
1478 * Some data has been sent to the client, just close the FD
1479 */
7a2f978b 1480 comm_close(fd);
1481 } else if (conn->nrequests) {
79e0dc20 1482 /*
1483 * assume its a persistent connection; just close it
1484 */
7a2f978b 1485 comm_close(fd);
1486 } else {
79e0dc20 1487 /*
1488 * Generate an error
1489 */
edce4d98 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 */
79e0dc20 1500 assert(conn->chr == NULL);
edce4d98 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);
79e0dc20 1506 /*
1507 * if we don't close() here, we still need a timeout handler!
1508 */
7a2f978b 1509 commSetTimeout(fd, 30, requestTimeout, conn);
7e3ce7b9 1510 /*
1511 * Aha, but we don't want a read handler!
1512 */
1513 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
7a2f978b 1514 }
af57a2e3 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 */
ad63ceea 1524 debug(33, 3) ("requestTimeout: FD %d: lifetime is expired.\n", fd);
af57a2e3 1525 comm_close(fd);
1526#endif
7a2f978b 1527}
1528
b5c39993 1529static void
1530clientLifetimeTimeout(int fd, void *data)
1531{
1532 clientHttpRequest *http = data;
7ec87701 1533 ConnStateData *conn = http->conn;
edce4d98 1534 debug(33,
1535 1) ("WARNING: Closing client %s connection due to lifetime timeout\n",
b5c39993 1536 inet_ntoa(conn->peer.sin_addr));
1537 debug(33, 1) ("\t%s\n", http->uri);
1538 comm_close(fd);
1539}
1540
ba4f8e5a 1541static int
d20b1cd0 1542httpAcceptDefer(int fdunused, void *dataunused)
7a2f978b 1543{
3d6629c6 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;
7a2f978b 1552}
1553
bf8e5903 1554/* Handle a new connection on HTTP socket. */
7a2f978b 1555void
ba4f8e5a 1556httpAccept(int sock, void *data)
7a2f978b 1557{
d193a436 1558 int *N = &incoming_sockets_accepted;
7a2f978b 1559 int fd = -1;
1560 ConnStateData *connState = NULL;
1561 struct sockaddr_in peer;
1562 struct sockaddr_in me;
309ad3b6 1563 int max = INCOMING_HTTP_MAX;
3898f57f 1564#if USE_IDENT
a40699cd 1565 static aclCheck_t identChecklist;
3898f57f 1566#endif
c411b98a 1567 commSetSelect(sock, COMM_SELECT_READ, httpAccept, NULL, 0);
d20b1cd0 1568 while (max-- && !httpAcceptDefer(sock, NULL)) {
e2525655 1569 memset(&peer, '\0', sizeof(struct sockaddr_in));
1570 memset(&me, '\0', sizeof(struct sockaddr_in));
e2525655 1571 if ((fd = comm_accept(sock, &peer, &me)) < 0) {
1572 if (!ignoreErrno(errno))
eeb423fb 1573 debug(50, 1) ("httpAccept: FD %d: accept failure: %s\n",
e2525655 1574 sock, xstrerror());
1575 break;
1576 }
1577 debug(33, 4) ("httpAccept: FD %d: accepted\n", fd);
72711e31 1578 connState = cbdataAlloc(ConnStateData);
e2525655 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;
1eb41ae8 1584 connState->in.buf = memAllocBuf(CLIENT_REQ_BUF_SZ, &connState->in.size);
e2525655 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);
3898f57f 1589#if USE_IDENT
a40699cd 1590 identChecklist.src_addr = peer.sin_addr;
b6a2f15e 1591 identChecklist.my_addr = me.sin_addr;
7e3ce7b9 1592 identChecklist.my_port = ntohs(me.sin_port);
a40699cd 1593 if (aclCheckFast(Config.accessList.identLookup, &identChecklist))
05832ae1 1594 identStart(&me, &peer, clientIdentDone, connState);
3898f57f 1595#endif
e2525655 1596 commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, connState, 0);
1597 commSetDefer(fd, clientReadDefer, connState);
9bc73deb 1598 clientdbEstablished(peer.sin_addr, 1);
41292f79 1599 assert(N);
ba4f8e5a 1600 (*N)++;
7a2f978b 1601 }
7a2f978b 1602}
1603
1f7c9178 1604#if USE_SSL
1605
1606/* negotiate an SSL connection */
1607static void
1608clientNegotiateSSL(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();
a4a80ff0 1620 if (ret) {
edce4d98 1621 debug(83, 1)
1622 ("clientNegotiateSSL: Error negotiating SSL connection on FD %d: %s\n",
a4a80ff0 1623 fd, ERR_error_string(ret, NULL));
1624 }
1f7c9178 1625 comm_close(fd);
1626 return;
1627 }
27d8545c 1628 debug(83, 5) ("clientNegotiateSSL: FD %d negotiated cipher %s\n", fd,
1f7c9178 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) {
edce4d98 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));
1f7c9178 1635
edce4d98 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));
1f7c9178 1638
1639 X509_free(client_cert);
1640 } else {
27d8545c 1641 debug(83, 5) ("clientNegotiateSSL: FD %d has no certificate.\n", fd);
1f7c9178 1642 }
1643
1644 commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, conn, 0);
1645}
1646
d193a436 1647struct _https_port_data {
1648 SSL_CTX *sslContext;
1649};
1650typedef struct _https_port_data https_port_data;
1651CBDATA_TYPE(https_port_data);
1652
1f7c9178 1653/* handle a new HTTPS connection */
1654static void
1655httpsAccept(int sock, void *data)
1656{
d193a436 1657 int *N = &incoming_sockets_accepted;
1658 https_port_data *https_port = data;
1659 SSL_CTX *sslContext = https_port->sslContext;
1f7c9178 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
d193a436 1670 commSetSelect(sock, COMM_SELECT_READ, httpsAccept, https_port, 0);
1f7c9178 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();
27d8545c 1682 debug(83, 1) ("httpsAccept: Error allocating handle: %s\n",
1f7c9178 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;
1eb41ae8 1698 connState->in.buf = memAllocBuf(CLIENT_REQ_BUF_SZ, &connState->in.size);
1f7c9178 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
7a2f978b 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
1733static void
88aad2e5 1734checkFailureRatio(err_type etype, hier_code hcode)
7a2f978b 1735{
88aad2e5 1736 static double magic_factor = 100.0;
7a2f978b 1737 double n_good;
1738 double n_bad;
1739 if (hcode == HIER_NONE)
1740 return;
88aad2e5 1741 n_good = magic_factor / (1.0 + request_failure_ratio);
7a2f978b 1742 n_bad = magic_factor - n_good;
88aad2e5 1743 switch (etype) {
7a2f978b 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 }
88aad2e5 1752 request_failure_ratio = n_bad / n_good;
7a2f978b 1753 if (hit_only_mode_until > squid_curtime)
1754 return;
88aad2e5 1755 if (request_failure_ratio < 1.0)
7a2f978b 1756 return;
93f9abd0 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",
7a2f978b 1759 FAILURE_MODE_TIME / 60);
1760 hit_only_mode_until = squid_curtime + FAILURE_MODE_TIME;
88aad2e5 1761 request_failure_ratio = 0.8; /* reset to something less than 1.0 */
7a2f978b 1762}
15df8349 1763
d193a436 1764static void
15df8349 1765clientHttpConnectionsOpen(void)
1766{
7e3ce7b9 1767 sockaddr_in_list *s;
15df8349 1768 int fd;
7e3ce7b9 1769 for (s = Config.Sockaddr.http; s; s = s->next) {
42b51993 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 }
8daca701 1775 enter_suid();
1776 fd = comm_open(SOCK_STREAM,
1777 0,
7e3ce7b9 1778 s->s.sin_addr,
edce4d98 1779 ntohs(s->s.sin_port), COMM_NONBLOCKING, "HTTP Socket");
8daca701 1780 leave_suid();
1781 if (fd < 0)
1782 continue;
1783 comm_listen(fd);
1784 commSetSelect(fd, COMM_SELECT_READ, httpAccept, NULL, 0);
d20b1cd0 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);
7e3ce7b9 1790 debug(1, 1) ("Accepting HTTP connections at %s, port %d, FD %d.\n",
edce4d98 1791 inet_ntoa(s->s.sin_addr), (int) ntohs(s->s.sin_port), fd);
8daca701 1792 HttpSockets[NHttpSockets++] = fd;
15df8349 1793 }
d193a436 1794}
1795
1796#if USE_SSL
1797static void
1798clientHttpsConnectionsOpen(void)
1799{
1800 https_port_list *s;
1801 https_port_data *https_port;
1802 int fd;
1f7c9178 1803 for (s = Config.Sockaddr.https; s; s = s->next) {
17848833 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 }
1f7c9178 1809 enter_suid();
1810 fd = comm_open(SOCK_STREAM,
1811 0,
1812 s->s.sin_addr,
edce4d98 1813 ntohs(s->s.sin_port), COMM_NONBLOCKING, "HTTPS Socket");
1f7c9178 1814 leave_suid();
1815 if (fd < 0)
1816 continue;
d193a436 1817 CBDATA_INIT_TYPE(https_port_data);
1818 https_port = cbdataAlloc(https_port_data);
edce4d98 1819 https_port->sslContext =
1820 sslCreateContext(s->cert, s->key, s->version, s->cipher,
1821 s->options);
1f7c9178 1822 comm_listen(fd);
d193a436 1823 commSetSelect(fd, COMM_SELECT_READ, httpsAccept, https_port, 0);
1824 commSetDefer(fd, httpAcceptDefer, NULL);
1f7c9178 1825 debug(1, 1) ("Accepting HTTPS connections at %s, port %d, FD %d.\n",
edce4d98 1826 inet_ntoa(s->s.sin_addr), (int) ntohs(s->s.sin_port), fd);
1f7c9178 1827 HttpSockets[NHttpSockets++] = fd;
1828 }
d193a436 1829}
1830
1831#endif
1832
1833void
1834clientOpenListenSockets(void)
1835{
1836 clientHttpConnectionsOpen();
1837#if USE_SSL
1838 clientHttpsConnectionsOpen();
1f7c9178 1839#endif
15df8349 1840 if (NHttpSockets < 1)
8daca701 1841 fatal("Cannot open HTTP Port");
15df8349 1842}
edce4d98 1843
c0fbae16 1844void
1845clientHttpConnectionsClose(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}
f66a9ef4 1857
1858int
1859varyEvaluateMatch(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
edce4d98 1864 has_vary |=
1865 httpHeaderHas(&entry->mem_obj->reply->header, HDR_X_ACCELERATOR_VARY);
f66a9ef4 1866#endif
1867 if (!has_vary || !entry->mem_obj->vary_headers) {
1868 if (vary) {
1869 /* Oops... something odd is going on here.. */
edce4d98 1870 debug(33,
1871 1)
1872 ("varyEvaluateMatch: Oops. Not a Vary object on second attempt, '%s' '%s'\n",
f66a9ef4 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 */
a16efaa4 1884 vary = httpMakeVaryMark(request, entry->mem_obj->reply);
1885 if (vary) {
1886 request->vary_headers = xstrdup(vary);
f66a9ef4 1887 return VARY_OTHER;
a16efaa4 1888 } else {
f66a9ef4 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 {
a16efaa4 1894 if (!vary) {
1895 vary = httpMakeVaryMark(request, entry->mem_obj->reply);
1896 if (vary)
1897 request->vary_headers = xstrdup(vary);
1898 }
f66a9ef4 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}