]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client_side.cc
Implement a new comm_write().
[thirdparty/squid.git] / src / client_side.cc
CommitLineData
3c66d057 1
dd11e0b7 2/*
d4cb310b 3 * $Id: client_side.cc,v 1.604 2002/10/21 14:00:01 adrian 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"
c8be6d7b 59#include "clientStream.h"
60#include "IPInterception.h"
e6ccf245 61#include "authenticate.h"
62#include "Store.h"
c4b7a5a9 63#include "comm.h"
e6ccf245 64
5cafc1d6 65
5492ad1d 66#if LINGERING_CLOSE
67#define comm_close comm_lingering_close
68#endif
69
7a2f978b 70#define FAILURE_MODE_TIME 300
71
edce4d98 72/* Persistent connection logic:
73 *
74 * requests (httpClientRequest structs) get added to the connection
75 * list, with the current one being chr
76 *
77 * The request is *immediately* kicked off, and data flows through
78 * to clientSocketRecipient.
79 *
80 * If the data that arrives at clientSocketRecipient is not for the current
81 * request, clientSocketRecipient simply returns, without requesting more
82 * data, or sending it.
83 *
84 * ClientKeepAliveNextRequest will then detect the presence of data in
85 * the next clientHttpRequest, and will send it, restablishing the
86 * data flow.
87 */
88
89/* our socket-related context */
90typedef struct _clientSocketContext {
91 clientHttpRequest *http; /* we own this */
92 char reqbuf[HTTP_REQBUF_SZ];
93 struct _clientSocketContext *next;
94 struct {
95 int deferred:1; /* This is a pipelined request waiting for the
96 * current object to complete */
c4b7a5a9 97 int parsed_ok:1; /* Was this parsed correctly? */
98 int mayUseConnection:1; /* This request may use the connection -
99 * don't read anymore requests for now
100 */
edce4d98 101 } flags;
102 struct {
103 clientStreamNode *node;
104 HttpReply *rep;
c8be6d7b 105 StoreIOBuffer queuedBuffer;
edce4d98 106 } deferredparams;
c8be6d7b 107 off_t writtenToSocket;
edce4d98 108} clientSocketContext;
109
110CBDATA_TYPE(clientSocketContext);
7a2f978b 111
edce4d98 112/* Local functions */
113/* clientSocketContext */
114static FREE clientSocketContextFree;
115static clientSocketContext *clientSocketContextNew(clientHttpRequest *);
116/* other */
fc5d6f7f 117static CWCB clientWriteComplete;
f4f278b5 118static CWCB clientWriteBodyComplete;
c4b7a5a9 119static IOCB clientReadRequest;
7a2f978b 120static PF connStateFree;
121static PF requestTimeout;
b5c39993 122static PF clientLifetimeTimeout;
88aad2e5 123static void checkFailureRatio(err_type, hier_code);
edce4d98 124static clientSocketContext *parseHttpRequestAbort(ConnStateData * conn,
125 const char *uri);
c4b7a5a9 126static clientSocketContext *parseHttpRequest(ConnStateData *, method_t *,
edce4d98 127 char **, size_t *);
3898f57f 128#if USE_IDENT
05832ae1 129static IDCB clientIdentDone;
3898f57f 130#endif
edce4d98 131static CSCB clientSocketRecipient;
132static CSD clientSocketDetach;
c68e9c6b 133static void clientSetKeepaliveFlag(clientHttpRequest *);
c8be6d7b 134static int clientIsContentLengthValid(request_t * r);
d20b1cd0 135static DEFER httpAcceptDefer;
c8be6d7b 136static int clientIsRequestBodyValid(int bodyLength);
e6ccf245 137static int clientIsRequestBodyTooLargeForPolicy(size_t bodyLength);
94439e4e 138static void clientProcessBody(ConnStateData * conn);
c8be6d7b 139static clientStreamNode *getTail(clientSocketContext *);
140static void clientSocketRemoveThisFromConnectionList(clientSocketContext *,
141 ConnStateData *);
142static void clientUpdateStatHistCounters(log_type logType, int svc_time);
143static void clientUpdateStatCounters(log_type logType);
144static void clientUpdateHierCounters(HierarchyLogEntry *);
145static int clientPingHasFinished(ping_data const *aPing);
146static MemObject *clientGetMemObject(clientHttpRequest * http);
147static void clientPrepareLogWithRequestDetails(request_t *, AccessLogEntry *);
148static void clientLogRequest(clientHttpRequest *);
149static void httpRequestFreeResources(clientHttpRequest *);
c8be6d7b 150static int connIsUsable(ConnStateData * conn);
151static clientSocketContext *connGetCurrentContext(ConnStateData * conn);
152static void contextDeferRecipientForLater(clientSocketContext * context, clientStreamNode * node, HttpReply * rep, StoreIOBuffer recievedData);
153static int responseFinishedOrFailed(HttpReply * rep, StoreIOBuffer recievedData);
154static int contextStartOfOutput(clientSocketContext * context);
155static void contextSendBody(clientSocketContext * context, HttpReply * rep, StoreIOBuffer bodyData);
156static void contextSendStartOfMessage(clientSocketContext * context, HttpReply * rep, StoreIOBuffer bodyData);
157static void connReadNextRequest(ConnStateData * conn);
158static void clientSocketContextPushDeferredIfNeeded(clientSocketContext * deferredRequest, ConnStateData * conn);
159static void clientUpdateSocketStats(log_type logType, size_t size);
160
161static clientSocketContext *clientCheckRequestLineIsParseable(char *inbuf, size_t req_sz, ConnStateData * conn);
162static clientSocketContext *clientParseRequestMethod(char *inbuf, method_t * method_p, ConnStateData * conn);
163static char *skipLeadingSpace(char *aString);
164static char *findTrailingHTTPVersion(char *uriAndHTTPVersion);
165static void trimTrailingSpaces(char *aString, size_t len);
166static clientSocketContext *parseURIandHTTPVersion(char **url_p, http_version_t * http_ver_p, ConnStateData * conn);
c4b7a5a9 167static void setLogUri(clientHttpRequest * http, char const *uri);
c8be6d7b 168static void prepareInternalUrl(clientHttpRequest * http, char *url);
169static void prepareForwardProxyUrl(clientHttpRequest * http, char *url);
170static void prepareAcceleratedUrl(clientHttpRequest * http, char *url, char *req_hdr);
171static int connGetAvailableBufferLength(ConnStateData const *conn);
172static void connMakeSpaceAvailable(ConnStateData * conn);
173static void connAddContextToQueue(ConnStateData * conn, clientSocketContext * context);
174static int connGetConcurrentRequestCount(ConnStateData * conn);
c4b7a5a9 175static int connReadWasError(ConnStateData * conn, comm_err_t, int size);
c8be6d7b 176static int connFinishedWithConn(ConnStateData * conn, int size);
e6ccf245 177static void connNoteUseOfBuffer(ConnStateData * conn, size_t byteCount);
c8be6d7b 178static int connKeepReadingIncompleteRequest(ConnStateData * conn);
179static void connCancelIncompleteRequests(ConnStateData * conn);
c4b7a5a9 180static ConnStateData *connStateCreate(struct sockaddr_in *peer, struct sockaddr_in *me, int fd);
0e3be1ea 181static clientStreamNode *getClientReplyContext(clientSocketContext * context);
bded68d8 182static int connAreAllContextsForThisConnection(ConnStateData * connState);
183static void connFreeAllContexts(ConnStateData * connState);
184static void clientPullData(clientSocketContext * context);
c8be6d7b 185
186clientStreamNode *
187getTail(clientSocketContext * context)
188{
e6ccf245 189 return (clientStreamNode *)context->http->client_stream.tail->data;
c8be6d7b 190}
191
192clientStreamNode *
193getClientReplyContext(clientSocketContext * context)
194{
e6ccf245 195 return (clientStreamNode *)context->http->client_stream.tail->prev->data;
c8be6d7b 196}
197
c4b7a5a9 198/*
199 * This routine should be called to grow the inbuf and then
200 * call comm_read().
201 */
202void
203clientReadSomeData(ConnStateData *conn)
204{
205 size_t len;
206
207 debug(33, 4) ("clientReadSomeData: FD %d: reading request...\n", conn->fd);
208
209 connMakeSpaceAvailable(conn);
210 len = connGetAvailableBufferLength(conn) - 1;
211
212 comm_read(conn->fd, conn->in.buf + conn->in.notYetUsed, len, clientReadRequest, conn);
213}
214
215
c8be6d7b 216void
217clientSocketRemoveThisFromConnectionList(clientSocketContext * context,
218 ConnStateData * conn)
219{
220 clientSocketContext **tempContextPointer;
221 assert(conn);
222 assert(connGetCurrentContext(conn) != NULL);
223 /* Unlink us from the connection request list */
224 tempContextPointer = (clientSocketContext **) & conn->currentobject;
225 while (*tempContextPointer) {
226 if (*tempContextPointer == context)
227 break;
228 tempContextPointer = &(*tempContextPointer)->next;
229 }
230 assert(*tempContextPointer != NULL);
231 *tempContextPointer = context->next;
232 context->next = NULL;
233}
ea6f43cd 234
b8d8561b 235void
edce4d98 236clientSocketContextFree(void *data)
f88bb09c 237{
e6ccf245 238 clientSocketContext *context = (clientSocketContext *)data;
edce4d98 239 ConnStateData *conn = context->http->conn;
c8be6d7b 240 clientStreamNode *node = getTail(context);
edce4d98 241 /* We are *always* the tail - prevent recursive free */
242 assert(context == node->data);
243 node->data = NULL;
244 httpRequestFree(context->http);
245 /* clean up connection links to us */
246 assert(context != context->next);
c8be6d7b 247 if (conn)
248 clientSocketRemoveThisFromConnectionList(context, conn);
f88bb09c 249}
250
edce4d98 251clientSocketContext *
252clientSocketContextNew(clientHttpRequest * http)
f88bb09c 253{
c8be6d7b 254 clientSocketContext *newContext;
edce4d98 255 assert(http != NULL);
256 CBDATA_INIT_TYPE_FREECB(clientSocketContext, clientSocketContextFree);
c8be6d7b 257 newContext = cbdataAlloc(clientSocketContext);
258 newContext->http = http;
259 return newContext;
4d55827a 260}
261
edce4d98 262#if USE_IDENT
4d55827a 263static void
edce4d98 264clientIdentDone(const char *ident, void *data)
4d55827a 265{
edce4d98 266 ConnStateData *conn = data;
267 xstrncpy(conn->rfc931, ident ? ident : dash_str, USER_IDENT_SZ);
e81957b7 268}
269
447e176b 270#endif
7a2f978b 271
c8be6d7b 272void
273clientUpdateStatCounters(log_type logType)
a7c05555 274{
83704487 275 statCounter.client_http.requests++;
c8be6d7b 276 if (logTypeIsATcpHit(logType))
83704487 277 statCounter.client_http.hits++;
c8be6d7b 278 if (logType == LOG_TCP_HIT)
83704487 279 statCounter.client_http.disk_hits++;
c8be6d7b 280 else if (logType == LOG_TCP_MEM_HIT)
83704487 281 statCounter.client_http.mem_hits++;
c8be6d7b 282}
283
284void
285clientUpdateStatHistCounters(log_type logType, int svc_time)
286{
83704487 287 statHistCount(&statCounter.client_http.all_svc_time, svc_time);
ee1679df 288 /*
289 * The idea here is not to be complete, but to get service times
290 * for only well-defined types. For example, we don't include
291 * LOG_TCP_REFRESH_FAIL_HIT because its not really a cache hit
292 * (we *tried* to validate it, but failed).
293 */
c8be6d7b 294 switch (logType) {
7c9c84ad 295 case LOG_TCP_REFRESH_HIT:
83704487 296 statHistCount(&statCounter.client_http.nh_svc_time, svc_time);
7c9c84ad 297 break;
ee1679df 298 case LOG_TCP_IMS_HIT:
83704487 299 statHistCount(&statCounter.client_http.nm_svc_time, svc_time);
ee1679df 300 break;
301 case LOG_TCP_HIT:
302 case LOG_TCP_MEM_HIT:
b540e168 303 case LOG_TCP_OFFLINE_HIT:
83704487 304 statHistCount(&statCounter.client_http.hit_svc_time, svc_time);
ee1679df 305 break;
306 case LOG_TCP_MISS:
307 case LOG_TCP_CLIENT_REFRESH_MISS:
83704487 308 statHistCount(&statCounter.client_http.miss_svc_time, svc_time);
ee1679df 309 break;
310 default:
311 /* make compiler warnings go away */
312 break;
313 }
c8be6d7b 314}
315
316int
317clientPingHasFinished(ping_data const *aPing)
318{
319 if (0 != aPing->stop.tv_sec && 0 != aPing->start.tv_sec)
320 return -1;
321 return 0;
322}
323
324void
325clientUpdateHierCounters(HierarchyLogEntry * someEntry)
326{
327 ping_data *i;
328 switch (someEntry->alg) {
69c95dd3 329 case PEER_SA_DIGEST:
83704487 330 statCounter.cd.times_used++;
69c95dd3 331 break;
332 case PEER_SA_ICP:
83704487 333 statCounter.icp.times_used++;
c8be6d7b 334 i = &someEntry->ping;
335 if (clientPingHasFinished(i))
83704487 336 statHistCount(&statCounter.icp.query_svc_time,
69c95dd3 337 tvSubUsec(i->start, i->stop));
338 if (i->timeout)
83704487 339 statCounter.icp.query_timeouts++;
69c95dd3 340 break;
341 case PEER_SA_NETDB:
83704487 342 statCounter.netdb.times_used++;
69c95dd3 343 break;
344 default:
345 break;
17b6e784 346 }
a7c05555 347}
348
c8be6d7b 349static void
350clientUpdateCounters(clientHttpRequest * http)
351{
352 clientUpdateStatCounters(http->logType);
353 if (http->request->errType != ERR_NONE)
354 statCounter.client_http.errors++;
355 clientUpdateStatHistCounters(http->logType,
356 tvSubMsec(http->start, current_time));
357 clientUpdateHierCounters(&http->request->hier);
358}
359
360MemObject *
361clientGetMemObject(clientHttpRequest * http)
362{
363 if (http->entry)
364 return http->entry->mem_obj;
365 return NULL;
366}
367
edce4d98 368void
c8be6d7b 369clientPrepareLogWithRequestDetails(request_t * request, AccessLogEntry * aLogEntry)
7a2f978b 370{
c8be6d7b 371 Packer p;
372 MemBuf mb;
373 assert(request);
374 assert(aLogEntry);
375 memBufDefInit(&mb);
376 packerToMemInit(&p, &mb);
377 httpHeaderPackInto(&request->header, &p);
378 aLogEntry->http.method = request->method;
379 aLogEntry->http.version = request->http_ver;
380 aLogEntry->headers.request = xstrdup(mb.buf);
381 aLogEntry->hier = request->hier;
382 if (request->auth_user_request) {
383 aLogEntry->cache.authuser =
384 xstrdup(authenticateUserRequestUsername(request->
385 auth_user_request));
386 authenticateAuthUserRequestUnlock(request->auth_user_request);
387 request->auth_user_request = NULL;
7a2f978b 388 }
c8be6d7b 389 packerClean(&p);
390 memBufClean(&mb);
391}
392
393void
394clientLogRequest(clientHttpRequest * http)
395{
29b8d8d6 396 if (http->out.size || http->logType) {
728da2ee 397 http->al.icp.opcode = ICP_INVALID;
ef65d6ca 398 http->al.url = http->log_uri;
c8be6d7b 399 debug(33, 9) ("clientLogRequest: al.url='%s'\n", http->al.url);
400 if (clientGetMemObject(http)) {
401 http->al.http.code = clientGetMemObject(http)->reply->sline.status;
402 http->al.http.content_type = strBuf(clientGetMemObject(http)->reply->content_type);
7a2f978b 403 }
c8be6d7b 404 http->al.cache.caddr = http->conn ? http->conn->log_addr : no_addr;
7a2f978b 405 http->al.cache.size = http->out.size;
29b8d8d6 406 http->al.cache.code = http->logType;
7a2f978b 407 http->al.cache.msec = tvSubMsec(http->start, current_time);
c8be6d7b 408 if (http->request)
409 clientPrepareLogWithRequestDetails(http->request, &http->al);
410 if (http->conn && http->conn->rfc931[0])
411 http->al.cache.rfc931 = http->conn->rfc931;
7a2f978b 412 accessLogLog(&http->al);
a7c05555 413 clientUpdateCounters(http);
c8be6d7b 414 if (http->conn)
415 clientdbUpdate(http->conn->peer.sin_addr, http->logType, PROTO_HTTP,
edce4d98 416 http->out.size);
7a2f978b 417 }
c8be6d7b 418}
419
420void
421httpRequestFreeResources(clientHttpRequest * http)
422{
23d92c64 423 safe_free(http->uri);
424 safe_free(http->log_uri);
6d38ef86 425 safe_free(http->redirect.location);
7a2f978b 426 requestUnlink(http->request);
c8be6d7b 427 http->request = NULL;
edce4d98 428 if (http->client_stream.tail)
e6ccf245 429 clientStreamAbort((clientStreamNode *)http->client_stream.tail->data, http);
c8be6d7b 430}
431
432void
433httpRequestFree(void *data)
434{
e6ccf245 435 clientHttpRequest *http = (clientHttpRequest *)data;
c8be6d7b 436 request_t *request = NULL;
437 assert(http != NULL);
438 request = http->request;
439 debug(33, 3) ("httpRequestFree: %s\n", http->uri);
0e3be1ea 440 /* if body_connection !NULL, then ProcessBody has not
441 * found the end of the body yet
442 */
443 if (request && request->body_connection)
444 clientAbortBody(request); /* abort body transter */
445 /* the ICP check here was erroneous
446 * - storeReleaseRequest was always called if entry was valid
447 */
c8be6d7b 448 assert(http->logType < LOG_TYPE_MAX);
449 clientLogRequest(http);
450 if (request)
451 checkFailureRatio(request->errType, http->al.hier.code);
452 httpRequestFreeResources(http);
edce4d98 453 /* moving to the next connection is handled by the context free */
0f1bc304 454 dlinkDelete(&http->active, &ClientActiveRequests);
7a2f978b 455 cbdataFree(http);
456}
457
c8be6d7b 458int
459connAreAllContextsForThisConnection(ConnStateData * connState)
460{
461 clientSocketContext *context;
462 assert(connState != NULL);
463 context = connGetCurrentContext(connState);
464 while (context) {
465 if (context->http->conn != connState)
466 return 0;
467 context = context->next;
468 }
469 return -1;
470}
471
472void
473connFreeAllContexts(ConnStateData * connState)
474{
475 clientSocketContext *context;
476 while ((context = connGetCurrentContext(connState)) != NULL) {
477 assert(connGetCurrentContext(connState) !=
478 connGetCurrentContext(connState)->next);
479 cbdataFree(context);
480 }
481}
482
7a2f978b 483/* This is a handler normally called by comm_close() */
484static void
485connStateFree(int fd, void *data)
486{
e6ccf245 487 ConnStateData *connState = (ConnStateData *)data;
93f9abd0 488 debug(33, 3) ("connStateFree: FD %d\n", fd);
7a2f978b 489 assert(connState != NULL);
9bc73deb 490 clientdbEstablished(connState->peer.sin_addr, -1); /* decrement */
c8be6d7b 491 assert(connAreAllContextsForThisConnection(connState));
492 connFreeAllContexts(connState);
5d146f7d 493 if (connState->auth_user_request)
494 authenticateAuthUserRequestUnlock(connState->auth_user_request);
495 connState->auth_user_request = NULL;
496 authenticateOnCloseConnection(connState);
c8be6d7b 497 memFreeBuf(connState->in.allocatedSize, connState->in.buf);
7a2f978b 498 pconnHistCount(0, connState->nrequests);
499 cbdataFree(connState);
500}
501
c68e9c6b 502/*
503 * clientSetKeepaliveFlag() sets request->flags.proxy_keepalive.
504 * This is the client-side persistent connection flag. We need
505 * to set this relatively early in the request processing
506 * to handle hacks for broken servers and clients.
507 */
508static void
509clientSetKeepaliveFlag(clientHttpRequest * http)
510{
511 request_t *request = http->request;
512 const HttpHeader *req_hdr = &request->header;
f6329bc3 513
ccf44862 514 debug(33, 3) ("clientSetKeepaliveFlag: http_ver = %d.%d\n",
515 request->http_ver.major, request->http_ver.minor);
c68e9c6b 516 debug(33, 3) ("clientSetKeepaliveFlag: method = %s\n",
517 RequestMethodStr[request->method]);
efd900cb 518 if (!Config.onoff.client_pconns)
519 request->flags.proxy_keepalive = 0;
f6329bc3 520 else {
521 http_version_t http_ver;
edce4d98 522 httpBuildVersion(&http_ver, 1, 0);
523 /* we are HTTP/1.0, no matter what the client requests... */
f6329bc3 524 if (httpMsgIsPersistent(http_ver, req_hdr))
525 request->flags.proxy_keepalive = 1;
526 }
c68e9c6b 527}
528
31be8b80 529static int
c8be6d7b 530clientIsContentLengthValid(request_t * r)
31be8b80 531{
ffc128c4 532 switch (r->method) {
533 case METHOD_PUT:
534 case METHOD_POST:
535 /* PUT/POST requires a request entity */
2a6b906c 536 return (r->content_length >= 0);
ffc128c4 537 case METHOD_GET:
538 case METHOD_HEAD:
539 /* We do not want to see a request entity on GET/HEAD requests */
2a6b906c 540 return (r->content_length <= 0);
ffc128c4 541 default:
542 /* For other types of requests we don't care */
0cdcddb9 543 return 1;
ffc128c4 544 }
545 /* NOT REACHED */
31be8b80 546}
547
cf50a0af 548int
c8be6d7b 549clientIsRequestBodyValid(int bodyLength)
7a2f978b 550{
c8be6d7b 551 if (bodyLength >= 0)
b540e168 552 return 1;
7a2f978b 553 return 0;
554}
555
c8be6d7b 556int
e6ccf245 557clientIsRequestBodyTooLargeForPolicy(size_t bodyLength)
efd900cb 558{
c8be6d7b 559 if (Config.maxRequestBodySize &&
560 bodyLength > Config.maxRequestBodySize)
efd900cb 561 return 1; /* too large */
562 return 0;
563}
564
c8be6d7b 565int
566connIsUsable(ConnStateData * conn)
567{
568 if (!conn || conn->fd == -1)
569 return 0;
570 return 1;
571}
572
573clientSocketContext *
574connGetCurrentContext(ConnStateData * conn)
575{
576 assert(conn);
e6ccf245 577 return (clientSocketContext *)conn->currentobject;
c8be6d7b 578}
579
580void
581contextDeferRecipientForLater(clientSocketContext * context, clientStreamNode * node, HttpReply * rep, StoreIOBuffer recievedData)
582{
583 debug(33, 2) ("clientSocketRecipient: Deferring %s\n", context->http->uri);
584 assert(context->flags.deferred == 0);
585 context->flags.deferred = 1;
586 context->deferredparams.node = node;
587 context->deferredparams.rep = rep;
588 context->deferredparams.queuedBuffer = recievedData;
589 return;
590}
591
592int
593responseFinishedOrFailed(HttpReply * rep, StoreIOBuffer recievedData)
594{
595 if (rep == NULL && recievedData.data == NULL && recievedData.length == 0)
596 return 1;
597 return 0;
598}
599
600int
601contextStartOfOutput(clientSocketContext * context)
602{
b891193f 603 return context->http->out.size == 0 ? 1 : 0;
c8be6d7b 604}
605
606void
607contextSendBody(clientSocketContext * context, HttpReply * rep, StoreIOBuffer bodyData)
608{
609 assert(rep == NULL);
610 context->http->out.offset += bodyData.length;
d4cb310b 611 comm_old_write(context->http->conn->fd, bodyData.data, bodyData.length,
c8be6d7b 612 clientWriteBodyComplete, context, NULL);
613 return;
614}
615
616void
617contextSendStartOfMessage(clientSocketContext * context, HttpReply * rep, StoreIOBuffer bodyData)
618{
619 MemBuf mb;
620 /* write headers and/or body if any */
621 assert(rep || (bodyData.data && bodyData.length));
622 /* init mb; put status line and headers if any */
623 if (rep) {
624 mb = httpReplyPack(rep);
0e3be1ea 625 /* Save length of headers for persistent conn checks */
626 context->http->out.headers_sz = mb.size;
c8be6d7b 627#if HEADERS_LOG
628 headersLog(0, 0, context->http->request->method, rep);
629#endif
630 httpReplyDestroy(rep);
631 rep = NULL;
632 } else {
633 memBufDefInit(&mb);
634 }
635 if (bodyData.data && bodyData.length) {
636 context->http->out.offset += bodyData.length;
637 memBufAppend(&mb, bodyData.data, bodyData.length);
638 }
639 /* write */
d4cb310b 640 comm_old_write_mbuf(context->http->conn->fd, mb, clientWriteComplete, context);
c8be6d7b 641 /* if we don't do it, who will? */
642}
643
2246b732 644/*
edce4d98 645 * Write a chunk of data to a client socket. If the reply is present, send the reply headers down the wire too,
646 * and clean them up when finished.
647 * Pre-condition:
648 * The request is one backed by a connection, not an internal request.
649 * data context is not NULL
650 * There are no more entries in the stream chain.
2246b732 651 */
edce4d98 652static void
653clientSocketRecipient(clientStreamNode * node, clientHttpRequest * http,
c8be6d7b 654 HttpReply * rep, StoreIOBuffer recievedData)
edce4d98 655{
656 int fd;
657 clientSocketContext *context;
658 /* Test preconditions */
659 assert(node != NULL);
c8be6d7b 660 /* TODO: handle this rather than asserting
661 * - it should only ever happen if we cause an abort and
edce4d98 662 * the callback chain loops back to here, so we can simply return.
663 * However, that itself shouldn't happen, so it stays as an assert for now.
664 */
665 assert(cbdataReferenceValid(node));
666 assert(node->data != NULL);
667 assert(node->node.next == NULL);
e6ccf245 668 context = (clientSocketContext *)node->data;
c8be6d7b 669 assert(connIsUsable(http->conn));
edce4d98 670 fd = http->conn->fd;
c8be6d7b 671 if (connGetCurrentContext(http->conn) != context) {
672 contextDeferRecipientForLater(context, node, rep, recievedData);
edce4d98 673 return;
674 }
c8be6d7b 675 if (responseFinishedOrFailed(rep, recievedData)) {
edce4d98 676 clientWriteComplete(fd, NULL, 0, COMM_OK, context);
677 return;
678 }
c8be6d7b 679 if (!contextStartOfOutput(context))
680 contextSendBody(context, rep, recievedData);
681 else
682 contextSendStartOfMessage(context, rep, recievedData);
edce4d98 683}
684
685/* Called when a downstream node is no longer interested in
686 * our data. As we are a terminal node, this means on aborts
687 * only
688 */
689void
690clientSocketDetach(clientStreamNode * node, clientHttpRequest * http)
691{
692 clientSocketContext *context;
693 /* Test preconditions */
694 assert(node != NULL);
c8be6d7b 695 /* TODO: handle this rather than asserting
696 * - it should only ever happen if we cause an abort and
edce4d98 697 * the callback chain loops back to here, so we can simply return.
698 * However, that itself shouldn't happen, so it stays as an assert for now.
699 */
700 assert(cbdataReferenceValid(node));
701 /* Set null by ContextFree */
702 assert(node->data == NULL);
703 assert(node->node.next == NULL);
e6ccf245 704 context = (clientSocketContext *)node->data;
edce4d98 705 /* We are only called when the client socket shutsdown.
706 * Tell the prev pipeline member we're finished
707 */
708 clientStreamDetach(node, http);
7a2f978b 709}
710
f4f278b5 711static void
e6ccf245 712clientWriteBodyComplete(int fd, char *buf, size_t size, comm_err_t errflag, void *data)
f4f278b5 713{
c8be6d7b 714 clientWriteComplete(fd, NULL, size, errflag, data);
715}
716
717void
718connReadNextRequest(ConnStateData * conn)
719{
720 debug(33, 5) ("clientReadNextRequest: FD %d reading next req\n",
721 conn->fd);
722 fd_note(conn->fd, "Waiting for next request");
f4f278b5 723 /*
c8be6d7b 724 * Set the timeout BEFORE calling clientReadRequest().
725 */
726 commSetTimeout(conn->fd, Config.Timeout.persistent_request,
727 requestTimeout, conn);
c8be6d7b 728 conn->defer.until = 0; /* Kick it to read a new request */
c4b7a5a9 729 /* Make sure we're still reading from the client side! */
730 /* XXX this could take a bit of CPU time! aiee! -- adrian */
731 assert(comm_has_pending_read(conn->fd));
732 /* clientReadSomeData(conn); */
733 /* Please don't do anything with the FD past here! */
c8be6d7b 734}
735
736void
737clientSocketContextPushDeferredIfNeeded(clientSocketContext * deferredRequest, ConnStateData * conn)
738{
739 debug(33, 2) ("clientSocketContextPushDeferredIfNeeded: FD %d Sending next\n",
740 conn->fd);
741 /* If the client stream is waiting on a socket write to occur, then */
742 if (deferredRequest->flags.deferred) {
743 /* NO data is allowed to have been sent */
744 assert(deferredRequest->http->out.size == 0);
745 clientSocketRecipient(deferredRequest->deferredparams.node,
746 deferredRequest->http,
747 deferredRequest->deferredparams.rep,
748 deferredRequest->deferredparams.queuedBuffer);
749 }
750 /* otherwise, the request is still active in a callbacksomewhere,
751 * and we are done
f4f278b5 752 */
f4f278b5 753}
754
21f2031d 755static void
edce4d98 756clientKeepaliveNextRequest(clientSocketContext * context)
1a92a1e2 757{
edce4d98 758 clientHttpRequest *http = context->http;
1a92a1e2 759 ConnStateData *conn = http->conn;
c8be6d7b 760 clientSocketContext *deferredRequest;
bd4e6ec8 761
ebb6e9a0 762 debug(33, 3) ("clientKeepaliveNextRequest: FD %d\n", conn->fd);
edce4d98 763 cbdataFree(context);
c8be6d7b 764 if ((deferredRequest = connGetCurrentContext(conn)) == NULL)
765 connReadNextRequest(conn);
766 else
767 clientSocketContextPushDeferredIfNeeded(deferredRequest, conn);
1a92a1e2 768}
769
c8be6d7b 770void
771clientUpdateSocketStats(log_type logType, size_t size)
772{
773 if (size == 0)
774 return;
775 kb_incr(&statCounter.client_http.kbytes_out, size);
776 if (logTypeIsATcpHit(logType))
777 kb_incr(&statCounter.client_http.hit_kbytes_out, size);
778}
779
780void
781clientPullData(clientSocketContext * context)
782{
783 /* More data will be coming from the stream. */
784 StoreIOBuffer readBuffer = EMPTYIOBUFFER;
785 readBuffer.offset = context->http->out.offset;
786 readBuffer.length = HTTP_REQBUF_SZ;
787 readBuffer.data = context->reqbuf;
788 clientStreamRead(getTail(context), context->http, readBuffer);
789}
edce4d98 790
791/* A write has just completed to the client, or we have just realised there is
792 * no more data to send.
793 */
e6ccf245 794void
795clientWriteComplete(int fd, char *bufnotused, size_t size, comm_err_t errflag, void *data)
7a2f978b 796{
e6ccf245 797 clientSocketContext *context = (clientSocketContext *)data;
edce4d98 798 clientHttpRequest *http = context->http;
7a2f978b 799 StoreEntry *entry = http->entry;
c8be6d7b 800 clientStreamNode *node = getTail(context);
7a2f978b 801 http->out.size += size;
c8be6d7b 802 assert(fd > -1);
32754419 803 debug(33, 5) ("clientWriteComplete: FD %d, sz %ld, err %d, off %ld, len %d\n",
edce4d98 804 fd, (long int) size, errflag, (long int) http->out.size, entry ? objectLen(entry) : 0);
c8be6d7b 805 clientUpdateSocketStats(http->logType, size);
806 if (errflag || clientHttpRequestStatus(fd, http)) {
7a2f978b 807 comm_close(fd);
edce4d98 808 /* Do we leak here ? */
809 return;
810 }
811 switch (clientStreamStatus(node, http)) {
812 case STREAM_NONE:
c8be6d7b 813 clientPullData(context);
edce4d98 814 break;
815 case STREAM_COMPLETE:
816 debug(33, 5) ("clientWriteComplete: FD %d Keeping Alive\n", fd);
817 clientKeepaliveNextRequest(context);
818 return;
819 case STREAM_UNPLANNED_COMPLETE:
820 /* fallthrough */
821 case STREAM_FAILED:
c8be6d7b 822 comm_close(fd);
edce4d98 823 return;
824 default:
825 fatal("Hit unreachable code in clientWriteComplete\n");
7a2f978b 826 }
827}
828
e6ccf245 829extern "C" CSR clientGetMoreData;
830extern "C" CSS clientReplyStatus;
831extern "C" CSD clientReplyDetach;
edce4d98 832
833static clientSocketContext *
834parseHttpRequestAbort(ConnStateData * conn, const char *uri)
038eb4ed 835{
edce4d98 836 clientHttpRequest *http;
837 clientSocketContext *context;
c8be6d7b 838 StoreIOBuffer tempBuffer = EMPTYIOBUFFER;
edce4d98 839 http = cbdataAlloc(clientHttpRequest);
840 http->conn = conn;
841 http->start = current_time;
c8be6d7b 842 http->req_sz = conn->in.notYetUsed;
edce4d98 843 http->uri = xstrdup(uri);
c4b7a5a9 844 setLogUri (http, uri);
edce4d98 845 context = clientSocketContextNew(http);
c8be6d7b 846 tempBuffer.data = context->reqbuf;
847 tempBuffer.length = HTTP_REQBUF_SZ;
edce4d98 848 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
849 clientReplyStatus, clientReplyNewContext(http), clientSocketRecipient,
c8be6d7b 850 clientSocketDetach, context, tempBuffer);
edce4d98 851 dlinkAdd(http, &http->active, &ClientActiveRequests);
852 return context;
038eb4ed 853}
854
c8be6d7b 855clientSocketContext *
856clientCheckRequestLineIsParseable(char *inbuf, size_t req_sz, ConnStateData * conn)
50ddd7a4 857{
edce4d98 858 if (strlen(inbuf) != req_sz) {
c8be6d7b 859 debug(33, 1) ("clientCheckRequestLineIsParseable: Requestheader contains NULL characters\n");
edce4d98 860 return parseHttpRequestAbort(conn, "error:invalid-request");
1b95ce49 861 }
c8be6d7b 862 return NULL;
863}
864
865clientSocketContext *
866clientParseRequestMethod(char *inbuf, method_t * method_p, ConnStateData * conn)
867{
868 char *mstr = NULL;
edce4d98 869 if ((mstr = strtok(inbuf, "\t ")) == NULL) {
c8be6d7b 870 debug(33, 1) ("clientParseRequestMethod: Can't get request method\n");
edce4d98 871 return parseHttpRequestAbort(conn, "error:invalid-request-method");
7ddc902f 872 }
edce4d98 873 *method_p = urlParseMethod(mstr);
874 if (*method_p == METHOD_NONE) {
c8be6d7b 875 debug(33, 1) ("clientParseRequestMethod: Unsupported method '%s'\n", mstr);
edce4d98 876 return parseHttpRequestAbort(conn, "error:unsupported-request-method");
50ddd7a4 877 }
c8be6d7b 878 debug(33, 5) ("clientParseRequestMethod: Method is '%s'\n", mstr);
879 return NULL;
880}
881
882char *
883skipLeadingSpace(char *aString)
884{
885 char *result = aString;
886 while (xisspace(*aString))
887 ++aString;
888 return result;
889}
890
891char *
892findTrailingHTTPVersion(char *uriAndHTTPVersion)
893{
894 char *token = uriAndHTTPVersion + strlen(uriAndHTTPVersion);
895 assert(*token == '\0');
896 while (token > uriAndHTTPVersion) {
897 --token;
898 if (xisspace(*token) && !strncmp(token + 1, "HTTP/", 5))
899 return token + 1;
900 }
901 return uriAndHTTPVersion;
902}
903
904void
905trimTrailingSpaces(char *aString, size_t len)
906{
907 char *endPointer = aString + len;
908 while (endPointer > aString && xisspace(*endPointer))
909 *(endPointer--) = '\0';
910}
50ddd7a4 911
c8be6d7b 912clientSocketContext *
913parseURIandHTTPVersion(char **url_p, http_version_t * http_ver_p,
914 ConnStateData * conn)
915{
916 char *url;
917 char *token;
edce4d98 918 /* look for URL+HTTP/x.x */
919 if ((url = strtok(NULL, "\n")) == NULL) {
920 debug(33, 1) ("parseHttpRequest: Missing URL\n");
921 return parseHttpRequestAbort(conn, "error:missing-url");
7a2f978b 922 }
c8be6d7b 923 url = skipLeadingSpace(url);
924 token = findTrailingHTTPVersion(url);
925 trimTrailingSpaces(url, token - url - 1);
926
edce4d98 927 debug(33, 5) ("parseHttpRequest: URI is '%s'\n", url);
928 *url_p = url;
929 if (token == NULL) {
930 debug(33, 3) ("parseHttpRequest: Missing HTTP identifier\n");
931#if RELAXED_HTTP_PARSER
932 httpBuildVersion(http_ver_p, 0, 9); /* wild guess */
933#else
934 return parseHttpRequestAbort(conn, "error:missing-http-ident");
2b906e48 935#endif
49d3fcb0 936 } else {
edce4d98 937 if (sscanf(token + 5, "%d.%d", &http_ver_p->major,
938 &http_ver_p->minor) != 2) {
939 debug(33, 3) ("parseHttpRequest: Invalid HTTP identifier.\n");
940 return parseHttpRequestAbort(conn, "error: invalid HTTP-ident");
d20b1cd0 941 }
edce4d98 942 debug(33, 6) ("parseHttpRequest: Client HTTP version %d.%d.\n",
943 http_ver_p->major, http_ver_p->minor);
6d38ef86 944 }
c8be6d7b 945 return NULL;
946}
947
948/* Utility function to perform part of request parsing */
949static clientSocketContext *
950clientParseHttpRequestLine(char *inbuf, size_t req_sz, ConnStateData * conn,
951 method_t * method_p, char **url_p, http_version_t * http_ver_p)
952{
953 clientSocketContext *result = NULL;
954 if ((result = clientCheckRequestLineIsParseable(inbuf, req_sz, conn))
955 || (result = clientParseRequestMethod(inbuf, method_p, conn))
956 || (result = parseURIandHTTPVersion(url_p, http_ver_p, conn)))
957 return result;
7a2f978b 958
edce4d98 959 return NULL;
99edd1c3 960}
961
c8be6d7b 962void
c4b7a5a9 963setLogUri(clientHttpRequest * http, char const *uri)
c8be6d7b 964{
965 if (!stringHasCntl(uri))
966 http->log_uri = xstrndup(uri, MAX_URL);
967 else
968 http->log_uri = xstrndup(rfc1738_escape_unescaped(uri), MAX_URL);
969}
970
971void
972prepareInternalUrl(clientHttpRequest * http, char *url)
973{
974 http->uri = xstrdup(internalLocalUri(NULL, url));
975 http->flags.internal = 1;
976 http->flags.accel = 1;
977}
978
979void
980prepareForwardProxyUrl(clientHttpRequest * http, char *url)
981{
982 size_t url_sz;
983 /* URL may be rewritten later, so make extra room */
984 url_sz = strlen(url) + Config.appendDomainLen + 5;
e6ccf245 985 http->uri = (char *)xcalloc(url_sz, 1);
c8be6d7b 986 strcpy(http->uri, url);
987 http->flags.accel = 0;
988}
989
990void
991prepareAcceleratedUrl(clientHttpRequest * http, char *url, char *req_hdr)
992{
993 size_t url_sz;
994 char *t;
995 /* prepend the accel prefix */
996 if (opt_accel_uses_host && (t = mime_get_header(req_hdr, "Host"))) {
997 int vport;
998 char *q;
999 const char *protocol_name = "http";
1000 if (vport_mode)
1001 vport = (int) ntohs(http->conn->me.sin_port);
1002 else
1003 vport = (int) Config.Accel.port;
1004 /* If a Host: header was specified, use it to build the URL
1005 * instead of the one in the Config file. */
1006 /*
1007 * XXX Use of the Host: header here opens a potential
1008 * security hole. There are no checks that the Host: value
1009 * corresponds to one of your servers. It might, for example,
1010 * refer to www.playboy.com. The 'dst' and/or 'dst_domain' ACL
1011 * types should be used to prevent httpd-accelerators
1012 * handling requests for non-local servers */
1013 strtok(t, " /;@");
1014 if ((q = strchr(t, ':'))) {
1015 *q++ = '\0';
1016 if (vport_mode)
1017 vport = atoi(q);
1018 }
1019 url_sz = strlen(url) + 32 + Config.appendDomainLen + strlen(t);
e6ccf245 1020 http->uri = (char *)xcalloc(url_sz, 1);
c8be6d7b 1021
1022#if SSL_FORWARDING_NOT_YET_DONE
1023 if (Config.Sockaddr.https->s.sin_port == http->conn->me.sin_port) {
1024 protocol_name = "https";
1025 vport = ntohs(http->conn->me.sin_port);
1026 }
1027#endif
1028 snprintf(http->uri, url_sz, "%s://%s:%d%s",
1029 protocol_name, t, vport, url);
1030 } else if (vhost_mode) {
1031 int vport;
1032 /* Put the local socket IP address as the hostname */
1033 url_sz = strlen(url) + 32 + Config.appendDomainLen;
e6ccf245 1034 http->uri = (char *)xcalloc(url_sz, 1);
c8be6d7b 1035 if (vport_mode)
1036 vport = (int) ntohs(http->conn->me.sin_port);
1037 else
1038 vport = (int) Config.Accel.port;
f95db407 1039 rewriteURIwithInterceptedDetails(url, http->uri, url_sz, http->conn->fd,
c8be6d7b 1040 http->conn->me, http->conn->peer, vport);
1041 debug(33, 5) ("VHOST REWRITE: '%s'\n", http->uri);
1042 } else {
1043 url_sz = strlen(Config2.Accel.prefix) + strlen(url) +
1044 Config.appendDomainLen + 1;
e6ccf245 1045 http->uri = (char *)xcalloc(url_sz, 1);
c8be6d7b 1046 snprintf(http->uri, url_sz, "%s%s", Config2.Accel.prefix, url);
1047 }
1048 http->flags.accel = 1;
1049}
1050
7a2f978b 1051/*
1052 * parseHttpRequest()
1053 *
1054 * Returns
c4b7a5a9 1055 * NULL on incomplete requests
1056 * a clientSocketContext structure on success or failure.
1057 * Sets result->flags.parsed_ok to 0 if failed to parse the request.
1058 * Sets result->flags.parsed_ok to 1 if we have a good request.
7a2f978b 1059 */
edce4d98 1060static clientSocketContext *
c4b7a5a9 1061parseHttpRequest(ConnStateData * conn, method_t * method_p,
ea285003 1062 char **prefix_p, size_t * req_line_sz_p)
7a2f978b 1063{
1064 char *inbuf = NULL;
7a2f978b 1065 char *url = NULL;
1066 char *req_hdr = NULL;
ccf44862 1067 http_version_t http_ver;
2334c194 1068 char *end;
7a2f978b 1069 size_t header_sz; /* size of headers, not including first line */
ea285003 1070 size_t prefix_sz; /* size of whole request (req-line + headers) */
c68e9c6b 1071 size_t req_sz;
edce4d98 1072 clientHttpRequest *http;
c8be6d7b 1073 clientSocketContext *result;
1074 StoreIOBuffer tempBuffer = EMPTYIOBUFFER;
7a2f978b 1075
6792f0d3 1076 /* pre-set these values to make aborting simpler */
1077 *prefix_p = NULL;
1078 *method_p = METHOD_NONE;
6792f0d3 1079
c8be6d7b 1080 if ((req_sz = headersEnd(conn->in.buf, conn->in.notYetUsed)) == 0) {
c68e9c6b 1081 debug(33, 5) ("Incomplete request, waiting for end of headers\n");
7a2f978b 1082 return NULL;
1083 }
c8be6d7b 1084 assert(req_sz <= conn->in.notYetUsed);
c68e9c6b 1085 /* Use memcpy, not strdup! */
e6ccf245 1086 inbuf = (char *)xmalloc(req_sz + 1);
c68e9c6b 1087 xmemcpy(inbuf, conn->in.buf, req_sz);
1088 *(inbuf + req_sz) = '\0';
7a2f978b 1089
edce4d98 1090 /* Is there a legitimate first line to the headers ? */
c8be6d7b 1091 if ((result =
edce4d98 1092 clientParseHttpRequestLine(inbuf, req_sz, conn, method_p, &url,
1093 &http_ver))) {
1094 /* something wrong, abort */
c797472e 1095 xfree(inbuf);
c8be6d7b 1096 return result;
99edd1c3 1097 }
c68e9c6b 1098 /*
1099 * Process headers after request line
c8be6d7b 1100 * TODO: Use httpRequestParse here.
c68e9c6b 1101 */
1102 req_hdr = strtok(NULL, null_string);
1103 header_sz = req_sz - (req_hdr - inbuf);
2334c194 1104 if (0 == header_sz) {
1a92a1e2 1105 debug(33, 3) ("parseHttpRequest: header_sz == 0\n");
c797472e 1106 xfree(inbuf);
7a2f978b 1107 return NULL;
1108 }
754b9ce9 1109 assert(header_sz > 0);
1110 debug(33, 3) ("parseHttpRequest: req_hdr = {%s}\n", req_hdr);
2334c194 1111 end = req_hdr + header_sz;
04990e2d 1112 debug(33, 3) ("parseHttpRequest: end = {%s}\n", end);
99edd1c3 1113
99edd1c3 1114 prefix_sz = end - inbuf;
1115 *req_line_sz_p = req_hdr - inbuf;
ea285003 1116 debug(33, 3) ("parseHttpRequest: prefix_sz = %d, req_line_sz = %d\n",
99edd1c3 1117 (int) prefix_sz, (int) *req_line_sz_p);
c8be6d7b 1118 assert(prefix_sz <= conn->in.notYetUsed);
7a2f978b 1119
1120 /* Ok, all headers are received */
72711e31 1121 http = cbdataAlloc(clientHttpRequest);
7a2f978b 1122 http->http_ver = http_ver;
1123 http->conn = conn;
1124 http->start = current_time;
99edd1c3 1125 http->req_sz = prefix_sz;
c8be6d7b 1126 result = clientSocketContextNew(http);
1127 tempBuffer.data = result->reqbuf;
1128 tempBuffer.length = HTTP_REQBUF_SZ;
edce4d98 1129 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
1130 clientReplyStatus, clientReplyNewContext(http), clientSocketRecipient,
c8be6d7b 1131 clientSocketDetach, result, tempBuffer);
e6ccf245 1132 *prefix_p = (char *)xmalloc(prefix_sz + 1);
99edd1c3 1133 xmemcpy(*prefix_p, conn->in.buf, prefix_sz);
1134 *(*prefix_p + prefix_sz) = '\0';
0f1bc304 1135 dlinkAdd(http, &http->active, &ClientActiveRequests);
7a2f978b 1136
edce4d98 1137 /* XXX this function is still way to long. here is a natural point for further simplification */
1138
1139 debug(33, 5) ("parseHttpRequest: Request Header is\n%s\n",
1140 (*prefix_p) + *req_line_sz_p);
ba9ebd0a 1141#if THIS_VIOLATES_HTTP_SPECS_ON_URL_TRANSFORMATION
7a2f978b 1142 if ((t = strchr(url, '#'))) /* remove HTML anchors */
1143 *t = '\0';
ba9ebd0a 1144#endif
7a2f978b 1145
c8be6d7b 1146 if (internalCheck(url))
1147 prepareInternalUrl(http, url);
1148 else if (Config2.Accel.on && *url == '/')
1149 prepareAcceleratedUrl(http, url, req_hdr);
d548ee64 1150 else
c8be6d7b 1151 prepareForwardProxyUrl(http, url);
1152 setLogUri(http, http->uri);
93f9abd0 1153 debug(33, 5) ("parseHttpRequest: Complete request received\n");
7a2f978b 1154 xfree(inbuf);
c4b7a5a9 1155 result->flags.parsed_ok = 1;
c8be6d7b 1156 return result;
7a2f978b 1157}
1158
1159static int
79d39a72 1160clientReadDefer(int fdnotused, void *data)
7a2f978b 1161{
e6ccf245 1162 ConnStateData *conn = (ConnStateData *)data;
94439e4e 1163 if (conn->body.size_left)
c8be6d7b 1164 return conn->in.notYetUsed >= conn->in.allocatedSize - 1;
94439e4e 1165 else
1166 return conn->defer.until > squid_curtime;
7a2f978b 1167}
1168
c8be6d7b 1169int
1170connGetAvailableBufferLength(ConnStateData const *conn)
1171{
1172 return conn->in.allocatedSize - conn->in.notYetUsed;
1173}
1174
1175void
1176connMakeSpaceAvailable(ConnStateData * conn)
1177{
1178 if (connGetAvailableBufferLength(conn) < 2) {
e6ccf245 1179 conn->in.buf = (char *)memReallocBuf(conn->in.buf, conn->in.allocatedSize * 2, &conn->in.allocatedSize);
c8be6d7b 1180 debug(33, 2) ("growing request buffer: notYetUsed=%ld size=%ld\n",
1181 (long) conn->in.notYetUsed, (long) conn->in.allocatedSize);
1182 }
1183}
1184
1185void
1186connAddContextToQueue(ConnStateData * conn, clientSocketContext * context)
1187{
1188 clientSocketContext **S;
1189 for (S = (clientSocketContext **) & conn->currentobject; *S;
1190 S = &(*S)->next);
1191 *S = context;
1192 ++conn->nrequests;
1193}
1194
1195int
1196connGetConcurrentRequestCount(ConnStateData * conn)
1197{
1198 int result = 0;
1199 clientSocketContext **T;
1200 for (T = (clientSocketContext **) & conn->currentobject;
1201 *T; T = &(*T)->next, ++result);
1202 return result;
1203}
1204
1205int
c4b7a5a9 1206connReadWasError(ConnStateData * conn, comm_err_t flag, int size)
c8be6d7b 1207{
c4b7a5a9 1208 if (flag != COMM_OK) {
1209 debug(50, 2) ("connReadWasError: FD %d: got flag %d\n", conn->fd, flag);
1210 return 1;
1211 }
c8be6d7b 1212 if (size < 0) {
1213 if (!ignoreErrno(errno)) {
1214 debug(50, 2) ("connReadWasError: FD %d: %s\n", conn->fd, xstrerror());
1215 return 1;
1216 } else if (conn->in.notYetUsed == 0) {
1217 debug(50, 2) ("connReadWasError: FD %d: no data to process (%s)\n",
1218 conn->fd, xstrerror());
1219 }
1220 }
1221 return 0;
1222}
1223
1224int
1225connFinishedWithConn(ConnStateData * conn, int size)
1226{
1227 if (size == 0) {
1228 if (connGetConcurrentRequestCount(conn) == 0 && conn->in.notYetUsed == 0) {
1229 /* no current or pending requests */
1230 debug(33, 4) ("connFinishedWithConn: FD %d closed\n", conn->fd);
1231 return 1;
1232 } else if (!Config.onoff.half_closed_clients) {
1233 /* admin doesn't want to support half-closed client sockets */
1234 debug(33, 3) ("connFinishedWithConn: FD %d aborted (half_closed_clients disabled)\n", conn->fd);
1235 return 1;
1236 }
1237 }
1238 return 0;
1239}
1240
1241void
e6ccf245 1242connNoteUseOfBuffer(ConnStateData * conn, size_t byteCount)
c8be6d7b 1243{
1244 assert(byteCount > 0 && byteCount <= conn->in.notYetUsed);
1245 conn->in.notYetUsed -= byteCount;
e6ccf245 1246 debug(33, 5) ("conn->in.notYetUsed = %u\n", (unsigned) conn->in.notYetUsed);
c8be6d7b 1247 /*
1248 * If there is still data that will be used,
1249 * move it to the beginning.
1250 */
1251 if (conn->in.notYetUsed > 0)
1252 xmemmove(conn->in.buf, conn->in.buf + byteCount,
1253 conn->in.notYetUsed);
1254}
1255
1256int
1257connKeepReadingIncompleteRequest(ConnStateData * conn)
1258{
1259 return conn->in.notYetUsed >= Config.maxRequestHeaderSize ? 0 : 1;
1260}
1261
1262void
1263connCancelIncompleteRequests(ConnStateData * conn)
1264{
1265 clientSocketContext *context = parseHttpRequestAbort(conn, "error:request-too-large");
1266 clientStreamNode *node = getClientReplyContext(context);
1267 assert(!connKeepReadingIncompleteRequest(conn));
e6ccf245 1268 debug(33, 1) ("Request header is too large (%u bytes)\n",
1269 (unsigned) conn->in.notYetUsed);
c8be6d7b 1270 debug(33, 1) ("Config 'request_header_max_size'= %ld bytes.\n",
1271 (long int) Config.maxRequestHeaderSize);
1272 clientSetReplyToError(node->data, ERR_TOO_BIG,
1273 HTTP_REQUEST_ENTITY_TOO_LARGE, METHOD_NONE, NULL,
1274 &conn->peer.sin_addr, NULL, NULL, NULL);
1275 connAddContextToQueue(conn, context);
1276 clientPullData(context);
1277}
1278
7a2f978b 1279static void
c4b7a5a9 1280clientMaybeReadData(ConnStateData *conn, int do_next_read)
7a2f978b 1281{
c4b7a5a9 1282 if (do_next_read) {
1283 conn->flags.readMoreRequests = 1;
1284 clientReadSomeData(conn);
1285 }
1286}
1287
1288static void
1289clientAfterReadingRequests(int fd, ConnStateData *conn, int do_next_read)
1290{
1291 fde *F = &fd_table[fd];
1292
1293 /* Check if a half-closed connection was aborted in the middle */
1294 if (F->flags.socket_eof) {
1295 if (conn->in.notYetUsed != conn->body.size_left) { /* != 0 when no
1296request body */
1297 /* Partial request received. Abort client connection! */
1298 debug(33, 3) ("clientReadRequest: FD %d aborted, partial request\n",+ fd);
1299 comm_close(fd);
1300 return;
1301 }
1302 }
1303
1304 clientMaybeReadData (conn, do_next_read);
1305}
1306
1307
1308static void
1309clientProcessRequest(ConnStateData *conn, clientSocketContext *context, method_t method, char *prefix, size_t req_line_sz)
1310{
1311 clientHttpRequest *http = context->http;
7a2f978b 1312 request_t *request = NULL;
c4b7a5a9 1313 /* We have an initial client stream in place should it be needed */
1314 /* setup our private context */
1315 connNoteUseOfBuffer(conn, http->req_sz);
1316
1317 connAddContextToQueue(conn, context);
1318
1319 if (context->flags.parsed_ok == 0) {
1320 clientStreamNode *node = getClientReplyContext(context);
1321 debug(33, 1) ("clientReadRequest: Invalid Request\n");
1322 clientSetReplyToError(node->data,
1323 ERR_INVALID_REQ, HTTP_BAD_REQUEST, method, NULL,
1324 &conn->peer.sin_addr, NULL, conn->in.buf, NULL);
1325 assert(context->http->out.offset == 0);
1326 clientPullData(context);
1327 conn->flags.readMoreRequests = 0;
1328 return;
1329 }
1330
1331 if ((request = urlParse(method, http->uri)) == NULL) {
1332 clientStreamNode *node = getClientReplyContext(context);
1333 debug(33, 5) ("Invalid URL: %s\n", http->uri);
1334 clientSetReplyToError(node->data,
1335 ERR_INVALID_URL, HTTP_BAD_REQUEST, method, http->uri,
1336 &conn->peer.sin_addr, NULL, NULL, NULL);
1337 assert(context->http->out.offset == 0);
1338 clientPullData(context);
1339 conn->flags.readMoreRequests = 0;
1340 return;
1341 } else {
1342
1343 /* compile headers */
1344 /* we should skip request line! */
1345 if (!httpRequestParseHeader(request, prefix + req_line_sz))
1346 debug(33, 1) ("Failed to parse request headers: %s\n%s\n",
1347 http->uri, prefix);
1348 /* continue anyway? */
1349 }
1350
1351 request->flags.accelerated = http->flags.accel;
1352 if (!http->flags.internal) {
1353 if (internalCheck(strBuf(request->urlpath))) {
1354 if (internalHostnameIs(request->host) &&
1355 request->port == getMyPort()) {
1356 http->flags.internal = 1;
1357 } else if (internalStaticCheck(strBuf(request->urlpath))) {
1358 xstrncpy(request->host, internalHostname(),
1359 SQUIDHOSTNAMELEN);
1360 request->port = getMyPort();
1361 http->flags.internal = 1;
1362 }
1363 }
1364 }
1365
1366 /*
1367 * cache the Content-length value in request_t.
1368 */
1369 request->content_length = httpHeaderGetInt(&request->header,
1370 HDR_CONTENT_LENGTH);
1371 request->flags.internal = http->flags.internal;
1372 setLogUri (http, urlCanonicalClean(request));
1373 request->client_addr = conn->peer.sin_addr;
1374 request->my_addr = conn->me.sin_addr;
1375 request->my_port = ntohs(conn->me.sin_port);
1376 request->http_ver = http->http_ver;
1377 if (!urlCheckRequest(request) ||
1378 httpHeaderHas(&request->header, HDR_TRANSFER_ENCODING)) {
1379 clientStreamNode *node = getClientReplyContext(context);
1380 clientSetReplyToError(node->data, ERR_UNSUP_REQ,
1381 HTTP_NOT_IMPLEMENTED, request->method, NULL,
1382 &conn->peer.sin_addr, request, NULL, NULL);
1383 assert(context->http->out.offset == 0);
1384 clientPullData(context);
1385 conn->flags.readMoreRequests = 0;
1386 return;
1387 }
1388
1389
1390 if (!clientIsContentLengthValid(request)) {
1391 clientStreamNode *node = getClientReplyContext(context);
1392 clientSetReplyToError(node->data, ERR_INVALID_REQ,
1393 HTTP_LENGTH_REQUIRED, request->method, NULL,
1394 &conn->peer.sin_addr, request, NULL, NULL);
1395 assert(context->http->out.offset == 0);
1396 clientPullData(context);
1397 conn->flags.readMoreRequests = 0;
1398 return;
1399 }
1400
1401 http->request = requestLink(request);
1402 clientSetKeepaliveFlag(http);
1403 /* Do we expect a request-body? */
1404 if (request->content_length > 0) {
1405 conn->body.size_left = request->content_length;
1406 request->body_connection = conn;
1407 /* Is it too large? */
1408 if (!clientIsRequestBodyValid(request->content_length) ||
1409 clientIsRequestBodyTooLargeForPolicy(request->content_length)) {
1410 clientStreamNode *node = getClientReplyContext(context);
1411 clientSetReplyToError(node->data, ERR_TOO_BIG,
1412 HTTP_REQUEST_ENTITY_TOO_LARGE, METHOD_NONE, NULL,
1413 &conn->peer.sin_addr, http->request, NULL, NULL);
1414 assert(context->http->out.offset == 0);
1415 clientPullData(context);
1416 conn->flags.readMoreRequests = 0;
1417 return;
1418 }
1419 }
1420
1421 /* If this is a CONNECT, don't schedule a read - ssl.c will handle it */
1422 if (http->request->method == METHOD_CONNECT)
1423 context->flags.mayUseConnection = 1;
1424 clientAccessCheck(http);
1425}
1426
1427static void
1428connStripBufferWhitespace (ConnStateData *conn)
1429{
1430 while (conn->in.notYetUsed > 0 && xisspace(conn->in.buf[0])) {
1431 xmemmove(conn->in.buf, conn->in.buf + 1, conn->in.notYetUsed - 1);
1432 --conn->in.notYetUsed;
1433 }
1434}
1435
1436static int
1437connOkToAddRequest(ConnStateData *conn)
1438{
1439 int result = connGetConcurrentRequestCount(conn) < (Config.onoff.pipeline_prefetch ? 2 : 1);
1440 if (!result) {
1441 debug(33, 3) ("clientReadRequest: FD %d max concurrent requests reached\n",
1442 conn->fd);
1443 debug(33, 5) ("clientReadRequest: FD %d defering new request until one is done\n",
1444 conn->fd);
1445 }
1446 return result;
1447}
1448
1449static void
1450connSetDefer (ConnStateData *conn, size_t milliSeconds)
1451{
1452 conn->defer.until = squid_curtime + milliSeconds;
1453 conn->defer.n++;
1454}
1455
1456static void
1457clientReadRequest(int fd, char *buf, size_t size, comm_err_t flag, int xerrno,
1458void *data)
1459{
1460 ConnStateData *conn = (ConnStateData *)data;
7a2f978b 1461 method_t method;
08e70b8f 1462 char *prefix = NULL;
edce4d98 1463 clientSocketContext *context;
c4b7a5a9 1464 int do_next_read = 1; /* the default _is_ to read data! - adrian */
1465
1466 assert (fd == conn->fd);
1467
1468 /* Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up */
1469 if (flag == COMM_ERR_CLOSING) {
1470 return;
1471 }
44db45e8 1472 /*
1473 * Don't reset the timeout value here. The timeout value will be
1474 * set to Config.Timeout.request by httpAccept() and
1475 * clientWriteComplete(), and should apply to the request as a
1476 * whole, not individual read() calls. Plus, it breaks our
1477 * lame half-close detection
1478 */
c4b7a5a9 1479 if (connReadWasError(conn, flag, size)) {
c8be6d7b 1480 comm_close(fd);
1481 return;
7a2f978b 1482 }
c4b7a5a9 1483
1484 if (flag == COMM_OK) {
1485 if (size > 0) {
1486 kb_incr(&statCounter.client_http.kbytes_in, size);
1487 conn->in.notYetUsed += size;
1488 conn->in.buf[conn->in.notYetUsed] = '\0'; /* Terminate the string
1489*/
1490 } else if (size == 0) {
1491 debug(33, 5) ("clientReadRequest: FD %d closed?\n", fd);
1492 if (connFinishedWithConn(conn, size)) {
1493 comm_close(fd);
1494 return;
1495 }
1496 /* It might be half-closed, we can't tell */
1497 fd_table[fd].flags.socket_eof = 1;
1498 connSetDefer (conn, 1);
1499 fd_note(fd, "half-closed");
1500 /* There is one more close check at the end, to detect aborted
1501 * (partial) requests. At this point we can't tell if the request
1502 * is partial.
1503 */
1504 /* Continue to process previously read data */
1505 }
1506 }
1507
1508
94439e4e 1509 /* Process request body if any */
c8be6d7b 1510 if (conn->in.notYetUsed > 0 && conn->body.callback != NULL)
94439e4e 1511 clientProcessBody(conn);
1512 /* Process next request */
c8be6d7b 1513 if (connGetConcurrentRequestCount(conn) == 0)
1514 fd_note(conn->fd, "Reading next request");
1515
1516 while (conn->in.notYetUsed > 0 && conn->body.size_left == 0) {
cff0c749 1517 size_t req_line_sz;
c4b7a5a9 1518 connStripBufferWhitespace (conn);
1519 if (conn->in.notYetUsed == 0) {
1520 clientAfterReadingRequests(fd, conn, do_next_read);
1521 return;
9a6f98a5 1522 }
ebf4efff 1523 /* Limit the number of concurrent requests to 2 */
c4b7a5a9 1524 if (!connOkToAddRequest(conn)) {
1525 /* Reset when a request is complete */
1526 connSetDefer (conn, 100);
1527 clientMaybeReadData (conn, do_next_read);
380991a7 1528 return;
ebf4efff 1529 }
c4b7a5a9 1530 /* Should not be needed anymore */
1531 /* Terminate the string */
1532 conn->in.buf[conn->in.notYetUsed] = '\0';
ebf4efff 1533 /* Process request */
edce4d98 1534 context = parseHttpRequest(conn,
c4b7a5a9 1535 &method, &prefix, &req_line_sz);
1536 /* partial or incomplete request */
1537 if (!context) {
13f11a4a 1538 safe_free(prefix);
c4b7a5a9 1539 if (!connKeepReadingIncompleteRequest(conn))
1540 connCancelIncompleteRequests(conn);
1541 break; /* conn->in.notYetUsed > 0 && conn->body.size_left == 0 */
1542 }
c8be6d7b 1543
c4b7a5a9 1544 /* status -1 or 1 */
1545 if (context) {
edce4d98 1546 commSetTimeout(fd, Config.Timeout.lifetime, clientLifetimeTimeout,
c4b7a5a9 1547 context->http);
1548
1549 clientProcessRequest(conn, context, method, prefix, req_line_sz);
1550
13f11a4a 1551 safe_free(prefix);
c4b7a5a9 1552 if (!conn->flags.readMoreRequests) {
1553 conn->flags.readMoreRequests = 1;
31be8b80 1554 break;
1555 }
c4b7a5a9 1556 if (context->flags.mayUseConnection)
1557 do_next_read = 0;
94439e4e 1558 continue; /* while offset > 0 && body.size_left == 0 */
7a2f978b 1559 }
94439e4e 1560 } /* while offset > 0 && conn->body.size_left == 0 */
c4b7a5a9 1561 clientAfterReadingRequests(fd, conn, do_next_read);
94439e4e 1562}
1563
1564/* file_read like function, for reading body content */
1565void
edce4d98 1566clientReadBody(request_t * request, char *buf, size_t size, CBCB * callback,
1567 void *cbdata)
94439e4e 1568{
1569 ConnStateData *conn = request->body_connection;
1570 if (!conn) {
1571 debug(33, 5) ("clientReadBody: no body to read, request=%p\n", request);
1572 callback(buf, 0, cbdata); /* Signal end of body */
1573 return;
7a2f978b 1574 }
c8be6d7b 1575 debug(33, 2) ("clientReadBody: start fd=%d body_size=%lu in.notYetUsed=%ld cb=%p req=%p\n",
edce4d98 1576 conn->fd, (unsigned long int) conn->body.size_left,
e6ccf245 1577 (unsigned long int) conn->in.notYetUsed, callback, request);
94439e4e 1578 conn->body.callback = callback;
1579 conn->body.cbdata = cbdata;
1580 conn->body.buf = buf;
1581 conn->body.bufsize = size;
1582 conn->body.request = requestLink(request);
28ee8ce5 1583 clientProcessBody(conn);
94439e4e 1584}
1585
1586/* Called by clientReadRequest to process body content */
1587static void
1588clientProcessBody(ConnStateData * conn)
1589{
e6ccf245 1590 size_t size;
94439e4e 1591 char *buf = conn->body.buf;
1592 void *cbdata = conn->body.cbdata;
1593 CBCB *callback = conn->body.callback;
1594 request_t *request = conn->body.request;
1595 /* Note: request is null while eating "aborted" transfers */
e6ccf245 1596 debug(33, 2) ("clientProcessBody: start fd=%d body_size=%lu in.notYetUsed=%lu cb=%p req=%p\n",
edce4d98 1597 conn->fd, (unsigned long int) conn->body.size_left,
e6ccf245 1598 (unsigned long int) conn->in.notYetUsed, callback, request);
c8be6d7b 1599 if (conn->in.notYetUsed) {
28ee8ce5 1600 /* Some sanity checks... */
1601 assert(conn->body.size_left > 0);
c8be6d7b 1602 assert(conn->in.notYetUsed > 0);
28ee8ce5 1603 assert(callback != NULL);
1604 assert(buf != NULL);
1605 /* How much do we have to process? */
c8be6d7b 1606 size = conn->in.notYetUsed;
28ee8ce5 1607 if (size > conn->body.size_left) /* only process the body part */
1608 size = conn->body.size_left;
1609 if (size > conn->body.bufsize) /* don't copy more than requested */
1610 size = conn->body.bufsize;
1611 xmemcpy(buf, conn->in.buf, size);
1612 conn->body.size_left -= size;
1613 /* Move any remaining data */
c8be6d7b 1614 conn->in.notYetUsed -= size;
1615 if (conn->in.notYetUsed > 0)
1616 xmemmove(conn->in.buf, conn->in.buf + size, conn->in.notYetUsed);
28ee8ce5 1617 /* Remove request link if this is the last part of the body, as
1618 * clientReadRequest automatically continues to process next request */
1619 if (conn->body.size_left <= 0 && request != NULL)
1620 request->body_connection = NULL;
1621 /* Remove clientReadBody arguments (the call is completed) */
1622 conn->body.request = NULL;
1623 conn->body.callback = NULL;
1624 conn->body.buf = NULL;
1625 conn->body.bufsize = 0;
1626 /* Remember that we have touched the body, not restartable */
1627 if (request != NULL)
1628 request->flags.body_sent = 1;
1629 /* Invoke callback function */
1630 callback(buf, size, cbdata);
1631 if (request != NULL)
1632 requestUnlink(request); /* Linked in clientReadBody */
2167ca34 1633 debug(33, 2) ("clientProcessBody: end fd=%d size=%lu body_size=%lu in.notYetUsed=%lu cb=%p req=%p\n",
1634 conn->fd, (unsigned long int)size, (unsigned long int) conn->body.size_left,
e6ccf245 1635 (unsigned long) conn->in.notYetUsed, callback, request);
28ee8ce5 1636 }
94439e4e 1637}
1638
1639/* A dummy handler that throws away a request-body */
2d72d4fd 1640static void
e6ccf245 1641clientReadBodyAbortHandler(char *buf, ssize_t size, void *data)
94439e4e 1642{
c8be6d7b 1643 static char bodyAbortBuf[SQUID_TCP_SO_RCVBUF];
94439e4e 1644 ConnStateData *conn = (ConnStateData *) data;
e6ccf245 1645 debug(33, 2) ("clientReadBodyAbortHandler: fd=%d body_size=%lu in.notYetUsed=%lu\n",
edce4d98 1646 conn->fd, (unsigned long int) conn->body.size_left,
e6ccf245 1647 (unsigned long) conn->in.notYetUsed);
94439e4e 1648 if (size != 0 && conn->body.size_left != 0) {
edce4d98 1649 debug(33, 3) ("clientReadBodyAbortHandler: fd=%d shedule next read\n",
1650 conn->fd);
94439e4e 1651 conn->body.callback = clientReadBodyAbortHandler;
1652 conn->body.buf = bodyAbortBuf;
1653 conn->body.bufsize = sizeof(bodyAbortBuf);
1654 conn->body.cbdata = data;
1655 }
1656}
1657
1658/* Abort a body request */
1659int
1660clientAbortBody(request_t * request)
1661{
1662 ConnStateData *conn = request->body_connection;
1663 char *buf;
1664 CBCB *callback;
1665 void *cbdata;
1666 request->body_connection = NULL;
1667 if (!conn || conn->body.size_left <= 0)
1668 return 0; /* No body to abort */
1669 if (conn->body.callback != NULL) {
1670 buf = conn->body.buf;
1671 callback = conn->body.callback;
1672 cbdata = conn->body.cbdata;
1673 assert(request == conn->body.request);
1674 conn->body.buf = NULL;
1675 conn->body.callback = NULL;
1676 conn->body.cbdata = NULL;
1677 conn->body.request = NULL;
1678 callback(buf, -1, cbdata); /* Signal abort to clientReadBody caller */
1679 requestUnlink(request);
1680 }
1681 clientReadBodyAbortHandler(NULL, -1, conn); /* Install abort handler */
1682 /* clientProcessBody() */
1683 return 1; /* Aborted */
7a2f978b 1684}
1685
1686/* general lifetime handler for HTTP requests */
1687static void
1688requestTimeout(int fd, void *data)
1689{
ad63ceea 1690#if THIS_CONFUSES_PERSISTENT_CONNECTION_AWARE_BROWSERS_AND_USERS
7a2f978b 1691 ConnStateData *conn = data;
badd8ff0 1692 debug(33, 3) ("requestTimeout: FD %d: lifetime is expired.\n", fd);
7a2f978b 1693 if (fd_table[fd].rwstate) {
79e0dc20 1694 /*
1695 * Some data has been sent to the client, just close the FD
1696 */
7a2f978b 1697 comm_close(fd);
1698 } else if (conn->nrequests) {
79e0dc20 1699 /*
1700 * assume its a persistent connection; just close it
1701 */
7a2f978b 1702 comm_close(fd);
1703 } else {
79e0dc20 1704 /*
1705 * Generate an error
1706 */
edce4d98 1707 clientHttpRequest **H;
1708 clientStreamNode *node;
1709 clientHttpRequest *http =
c8be6d7b 1710 parseHttpRequestAbort(conn, "error:Connection%20lifetime%20expired");
edce4d98 1711 node = http->client_stream.tail->prev->data;
1712 clientSetReplyToError(node->data, ERR_LIFETIME_EXP,
1713 HTTP_REQUEST_TIMEOUT, METHOD_NONE, "N/A", &conn->peer.sin_addr,
1714 NULL, NULL, NULL);
1715 /* No requests can be outstanded */
79e0dc20 1716 assert(conn->chr == NULL);
edce4d98 1717 /* add to the client request queue */
1718 for (H = &conn->chr; *H; H = &(*H)->next);
1719 *H = http;
1720 clientStreamRead(http->client_stream.tail->data, http, 0,
1721 HTTP_REQBUF_SZ, context->reqbuf);
79e0dc20 1722 /*
1723 * if we don't close() here, we still need a timeout handler!
1724 */
7a2f978b 1725 commSetTimeout(fd, 30, requestTimeout, conn);
7e3ce7b9 1726 /*
1727 * Aha, but we don't want a read handler!
1728 */
1729 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
7a2f978b 1730 }
af57a2e3 1731#else
1732 /*
1733 * Just close the connection to not confuse browsers
1734 * using persistent connections. Some browsers opens
1735 * an connection and then does not use it until much
1736 * later (presumeably because the request triggering
1737 * the open has already been completed on another
1738 * connection)
1739 */
ad63ceea 1740 debug(33, 3) ("requestTimeout: FD %d: lifetime is expired.\n", fd);
af57a2e3 1741 comm_close(fd);
1742#endif
7a2f978b 1743}
1744
b5c39993 1745static void
1746clientLifetimeTimeout(int fd, void *data)
1747{
e6ccf245 1748 clientHttpRequest *http = (clientHttpRequest *)data;
7ec87701 1749 ConnStateData *conn = http->conn;
edce4d98 1750 debug(33,
1751 1) ("WARNING: Closing client %s connection due to lifetime timeout\n",
b5c39993 1752 inet_ntoa(conn->peer.sin_addr));
1753 debug(33, 1) ("\t%s\n", http->uri);
1754 comm_close(fd);
1755}
1756
ba4f8e5a 1757static int
d20b1cd0 1758httpAcceptDefer(int fdunused, void *dataunused)
7a2f978b 1759{
3d6629c6 1760 static time_t last_warn = 0;
1761 if (fdNFree() >= RESERVED_FD)
1762 return 0;
1763 if (last_warn + 15 < squid_curtime) {
1764 debug(33, 0) ("WARNING! Your cache is running out of filedescriptors\n");
1765 last_warn = squid_curtime;
1766 }
1767 return 1;
7a2f978b 1768}
1769
c8be6d7b 1770ConnStateData *
c4b7a5a9 1771connStateCreate(struct sockaddr_in *peer, struct sockaddr_in *me, int fd)
c8be6d7b 1772{
1773 ConnStateData *result = cbdataAlloc(ConnStateData);
c4b7a5a9 1774 result->peer = *peer;
1775 result->log_addr = peer->sin_addr;
c8be6d7b 1776 result->log_addr.s_addr &= Config.Addrs.client_netmask.s_addr;
c4b7a5a9 1777 result->me = *me;
c8be6d7b 1778 result->fd = fd;
e6ccf245 1779 result->in.buf = (char *)memAllocBuf(CLIENT_REQ_BUF_SZ, &result->in.allocatedSize);
c4b7a5a9 1780 result->flags.readMoreRequests = 1;
c8be6d7b 1781 return result;
1782}
1783
bf8e5903 1784/* Handle a new connection on HTTP socket. */
7a2f978b 1785void
c4b7a5a9 1786httpAccept(int sock, int newfd, struct sockaddr_in *me, struct sockaddr_in *peer,
1787 comm_err_t flag, int xerrno, void *data)
7a2f978b 1788{
d193a436 1789 int *N = &incoming_sockets_accepted;
7a2f978b 1790 ConnStateData *connState = NULL;
3898f57f 1791#if USE_IDENT
a40699cd 1792 static aclCheck_t identChecklist;
3898f57f 1793#endif
02d1422b 1794
1795 if (flag == COMM_ERR_CLOSING) {
1796 return;
1797 }
1798
c4b7a5a9 1799 /* kick off another one for later */
1800 comm_accept(sock, httpAccept, NULL);
1801
1802 /* XXX we're not considering httpAcceptDefer yet! */
1803
1804 if (flag != COMM_OK) {
1805 errno = xerrno;
1806 debug(50, 1) ("httpAccept: FD %d: accept failure: %s\n",
e2525655 1807 sock, xstrerror());
c4b7a5a9 1808 return;
e2525655 1809 }
c4b7a5a9 1810
1811 debug(33, 4) ("httpAccept: FD %d: accepted\n", newfd);
1812 connState = connStateCreate(peer, me, newfd);
1813 comm_add_close_handler(newfd, connStateFree, connState);
e2525655 1814 if (Config.onoff.log_fqdn)
c4b7a5a9 1815 fqdncache_gethostbyaddr(peer->sin_addr, FQDN_LOOKUP_IF_MISS);
1816 commSetTimeout(newfd, Config.Timeout.request, requestTimeout, connState);
3898f57f 1817#if USE_IDENT
c4b7a5a9 1818 identChecklist.src_addr = peer->sin_addr;
1819 identChecklist.my_addr = me->sin_addr;
1820 identChecklist.my_port = ntohs(me->sin_port);
a40699cd 1821 if (aclCheckFast(Config.accessList.identLookup, &identChecklist))
c4b7a5a9 1822 identStart(me, peer, clientIdentDone, connState);
3898f57f 1823#endif
c4b7a5a9 1824 clientReadSomeData(connState);
1825 commSetDefer(newfd, clientReadDefer, connState);
1826 clientdbEstablished(peer->sin_addr, 1);
41292f79 1827 assert(N);
ba4f8e5a 1828 (*N)++;
7a2f978b 1829}
1830
1f7c9178 1831#if USE_SSL
1832
1833/* negotiate an SSL connection */
1834static void
1835clientNegotiateSSL(int fd, void *data)
1836{
e6ccf245 1837 ConnStateData *conn = (ConnStateData *)data;
1f7c9178 1838 X509 *client_cert;
1839 int ret;
1840
1841 if ((ret = SSL_accept(fd_table[fd].ssl)) <= 0) {
1842 if (BIO_sock_should_retry(ret)) {
1843 commSetSelect(fd, COMM_SELECT_READ, clientNegotiateSSL, conn, 0);
1844 return;
1845 }
1846 ret = ERR_get_error();
a4a80ff0 1847 if (ret) {
edce4d98 1848 debug(83, 1)
1849 ("clientNegotiateSSL: Error negotiating SSL connection on FD %d: %s\n",
a4a80ff0 1850 fd, ERR_error_string(ret, NULL));
1851 }
1f7c9178 1852 comm_close(fd);
1853 return;
1854 }
27d8545c 1855 debug(83, 5) ("clientNegotiateSSL: FD %d negotiated cipher %s\n", fd,
1f7c9178 1856 SSL_get_cipher(fd_table[fd].ssl));
1857
1858 client_cert = SSL_get_peer_certificate(fd_table[fd].ssl);
1859 if (client_cert != NULL) {
edce4d98 1860 debug(83, 5) ("clientNegotiateSSL: FD %d client certificate: subject: %s\n",
1861 fd, X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0));
1f7c9178 1862
edce4d98 1863 debug(83, 5) ("clientNegotiateSSL: FD %d client certificate: issuer: %s\n",
1864 fd, X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0));
1f7c9178 1865
1866 X509_free(client_cert);
1867 } else {
27d8545c 1868 debug(83, 5) ("clientNegotiateSSL: FD %d has no certificate.\n", fd);
1f7c9178 1869 }
1870
c4b7a5a9 1871 clientReadSomeData(conn);
1f7c9178 1872}
1873
d193a436 1874struct _https_port_data {
1875 SSL_CTX *sslContext;
1876};
1877typedef struct _https_port_data https_port_data;
1878CBDATA_TYPE(https_port_data);
1879
1f7c9178 1880/* handle a new HTTPS connection */
1881static void
c4b7a5a9 1882httpsAccept(int sock, int newfd, struct sockaddr_in *me, struct sockaddr_in *peer,
1883 comm_err_t flag, int xerrno, void *data)
1f7c9178 1884{
d193a436 1885 int *N = &incoming_sockets_accepted;
e6ccf245 1886 https_port_data *https_port = (https_port_data *)data;
d193a436 1887 SSL_CTX *sslContext = https_port->sslContext;
1f7c9178 1888 ConnStateData *connState = NULL;
1f7c9178 1889 SSL *ssl;
1890 int ssl_error;
1891#if USE_IDENT
1892 static aclCheck_t identChecklist;
1893#endif
c4b7a5a9 1894
02d1422b 1895 if (flag == COMM_ERR_CLOSING) {
1896 return;
1897 }
1898
c4b7a5a9 1899 if (flag != COMM_OK) {
1900 errno = xerrno;
1901 debug(50, 1) ("httpsAccept: FD %d: accept failure: %s\n",
1902 sock, xstrerror());
1903 return;
1904 }
1905
1906 if ((ssl = SSL_new(sslContext)) == NULL) {
1907 ssl_error = ERR_get_error();
1908 debug(83, 1) ("httpsAccept: Error allocating handle: %s\n",
1909 ERR_error_string(ssl_error, NULL));
1910 return;
1911 }
1912
1913 SSL_set_fd(ssl, newfd);
1914 fd_table[newfd].ssl = ssl;
1915 fd_table[newfd].read_method = &ssl_read_method;
1916 fd_table[newfd].write_method = &ssl_write_method;
1917 debug(50, 5) ("httpsAccept: FD %d accepted, starting SSL negotiation.\n", newfd);
1918
1919 connState = connStateCreate(peer, me, newfd);
1920 /* XXX account connState->in.buf */
1921 comm_add_close_handler(newfd, connStateFree, connState);
1922 if (Config.onoff.log_fqdn)
1923 fqdncache_gethostbyaddr(peer->sin_addr, FQDN_LOOKUP_IF_MISS);
1924 commSetTimeout(newfd, Config.Timeout.request, requestTimeout, connState);
1f7c9178 1925#if USE_IDENT
c4b7a5a9 1926 identChecklist.src_addr = peer->sin_addr;
1927 identChecklist.my_addr = me->sin_addr;
1928 identChecklist.my_port = ntohs(me->sin_port);
1929 if (aclCheckFast(Config.accessList.identLookup, &identChecklist))
1930 identStart(me, peer, clientIdentDone, connState);
1f7c9178 1931#endif
c4b7a5a9 1932 commSetSelect(newfd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
1933 commSetDefer(newfd, clientReadDefer, connState);
1934 clientdbEstablished(peer->sin_addr, 1);
1935 (*N)++;
1f7c9178 1936}
1937
1938#endif /* USE_SSL */
1939
7a2f978b 1940/*
1941 * This function is designed to serve a fairly specific purpose.
1942 * Occasionally our vBNS-connected caches can talk to each other, but not
1943 * the rest of the world. Here we try to detect frequent failures which
1944 * make the cache unusable (e.g. DNS lookup and connect() failures). If
1945 * the failure:success ratio goes above 1.0 then we go into "hit only"
1946 * mode where we only return UDP_HIT or UDP_MISS_NOFETCH. Neighbors
1947 * will only fetch HITs from us if they are using the ICP protocol. We
1948 * stay in this mode for 5 minutes.
1949 *
1950 * Duane W., Sept 16, 1996
1951 */
1952
1953static void
88aad2e5 1954checkFailureRatio(err_type etype, hier_code hcode)
7a2f978b 1955{
88aad2e5 1956 static double magic_factor = 100.0;
7a2f978b 1957 double n_good;
1958 double n_bad;
1959 if (hcode == HIER_NONE)
1960 return;
88aad2e5 1961 n_good = magic_factor / (1.0 + request_failure_ratio);
7a2f978b 1962 n_bad = magic_factor - n_good;
88aad2e5 1963 switch (etype) {
7a2f978b 1964 case ERR_DNS_FAIL:
1965 case ERR_CONNECT_FAIL:
1966 case ERR_READ_ERROR:
1967 n_bad++;
1968 break;
1969 default:
1970 n_good++;
1971 }
88aad2e5 1972 request_failure_ratio = n_bad / n_good;
7a2f978b 1973 if (hit_only_mode_until > squid_curtime)
1974 return;
88aad2e5 1975 if (request_failure_ratio < 1.0)
7a2f978b 1976 return;
93f9abd0 1977 debug(33, 0) ("Failure Ratio at %4.2f\n", request_failure_ratio);
1978 debug(33, 0) ("Going into hit-only-mode for %d minutes...\n",
7a2f978b 1979 FAILURE_MODE_TIME / 60);
1980 hit_only_mode_until = squid_curtime + FAILURE_MODE_TIME;
88aad2e5 1981 request_failure_ratio = 0.8; /* reset to something less than 1.0 */
7a2f978b 1982}
15df8349 1983
d193a436 1984static void
15df8349 1985clientHttpConnectionsOpen(void)
1986{
7e3ce7b9 1987 sockaddr_in_list *s;
15df8349 1988 int fd;
7e3ce7b9 1989 for (s = Config.Sockaddr.http; s; s = s->next) {
42b51993 1990 if (MAXHTTPPORTS == NHttpSockets) {
1991 debug(1, 1) ("WARNING: You have too many 'http_port' lines.\n");
1992 debug(1, 1) (" The limit is %d\n", MAXHTTPPORTS);
1993 continue;
1994 }
8daca701 1995 enter_suid();
1996 fd = comm_open(SOCK_STREAM,
1997 0,
7e3ce7b9 1998 s->s.sin_addr,
edce4d98 1999 ntohs(s->s.sin_port), COMM_NONBLOCKING, "HTTP Socket");
8daca701 2000 leave_suid();
2001 if (fd < 0)
2002 continue;
2003 comm_listen(fd);
c4b7a5a9 2004 comm_accept(fd, httpAccept, NULL);
d20b1cd0 2005 /*
2006 * We need to set a defer handler here so that we don't
2007 * peg the CPU with select() when we hit the FD limit.
2008 */
2009 commSetDefer(fd, httpAcceptDefer, NULL);
7e3ce7b9 2010 debug(1, 1) ("Accepting HTTP connections at %s, port %d, FD %d.\n",
edce4d98 2011 inet_ntoa(s->s.sin_addr), (int) ntohs(s->s.sin_port), fd);
8daca701 2012 HttpSockets[NHttpSockets++] = fd;
15df8349 2013 }
d193a436 2014}
2015
2016#if USE_SSL
2017static void
2018clientHttpsConnectionsOpen(void)
2019{
2020 https_port_list *s;
2021 https_port_data *https_port;
2022 int fd;
1f7c9178 2023 for (s = Config.Sockaddr.https; s; s = s->next) {
17848833 2024 if (MAXHTTPPORTS == NHttpSockets) {
2025 debug(1, 1) ("WARNING: You have too many 'https_port' lines.\n");
2026 debug(1, 1) (" The limit is %d\n", MAXHTTPPORTS);
2027 continue;
2028 }
1f7c9178 2029 enter_suid();
2030 fd = comm_open(SOCK_STREAM,
2031 0,
2032 s->s.sin_addr,
edce4d98 2033 ntohs(s->s.sin_port), COMM_NONBLOCKING, "HTTPS Socket");
1f7c9178 2034 leave_suid();
2035 if (fd < 0)
2036 continue;
d193a436 2037 CBDATA_INIT_TYPE(https_port_data);
2038 https_port = cbdataAlloc(https_port_data);
edce4d98 2039 https_port->sslContext =
2040 sslCreateContext(s->cert, s->key, s->version, s->cipher,
2041 s->options);
1f7c9178 2042 comm_listen(fd);
c4b7a5a9 2043 comm_accept(fd, httpsAccept, NULL);
d193a436 2044 commSetDefer(fd, httpAcceptDefer, NULL);
1f7c9178 2045 debug(1, 1) ("Accepting HTTPS connections at %s, port %d, FD %d.\n",
edce4d98 2046 inet_ntoa(s->s.sin_addr), (int) ntohs(s->s.sin_port), fd);
1f7c9178 2047 HttpSockets[NHttpSockets++] = fd;
2048 }
d193a436 2049}
2050
2051#endif
2052
2053void
2054clientOpenListenSockets(void)
2055{
2056 clientHttpConnectionsOpen();
2057#if USE_SSL
2058 clientHttpsConnectionsOpen();
1f7c9178 2059#endif
15df8349 2060 if (NHttpSockets < 1)
8daca701 2061 fatal("Cannot open HTTP Port");
15df8349 2062}
edce4d98 2063
c0fbae16 2064void
2065clientHttpConnectionsClose(void)
2066{
2067 int i;
2068 for (i = 0; i < NHttpSockets; i++) {
2069 if (HttpSockets[i] >= 0) {
2070 debug(1, 1) ("FD %d Closing HTTP connection\n", HttpSockets[i]);
2071 comm_close(HttpSockets[i]);
2072 HttpSockets[i] = -1;
2073 }
2074 }
2075 NHttpSockets = 0;
2076}
f66a9ef4 2077
2078int
2079varyEvaluateMatch(StoreEntry * entry, request_t * request)
2080{
2081 const char *vary = request->vary_headers;
2082 int has_vary = httpHeaderHas(&entry->mem_obj->reply->header, HDR_VARY);
2083#if X_ACCELERATOR_VARY
edce4d98 2084 has_vary |=
2085 httpHeaderHas(&entry->mem_obj->reply->header, HDR_X_ACCELERATOR_VARY);
f66a9ef4 2086#endif
2087 if (!has_vary || !entry->mem_obj->vary_headers) {
2088 if (vary) {
2089 /* Oops... something odd is going on here.. */
edce4d98 2090 debug(33,
2091 1)
2092 ("varyEvaluateMatch: Oops. Not a Vary object on second attempt, '%s' '%s'\n",
f66a9ef4 2093 entry->mem_obj->url, vary);
2094 safe_free(request->vary_headers);
2095 return VARY_CANCEL;
2096 }
2097 if (!has_vary) {
2098 /* This is not a varying object */
2099 return VARY_NONE;
2100 }
2101 /* virtual "vary" object found. Calculate the vary key and
2102 * continue the search
2103 */
a16efaa4 2104 vary = httpMakeVaryMark(request, entry->mem_obj->reply);
2105 if (vary) {
2106 request->vary_headers = xstrdup(vary);
f66a9ef4 2107 return VARY_OTHER;
a16efaa4 2108 } else {
f66a9ef4 2109 /* Ouch.. we cannot handle this kind of variance */
2110 /* XXX This cannot really happen, but just to be complete */
2111 return VARY_CANCEL;
2112 }
2113 } else {
a16efaa4 2114 if (!vary) {
2115 vary = httpMakeVaryMark(request, entry->mem_obj->reply);
2116 if (vary)
2117 request->vary_headers = xstrdup(vary);
2118 }
f66a9ef4 2119 if (!vary) {
2120 /* Ouch.. we cannot handle this kind of variance */
2121 /* XXX This cannot really happen, but just to be complete */
2122 return VARY_CANCEL;
2123 } else if (strcmp(vary, entry->mem_obj->vary_headers) == 0) {
2124 return VARY_MATCH;
2125 } else {
2126 /* Oops.. we have already been here and still haven't
2127 * found the requested variant. Bail out
2128 */
2129 debug(33, 1) ("varyEvaluateMatch: Oops. Not a Vary match on second attempt, '%s' '%s'\n",
2130 entry->mem_obj->url, vary);
2131 return VARY_CANCEL;
2132 }
2133 }
2134}
28d4805a 2135
2136aclCheck_t *
2137clientAclChecklistCreate(const acl_access * acl, const clientHttpRequest * http)
2138{
2139 aclCheck_t *ch;
2140 ConnStateData *conn = http->conn;
2141 ch = aclChecklistCreate(acl, http->request, conn ? conn->rfc931 : dash_str);
2142
2143 /*
2144 * hack for ident ACL. It needs to get full addresses, and a place to store
2145 * the ident result on persistent connections...
2146 */
2147 /* connection oriented auth also needs these two lines for it's operation. */
2148 /*
2149 * Internal requests do not have a connection reference, because: A) their
2150 * byte count may be transformed before being applied to an outbound
2151 * connection B) they are internal - any limiting on them should be done on
2152 * the server end.
2153 */
2154 if (conn)
2155 ch->conn = cbdataReference(conn); /* unreferenced in acl.c */
2156
2157 return ch;
2158}