Update CHANGES for 0.9.99.1.
[polipo.git] / server.c
blob8c93ce78ddf52e80eb63348f92b7a491fb599981
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;
39 static HTTPServerPtr servers = 0;
41 static int httpServerContinueConditionHandler(int, ConditionHandlerPtr);
42 static int initParentProxy(void);
43 static int parentProxySetter(ConfigVariablePtr var, void *value);
44 static void httpServerDelayedFinish(HTTPConnectionPtr);
46 void
47 preinitServer(void)
49 CONFIG_VARIABLE_SETTABLE(parentProxy, CONFIG_ATOM_LOWER, parentProxySetter,
50 "Parent proxy (host:port).");
51 CONFIG_VARIABLE(serverExpireTime, CONFIG_TIME,
52 "Time during which server data is valid.");
53 CONFIG_VARIABLE_SETTABLE(smallRequestTime, CONFIG_TIME, configIntSetter,
54 "Estimated time for a small request.");
55 CONFIG_VARIABLE_SETTABLE(replyUnpipelineTime, CONFIG_TIME, configIntSetter,
56 "Estimated time for a pipeline break.");
57 CONFIG_VARIABLE_SETTABLE(replyUnpipelineSize, CONFIG_INT, configIntSetter,
58 "Size for a pipeline break.");
59 CONFIG_VARIABLE_SETTABLE(pipelineAdditionalRequests, CONFIG_TRISTATE,
60 configIntSetter,
61 "Pipeline requests on an active connection.");
62 CONFIG_VARIABLE_SETTABLE(maxPipelineTrain, CONFIG_INT,
63 configIntSetter,
64 "Maximum number of requests "
65 "pipelined at a time.");
66 CONFIG_VARIABLE(pmmFirstSize, CONFIG_INT,
67 "The size of the first PMM chunk.");
68 CONFIG_VARIABLE(pmmSize, CONFIG_INT,
69 "The size of a PMM chunk.");
70 CONFIG_VARIABLE(serverSlots, CONFIG_INT,
71 "Maximum number of connections per server.");
72 CONFIG_VARIABLE(serverMaxSlots, CONFIG_INT,
73 "Maximum number of connections per broken server.");
74 CONFIG_VARIABLE(dontCacheRedirects, CONFIG_BOOLEAN,
75 "If true, don't cache redirects.");
78 static int
79 parentProxySetter(ConfigVariablePtr var, void *value)
81 configAtomSetter(var, value);
82 initParentProxy();
83 return 1;
86 static void
87 discardServer(HTTPServerPtr server)
89 HTTPServerPtr previous;
90 assert(!server->request);
92 if(server == servers)
93 servers = server->next;
94 else {
95 previous = servers;
96 while(previous->next != server)
97 previous = previous->next;
98 previous->next = server->next;
101 free(server);
104 static int
105 httpServerIdle(HTTPServerPtr server)
107 int i;
108 if(server->request)
109 return 0;
110 for(i = 0; i < server->maxslots; i++)
111 if(server->connection[i])
112 return 0;
113 return 1;
116 static int
117 expireServersHandler(TimeEventHandlerPtr event)
119 HTTPServerPtr server, next;
120 TimeEventHandlerPtr e;
121 server = servers;
122 while(server) {
123 next = server->next;
124 if(httpServerIdle(server) &&
125 server->time + serverExpireTime < current_time.tv_sec)
126 discardServer(server);
127 server = next;
129 e = scheduleTimeEvent(serverExpireTime / 60 + 60,
130 expireServersHandler, 0, NULL);
131 if(!e) {
132 do_log(L_ERROR, "Couldn't schedule server expiry.\n");
133 polipoExit();
135 return 1;
138 static int
139 roundSize(int size)
141 if(size < CHUNK_SIZE)
142 return 1 << log2_ceil(pmmSize);
143 else if(size > CHUNK_SIZE)
144 return (size + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE;
145 else
146 return size;
149 static int
150 initParentProxy()
152 AtomPtr host, port_atom;
153 int rc, port;
155 if(parentHost) {
156 releaseAtom(parentHost);
157 parentHost = NULL;
159 if(parentPort >= 0)
160 parentPort = -1;
162 if(parentProxy != NULL && parentProxy->length == 0) {
163 releaseAtom(parentProxy);
164 parentProxy = NULL;
167 if(parentProxy == NULL)
168 return 1;
170 rc = atomSplit(parentProxy, ':', &host, &port_atom);
171 if(rc <= 0) {
172 do_log(L_ERROR, "Couldn't parse parentProxy.");
173 releaseAtom(parentProxy);
174 parentProxy = NULL;
175 return -1;
178 port = atoi(port_atom->string);
179 if(port <= 0 || port >= 0x10000) {
180 releaseAtom(host);
181 releaseAtom(port_atom);
182 do_log(L_ERROR, "Couldn't parse parentProxy.");
183 releaseAtom(parentProxy);
184 parentProxy = NULL;
185 return -1;
188 parentHost = host;
189 parentPort = port;
190 return 1;
193 void
194 initServer(void)
196 TimeEventHandlerPtr event;
197 servers = NULL;
199 if(pmmFirstSize || pmmSize) {
200 if(pmmSize == 0) pmmSize = pmmFirstSize;
201 if(pmmFirstSize == 0) pmmFirstSize = pmmSize;
202 pmmSize = roundSize(pmmSize);
203 pmmFirstSize = roundSize(pmmFirstSize);
206 if(serverMaxSlots < 1)
207 serverMaxSlots = 1;
208 if(serverSlots < 1)
209 serverSlots = 1;
210 if(serverSlots > serverMaxSlots)
211 serverSlots = serverMaxSlots;
213 initParentProxy();
215 event = scheduleTimeEvent(serverExpireTime / 60 + 60, expireServersHandler,
216 0, NULL);
217 if(event == NULL) {
218 do_log(L_ERROR, "Couldn't schedule server expiry.\n");
219 exit(1);
223 static HTTPServerPtr
224 getServer(char *name, int port, int proxy)
226 HTTPServerPtr server;
227 int i;
229 server = servers;
230 while(server) {
231 if(strcmp(server->name, name) == 0 && server->port == port &&
232 server->isProxy == proxy) {
233 if(httpServerIdle(server) &&
234 server->time + serverExpireTime < current_time.tv_sec) {
235 discardServer(server);
236 server = NULL;
237 break;
238 } else {
239 server->time = current_time.tv_sec;
240 return server;
243 server = server->next;
246 server = malloc(sizeof(HTTPServerRec));
247 if(server == NULL) {
248 do_log(L_ERROR, "Couldn't allocate server.\n");
249 return NULL;
252 server->connection = malloc(serverMaxSlots * sizeof(HTTPConnectionPtr));
253 if(server->connection == NULL) {
254 do_log(L_ERROR, "Couldn't allocate server.\n");
255 free(server);
256 return NULL;
259 server->idleHandler = malloc(serverMaxSlots * sizeof(FdEventHandlerPtr));
260 if(server->connection == NULL) {
261 do_log(L_ERROR, "Couldn't allocate server.\n");
262 free(server->connection);
263 free(server);
264 return NULL;
267 server->maxslots = serverMaxSlots;
269 server->name = strdup(name);
270 if(server->name == NULL) {
271 do_log(L_ERROR, "Couldn't allocate server name.\n");
272 free(server);
273 return NULL;
276 server->port = port;
277 server->addrindex = 0;
278 server->isProxy = proxy;
279 server->version = HTTP_UNKNOWN;
280 server->persistent = 0;
281 server->pipeline = 0;
282 server->time = current_time.tv_sec;
283 server->rtt = -1;
284 server->rate = -1;
285 server->numslots = MIN(serverSlots, server->maxslots);
286 for(i = 0; i < server->maxslots; i++) {
287 server->connection[i] = NULL;
288 server->idleHandler[i] = NULL;
290 server->request = NULL;
291 server->request_last = NULL;
292 server->lies = 0;
294 server->next = servers;
295 servers = server;
296 return server;
300 httpServerQueueRequest(HTTPServerPtr server, HTTPRequestPtr request)
302 assert(request->request && request->request->request == request);
303 assert(request->connection == NULL);
304 if(server->request) {
305 server->request_last->next = request;
306 server->request_last = request;
307 } else {
308 server->request_last = request;
309 server->request = request;
311 return 1;
314 void
315 httpServerAbort(HTTPConnectionPtr connection, int fail,
316 int code, AtomPtr message)
318 HTTPRequestPtr request = connection->request;
319 if(request) {
320 if(request->request) {
321 httpClientError(request->request, code, retainAtom(message));
323 if(fail) {
324 request->object->flags |= OBJECT_FAILED;
325 if(request->object->flags & OBJECT_INITIAL)
326 abortObject(request->object, code, retainAtom(message));
327 notifyObject(request->object);
330 releaseAtom(message);
331 if(!connection->connecting)
332 httpServerFinish(connection, 1, 0);
335 void
336 httpServerAbortRequest(HTTPRequestPtr request, int fail,
337 int code, AtomPtr message)
339 if(request->connection && request == request->connection->request) {
340 httpServerAbort(request->connection, fail, code, message);
341 } else {
342 HTTPRequestPtr requestor = request->request;
343 if(requestor) {
344 requestor->request = NULL;
345 request->request = NULL;
346 httpClientError(requestor, code, retainAtom(message));
348 if(fail) {
349 request->object->flags |= OBJECT_FAILED;
350 if(request->object->flags & OBJECT_INITIAL)
351 abortObject(request->object, code, retainAtom(message));
352 notifyObject(request->object);
354 releaseAtom(message);
358 void
359 httpServerClientReset(HTTPRequestPtr request)
361 if(request->connection &&
362 request->connection->fd >= 0 &&
363 !request->connection->connecting &&
364 request->connection->request == request)
365 pokeFdEvent(request->connection->fd, -ECLIENTRESET, POLLIN | POLLOUT);
370 httpMakeServerRequest(char *name, int port, ObjectPtr object,
371 int method, int from, int to, HTTPRequestPtr requestor)
373 HTTPServerPtr server;
374 HTTPRequestPtr request;
375 int rc;
377 assert(!(object->flags & OBJECT_INPROGRESS));
379 if(parentHost) {
380 server = getServer(parentHost->string, parentPort, 1);
381 } else {
382 server = getServer(name, port, 0);
384 if(server == NULL) return -1;
386 object->flags |= OBJECT_INPROGRESS;
387 object->requestor = requestor;
389 request = httpMakeRequest();
390 if(!request) {
391 do_log(L_ERROR, "Couldn't allocate request.\n");
392 return -1;
395 /* Because we allocate objects in chunks, we cannot have data that
396 doesn't start at a chunk boundary. */
397 if(from % CHUNK_SIZE != 0) {
398 objectFillFromDisk(object, from / CHUNK_SIZE * CHUNK_SIZE, 1);
399 if(objectHoleSize(object, from - 1) != 0)
400 from = from / CHUNK_SIZE * CHUNK_SIZE;
403 request->object = retainObject(object);
404 request->method = method;
405 if(method == METHOD_CONDITIONAL_GET) {
406 if(server->lies > 0)
407 request->method = METHOD_HEAD;
409 request->flags =
410 REQUEST_PERSISTENT |
411 (expectContinue ? (requestor->flags & REQUEST_WAIT_CONTINUE) : 0);
412 request->from = from;
413 request->to = to;
414 request->request = requestor;
415 requestor->request = request;
416 request->cache_control = requestor->cache_control;
417 request->time0 = null_time;
418 request->time1 = null_time;
420 rc = httpServerQueueRequest(server, request);
421 if(rc < 0) {
422 do_log(L_ERROR, "Couldn't queue request.\n");
423 request->request = NULL;
424 requestor->request = NULL;
425 object->flags &= ~(OBJECT_INPROGRESS | OBJECT_VALIDATING);
426 releaseNotifyObject(object);
427 httpDestroyRequest(request);
428 return 1;
431 if(request->flags & REQUEST_WAIT_CONTINUE) {
432 if(server->version == HTTP_10) {
433 httpServerAbortRequest(request, 1,
434 417, internAtom("Expectation failed"));
435 return 1;
437 } else if(expectContinue >= 2 && server->version == HTTP_11) {
438 if(request->method == METHOD_POST || request->method == METHOD_PUT)
439 request->flags |= REQUEST_WAIT_CONTINUE;
442 again:
443 rc = httpServerTrigger(server);
444 if(rc < 0) {
445 /* We must be very short on memory. If there are any requests
446 queued, we abort one and try again. If there aren't, we
447 give up. */
448 do_log(L_ERROR, "Couldn't trigger server -- out of memory?\n");
449 if(server->request) {
450 httpServerAbortRequest(server->request, 1, 503,
451 internAtom("Couldn't trigger server"));
452 goto again;
455 return 1;
459 httpServerConnection(HTTPServerPtr server)
461 HTTPConnectionPtr connection;
462 int i;
464 connection = httpMakeConnection();
465 if(connection == NULL) {
466 do_log(L_ERROR, "Couldn't allocate server connection.\n");
467 return -1;
469 connection->server = server;
471 for(i = 0; i < server->numslots; i++) {
472 if(!server->connection[i]) {
473 server->connection[i] = connection;
474 break;
477 assert(i < server->numslots);
479 connection->request = NULL;
480 connection->request_last = NULL;
482 do_log(D_SERVER_CONN, "C... %s:%d.\n",
483 connection->server->name, connection->server->port);
484 httpSetTimeout(connection, serverTimeout);
485 if(socksParentProxy) {
486 connection->connecting = CONNECTING_SOCKS;
487 do_socks_connect(server->name, connection->server->port,
488 httpServerSocksHandler, connection);
489 } else {
490 connection->connecting = CONNECTING_DNS;
491 do_gethostbyname(server->name, 0,
492 httpServerConnectionDnsHandler,
493 connection);
495 return 1;
499 httpServerConnectionDnsHandler(int status, GethostbynameRequestPtr request)
501 HTTPConnectionPtr connection = request->data;
503 httpSetTimeout(connection, -1);
505 if(status <= 0) {
506 AtomPtr message;
507 message = internAtomF("Host %s lookup failed: %s",
508 request->name ?
509 request->name->string : "(unknown)",
510 request->error_message ?
511 request->error_message->string :
512 pstrerror(-status));
513 do_log(L_ERROR, "Host %s lookup failed: %s (%d).\n",
514 request->name ?
515 request->name->string : "(unknown)",
516 request->error_message ?
517 request->error_message->string :
518 pstrerror(-status), -status);
519 connection->connecting = 0;
520 if(connection->server->request)
521 httpServerAbortRequest(connection->server->request, 1, 504,
522 retainAtom(message));
523 httpServerAbort(connection, 1, 502, message);
524 return 1;
527 if(request->addr->string[0] == DNS_CNAME) {
528 if(request->count > 10) {
529 AtomPtr message = internAtom("DNS CNAME loop");
530 do_log(L_ERROR, "DNS CNAME loop.\n");
531 connection->connecting = 0;
532 if(connection->server->request)
533 httpServerAbortRequest(connection->server->request, 1, 504,
534 retainAtom(message));
535 httpServerAbort(connection, 1, 504, message);
536 return 1;
539 httpSetTimeout(connection, serverTimeout);
540 do_gethostbyname(request->addr->string + 1, request->count + 1,
541 httpServerConnectionDnsHandler,
542 connection);
543 return 1;
546 connection->connecting = CONNECTING_CONNECT;
547 httpSetTimeout(connection, serverTimeout);
548 do_connect(retainAtom(request->addr), connection->server->addrindex,
549 connection->server->port,
550 httpServerConnectionHandler, connection);
551 return 1;
555 httpServerConnectionHandler(int status,
556 FdEventHandlerPtr event,
557 ConnectRequestPtr request)
559 HTTPConnectionPtr connection = request->data;
561 assert(connection->fd < 0);
562 if(request->fd >= 0) {
563 int rc;
564 connection->fd = request->fd;
565 connection->server->addrindex = request->index;
566 rc = setNodelay(connection->fd, 1);
567 if(rc < 0)
568 do_log_error(L_WARN, errno, "Couldn't disable Nagle's algorithm");
571 return httpServerConnectionHandlerCommon(status, connection);
575 httpServerSocksHandler(int status, SocksRequestPtr request)
577 HTTPConnectionPtr connection = request->data;
579 assert(connection->fd < 0);
580 if(request->fd >= 0) {
581 connection->fd = request->fd;
582 connection->server->addrindex = 0;
584 return httpServerConnectionHandlerCommon(status, connection);
588 httpServerConnectionHandlerCommon(int status, HTTPConnectionPtr connection)
590 httpSetTimeout(connection, -1);
592 if(status < 0) {
593 AtomPtr message =
594 internAtomError(-status, "Connect to %s:%d failed",
595 connection->server->name,
596 connection->server->port);
597 if(status != -ECLIENTRESET)
598 do_log_error(L_ERROR, -status, "Connect to %s:%d failed",
599 connection->server->name, connection->server->port);
600 connection->connecting = 0;
601 if(connection->server->request)
602 httpServerAbortRequest(connection->server->request,
603 status != -ECLIENTRESET, 504,
604 retainAtom(message));
605 httpServerAbort(connection, status != -ECLIENTRESET, 504, message);
606 return 1;
609 do_log(D_SERVER_CONN, "C %s:%d.\n",
610 connection->server->name, connection->server->port);
612 connection->connecting = 0;
613 httpServerTrigger(connection->server);
614 return 1;
618 httpServerIdleHandler(int a, FdEventHandlerPtr event)
620 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
621 HTTPServerPtr server = connection->server;
622 int i;
624 assert(!connection->request);
626 do_log(D_SERVER_CONN, "Idle connection to %s:%d died.\n",
627 connection->server->name, connection->server->port);
629 for(i = 0; i < server->maxslots; i++) {
630 if(connection == server->connection[i]) {
631 server->idleHandler[i] = NULL;
632 break;
635 assert(i < server->maxslots);
637 httpServerAbort(connection, 1, 504, internAtom("Timeout"));
638 return 1;
641 /* Discard aborted requests at the head of the queue. */
642 static void
643 httpServerDiscardRequests(HTTPServerPtr server)
645 HTTPRequestPtr request;
646 while(server->request && !server->request->request) {
647 request = server->request;
648 server->request = request->next;
649 request->next = NULL;
650 if(server->request == NULL)
651 server->request_last = NULL;
652 request->object->flags &= ~(OBJECT_INPROGRESS | OBJECT_VALIDATING);
653 releaseNotifyObject(request->object);
654 request->object = NULL;
655 httpDestroyRequest(request);
659 static int
660 pipelineIsSmall(HTTPConnectionPtr connection)
662 HTTPRequestPtr request = connection->request;
664 if(pipelineAdditionalRequests <= 0)
665 return 0;
666 else if(pipelineAdditionalRequests >= 2)
667 return 1;
669 if(!request)
670 return 1;
671 if(request->next || !(request->flags & REQUEST_PERSISTENT))
672 return 0;
673 if(request->method == METHOD_HEAD ||
674 request->method == METHOD_CONDITIONAL_GET)
675 return 1;
676 if(request->to >= 0 && connection->server->rate > 0 &&
677 request->to - request->from < connection->server->rate *
678 smallRequestTime)
679 return 1;
680 return 0;
683 static int
684 numRequests(HTTPServerPtr server)
686 int n = 0;
687 HTTPRequestPtr request = server->request;
688 while(request) {
689 n++;
690 request = request->next;
692 return n;
695 HTTPConnectionPtr
696 httpServerGetConnection(HTTPServerPtr server, int *idle_return)
698 int i, j;
699 int connecting = 0, empty = 0, idle = 0;
701 j = -1;
702 /* Try to find an idle connection */
703 for(i = 0; i < server->numslots; i++) {
704 if(server->connection[i]) {
705 if(!server->connection[i]->connecting) {
706 if(!server->connection[i]->request) {
707 if(server->idleHandler[i])
708 unregisterFdEvent(server->idleHandler[i]);
709 server->idleHandler[i] = NULL;
710 if(j < 0) j = i;
711 idle++;
713 } else
714 connecting++;
715 } else
716 empty++;
719 if(j >= 0) {
720 *idle_return = idle;
721 return server->connection[j];
724 /* If there's an empty slot, schedule connection creation */
725 if(empty) {
726 /* Don't open a connection if there are already enough in
727 progress, except if the server doesn't do persistent
728 connections and there's only one in progress. */
729 if((connecting == 0 || (server->persistent <= 0 && connecting <= 1)) ||
730 connecting < numRequests(server)) {
731 httpServerConnection(server);
735 /* Find a connection that can accept additional requests */
736 if(server->version == HTTP_11 && server->pipeline >= 4) {
737 for(i = 0; i < serverSlots; i++) {
738 if(server->connection[i] && !server->connection[i]->connecting &&
739 pipelineIsSmall(server->connection[i])) {
740 if(server->idleHandler[i])
741 unregisterFdEvent(server->idleHandler[i]);
742 server->idleHandler[i] = NULL;
743 *idle_return = 0;
744 return server->connection[i];
748 *idle_return = idle;
749 return NULL;
753 httpServerTrigger(HTTPServerPtr server)
755 HTTPConnectionPtr connection;
756 HTTPRequestPtr request;
757 int idle, n, i, rc, numidle;
759 while(server->request) {
760 httpServerDiscardRequests(server);
762 if(!server->request)
763 break;
765 if(REQUEST_SIDE(server->request)) {
766 rc = httpServerSideRequest(server);
767 /* If rc is 0, httpServerSideRequest didn't dequeue this
768 request. Go through the scheduling loop again, come
769 back later. */
770 if(rc <= 0) break;
771 continue;
773 connection = httpServerGetConnection(server, &numidle);
774 if(!connection) break;
776 /* If server->pipeline <= 0, we don't do pipelining. If
777 server->pipeline is 1, then we are ready to start probing
778 for pipelining on the server; we then send exactly two
779 requests in what is hopefully a single packet to check
780 whether the server has the nasty habit of discarding its
781 input buffers after each request.
782 If server->pipeline is 2 or 3, the pipelining probe is in
783 progress on this server, and we don't pipeline anything
784 until it succeeds. When server->pipeline >= 4, pipelining
785 is believed to work on this server. */
786 if(server->version != HTTP_11 || server->pipeline <= 0 ||
787 server->pipeline == 2 || server->pipeline == 3) {
788 if(connection->pipelined == 0)
789 n = 1;
790 else
791 n = 0;
792 } else if(server->pipeline == 1) {
793 if(connection->pipelined == 0)
794 n = MIN(2, maxPipelineTrain);
795 else
796 n = 0;
797 } else {
798 n = maxPipelineTrain;
801 /* Don't pipeline if there are more idle connections */
802 if(numidle >= 2)
803 n = MIN(n, 1);
805 idle = !connection->pipelined;
806 i = 0;
807 while(server->request && connection->pipelined < n) {
808 httpServerDiscardRequests(server);
809 if(!server->request) break;
810 request = server->request;
811 assert(request->request->request == request);
812 rc = httpWriteRequest(connection, request, -1);
813 if(rc < 0) {
814 if(i == 0)
815 httpServerAbortRequest(request, rc != -ECLIENTRESET, 503,
816 internAtom("Couldn't "
817 "write request"));
818 break;
820 do_log(D_SERVER_CONN, "W: ");
821 do_log_n(D_SERVER_CONN,
822 request->object->key, request->object->key_size);
823 do_log(D_SERVER_CONN, " (%d)\n", request->method);
824 if(connection->pipelined > 0)
825 request->flags |= REQUEST_PIPELINED;
826 request->time0 = current_time;
827 i++;
828 server->request = request->next;
829 request->next = NULL;
830 if(server->request == NULL)
831 server->request_last = NULL;
832 httpQueueRequest(connection, request);
833 connection->pipelined++;
835 if(server->persistent > 0 && server->pipeline == 1 && i >= 2)
836 server->pipeline = 2;
838 if(i > 0) httpServerSendRequest(connection);
840 if(idle && connection->pipelined > 0)
841 httpServerReply(connection, 0);
843 if(i == 0) break;
846 for(i = 0; i < server->maxslots; i++) {
847 if(server->connection[i] &&
848 !server->connection[i]->connecting &&
849 !server->connection[i]->request) {
850 /* Artificially age any fresh connections that aren't used
851 straight away; this is necessary for the logic for POST and
852 the logic that determines whether a given request should be
853 restarted. */
854 if(server->connection[i]->serviced == 0)
855 server->connection[i]->serviced = 1;
856 if(!server->idleHandler[i])
857 server->idleHandler[i] =
858 registerFdEvent(server->connection[i]->fd, POLLIN,
859 httpServerIdleHandler,
860 sizeof(HTTPConnectionPtr),
861 &server->connection[i]);
862 if(!server->idleHandler[i]) {
863 do_log(L_ERROR, "Couldn't register idle handler.\n");
864 httpServerFinish(server->connection[i], 1, 0);
869 return 1;
873 httpServerSideRequest(HTTPServerPtr server)
875 HTTPRequestPtr request = server->request;
876 HTTPConnectionPtr connection;
877 HTTPRequestPtr requestor = request->request;
878 HTTPConnectionPtr client = requestor->connection;
879 int rc, i, freeslots, idle, connecting;
881 assert(REQUEST_SIDE(request));
883 connection = NULL;
884 freeslots = 0;
885 idle = -1;
886 connecting = 0;
888 /* Find a fresh connection */
889 for(i = 0; i < server->numslots; i++) {
890 if(!server->connection[i])
891 freeslots++;
892 else if(!server->connection[i]->connecting) {
893 if(!server->connection[i]->request) {
894 if(server->connection[i]->serviced == 0) {
895 if(server->idleHandler[i])
896 unregisterFdEvent(server->idleHandler[i]);
897 server->idleHandler[i] = NULL;
898 connection = server->connection[i];
899 break;
900 } else {
901 idle = i;
904 } else {
905 connecting++;
909 if(!connection) {
910 /* Make sure that a fresh connection will be established at some
911 point, then wait until httpServerTrigger calls us again. */
912 if(freeslots) {
913 httpServerConnection(server);
914 } else {
915 if(idle >= 0) {
916 /* Shutdown a random idle connection */
917 pokeFdEvent(server->connection[idle]->fd,
918 -EDOSHUTDOWN, POLLIN | POLLOUT);
921 return 0;
924 rc = httpWriteRequest(connection, request, client->bodylen);
925 if(rc < 0) {
926 do_log(L_ERROR, "Couldn't write POST or PUT request.\n");
927 httpServerAbortRequest(request, rc != -ECLIENTRESET, 503,
928 internAtom("Couldn't write request"));
929 return 0;
931 server->request = request->next;
932 request->next = NULL;
933 if(server->request == NULL)
934 server->request_last = NULL;
935 httpQueueRequest(connection, request);
936 connection->pipelined = 1;
937 request->time0 = current_time;
938 connection->reqoffset = 0;
939 connection->bodylen = client->bodylen;
940 httpServerDoSide(connection);
941 return 1;
944 int
945 httpServerDoSide(HTTPConnectionPtr connection)
947 HTTPRequestPtr request = connection->request;
948 HTTPRequestPtr requestor = request->request;
949 HTTPConnectionPtr client = requestor->connection;
950 int len = MIN(client->reqlen - client->reqbegin,
951 connection->bodylen - connection->reqoffset);
952 int doflush =
953 len > 0 &&
954 (len >= 1500 ||
955 client->reqbegin > 0 ||
956 (connection->reqoffset + client->reqlen - client->reqbegin) >=
957 connection->bodylen);
958 int done = connection->reqoffset >= connection->bodylen;
960 assert(connection->bodylen >= 0);
962 httpSetTimeout(connection, 60);
964 if(connection->reqlen > 0) {
965 /* Send the headers, but don't send any part of the body if
966 we're in wait_continue. */
967 do_stream_2(IO_WRITE,
968 connection->fd, 0,
969 connection->reqbuf, connection->reqlen,
970 client->reqbuf + client->reqbegin,
971 (request->flags & REQUEST_WAIT_CONTINUE) ? 0 : len,
972 httpServerSideHandler2, connection);
973 httpServerReply(connection, 0);
974 } else if(request->object->flags & OBJECT_ABORTED) {
975 if(connection->reqbuf)
976 dispose_chunk(connection->reqbuf);
977 connection->reqbuf = NULL;
978 connection->reqlen = 0;
979 pokeFdEvent(connection->fd, -ESHUTDOWN, POLLIN);
980 client->flags |= CONN_SIDE_READER;
981 do_stream(IO_READ | IO_IMMEDIATE,
982 client->fd, 0, NULL, 0,
983 httpClientSideHandler, client);
984 } else if(!(request->flags & REQUEST_WAIT_CONTINUE) && doflush) {
985 /* Make sure there's a reqbuf, as httpServerFinish uses
986 it to determine if there's a writer. */
987 if(connection->reqbuf == NULL)
988 connection->reqbuf = get_chunk();
989 assert(connection->reqbuf != NULL);
990 do_stream(IO_WRITE,
991 connection->fd, 0,
992 client->reqbuf + client->reqbegin, len,
993 httpServerSideHandler, connection);
994 } else {
995 if(connection->reqbuf) {
996 httpConnectionDestroyReqbuf(connection);
997 connection->reqlen = 0;
999 if(request->flags & REQUEST_WAIT_CONTINUE) {
1000 ConditionHandlerPtr chandler;
1001 do_log(D_SERVER_CONN, "W... %s:%d.\n",
1002 connection->server->name, connection->server->port);
1003 chandler =
1004 conditionWait(&request->object->condition,
1005 httpServerContinueConditionHandler,
1006 sizeof(connection), &connection);
1007 if(chandler)
1008 return 1;
1009 else
1010 do_log(L_ERROR, "Couldn't register condition handler.\n");
1011 /* Fall through -- the client side will clean up. */
1013 client->flags |= CONN_SIDE_READER;
1014 do_stream(IO_READ | (done ? IO_IMMEDIATE : 0 ) | IO_NOTNOW,
1015 client->fd, client->reqlen,
1016 client->reqbuf, CHUNK_SIZE,
1017 httpClientSideHandler, client);
1019 return 1;
1022 static int
1023 httpClientDelayedDoSideHandler(TimeEventHandlerPtr event)
1025 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1026 httpServerDoSide(connection);
1027 return 1;
1030 static int
1031 httpServerDelayedDoSide(HTTPConnectionPtr connection)
1033 TimeEventHandlerPtr handler;
1034 handler = scheduleTimeEvent(1, httpClientDelayedDoSideHandler,
1035 sizeof(connection), &connection);
1036 if(!handler) {
1037 do_log(L_ERROR, "Couldn't schedule DoSide -- freeing memory.\n");
1038 free_chunk_arenas();
1039 handler = scheduleTimeEvent(1, httpClientDelayedDoSideHandler,
1040 sizeof(connection), &connection);
1041 do_log(L_ERROR, "Couldn't schedule DoSide.\n");
1042 /* Somebody will hopefully end up timing out. */
1043 return 1;
1045 return 1;
1048 static int
1049 httpServerSideHandlerCommon(int kind, int status,
1050 FdEventHandlerPtr event,
1051 StreamRequestPtr srequest)
1053 HTTPConnectionPtr connection = srequest->data;
1054 HTTPRequestPtr request = connection->request;
1055 HTTPRequestPtr requestor = request->request;
1056 HTTPConnectionPtr client = requestor->connection;
1057 int bodylen;
1059 assert(request->object->flags & OBJECT_INPROGRESS);
1061 if(status) {
1062 do_log_error(L_ERROR, -status, "Couldn't write to server");
1063 httpConnectionDestroyReqbuf(connection);
1064 if(status != -ECLIENTRESET)
1065 shutdown(connection->fd, 2);
1066 abortObject(request->object, 503,
1067 internAtom("Couldn't write to server"));
1068 /* Let the read side handle the error */
1069 httpServerDoSide(connection);
1070 return 1;
1073 assert(srequest->offset > 0);
1075 if(kind == 2) {
1076 if(srequest->offset < connection->reqlen)
1077 return 0;
1078 bodylen = srequest->offset - connection->reqlen;
1079 connection->reqlen = 0;
1080 httpConnectionDestroyReqbuf(connection);
1081 } else {
1082 bodylen = srequest->offset;
1086 assert(client->reqbegin + bodylen <= client->reqlen);
1088 if(client->reqlen > client->reqbegin + bodylen)
1089 memmove(client->reqbuf, client->reqbuf + client->reqbegin + bodylen,
1090 client->reqlen - client->reqbegin - bodylen);
1091 client->reqlen -= bodylen + client->reqbegin;
1092 client->reqbegin = 0;
1093 connection->reqoffset += bodylen;
1094 httpServerDoSide(connection);
1095 return 1;
1099 httpServerSideHandler(int status,
1100 FdEventHandlerPtr event,
1101 StreamRequestPtr srequest)
1103 return httpServerSideHandlerCommon(1, status, event, srequest);
1107 httpServerSideHandler2(int status,
1108 FdEventHandlerPtr event,
1109 StreamRequestPtr srequest)
1111 return httpServerSideHandlerCommon(2, status, event, srequest);
1114 static int
1115 httpServerContinueConditionHandler(int status, ConditionHandlerPtr chandler)
1117 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)chandler->data;
1119 if(connection->request->flags & REQUEST_WAIT_CONTINUE)
1120 return 0;
1121 httpServerDelayedDoSide(connection);
1122 return 1;
1125 /* s is 0 to keep the connection alive, 1 to shutdown the connection,
1126 and -1 to keep the connection alive and keep the current request. */
1127 void
1128 httpServerFinish(HTTPConnectionPtr connection, int s, int offset)
1130 HTTPServerPtr server = connection->server;
1131 HTTPRequestPtr request = connection->request;
1132 int i;
1134 if(request) {
1135 assert(connection->pipelined >= 1);
1136 assert((connection->pipelined > 1) == (request->next != NULL));
1137 } else {
1138 assert(connection->pipelined == 0);
1141 if(s == 0 && (!connection->request ||
1142 !(connection->request->flags & REQUEST_PERSISTENT)))
1143 s = 1;
1145 if(connection->reqbuf) {
1146 /* As most normal requests go out in a single packet, this is
1147 extremely unlikely to happen. As for POST/PUT requests,
1148 they are not pipelined, so this can only happen if the
1149 server sent an error reply early. */
1150 assert(connection->fd >= 0);
1151 shutdown(connection->fd, 1);
1152 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLOUT);
1153 httpServerDelayedFinish(connection);
1154 goto done;
1157 if(s >= 0 && request) {
1158 /* Update statistics about the server */
1159 int size = -1, d = -1, rtt = -1, rate = -1;
1160 if(connection->offset > 0 && request->from >= 0)
1161 size = connection->offset - request->from;
1162 if(request->time1.tv_sec != null_time.tv_sec) {
1163 d = timeval_minus_usec(&current_time, &request->time1);
1164 if(!(request->flags & REQUEST_PIPELINED) &&
1165 request->time0.tv_sec != null_time.tv_sec)
1166 rtt = timeval_minus_usec(&request->time1, &request->time0);
1167 if(size >= 8192 && d > 50000)
1168 rate = ((double)size / (double)d) * 1000000.0 + 0.5;
1170 request->time0 = null_time;
1171 request->time1 = null_time;
1173 if(rtt >= 0) {
1174 if(server->rtt >= 0)
1175 server->rtt = (3 * server->rtt + rtt + 2) / 4;
1176 else
1177 server->rtt = rtt;
1179 if(rate >= 0) {
1180 if(server->rate >= 0)
1181 server->rate = (3 * server->rate + rate + 2) / 4;
1182 else
1183 server->rate = rate;
1186 httpDequeueRequest(connection);
1187 connection->pipelined--;
1188 request->object->flags &= ~(OBJECT_INPROGRESS | OBJECT_VALIDATING);
1189 if(request->request) {
1190 request->request->request = NULL;
1191 request->request = NULL;
1193 releaseNotifyObject(request->object);
1194 request->object = NULL;
1195 httpDestroyRequest(request);
1198 do_log(D_SERVER_CONN, "Done with server %s:%d connection (%d)\n",
1199 connection->server->name, connection->server->port, s);
1201 assert(offset <= connection->len);
1203 if(s <= 0) {
1204 if(offset < connection->len) {
1205 assert(connection->buf != NULL);
1206 if(!connection->pipelined) {
1207 do_log(L_WARN,
1208 "Closing connection to %s:%d: "
1209 "%d stray bytes of data.\n",
1210 server->name, server->port, connection->len - offset);
1211 s = 1;
1212 } else {
1213 memmove(connection->buf, connection->buf + offset,
1214 connection->len - offset);
1215 connection->len = connection->len - offset;
1216 if((connection->flags & CONN_BIGBUF) &&
1217 connection->len <= CHUNK_SIZE)
1218 httpConnectionUnbigify(connection);
1220 } else {
1221 connection->len = 0;
1225 connection->server->time = current_time.tv_sec;
1226 connection->serviced++;
1228 if(s > 0) {
1229 if(connection->timeout)
1230 cancelTimeEvent(connection->timeout);
1231 connection->timeout = NULL;
1232 httpConnectionDestroyBuf(connection);
1233 if(connection->fd >= 0)
1234 close(connection->fd);
1235 connection->fd = -1;
1236 server->persistent -= 1;
1237 if(server->persistent < -5)
1238 server->numslots = MIN(server->maxslots, serverMaxSlots);
1239 if(connection->request) {
1240 HTTPRequestPtr req;
1241 do_log(D_SERVER_CONN, "Restarting pipeline to %s:%d.\n",
1242 server->name, server->port);
1243 if(server->pipeline == 2)
1244 server->pipeline -= 20;
1245 else
1246 server->pipeline -= 5;
1247 req = connection->request;
1248 while(req) {
1249 req->connection = NULL;
1250 req = req->next;
1252 if(server->request)
1253 connection->request_last->next = server->request;
1254 else
1255 server->request_last = connection->request_last;
1256 server->request = connection->request;
1257 connection->request = NULL;
1258 connection->request_last = NULL;
1260 /* Make sure we don't get confused into thinking a probe
1261 is in progress. */
1262 if(server->pipeline == 2 || server->pipeline == 3)
1263 server->pipeline = 1;
1264 for(i = 0; i < server->maxslots; i++)
1265 if(connection == server->connection[i])
1266 break;
1267 assert(i < server->maxslots);
1268 if(server->idleHandler[i])
1269 unregisterFdEvent(server->idleHandler[i]);
1270 server->idleHandler[i] = NULL;
1271 server->connection[i] = NULL;
1272 free(connection);
1273 } else {
1274 server->persistent += 1;
1275 if(server->persistent > 0)
1276 server->numslots = MIN(server->maxslots, serverSlots);
1277 httpSetTimeout(connection, serverTimeout);
1278 /* See httpServerTrigger */
1279 if(connection->pipelined ||
1280 (server->version == HTTP_11 && server->pipeline <= 0) ||
1281 (server->pipeline == 3)) {
1282 server->pipeline++;
1284 if(s < 0 || connection->pipelined) {
1285 httpServerReply(connection, 1);
1286 } else {
1287 httpConnectionDestroyBuf(connection);
1291 done:
1292 httpServerTrigger(server);
1295 static int
1296 httpServerDelayedFinishHandler(TimeEventHandlerPtr event)
1298 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1299 httpServerFinish(connection, 1, 0);
1300 return 1;
1303 static void
1304 httpServerDelayedFinish(HTTPConnectionPtr connection)
1306 TimeEventHandlerPtr handler;
1308 handler = scheduleTimeEvent(1, httpServerDelayedFinishHandler,
1309 sizeof(connection), &connection);
1310 if(!handler) {
1311 do_log(L_ERROR,
1312 "Couldn't schedule delayed finish -- freeing memory.");
1313 free_chunk_arenas();
1314 handler = scheduleTimeEvent(1, httpServerDelayedFinishHandler,
1315 sizeof(connection), &connection);
1316 if(!handler) {
1317 do_log(L_ERROR,
1318 "Couldn't schedule delayed finish -- aborting.\n");
1319 polipoExit();
1324 void
1325 httpServerReply(HTTPConnectionPtr connection, int immediate)
1327 assert(connection->pipelined > 0);
1329 if(connection->request->request == NULL) {
1330 do_log(L_WARN, "Aborting pipeline on %s:%d.\n",
1331 connection->server->name, connection->server->port);
1332 httpServerFinish(connection, 1, 0);
1333 return;
1336 do_log(D_SERVER_CONN, "R: ");
1337 do_log_n(D_SERVER_CONN, connection->request->object->key,
1338 connection->request->object->key_size);
1339 do_log(D_SERVER_CONN, " (%d)\n", connection->request->method);
1341 if(connection->len == 0)
1342 httpConnectionDestroyBuf(connection);
1344 httpSetTimeout(connection, serverTimeout);
1345 do_stream_buf(IO_READ | (immediate ? IO_IMMEDIATE : 0) | IO_NOTNOW,
1346 connection->fd, connection->len,
1347 &connection->buf, CHUNK_SIZE,
1348 httpServerReplyHandler, connection);
1352 httpConnectionPipelined(HTTPConnectionPtr connection)
1354 HTTPRequestPtr request = connection->request;
1355 int i = 0;
1356 while(request) {
1357 i++;
1358 request = request->next;
1360 return i;
1363 void
1364 httpServerUnpipeline(HTTPRequestPtr request)
1366 HTTPConnectionPtr connection = request->connection;
1367 HTTPServerPtr server = connection->server;
1369 request->flags &= ~REQUEST_PERSISTENT;
1370 if(request->next) {
1371 HTTPRequestPtr req;
1372 do_log(L_WARN,
1373 "Restarting pipeline to %s:%d.\n",
1374 connection->server->name, connection->server->port);
1375 req = request->next;
1376 while(req) {
1377 req->connection = NULL;
1378 req = req->next;
1380 if(server->request)
1381 connection->request_last->next = server->request;
1382 else
1383 server->request_last = connection->request_last;
1384 server->request = request->next;
1385 request->next = NULL;
1386 connection->request_last = request;
1388 connection->pipelined = httpConnectionPipelined(connection);
1391 void
1392 httpServerRestart(HTTPConnectionPtr connection)
1394 HTTPServerPtr server = connection->server;
1395 HTTPRequestPtr request = connection->request;
1397 if(request) {
1398 HTTPRequestPtr req;
1399 if(request->next)
1400 do_log(L_WARN,
1401 "Restarting pipeline to %s:%d.\n",
1402 connection->server->name, connection->server->port);
1403 req = request;
1404 while(req) {
1405 req->connection = NULL;
1406 req = req->next;
1408 if(server->request)
1409 connection->request_last->next = server->request;
1410 else
1411 server->request_last = connection->request_last;
1412 server->request = request;
1413 connection->request = NULL;
1414 connection->request_last = NULL;
1416 connection->pipelined = 0;
1417 httpServerFinish(connection, 1, 0);
1421 httpServerRequest(ObjectPtr object, int method, int from, int to,
1422 HTTPRequestPtr requestor, void *closure)
1424 int rc;
1425 char name[132];
1426 int port;
1427 int x, y, z;
1429 assert(from >= 0 && (to < 0 || to > from));
1430 assert(closure == NULL);
1431 assert(!(object->flags & OBJECT_LOCAL));
1432 assert(object->type == OBJECT_HTTP);
1434 if(object->flags & OBJECT_INPROGRESS)
1435 return 1;
1437 if(requestor->flags & REQUEST_REQUESTED)
1438 return 0;
1440 assert(requestor->request == NULL);
1442 if(proxyOffline)
1443 return -1;
1445 rc = parseUrl(object->key, object->key_size, &x, &y, &port, &z);
1447 if(rc < 0 || x < 0 || y < 0 || y - x > 131) {
1448 do_log(L_ERROR, "Couldn't parse URL: ");
1449 do_log_n(L_ERROR, object->key, object->key_size);
1450 do_log(L_ERROR, "\n");
1451 abortObject(object, 400, internAtom("Couldn't parse URL"));
1452 notifyObject(object);
1453 return 1;
1456 if(!intListMember(port, allowedPorts)) {
1457 do_log(L_ERROR, "Attempted connection to port %d.\n", port);
1458 abortObject(object, 403, internAtom("Forbidden port"));
1459 notifyObject(object);
1460 return 1;
1463 memcpy(name, ((char*)object->key) + x, y - x);
1464 name[y - x] = '\0';
1466 requestor->flags |= REQUEST_REQUESTED;
1467 rc = httpMakeServerRequest(name, port, object, method, from, to,
1468 requestor);
1470 if(rc < 0) {
1471 abortObject(object,
1472 503, internAtom("Couldn't schedule server request"));
1473 notifyObject(object);
1474 return 1;
1477 return 1;
1481 httpWriteRequest(HTTPConnectionPtr connection, HTTPRequestPtr request,
1482 int bodylen)
1484 ObjectPtr object = request->object;
1485 int from = request->from, to = request->to, method = request->method;
1486 char *url = object->key, *m;
1487 int url_size = object->key_size;
1488 int x, y, port, z, location_size;
1489 char *location;
1490 int l, n, rc, bufsize;
1492 assert(method != METHOD_NONE);
1494 if(request->method == METHOD_GET ||
1495 request->method == METHOD_CONDITIONAL_GET) {
1496 if(to >= 0) {
1497 assert(to >= from);
1498 if(to == from) {
1499 do_log(L_ERROR, "Requesting empty segment?\n");
1500 return -1;
1504 if(object->flags & OBJECT_DYNAMIC) {
1505 from = 0;
1506 to = -1;
1507 } else {
1508 objectFillFromDisk(object, from / CHUNK_SIZE * CHUNK_SIZE, 1);
1509 l = objectHoleSize(request->object, from);
1510 if(l > 0) {
1511 if(to <= 0 || to > from + l)
1512 to = from + l;
1515 if(pmmSize && connection->server->pipeline >= 4) {
1516 if(from == 0)
1517 to = to < 0 ? pmmFirstSize : MIN(to, pmmFirstSize);
1518 else
1519 to = to < 0 ? from + pmmSize : MIN(to, from + pmmSize);
1522 if(from % CHUNK_SIZE != 0)
1523 if(objectHoleSize(object, from - 1) != 0)
1524 from = from / CHUNK_SIZE * CHUNK_SIZE;
1528 rc = parseUrl(url, url_size, &x, &y, &port, &z);
1530 if(rc < 0 || x < 0 || y < 0) {
1531 return -1;
1534 if(connection->reqbuf == NULL) {
1535 connection->reqbuf = get_chunk();
1536 if(connection->reqbuf == NULL)
1537 return -1;
1538 connection->reqlen = 0;
1541 if(method == METHOD_CONDITIONAL_GET &&
1542 object->last_modified < 0 && object->etag == NULL)
1543 method = request->method = METHOD_GET;
1545 again:
1546 bufsize =
1547 (connection->flags & CONN_BIGREQBUF) ? bigBufferSize : CHUNK_SIZE;
1548 n = connection->reqlen;
1549 switch(method) {
1550 case METHOD_GET:
1551 case METHOD_CONDITIONAL_GET: m = "GET"; break;
1552 case METHOD_HEAD: m = "HEAD"; break;
1553 case METHOD_POST: m = "POST"; break;
1554 case METHOD_PUT: m = "PUT"; break;
1555 default: abort();
1557 n = snnprintf(connection->reqbuf, n, bufsize, "%s ", m);
1559 if(connection->server->isProxy) {
1560 n = snnprint_n(connection->reqbuf, n, bufsize,
1561 url, url_size);
1562 } else {
1563 if(url_size - z == 0) {
1564 location = "/";
1565 location_size = 1;
1566 } else {
1567 location = url + z;
1568 location_size = url_size - z;
1571 n = snnprint_n(connection->reqbuf, n, bufsize,
1572 location, location_size);
1575 do_log(D_SERVER_REQ, "Server request: ");
1576 do_log_n(D_SERVER_REQ, url + x, y - x);
1577 do_log(D_SERVER_REQ, ": ");
1578 do_log_n(D_SERVER_REQ, connection->reqbuf, n);
1579 do_log(D_SERVER_REQ, " (method %d from %d to %d, 0x%lx for 0x%lx)\n",
1580 method, from, to,
1581 (unsigned long)connection, (unsigned long)object);
1583 n = snnprintf(connection->reqbuf, n, bufsize, " HTTP/1.1");
1585 n = snnprintf(connection->reqbuf, n, bufsize, "\r\nHost: ");
1586 n = snnprint_n(connection->reqbuf, n, bufsize, url + x, y - x);
1587 if(port != 80)
1588 n = snnprintf(connection->reqbuf, n, bufsize, ":%d", port);
1590 if(connection->server->isProxy && parentAuthCredentials) {
1591 n = buildServerAuthHeaders(connection->reqbuf, n, bufsize,
1592 parentAuthCredentials);
1595 if(bodylen >= 0)
1596 n = snnprintf(connection->reqbuf, n, bufsize,
1597 "\r\nContent-Length: %d", bodylen);
1599 if(request->flags & REQUEST_WAIT_CONTINUE)
1600 n = snnprintf(connection->reqbuf, n, bufsize,
1601 "\r\nExpect: 100-continue");
1603 if(method != METHOD_HEAD && (from > 0 || to >= 0)) {
1604 if(to >= 0) {
1605 n = snnprintf(connection->reqbuf, n, bufsize,
1606 "\r\nRange: bytes=%d-%d", from, to - 1);
1607 } else {
1608 n = snnprintf(connection->reqbuf, n, bufsize,
1609 "\r\nRange: bytes=%d-", from);
1613 if(method == METHOD_GET && object->etag && (from > 0 || to >= 0)) {
1614 if(request->request && request->request->request == request &&
1615 request->request->from == 0 && request->request->to == -1 &&
1616 pmmSize == 0 && pmmFirstSize == 0)
1617 n = snnprintf(connection->reqbuf, n, bufsize,
1618 "\r\nIf-Range: \"%s\"", object->etag);
1621 if(method == METHOD_CONDITIONAL_GET) {
1622 if(object->last_modified >= 0) {
1623 n = snnprintf(connection->reqbuf, n, bufsize,
1624 "\r\nIf-Modified-Since: ");
1625 n = format_time(connection->reqbuf, n, bufsize,
1626 object->last_modified);
1628 if(object->etag) {
1629 n = snnprintf(connection->reqbuf, n, bufsize,
1630 "\r\nIf-None-Match: \"%s\"", object->etag);
1634 n = httpPrintCacheControl(connection->reqbuf, n, bufsize,
1635 0, &request->cache_control);
1636 if(n < 0)
1637 goto fail;
1639 if(request->request && request->request->headers) {
1640 n = snnprint_n(connection->reqbuf, n, bufsize,
1641 request->request->headers->string,
1642 request->request->headers->length);
1644 if(!disableVia) {
1645 if(request->request && request->request->via) {
1646 n = snnprintf(connection->reqbuf, n, bufsize,
1647 "\r\nVia: %s, 1.1 %s",
1648 request->request->via->string, proxyName->string);
1649 } else {
1650 n = snnprintf(connection->reqbuf, n, bufsize,
1651 "\r\nVia: 1.1 %s",
1652 proxyName->string);
1656 n = snnprintf(connection->reqbuf, n, bufsize,
1657 "\r\nConnection: %s\r\n\r\n",
1658 (request->flags & REQUEST_PERSISTENT) ?
1659 "keep-alive" : "close");
1660 if(n < 0 || n >= bufsize - 1)
1661 goto fail;
1662 connection->reqlen = n;
1663 return n;
1665 fail:
1666 rc = 0;
1667 if(!(connection->flags & CONN_BIGREQBUF))
1668 rc = httpConnectionBigifyReqbuf(connection);
1669 if(rc == 1)
1670 goto again;
1671 return -1;
1675 httpServerHandler(int status,
1676 FdEventHandlerPtr event,
1677 StreamRequestPtr srequest)
1679 HTTPConnectionPtr connection = srequest->data;
1680 AtomPtr message;
1682 assert(connection->request->object->flags & OBJECT_INPROGRESS);
1684 if(connection->reqlen == 0) {
1685 do_log(D_SERVER_REQ, "Writing aborted on 0x%lx\n",
1686 (unsigned long)connection);
1687 goto fail;
1690 if(status == 0 && !streamRequestDone(srequest)) {
1691 httpSetTimeout(connection, serverTimeout);
1692 return 0;
1695 httpConnectionDestroyReqbuf(connection);
1697 if(status) {
1698 if(connection->serviced >= 1) {
1699 httpServerRestart(connection);
1700 return 1;
1702 if(status >= 0 || status == ECONNRESET) {
1703 message = internAtom("Couldn't send request to server: "
1704 "short write");
1705 } else {
1706 if(status != -EPIPE)
1707 do_log_error(L_ERROR, -status,
1708 "Couldn't send request to server");
1709 message =
1710 internAtomError(-status, "Couldn't send request to server");
1712 goto fail;
1715 return 1;
1717 fail:
1718 dispose_chunk(connection->reqbuf);
1719 connection->reqbuf = NULL;
1720 shutdown(connection->fd, 2);
1721 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLIN);
1722 httpSetTimeout(connection, 60);
1723 return 1;
1727 httpServerSendRequest(HTTPConnectionPtr connection)
1729 assert(connection->server);
1731 if(connection->reqlen == 0) {
1732 do_log(D_SERVER_REQ,
1733 "Writing aborted on 0x%lx\n", (unsigned long)connection);
1734 httpConnectionDestroyReqbuf(connection);
1735 shutdown(connection->fd, 2);
1736 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLIN | POLLOUT);
1737 return -1;
1740 httpSetTimeout(connection, serverTimeout);
1741 do_stream(IO_WRITE, connection->fd, 0,
1742 connection->reqbuf, connection->reqlen,
1743 httpServerHandler, connection);
1744 return 1;
1748 httpServerReplyHandler(int status,
1749 FdEventHandlerPtr event,
1750 StreamRequestPtr srequest)
1752 HTTPConnectionPtr connection = srequest->data;
1753 HTTPRequestPtr request = connection->request;
1754 int i, body;
1755 int bufsize =
1756 (connection->flags & CONN_BIGBUF) ? bigBufferSize : CHUNK_SIZE;
1758 assert(request->object->flags & OBJECT_INPROGRESS);
1759 if(status < 0) {
1760 if(connection->serviced >= 1) {
1761 httpServerRestart(connection);
1762 return 1;
1764 if(status != -ECLIENTRESET)
1765 do_log_error(L_ERROR, -status, "Read from server failed");
1766 httpServerAbort(connection, status != -ECLIENTRESET, 502,
1767 internAtomError(-status, "Read from server failed"));
1768 return 1;
1771 i = findEndOfHeaders(connection->buf, 0, srequest->offset, &body);
1772 connection->len = srequest->offset;
1774 if(i >= 0) {
1775 request->time1 = current_time;
1776 return httpServerHandlerHeaders(status, event, srequest, connection);
1779 if(status) {
1780 if(connection->serviced >= 1) {
1781 httpServerRestart(connection);
1782 return 1;
1784 if(status < 0) {
1785 do_log(L_ERROR,
1786 "Error reading server headers: %d\n", -status);
1787 httpServerAbort(connection, status != -ECLIENTRESET, 502,
1788 internAtomError(-status,
1789 "Error reading server headers"));
1790 } else
1791 httpServerAbort(connection, 1, 502,
1792 internAtom("Server dropped connection"));
1793 return 1;
1796 if(connection->len >= bufsize) {
1797 int rc = 0;
1798 if(!(connection->flags & CONN_BIGBUF))
1799 rc = httpConnectionBigify(connection);
1800 if(rc == 0) {
1801 do_log(L_ERROR, "Couldn't find end of server's headers.\n");
1802 httpServerAbort(connection, 1, 502,
1803 internAtom("Couldn't find end "
1804 "of server's headers"));
1805 return 1;
1806 } else if(rc < 0) {
1807 do_log(L_ERROR, "Couldn't allocate big buffer.\n");
1808 httpServerAbort(connection, 1, 500,
1809 internAtom("Couldn't allocate big buffer"));
1810 return 1;
1812 /* Can't just return 0 -- buf has moved. */
1813 do_stream(IO_READ,
1814 connection->fd, connection->len,
1815 connection->buf, bigBufferSize,
1816 httpServerReplyHandler, connection);
1817 return 1;
1820 return 0;
1824 httpServerHandlerHeaders(int eof,
1825 FdEventHandlerPtr event,
1826 StreamRequestPtr srequest,
1827 HTTPConnectionPtr connection)
1829 HTTPRequestPtr request = connection->request;
1830 ObjectPtr object = request->object;
1831 int rc;
1832 int code, version;
1833 int full_len;
1834 AtomPtr headers;
1835 int len;
1836 int te;
1837 CacheControlRec cache_control;
1838 int age = -1;
1839 time_t date, last_modified, expires;
1840 struct timeval *init_time;
1841 char *etag;
1842 AtomPtr via, new_via;
1843 int expect_body;
1844 HTTPRangeRec content_range;
1845 ObjectPtr new_object = NULL, old_object = NULL;
1846 int supersede = 0;
1847 AtomPtr message = NULL;
1848 int suspectDynamic;
1849 AtomPtr url = NULL;
1851 assert(request->object->flags & OBJECT_INPROGRESS);
1852 assert(eof >= 0);
1854 httpSetTimeout(connection, -1);
1856 if(request->flags & REQUEST_WAIT_CONTINUE) {
1857 do_log(D_SERVER_CONN, "W %s:%d.\n",
1858 connection->server->name, connection->server->port);
1859 request->flags &= ~REQUEST_WAIT_CONTINUE;
1862 rc = httpParseServerFirstLine(connection->buf, &code, &version, &message);
1863 if(rc <= 0) {
1864 do_log(L_ERROR, "Couldn't parse server status line.\n");
1865 httpServerAbort(connection, 1, 502,
1866 internAtom("Couldn't parse server status line"));
1867 return 1;
1870 do_log(D_SERVER_REQ, "Server status: ");
1871 do_log_n(D_SERVER_REQ, connection->buf,
1872 connection->buf[rc - 1] == '\r' ? rc - 2 : rc - 2);
1873 do_log(D_SERVER_REQ, " (0x%lx for 0x%lx)\n",
1874 (unsigned long)connection, (unsigned long)object);
1876 if(version != HTTP_10 && version != HTTP_11) {
1877 do_log(L_ERROR, "Unknown server HTTP version\n");
1878 httpServerAbort(connection, 1, 502,
1879 internAtom("Unknown server HTTP version"));
1880 releaseAtom(message);
1881 return 1;
1884 connection->version = version;
1885 connection->server->version = version;
1886 request->flags |= REQUEST_PERSISTENT;
1888 url = internAtomN(object->key, object->key_size);
1889 rc = httpParseHeaders(0, url, connection->buf, rc, request,
1890 &headers, &len, &cache_control, NULL, &te,
1891 &date, &last_modified, &expires, NULL, NULL, NULL,
1892 &age, &etag, NULL, NULL, &content_range,
1893 NULL, &via, NULL);
1894 if(rc < 0) {
1895 do_log(L_ERROR, "Couldn't parse server headers\n");
1896 releaseAtom(url);
1897 releaseAtom(message);
1898 httpServerAbort(connection, 1, 502,
1899 internAtom("Couldn't parse server headers"));
1900 return 1;
1903 if(code == 100) {
1904 releaseAtom(url);
1905 releaseAtom(message);
1906 /* We've already reset wait_continue above, but we must still
1907 ensure that the writer notices. */
1908 notifyObject(request->object);
1909 httpServerFinish(connection, -1, rc);
1910 return 1;
1913 if(code == 101) {
1914 httpServerAbort(connection, 1, 501,
1915 internAtom("Upgrade not implemented"));
1916 goto fail;
1919 if(via && !checkVia(proxyName, via)) {
1920 httpServerAbort(connection, 1, 504, internAtom("Proxy loop detected"));
1921 goto fail;
1923 full_len = content_range.full_length;
1925 if(code == 206) {
1926 if(content_range.from == -1 || content_range.to == -1) {
1927 do_log(L_ERROR, "Partial content without range.\n");
1928 httpServerAbort(connection, 1, 502,
1929 internAtom("Partial content without range"));
1930 goto fail;
1932 if(len >= 0 && len != content_range.to - content_range.from) {
1933 do_log(L_ERROR, "Inconsistent partial content.\n");
1934 httpServerAbort(connection, 1, 502,
1935 internAtom("Inconsistent partial content"));
1936 goto fail;
1938 } else if(code < 400 &&
1939 (content_range.from >= 0 || content_range.to >= 0 ||
1940 content_range.full_length >= 0)) {
1941 do_log(L_WARN, "Range without partial content.\n");
1942 /* Damn anakata. */
1943 content_range.from = -1;
1944 content_range.to = -1;
1945 content_range.full_length = -1;
1946 } else if(code != 304 && code != 412) {
1947 full_len = len;
1950 if(te != TE_IDENTITY && te != TE_CHUNKED) {
1951 do_log(L_ERROR, "Unsupported transfer-encoding\n");
1952 httpServerAbort(connection, 1, 502,
1953 internAtom("Unsupported transfer-encoding"));
1954 goto fail;
1957 if(code == 304) {
1958 if(request->method != METHOD_CONDITIONAL_GET) {
1959 do_log(L_ERROR, "Unexpected \"not changed\" reply from server\n");
1960 httpServerAbort(connection, 1, 502,
1961 internAtom("Unexpected \"not changed\" "
1962 "reply from server"));
1963 goto fail;
1965 if(object->etag && !etag) {
1966 /* RFC 2616 10.3.5. Violated by some front-end proxies. */
1967 do_log(L_WARN, "\"Not changed\" reply with no ETag.\n");
1971 if(code == 412) {
1972 if(request->method != METHOD_CONDITIONAL_GET ||
1973 (!object->etag && !object->last_modified)) {
1974 do_log(L_ERROR,
1975 "Unexpected \"precondition failed\" reply from server.\n");
1976 httpServerAbort(connection, 1, 502,
1977 internAtom("Unexpected \"precondition failed\" "
1978 "reply from server"));
1979 goto fail;
1983 releaseAtom(url);
1985 /* Okay, we're going to accept this reply. */
1987 if((code == 200 || code == 206 || code == 304 || code == 412) &&
1988 (cache_control.flags & (CACHE_NO | CACHE_NO_STORE) ||
1989 cache_control.max_age == 0 ||
1990 (cacheIsShared && cache_control.s_maxage == 0) ||
1991 (expires >= 0 && expires <= object->age))) {
1992 do_log(L_UNCACHEABLE, "Uncacheable object ");
1993 do_log_n(L_UNCACHEABLE, object->key, object->key_size);
1994 do_log(L_UNCACHEABLE, " (%d)\n", cache_control.flags);
1997 if(request->time0.tv_sec != null_time.tv_sec)
1998 init_time = &request->time0;
1999 else
2000 init_time = &current_time;
2001 age = MIN(init_time->tv_sec - age, init_time->tv_sec);
2003 if(request->method == METHOD_HEAD ||
2004 code < 200 || code == 204 || code == 304)
2005 expect_body = 0;
2006 else if(te == TE_IDENTITY)
2007 expect_body = (len != 0);
2008 else
2009 expect_body = 1;
2011 connection->chunk_remaining = -1;
2012 connection->te = te;
2014 old_object = object;
2016 connection->server->lies--;
2018 if(object->cache_control & CACHE_MISMATCH)
2019 supersede = 1;
2021 if(code == 304 || code == 412) {
2022 if((object->etag && etag && strcmp(object->etag, etag) != 0) ||
2023 (object->last_modified >= 0 && last_modified >= 0 &&
2024 object->last_modified != last_modified)) {
2025 do_log(L_ERROR, "Inconsistent \"%s\" reply for ",
2026 code == 304 ? "not changed":"precondition failed");
2027 do_log_n(L_ERROR, object->key, object->key_size);
2028 do_log(L_ERROR, "\n");
2029 object->flags |= OBJECT_DYNAMIC;
2030 supersede = 1;
2032 } else if(!(object->flags & OBJECT_INITIAL)) {
2033 if((object->last_modified < 0 || last_modified < 0) &&
2034 (!object->etag || !etag))
2035 supersede = 1;
2036 else if(object->last_modified != last_modified)
2037 supersede = 1;
2038 else if(object->etag || etag) {
2039 /* We need to be permissive here so as to deal with some
2040 front-end proxies that discard ETags on partial
2041 replies but not on full replies. */
2042 if(etag && object->etag && strcmp(object->etag, etag) != 0)
2043 supersede = 1;
2044 else if(!object->etag)
2045 supersede = 1;
2048 if(!supersede && (object->cache_control & CACHE_VARY) &&
2049 dontTrustVaryETag >= 1) {
2050 /* Check content-type to work around mod_gzip bugs */
2051 if(!httpHeaderMatch(atomContentType, object->headers, headers) ||
2052 !httpHeaderMatch(atomContentEncoding, object->headers, headers))
2053 supersede = 1;
2056 if(full_len < 0 && te == TE_IDENTITY) {
2057 /* It's an HTTP/1.0 CGI. Be afraid. */
2058 if(expect_body && content_range.from < 0 && content_range.to < 0)
2059 supersede = 1;
2062 if(!supersede && object->length >= 0 && full_len >= 0 &&
2063 object->length != full_len) {
2064 do_log(L_WARN, "Inconsistent length.\n");
2065 supersede = 1;
2068 if(!supersede &&
2069 ((object->last_modified >= 0 && last_modified >= 0) ||
2070 (object->etag && etag))) {
2071 if(request->method == METHOD_CONDITIONAL_GET) {
2072 do_log(L_WARN, "Server ignored conditional request.\n");
2073 connection->server->lies += 10;
2074 /* Drop the connection? */
2077 } else if(code == 416) {
2078 do_log(L_ERROR, "Unexpected \"range not satisfiable\" reply\n");
2079 httpServerAbort(connection, 1, 502,
2080 internAtom("Unexpected \"range not satisfiable\" "
2081 "reply"));
2082 /* The object may be superseded. Make sure the next request
2083 won't be partial. */
2084 abortObject(object, 502,
2085 internAtom("Unexpected \"range not satisfiable\" reply"));
2086 return 1;
2089 if(object->flags & OBJECT_INITIAL)
2090 supersede = 0;
2092 if(supersede) {
2093 do_log(L_SUPERSEDED, "Superseding object: ");
2094 do_log_n(L_SUPERSEDED, old_object->key, old_object->key_size);
2095 do_log(L_SUPERSEDED, " (%d %d %d %s -> %d %d %d %s)\n",
2096 object->code, object->length, (int)object->last_modified,
2097 object->etag?object->etag: "(none)",
2098 code, full_len, (int)last_modified,
2099 etag?etag:"(none)");
2100 privatiseObject(old_object, 0);
2101 new_object = makeObject(object->type, object->key,
2102 object->key_size, 1, 0,
2103 object->request, NULL);
2104 if(new_object == NULL) {
2105 do_log(L_ERROR, "Couldn't allocate object\n");
2106 httpServerAbort(connection, 1, 500,
2107 internAtom("Couldn't allocate object"));
2108 return 1;
2110 if(urlIsLocal(new_object->key, new_object->key_size))
2111 new_object->flags |= OBJECT_LOCAL;
2112 } else {
2113 new_object = object;
2116 suspectDynamic =
2117 (!etag && last_modified < 0) ||
2118 (cache_control.flags &
2119 (CACHE_NO_HIDDEN | CACHE_NO | CACHE_NO_STORE |
2120 (cacheIsShared ? CACHE_PRIVATE : 0))) ||
2121 (cache_control.max_age >= 0 && cache_control.max_age <= 2) ||
2122 (cacheIsShared &&
2123 cache_control.s_maxage >= 0 && cache_control.s_maxage <= 5) ||
2124 (old_object->last_modified >= 0 && old_object->expires >= 0 &&
2125 (old_object->expires - old_object->last_modified <= 1)) ||
2126 (supersede && (old_object->date - date <= 5));
2128 if(suspectDynamic)
2129 new_object->flags |= OBJECT_DYNAMIC;
2130 else if(!supersede)
2131 new_object->flags &= ~OBJECT_DYNAMIC;
2132 else if(old_object->flags & OBJECT_DYNAMIC)
2133 new_object->flags |= OBJECT_DYNAMIC;
2135 new_object->age = age;
2136 new_object->cache_control |= cache_control.flags;
2137 new_object->max_age = cache_control.max_age;
2138 new_object->s_maxage = cache_control.s_maxage;
2139 new_object->flags &= ~OBJECT_FAILED;
2141 if(date >= 0)
2142 new_object->date = date;
2143 if(last_modified >= 0)
2144 new_object->last_modified = last_modified;
2145 if(expires >= 0)
2146 new_object->expires = expires;
2147 if(new_object->etag == NULL)
2148 new_object->etag = etag;
2149 else
2150 free(etag);
2152 switch(code) {
2153 case 200:
2154 case 300: case 301: case 302: case 303: case 307:
2155 case 403: case 404: case 405: case 401:
2156 if(new_object->message) releaseAtom(new_object->message);
2157 new_object->code = code;
2158 new_object->message = message;
2159 break;
2160 case 206: case 304: case 412:
2161 if(new_object->code != 200 || !new_object->message) {
2162 if(new_object->message) releaseAtom(new_object->message);
2163 new_object->code = 200;
2164 new_object->message = internAtom("OK");
2166 releaseAtom(message);
2167 break;
2168 default:
2169 if(new_object->message) releaseAtom(new_object->message);
2170 new_object->code = code;
2171 new_object->message = retainAtom(message);
2172 break;
2175 httpTweakCachability(new_object);
2177 if(!via)
2178 new_via = internAtomF("%s %s",
2179 version == HTTP_11 ? "1.1" : "1.0",
2180 proxyName->string);
2181 else
2182 new_via = internAtomF("%s, %s %s", via->string,
2183 version == HTTP_11 ? "1.1" : "1.0",
2184 proxyName->string);
2185 if(new_via == NULL) {
2186 do_log(L_ERROR, "Couldn't allocate Via.\n");
2187 } else {
2188 if(new_object->via) releaseAtom(new_object->via);
2189 new_object->via = new_via;
2192 if(new_object->flags & OBJECT_INITIAL) {
2193 objectPartial(new_object, full_len, headers);
2194 } else {
2195 if(new_object->length < 0)
2196 new_object->length = full_len;
2197 /* XXX -- RFC 2616 13.5.3 */
2198 releaseAtom(headers);
2201 if(supersede) {
2202 assert(new_object != old_object);
2203 supersedeObject(old_object);
2206 if(new_object != old_object) {
2207 if(new_object->flags & OBJECT_INPROGRESS) {
2208 /* Make sure we don't fetch this object two times at the
2209 same time. Just drop the connection. */
2210 releaseObject(new_object);
2211 httpServerFinish(connection, 1, 0);
2212 return 1;
2214 old_object->flags &= ~OBJECT_VALIDATING;
2215 new_object->flags |= OBJECT_INPROGRESS;
2216 /* Signal the client side to switch to the new object -- see
2217 httpClientGetHandler. If it doesn't, we'll give up on this
2218 request below. */
2219 new_object->flags |= OBJECT_MUTATING;
2220 request->can_mutate = new_object;
2221 notifyObject(old_object);
2222 request->can_mutate = NULL;
2223 new_object->flags &= ~OBJECT_MUTATING;
2224 old_object->flags &= ~OBJECT_INPROGRESS;
2225 if(request->object == old_object) {
2226 if(request->request)
2227 request->request->request = NULL;
2228 request->request = NULL;
2229 request->object = new_object;
2230 } else {
2231 assert(request->object == new_object);
2233 releaseNotifyObject(old_object);
2234 old_object = NULL;
2235 object = new_object;
2236 } else {
2237 objectMetadataChanged(new_object, 0);
2240 if(object->flags & OBJECT_VALIDATING) {
2241 object->flags &= ~OBJECT_VALIDATING;
2242 notifyObject(object);
2245 if(!expect_body) {
2246 httpServerFinish(connection, 0, rc);
2247 return 1;
2250 if(request->request == NULL) {
2251 httpServerFinish(connection, 1, 0);
2252 return 1;
2255 if(code == 412) {
2256 /* 412 replies contain a useless body. For now, we
2257 drop the connection. */
2258 httpServerFinish(connection, 1, 0);
2259 return 1;
2263 if(request->flags & REQUEST_PERSISTENT) {
2264 if(request->method != METHOD_HEAD &&
2265 connection->te == TE_IDENTITY && len < 0) {
2266 do_log(L_ERROR, "Persistent reply with no Content-Length\n");
2267 /* That's potentially dangerous, as we could start reading
2268 arbitrary data into the object. Unfortunately, some
2269 servers do that. */
2270 request->flags &= ~REQUEST_PERSISTENT;
2274 /* we're getting a body */
2275 if(content_range.from > 0)
2276 connection->offset = content_range.from;
2277 else
2278 connection->offset = 0;
2280 if(content_range.to >= 0)
2281 request->to = content_range.to;
2283 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2284 (unsigned long)connection, (unsigned long)object,
2285 connection->offset);
2287 if(connection->len > rc) {
2288 rc = connectionAddData(connection, rc);
2289 if(rc) {
2290 if(rc < 0) {
2291 if(rc == -2) {
2292 do_log(L_ERROR, "Couldn't parse chunk size.\n");
2293 httpServerAbort(connection, 1, 502,
2294 internAtom("Couldn't parse chunk size"));
2295 } else {
2296 do_log(L_ERROR, "Couldn't add data to connection.\n");
2297 httpServerAbort(connection, 1, 500,
2298 internAtom("Couldn't add data "
2299 "to connection"));
2301 return 1;
2302 } else {
2303 if(code != 206) {
2304 if(object->length < 0) {
2305 object->length = object->size;
2306 objectMetadataChanged(object, 0);
2307 } else if(object->length != object->size) {
2308 httpServerAbort(connection, 1, 500,
2309 internAtom("Inconsistent "
2310 "object size"));
2311 object->length = -1;
2312 return 1;
2315 httpServerFinish(connection, 0, 0);
2316 return 1;
2319 } else {
2320 connection->len = 0;
2323 if(eof) {
2324 if(connection->te == TE_CHUNKED ||
2325 (object->length >= 0 &&
2326 connection->offset < object->length)) {
2327 do_log(L_ERROR, "Server closed connection.\n");
2328 httpServerAbort(connection, 1, 502,
2329 internAtom("Server closed connection"));
2330 return 1;
2331 } else {
2332 if(code != 206 && eof > 0 && object->length < 0) {
2333 object->length = object->size;
2334 objectMetadataChanged(object, 0);
2336 httpServerFinish(connection, 1, 0);
2337 return 1;
2339 } else {
2340 return httpServerReadData(connection, 1);
2342 return 0;
2344 fail:
2345 releaseAtom(url);
2346 releaseAtom(message);
2347 if(headers)
2348 releaseAtom(headers);
2349 if(etag)
2350 free(etag);
2351 if(via)
2352 releaseAtom(via);
2353 return 1;
2357 httpServerIndirectHandlerCommon(HTTPConnectionPtr connection, int eof)
2359 HTTPRequestPtr request = connection->request;
2361 assert(eof >= 0);
2362 assert(request->object->flags & OBJECT_INPROGRESS);
2364 if(connection->len > 0) {
2365 int rc;
2366 rc = connectionAddData(connection, 0);
2367 if(rc) {
2368 if(rc < 0) {
2369 if(rc == -2) {
2370 do_log(L_ERROR, "Couldn't parse chunk size.\n");
2371 httpServerAbort(connection, 1, 502,
2372 internAtom("Couldn't parse chunk size"));
2373 } else {
2374 do_log(L_ERROR, "Couldn't add data to connection.\n");
2375 httpServerAbort(connection, 1, 500,
2376 internAtom("Couldn't add data "
2377 "to connection"));
2379 return 1;
2380 } else {
2381 if(request->to < 0) {
2382 if(request->object->length < 0) {
2383 request->object->length = request->object->size;
2384 objectMetadataChanged(request->object, 0);
2385 } else if(request->object->length !=
2386 request->object->size) {
2387 request->object->length = -1;
2388 httpServerAbort(connection, 1, 502,
2389 internAtom("Inconsistent "
2390 "object size"));
2391 return 1;
2394 httpServerFinish(connection, 0, 0);
2396 return 1;
2400 if(eof && connection->len == 0) {
2401 if(connection->te == TE_CHUNKED ||
2402 (request->to >= 0 && connection->offset < request->to)) {
2403 do_log(L_ERROR, "Server dropped connection.\n");
2404 httpServerAbort(connection, 1, 502,
2405 internAtom("Server dropped connection"));
2406 return 1;
2407 } else {
2408 if(request->object->length < 0 && eof > 0 &&
2409 (request->to < 0 || request->to > request->object->size)) {
2410 request->object->length = request->object->size;
2411 objectMetadataChanged(request->object, 0);
2413 httpServerFinish(connection, 1, 0);
2414 return 1;
2416 } else {
2417 return httpServerReadData(connection, 0);
2422 httpServerIndirectHandler(int status,
2423 FdEventHandlerPtr event,
2424 StreamRequestPtr srequest)
2426 HTTPConnectionPtr connection = srequest->data;
2427 assert(connection->request->object->flags & OBJECT_INPROGRESS);
2429 httpSetTimeout(connection, -1);
2430 if(status < 0) {
2431 if(status != -ECLIENTRESET)
2432 do_log_error(L_ERROR, -status, "Read from server failed");
2433 httpServerAbort(connection, status != -ECLIENTRESET, 502,
2434 internAtomError(-status, "Read from server failed"));
2435 return 1;
2438 connection->len = srequest->offset;
2440 return httpServerIndirectHandlerCommon(connection, status);
2444 httpServerReadData(HTTPConnectionPtr connection, int immediate)
2446 HTTPRequestPtr request = connection->request;
2447 ObjectPtr object = request->object;
2448 int to = -1;
2450 assert(object->flags & OBJECT_INPROGRESS);
2452 if(request->request == NULL) {
2453 httpServerFinish(connection, 1, 0);
2454 return 1;
2457 if(request->to >= 0)
2458 to = request->to;
2459 else
2460 to = object->length;
2462 if(to >= 0 && to == connection->offset) {
2463 httpServerFinish(connection, 0, 0);
2464 return 1;
2467 if(connection->len == 0 &&
2468 ((connection->te == TE_IDENTITY && to > connection->offset) ||
2469 (connection->te == TE_CHUNKED && connection->chunk_remaining > 0))) {
2470 /* Read directly into the object */
2471 int i = connection->offset / CHUNK_SIZE;
2472 int j = connection->offset % CHUNK_SIZE;
2473 int end, len, more;
2474 /* See httpServerDirectHandlerCommon if you change this */
2475 if(connection->te == TE_CHUNKED) {
2476 len = connection->chunk_remaining;
2477 /* The logic here is that we want more to just fit the
2478 chunk header if we're doing a large read, but do a
2479 large read if we would otherwise do a small one. The
2480 magic constant 2000 comes from the assumption that the
2481 server uses chunks that have a size that are a power of
2482 two (possibly including the chunk header), and that we
2483 want a full ethernet packet to fit into our read. */
2484 more = (len >= 2000 ? 20 : MIN(2048 - len, CHUNK_SIZE));
2485 } else {
2486 len = to - connection->offset;
2487 /* We read more data only when there is a reasonable
2488 chance of there being another reply coming. */
2489 more = (connection->pipelined > 1) ? CHUNK_SIZE : 0;
2491 end = len + connection->offset;
2493 httpConnectionDestroyBuf(connection);
2495 /* The order of allocation is important in case we run out of
2496 memory. */
2497 lockChunk(object, i);
2498 if(object->chunks[i].data == NULL)
2499 object->chunks[i].data = get_chunk();
2500 if(object->chunks[i].data && object->chunks[i].size >= j) {
2501 if(len + j > CHUNK_SIZE) {
2502 lockChunk(object, i + 1);
2503 if(object->chunks[i + 1].data == NULL)
2504 object->chunks[i + 1].data = get_chunk();
2505 /* Unless we're grabbing all len of data, we do not
2506 want to do an indirect read immediately afterwards. */
2507 if(more && len + j <= 2 * CHUNK_SIZE) {
2508 if(!connection->buf)
2509 connection->buf = get_chunk(); /* checked below */
2511 if(object->chunks[i + 1].data) {
2512 do_stream_3(IO_READ | IO_NOTNOW, connection->fd, j,
2513 object->chunks[i].data, CHUNK_SIZE,
2514 object->chunks[i + 1].data,
2515 MIN(CHUNK_SIZE,
2516 end - (i + 1) * CHUNK_SIZE),
2517 connection->buf, connection->buf ? more : 0,
2518 httpServerDirectHandler2, connection);
2519 return 1;
2521 unlockChunk(object, i + 1);
2523 if(more && len + j <= CHUNK_SIZE) {
2524 if(!connection->buf)
2525 connection->buf = get_chunk();
2527 do_stream_2(IO_READ | IO_NOTNOW, connection->fd, j,
2528 object->chunks[i].data,
2529 MIN(CHUNK_SIZE, end - i * CHUNK_SIZE),
2530 connection->buf, connection->buf ? more : 0,
2531 httpServerDirectHandler, connection);
2532 return 1;
2533 } else {
2534 unlockChunk(object, i);
2538 if(connection->len == 0)
2539 httpConnectionDestroyBuf(connection);
2541 httpSetTimeout(connection, serverTimeout);
2542 do_stream_buf(IO_READ | IO_NOTNOW |
2543 ((immediate && connection->len) ? IO_IMMEDIATE : 0),
2544 connection->fd, connection->len,
2545 &connection->buf,
2546 (connection->te == TE_CHUNKED ?
2547 MIN(2048, CHUNK_SIZE) : CHUNK_SIZE),
2548 httpServerIndirectHandler, connection);
2549 return 1;
2553 httpServerDirectHandlerCommon(int kind, int status,
2554 FdEventHandlerPtr event,
2555 StreamRequestPtr srequest)
2557 HTTPConnectionPtr connection = srequest->data;
2558 HTTPRequestPtr request = connection->request;
2559 ObjectPtr object = request->object;
2560 int i = connection->offset / CHUNK_SIZE;
2561 int to, end, end1;
2563 assert(request->object->flags & OBJECT_INPROGRESS);
2565 httpSetTimeout(connection, -1);
2567 if(status < 0) {
2568 unlockChunk(object, i);
2569 if(kind == 2) unlockChunk(object, i + 1);
2570 if(status != -ECLIENTRESET)
2571 do_log_error(L_ERROR, -status, "Read from server failed");
2572 httpServerAbort(connection, status != -ECLIENTRESET, 502,
2573 internAtomError(-status, "Read from server failed"));
2574 return 1;
2577 /* We have incestuous knowledge of the decisions made in
2578 httpServerReadData */
2579 if(request->to >= 0)
2580 to = request->to;
2581 else
2582 to = object->length;
2583 if(connection->te == TE_CHUNKED)
2584 end = connection->offset + connection->chunk_remaining;
2585 else
2586 end = to;
2587 /* The amount of data actually read into the object */
2588 end1 = MIN(end, i * CHUNK_SIZE + MIN(kind * CHUNK_SIZE, srequest->offset));
2590 assert(end >= 0 && end1 >= i * CHUNK_SIZE && end1 <= (i + 2) * CHUNK_SIZE);
2592 object->chunks[i].size =
2593 MAX(object->chunks[i].size, MIN(end1 - i * CHUNK_SIZE, CHUNK_SIZE));
2594 if(kind == 2 && end1 > (i + 1) * CHUNK_SIZE) {
2595 object->chunks[i + 1].size =
2596 MAX(object->chunks[i + 1].size, end1 - (i + 1) * CHUNK_SIZE);
2598 if(connection->te == TE_CHUNKED) {
2599 connection->chunk_remaining -= (end1 - connection->offset);
2600 assert(connection->chunk_remaining >= 0);
2602 connection->offset = end1;
2603 object->size = MAX(object->size, end1);
2604 unlockChunk(object, i);
2605 if(kind == 2) unlockChunk(object, i + 1);
2607 if(i * CHUNK_SIZE + srequest->offset > end1) {
2608 connection->len = i * CHUNK_SIZE + srequest->offset - end1;
2609 return httpServerIndirectHandlerCommon(connection, status);
2610 } else {
2611 notifyObject(object);
2612 if(status) {
2613 httpServerFinish(connection, 1, 0);
2614 return 1;
2615 } else {
2616 return httpServerReadData(connection, 0);
2622 httpServerDirectHandler(int status,
2623 FdEventHandlerPtr event,
2624 StreamRequestPtr srequest)
2626 return httpServerDirectHandlerCommon(1, status, event, srequest);
2630 httpServerDirectHandler2(int status,
2631 FdEventHandlerPtr event,
2632 StreamRequestPtr srequest)
2634 return httpServerDirectHandlerCommon(2, status, event, srequest);
2637 /* Add the data accumulated in connection->buf into the object in
2638 connection->request. Returns 0 in the normal case, 1 if the TE is
2639 self-terminating and we're done, -1 if there was a problem with
2640 objectAddData, -2 if there was a problem with the data. */
2642 connectionAddData(HTTPConnectionPtr connection, int skip)
2644 HTTPRequestPtr request = connection->request;
2645 ObjectPtr object = request->object;
2646 int rc;
2648 if(connection->te == TE_IDENTITY) {
2649 int len;
2651 len = connection->len - skip;
2652 if(object->length >= 0) {
2653 len = MIN(object->length - connection->offset, len);
2655 if(request->to >= 0)
2656 len = MIN(request->to - connection->offset, len);
2657 if(len > 0) {
2658 rc = objectAddData(object, connection->buf + skip,
2659 connection->offset, len);
2660 if(rc < 0)
2661 return -1;
2662 connection->offset += len;
2663 connection->len -= (len + skip);
2664 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2665 (unsigned long)connection, (unsigned long)object,
2666 connection->offset);
2669 if(connection->len > 0 && skip + len > 0) {
2670 memmove(connection->buf,
2671 connection->buf + skip + len, connection->len);
2674 if((object->length >= 0 && object->length <= connection->offset) ||
2675 (request->to >= 0 && request->to <= connection->offset)) {
2676 notifyObject(object);
2677 return 1;
2678 } else {
2679 if(len > 0)
2680 notifyObject(object);
2681 return 0;
2683 } else if(connection->te == TE_CHUNKED) {
2684 int i = skip, j, size;
2685 /* connection->chunk_remaining is 0 at the end of a chunk, -1
2686 after the CR/LF pair ending a chunk, and -2 after we've
2687 seen a chunk of length 0. */
2688 if(connection->chunk_remaining > -2) {
2689 while(1) {
2690 if(connection->chunk_remaining <= 0) {
2691 if(connection->chunk_remaining == 0) {
2692 if(connection->len < i + 2)
2693 break;
2694 if(connection->buf[i] != '\r' ||
2695 connection->buf[i + 1] != '\n')
2696 return -1;
2697 i += 2;
2698 connection->chunk_remaining = -1;
2700 if(connection->len < i + 2)
2701 break;
2702 j = parseChunkSize(connection->buf, i,
2703 connection->len, &size);
2704 if(j < 0)
2705 return -2;
2706 if(j == 0)
2707 break;
2708 else
2709 i = j;
2710 if(size == 0) {
2711 connection->chunk_remaining = -2;
2712 break;
2713 } else {
2714 connection->chunk_remaining = size;
2716 } else {
2717 /* connection->chunk_remaining > 0 */
2718 size = MIN(connection->chunk_remaining,
2719 connection->len - i);
2720 if(size <= 0)
2721 break;
2722 rc = objectAddData(object, connection->buf + i,
2723 connection->offset, size);
2724 connection->offset += size;
2725 if(rc < 0)
2726 return -1;
2727 i += size;
2728 connection->chunk_remaining -= size;
2729 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2730 (unsigned long)connection,
2731 (unsigned long)object,
2732 connection->offset);
2736 connection->len -= i;
2737 if(connection->len > 0)
2738 memmove(connection->buf, connection->buf + i, connection->len);
2739 if(i > 0 || connection->chunk_remaining == -2)
2740 notifyObject(object);
2741 if(connection->chunk_remaining == -2)
2742 return 1;
2743 else
2744 return 0;
2745 } else {
2746 abort();
2750 void
2751 listServers(FILE *out)
2753 HTTPServerPtr server;
2754 int i, n, m, entry;
2756 fprintf(out, "<!DOCTYPE HTML PUBLIC "
2757 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2758 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2759 "<html><head>\n"
2760 "\r\n<title>Known servers</title>\n"
2761 "</head><body>\n"
2762 "<h1>Known servers</h1>\n");
2764 alternatingHttpStyle(out, "servers");
2765 fprintf(out, "<table id=servers>\n");
2766 fprintf(out, "<thead><tr><th>Server</th>"
2767 "<th>Version</th>"
2768 "<th>Persistent</th>"
2769 "<th>Pipeline</th>"
2770 "<th>Connections</th>"
2771 "<th></th>"
2772 "<th>rtt</th>"
2773 "<th>rate</th>"
2774 "</tr></thead>\n");
2775 fprintf(out, "<tbody>\n");
2776 server = servers;
2777 entry = 0;
2778 while(server) {
2779 fprintf(out, "<tr class=\"%s\">", entry % 2 == 0 ? "even" : "odd");
2780 if(server->port == 80)
2781 fprintf(out, "<td>%s</td>", server->name);
2782 else
2783 fprintf(out, "<td>%s:%d</td>", server->name, server->port);
2785 if(server->version == HTTP_11)
2786 fprintf(out, "<td>1.1</td>");
2787 else if(server->version == HTTP_10)
2788 fprintf(out, "<td>1.0</td>");
2789 else
2790 fprintf(out, "<td>unknown</td>");
2792 if(server->persistent < 0)
2793 fprintf(out, "<td>no</td>");
2794 else if(server->persistent > 0)
2795 fprintf(out, "<td>yes</td>");
2796 else
2797 fprintf(out, "<td>unknown</td>");
2799 if(server->version != HTTP_11 || server->persistent <= 0)
2800 fprintf(out, "<td></td>");
2801 else if(server->pipeline < 0)
2802 fprintf(out, "<td>no</td>");
2803 else if(server->pipeline >= 0 && server->pipeline <= 1)
2804 fprintf(out, "<td>unknown</td>");
2805 else if(server->pipeline == 2 || server->pipeline == 3)
2806 fprintf(out, "<td>probing</td>");
2807 else
2808 fprintf(out, "<td>yes</td>");
2810 n = 0; m = 0;
2811 for(i = 0; i < server->maxslots; i++)
2812 if(server->connection[i] && !server->connection[i]->connecting) {
2813 if(i < server->numslots)
2814 n++;
2815 else
2816 m++;
2819 fprintf(out, "<td>%d/%d", n, server->numslots);
2820 if(m)
2821 fprintf(out, " + %d</td>", m);
2822 else
2823 fprintf(out, "</td>");
2825 if(server->lies > 0)
2826 fprintf(out, "<td>(%d lies)</td>", (server->lies + 9) / 10);
2827 else
2828 fprintf(out, "<td></td>");
2830 if(server->rtt > 0)
2831 fprintf(out, "<td>%.3f</td>", (double)server->rtt / 1000000.0);
2832 else
2833 fprintf(out, "<td></td>");
2834 if(server->rate > 0)
2835 fprintf(out, "<td>%d</td>", server->rate);
2836 else
2837 fprintf(out, "<td></td>");
2839 fprintf(out, "</tr>\n");
2840 server = server->next;
2841 entry++;
2843 fprintf(out, "</tbody>\n");
2844 fprintf(out, "</table>\n");
2845 fprintf(out, "<p><a href=\"/polipo/\">back</a></p>");
2846 fprintf(out, "</body></html>\n");