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