Use closesocket in tunnel.c.
[polipo.git] / server.c
blob049e95e955429e5fb4df99220cc23edeee00b299
1 /*
2 Copyright (c) 2003-2006 by Juliusz Chroboczek
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
23 #include "polipo.h"
25 int serverExpireTime = 24 * 60 * 60;
26 int smallRequestTime = 10;
27 int replyUnpipelineTime = 20;
28 int replyUnpipelineSize = 1024 * 1024;
29 int pipelineAdditionalRequests = 1;
30 int maxPipelineTrain = 10;
31 AtomPtr parentProxy = NULL;
32 AtomPtr parentHost = NULL;
33 int parentPort = -1;
34 int pmmFirstSize = 0, pmmSize = 0;
35 int serverSlots = 2;
36 int serverMaxSlots = 8;
37 int dontCacheRedirects = 0;
38 int maxSideBuffering = 1500;
40 static HTTPServerPtr servers = 0;
42 static int httpServerContinueConditionHandler(int, ConditionHandlerPtr);
43 static int initParentProxy(void);
44 static int parentProxySetter(ConfigVariablePtr var, void *value);
45 static void httpServerDelayedFinish(HTTPConnectionPtr);
46 static int allowUnalignedRangeRequests = 0;
48 void
49 preinitServer(void)
51 CONFIG_VARIABLE_SETTABLE(parentProxy, CONFIG_ATOM_LOWER, parentProxySetter,
52 "Parent proxy (host:port).");
53 CONFIG_VARIABLE(serverExpireTime, CONFIG_TIME,
54 "Time during which server data is valid.");
55 CONFIG_VARIABLE_SETTABLE(smallRequestTime, CONFIG_TIME, configIntSetter,
56 "Estimated time for a small request.");
57 CONFIG_VARIABLE_SETTABLE(replyUnpipelineTime, CONFIG_TIME, configIntSetter,
58 "Estimated time for a pipeline break.");
59 CONFIG_VARIABLE_SETTABLE(replyUnpipelineSize, CONFIG_INT, configIntSetter,
60 "Size for a pipeline break.");
61 CONFIG_VARIABLE_SETTABLE(pipelineAdditionalRequests, CONFIG_TRISTATE,
62 configIntSetter,
63 "Pipeline requests on an active connection.");
64 CONFIG_VARIABLE_SETTABLE(maxPipelineTrain, CONFIG_INT,
65 configIntSetter,
66 "Maximum number of requests "
67 "pipelined at a time.");
68 CONFIG_VARIABLE(pmmFirstSize, CONFIG_INT,
69 "The size of the first PMM chunk.");
70 CONFIG_VARIABLE(pmmSize, CONFIG_INT,
71 "The size of a PMM chunk.");
72 CONFIG_VARIABLE(serverSlots, CONFIG_INT,
73 "Maximum number of connections per server.");
74 CONFIG_VARIABLE(serverMaxSlots, CONFIG_INT,
75 "Maximum number of connections per broken server.");
76 CONFIG_VARIABLE(dontCacheRedirects, CONFIG_BOOLEAN,
77 "If true, don't cache redirects.");
78 CONFIG_VARIABLE_SETTABLE(allowUnalignedRangeRequests,
79 CONFIG_BOOLEAN, configIntSetter,
80 "Allow unaligned range requests (unreliable).");
81 CONFIG_VARIABLE_SETTABLE(maxSideBuffering,
82 CONFIG_INT, configIntSetter,
83 "Maximum buffering for PUT and POST requests.");
86 static int
87 parentProxySetter(ConfigVariablePtr var, void *value)
89 configAtomSetter(var, value);
90 initParentProxy();
91 return 1;
94 static void
95 discardServer(HTTPServerPtr server)
97 HTTPServerPtr previous;
98 assert(!server->request);
100 if(server == servers)
101 servers = server->next;
102 else {
103 previous = servers;
104 while(previous->next != server)
105 previous = previous->next;
106 previous->next = server->next;
109 free(server);
112 static int
113 httpServerIdle(HTTPServerPtr server)
115 int i;
116 if(server->request)
117 return 0;
118 for(i = 0; i < server->maxslots; i++)
119 if(server->connection[i])
120 return 0;
121 return 1;
124 static int
125 expireServersHandler(TimeEventHandlerPtr event)
127 HTTPServerPtr server, next;
128 TimeEventHandlerPtr e;
129 server = servers;
130 while(server) {
131 next = server->next;
132 if(httpServerIdle(server) &&
133 server->time + serverExpireTime < current_time.tv_sec)
134 discardServer(server);
135 server = next;
137 e = scheduleTimeEvent(serverExpireTime / 60 + 60,
138 expireServersHandler, 0, NULL);
139 if(!e) {
140 do_log(L_ERROR, "Couldn't schedule server expiry.\n");
141 polipoExit();
143 return 1;
146 static int
147 roundSize(int size)
149 return (size + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE;
152 static int
153 initParentProxy()
155 AtomPtr host, port_atom;
156 int rc, port;
158 if(parentHost) {
159 releaseAtom(parentHost);
160 parentHost = NULL;
162 if(parentPort >= 0)
163 parentPort = -1;
165 if(parentProxy != NULL && parentProxy->length == 0) {
166 releaseAtom(parentProxy);
167 parentProxy = NULL;
170 if(parentProxy == NULL)
171 return 1;
173 rc = atomSplit(parentProxy, ':', &host, &port_atom);
174 if(rc <= 0) {
175 do_log(L_ERROR, "Couldn't parse parentProxy.");
176 releaseAtom(parentProxy);
177 parentProxy = NULL;
178 return -1;
181 port = atoi(port_atom->string);
182 if(port <= 0 || port >= 0x10000) {
183 releaseAtom(host);
184 releaseAtom(port_atom);
185 do_log(L_ERROR, "Couldn't parse parentProxy.");
186 releaseAtom(parentProxy);
187 parentProxy = NULL;
188 return -1;
191 parentHost = host;
192 parentPort = port;
193 return 1;
196 void
197 initServer(void)
199 TimeEventHandlerPtr event;
200 servers = NULL;
202 if(pmmFirstSize || pmmSize) {
203 if(pmmSize == 0) pmmSize = pmmFirstSize;
204 if(pmmFirstSize == 0) pmmFirstSize = pmmSize;
205 pmmSize = roundSize(pmmSize);
206 pmmFirstSize = roundSize(pmmFirstSize);
209 if(serverMaxSlots < 1)
210 serverMaxSlots = 1;
211 if(serverSlots < 1)
212 serverSlots = 1;
213 if(serverSlots > serverMaxSlots)
214 serverSlots = serverMaxSlots;
216 initParentProxy();
218 event = scheduleTimeEvent(serverExpireTime / 60 + 60, expireServersHandler,
219 0, NULL);
220 if(event == NULL) {
221 do_log(L_ERROR, "Couldn't schedule server expiry.\n");
222 exit(1);
226 static HTTPServerPtr
227 getServer(char *name, int port, int proxy)
229 HTTPServerPtr server;
230 int i;
232 server = servers;
233 while(server) {
234 if(strcmp(server->name, name) == 0 && server->port == port &&
235 server->isProxy == proxy) {
236 if(httpServerIdle(server) &&
237 server->time + serverExpireTime < current_time.tv_sec) {
238 discardServer(server);
239 server = NULL;
240 break;
241 } else {
242 server->time = current_time.tv_sec;
243 return server;
246 server = server->next;
249 server = malloc(sizeof(HTTPServerRec));
250 if(server == NULL) {
251 do_log(L_ERROR, "Couldn't allocate server.\n");
252 return NULL;
255 server->connection = malloc(serverMaxSlots * sizeof(HTTPConnectionPtr));
256 if(server->connection == NULL) {
257 do_log(L_ERROR, "Couldn't allocate server.\n");
258 free(server);
259 return NULL;
262 server->idleHandler = malloc(serverMaxSlots * sizeof(FdEventHandlerPtr));
263 if(server->connection == NULL) {
264 do_log(L_ERROR, "Couldn't allocate server.\n");
265 free(server->connection);
266 free(server);
267 return NULL;
270 server->maxslots = serverMaxSlots;
272 server->name = strdup(name);
273 if(server->name == NULL) {
274 do_log(L_ERROR, "Couldn't allocate server name.\n");
275 free(server);
276 return NULL;
279 server->port = port;
280 server->addrindex = 0;
281 server->isProxy = proxy;
282 server->version = HTTP_UNKNOWN;
283 server->persistent = 0;
284 server->pipeline = 0;
285 server->time = current_time.tv_sec;
286 server->rtt = -1;
287 server->rate = -1;
288 server->numslots = MIN(serverSlots, server->maxslots);
289 for(i = 0; i < server->maxslots; i++) {
290 server->connection[i] = NULL;
291 server->idleHandler[i] = NULL;
293 server->request = NULL;
294 server->request_last = NULL;
295 server->lies = 0;
297 server->next = servers;
298 servers = server;
299 return server;
303 httpServerQueueRequest(HTTPServerPtr server, HTTPRequestPtr request)
305 assert(request->request && request->request->request == request);
306 assert(request->connection == NULL);
307 if(server->request) {
308 server->request_last->next = request;
309 server->request_last = request;
310 } else {
311 server->request_last = request;
312 server->request = request;
314 return 1;
317 void
318 httpServerAbort(HTTPConnectionPtr connection, int fail,
319 int code, AtomPtr message)
321 HTTPRequestPtr request = connection->request;
322 if(request) {
323 if(request->request) {
324 httpClientError(request->request, code, retainAtom(message));
326 if(fail) {
327 request->object->flags |= OBJECT_FAILED;
328 if(request->object->flags & OBJECT_INITIAL)
329 abortObject(request->object, code, retainAtom(message));
330 notifyObject(request->object);
333 releaseAtom(message);
334 if(!connection->connecting)
335 httpServerFinish(connection, 1, 0);
338 void
339 httpServerAbortRequest(HTTPRequestPtr request, int fail,
340 int code, AtomPtr message)
342 if(request->connection && request == request->connection->request) {
343 httpServerAbort(request->connection, fail, code, message);
344 } else {
345 HTTPRequestPtr requestor = request->request;
346 if(requestor) {
347 requestor->request = NULL;
348 request->request = NULL;
349 httpClientError(requestor, code, retainAtom(message));
351 if(fail) {
352 request->object->flags |= OBJECT_FAILED;
353 if(request->object->flags & OBJECT_INITIAL)
354 abortObject(request->object, code, retainAtom(message));
355 notifyObject(request->object);
357 releaseAtom(message);
361 void
362 httpServerClientReset(HTTPRequestPtr request)
364 if(request->connection &&
365 request->connection->fd >= 0 &&
366 !request->connection->connecting &&
367 request->connection->request == request)
368 pokeFdEvent(request->connection->fd, -ECLIENTRESET, POLLIN | POLLOUT);
373 httpMakeServerRequest(char *name, int port, ObjectPtr object,
374 int method, int from, int to, HTTPRequestPtr requestor)
376 HTTPServerPtr server;
377 HTTPRequestPtr request;
378 int rc;
380 assert(!(object->flags & OBJECT_INPROGRESS));
382 if(parentHost) {
383 server = getServer(parentHost->string, parentPort, 1);
384 } else {
385 server = getServer(name, port, 0);
387 if(server == NULL) return -1;
389 object->flags |= OBJECT_INPROGRESS;
390 object->requestor = requestor;
392 request = httpMakeRequest();
393 if(!request) {
394 do_log(L_ERROR, "Couldn't allocate request.\n");
395 return -1;
398 /* Because we allocate objects in chunks, we cannot have data that
399 doesn't start at a chunk boundary. */
400 if(from % CHUNK_SIZE != 0) {
401 if(allowUnalignedRangeRequests) {
402 objectFillFromDisk(object, from / CHUNK_SIZE * CHUNK_SIZE, 1);
403 if(objectHoleSize(object, from - 1) != 0)
404 from = from / CHUNK_SIZE * CHUNK_SIZE;
405 } else {
406 from = from / CHUNK_SIZE * CHUNK_SIZE;
410 request->object = retainObject(object);
411 request->method = method;
412 if(method == METHOD_CONDITIONAL_GET) {
413 if(server->lies > 0)
414 request->method = METHOD_HEAD;
416 request->flags =
417 REQUEST_PERSISTENT |
418 (expectContinue ? (requestor->flags & REQUEST_WAIT_CONTINUE) : 0);
419 request->from = from;
420 request->to = to;
421 request->request = requestor;
422 requestor->request = request;
423 request->cache_control = requestor->cache_control;
424 request->time0 = null_time;
425 request->time1 = null_time;
427 rc = httpServerQueueRequest(server, request);
428 if(rc < 0) {
429 do_log(L_ERROR, "Couldn't queue request.\n");
430 request->request = NULL;
431 requestor->request = NULL;
432 object->flags &= ~(OBJECT_INPROGRESS | OBJECT_VALIDATING);
433 releaseNotifyObject(object);
434 httpDestroyRequest(request);
435 return 1;
438 if(request->flags & REQUEST_WAIT_CONTINUE) {
439 if(server->version == HTTP_10) {
440 httpServerAbortRequest(request, 1,
441 417, internAtom("Expectation failed"));
442 return 1;
444 } else if(expectContinue >= 2 && server->version == HTTP_11) {
445 if(request->method == METHOD_POST || request->method == METHOD_PUT)
446 request->flags |= REQUEST_WAIT_CONTINUE;
449 again:
450 rc = httpServerTrigger(server);
451 if(rc < 0) {
452 /* We must be very short on memory. If there are any requests
453 queued, we abort one and try again. If there aren't, we
454 give up. */
455 do_log(L_ERROR, "Couldn't trigger server -- out of memory?\n");
456 if(server->request) {
457 httpServerAbortRequest(server->request, 1, 503,
458 internAtom("Couldn't trigger server"));
459 goto again;
462 return 1;
466 httpServerConnection(HTTPServerPtr server)
468 HTTPConnectionPtr connection;
469 int i;
471 connection = httpMakeConnection();
472 if(connection == NULL) {
473 do_log(L_ERROR, "Couldn't allocate server connection.\n");
474 return -1;
476 connection->server = server;
478 for(i = 0; i < server->numslots; i++) {
479 if(!server->connection[i]) {
480 server->connection[i] = connection;
481 break;
484 assert(i < server->numslots);
486 connection->request = NULL;
487 connection->request_last = NULL;
489 do_log(D_SERVER_CONN, "C... %s:%d.\n",
490 connection->server->name, connection->server->port);
491 httpSetTimeout(connection, serverTimeout);
492 if(socksParentProxy) {
493 connection->connecting = CONNECTING_SOCKS;
494 do_socks_connect(server->name, connection->server->port,
495 httpServerSocksHandler, connection);
496 } else {
497 connection->connecting = CONNECTING_DNS;
498 do_gethostbyname(server->name, 0,
499 httpServerConnectionDnsHandler,
500 connection);
502 return 1;
506 httpServerConnectionDnsHandler(int status, GethostbynameRequestPtr request)
508 HTTPConnectionPtr connection = request->data;
510 httpSetTimeout(connection, -1);
512 if(status <= 0) {
513 AtomPtr message;
514 message = internAtomF("Host %s lookup failed: %s",
515 request->name ?
516 request->name->string : "(unknown)",
517 request->error_message ?
518 request->error_message->string :
519 pstrerror(-status));
520 do_log(L_ERROR, "Host %s lookup failed: %s (%d).\n",
521 request->name ?
522 request->name->string : "(unknown)",
523 request->error_message ?
524 request->error_message->string :
525 pstrerror(-status), -status);
526 connection->connecting = 0;
527 if(connection->server->request)
528 httpServerAbortRequest(connection->server->request, 1, 504,
529 retainAtom(message));
530 httpServerAbort(connection, 1, 502, message);
531 return 1;
534 if(request->addr->string[0] == DNS_CNAME) {
535 if(request->count > 10) {
536 AtomPtr message = internAtom("DNS CNAME loop");
537 do_log(L_ERROR, "DNS CNAME loop.\n");
538 connection->connecting = 0;
539 if(connection->server->request)
540 httpServerAbortRequest(connection->server->request, 1, 504,
541 retainAtom(message));
542 httpServerAbort(connection, 1, 504, message);
543 return 1;
546 httpSetTimeout(connection, serverTimeout);
547 do_gethostbyname(request->addr->string + 1, request->count + 1,
548 httpServerConnectionDnsHandler,
549 connection);
550 return 1;
553 connection->connecting = CONNECTING_CONNECT;
554 httpSetTimeout(connection, serverTimeout);
555 do_connect(retainAtom(request->addr), connection->server->addrindex,
556 connection->server->port,
557 httpServerConnectionHandler, connection);
558 return 1;
562 httpServerConnectionHandler(int status,
563 FdEventHandlerPtr event,
564 ConnectRequestPtr request)
566 HTTPConnectionPtr connection = request->data;
568 assert(connection->fd < 0);
569 if(request->fd >= 0) {
570 int rc;
571 connection->fd = request->fd;
572 connection->server->addrindex = request->index;
573 rc = setNodelay(connection->fd, 1);
574 if(rc < 0)
575 do_log_error(L_WARN, errno, "Couldn't disable Nagle's algorithm");
578 return httpServerConnectionHandlerCommon(status, connection);
582 httpServerSocksHandler(int status, SocksRequestPtr request)
584 HTTPConnectionPtr connection = request->data;
586 assert(connection->fd < 0);
587 if(request->fd >= 0) {
588 connection->fd = request->fd;
589 connection->server->addrindex = 0;
591 return httpServerConnectionHandlerCommon(status, connection);
595 httpServerConnectionHandlerCommon(int status, HTTPConnectionPtr connection)
597 httpSetTimeout(connection, -1);
599 if(status < 0) {
600 AtomPtr message =
601 internAtomError(-status, "Connect to %s:%d failed",
602 connection->server->name,
603 connection->server->port);
604 if(status != -ECLIENTRESET)
605 do_log_error(L_ERROR, -status, "Connect to %s:%d failed",
606 connection->server->name, connection->server->port);
607 connection->connecting = 0;
608 if(connection->server->request)
609 httpServerAbortRequest(connection->server->request,
610 status != -ECLIENTRESET, 504,
611 retainAtom(message));
612 httpServerAbort(connection, status != -ECLIENTRESET, 504, message);
613 return 1;
616 do_log(D_SERVER_CONN, "C %s:%d.\n",
617 connection->server->name, connection->server->port);
619 connection->connecting = 0;
620 /* serverTrigger will take care of inserting any timeouts */
621 httpServerTrigger(connection->server);
622 return 1;
626 httpServerIdleHandler(int a, FdEventHandlerPtr event)
628 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
629 HTTPServerPtr server = connection->server;
630 int i;
632 assert(!connection->request);
634 do_log(D_SERVER_CONN, "Idle connection to %s:%d died.\n",
635 connection->server->name, connection->server->port);
637 for(i = 0; i < server->maxslots; i++) {
638 if(connection == server->connection[i]) {
639 server->idleHandler[i] = NULL;
640 break;
643 assert(i < server->maxslots);
645 httpServerAbort(connection, 1, 504, internAtom("Timeout"));
646 return 1;
649 /* Discard aborted requests at the head of the queue. */
650 static void
651 httpServerDiscardRequests(HTTPServerPtr server)
653 HTTPRequestPtr request;
654 while(server->request && !server->request->request) {
655 request = server->request;
656 server->request = request->next;
657 request->next = NULL;
658 if(server->request == NULL)
659 server->request_last = NULL;
660 request->object->flags &= ~(OBJECT_INPROGRESS | OBJECT_VALIDATING);
661 releaseNotifyObject(request->object);
662 request->object = NULL;
663 httpDestroyRequest(request);
667 static int
668 pipelineIsSmall(HTTPConnectionPtr connection)
670 HTTPRequestPtr request = connection->request;
672 if(pipelineAdditionalRequests <= 0)
673 return 0;
674 else if(pipelineAdditionalRequests >= 2)
675 return 1;
677 if(!request)
678 return 1;
679 if(request->next || !(request->flags & REQUEST_PERSISTENT))
680 return 0;
681 if(request->method == METHOD_HEAD ||
682 request->method == METHOD_CONDITIONAL_GET)
683 return 1;
684 if(request->to >= 0 && connection->server->rate > 0 &&
685 request->to - request->from < connection->server->rate *
686 smallRequestTime)
687 return 1;
688 return 0;
691 static int
692 numRequests(HTTPServerPtr server)
694 int n = 0;
695 HTTPRequestPtr request = server->request;
696 while(request) {
697 n++;
698 request = request->next;
700 return n;
703 HTTPConnectionPtr
704 httpServerGetConnection(HTTPServerPtr server, int *idle_return)
706 int i, j;
707 int connecting = 0, empty = 0, idle = 0;
709 j = -1;
710 /* Try to find an idle connection */
711 for(i = 0; i < server->numslots; i++) {
712 if(server->connection[i]) {
713 if(!server->connection[i]->connecting) {
714 if(!server->connection[i]->request) {
715 if(server->idleHandler[i])
716 unregisterFdEvent(server->idleHandler[i]);
717 server->idleHandler[i] = NULL;
718 if(j < 0) j = i;
719 idle++;
721 } else
722 connecting++;
723 } else
724 empty++;
727 if(j >= 0) {
728 *idle_return = idle;
729 return server->connection[j];
732 /* If there's an empty slot, schedule connection creation */
733 if(empty) {
734 /* Don't open a connection if there are already enough in
735 progress, except if the server doesn't do persistent
736 connections and there's only one in progress. */
737 if((connecting == 0 || (server->persistent <= 0 && connecting <= 1)) ||
738 connecting < numRequests(server)) {
739 httpServerConnection(server);
743 /* Find a connection that can accept additional requests */
744 if(server->version == HTTP_11 && server->pipeline >= 4) {
745 for(i = 0; i < serverSlots; i++) {
746 if(server->connection[i] && !server->connection[i]->connecting &&
747 pipelineIsSmall(server->connection[i])) {
748 if(server->idleHandler[i])
749 unregisterFdEvent(server->idleHandler[i]);
750 server->idleHandler[i] = NULL;
751 *idle_return = 0;
752 return server->connection[i];
756 *idle_return = idle;
757 return NULL;
761 httpServerTrigger(HTTPServerPtr server)
763 HTTPConnectionPtr connection;
764 HTTPRequestPtr request;
765 int idle, n, i, rc, numidle;
767 while(server->request) {
768 httpServerDiscardRequests(server);
770 if(!server->request)
771 break;
773 if(REQUEST_SIDE(server->request)) {
774 rc = httpServerSideRequest(server);
775 /* If rc is 0, httpServerSideRequest didn't dequeue this
776 request. Go through the scheduling loop again, come
777 back later. */
778 if(rc <= 0) break;
779 continue;
781 connection = httpServerGetConnection(server, &numidle);
782 if(!connection) break;
784 /* If server->pipeline <= 0, we don't do pipelining. If
785 server->pipeline is 1, then we are ready to start probing
786 for pipelining on the server; we then send exactly two
787 requests in what is hopefully a single packet to check
788 whether the server has the nasty habit of discarding its
789 input buffers after each request.
790 If server->pipeline is 2 or 3, the pipelining probe is in
791 progress on this server, and we don't pipeline anything
792 until it succeeds. When server->pipeline >= 4, pipelining
793 is believed to work on this server. */
794 if(server->version != HTTP_11 || server->pipeline <= 0 ||
795 server->pipeline == 2 || server->pipeline == 3) {
796 if(connection->pipelined == 0)
797 n = 1;
798 else
799 n = 0;
800 } else if(server->pipeline == 1) {
801 if(connection->pipelined == 0)
802 n = MIN(2, maxPipelineTrain);
803 else
804 n = 0;
805 } else {
806 n = maxPipelineTrain;
809 /* Don't pipeline if there are more idle connections */
810 if(numidle >= 2)
811 n = MIN(n, 1);
813 idle = !connection->pipelined;
814 i = 0;
815 while(server->request && connection->pipelined < n) {
816 httpServerDiscardRequests(server);
817 if(!server->request) break;
818 request = server->request;
819 assert(request->request->request == request);
820 rc = httpWriteRequest(connection, request, -1);
821 if(rc < 0) {
822 if(i == 0)
823 httpServerAbortRequest(request, rc != -ECLIENTRESET, 503,
824 internAtom("Couldn't "
825 "write request"));
826 break;
828 do_log(D_SERVER_CONN, "W: ");
829 do_log_n(D_SERVER_CONN,
830 request->object->key, request->object->key_size);
831 do_log(D_SERVER_CONN, " (%d)\n", request->method);
832 if(connection->pipelined > 0)
833 request->flags |= REQUEST_PIPELINED;
834 request->time0 = current_time;
835 i++;
836 server->request = request->next;
837 request->next = NULL;
838 if(server->request == NULL)
839 server->request_last = NULL;
840 httpQueueRequest(connection, request);
841 connection->pipelined++;
843 if(server->persistent > 0 && server->pipeline == 1 && i >= 2)
844 server->pipeline = 2;
846 if(i > 0) httpServerSendRequest(connection);
848 if(idle && connection->pipelined > 0)
849 httpServerReply(connection, 0);
851 if(i == 0) break;
854 for(i = 0; i < server->maxslots; i++) {
855 if(server->connection[i] &&
856 !server->connection[i]->connecting &&
857 !server->connection[i]->request) {
858 /* Artificially age any fresh connections that aren't used
859 straight away; this is necessary for the logic for POST and
860 the logic that determines whether a given request should be
861 restarted. */
862 if(server->connection[i]->serviced == 0)
863 server->connection[i]->serviced = 1;
864 if(!server->idleHandler[i])
865 server->idleHandler[i] =
866 registerFdEvent(server->connection[i]->fd, POLLIN,
867 httpServerIdleHandler,
868 sizeof(HTTPConnectionPtr),
869 &server->connection[i]);
870 if(!server->idleHandler[i]) {
871 do_log(L_ERROR, "Couldn't register idle handler.\n");
872 httpServerFinish(server->connection[i], 1, 0);
877 return 1;
881 httpServerSideRequest(HTTPServerPtr server)
883 HTTPRequestPtr request = server->request;
884 HTTPConnectionPtr connection;
885 HTTPRequestPtr requestor = request->request;
886 HTTPConnectionPtr client = requestor->connection;
887 int rc, i, freeslots, idle, connecting;
889 assert(REQUEST_SIDE(request));
891 connection = NULL;
892 freeslots = 0;
893 idle = -1;
894 connecting = 0;
896 /* Find a fresh connection */
897 for(i = 0; i < server->numslots; i++) {
898 if(!server->connection[i])
899 freeslots++;
900 else if(!server->connection[i]->connecting) {
901 if(!server->connection[i]->request) {
902 if(server->connection[i]->serviced == 0) {
903 if(server->idleHandler[i])
904 unregisterFdEvent(server->idleHandler[i]);
905 server->idleHandler[i] = NULL;
906 connection = server->connection[i];
907 break;
908 } else {
909 idle = i;
912 } else {
913 connecting++;
917 if(!connection) {
918 /* Make sure that a fresh connection will be established at some
919 point, then wait until httpServerTrigger calls us again. */
920 if(freeslots) {
921 httpServerConnection(server);
922 } else {
923 if(idle >= 0) {
924 /* Shutdown a random idle connection */
925 pokeFdEvent(server->connection[idle]->fd,
926 -EDOSHUTDOWN, POLLIN | POLLOUT);
929 return 0;
932 rc = httpWriteRequest(connection, request, client->bodylen);
933 if(rc < 0) {
934 do_log(L_ERROR, "Couldn't write POST or PUT request.\n");
935 httpServerAbortRequest(request, rc != -ECLIENTRESET, 503,
936 internAtom("Couldn't write request"));
937 return 0;
939 server->request = request->next;
940 request->next = NULL;
941 if(server->request == NULL)
942 server->request_last = NULL;
943 httpQueueRequest(connection, request);
944 connection->pipelined = 1;
945 request->time0 = current_time;
946 connection->reqoffset = 0;
947 connection->bodylen = client->bodylen;
948 httpServerDoSide(connection);
949 return 1;
952 int
953 httpServerDoSide(HTTPConnectionPtr connection)
955 HTTPRequestPtr request = connection->request;
956 HTTPRequestPtr requestor = request->request;
957 HTTPConnectionPtr client = requestor->connection;
958 int len = MIN(client->reqlen - client->reqbegin,
959 connection->bodylen - connection->reqoffset);
960 int doflush =
961 len > 0 &&
962 (len >= maxSideBuffering ||
963 client->reqbegin > 0 ||
964 (connection->reqoffset + client->reqlen - client->reqbegin) >=
965 connection->bodylen);
966 int done = connection->reqoffset >= connection->bodylen;
968 assert(connection->bodylen >= 0);
970 httpSetTimeout(connection, 60);
972 if(connection->reqlen > 0) {
973 /* Send the headers, but don't send any part of the body if
974 we're in wait_continue. */
975 do_stream_2(IO_WRITE,
976 connection->fd, 0,
977 connection->reqbuf, connection->reqlen,
978 client->reqbuf + client->reqbegin,
979 (request->flags & REQUEST_WAIT_CONTINUE) ? 0 : len,
980 httpServerSideHandler2, connection);
981 httpServerReply(connection, 0);
982 } else if(request->object->flags & OBJECT_ABORTED) {
983 if(connection->reqbuf)
984 dispose_chunk(connection->reqbuf);
985 connection->reqbuf = NULL;
986 connection->reqlen = 0;
987 pokeFdEvent(connection->fd, -ESHUTDOWN, POLLIN);
988 client->flags |= CONN_SIDE_READER;
989 do_stream(IO_READ | IO_IMMEDIATE,
990 client->fd, 0, NULL, 0,
991 httpClientSideHandler, client);
992 } else if(!(request->flags & REQUEST_WAIT_CONTINUE) && doflush) {
993 /* Make sure there's a reqbuf, as httpServerFinish uses
994 it to determine if there's a writer. */
995 if(connection->reqbuf == NULL)
996 connection->reqbuf = get_chunk();
997 assert(connection->reqbuf != NULL);
998 do_stream(IO_WRITE,
999 connection->fd, 0,
1000 client->reqbuf + client->reqbegin, len,
1001 httpServerSideHandler, connection);
1002 } else {
1003 if(connection->reqbuf) {
1004 httpConnectionDestroyReqbuf(connection);
1005 connection->reqlen = 0;
1007 if(request->flags & REQUEST_WAIT_CONTINUE) {
1008 ConditionHandlerPtr chandler;
1009 do_log(D_SERVER_CONN, "W... %s:%d.\n",
1010 connection->server->name, connection->server->port);
1011 chandler =
1012 conditionWait(&request->object->condition,
1013 httpServerContinueConditionHandler,
1014 sizeof(connection), &connection);
1015 if(chandler)
1016 return 1;
1017 else
1018 do_log(L_ERROR, "Couldn't register condition handler.\n");
1019 /* Fall through -- the client side will clean up. */
1021 client->flags |= CONN_SIDE_READER;
1022 do_stream(IO_READ | (done ? IO_IMMEDIATE : 0 ) | IO_NOTNOW,
1023 client->fd, client->reqlen,
1024 client->reqbuf, CHUNK_SIZE,
1025 httpClientSideHandler, client);
1027 return 1;
1030 static int
1031 httpClientDelayedDoSideHandler(TimeEventHandlerPtr event)
1033 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1034 httpServerDoSide(connection);
1035 return 1;
1038 static int
1039 httpServerDelayedDoSide(HTTPConnectionPtr connection)
1041 TimeEventHandlerPtr handler;
1042 handler = scheduleTimeEvent(1, httpClientDelayedDoSideHandler,
1043 sizeof(connection), &connection);
1044 if(!handler) {
1045 do_log(L_ERROR, "Couldn't schedule DoSide -- freeing memory.\n");
1046 free_chunk_arenas();
1047 handler = scheduleTimeEvent(1, httpClientDelayedDoSideHandler,
1048 sizeof(connection), &connection);
1049 do_log(L_ERROR, "Couldn't schedule DoSide.\n");
1050 /* Somebody will hopefully end up timing out. */
1051 return 1;
1053 return 1;
1056 static int
1057 httpServerSideHandlerCommon(int kind, int status,
1058 FdEventHandlerPtr event,
1059 StreamRequestPtr srequest)
1061 HTTPConnectionPtr connection = srequest->data;
1062 HTTPRequestPtr request = connection->request;
1063 HTTPRequestPtr requestor = request->request;
1064 HTTPConnectionPtr client = requestor->connection;
1065 int bodylen;
1067 assert(request->object->flags & OBJECT_INPROGRESS);
1069 if(status) {
1070 do_log_error(L_ERROR, -status, "Couldn't write to server");
1071 httpConnectionDestroyReqbuf(connection);
1072 if(status != -ECLIENTRESET)
1073 shutdown(connection->fd, 2);
1074 abortObject(request->object, 503,
1075 internAtom("Couldn't write to server"));
1076 /* Let the read side handle the error */
1077 httpServerDoSide(connection);
1078 return 1;
1081 assert(srequest->offset > 0);
1083 if(kind == 2) {
1084 if(srequest->offset < connection->reqlen)
1085 return 0;
1086 bodylen = srequest->offset - connection->reqlen;
1087 connection->reqlen = 0;
1088 httpConnectionDestroyReqbuf(connection);
1089 } else {
1090 bodylen = srequest->offset;
1094 assert(client->reqbegin + bodylen <= client->reqlen);
1096 if(client->reqlen > client->reqbegin + bodylen)
1097 memmove(client->reqbuf, client->reqbuf + client->reqbegin + bodylen,
1098 client->reqlen - client->reqbegin - bodylen);
1099 client->reqlen -= bodylen + client->reqbegin;
1100 client->reqbegin = 0;
1101 connection->reqoffset += bodylen;
1102 httpServerDoSide(connection);
1103 return 1;
1107 httpServerSideHandler(int status,
1108 FdEventHandlerPtr event,
1109 StreamRequestPtr srequest)
1111 return httpServerSideHandlerCommon(1, status, event, srequest);
1115 httpServerSideHandler2(int status,
1116 FdEventHandlerPtr event,
1117 StreamRequestPtr srequest)
1119 return httpServerSideHandlerCommon(2, status, event, srequest);
1122 static int
1123 httpServerContinueConditionHandler(int status, ConditionHandlerPtr chandler)
1125 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)chandler->data;
1127 if(connection->request->flags & REQUEST_WAIT_CONTINUE)
1128 return 0;
1129 httpServerDelayedDoSide(connection);
1130 return 1;
1133 /* s is 0 to keep the connection alive, 1 to shutdown the connection,
1134 and -1 to keep the connection alive and keep the current request. */
1135 void
1136 httpServerFinish(HTTPConnectionPtr connection, int s, int offset)
1138 HTTPServerPtr server = connection->server;
1139 HTTPRequestPtr request = connection->request;
1140 int i;
1142 if(request) {
1143 assert(connection->pipelined >= 1);
1144 assert((connection->pipelined > 1) == (request->next != NULL));
1145 } else {
1146 assert(connection->pipelined == 0);
1149 if(s == 0 && (!connection->request ||
1150 !(connection->request->flags & REQUEST_PERSISTENT)))
1151 s = 1;
1153 if(connection->reqbuf) {
1154 /* As most normal requests go out in a single packet, this is
1155 extremely unlikely to happen. As for POST/PUT requests,
1156 they are not pipelined, so this can only happen if the
1157 server sent an error reply early. */
1158 assert(connection->fd >= 0);
1159 shutdown(connection->fd, 1);
1160 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLOUT);
1161 httpServerDelayedFinish(connection);
1162 goto done;
1165 if(s >= 0 && request) {
1166 /* Update statistics about the server */
1167 int size = -1, d = -1, rtt = -1, rate = -1;
1168 if(connection->offset > 0 && request->from >= 0)
1169 size = connection->offset - request->from;
1170 if(request->time1.tv_sec != null_time.tv_sec) {
1171 d = timeval_minus_usec(&current_time, &request->time1);
1172 if(!(request->flags & REQUEST_PIPELINED) &&
1173 request->time0.tv_sec != null_time.tv_sec)
1174 rtt = timeval_minus_usec(&request->time1, &request->time0);
1175 if(size >= 8192 && d > 50000)
1176 rate = ((double)size / (double)d) * 1000000.0 + 0.5;
1178 request->time0 = null_time;
1179 request->time1 = null_time;
1181 if(rtt >= 0) {
1182 if(server->rtt >= 0)
1183 server->rtt = (3 * server->rtt + rtt + 2) / 4;
1184 else
1185 server->rtt = rtt;
1187 if(rate >= 0) {
1188 if(server->rate >= 0)
1189 server->rate = (3 * server->rate + rate + 2) / 4;
1190 else
1191 server->rate = rate;
1194 httpDequeueRequest(connection);
1195 connection->pipelined--;
1196 request->object->flags &= ~(OBJECT_INPROGRESS | OBJECT_VALIDATING);
1197 if(request->request) {
1198 request->request->request = NULL;
1199 request->request = NULL;
1201 releaseNotifyObject(request->object);
1202 request->object = NULL;
1203 httpDestroyRequest(request);
1206 do_log(D_SERVER_CONN, "Done with server %s:%d connection (%d)\n",
1207 connection->server->name, connection->server->port, s);
1209 assert(offset <= connection->len);
1211 if(s <= 0) {
1212 if(offset < connection->len) {
1213 assert(connection->buf != NULL);
1214 if(!connection->pipelined) {
1215 do_log(L_WARN,
1216 "Closing connection to %s:%d: "
1217 "%d stray bytes of data.\n",
1218 server->name, server->port, connection->len - offset);
1219 s = 1;
1220 } else {
1221 memmove(connection->buf, connection->buf + offset,
1222 connection->len - offset);
1223 connection->len = connection->len - offset;
1224 if((connection->flags & CONN_BIGBUF) &&
1225 connection->len <= CHUNK_SIZE)
1226 httpConnectionUnbigify(connection);
1228 } else {
1229 connection->len = 0;
1233 connection->server->time = current_time.tv_sec;
1234 connection->serviced++;
1236 if(s > 0) {
1237 if(connection->timeout)
1238 cancelTimeEvent(connection->timeout);
1239 connection->timeout = NULL;
1240 httpConnectionDestroyBuf(connection);
1241 if(connection->fd >= 0)
1242 CLOSE(connection->fd);
1243 connection->fd = -1;
1244 server->persistent -= 1;
1245 if(server->persistent < -5)
1246 server->numslots = MIN(server->maxslots, serverMaxSlots);
1247 if(connection->request) {
1248 HTTPRequestPtr req;
1249 do_log(D_SERVER_CONN, "Restarting pipeline to %s:%d.\n",
1250 server->name, server->port);
1251 if(server->pipeline == 2)
1252 server->pipeline -= 20;
1253 else
1254 server->pipeline -= 5;
1255 req = connection->request;
1256 while(req) {
1257 req->connection = NULL;
1258 req = req->next;
1260 if(server->request)
1261 connection->request_last->next = server->request;
1262 else
1263 server->request_last = connection->request_last;
1264 server->request = connection->request;
1265 connection->request = NULL;
1266 connection->request_last = NULL;
1268 /* Make sure we don't get confused into thinking a probe
1269 is in progress. */
1270 if(server->pipeline == 2 || server->pipeline == 3)
1271 server->pipeline = 1;
1272 for(i = 0; i < server->maxslots; i++)
1273 if(connection == server->connection[i])
1274 break;
1275 assert(i < server->maxslots);
1276 if(server->idleHandler[i])
1277 unregisterFdEvent(server->idleHandler[i]);
1278 server->idleHandler[i] = NULL;
1279 server->connection[i] = NULL;
1280 free(connection);
1281 } else {
1282 server->persistent += 1;
1283 if(server->persistent > 0)
1284 server->numslots = MIN(server->maxslots, serverSlots);
1285 httpSetTimeout(connection, serverTimeout);
1286 /* See httpServerTrigger */
1287 if(connection->pipelined ||
1288 (server->version == HTTP_11 && server->pipeline <= 0) ||
1289 (server->pipeline == 3)) {
1290 server->pipeline++;
1292 if(s < 0 || connection->pipelined) {
1293 httpServerReply(connection, 1);
1294 } else {
1295 httpConnectionDestroyBuf(connection);
1299 done:
1300 httpServerTrigger(server);
1303 static int
1304 httpServerDelayedFinishHandler(TimeEventHandlerPtr event)
1306 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1307 httpServerFinish(connection, 1, 0);
1308 return 1;
1311 static void
1312 httpServerDelayedFinish(HTTPConnectionPtr connection)
1314 TimeEventHandlerPtr handler;
1316 handler = scheduleTimeEvent(1, httpServerDelayedFinishHandler,
1317 sizeof(connection), &connection);
1318 if(!handler) {
1319 do_log(L_ERROR,
1320 "Couldn't schedule delayed finish -- freeing memory.");
1321 free_chunk_arenas();
1322 handler = scheduleTimeEvent(1, httpServerDelayedFinishHandler,
1323 sizeof(connection), &connection);
1324 if(!handler) {
1325 do_log(L_ERROR,
1326 "Couldn't schedule delayed finish -- aborting.\n");
1327 polipoExit();
1332 void
1333 httpServerReply(HTTPConnectionPtr connection, int immediate)
1335 assert(connection->pipelined > 0);
1337 if(connection->request->request == NULL) {
1338 do_log(L_WARN, "Aborting pipeline on %s:%d.\n",
1339 connection->server->name, connection->server->port);
1340 httpServerFinish(connection, 1, 0);
1341 return;
1344 do_log(D_SERVER_CONN, "R: ");
1345 do_log_n(D_SERVER_CONN, connection->request->object->key,
1346 connection->request->object->key_size);
1347 do_log(D_SERVER_CONN, " (%d)\n", connection->request->method);
1349 if(connection->len == 0)
1350 httpConnectionDestroyBuf(connection);
1352 httpSetTimeout(connection, serverTimeout);
1353 do_stream_buf(IO_READ | (immediate ? IO_IMMEDIATE : 0) | IO_NOTNOW,
1354 connection->fd, connection->len,
1355 &connection->buf, CHUNK_SIZE,
1356 httpServerReplyHandler, connection);
1360 httpConnectionPipelined(HTTPConnectionPtr connection)
1362 HTTPRequestPtr request = connection->request;
1363 int i = 0;
1364 while(request) {
1365 i++;
1366 request = request->next;
1368 return i;
1371 void
1372 httpServerUnpipeline(HTTPRequestPtr request)
1374 HTTPConnectionPtr connection = request->connection;
1375 HTTPServerPtr server = connection->server;
1377 request->flags &= ~REQUEST_PERSISTENT;
1378 if(request->next) {
1379 HTTPRequestPtr req;
1380 do_log(L_WARN,
1381 "Restarting pipeline to %s:%d.\n",
1382 connection->server->name, connection->server->port);
1383 req = request->next;
1384 while(req) {
1385 req->connection = NULL;
1386 req = req->next;
1388 if(server->request)
1389 connection->request_last->next = server->request;
1390 else
1391 server->request_last = connection->request_last;
1392 server->request = request->next;
1393 request->next = NULL;
1394 connection->request_last = request;
1396 connection->pipelined = httpConnectionPipelined(connection);
1399 void
1400 httpServerRestart(HTTPConnectionPtr connection)
1402 HTTPServerPtr server = connection->server;
1403 HTTPRequestPtr request = connection->request;
1405 if(request) {
1406 HTTPRequestPtr req;
1407 if(request->next)
1408 do_log(L_WARN,
1409 "Restarting pipeline to %s:%d.\n",
1410 connection->server->name, connection->server->port);
1411 req = request;
1412 while(req) {
1413 req->connection = NULL;
1414 req = req->next;
1416 if(server->request)
1417 connection->request_last->next = server->request;
1418 else
1419 server->request_last = connection->request_last;
1420 server->request = request;
1421 connection->request = NULL;
1422 connection->request_last = NULL;
1424 connection->pipelined = 0;
1425 httpServerFinish(connection, 1, 0);
1429 httpServerRequest(ObjectPtr object, int method, int from, int to,
1430 HTTPRequestPtr requestor, void *closure)
1432 int rc;
1433 char name[132];
1434 int port;
1435 int x, y, z;
1437 assert(from >= 0 && (to < 0 || to > from));
1438 assert(closure == NULL);
1439 assert(!(object->flags & OBJECT_LOCAL));
1440 assert(object->type == OBJECT_HTTP);
1442 if(object->flags & OBJECT_INPROGRESS)
1443 return 1;
1445 if(requestor->flags & REQUEST_REQUESTED)
1446 return 0;
1448 assert(requestor->request == NULL);
1450 if(proxyOffline)
1451 return -1;
1453 rc = parseUrl(object->key, object->key_size, &x, &y, &port, &z);
1455 if(rc < 0 || x < 0 || y < 0 || y - x > 131) {
1456 do_log(L_ERROR, "Couldn't parse URL: ");
1457 do_log_n(L_ERROR, object->key, object->key_size);
1458 do_log(L_ERROR, "\n");
1459 abortObject(object, 400, internAtom("Couldn't parse URL"));
1460 notifyObject(object);
1461 return 1;
1464 if(!intListMember(port, allowedPorts)) {
1465 do_log(L_ERROR, "Attempted connection to port %d.\n", port);
1466 abortObject(object, 403, internAtom("Forbidden port"));
1467 notifyObject(object);
1468 return 1;
1471 memcpy(name, ((char*)object->key) + x, y - x);
1472 name[y - x] = '\0';
1474 requestor->flags |= REQUEST_REQUESTED;
1475 rc = httpMakeServerRequest(name, port, object, method, from, to,
1476 requestor);
1478 if(rc < 0) {
1479 abortObject(object,
1480 503, internAtom("Couldn't schedule server request"));
1481 notifyObject(object);
1482 return 1;
1485 return 1;
1489 httpWriteRequest(HTTPConnectionPtr connection, HTTPRequestPtr request,
1490 int bodylen)
1492 ObjectPtr object = request->object;
1493 int from = request->from, to = request->to, method = request->method;
1494 char *url = object->key, *m;
1495 int url_size = object->key_size;
1496 int x, y, port, z, location_size;
1497 char *location;
1498 int l, n, rc, bufsize;
1500 assert(method != METHOD_NONE);
1502 if(request->method == METHOD_GET ||
1503 request->method == METHOD_CONDITIONAL_GET) {
1504 if(to >= 0) {
1505 assert(to >= from);
1506 if(to == from) {
1507 do_log(L_ERROR, "Requesting empty segment?\n");
1508 return -1;
1512 if(object->flags & OBJECT_DYNAMIC) {
1513 from = 0;
1514 to = -1;
1515 } else {
1516 objectFillFromDisk(object, from / CHUNK_SIZE * CHUNK_SIZE, 1);
1517 l = objectHoleSize(request->object, from);
1518 if(l > 0) {
1519 if(to <= 0 || to > from + l)
1520 to = from + l;
1523 if(pmmSize && connection->server->pipeline >= 4) {
1524 if(from == 0)
1525 to = to < 0 ? pmmFirstSize : MIN(to, pmmFirstSize);
1526 else
1527 to = to < 0 ? from + pmmSize : MIN(to, from + pmmSize);
1530 if(from % CHUNK_SIZE != 0)
1531 if(objectHoleSize(object, from - 1) != 0)
1532 from = from / CHUNK_SIZE * CHUNK_SIZE;
1536 rc = parseUrl(url, url_size, &x, &y, &port, &z);
1538 if(rc < 0 || x < 0 || y < 0) {
1539 return -1;
1542 if(connection->reqbuf == NULL) {
1543 connection->reqbuf = get_chunk();
1544 if(connection->reqbuf == NULL)
1545 return -1;
1546 connection->reqlen = 0;
1549 if(method == METHOD_CONDITIONAL_GET &&
1550 object->last_modified < 0 && object->etag == NULL)
1551 method = request->method = METHOD_GET;
1553 again:
1554 bufsize =
1555 (connection->flags & CONN_BIGREQBUF) ? bigBufferSize : CHUNK_SIZE;
1556 n = connection->reqlen;
1557 switch(method) {
1558 case METHOD_GET:
1559 case METHOD_CONDITIONAL_GET: m = "GET"; break;
1560 case METHOD_HEAD: m = "HEAD"; break;
1561 case METHOD_POST: m = "POST"; break;
1562 case METHOD_PUT: m = "PUT"; break;
1563 default: abort();
1565 n = snnprintf(connection->reqbuf, n, bufsize, "%s ", m);
1567 if(connection->server->isProxy) {
1568 n = snnprint_n(connection->reqbuf, n, bufsize,
1569 url, url_size);
1570 } else {
1571 if(url_size - z == 0) {
1572 location = "/";
1573 location_size = 1;
1574 } else {
1575 location = url + z;
1576 location_size = url_size - z;
1579 n = snnprint_n(connection->reqbuf, n, bufsize,
1580 location, location_size);
1583 do_log(D_SERVER_REQ, "Server request: ");
1584 do_log_n(D_SERVER_REQ, url + x, y - x);
1585 do_log(D_SERVER_REQ, ": ");
1586 do_log_n(D_SERVER_REQ, connection->reqbuf, n);
1587 do_log(D_SERVER_REQ, " (method %d from %d to %d, 0x%lx for 0x%lx)\n",
1588 method, from, to,
1589 (unsigned long)connection, (unsigned long)object);
1591 n = snnprintf(connection->reqbuf, n, bufsize, " HTTP/1.1");
1593 n = snnprintf(connection->reqbuf, n, bufsize, "\r\nHost: ");
1594 n = snnprint_n(connection->reqbuf, n, bufsize, url + x, y - x);
1595 if(port != 80)
1596 n = snnprintf(connection->reqbuf, n, bufsize, ":%d", port);
1598 if(connection->server->isProxy && parentAuthCredentials) {
1599 n = buildServerAuthHeaders(connection->reqbuf, n, bufsize,
1600 parentAuthCredentials);
1603 if(bodylen >= 0)
1604 n = snnprintf(connection->reqbuf, n, bufsize,
1605 "\r\nContent-Length: %d", bodylen);
1607 if(request->flags & REQUEST_WAIT_CONTINUE)
1608 n = snnprintf(connection->reqbuf, n, bufsize,
1609 "\r\nExpect: 100-continue");
1611 if(method != METHOD_HEAD && (from > 0 || to >= 0)) {
1612 if(to >= 0) {
1613 n = snnprintf(connection->reqbuf, n, bufsize,
1614 "\r\nRange: bytes=%d-%d", from, to - 1);
1615 } else {
1616 n = snnprintf(connection->reqbuf, n, bufsize,
1617 "\r\nRange: bytes=%d-", from);
1621 if(method == METHOD_GET && object->etag && (from > 0 || to >= 0)) {
1622 if(request->request && request->request->request == request &&
1623 request->request->from == 0 && request->request->to == -1 &&
1624 pmmSize == 0 && pmmFirstSize == 0)
1625 n = snnprintf(connection->reqbuf, n, bufsize,
1626 "\r\nIf-Range: \"%s\"", object->etag);
1629 if(method == METHOD_CONDITIONAL_GET) {
1630 if(object->last_modified >= 0) {
1631 n = snnprintf(connection->reqbuf, n, bufsize,
1632 "\r\nIf-Modified-Since: ");
1633 n = format_time(connection->reqbuf, n, bufsize,
1634 object->last_modified);
1636 if(object->etag) {
1637 n = snnprintf(connection->reqbuf, n, bufsize,
1638 "\r\nIf-None-Match: \"%s\"", object->etag);
1642 n = httpPrintCacheControl(connection->reqbuf, n, bufsize,
1643 0, &request->cache_control);
1644 if(n < 0)
1645 goto fail;
1647 if(request->request && request->request->headers) {
1648 n = snnprint_n(connection->reqbuf, n, bufsize,
1649 request->request->headers->string,
1650 request->request->headers->length);
1652 if(!disableVia) {
1653 if(request->request && request->request->via) {
1654 n = snnprintf(connection->reqbuf, n, bufsize,
1655 "\r\nVia: %s, 1.1 %s",
1656 request->request->via->string, proxyName->string);
1657 } else {
1658 n = snnprintf(connection->reqbuf, n, bufsize,
1659 "\r\nVia: 1.1 %s",
1660 proxyName->string);
1664 n = snnprintf(connection->reqbuf, n, bufsize,
1665 "\r\nConnection: %s\r\n\r\n",
1666 (request->flags & REQUEST_PERSISTENT) ?
1667 "keep-alive" : "close");
1668 if(n < 0 || n >= bufsize - 1)
1669 goto fail;
1670 connection->reqlen = n;
1671 return n;
1673 fail:
1674 rc = 0;
1675 if(!(connection->flags & CONN_BIGREQBUF))
1676 rc = httpConnectionBigifyReqbuf(connection);
1677 if(rc == 1)
1678 goto again;
1679 return -1;
1683 httpServerHandler(int status,
1684 FdEventHandlerPtr event,
1685 StreamRequestPtr srequest)
1687 HTTPConnectionPtr connection = srequest->data;
1688 AtomPtr message;
1690 assert(connection->request->object->flags & OBJECT_INPROGRESS);
1692 if(connection->reqlen == 0) {
1693 do_log(D_SERVER_REQ, "Writing aborted on 0x%lx\n",
1694 (unsigned long)connection);
1695 goto fail;
1698 if(status == 0 && !streamRequestDone(srequest)) {
1699 httpSetTimeout(connection, serverTimeout);
1700 return 0;
1703 httpConnectionDestroyReqbuf(connection);
1705 if(status) {
1706 if(connection->serviced >= 1) {
1707 httpServerRestart(connection);
1708 return 1;
1710 if(status >= 0 || status == ECONNRESET) {
1711 message = internAtom("Couldn't send request to server: "
1712 "short write");
1713 } else {
1714 if(status != -EPIPE)
1715 do_log_error(L_ERROR, -status,
1716 "Couldn't send request to server");
1717 message =
1718 internAtomError(-status, "Couldn't send request to server");
1720 goto fail;
1723 return 1;
1725 fail:
1726 dispose_chunk(connection->reqbuf);
1727 connection->reqbuf = NULL;
1728 shutdown(connection->fd, 2);
1729 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLIN);
1730 httpSetTimeout(connection, 60);
1731 return 1;
1735 httpServerSendRequest(HTTPConnectionPtr connection)
1737 assert(connection->server);
1739 if(connection->reqlen == 0) {
1740 do_log(D_SERVER_REQ,
1741 "Writing aborted on 0x%lx\n", (unsigned long)connection);
1742 httpConnectionDestroyReqbuf(connection);
1743 shutdown(connection->fd, 2);
1744 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLIN | POLLOUT);
1745 return -1;
1748 httpSetTimeout(connection, serverTimeout);
1749 do_stream(IO_WRITE, connection->fd, 0,
1750 connection->reqbuf, connection->reqlen,
1751 httpServerHandler, connection);
1752 return 1;
1756 httpServerReplyHandler(int status,
1757 FdEventHandlerPtr event,
1758 StreamRequestPtr srequest)
1760 HTTPConnectionPtr connection = srequest->data;
1761 HTTPRequestPtr request = connection->request;
1762 int i, body;
1763 int bufsize =
1764 (connection->flags & CONN_BIGBUF) ? bigBufferSize : CHUNK_SIZE;
1766 assert(request->object->flags & OBJECT_INPROGRESS);
1767 if(status < 0) {
1768 if(connection->serviced >= 1) {
1769 httpServerRestart(connection);
1770 return 1;
1772 if(status != -ECLIENTRESET)
1773 do_log_error(L_ERROR, -status, "Read from server failed");
1774 httpServerAbort(connection, status != -ECLIENTRESET, 502,
1775 internAtomError(-status, "Read from server failed"));
1776 return 1;
1779 i = findEndOfHeaders(connection->buf, 0, srequest->offset, &body);
1780 connection->len = srequest->offset;
1782 if(i >= 0) {
1783 request->time1 = current_time;
1784 return httpServerHandlerHeaders(status, event, srequest, connection);
1787 if(status) {
1788 if(connection->serviced >= 1) {
1789 httpServerRestart(connection);
1790 return 1;
1792 if(status < 0) {
1793 do_log(L_ERROR,
1794 "Error reading server headers: %d\n", -status);
1795 httpServerAbort(connection, status != -ECLIENTRESET, 502,
1796 internAtomError(-status,
1797 "Error reading server headers"));
1798 } else
1799 httpServerAbort(connection, 1, 502,
1800 internAtom("Server dropped connection"));
1801 return 1;
1804 if(connection->len >= bufsize) {
1805 int rc = 0;
1806 if(!(connection->flags & CONN_BIGBUF))
1807 rc = httpConnectionBigify(connection);
1808 if(rc == 0) {
1809 do_log(L_ERROR, "Couldn't find end of server's headers.\n");
1810 httpServerAbort(connection, 1, 502,
1811 internAtom("Couldn't find end "
1812 "of server's headers"));
1813 return 1;
1814 } else if(rc < 0) {
1815 do_log(L_ERROR, "Couldn't allocate big buffer.\n");
1816 httpServerAbort(connection, 1, 500,
1817 internAtom("Couldn't allocate big buffer"));
1818 return 1;
1820 /* Can't just return 0 -- buf has moved. */
1821 do_stream(IO_READ,
1822 connection->fd, connection->len,
1823 connection->buf, bigBufferSize,
1824 httpServerReplyHandler, connection);
1825 return 1;
1828 return 0;
1832 httpServerHandlerHeaders(int eof,
1833 FdEventHandlerPtr event,
1834 StreamRequestPtr srequest,
1835 HTTPConnectionPtr connection)
1837 HTTPRequestPtr request = connection->request;
1838 ObjectPtr object = request->object;
1839 int rc;
1840 int code, version;
1841 int full_len;
1842 AtomPtr headers;
1843 int len;
1844 int te;
1845 CacheControlRec cache_control;
1846 int age = -1;
1847 time_t date, last_modified, expires;
1848 struct timeval *init_time;
1849 char *etag;
1850 AtomPtr via, new_via;
1851 int expect_body;
1852 HTTPRangeRec content_range;
1853 ObjectPtr new_object = NULL, old_object = NULL;
1854 int supersede = 0;
1855 AtomPtr message = NULL;
1856 int suspectDynamic;
1857 AtomPtr url = NULL;
1859 assert(request->object->flags & OBJECT_INPROGRESS);
1860 assert(eof >= 0);
1862 httpSetTimeout(connection, -1);
1864 if(request->flags & REQUEST_WAIT_CONTINUE) {
1865 do_log(D_SERVER_CONN, "W %s:%d.\n",
1866 connection->server->name, connection->server->port);
1867 request->flags &= ~REQUEST_WAIT_CONTINUE;
1870 rc = httpParseServerFirstLine(connection->buf, &code, &version, &message);
1871 if(rc <= 0) {
1872 do_log(L_ERROR, "Couldn't parse server status line.\n");
1873 httpServerAbort(connection, 1, 502,
1874 internAtom("Couldn't parse server status line"));
1875 return 1;
1878 do_log(D_SERVER_REQ, "Server status: ");
1879 do_log_n(D_SERVER_REQ, connection->buf,
1880 connection->buf[rc - 1] == '\r' ? rc - 2 : rc - 2);
1881 do_log(D_SERVER_REQ, " (0x%lx for 0x%lx)\n",
1882 (unsigned long)connection, (unsigned long)object);
1884 if(version != HTTP_10 && version != HTTP_11) {
1885 do_log(L_ERROR, "Unknown server HTTP version\n");
1886 httpServerAbort(connection, 1, 502,
1887 internAtom("Unknown server HTTP version"));
1888 releaseAtom(message);
1889 return 1;
1892 connection->version = version;
1893 connection->server->version = version;
1894 request->flags |= REQUEST_PERSISTENT;
1896 url = internAtomN(object->key, object->key_size);
1897 rc = httpParseHeaders(0, url, connection->buf, rc, request,
1898 &headers, &len, &cache_control, NULL, &te,
1899 &date, &last_modified, &expires, NULL, NULL, NULL,
1900 &age, &etag, NULL, NULL, &content_range,
1901 NULL, &via, NULL);
1902 if(rc < 0) {
1903 do_log(L_ERROR, "Couldn't parse server headers\n");
1904 releaseAtom(url);
1905 releaseAtom(message);
1906 httpServerAbort(connection, 1, 502,
1907 internAtom("Couldn't parse server headers"));
1908 return 1;
1911 if(code == 100) {
1912 releaseAtom(url);
1913 releaseAtom(message);
1914 /* We've already reset wait_continue above, but we must still
1915 ensure that the writer notices. */
1916 notifyObject(request->object);
1917 httpServerFinish(connection, -1, rc);
1918 return 1;
1921 if(code == 101) {
1922 httpServerAbort(connection, 1, 501,
1923 internAtom("Upgrade not implemented"));
1924 goto fail;
1927 if(via && !checkVia(proxyName, via)) {
1928 httpServerAbort(connection, 1, 504, internAtom("Proxy loop detected"));
1929 goto fail;
1931 full_len = content_range.full_length;
1933 if(code == 206) {
1934 if(content_range.from == -1 || content_range.to == -1) {
1935 do_log(L_ERROR, "Partial content without range.\n");
1936 httpServerAbort(connection, 1, 502,
1937 internAtom("Partial content without range"));
1938 goto fail;
1940 if(len >= 0 && len != content_range.to - content_range.from) {
1941 do_log(L_ERROR, "Inconsistent partial content.\n");
1942 httpServerAbort(connection, 1, 502,
1943 internAtom("Inconsistent partial content"));
1944 goto fail;
1946 } else if(code < 400 &&
1947 (content_range.from >= 0 || content_range.to >= 0 ||
1948 content_range.full_length >= 0)) {
1949 do_log(L_WARN, "Range without partial content.\n");
1950 /* Damn anakata. */
1951 content_range.from = -1;
1952 content_range.to = -1;
1953 content_range.full_length = -1;
1954 } else if(code != 304 && code != 412) {
1955 full_len = len;
1958 if(te != TE_IDENTITY && te != TE_CHUNKED) {
1959 do_log(L_ERROR, "Unsupported transfer-encoding\n");
1960 httpServerAbort(connection, 1, 502,
1961 internAtom("Unsupported transfer-encoding"));
1962 goto fail;
1965 if(code == 304) {
1966 if(request->method != METHOD_CONDITIONAL_GET) {
1967 do_log(L_ERROR, "Unexpected \"not changed\" reply from server\n");
1968 httpServerAbort(connection, 1, 502,
1969 internAtom("Unexpected \"not changed\" "
1970 "reply from server"));
1971 goto fail;
1973 if(object->etag && !etag) {
1974 /* RFC 2616 10.3.5. Violated by some front-end proxies. */
1975 do_log(L_WARN, "\"Not changed\" reply with no ETag.\n");
1979 if(code == 412) {
1980 if(request->method != METHOD_CONDITIONAL_GET ||
1981 (!object->etag && !object->last_modified)) {
1982 do_log(L_ERROR,
1983 "Unexpected \"precondition failed\" reply from server.\n");
1984 httpServerAbort(connection, 1, 502,
1985 internAtom("Unexpected \"precondition failed\" "
1986 "reply from server"));
1987 goto fail;
1991 releaseAtom(url);
1993 /* Okay, we're going to accept this reply. */
1995 if((code == 200 || code == 206 || code == 304 || code == 412) &&
1996 (cache_control.flags & (CACHE_NO | CACHE_NO_STORE) ||
1997 cache_control.max_age == 0 ||
1998 (cacheIsShared && cache_control.s_maxage == 0) ||
1999 (expires >= 0 && expires <= object->age))) {
2000 do_log(L_UNCACHEABLE, "Uncacheable object ");
2001 do_log_n(L_UNCACHEABLE, object->key, object->key_size);
2002 do_log(L_UNCACHEABLE, " (%d)\n", cache_control.flags);
2005 if(request->time0.tv_sec != null_time.tv_sec)
2006 init_time = &request->time0;
2007 else
2008 init_time = &current_time;
2009 age = MIN(init_time->tv_sec - age, init_time->tv_sec);
2011 if(request->method == METHOD_HEAD ||
2012 code < 200 || code == 204 || code == 304)
2013 expect_body = 0;
2014 else if(te == TE_IDENTITY)
2015 expect_body = (len != 0);
2016 else
2017 expect_body = 1;
2019 connection->chunk_remaining = -1;
2020 connection->te = te;
2022 old_object = object;
2024 connection->server->lies--;
2026 if(object->cache_control & CACHE_MISMATCH)
2027 supersede = 1;
2029 if(code == 304 || code == 412) {
2030 if((object->etag && etag && strcmp(object->etag, etag) != 0) ||
2031 (object->last_modified >= 0 && last_modified >= 0 &&
2032 object->last_modified != last_modified)) {
2033 do_log(L_ERROR, "Inconsistent \"%s\" reply for ",
2034 code == 304 ? "not changed":"precondition failed");
2035 do_log_n(L_ERROR, object->key, object->key_size);
2036 do_log(L_ERROR, "\n");
2037 object->flags |= OBJECT_DYNAMIC;
2038 supersede = 1;
2040 } else if(!(object->flags & OBJECT_INITIAL)) {
2041 if((object->last_modified < 0 || last_modified < 0) &&
2042 (!object->etag || !etag))
2043 supersede = 1;
2044 else if(object->last_modified != last_modified)
2045 supersede = 1;
2046 else if(object->etag || etag) {
2047 /* We need to be permissive here so as to deal with some
2048 front-end proxies that discard ETags on partial
2049 replies but not on full replies. */
2050 if(etag && object->etag && strcmp(object->etag, etag) != 0)
2051 supersede = 1;
2052 else if(!object->etag)
2053 supersede = 1;
2056 if(!supersede && (object->cache_control & CACHE_VARY) &&
2057 dontTrustVaryETag >= 1) {
2058 /* Check content-type to work around mod_gzip bugs */
2059 if(!httpHeaderMatch(atomContentType, object->headers, headers) ||
2060 !httpHeaderMatch(atomContentEncoding, object->headers, headers))
2061 supersede = 1;
2064 if(full_len < 0 && te == TE_IDENTITY) {
2065 /* It's an HTTP/1.0 CGI. Be afraid. */
2066 if(expect_body && content_range.from < 0 && content_range.to < 0)
2067 supersede = 1;
2070 if(!supersede && object->length >= 0 && full_len >= 0 &&
2071 object->length != full_len) {
2072 do_log(L_WARN, "Inconsistent length.\n");
2073 supersede = 1;
2076 if(!supersede &&
2077 ((object->last_modified >= 0 && last_modified >= 0) ||
2078 (object->etag && etag))) {
2079 if(request->method == METHOD_CONDITIONAL_GET) {
2080 do_log(L_WARN, "Server ignored conditional request.\n");
2081 connection->server->lies += 10;
2082 /* Drop the connection? */
2085 } else if(code == 416) {
2086 do_log(L_ERROR, "Unexpected \"range not satisfiable\" reply\n");
2087 httpServerAbort(connection, 1, 502,
2088 internAtom("Unexpected \"range not satisfiable\" "
2089 "reply"));
2090 /* The object may be superseded. Make sure the next request
2091 won't be partial. */
2092 abortObject(object, 502,
2093 internAtom("Unexpected \"range not satisfiable\" reply"));
2094 return 1;
2097 if(object->flags & OBJECT_INITIAL)
2098 supersede = 0;
2100 if(supersede) {
2101 do_log(L_SUPERSEDED, "Superseding object: ");
2102 do_log_n(L_SUPERSEDED, old_object->key, old_object->key_size);
2103 do_log(L_SUPERSEDED, " (%d %d %d %s -> %d %d %d %s)\n",
2104 object->code, object->length, (int)object->last_modified,
2105 object->etag?object->etag: "(none)",
2106 code, full_len, (int)last_modified,
2107 etag?etag:"(none)");
2108 privatiseObject(old_object, 0);
2109 new_object = makeObject(object->type, object->key,
2110 object->key_size, 1, 0,
2111 object->request, NULL);
2112 if(new_object == NULL) {
2113 do_log(L_ERROR, "Couldn't allocate object\n");
2114 httpServerAbort(connection, 1, 500,
2115 internAtom("Couldn't allocate object"));
2116 return 1;
2118 if(urlIsLocal(new_object->key, new_object->key_size))
2119 new_object->flags |= OBJECT_LOCAL;
2120 } else {
2121 new_object = object;
2124 suspectDynamic =
2125 (!etag && last_modified < 0) ||
2126 (cache_control.flags &
2127 (CACHE_NO_HIDDEN | CACHE_NO | CACHE_NO_STORE |
2128 (cacheIsShared ? CACHE_PRIVATE : 0))) ||
2129 (cache_control.max_age >= 0 && cache_control.max_age <= 2) ||
2130 (cacheIsShared &&
2131 cache_control.s_maxage >= 0 && cache_control.s_maxage <= 5) ||
2132 (old_object->last_modified >= 0 && old_object->expires >= 0 &&
2133 (old_object->expires - old_object->last_modified <= 1)) ||
2134 (supersede && (old_object->date - date <= 5));
2136 if(suspectDynamic)
2137 new_object->flags |= OBJECT_DYNAMIC;
2138 else if(!supersede)
2139 new_object->flags &= ~OBJECT_DYNAMIC;
2140 else if(old_object->flags & OBJECT_DYNAMIC)
2141 new_object->flags |= OBJECT_DYNAMIC;
2143 new_object->age = age;
2144 new_object->cache_control |= cache_control.flags;
2145 new_object->max_age = cache_control.max_age;
2146 new_object->s_maxage = cache_control.s_maxage;
2147 new_object->flags &= ~OBJECT_FAILED;
2149 if(date >= 0)
2150 new_object->date = date;
2151 if(last_modified >= 0)
2152 new_object->last_modified = last_modified;
2153 if(expires >= 0)
2154 new_object->expires = expires;
2155 if(new_object->etag == NULL)
2156 new_object->etag = etag;
2157 else
2158 free(etag);
2160 switch(code) {
2161 case 200:
2162 case 300: case 301: case 302: case 303: case 307:
2163 case 403: case 404: case 405: case 401:
2164 if(new_object->message) releaseAtom(new_object->message);
2165 new_object->code = code;
2166 new_object->message = message;
2167 break;
2168 case 206: case 304: case 412:
2169 if(new_object->code != 200 || !new_object->message) {
2170 if(new_object->message) releaseAtom(new_object->message);
2171 new_object->code = 200;
2172 new_object->message = internAtom("OK");
2174 releaseAtom(message);
2175 break;
2176 default:
2177 if(new_object->message) releaseAtom(new_object->message);
2178 new_object->code = code;
2179 new_object->message = retainAtom(message);
2180 break;
2183 httpTweakCachability(new_object);
2185 if(!via)
2186 new_via = internAtomF("%s %s",
2187 version == HTTP_11 ? "1.1" : "1.0",
2188 proxyName->string);
2189 else
2190 new_via = internAtomF("%s, %s %s", via->string,
2191 version == HTTP_11 ? "1.1" : "1.0",
2192 proxyName->string);
2193 if(new_via == NULL) {
2194 do_log(L_ERROR, "Couldn't allocate Via.\n");
2195 } else {
2196 if(new_object->via) releaseAtom(new_object->via);
2197 new_object->via = new_via;
2200 if(new_object->flags & OBJECT_INITIAL) {
2201 objectPartial(new_object, full_len, headers);
2202 } else {
2203 if(new_object->length < 0)
2204 new_object->length = full_len;
2205 /* XXX -- RFC 2616 13.5.3 */
2206 releaseAtom(headers);
2209 if(supersede) {
2210 assert(new_object != old_object);
2211 supersedeObject(old_object);
2214 if(new_object != old_object) {
2215 if(new_object->flags & OBJECT_INPROGRESS) {
2216 /* Make sure we don't fetch this object two times at the
2217 same time. Just drop the connection. */
2218 releaseObject(new_object);
2219 httpServerFinish(connection, 1, 0);
2220 return 1;
2222 old_object->flags &= ~OBJECT_VALIDATING;
2223 new_object->flags |= OBJECT_INPROGRESS;
2224 /* Signal the client side to switch to the new object -- see
2225 httpClientGetHandler. If it doesn't, we'll give up on this
2226 request below. */
2227 new_object->flags |= OBJECT_MUTATING;
2228 request->can_mutate = new_object;
2229 notifyObject(old_object);
2230 request->can_mutate = NULL;
2231 new_object->flags &= ~OBJECT_MUTATING;
2232 old_object->flags &= ~OBJECT_INPROGRESS;
2233 if(request->object == old_object) {
2234 if(request->request)
2235 request->request->request = NULL;
2236 request->request = NULL;
2237 request->object = new_object;
2238 } else {
2239 assert(request->object == new_object);
2241 releaseNotifyObject(old_object);
2242 old_object = NULL;
2243 object = new_object;
2244 } else {
2245 objectMetadataChanged(new_object, 0);
2248 if(object->flags & OBJECT_VALIDATING) {
2249 object->flags &= ~OBJECT_VALIDATING;
2250 notifyObject(object);
2253 if(!expect_body) {
2254 httpServerFinish(connection, 0, rc);
2255 return 1;
2258 if(request->request == NULL) {
2259 httpServerFinish(connection, 1, 0);
2260 return 1;
2263 if(code == 412) {
2264 /* 412 replies contain a useless body. For now, we
2265 drop the connection. */
2266 httpServerFinish(connection, 1, 0);
2267 return 1;
2271 if(request->flags & REQUEST_PERSISTENT) {
2272 if(request->method != METHOD_HEAD &&
2273 connection->te == TE_IDENTITY && len < 0) {
2274 do_log(L_ERROR, "Persistent reply with no Content-Length\n");
2275 /* That's potentially dangerous, as we could start reading
2276 arbitrary data into the object. Unfortunately, some
2277 servers do that. */
2278 request->flags &= ~REQUEST_PERSISTENT;
2282 /* we're getting a body */
2283 if(content_range.from > 0)
2284 connection->offset = content_range.from;
2285 else
2286 connection->offset = 0;
2288 if(content_range.to >= 0)
2289 request->to = content_range.to;
2291 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2292 (unsigned long)connection, (unsigned long)object,
2293 connection->offset);
2295 if(connection->len > rc) {
2296 rc = connectionAddData(connection, rc);
2297 if(rc) {
2298 if(rc < 0) {
2299 if(rc == -2) {
2300 do_log(L_ERROR, "Couldn't parse chunk size.\n");
2301 httpServerAbort(connection, 1, 502,
2302 internAtom("Couldn't parse chunk size"));
2303 } else {
2304 do_log(L_ERROR, "Couldn't add data to connection.\n");
2305 httpServerAbort(connection, 1, 500,
2306 internAtom("Couldn't add data "
2307 "to connection"));
2309 return 1;
2310 } else {
2311 if(code != 206) {
2312 if(object->length < 0) {
2313 object->length = object->size;
2314 objectMetadataChanged(object, 0);
2315 } else if(object->length != object->size) {
2316 httpServerAbort(connection, 1, 500,
2317 internAtom("Inconsistent "
2318 "object size"));
2319 object->length = -1;
2320 return 1;
2323 httpServerFinish(connection, 0, 0);
2324 return 1;
2327 } else {
2328 connection->len = 0;
2331 if(eof) {
2332 if(connection->te == TE_CHUNKED ||
2333 (object->length >= 0 &&
2334 connection->offset < object->length)) {
2335 do_log(L_ERROR, "Server closed connection.\n");
2336 httpServerAbort(connection, 1, 502,
2337 internAtom("Server closed connection"));
2338 return 1;
2339 } else {
2340 if(code != 206 && eof > 0 && object->length < 0) {
2341 object->length = object->size;
2342 objectMetadataChanged(object, 0);
2344 httpServerFinish(connection, 1, 0);
2345 return 1;
2347 } else {
2348 return httpServerReadData(connection, 1);
2350 return 0;
2352 fail:
2353 releaseAtom(url);
2354 releaseAtom(message);
2355 if(headers)
2356 releaseAtom(headers);
2357 if(etag)
2358 free(etag);
2359 if(via)
2360 releaseAtom(via);
2361 return 1;
2365 httpServerIndirectHandlerCommon(HTTPConnectionPtr connection, int eof)
2367 HTTPRequestPtr request = connection->request;
2369 assert(eof >= 0);
2370 assert(request->object->flags & OBJECT_INPROGRESS);
2372 if(connection->len > 0) {
2373 int rc;
2374 rc = connectionAddData(connection, 0);
2375 if(rc) {
2376 if(rc < 0) {
2377 if(rc == -2) {
2378 do_log(L_ERROR, "Couldn't parse chunk size.\n");
2379 httpServerAbort(connection, 1, 502,
2380 internAtom("Couldn't parse chunk size"));
2381 } else {
2382 do_log(L_ERROR, "Couldn't add data to connection.\n");
2383 httpServerAbort(connection, 1, 500,
2384 internAtom("Couldn't add data "
2385 "to connection"));
2387 return 1;
2388 } else {
2389 if(request->to < 0) {
2390 if(request->object->length < 0) {
2391 request->object->length = request->object->size;
2392 objectMetadataChanged(request->object, 0);
2393 } else if(request->object->length !=
2394 request->object->size) {
2395 request->object->length = -1;
2396 httpServerAbort(connection, 1, 502,
2397 internAtom("Inconsistent "
2398 "object size"));
2399 return 1;
2402 httpServerFinish(connection, 0, 0);
2404 return 1;
2408 if(eof && connection->len == 0) {
2409 if(connection->te == TE_CHUNKED ||
2410 (request->to >= 0 && connection->offset < request->to)) {
2411 do_log(L_ERROR, "Server dropped connection.\n");
2412 httpServerAbort(connection, 1, 502,
2413 internAtom("Server dropped connection"));
2414 return 1;
2415 } else {
2416 if(request->object->length < 0 && eof > 0 &&
2417 (request->to < 0 || request->to > request->object->size)) {
2418 request->object->length = request->object->size;
2419 objectMetadataChanged(request->object, 0);
2421 httpServerFinish(connection, 1, 0);
2422 return 1;
2424 } else {
2425 return httpServerReadData(connection, 0);
2430 httpServerIndirectHandler(int status,
2431 FdEventHandlerPtr event,
2432 StreamRequestPtr srequest)
2434 HTTPConnectionPtr connection = srequest->data;
2435 assert(connection->request->object->flags & OBJECT_INPROGRESS);
2437 httpSetTimeout(connection, -1);
2438 if(status < 0) {
2439 if(status != -ECLIENTRESET)
2440 do_log_error(L_ERROR, -status, "Read from server failed");
2441 httpServerAbort(connection, status != -ECLIENTRESET, 502,
2442 internAtomError(-status, "Read from server failed"));
2443 return 1;
2446 connection->len = srequest->offset;
2448 return httpServerIndirectHandlerCommon(connection, status);
2452 httpServerReadData(HTTPConnectionPtr connection, int immediate)
2454 HTTPRequestPtr request = connection->request;
2455 ObjectPtr object = request->object;
2456 int to = -1;
2458 assert(object->flags & OBJECT_INPROGRESS);
2460 if(request->request == NULL) {
2461 httpServerFinish(connection, 1, 0);
2462 return 1;
2465 if(request->to >= 0)
2466 to = request->to;
2467 else
2468 to = object->length;
2470 if(to >= 0 && to == connection->offset) {
2471 httpServerFinish(connection, 0, 0);
2472 return 1;
2475 if(connection->len == 0 &&
2476 ((connection->te == TE_IDENTITY && to > connection->offset) ||
2477 (connection->te == TE_CHUNKED && connection->chunk_remaining > 0))) {
2478 /* Read directly into the object */
2479 int i = connection->offset / CHUNK_SIZE;
2480 int j = connection->offset % CHUNK_SIZE;
2481 int end, len, more;
2482 /* See httpServerDirectHandlerCommon if you change this */
2483 if(connection->te == TE_CHUNKED) {
2484 len = connection->chunk_remaining;
2485 /* The logic here is that we want more to just fit the
2486 chunk header if we're doing a large read, but do a
2487 large read if we would otherwise do a small one. The
2488 magic constant 2000 comes from the assumption that the
2489 server uses chunks that have a size that are a power of
2490 two (possibly including the chunk header), and that we
2491 want a full ethernet packet to fit into our read. */
2492 more = (len >= 2000 ? 20 : MIN(2048 - len, CHUNK_SIZE));
2493 } else {
2494 len = to - connection->offset;
2495 /* We read more data only when there is a reasonable
2496 chance of there being another reply coming. */
2497 more = (connection->pipelined > 1) ? CHUNK_SIZE : 0;
2499 end = len + connection->offset;
2501 httpConnectionDestroyBuf(connection);
2503 /* The order of allocation is important in case we run out of
2504 memory. */
2505 lockChunk(object, i);
2506 if(object->chunks[i].data == NULL)
2507 object->chunks[i].data = get_chunk();
2508 if(object->chunks[i].data && object->chunks[i].size >= j) {
2509 if(len + j > CHUNK_SIZE) {
2510 lockChunk(object, i + 1);
2511 if(object->chunks[i + 1].data == NULL)
2512 object->chunks[i + 1].data = get_chunk();
2513 /* Unless we're grabbing all len of data, we do not
2514 want to do an indirect read immediately afterwards. */
2515 if(more && len + j <= 2 * CHUNK_SIZE) {
2516 if(!connection->buf)
2517 connection->buf = get_chunk(); /* checked below */
2519 if(object->chunks[i + 1].data) {
2520 do_stream_3(IO_READ | IO_NOTNOW, connection->fd, j,
2521 object->chunks[i].data, CHUNK_SIZE,
2522 object->chunks[i + 1].data,
2523 MIN(CHUNK_SIZE,
2524 end - (i + 1) * CHUNK_SIZE),
2525 connection->buf, connection->buf ? more : 0,
2526 httpServerDirectHandler2, connection);
2527 return 1;
2529 unlockChunk(object, i + 1);
2531 if(more && len + j <= CHUNK_SIZE) {
2532 if(!connection->buf)
2533 connection->buf = get_chunk();
2535 do_stream_2(IO_READ | IO_NOTNOW, connection->fd, j,
2536 object->chunks[i].data,
2537 MIN(CHUNK_SIZE, end - i * CHUNK_SIZE),
2538 connection->buf, connection->buf ? more : 0,
2539 httpServerDirectHandler, connection);
2540 return 1;
2541 } else {
2542 unlockChunk(object, i);
2546 if(connection->len == 0)
2547 httpConnectionDestroyBuf(connection);
2549 httpSetTimeout(connection, serverTimeout);
2550 do_stream_buf(IO_READ | IO_NOTNOW |
2551 ((immediate && connection->len) ? IO_IMMEDIATE : 0),
2552 connection->fd, connection->len,
2553 &connection->buf,
2554 (connection->te == TE_CHUNKED ?
2555 MIN(2048, CHUNK_SIZE) : CHUNK_SIZE),
2556 httpServerIndirectHandler, connection);
2557 return 1;
2561 httpServerDirectHandlerCommon(int kind, int status,
2562 FdEventHandlerPtr event,
2563 StreamRequestPtr srequest)
2565 HTTPConnectionPtr connection = srequest->data;
2566 HTTPRequestPtr request = connection->request;
2567 ObjectPtr object = request->object;
2568 int i = connection->offset / CHUNK_SIZE;
2569 int to, end, end1;
2571 assert(request->object->flags & OBJECT_INPROGRESS);
2573 httpSetTimeout(connection, -1);
2575 if(status < 0) {
2576 unlockChunk(object, i);
2577 if(kind == 2) unlockChunk(object, i + 1);
2578 if(status != -ECLIENTRESET)
2579 do_log_error(L_ERROR, -status, "Read from server failed");
2580 httpServerAbort(connection, status != -ECLIENTRESET, 502,
2581 internAtomError(-status, "Read from server failed"));
2582 return 1;
2585 /* We have incestuous knowledge of the decisions made in
2586 httpServerReadData */
2587 if(request->to >= 0)
2588 to = request->to;
2589 else
2590 to = object->length;
2591 if(connection->te == TE_CHUNKED)
2592 end = connection->offset + connection->chunk_remaining;
2593 else
2594 end = to;
2595 /* The amount of data actually read into the object */
2596 end1 = MIN(end, i * CHUNK_SIZE + MIN(kind * CHUNK_SIZE, srequest->offset));
2598 assert(end >= 0 && end1 >= i * CHUNK_SIZE && end1 <= (i + 2) * CHUNK_SIZE);
2600 object->chunks[i].size =
2601 MAX(object->chunks[i].size, MIN(end1 - i * CHUNK_SIZE, CHUNK_SIZE));
2602 if(kind == 2 && end1 > (i + 1) * CHUNK_SIZE) {
2603 object->chunks[i + 1].size =
2604 MAX(object->chunks[i + 1].size, end1 - (i + 1) * CHUNK_SIZE);
2606 if(connection->te == TE_CHUNKED) {
2607 connection->chunk_remaining -= (end1 - connection->offset);
2608 assert(connection->chunk_remaining >= 0);
2610 connection->offset = end1;
2611 object->size = MAX(object->size, end1);
2612 unlockChunk(object, i);
2613 if(kind == 2) unlockChunk(object, i + 1);
2615 if(i * CHUNK_SIZE + srequest->offset > end1) {
2616 connection->len = i * CHUNK_SIZE + srequest->offset - end1;
2617 return httpServerIndirectHandlerCommon(connection, status);
2618 } else {
2619 notifyObject(object);
2620 if(status) {
2621 httpServerFinish(connection, 1, 0);
2622 return 1;
2623 } else {
2624 return httpServerReadData(connection, 0);
2630 httpServerDirectHandler(int status,
2631 FdEventHandlerPtr event,
2632 StreamRequestPtr srequest)
2634 return httpServerDirectHandlerCommon(1, status, event, srequest);
2638 httpServerDirectHandler2(int status,
2639 FdEventHandlerPtr event,
2640 StreamRequestPtr srequest)
2642 return httpServerDirectHandlerCommon(2, status, event, srequest);
2645 /* Add the data accumulated in connection->buf into the object in
2646 connection->request. Returns 0 in the normal case, 1 if the TE is
2647 self-terminating and we're done, -1 if there was a problem with
2648 objectAddData, -2 if there was a problem with the data. */
2650 connectionAddData(HTTPConnectionPtr connection, int skip)
2652 HTTPRequestPtr request = connection->request;
2653 ObjectPtr object = request->object;
2654 int rc;
2656 if(connection->te == TE_IDENTITY) {
2657 int len;
2659 len = connection->len - skip;
2660 if(object->length >= 0) {
2661 len = MIN(object->length - connection->offset, len);
2663 if(request->to >= 0)
2664 len = MIN(request->to - connection->offset, len);
2665 if(len > 0) {
2666 rc = objectAddData(object, connection->buf + skip,
2667 connection->offset, len);
2668 if(rc < 0)
2669 return -1;
2670 connection->offset += len;
2671 connection->len -= (len + skip);
2672 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2673 (unsigned long)connection, (unsigned long)object,
2674 connection->offset);
2677 if(connection->len > 0 && skip + len > 0) {
2678 memmove(connection->buf,
2679 connection->buf + skip + len, connection->len);
2682 if((object->length >= 0 && object->length <= connection->offset) ||
2683 (request->to >= 0 && request->to <= connection->offset)) {
2684 notifyObject(object);
2685 return 1;
2686 } else {
2687 if(len > 0)
2688 notifyObject(object);
2689 return 0;
2691 } else if(connection->te == TE_CHUNKED) {
2692 int i = skip, j, size;
2693 /* connection->chunk_remaining is 0 at the end of a chunk, -1
2694 after the CR/LF pair ending a chunk, and -2 after we've
2695 seen a chunk of length 0. */
2696 if(connection->chunk_remaining > -2) {
2697 while(1) {
2698 if(connection->chunk_remaining <= 0) {
2699 if(connection->chunk_remaining == 0) {
2700 if(connection->len < i + 2)
2701 break;
2702 if(connection->buf[i] != '\r' ||
2703 connection->buf[i + 1] != '\n')
2704 return -1;
2705 i += 2;
2706 connection->chunk_remaining = -1;
2708 if(connection->len < i + 2)
2709 break;
2710 j = parseChunkSize(connection->buf, i,
2711 connection->len, &size);
2712 if(j < 0)
2713 return -2;
2714 if(j == 0)
2715 break;
2716 else
2717 i = j;
2718 if(size == 0) {
2719 connection->chunk_remaining = -2;
2720 break;
2721 } else {
2722 connection->chunk_remaining = size;
2724 } else {
2725 /* connection->chunk_remaining > 0 */
2726 size = MIN(connection->chunk_remaining,
2727 connection->len - i);
2728 if(size <= 0)
2729 break;
2730 rc = objectAddData(object, connection->buf + i,
2731 connection->offset, size);
2732 connection->offset += size;
2733 if(rc < 0)
2734 return -1;
2735 i += size;
2736 connection->chunk_remaining -= size;
2737 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2738 (unsigned long)connection,
2739 (unsigned long)object,
2740 connection->offset);
2744 connection->len -= i;
2745 if(connection->len > 0)
2746 memmove(connection->buf, connection->buf + i, connection->len);
2747 if(i > 0 || connection->chunk_remaining == -2)
2748 notifyObject(object);
2749 if(connection->chunk_remaining == -2)
2750 return 1;
2751 else
2752 return 0;
2753 } else {
2754 abort();
2758 void
2759 listServers(FILE *out)
2761 HTTPServerPtr server;
2762 int i, n, m, entry;
2764 fprintf(out, "<!DOCTYPE HTML PUBLIC "
2765 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2766 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2767 "<html><head>\n"
2768 "\r\n<title>Known servers</title>\n"
2769 "</head><body>\n"
2770 "<h1>Known servers</h1>\n");
2772 alternatingHttpStyle(out, "servers");
2773 fprintf(out, "<table id=servers>\n");
2774 fprintf(out, "<thead><tr><th>Server</th>"
2775 "<th>Version</th>"
2776 "<th>Persistent</th>"
2777 "<th>Pipeline</th>"
2778 "<th>Connections</th>"
2779 "<th></th>"
2780 "<th>rtt</th>"
2781 "<th>rate</th>"
2782 "</tr></thead>\n");
2783 fprintf(out, "<tbody>\n");
2784 server = servers;
2785 entry = 0;
2786 while(server) {
2787 fprintf(out, "<tr class=\"%s\">", entry % 2 == 0 ? "even" : "odd");
2788 if(server->port == 80)
2789 fprintf(out, "<td>%s</td>", server->name);
2790 else
2791 fprintf(out, "<td>%s:%d</td>", server->name, server->port);
2793 if(server->version == HTTP_11)
2794 fprintf(out, "<td>1.1</td>");
2795 else if(server->version == HTTP_10)
2796 fprintf(out, "<td>1.0</td>");
2797 else
2798 fprintf(out, "<td>unknown</td>");
2800 if(server->persistent < 0)
2801 fprintf(out, "<td>no</td>");
2802 else if(server->persistent > 0)
2803 fprintf(out, "<td>yes</td>");
2804 else
2805 fprintf(out, "<td>unknown</td>");
2807 if(server->version != HTTP_11 || server->persistent <= 0)
2808 fprintf(out, "<td></td>");
2809 else if(server->pipeline < 0)
2810 fprintf(out, "<td>no</td>");
2811 else if(server->pipeline >= 0 && server->pipeline <= 1)
2812 fprintf(out, "<td>unknown</td>");
2813 else if(server->pipeline == 2 || server->pipeline == 3)
2814 fprintf(out, "<td>probing</td>");
2815 else
2816 fprintf(out, "<td>yes</td>");
2818 n = 0; m = 0;
2819 for(i = 0; i < server->maxslots; i++)
2820 if(server->connection[i] && !server->connection[i]->connecting) {
2821 if(i < server->numslots)
2822 n++;
2823 else
2824 m++;
2827 fprintf(out, "<td>%d/%d", n, server->numslots);
2828 if(m)
2829 fprintf(out, " + %d</td>", m);
2830 else
2831 fprintf(out, "</td>");
2833 if(server->lies > 0)
2834 fprintf(out, "<td>(%d lies)</td>", (server->lies + 9) / 10);
2835 else
2836 fprintf(out, "<td></td>");
2838 if(server->rtt > 0)
2839 fprintf(out, "<td>%.3f</td>", (double)server->rtt / 1000000.0);
2840 else
2841 fprintf(out, "<td></td>");
2842 if(server->rate > 0)
2843 fprintf(out, "<td>%d</td>", server->rate);
2844 else
2845 fprintf(out, "<td></td>");
2847 fprintf(out, "</tr>\n");
2848 server = server->next;
2849 entry++;
2851 fprintf(out, "</tbody>\n");
2852 fprintf(out, "</table>\n");
2853 fprintf(out, "<p><a href=\"/polipo/\">back</a></p>");
2854 fprintf(out, "</body></html>\n");