Allow internAtomError to take a NULL format.
[polipo.git] / server.c
blob24d8e6105b10d9c7605c812c3f1f198db18176c4
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 = 0;
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->time0 = current_time;
826 i++;
827 server->request = request->next;
828 request->next = NULL;
829 if(server->request == NULL)
830 server->request_last = NULL;
831 httpQueueRequest(connection, request);
832 connection->pipelined++;
834 if(server->persistent > 0 && server->pipeline == 1 && i >= 2)
835 server->pipeline = 2;
837 if(i > 0) httpServerSendRequest(connection);
839 if(idle && connection->pipelined > 0)
840 httpServerReply(connection, 0);
842 if(i == 0) break;
845 for(i = 0; i < server->maxslots; i++) {
846 if(server->connection[i] &&
847 !server->connection[i]->connecting &&
848 !server->connection[i]->request) {
849 /* Artificially age any fresh connections that aren't used
850 straight away; this is necessary for the logic for POST and
851 the logic that determines whether a given request should be
852 restarted. */
853 if(server->connection[i]->serviced == 0)
854 server->connection[i]->serviced = 1;
855 if(!server->idleHandler[i])
856 server->idleHandler[i] =
857 registerFdEvent(server->connection[i]->fd, POLLIN,
858 httpServerIdleHandler,
859 sizeof(HTTPConnectionPtr),
860 &server->connection[i]);
861 if(!server->idleHandler[i]) {
862 do_log(L_ERROR, "Couldn't register idle handler.\n");
863 httpServerFinish(server->connection[i], 1, 0);
868 return 1;
872 httpServerSideRequest(HTTPServerPtr server)
874 HTTPRequestPtr request = server->request;
875 HTTPConnectionPtr connection;
876 HTTPRequestPtr requestor = request->request;
877 HTTPConnectionPtr client = requestor->connection;
878 int rc, i, freeslots, idle, connecting;
880 assert(REQUEST_SIDE(request));
882 connection = NULL;
883 freeslots = 0;
884 idle = -1;
885 connecting = 0;
887 /* Find a fresh connection */
888 for(i = 0; i < server->numslots; i++) {
889 if(!server->connection[i])
890 freeslots++;
891 else if(!server->connection[i]->connecting) {
892 if(!server->connection[i]->request) {
893 if(server->connection[i]->serviced == 0) {
894 if(server->idleHandler[i])
895 unregisterFdEvent(server->idleHandler[i]);
896 server->idleHandler[i] = NULL;
897 connection = server->connection[i];
898 break;
899 } else {
900 idle = i;
903 } else {
904 connecting++;
908 if(!connection) {
909 /* Make sure that a fresh connection will be established at some
910 point, then wait until httpServerTrigger calls us again. */
911 if(freeslots) {
912 httpServerConnection(server);
913 } else {
914 if(idle >= 0) {
915 /* Shutdown a random idle connection */
916 pokeFdEvent(server->connection[idle]->fd,
917 -EDOSHUTDOWN, POLLIN | POLLOUT);
920 return 0;
923 rc = httpWriteRequest(connection, request, client->bodylen);
924 if(rc < 0) {
925 do_log(L_ERROR, "Couldn't write POST or PUT request.\n");
926 httpServerAbortRequest(request, rc != -ECLIENTRESET, 503,
927 internAtom("Couldn't write request"));
928 return 0;
930 server->request = request->next;
931 request->next = NULL;
932 if(server->request == NULL)
933 server->request_last = NULL;
934 httpQueueRequest(connection, request);
935 connection->pipelined = 1;
936 request->time0 = current_time;
937 connection->reqoffset = 0;
938 connection->bodylen = client->bodylen;
939 httpServerDoSide(connection);
940 return 1;
943 int
944 httpServerDoSide(HTTPConnectionPtr connection)
946 HTTPRequestPtr request = connection->request;
947 HTTPRequestPtr requestor = request->request;
948 HTTPConnectionPtr client = requestor->connection;
949 int len = MIN(client->reqlen - client->reqbegin,
950 connection->bodylen - connection->reqoffset);
951 int doflush =
952 len > 0 &&
953 (len >= 1500 ||
954 client->reqbegin > 0 ||
955 (connection->reqoffset + client->reqlen - client->reqbegin) >=
956 connection->bodylen);
957 int done = connection->reqoffset >= connection->bodylen;
959 assert(connection->bodylen >= 0);
961 httpSetTimeout(connection, 60);
963 if(connection->reqlen > 0) {
964 /* Send the headers, but don't send any part of the body if
965 we're in wait_continue. */
966 do_stream_2(IO_WRITE,
967 connection->fd, 0,
968 connection->reqbuf, connection->reqlen,
969 client->reqbuf + client->reqbegin,
970 (request->flags & REQUEST_WAIT_CONTINUE) ? 0 : len,
971 httpServerSideHandler2, connection);
972 httpServerReply(connection, 0);
973 } else if(request->object->flags & OBJECT_ABORTED) {
974 if(connection->reqbuf)
975 dispose_chunk(connection->reqbuf);
976 connection->reqbuf = NULL;
977 connection->reqlen = 0;
978 pokeFdEvent(connection->fd, -ESHUTDOWN, POLLIN);
979 client->flags |= CONN_SIDE_READER;
980 do_stream(IO_READ | IO_IMMEDIATE,
981 client->fd, 0, NULL, 0,
982 httpClientSideHandler, client);
983 } else if(!(request->flags & REQUEST_WAIT_CONTINUE) && doflush) {
984 /* Make sure there's a reqbuf, as httpServerFinish uses
985 it to determine if there's a writer. */
986 if(connection->reqbuf == NULL)
987 connection->reqbuf = get_chunk();
988 assert(connection->reqbuf != NULL);
989 do_stream(IO_WRITE,
990 connection->fd, 0,
991 client->reqbuf + client->reqbegin, len,
992 httpServerSideHandler, connection);
993 } else {
994 if(connection->reqbuf) {
995 httpConnectionDestroyReqbuf(connection);
996 connection->reqlen = 0;
998 if(request->flags & REQUEST_WAIT_CONTINUE) {
999 ConditionHandlerPtr chandler;
1000 do_log(D_SERVER_CONN, "W... %s:%d.\n",
1001 connection->server->name, connection->server->port);
1002 chandler =
1003 conditionWait(&request->object->condition,
1004 httpServerContinueConditionHandler,
1005 sizeof(connection), &connection);
1006 if(chandler)
1007 return 1;
1008 else
1009 do_log(L_ERROR, "Couldn't register condition handler.\n");
1010 /* Fall through -- the client side will clean up. */
1012 client->flags |= CONN_SIDE_READER;
1013 do_stream(IO_READ | (done ? IO_IMMEDIATE : 0 ) | IO_NOTNOW,
1014 client->fd, client->reqlen,
1015 client->reqbuf, CHUNK_SIZE,
1016 httpClientSideHandler, client);
1018 return 1;
1021 static int
1022 httpClientDelayedDoSideHandler(TimeEventHandlerPtr event)
1024 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1025 httpServerDoSide(connection);
1026 return 1;
1029 static int
1030 httpServerDelayedDoSide(HTTPConnectionPtr connection)
1032 TimeEventHandlerPtr handler;
1033 handler = scheduleTimeEvent(1, httpClientDelayedDoSideHandler,
1034 sizeof(connection), &connection);
1035 if(!handler) {
1036 do_log(L_ERROR, "Couldn't schedule DoSide -- freeing memory.\n");
1037 free_chunk_arenas();
1038 handler = scheduleTimeEvent(1, httpClientDelayedDoSideHandler,
1039 sizeof(connection), &connection);
1040 do_log(L_ERROR, "Couldn't schedule DoSide.\n");
1041 /* Somebody will hopefully end up timing out. */
1042 return 1;
1044 return 1;
1047 static int
1048 httpServerSideHandlerCommon(int kind, int status,
1049 FdEventHandlerPtr event,
1050 StreamRequestPtr srequest)
1052 HTTPConnectionPtr connection = srequest->data;
1053 HTTPRequestPtr request = connection->request;
1054 HTTPRequestPtr requestor = request->request;
1055 HTTPConnectionPtr client = requestor->connection;
1056 int bodylen;
1058 assert(request->object->flags & OBJECT_INPROGRESS);
1060 if(status) {
1061 do_log_error(L_ERROR, -status, "Couldn't write to server");
1062 httpConnectionDestroyReqbuf(connection);
1063 if(status != -ECLIENTRESET)
1064 shutdown(connection->fd, 2);
1065 abortObject(request->object, 503,
1066 internAtom("Couldn't write to server"));
1067 /* Let the read side handle the error */
1068 httpServerDoSide(connection);
1069 return 1;
1072 assert(srequest->offset > 0);
1074 if(kind == 2) {
1075 if(srequest->offset < connection->reqlen)
1076 return 0;
1077 bodylen = srequest->offset - connection->reqlen;
1078 connection->reqlen = 0;
1079 httpConnectionDestroyReqbuf(connection);
1080 } else {
1081 bodylen = srequest->offset;
1085 assert(client->reqbegin + bodylen <= client->reqlen);
1087 if(client->reqlen > client->reqbegin + bodylen)
1088 memmove(client->reqbuf, client->reqbuf + client->reqbegin + bodylen,
1089 client->reqlen - client->reqbegin - bodylen);
1090 client->reqlen -= bodylen + client->reqbegin;
1091 client->reqbegin = 0;
1092 connection->reqoffset += bodylen;
1093 httpServerDoSide(connection);
1094 return 1;
1098 httpServerSideHandler(int status,
1099 FdEventHandlerPtr event,
1100 StreamRequestPtr srequest)
1102 return httpServerSideHandlerCommon(1, status, event, srequest);
1106 httpServerSideHandler2(int status,
1107 FdEventHandlerPtr event,
1108 StreamRequestPtr srequest)
1110 return httpServerSideHandlerCommon(2, status, event, srequest);
1113 static int
1114 httpServerContinueConditionHandler(int status, ConditionHandlerPtr chandler)
1116 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)chandler->data;
1118 if(connection->request->flags & REQUEST_WAIT_CONTINUE)
1119 return 0;
1120 httpServerDelayedDoSide(connection);
1121 return 1;
1124 /* s is 0 to keep the connection alive, 1 to shutdown the connection,
1125 and -1 to keep the connection alive and keep the current request. */
1126 void
1127 httpServerFinish(HTTPConnectionPtr connection, int s, int offset)
1129 HTTPServerPtr server = connection->server;
1130 HTTPRequestPtr request = connection->request;
1131 int i;
1133 if(request) {
1134 assert(connection->pipelined >= 1);
1135 assert((connection->pipelined > 1) == (request->next != NULL));
1136 } else {
1137 assert(connection->pipelined == 0);
1140 if(s == 0 && (!connection->request ||
1141 !(connection->request->flags & REQUEST_PERSISTENT)))
1142 s = 1;
1144 if(connection->reqbuf) {
1145 /* As most normal requests go out in a single packet, this is
1146 extremely unlikely to happen. As for POST/PUT requests,
1147 they are not pipelined, so this can only happen if the
1148 server sent an error reply early. */
1149 assert(connection->fd >= 0);
1150 shutdown(connection->fd, 1);
1151 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLOUT);
1152 httpServerDelayedFinish(connection);
1153 goto done;
1156 if(s >= 0 && request) {
1157 /* Update statistics about the server */
1158 int size = -1, d = -1, rtt = -1, rate = -1;
1159 if(connection->offset > 0 && request->from >= 0)
1160 size = connection->offset - request->from;
1161 if(request->time0.tv_sec > 0 && request->time1.tv_sec > 0) {
1162 d = timeval_minus_usec(&current_time, &request->time1);
1163 rtt = timeval_minus_usec(&request->time1, &request->time0);
1164 if(size >= 4096 && d > 50000)
1165 rate = ((double)size / (double)d) * 1000000.0 + 0.5;
1167 request->time0 = null_time;
1168 request->time1 = null_time;
1170 if(rtt > 0) {
1171 if(server->rtt > 0)
1172 server->rtt = (3 * server->rtt + rtt + 2) / 4;
1173 else
1174 server->rtt = rtt;
1176 if(rate > 0) {
1177 if(server->rate > 0)
1178 server->rate = (3 * server->rate + rate + 2) / 4;
1179 else
1180 server->rate = rate;
1183 httpDequeueRequest(connection);
1184 connection->pipelined--;
1185 request->object->flags &= ~(OBJECT_INPROGRESS | OBJECT_VALIDATING);
1186 if(request->request) {
1187 request->request->request = NULL;
1188 request->request = NULL;
1190 releaseNotifyObject(request->object);
1191 request->object = NULL;
1192 httpDestroyRequest(request);
1195 do_log(D_SERVER_CONN, "Done with server %s:%d connection (%d)\n",
1196 connection->server->name, connection->server->port, s);
1198 assert(offset <= connection->len);
1200 if(s <= 0) {
1201 if(offset < connection->len) {
1202 assert(connection->buf != NULL);
1203 if(!connection->pipelined) {
1204 do_log(L_WARN,
1205 "Closing connection to %s:%d: "
1206 "%d stray bytes of data.\n",
1207 server->name, server->port, connection->len - offset);
1208 s = 1;
1209 } else {
1210 memmove(connection->buf, connection->buf + offset,
1211 connection->len - offset);
1212 connection->len = connection->len - offset;
1213 if((connection->flags & CONN_BIGBUF) &&
1214 connection->len <= CHUNK_SIZE)
1215 httpConnectionUnbigify(connection);
1217 } else {
1218 connection->len = 0;
1222 connection->server->time = current_time.tv_sec;
1223 connection->serviced++;
1225 if(s > 0) {
1226 if(connection->timeout)
1227 cancelTimeEvent(connection->timeout);
1228 connection->timeout = NULL;
1229 httpConnectionDestroyBuf(connection);
1230 if(connection->fd >= 0)
1231 close(connection->fd);
1232 connection->fd = -1;
1233 server->persistent -= 1;
1234 if(server->persistent < -5)
1235 server->numslots = MIN(server->maxslots, serverMaxSlots);
1236 if(connection->request) {
1237 HTTPRequestPtr req;
1238 do_log(D_SERVER_CONN, "Restarting pipeline to %s:%d.\n",
1239 server->name, server->port);
1240 if(server->pipeline == 2)
1241 server->pipeline -= 20;
1242 else
1243 server->pipeline -= 5;
1244 req = connection->request;
1245 while(req) {
1246 req->connection = NULL;
1247 req = req->next;
1249 if(server->request)
1250 connection->request_last->next = server->request;
1251 else
1252 server->request_last = connection->request_last;
1253 server->request = connection->request;
1254 connection->request = NULL;
1255 connection->request_last = NULL;
1257 /* Make sure we don't get confused into thinking a probe
1258 is in progress. */
1259 if(server->pipeline == 2 || server->pipeline == 3)
1260 server->pipeline = 1;
1261 for(i = 0; i < server->maxslots; i++)
1262 if(connection == server->connection[i])
1263 break;
1264 assert(i < server->maxslots);
1265 if(server->idleHandler[i])
1266 unregisterFdEvent(server->idleHandler[i]);
1267 server->idleHandler[i] = NULL;
1268 server->connection[i] = NULL;
1269 free(connection);
1270 } else {
1271 server->persistent += 1;
1272 if(server->persistent > 0)
1273 server->numslots = MIN(server->maxslots, serverSlots);
1274 httpSetTimeout(connection, serverTimeout);
1275 /* See httpServerTrigger */
1276 if(connection->pipelined ||
1277 (server->version == HTTP_11 && server->pipeline <= 0) ||
1278 (server->pipeline == 3)) {
1279 server->pipeline++;
1281 if(s < 0 || connection->pipelined) {
1282 httpServerReply(connection, 1);
1283 } else {
1284 httpConnectionDestroyBuf(connection);
1288 done:
1289 httpServerTrigger(server);
1292 static int
1293 httpServerDelayedFinishHandler(TimeEventHandlerPtr event)
1295 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1296 httpServerFinish(connection, 1, 0);
1297 return 1;
1300 static void
1301 httpServerDelayedFinish(HTTPConnectionPtr connection)
1303 TimeEventHandlerPtr handler;
1305 handler = scheduleTimeEvent(1, httpServerDelayedFinishHandler,
1306 sizeof(connection), &connection);
1307 if(!handler) {
1308 do_log(L_ERROR,
1309 "Couldn't schedule delayed finish -- freeing memory.");
1310 free_chunk_arenas();
1311 handler = scheduleTimeEvent(1, httpServerDelayedFinishHandler,
1312 sizeof(connection), &connection);
1313 if(!handler) {
1314 do_log(L_ERROR,
1315 "Couldn't schedule delayed finish -- aborting.\n");
1316 polipoExit();
1321 void
1322 httpServerReply(HTTPConnectionPtr connection, int immediate)
1324 assert(connection->pipelined > 0);
1326 if(connection->request->request == NULL) {
1327 do_log(L_WARN, "Aborting pipeline on %s:%d.\n",
1328 connection->server->name, connection->server->port);
1329 httpServerFinish(connection, 1, 0);
1330 return;
1333 do_log(D_SERVER_CONN, "R: ");
1334 do_log_n(D_SERVER_CONN, connection->request->object->key,
1335 connection->request->object->key_size);
1336 do_log(D_SERVER_CONN, " (%d)\n", connection->request->method);
1338 if(connection->len == 0)
1339 httpConnectionDestroyBuf(connection);
1341 httpSetTimeout(connection, serverTimeout);
1342 do_stream_buf(IO_READ | (immediate ? IO_IMMEDIATE : 0) | IO_NOTNOW,
1343 connection->fd, connection->len,
1344 &connection->buf, CHUNK_SIZE,
1345 httpServerReplyHandler, connection);
1349 httpConnectionPipelined(HTTPConnectionPtr connection)
1351 HTTPRequestPtr request = connection->request;
1352 int i = 0;
1353 while(request) {
1354 i++;
1355 request = request->next;
1357 return i;
1360 void
1361 httpServerUnpipeline(HTTPRequestPtr request)
1363 HTTPConnectionPtr connection = request->connection;
1364 HTTPServerPtr server = connection->server;
1366 request->flags &= ~REQUEST_PERSISTENT;
1367 if(request->next) {
1368 HTTPRequestPtr req;
1369 do_log(L_WARN,
1370 "Restarting pipeline to %s:%d.\n",
1371 connection->server->name, connection->server->port);
1372 req = request->next;
1373 while(req) {
1374 req->connection = NULL;
1375 req = req->next;
1377 if(server->request)
1378 connection->request_last->next = server->request;
1379 else
1380 server->request_last = connection->request_last;
1381 server->request = request->next;
1382 request->next = NULL;
1383 connection->request_last = request;
1385 connection->pipelined = httpConnectionPipelined(connection);
1388 void
1389 httpServerRestart(HTTPConnectionPtr connection)
1391 HTTPServerPtr server = connection->server;
1392 HTTPRequestPtr request = connection->request;
1394 if(request) {
1395 HTTPRequestPtr req;
1396 if(request->next)
1397 do_log(L_WARN,
1398 "Restarting pipeline to %s:%d.\n",
1399 connection->server->name, connection->server->port);
1400 req = request;
1401 while(req) {
1402 req->connection = NULL;
1403 req = req->next;
1405 if(server->request)
1406 connection->request_last->next = server->request;
1407 else
1408 server->request_last = connection->request_last;
1409 server->request = request;
1410 connection->request = NULL;
1411 connection->request_last = NULL;
1413 connection->pipelined = 0;
1414 httpServerFinish(connection, 1, 0);
1418 httpServerRequest(ObjectPtr object, int method, int from, int to,
1419 HTTPRequestPtr requestor, void *closure)
1421 int rc;
1422 char name[132];
1423 int port;
1424 int x, y, z;
1426 assert(from >= 0 && (to < 0 || to > from));
1427 assert(closure == NULL);
1428 assert(!(object->flags & OBJECT_LOCAL));
1429 assert(object->type == OBJECT_HTTP);
1431 if(object->flags & OBJECT_INPROGRESS)
1432 return 1;
1434 if(requestor->flags & REQUEST_REQUESTED)
1435 return 0;
1437 assert(requestor->request == NULL);
1439 if(proxyOffline)
1440 return -1;
1442 rc = parseUrl(object->key, object->key_size, &x, &y, &port, &z);
1444 if(rc < 0 || x < 0 || y < 0 || y - x > 131) {
1445 do_log(L_ERROR, "Couldn't parse URL: ");
1446 do_log_n(L_ERROR, object->key, object->key_size);
1447 do_log(L_ERROR, "\n");
1448 abortObject(object, 400, internAtom("Couldn't parse URL"));
1449 notifyObject(object);
1450 return 1;
1453 if(!intListMember(port, allowedPorts)) {
1454 do_log(L_ERROR, "Attempted connection to port %d.\n", port);
1455 abortObject(object, 403, internAtom("Forbidden port"));
1456 notifyObject(object);
1457 return 1;
1460 memcpy(name, ((char*)object->key) + x, y - x);
1461 name[y - x] = '\0';
1463 requestor->flags |= REQUEST_REQUESTED;
1464 rc = httpMakeServerRequest(name, port, object, method, from, to,
1465 requestor);
1467 if(rc < 0) {
1468 abortObject(object,
1469 503, internAtom("Couldn't schedule server request"));
1470 notifyObject(object);
1471 return 1;
1474 return 1;
1478 httpWriteRequest(HTTPConnectionPtr connection, HTTPRequestPtr request,
1479 int bodylen)
1481 ObjectPtr object = request->object;
1482 int from = request->from, to = request->to, method = request->method;
1483 char *url = object->key, *m;
1484 int url_size = object->key_size;
1485 int x, y, port, z, location_size;
1486 char *location;
1487 int l, n, rc, bufsize;
1489 assert(method != METHOD_NONE);
1491 if(request->method == METHOD_GET ||
1492 request->method == METHOD_CONDITIONAL_GET) {
1493 if(to >= 0) {
1494 assert(to >= from);
1495 if(to == from) {
1496 do_log(L_ERROR, "Requesting empty segment?\n");
1497 return -1;
1501 if(object->flags & OBJECT_DYNAMIC) {
1502 from = 0;
1503 to = -1;
1504 } else {
1505 objectFillFromDisk(object, from / CHUNK_SIZE * CHUNK_SIZE, 1);
1506 l = objectHoleSize(request->object, from);
1507 if(l > 0) {
1508 if(to <= 0 || to > from + l)
1509 to = from + l;
1512 if(pmmSize && connection->server->pipeline >= 4) {
1513 if(from == 0)
1514 to = to < 0 ? pmmFirstSize : MIN(to, pmmFirstSize);
1515 else
1516 to = to < 0 ? from + pmmSize : MIN(to, from + pmmSize);
1519 if(from % CHUNK_SIZE != 0)
1520 if(objectHoleSize(object, from - 1) != 0)
1521 from = from / CHUNK_SIZE * CHUNK_SIZE;
1525 rc = parseUrl(url, url_size, &x, &y, &port, &z);
1527 if(rc < 0 || x < 0 || y < 0) {
1528 return -1;
1531 if(connection->reqbuf == NULL) {
1532 connection->reqbuf = get_chunk();
1533 if(connection->reqbuf == NULL)
1534 return -1;
1535 connection->reqlen = 0;
1538 if(method == METHOD_CONDITIONAL_GET &&
1539 object->last_modified < 0 && object->etag == NULL)
1540 method = request->method = METHOD_GET;
1542 again:
1543 bufsize =
1544 (connection->flags & CONN_BIGREQBUF) ? bigBufferSize : CHUNK_SIZE;
1545 n = connection->reqlen;
1546 switch(method) {
1547 case METHOD_GET:
1548 case METHOD_CONDITIONAL_GET: m = "GET"; break;
1549 case METHOD_HEAD: m = "HEAD"; break;
1550 case METHOD_POST: m = "POST"; break;
1551 case METHOD_PUT: m = "PUT"; break;
1552 default: abort();
1554 n = snnprintf(connection->reqbuf, n, bufsize, "%s ", m);
1556 if(connection->server->isProxy) {
1557 n = snnprint_n(connection->reqbuf, n, bufsize,
1558 url, url_size);
1559 } else {
1560 if(url_size - z == 0) {
1561 location = "/";
1562 location_size = 1;
1563 } else {
1564 location = url + z;
1565 location_size = url_size - z;
1568 n = snnprint_n(connection->reqbuf, n, bufsize,
1569 location, location_size);
1572 do_log(D_SERVER_REQ, "Server request: ");
1573 do_log_n(D_SERVER_REQ, url + x, y - x);
1574 do_log(D_SERVER_REQ, ": ");
1575 do_log_n(D_SERVER_REQ, connection->reqbuf, n);
1576 do_log(D_SERVER_REQ, " (method %d from %d to %d, 0x%lx for 0x%lx)\n",
1577 method, from, to,
1578 (unsigned long)connection, (unsigned long)object);
1580 n = snnprintf(connection->reqbuf, n, bufsize, " HTTP/1.1");
1582 n = snnprintf(connection->reqbuf, n, bufsize, "\r\nHost: ");
1583 n = snnprint_n(connection->reqbuf, n, bufsize, url + x, y - x);
1584 if(port != 80)
1585 n = snnprintf(connection->reqbuf, n, bufsize, ":%d", port);
1587 if(connection->server->isProxy && parentAuthCredentials) {
1588 n = buildServerAuthHeaders(connection->reqbuf, n, bufsize,
1589 parentAuthCredentials);
1592 if(bodylen >= 0)
1593 n = snnprintf(connection->reqbuf, n, bufsize,
1594 "\r\nContent-Length: %d", bodylen);
1596 if(request->flags & REQUEST_WAIT_CONTINUE)
1597 n = snnprintf(connection->reqbuf, n, bufsize,
1598 "\r\nExpect: 100-continue");
1600 if(method != METHOD_HEAD && (from > 0 || to >= 0)) {
1601 if(to >= 0) {
1602 n = snnprintf(connection->reqbuf, n, bufsize,
1603 "\r\nRange: bytes=%d-%d", from, to - 1);
1604 } else {
1605 n = snnprintf(connection->reqbuf, n, bufsize,
1606 "\r\nRange: bytes=%d-", from);
1610 if(method == METHOD_GET && object->etag && (from > 0 || to >= 0)) {
1611 if(request->request && request->request->request == request &&
1612 request->request->from == 0 && request->request->to == -1 &&
1613 pmmSize == 0 && pmmFirstSize == 0)
1614 n = snnprintf(connection->reqbuf, n, bufsize,
1615 "\r\nIf-Range: \"%s\"", object->etag);
1618 if(method == METHOD_CONDITIONAL_GET) {
1619 if(object->last_modified >= 0) {
1620 n = snnprintf(connection->reqbuf, n, bufsize,
1621 "\r\nIf-Modified-Since: ");
1622 n = format_time(connection->reqbuf, n, bufsize,
1623 object->last_modified);
1625 if(object->etag) {
1626 n = snnprintf(connection->reqbuf, n, bufsize,
1627 "\r\nIf-None-Match: \"%s\"", object->etag);
1631 n = httpPrintCacheControl(connection->reqbuf, n, bufsize,
1632 0, &request->cache_control);
1633 if(n < 0)
1634 goto fail;
1636 if(request->request && request->request->headers) {
1637 n = snnprint_n(connection->reqbuf, n, bufsize,
1638 request->request->headers->string,
1639 request->request->headers->length);
1641 if(!disableVia) {
1642 if(request->request && request->request->via) {
1643 n = snnprintf(connection->reqbuf, n, bufsize,
1644 "\r\nVia: %s, 1.1 %s",
1645 request->request->via->string, proxyName->string);
1646 } else {
1647 n = snnprintf(connection->reqbuf, n, bufsize,
1648 "\r\nVia: 1.1 %s",
1649 proxyName->string);
1653 n = snnprintf(connection->reqbuf, n, bufsize,
1654 "\r\nConnection: %s\r\n\r\n",
1655 (request->flags & REQUEST_PERSISTENT) ?
1656 "keep-alive" : "close");
1657 if(n < 0 || n >= bufsize - 1)
1658 goto fail;
1659 connection->reqlen = n;
1660 return n;
1662 fail:
1663 rc = 0;
1664 if(!(connection->flags & CONN_BIGREQBUF))
1665 rc = httpConnectionBigifyReqbuf(connection);
1666 if(rc == 1)
1667 goto again;
1668 return -1;
1672 httpServerHandler(int status,
1673 FdEventHandlerPtr event,
1674 StreamRequestPtr srequest)
1676 HTTPConnectionPtr connection = srequest->data;
1677 AtomPtr message;
1679 assert(connection->request->object->flags & OBJECT_INPROGRESS);
1681 if(connection->reqlen == 0) {
1682 do_log(D_SERVER_REQ, "Writing aborted on 0x%lx\n",
1683 (unsigned long)connection);
1684 goto fail;
1687 if(status == 0 && !streamRequestDone(srequest)) {
1688 httpSetTimeout(connection, serverTimeout);
1689 return 0;
1692 httpConnectionDestroyReqbuf(connection);
1694 if(status) {
1695 if(connection->serviced >= 1) {
1696 httpServerRestart(connection);
1697 return 1;
1699 if(status >= 0 || status == ECONNRESET) {
1700 message = internAtom("Couldn't send request to server: "
1701 "short write");
1702 } else {
1703 if(status != -EPIPE)
1704 do_log_error(L_ERROR, -status,
1705 "Couldn't send request to server");
1706 message =
1707 internAtomError(-status, "Couldn't send request to server");
1709 goto fail;
1712 return 1;
1714 fail:
1715 dispose_chunk(connection->reqbuf);
1716 connection->reqbuf = NULL;
1717 shutdown(connection->fd, 2);
1718 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLIN);
1719 httpSetTimeout(connection, 60);
1720 return 1;
1724 httpServerSendRequest(HTTPConnectionPtr connection)
1726 assert(connection->server);
1728 if(connection->reqlen == 0) {
1729 do_log(D_SERVER_REQ,
1730 "Writing aborted on 0x%lx\n", (unsigned long)connection);
1731 httpConnectionDestroyReqbuf(connection);
1732 shutdown(connection->fd, 2);
1733 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLIN | POLLOUT);
1734 return -1;
1737 httpSetTimeout(connection, serverTimeout);
1738 do_stream(IO_WRITE, connection->fd, 0,
1739 connection->reqbuf, connection->reqlen,
1740 httpServerHandler, connection);
1741 return 1;
1745 httpServerReplyHandler(int status,
1746 FdEventHandlerPtr event,
1747 StreamRequestPtr srequest)
1749 HTTPConnectionPtr connection = srequest->data;
1750 HTTPRequestPtr request = connection->request;
1751 int i, body;
1752 int bufsize =
1753 (connection->flags & CONN_BIGBUF) ? bigBufferSize : CHUNK_SIZE;
1755 assert(request->object->flags & OBJECT_INPROGRESS);
1756 if(status < 0) {
1757 if(connection->serviced >= 1) {
1758 httpServerRestart(connection);
1759 return 1;
1761 if(status != -ECLIENTRESET)
1762 do_log_error(L_ERROR, -status, "Read from server failed");
1763 httpServerAbort(connection, status != -ECLIENTRESET, 502,
1764 internAtomError(-status, "Read from server failed"));
1765 return 1;
1768 i = findEndOfHeaders(connection->buf, 0, srequest->offset, &body);
1769 connection->len = srequest->offset;
1771 if(i >= 0) {
1772 request->time1 = current_time;
1773 return httpServerHandlerHeaders(status, event, srequest, connection);
1776 if(status) {
1777 if(connection->serviced >= 1) {
1778 httpServerRestart(connection);
1779 return 1;
1781 if(status < 0) {
1782 do_log(L_ERROR,
1783 "Error reading server headers: %d\n", -status);
1784 httpServerAbort(connection, status != -ECLIENTRESET, 502,
1785 internAtomError(-status,
1786 "Error reading server headers"));
1787 } else
1788 httpServerAbort(connection, 1, 502,
1789 internAtom("Server dropped connection"));
1790 return 1;
1793 if(connection->len >= bufsize) {
1794 int rc = 0;
1795 if(!(connection->flags & CONN_BIGBUF))
1796 rc = httpConnectionBigify(connection);
1797 if(rc == 0) {
1798 do_log(L_ERROR, "Couldn't find end of server's headers.\n");
1799 httpServerAbort(connection, 1, 502,
1800 internAtom("Couldn't find end "
1801 "of server's headers"));
1802 return 1;
1803 } else if(rc < 0) {
1804 do_log(L_ERROR, "Couldn't allocate big buffer.\n");
1805 httpServerAbort(connection, 1, 500,
1806 internAtom("Couldn't allocate big buffer"));
1807 return 1;
1809 /* Can't just return 0 -- buf has moved. */
1810 do_stream(IO_READ,
1811 connection->fd, connection->len,
1812 connection->buf, bigBufferSize,
1813 httpServerReplyHandler, connection);
1814 return 1;
1817 return 0;
1821 httpServerHandlerHeaders(int eof,
1822 FdEventHandlerPtr event,
1823 StreamRequestPtr srequest,
1824 HTTPConnectionPtr connection)
1826 HTTPRequestPtr request = connection->request;
1827 ObjectPtr object = request->object;
1828 int rc;
1829 int code, version;
1830 int full_len;
1831 AtomPtr headers;
1832 int len;
1833 int te;
1834 CacheControlRec cache_control;
1835 int age = -1;
1836 time_t date, last_modified, expires;
1837 struct timeval *init_time;
1838 char *etag;
1839 AtomPtr via, new_via;
1840 int expect_body;
1841 HTTPRangeRec content_range;
1842 ObjectPtr new_object = NULL, old_object = NULL;
1843 int supersede = 0;
1844 AtomPtr message = NULL;
1845 int suspectDynamic;
1846 AtomPtr url = NULL;
1848 assert(request->object->flags & OBJECT_INPROGRESS);
1849 assert(eof >= 0);
1851 httpSetTimeout(connection, -1);
1853 if(request->flags & REQUEST_WAIT_CONTINUE) {
1854 do_log(D_SERVER_CONN, "W %s:%d.\n",
1855 connection->server->name, connection->server->port);
1856 request->flags &= ~REQUEST_WAIT_CONTINUE;
1859 rc = httpParseServerFirstLine(connection->buf, &code, &version, &message);
1860 if(rc <= 0) {
1861 do_log(L_ERROR, "Couldn't parse server status line.\n");
1862 httpServerAbort(connection, 1, 502,
1863 internAtom("Couldn't parse server status line"));
1864 return 1;
1867 do_log(D_SERVER_REQ, "Server status: ");
1868 do_log_n(D_SERVER_REQ, connection->buf,
1869 connection->buf[rc - 1] == '\r' ? rc - 2 : rc - 2);
1870 do_log(D_SERVER_REQ, " (0x%lx for 0x%lx)\n",
1871 (unsigned long)connection, (unsigned long)object);
1873 if(version != HTTP_10 && version != HTTP_11) {
1874 do_log(L_ERROR, "Unknown server HTTP version\n");
1875 httpServerAbort(connection, 1, 502,
1876 internAtom("Unknown server HTTP version"));
1877 releaseAtom(message);
1878 return 1;
1881 connection->version = version;
1882 connection->server->version = version;
1883 request->flags |= REQUEST_PERSISTENT;
1885 url = internAtomN(object->key, object->key_size);
1886 rc = httpParseHeaders(0, url, connection->buf, rc, request,
1887 &headers, &len, &cache_control, NULL, &te,
1888 &date, &last_modified, &expires, NULL, NULL, NULL,
1889 &age, &etag, NULL, NULL, &content_range,
1890 NULL, &via, NULL);
1891 if(rc < 0) {
1892 do_log(L_ERROR, "Couldn't parse server headers\n");
1893 releaseAtom(url);
1894 releaseAtom(message);
1895 httpServerAbort(connection, 1, 502,
1896 internAtom("Couldn't parse server headers"));
1897 return 1;
1900 if(code == 100) {
1901 releaseAtom(url);
1902 releaseAtom(message);
1903 /* We've already reset wait_continue above, but we must still
1904 ensure that the writer notices. */
1905 notifyObject(request->object);
1906 httpServerFinish(connection, -1, rc);
1907 return 1;
1910 if(code == 101) {
1911 httpServerAbort(connection, 1, 501,
1912 internAtom("Upgrade not implemented"));
1913 goto fail;
1916 if(via && !checkVia(proxyName, via)) {
1917 httpServerAbort(connection, 1, 504, internAtom("Proxy loop detected"));
1918 goto fail;
1920 full_len = content_range.full_length;
1922 if(code == 206) {
1923 if(content_range.from == -1 || content_range.to == -1) {
1924 do_log(L_ERROR, "Partial content without range.\n");
1925 httpServerAbort(connection, 1, 502,
1926 internAtom("Partial content without range"));
1927 goto fail;
1929 if(len >= 0 && len != content_range.to - content_range.from) {
1930 do_log(L_ERROR, "Inconsistent partial content.\n");
1931 httpServerAbort(connection, 1, 502,
1932 internAtom("Inconsistent partial content"));
1933 goto fail;
1935 } else if(code < 400 &&
1936 (content_range.from >= 0 || content_range.to >= 0 ||
1937 content_range.full_length >= 0)) {
1938 do_log(L_WARN, "Range without partial content.\n");
1939 /* Damn anakata. */
1940 content_range.from = -1;
1941 content_range.to = -1;
1942 content_range.full_length = -1;
1943 } else if(code != 304 && code != 412) {
1944 full_len = len;
1947 if(te != TE_IDENTITY && te != TE_CHUNKED) {
1948 do_log(L_ERROR, "Unsupported transfer-encoding\n");
1949 httpServerAbort(connection, 1, 502,
1950 internAtom("Unsupported transfer-encoding"));
1951 goto fail;
1954 if(code == 304) {
1955 if(request->method != METHOD_CONDITIONAL_GET) {
1956 do_log(L_ERROR, "Unexpected \"not changed\" reply from server\n");
1957 httpServerAbort(connection, 1, 502,
1958 internAtom("Unexpected \"not changed\" "
1959 "reply from server"));
1960 goto fail;
1962 if(object->etag && !etag) {
1963 /* RFC 2616 10.3.5. Violated by some front-end proxies. */
1964 do_log(L_WARN, "\"Not changed\" reply with no ETag.\n");
1968 if(code == 412) {
1969 if(request->method != METHOD_CONDITIONAL_GET) {
1970 do_log(L_ERROR,
1971 "Unexpected \"precondition failed\" reply from server.\n");
1972 httpServerAbort(connection, 1, 502,
1973 internAtom("Unexpected \"precondition failed\" "
1974 "reply from server"));
1975 goto fail;
1977 assert(object->etag || object->last_modified);
1980 releaseAtom(url);
1982 /* Okay, we're going to accept this reply. */
1984 if((code == 200 || code == 206 || code == 304 || code == 412) &&
1985 (cache_control.flags & (CACHE_NO | CACHE_NO_STORE) ||
1986 cache_control.max_age == 0 ||
1987 (cacheIsShared && cache_control.s_maxage == 0) ||
1988 (expires >= 0 && expires <= object->age))) {
1989 do_log(L_UNCACHEABLE, "Uncacheable object ");
1990 do_log_n(L_UNCACHEABLE, object->key, object->key_size);
1991 do_log(L_UNCACHEABLE, " (%d)\n", cache_control.flags);
1994 if(request->time0.tv_sec != null_time.tv_sec)
1995 init_time = &request->time0;
1996 else
1997 init_time = &current_time;
1998 age = MIN(init_time->tv_sec - age, init_time->tv_sec);
2000 if(request->method == METHOD_HEAD ||
2001 code < 200 || code == 204 || code == 304)
2002 expect_body = 0;
2003 else if(te == TE_IDENTITY)
2004 expect_body = (len != 0);
2005 else
2006 expect_body = 1;
2008 connection->chunk_remaining = -1;
2009 connection->te = te;
2011 old_object = object;
2013 connection->server->lies--;
2015 if(code == 304 || code == 412) {
2016 if((object->etag && etag && strcmp(object->etag, etag) != 0) ||
2017 (object->last_modified >= 0 && last_modified >= 0 &&
2018 object->last_modified != last_modified)) {
2019 do_log(L_ERROR, "Inconsistent \"%s\" reply for ",
2020 code == 304 ? "not changed":"precondition failed");
2021 do_log_n(L_ERROR, object->key, object->key_size);
2022 do_log(L_ERROR, "\n");
2023 object->flags |= OBJECT_DYNAMIC;
2024 supersede = 1;
2026 } else if(!(object->flags & OBJECT_INITIAL)) {
2027 if((object->last_modified < 0 || last_modified < 0) &&
2028 (!object->etag || !etag))
2029 supersede = 1;
2030 else if(object->last_modified != last_modified)
2031 supersede = 1;
2032 else if(object->etag || etag) {
2033 /* We need to be permissive here so as to deal with some
2034 front-end proxies that discard ETags on partial
2035 replies but not on full replies. */
2036 if(etag && object->etag && strcmp(object->etag, etag) != 0)
2037 supersede = 1;
2038 else if(!object->etag)
2039 supersede = 1;
2042 if(!supersede && object->length >= 0 && full_len >= 0 &&
2043 object->length != full_len) {
2044 do_log(L_WARN, "Inconsistent length.\n");
2045 supersede = 1;
2048 if(!supersede &&
2049 ((object->last_modified >= 0 && last_modified >= 0) ||
2050 (object->etag && etag))) {
2051 if(request->method == METHOD_CONDITIONAL_GET) {
2052 do_log(L_WARN, "Server ignored conditional request.\n");
2053 connection->server->lies += 10;
2054 /* Drop the connection? */
2057 } else if(code == 416) {
2058 do_log(L_ERROR, "Unexpected \"range not satisfiable\" reply\n");
2059 httpServerAbort(connection, 1, 502,
2060 internAtom("Unexpected \"range not satisfiable\" "
2061 "reply"));
2062 /* The object may be superseded. Make sure the next request
2063 won't be partial. */
2064 abortObject(object, 502,
2065 internAtom("Unexpected \"range not satisfiable\" reply"));
2066 return 1;
2069 if(object->flags & OBJECT_INITIAL)
2070 supersede = 0;
2072 if(supersede) {
2073 do_log(L_SUPERSEDED, "Superseding object: ");
2074 do_log_n(L_SUPERSEDED, old_object->key, old_object->key_size);
2075 do_log(L_SUPERSEDED, " (%d %d %d %s -> %d %d %d %s)\n",
2076 object->code, object->length, (int)object->last_modified,
2077 object->etag?object->etag: "(none)",
2078 code, full_len, (int)last_modified,
2079 etag?etag:"(none)");
2080 privatiseObject(old_object, 0);
2081 new_object = makeObject(object->type, object->key,
2082 object->key_size, 1, 0,
2083 object->request, NULL);
2084 if(new_object == NULL) {
2085 do_log(L_ERROR, "Couldn't allocate object\n");
2086 httpServerAbort(connection, 1, 500,
2087 internAtom("Couldn't allocate object"));
2088 return 1;
2090 if(urlIsLocal(new_object->key, new_object->key_size))
2091 new_object->flags |= OBJECT_LOCAL;
2092 } else {
2093 new_object = object;
2096 suspectDynamic =
2097 (!etag && last_modified < 0) ||
2098 (cache_control.flags &
2099 (CACHE_NO_HIDDEN | CACHE_NO | CACHE_NO_STORE |
2100 (cacheIsShared ? CACHE_PRIVATE : 0))) ||
2101 (cache_control.max_age >= 0 && cache_control.max_age <= 2) ||
2102 (cacheIsShared &&
2103 cache_control.s_maxage >= 0 && cache_control.s_maxage <= 5) ||
2104 (old_object->last_modified >= 0 && old_object->expires >= 0 &&
2105 (old_object->expires - old_object->last_modified <= 1)) ||
2106 (supersede && (old_object->date - date <= 5));
2108 if(suspectDynamic)
2109 new_object->flags |= OBJECT_DYNAMIC;
2110 else if(!supersede)
2111 new_object->flags &= ~OBJECT_DYNAMIC;
2112 else if(old_object->flags & OBJECT_DYNAMIC)
2113 new_object->flags |= OBJECT_DYNAMIC;
2115 new_object->age = age;
2116 new_object->cache_control |= cache_control.flags;
2117 new_object->max_age = cache_control.max_age;
2118 new_object->s_maxage = cache_control.s_maxage;
2119 new_object->flags &= ~OBJECT_FAILED;
2121 if(date >= 0)
2122 new_object->date = date;
2123 if(last_modified >= 0)
2124 new_object->last_modified = last_modified;
2125 if(expires >= 0)
2126 new_object->expires = expires;
2127 if(new_object->etag == NULL)
2128 new_object->etag = etag;
2129 else
2130 free(etag);
2132 switch(code) {
2133 case 200:
2134 case 300: case 301: case 302: case 303: case 307:
2135 case 403: case 404: case 405: case 401:
2136 if(new_object->message) releaseAtom(new_object->message);
2137 new_object->code = code;
2138 new_object->message = message;
2139 break;
2140 case 206: case 304: case 412:
2141 if(new_object->code != 200 || !new_object->message) {
2142 if(new_object->message) releaseAtom(new_object->message);
2143 new_object->code = 200;
2144 new_object->message = internAtom("OK");
2146 releaseAtom(message);
2147 break;
2148 default:
2149 if(new_object->message) releaseAtom(new_object->message);
2150 new_object->code = code;
2151 new_object->message = retainAtom(message);
2152 break;
2155 if((cache_control.flags & CACHE_AUTHORIZATION) &&
2156 !(cache_control.flags & CACHE_PUBLIC))
2157 new_object->cache_control |= (CACHE_NO_HIDDEN | OBJECT_LINEAR);
2159 /* This is not required by RFC 2616 -- but see RFC 3143 2.1.1. We
2160 manically avoid caching replies that we don't know how to
2161 handle, even if Expires or Cache-Control says otherwise. As to
2162 known uncacheable replies, we obey Cache-Control and default to
2163 allowing sharing but not caching. */
2164 if(code != 200 && code != 206 &&
2165 code != 300 && code != 301 && code != 302 && code != 303 &&
2166 code != 304 && code != 307 &&
2167 code != 403 && code != 404 && code != 405 && code != 416) {
2168 new_object->cache_control |= (CACHE_NO_HIDDEN | OBJECT_LINEAR);
2169 } else if(code != 200 && code != 206 &&
2170 code != 300 && code != 302 && code != 304 &&
2171 code != 410) {
2172 if(new_object->expires < 0 && !(cache_control.flags & CACHE_PUBLIC)) {
2173 new_object->cache_control |= CACHE_NO_HIDDEN;
2175 } else if(dontCacheRedirects && (code == 301 || code == 302)) {
2176 new_object->cache_control |= CACHE_NO_HIDDEN;
2179 if(urlIsUncachable(new_object->key, new_object->key_size)) {
2180 new_object->cache_control |= CACHE_NO_HIDDEN;
2183 if(!via)
2184 new_via = internAtomF("%s %s",
2185 version == HTTP_11 ? "1.1" : "1.0",
2186 proxyName->string);
2187 else
2188 new_via = internAtomF("%s, %s %s", via->string,
2189 version == HTTP_11 ? "1.1" : "1.0",
2190 proxyName->string);
2191 if(new_via == NULL) {
2192 do_log(L_ERROR, "Couldn't allocate Via.\n");
2193 } else {
2194 if(new_object->via) releaseAtom(new_object->via);
2195 new_object->via = new_via;
2198 if((new_object->cache_control & CACHE_NO_STORE) ||
2199 ((new_object->cache_control & CACHE_VARY) && !new_object->etag))
2200 new_object->cache_control |= CACHE_NO_HIDDEN;
2202 if(new_object->flags & OBJECT_INITIAL) {
2203 objectPartial(new_object, full_len, headers);
2204 } else {
2205 if(new_object->length < 0)
2206 new_object->length = full_len;
2207 /* XXX -- RFC 2616 13.5.3 */
2208 releaseAtom(headers);
2211 if(supersede) {
2212 assert(new_object != old_object);
2213 supersedeObject(old_object);
2216 if(new_object != old_object) {
2217 if(new_object->flags & OBJECT_INPROGRESS) {
2218 /* Make sure we don't fetch this object two times at the
2219 same time. Just drop the connection. */
2220 releaseObject(new_object);
2221 httpServerFinish(connection, 1, 0);
2222 return 1;
2224 old_object->flags &= ~OBJECT_VALIDATING;
2225 new_object->flags |= OBJECT_INPROGRESS;
2226 /* Signal the client side to switch to the new object -- see
2227 httpClientGetHandler. If it doesn't, we'll give up on this
2228 request below. */
2229 new_object->flags |= OBJECT_MUTATING;
2230 request->can_mutate = new_object;
2231 notifyObject(old_object);
2232 request->can_mutate = NULL;
2233 new_object->flags &= ~OBJECT_MUTATING;
2234 old_object->flags &= ~OBJECT_INPROGRESS;
2235 if(request->object == old_object) {
2236 if(request->request)
2237 request->request->request = NULL;
2238 request->request = NULL;
2239 request->object = new_object;
2240 } else {
2241 assert(request->object == new_object);
2243 releaseNotifyObject(old_object);
2244 old_object = NULL;
2245 object = new_object;
2246 } else {
2247 objectMetadataChanged(new_object, 0);
2250 if(object->flags & OBJECT_VALIDATING) {
2251 object->flags &= ~OBJECT_VALIDATING;
2252 notifyObject(object);
2255 if(!expect_body) {
2256 httpServerFinish(connection, 0, rc);
2257 return 1;
2260 if(request->request == NULL) {
2261 httpServerFinish(connection, 1, 0);
2262 return 1;
2265 if(code == 412) {
2266 /* 412 replies contain a useless body. For now, we
2267 drop the connection. */
2268 httpServerFinish(connection, 1, 0);
2269 return 1;
2273 if(request->flags & REQUEST_PERSISTENT) {
2274 if(request->method != METHOD_HEAD &&
2275 connection->te == TE_IDENTITY && len < 0) {
2276 do_log(L_ERROR, "Persistent reply with no Content-Length\n");
2277 /* That's potentially dangerous, as we could start reading
2278 arbitrary data into the object. Unfortunately, some
2279 servers do that. */
2280 request->flags &= ~REQUEST_PERSISTENT;
2284 /* we're getting a body */
2285 if(content_range.from > 0)
2286 connection->offset = content_range.from;
2287 else
2288 connection->offset = 0;
2290 if(content_range.to >= 0)
2291 request->to = content_range.to;
2293 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2294 (unsigned long)connection, (unsigned long)object,
2295 connection->offset);
2297 if(connection->len > rc) {
2298 rc = connectionAddData(connection, rc);
2299 if(rc) {
2300 if(rc < 0) {
2301 if(rc == -2) {
2302 do_log(L_ERROR, "Couldn't parse chunk size.\n");
2303 httpServerAbort(connection, 1, 502,
2304 internAtom("Couldn't parse chunk size"));
2305 } else {
2306 do_log(L_ERROR, "Couldn't add data to connection.\n");
2307 httpServerAbort(connection, 1, 500,
2308 internAtom("Couldn't add data "
2309 "to connection"));
2311 return 1;
2312 } else {
2313 if(code != 206) {
2314 if(object->length < 0) {
2315 object->length = object->size;
2316 objectMetadataChanged(object, 0);
2317 } else if(object->length != object->size) {
2318 httpServerAbort(connection, 1, 500,
2319 internAtom("Inconsistent "
2320 "object size"));
2321 object->length = -1;
2322 return 1;
2325 httpServerFinish(connection, 0, 0);
2326 return 1;
2329 } else {
2330 connection->len = 0;
2333 if(eof) {
2334 if(connection->te == TE_CHUNKED ||
2335 (object->length >= 0 &&
2336 connection->offset < object->length)) {
2337 do_log(L_ERROR, "Server closed connection.\n");
2338 httpServerAbort(connection, 1, 502,
2339 internAtom("Server closed connection"));
2340 return 1;
2341 } else {
2342 if(code != 206 && eof > 0 && object->length < 0) {
2343 object->length = object->size;
2344 objectMetadataChanged(object, 0);
2346 httpServerFinish(connection, 1, 0);
2347 return 1;
2349 } else {
2350 return httpServerReadData(connection, 1);
2352 return 0;
2354 fail:
2355 releaseAtom(url);
2356 releaseAtom(message);
2357 if(headers)
2358 releaseAtom(headers);
2359 if(etag)
2360 free(etag);
2361 if(via)
2362 releaseAtom(via);
2363 return 1;
2367 httpServerIndirectHandlerCommon(HTTPConnectionPtr connection, int eof)
2369 HTTPRequestPtr request = connection->request;
2371 assert(eof >= 0);
2372 assert(request->object->flags & OBJECT_INPROGRESS);
2374 if(connection->len > 0) {
2375 int rc;
2376 rc = connectionAddData(connection, 0);
2377 if(rc) {
2378 if(rc < 0) {
2379 if(rc == -2) {
2380 do_log(L_ERROR, "Couldn't parse chunk size.\n");
2381 httpServerAbort(connection, 1, 502,
2382 internAtom("Couldn't parse chunk size"));
2383 } else {
2384 do_log(L_ERROR, "Couldn't add data to connection.\n");
2385 httpServerAbort(connection, 1, 500,
2386 internAtom("Couldn't add data "
2387 "to connection"));
2389 return 1;
2390 } else {
2391 if(request->to < 0) {
2392 if(request->object->length < 0) {
2393 request->object->length = request->object->size;
2394 objectMetadataChanged(request->object, 0);
2395 } else if(request->object->length !=
2396 request->object->size) {
2397 request->object->length = -1;
2398 httpServerAbort(connection, 1, 502,
2399 internAtom("Inconsistent "
2400 "object size"));
2401 return 1;
2404 httpServerFinish(connection, 0, 0);
2406 return 1;
2410 if(eof && connection->len == 0) {
2411 if(connection->te == TE_CHUNKED ||
2412 (request->to >= 0 && connection->offset < request->to)) {
2413 do_log(L_ERROR, "Server dropped connection.\n");
2414 httpServerAbort(connection, 1, 502,
2415 internAtom("Server dropped connection"));
2416 return 1;
2417 } else {
2418 if(request->object->length < 0 && eof > 0 &&
2419 (request->to < 0 || request->to > request->object->size)) {
2420 request->object->length = request->object->size;
2421 objectMetadataChanged(request->object, 0);
2423 httpServerFinish(connection, 1, 0);
2424 return 1;
2426 } else {
2427 return httpServerReadData(connection, 0);
2432 httpServerIndirectHandler(int status,
2433 FdEventHandlerPtr event,
2434 StreamRequestPtr srequest)
2436 HTTPConnectionPtr connection = srequest->data;
2437 assert(connection->request->object->flags & OBJECT_INPROGRESS);
2439 httpSetTimeout(connection, -1);
2440 if(status < 0) {
2441 if(status != -ECLIENTRESET)
2442 do_log_error(L_ERROR, -status, "Read from server failed");
2443 httpServerAbort(connection, status != -ECLIENTRESET, 502,
2444 internAtomError(-status, "Read from server failed"));
2445 return 1;
2448 connection->len = srequest->offset;
2450 return httpServerIndirectHandlerCommon(connection, status);
2454 httpServerReadData(HTTPConnectionPtr connection, int immediate)
2456 HTTPRequestPtr request = connection->request;
2457 ObjectPtr object = request->object;
2458 int to = -1;
2460 assert(object->flags & OBJECT_INPROGRESS);
2462 if(request->request == NULL) {
2463 httpServerFinish(connection, 1, 0);
2464 return 1;
2467 if(request->to >= 0)
2468 to = request->to;
2469 else
2470 to = object->length;
2472 if(to >= 0 && to == connection->offset) {
2473 httpServerFinish(connection, 0, 0);
2474 return 1;
2477 if(connection->len == 0 &&
2478 ((connection->te == TE_IDENTITY && to > connection->offset) ||
2479 (connection->te == TE_CHUNKED && connection->chunk_remaining > 0))) {
2480 /* Read directly into the object */
2481 int i = connection->offset / CHUNK_SIZE;
2482 int j = connection->offset % CHUNK_SIZE;
2483 int end, len, more;
2484 /* See httpServerDirectHandlerCommon if you change this */
2485 if(connection->te == TE_CHUNKED) {
2486 len = connection->chunk_remaining;
2487 /* The logic here is that we want more to just fit the
2488 chunk header if we're doing a large read, but do a
2489 large read if we would otherwise do a small one. The
2490 magic constant 2000 comes from the assumption that the
2491 server uses chunks that have a size that are a power of
2492 two (possibly including the chunk header), and that we
2493 want a full ethernet packet to fit into our read. */
2494 more = (len >= 2000 ? 20 : MIN(2048 - len, CHUNK_SIZE));
2495 } else {
2496 len = to - connection->offset;
2497 /* We read more data only when there is a reasonable
2498 chance of there being another reply coming. */
2499 more = (connection->pipelined > 1) ? CHUNK_SIZE : 0;
2501 end = len + connection->offset;
2503 httpConnectionDestroyBuf(connection);
2505 /* The order of allocation is important in case we run out of
2506 memory. */
2507 lockChunk(object, i);
2508 if(object->chunks[i].data == NULL)
2509 object->chunks[i].data = get_chunk();
2510 if(object->chunks[i].data && object->chunks[i].size >= j) {
2511 if(len + j > CHUNK_SIZE) {
2512 lockChunk(object, i + 1);
2513 if(object->chunks[i + 1].data == NULL)
2514 object->chunks[i + 1].data = get_chunk();
2515 /* Unless we're grabbing all len of data, we do not
2516 want to do an indirect read immediately afterwards. */
2517 if(more && len + j <= 2 * CHUNK_SIZE) {
2518 if(!connection->buf)
2519 connection->buf = get_chunk(); /* checked below */
2521 if(object->chunks[i + 1].data) {
2522 do_stream_3(IO_READ | IO_NOTNOW, connection->fd, j,
2523 object->chunks[i].data, CHUNK_SIZE,
2524 object->chunks[i + 1].data,
2525 MIN(CHUNK_SIZE,
2526 end - (i + 1) * CHUNK_SIZE),
2527 connection->buf, connection->buf ? more : 0,
2528 httpServerDirectHandler2, connection);
2529 return 1;
2531 unlockChunk(object, i + 1);
2533 if(more && len + j <= CHUNK_SIZE) {
2534 if(!connection->buf)
2535 connection->buf = get_chunk();
2537 do_stream_2(IO_READ | IO_NOTNOW, connection->fd, j,
2538 object->chunks[i].data,
2539 MIN(CHUNK_SIZE, end - i * CHUNK_SIZE),
2540 connection->buf, connection->buf ? more : 0,
2541 httpServerDirectHandler, connection);
2542 return 1;
2543 } else {
2544 unlockChunk(object, i);
2548 if(connection->len == 0)
2549 httpConnectionDestroyBuf(connection);
2551 httpSetTimeout(connection, serverTimeout);
2552 do_stream_buf(IO_READ | IO_NOTNOW |
2553 ((immediate && connection->len) ? IO_IMMEDIATE : 0),
2554 connection->fd, connection->len,
2555 &connection->buf,
2556 (connection->te == TE_CHUNKED ?
2557 MIN(2048, CHUNK_SIZE) : CHUNK_SIZE),
2558 httpServerIndirectHandler, connection);
2559 return 1;
2563 httpServerDirectHandlerCommon(int kind, int status,
2564 FdEventHandlerPtr event,
2565 StreamRequestPtr srequest)
2567 HTTPConnectionPtr connection = srequest->data;
2568 HTTPRequestPtr request = connection->request;
2569 ObjectPtr object = request->object;
2570 int i = connection->offset / CHUNK_SIZE;
2571 int to, end, end1;
2573 assert(request->object->flags & OBJECT_INPROGRESS);
2575 httpSetTimeout(connection, -1);
2577 if(status < 0) {
2578 unlockChunk(object, i);
2579 if(kind == 2) unlockChunk(object, i + 1);
2580 if(status != -ECLIENTRESET)
2581 do_log_error(L_ERROR, -status, "Read from server failed");
2582 httpServerAbort(connection, status != -ECLIENTRESET, 502,
2583 internAtomError(-status, "Read from server failed"));
2584 return 1;
2587 /* We have incestuous knowledge of the decisions made in
2588 httpServerReadData */
2589 if(request->to >= 0)
2590 to = request->to;
2591 else
2592 to = object->length;
2593 if(connection->te == TE_CHUNKED)
2594 end = connection->offset + connection->chunk_remaining;
2595 else
2596 end = to;
2597 /* The amount of data actually read into the object */
2598 end1 = MIN(end, i * CHUNK_SIZE + MIN(kind * CHUNK_SIZE, srequest->offset));
2600 assert(end >= 0 && end1 >= i * CHUNK_SIZE && end1 <= (i + 2) * CHUNK_SIZE);
2602 object->chunks[i].size =
2603 MAX(object->chunks[i].size, MIN(end1 - i * CHUNK_SIZE, CHUNK_SIZE));
2604 if(kind == 2 && end1 > (i + 1) * CHUNK_SIZE) {
2605 object->chunks[i + 1].size =
2606 MAX(object->chunks[i + 1].size, end1 - (i + 1) * CHUNK_SIZE);
2608 if(connection->te == TE_CHUNKED) {
2609 connection->chunk_remaining -= (end1 - connection->offset);
2610 assert(connection->chunk_remaining >= 0);
2612 connection->offset = end1;
2613 object->size = MAX(object->size, end1);
2614 unlockChunk(object, i);
2615 if(kind == 2) unlockChunk(object, i + 1);
2617 if(i * CHUNK_SIZE + srequest->offset > end1) {
2618 connection->len = i * CHUNK_SIZE + srequest->offset - end1;
2619 return httpServerIndirectHandlerCommon(connection, status);
2620 } else {
2621 notifyObject(object);
2622 if(status) {
2623 httpServerFinish(connection, 1, 0);
2624 return 1;
2625 } else {
2626 return httpServerReadData(connection, 0);
2632 httpServerDirectHandler(int status,
2633 FdEventHandlerPtr event,
2634 StreamRequestPtr srequest)
2636 return httpServerDirectHandlerCommon(1, status, event, srequest);
2640 httpServerDirectHandler2(int status,
2641 FdEventHandlerPtr event,
2642 StreamRequestPtr srequest)
2644 return httpServerDirectHandlerCommon(2, status, event, srequest);
2647 /* Add the data accumulated in connection->buf into the object in
2648 connection->request. Returns 0 in the normal case, 1 if the TE is
2649 self-terminating and we're done, -1 if there was a problem with
2650 objectAddData, -2 if there was a problem with the data. */
2652 connectionAddData(HTTPConnectionPtr connection, int skip)
2654 HTTPRequestPtr request = connection->request;
2655 ObjectPtr object = request->object;
2656 int rc;
2658 if(connection->te == TE_IDENTITY) {
2659 int len;
2661 len = connection->len - skip;
2662 if(object->length >= 0) {
2663 len = MIN(object->length - connection->offset, len);
2665 if(request->to >= 0)
2666 len = MIN(request->to - connection->offset, len);
2667 if(len > 0) {
2668 rc = objectAddData(object, connection->buf + skip,
2669 connection->offset, len);
2670 if(rc < 0)
2671 return -1;
2672 connection->offset += len;
2673 connection->len -= (len + skip);
2674 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2675 (unsigned long)connection, (unsigned long)object,
2676 connection->offset);
2679 if(connection->len > 0 && skip + len > 0) {
2680 memmove(connection->buf,
2681 connection->buf + skip + len, connection->len);
2684 if((object->length >= 0 && object->length <= connection->offset) ||
2685 (request->to >= 0 && request->to <= connection->offset)) {
2686 notifyObject(object);
2687 return 1;
2688 } else {
2689 if(len > 0)
2690 notifyObject(object);
2691 return 0;
2693 } else if(connection->te == TE_CHUNKED) {
2694 int i = skip, j, size;
2695 /* connection->chunk_remaining is 0 at the end of a chunk, -1
2696 after the CR/LF pair ending a chunk, and -2 after we've
2697 seen a chunk of length 0. */
2698 if(connection->chunk_remaining > -2) {
2699 while(1) {
2700 if(connection->chunk_remaining <= 0) {
2701 if(connection->chunk_remaining == 0) {
2702 if(connection->len < i + 2)
2703 break;
2704 if(connection->buf[i] != '\r' ||
2705 connection->buf[i + 1] != '\n')
2706 return -1;
2707 i += 2;
2708 connection->chunk_remaining = -1;
2710 if(connection->len < i + 2)
2711 break;
2712 j = parseChunkSize(connection->buf, i,
2713 connection->len, &size);
2714 if(j < 0)
2715 return -2;
2716 if(j == 0)
2717 break;
2718 else
2719 i = j;
2720 if(size == 0) {
2721 connection->chunk_remaining = -2;
2722 break;
2723 } else {
2724 connection->chunk_remaining = size;
2726 } else {
2727 /* connection->chunk_remaining > 0 */
2728 size = MIN(connection->chunk_remaining,
2729 connection->len - i);
2730 if(size <= 0)
2731 break;
2732 rc = objectAddData(object, connection->buf + i,
2733 connection->offset, size);
2734 connection->offset += size;
2735 if(rc < 0)
2736 return -1;
2737 i += size;
2738 connection->chunk_remaining -= size;
2739 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2740 (unsigned long)connection,
2741 (unsigned long)object,
2742 connection->offset);
2746 connection->len -= i;
2747 if(connection->len > 0)
2748 memmove(connection->buf, connection->buf + i, connection->len);
2749 if(i > 0 || connection->chunk_remaining == -2)
2750 notifyObject(object);
2751 if(connection->chunk_remaining == -2)
2752 return 1;
2753 else
2754 return 0;
2755 } else {
2756 abort();
2760 void
2761 listServers(FILE *out)
2763 HTTPServerPtr server;
2764 int i, n, m, entry;
2766 fprintf(out, "<!DOCTYPE HTML PUBLIC "
2767 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2768 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2769 "<html><head>\n"
2770 "\r\n<title>Known servers</title>\n"
2771 "</head><body>\n"
2772 "<h1>Known servers</h1>\n");
2774 alternatingHttpStyle(out, "servers");
2775 fprintf(out, "<table id=servers>\n");
2776 fprintf(out, "<thead><tr><th>Server</th>"
2777 "<th>Version</th>"
2778 "<th>Persistent</th>"
2779 "<th>Pipeline</th>"
2780 "<th>Connections</th>"
2781 "<th></th>"
2782 "<th>rtt</th>"
2783 "<th>rate</th>"
2784 "</tr></thead>\n");
2785 fprintf(out, "<tbody>\n");
2786 server = servers;
2787 entry = 0;
2788 while(server) {
2789 fprintf(out, "<tr class=\"%s\">", entry % 2 == 0 ? "even" : "odd");
2790 if(server->port == 80)
2791 fprintf(out, "<td>%s</td>", server->name);
2792 else
2793 fprintf(out, "<td>%s:%d</td>", server->name, server->port);
2795 if(server->version == HTTP_11)
2796 fprintf(out, "<td>1.1</td>");
2797 else if(server->version == HTTP_10)
2798 fprintf(out, "<td>1.0</td>");
2799 else
2800 fprintf(out, "<td>unknown</td>");
2802 if(server->persistent < 0)
2803 fprintf(out, "<td>no</td>");
2804 else if(server->persistent > 0)
2805 fprintf(out, "<td>yes</td>");
2806 else
2807 fprintf(out, "<td>unknown</td>");
2809 if(server->version != HTTP_11 || server->persistent <= 0)
2810 fprintf(out, "<td></td>");
2811 else if(server->pipeline < 0)
2812 fprintf(out, "<td>no</td>");
2813 else if(server->pipeline >= 0 && server->pipeline <= 1)
2814 fprintf(out, "<td>unknown</td>");
2815 else if(server->pipeline == 2 || server->pipeline == 3)
2816 fprintf(out, "<td>probing</td>");
2817 else
2818 fprintf(out, "<td>yes</td>");
2820 n = 0; m = 0;
2821 for(i = 0; i < server->maxslots; i++)
2822 if(server->connection[i] && !server->connection[i]->connecting) {
2823 if(i < server->numslots)
2824 n++;
2825 else
2826 m++;
2829 fprintf(out, "<td>%d/%d", n, server->numslots);
2830 if(m)
2831 fprintf(out, " + %d</td>", m);
2832 else
2833 fprintf(out, "</td>");
2835 if(server->lies > 0)
2836 fprintf(out, "<td>(%d lies)</td>", (server->lies + 9) / 10);
2837 else
2838 fprintf(out, "<td></td>");
2840 if(server->rtt > 0)
2841 fprintf(out, "<td>%.3f</td>", (double)server->rtt / 1000000.0);
2842 else
2843 fprintf(out, "<td></td>");
2844 if(server->rate > 0)
2845 fprintf(out, "<td>%d</td>", server->rate);
2846 else
2847 fprintf(out, "<td></td>");
2849 fprintf(out, "</tr>\n");
2850 server = server->next;
2851 entry++;
2853 fprintf(out, "</tbody>\n");
2854 fprintf(out, "</table>\n");
2855 fprintf(out, "<p><a href=\"/polipo/\">back</a></p>");
2856 fprintf(out, "</body></html>\n");