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