Document the change in the default values of disk cache permissions.
[polipo.git] / server.c
blob1ea1011229ffbdc4a15037c999a8f55e4c604a42
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 (!object->etag && !object->last_modified)) {
1971 do_log(L_ERROR,
1972 "Unexpected \"precondition failed\" reply from server.\n");
1973 httpServerAbort(connection, 1, 502,
1974 internAtom("Unexpected \"precondition failed\" "
1975 "reply from server"));
1976 goto fail;
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(full_len < 0 && te == TE_IDENTITY) {
2043 /* It's an HTTP/1.0 CGI. Be afraid. */
2044 if(expect_body && content_range.from < 0 && content_range.to < 0)
2045 supersede = 1;
2048 if(!supersede && object->length >= 0 && full_len >= 0 &&
2049 object->length != full_len) {
2050 do_log(L_WARN, "Inconsistent length.\n");
2051 supersede = 1;
2054 if(!supersede &&
2055 ((object->last_modified >= 0 && last_modified >= 0) ||
2056 (object->etag && etag))) {
2057 if(request->method == METHOD_CONDITIONAL_GET) {
2058 do_log(L_WARN, "Server ignored conditional request.\n");
2059 connection->server->lies += 10;
2060 /* Drop the connection? */
2063 } else if(code == 416) {
2064 do_log(L_ERROR, "Unexpected \"range not satisfiable\" reply\n");
2065 httpServerAbort(connection, 1, 502,
2066 internAtom("Unexpected \"range not satisfiable\" "
2067 "reply"));
2068 /* The object may be superseded. Make sure the next request
2069 won't be partial. */
2070 abortObject(object, 502,
2071 internAtom("Unexpected \"range not satisfiable\" reply"));
2072 return 1;
2075 if(object->flags & OBJECT_INITIAL)
2076 supersede = 0;
2078 if(supersede) {
2079 do_log(L_SUPERSEDED, "Superseding object: ");
2080 do_log_n(L_SUPERSEDED, old_object->key, old_object->key_size);
2081 do_log(L_SUPERSEDED, " (%d %d %d %s -> %d %d %d %s)\n",
2082 object->code, object->length, (int)object->last_modified,
2083 object->etag?object->etag: "(none)",
2084 code, full_len, (int)last_modified,
2085 etag?etag:"(none)");
2086 privatiseObject(old_object, 0);
2087 new_object = makeObject(object->type, object->key,
2088 object->key_size, 1, 0,
2089 object->request, NULL);
2090 if(new_object == NULL) {
2091 do_log(L_ERROR, "Couldn't allocate object\n");
2092 httpServerAbort(connection, 1, 500,
2093 internAtom("Couldn't allocate object"));
2094 return 1;
2096 if(urlIsLocal(new_object->key, new_object->key_size))
2097 new_object->flags |= OBJECT_LOCAL;
2098 } else {
2099 new_object = object;
2102 suspectDynamic =
2103 (!etag && last_modified < 0) ||
2104 (cache_control.flags &
2105 (CACHE_NO_HIDDEN | CACHE_NO | CACHE_NO_STORE |
2106 (cacheIsShared ? CACHE_PRIVATE : 0))) ||
2107 (cache_control.max_age >= 0 && cache_control.max_age <= 2) ||
2108 (cacheIsShared &&
2109 cache_control.s_maxage >= 0 && cache_control.s_maxage <= 5) ||
2110 (old_object->last_modified >= 0 && old_object->expires >= 0 &&
2111 (old_object->expires - old_object->last_modified <= 1)) ||
2112 (supersede && (old_object->date - date <= 5));
2114 if(suspectDynamic)
2115 new_object->flags |= OBJECT_DYNAMIC;
2116 else if(!supersede)
2117 new_object->flags &= ~OBJECT_DYNAMIC;
2118 else if(old_object->flags & OBJECT_DYNAMIC)
2119 new_object->flags |= OBJECT_DYNAMIC;
2121 new_object->age = age;
2122 new_object->cache_control |= cache_control.flags;
2123 new_object->max_age = cache_control.max_age;
2124 new_object->s_maxage = cache_control.s_maxage;
2125 new_object->flags &= ~OBJECT_FAILED;
2127 if(date >= 0)
2128 new_object->date = date;
2129 if(last_modified >= 0)
2130 new_object->last_modified = last_modified;
2131 if(expires >= 0)
2132 new_object->expires = expires;
2133 if(new_object->etag == NULL)
2134 new_object->etag = etag;
2135 else
2136 free(etag);
2138 switch(code) {
2139 case 200:
2140 case 300: case 301: case 302: case 303: case 307:
2141 case 403: case 404: case 405: case 401:
2142 if(new_object->message) releaseAtom(new_object->message);
2143 new_object->code = code;
2144 new_object->message = message;
2145 break;
2146 case 206: case 304: case 412:
2147 if(new_object->code != 200 || !new_object->message) {
2148 if(new_object->message) releaseAtom(new_object->message);
2149 new_object->code = 200;
2150 new_object->message = internAtom("OK");
2152 releaseAtom(message);
2153 break;
2154 default:
2155 if(new_object->message) releaseAtom(new_object->message);
2156 new_object->code = code;
2157 new_object->message = retainAtom(message);
2158 break;
2161 if((cache_control.flags & CACHE_AUTHORIZATION) &&
2162 !(cache_control.flags & CACHE_PUBLIC))
2163 new_object->cache_control |= (CACHE_NO_HIDDEN | OBJECT_LINEAR);
2165 /* This is not required by RFC 2616 -- but see RFC 3143 2.1.1. We
2166 manically avoid caching replies that we don't know how to
2167 handle, even if Expires or Cache-Control says otherwise. As to
2168 known uncacheable replies, we obey Cache-Control and default to
2169 allowing sharing but not caching. */
2170 if(code != 200 && code != 206 &&
2171 code != 300 && code != 301 && code != 302 && code != 303 &&
2172 code != 304 && code != 307 &&
2173 code != 403 && code != 404 && code != 405 && code != 416) {
2174 new_object->cache_control |= (CACHE_NO_HIDDEN | OBJECT_LINEAR);
2175 } else if(code != 200 && code != 206 &&
2176 code != 300 && code != 302 && code != 304 &&
2177 code != 410) {
2178 if(new_object->expires < 0 && !(cache_control.flags & CACHE_PUBLIC)) {
2179 new_object->cache_control |= CACHE_NO_HIDDEN;
2181 } else if(dontCacheRedirects && (code == 301 || code == 302)) {
2182 new_object->cache_control |= CACHE_NO_HIDDEN;
2185 if(urlIsUncachable(new_object->key, new_object->key_size)) {
2186 new_object->cache_control |= CACHE_NO_HIDDEN;
2189 if(!via)
2190 new_via = internAtomF("%s %s",
2191 version == HTTP_11 ? "1.1" : "1.0",
2192 proxyName->string);
2193 else
2194 new_via = internAtomF("%s, %s %s", via->string,
2195 version == HTTP_11 ? "1.1" : "1.0",
2196 proxyName->string);
2197 if(new_via == NULL) {
2198 do_log(L_ERROR, "Couldn't allocate Via.\n");
2199 } else {
2200 if(new_object->via) releaseAtom(new_object->via);
2201 new_object->via = new_via;
2204 if((new_object->cache_control & CACHE_NO_STORE) ||
2205 ((new_object->cache_control & CACHE_VARY) && !new_object->etag))
2206 new_object->cache_control |= CACHE_NO_HIDDEN;
2208 if(new_object->flags & OBJECT_INITIAL) {
2209 objectPartial(new_object, full_len, headers);
2210 } else {
2211 if(new_object->length < 0)
2212 new_object->length = full_len;
2213 /* XXX -- RFC 2616 13.5.3 */
2214 releaseAtom(headers);
2217 if(supersede) {
2218 assert(new_object != old_object);
2219 supersedeObject(old_object);
2222 if(new_object != old_object) {
2223 if(new_object->flags & OBJECT_INPROGRESS) {
2224 /* Make sure we don't fetch this object two times at the
2225 same time. Just drop the connection. */
2226 releaseObject(new_object);
2227 httpServerFinish(connection, 1, 0);
2228 return 1;
2230 old_object->flags &= ~OBJECT_VALIDATING;
2231 new_object->flags |= OBJECT_INPROGRESS;
2232 /* Signal the client side to switch to the new object -- see
2233 httpClientGetHandler. If it doesn't, we'll give up on this
2234 request below. */
2235 new_object->flags |= OBJECT_MUTATING;
2236 request->can_mutate = new_object;
2237 notifyObject(old_object);
2238 request->can_mutate = NULL;
2239 new_object->flags &= ~OBJECT_MUTATING;
2240 old_object->flags &= ~OBJECT_INPROGRESS;
2241 if(request->object == old_object) {
2242 if(request->request)
2243 request->request->request = NULL;
2244 request->request = NULL;
2245 request->object = new_object;
2246 } else {
2247 assert(request->object == new_object);
2249 releaseNotifyObject(old_object);
2250 old_object = NULL;
2251 object = new_object;
2252 } else {
2253 objectMetadataChanged(new_object, 0);
2256 if(object->flags & OBJECT_VALIDATING) {
2257 object->flags &= ~OBJECT_VALIDATING;
2258 notifyObject(object);
2261 if(!expect_body) {
2262 httpServerFinish(connection, 0, rc);
2263 return 1;
2266 if(request->request == NULL) {
2267 httpServerFinish(connection, 1, 0);
2268 return 1;
2271 if(code == 412) {
2272 /* 412 replies contain a useless body. For now, we
2273 drop the connection. */
2274 httpServerFinish(connection, 1, 0);
2275 return 1;
2279 if(request->flags & REQUEST_PERSISTENT) {
2280 if(request->method != METHOD_HEAD &&
2281 connection->te == TE_IDENTITY && len < 0) {
2282 do_log(L_ERROR, "Persistent reply with no Content-Length\n");
2283 /* That's potentially dangerous, as we could start reading
2284 arbitrary data into the object. Unfortunately, some
2285 servers do that. */
2286 request->flags &= ~REQUEST_PERSISTENT;
2290 /* we're getting a body */
2291 if(content_range.from > 0)
2292 connection->offset = content_range.from;
2293 else
2294 connection->offset = 0;
2296 if(content_range.to >= 0)
2297 request->to = content_range.to;
2299 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2300 (unsigned long)connection, (unsigned long)object,
2301 connection->offset);
2303 if(connection->len > rc) {
2304 rc = connectionAddData(connection, rc);
2305 if(rc) {
2306 if(rc < 0) {
2307 if(rc == -2) {
2308 do_log(L_ERROR, "Couldn't parse chunk size.\n");
2309 httpServerAbort(connection, 1, 502,
2310 internAtom("Couldn't parse chunk size"));
2311 } else {
2312 do_log(L_ERROR, "Couldn't add data to connection.\n");
2313 httpServerAbort(connection, 1, 500,
2314 internAtom("Couldn't add data "
2315 "to connection"));
2317 return 1;
2318 } else {
2319 if(code != 206) {
2320 if(object->length < 0) {
2321 object->length = object->size;
2322 objectMetadataChanged(object, 0);
2323 } else if(object->length != object->size) {
2324 httpServerAbort(connection, 1, 500,
2325 internAtom("Inconsistent "
2326 "object size"));
2327 object->length = -1;
2328 return 1;
2331 httpServerFinish(connection, 0, 0);
2332 return 1;
2335 } else {
2336 connection->len = 0;
2339 if(eof) {
2340 if(connection->te == TE_CHUNKED ||
2341 (object->length >= 0 &&
2342 connection->offset < object->length)) {
2343 do_log(L_ERROR, "Server closed connection.\n");
2344 httpServerAbort(connection, 1, 502,
2345 internAtom("Server closed connection"));
2346 return 1;
2347 } else {
2348 if(code != 206 && eof > 0 && object->length < 0) {
2349 object->length = object->size;
2350 objectMetadataChanged(object, 0);
2352 httpServerFinish(connection, 1, 0);
2353 return 1;
2355 } else {
2356 return httpServerReadData(connection, 1);
2358 return 0;
2360 fail:
2361 releaseAtom(url);
2362 releaseAtom(message);
2363 if(headers)
2364 releaseAtom(headers);
2365 if(etag)
2366 free(etag);
2367 if(via)
2368 releaseAtom(via);
2369 return 1;
2373 httpServerIndirectHandlerCommon(HTTPConnectionPtr connection, int eof)
2375 HTTPRequestPtr request = connection->request;
2377 assert(eof >= 0);
2378 assert(request->object->flags & OBJECT_INPROGRESS);
2380 if(connection->len > 0) {
2381 int rc;
2382 rc = connectionAddData(connection, 0);
2383 if(rc) {
2384 if(rc < 0) {
2385 if(rc == -2) {
2386 do_log(L_ERROR, "Couldn't parse chunk size.\n");
2387 httpServerAbort(connection, 1, 502,
2388 internAtom("Couldn't parse chunk size"));
2389 } else {
2390 do_log(L_ERROR, "Couldn't add data to connection.\n");
2391 httpServerAbort(connection, 1, 500,
2392 internAtom("Couldn't add data "
2393 "to connection"));
2395 return 1;
2396 } else {
2397 if(request->to < 0) {
2398 if(request->object->length < 0) {
2399 request->object->length = request->object->size;
2400 objectMetadataChanged(request->object, 0);
2401 } else if(request->object->length !=
2402 request->object->size) {
2403 request->object->length = -1;
2404 httpServerAbort(connection, 1, 502,
2405 internAtom("Inconsistent "
2406 "object size"));
2407 return 1;
2410 httpServerFinish(connection, 0, 0);
2412 return 1;
2416 if(eof && connection->len == 0) {
2417 if(connection->te == TE_CHUNKED ||
2418 (request->to >= 0 && connection->offset < request->to)) {
2419 do_log(L_ERROR, "Server dropped connection.\n");
2420 httpServerAbort(connection, 1, 502,
2421 internAtom("Server dropped connection"));
2422 return 1;
2423 } else {
2424 if(request->object->length < 0 && eof > 0 &&
2425 (request->to < 0 || request->to > request->object->size)) {
2426 request->object->length = request->object->size;
2427 objectMetadataChanged(request->object, 0);
2429 httpServerFinish(connection, 1, 0);
2430 return 1;
2432 } else {
2433 return httpServerReadData(connection, 0);
2438 httpServerIndirectHandler(int status,
2439 FdEventHandlerPtr event,
2440 StreamRequestPtr srequest)
2442 HTTPConnectionPtr connection = srequest->data;
2443 assert(connection->request->object->flags & OBJECT_INPROGRESS);
2445 httpSetTimeout(connection, -1);
2446 if(status < 0) {
2447 if(status != -ECLIENTRESET)
2448 do_log_error(L_ERROR, -status, "Read from server failed");
2449 httpServerAbort(connection, status != -ECLIENTRESET, 502,
2450 internAtomError(-status, "Read from server failed"));
2451 return 1;
2454 connection->len = srequest->offset;
2456 return httpServerIndirectHandlerCommon(connection, status);
2460 httpServerReadData(HTTPConnectionPtr connection, int immediate)
2462 HTTPRequestPtr request = connection->request;
2463 ObjectPtr object = request->object;
2464 int to = -1;
2466 assert(object->flags & OBJECT_INPROGRESS);
2468 if(request->request == NULL) {
2469 httpServerFinish(connection, 1, 0);
2470 return 1;
2473 if(request->to >= 0)
2474 to = request->to;
2475 else
2476 to = object->length;
2478 if(to >= 0 && to == connection->offset) {
2479 httpServerFinish(connection, 0, 0);
2480 return 1;
2483 if(connection->len == 0 &&
2484 ((connection->te == TE_IDENTITY && to > connection->offset) ||
2485 (connection->te == TE_CHUNKED && connection->chunk_remaining > 0))) {
2486 /* Read directly into the object */
2487 int i = connection->offset / CHUNK_SIZE;
2488 int j = connection->offset % CHUNK_SIZE;
2489 int end, len, more;
2490 /* See httpServerDirectHandlerCommon if you change this */
2491 if(connection->te == TE_CHUNKED) {
2492 len = connection->chunk_remaining;
2493 /* The logic here is that we want more to just fit the
2494 chunk header if we're doing a large read, but do a
2495 large read if we would otherwise do a small one. The
2496 magic constant 2000 comes from the assumption that the
2497 server uses chunks that have a size that are a power of
2498 two (possibly including the chunk header), and that we
2499 want a full ethernet packet to fit into our read. */
2500 more = (len >= 2000 ? 20 : MIN(2048 - len, CHUNK_SIZE));
2501 } else {
2502 len = to - connection->offset;
2503 /* We read more data only when there is a reasonable
2504 chance of there being another reply coming. */
2505 more = (connection->pipelined > 1) ? CHUNK_SIZE : 0;
2507 end = len + connection->offset;
2509 httpConnectionDestroyBuf(connection);
2511 /* The order of allocation is important in case we run out of
2512 memory. */
2513 lockChunk(object, i);
2514 if(object->chunks[i].data == NULL)
2515 object->chunks[i].data = get_chunk();
2516 if(object->chunks[i].data && object->chunks[i].size >= j) {
2517 if(len + j > CHUNK_SIZE) {
2518 lockChunk(object, i + 1);
2519 if(object->chunks[i + 1].data == NULL)
2520 object->chunks[i + 1].data = get_chunk();
2521 /* Unless we're grabbing all len of data, we do not
2522 want to do an indirect read immediately afterwards. */
2523 if(more && len + j <= 2 * CHUNK_SIZE) {
2524 if(!connection->buf)
2525 connection->buf = get_chunk(); /* checked below */
2527 if(object->chunks[i + 1].data) {
2528 do_stream_3(IO_READ | IO_NOTNOW, connection->fd, j,
2529 object->chunks[i].data, CHUNK_SIZE,
2530 object->chunks[i + 1].data,
2531 MIN(CHUNK_SIZE,
2532 end - (i + 1) * CHUNK_SIZE),
2533 connection->buf, connection->buf ? more : 0,
2534 httpServerDirectHandler2, connection);
2535 return 1;
2537 unlockChunk(object, i + 1);
2539 if(more && len + j <= CHUNK_SIZE) {
2540 if(!connection->buf)
2541 connection->buf = get_chunk();
2543 do_stream_2(IO_READ | IO_NOTNOW, connection->fd, j,
2544 object->chunks[i].data,
2545 MIN(CHUNK_SIZE, end - i * CHUNK_SIZE),
2546 connection->buf, connection->buf ? more : 0,
2547 httpServerDirectHandler, connection);
2548 return 1;
2549 } else {
2550 unlockChunk(object, i);
2554 if(connection->len == 0)
2555 httpConnectionDestroyBuf(connection);
2557 httpSetTimeout(connection, serverTimeout);
2558 do_stream_buf(IO_READ | IO_NOTNOW |
2559 ((immediate && connection->len) ? IO_IMMEDIATE : 0),
2560 connection->fd, connection->len,
2561 &connection->buf,
2562 (connection->te == TE_CHUNKED ?
2563 MIN(2048, CHUNK_SIZE) : CHUNK_SIZE),
2564 httpServerIndirectHandler, connection);
2565 return 1;
2569 httpServerDirectHandlerCommon(int kind, int status,
2570 FdEventHandlerPtr event,
2571 StreamRequestPtr srequest)
2573 HTTPConnectionPtr connection = srequest->data;
2574 HTTPRequestPtr request = connection->request;
2575 ObjectPtr object = request->object;
2576 int i = connection->offset / CHUNK_SIZE;
2577 int to, end, end1;
2579 assert(request->object->flags & OBJECT_INPROGRESS);
2581 httpSetTimeout(connection, -1);
2583 if(status < 0) {
2584 unlockChunk(object, i);
2585 if(kind == 2) unlockChunk(object, i + 1);
2586 if(status != -ECLIENTRESET)
2587 do_log_error(L_ERROR, -status, "Read from server failed");
2588 httpServerAbort(connection, status != -ECLIENTRESET, 502,
2589 internAtomError(-status, "Read from server failed"));
2590 return 1;
2593 /* We have incestuous knowledge of the decisions made in
2594 httpServerReadData */
2595 if(request->to >= 0)
2596 to = request->to;
2597 else
2598 to = object->length;
2599 if(connection->te == TE_CHUNKED)
2600 end = connection->offset + connection->chunk_remaining;
2601 else
2602 end = to;
2603 /* The amount of data actually read into the object */
2604 end1 = MIN(end, i * CHUNK_SIZE + MIN(kind * CHUNK_SIZE, srequest->offset));
2606 assert(end >= 0 && end1 >= i * CHUNK_SIZE && end1 <= (i + 2) * CHUNK_SIZE);
2608 object->chunks[i].size =
2609 MAX(object->chunks[i].size, MIN(end1 - i * CHUNK_SIZE, CHUNK_SIZE));
2610 if(kind == 2 && end1 > (i + 1) * CHUNK_SIZE) {
2611 object->chunks[i + 1].size =
2612 MAX(object->chunks[i + 1].size, end1 - (i + 1) * CHUNK_SIZE);
2614 if(connection->te == TE_CHUNKED) {
2615 connection->chunk_remaining -= (end1 - connection->offset);
2616 assert(connection->chunk_remaining >= 0);
2618 connection->offset = end1;
2619 object->size = MAX(object->size, end1);
2620 unlockChunk(object, i);
2621 if(kind == 2) unlockChunk(object, i + 1);
2623 if(i * CHUNK_SIZE + srequest->offset > end1) {
2624 connection->len = i * CHUNK_SIZE + srequest->offset - end1;
2625 return httpServerIndirectHandlerCommon(connection, status);
2626 } else {
2627 notifyObject(object);
2628 if(status) {
2629 httpServerFinish(connection, 1, 0);
2630 return 1;
2631 } else {
2632 return httpServerReadData(connection, 0);
2638 httpServerDirectHandler(int status,
2639 FdEventHandlerPtr event,
2640 StreamRequestPtr srequest)
2642 return httpServerDirectHandlerCommon(1, status, event, srequest);
2646 httpServerDirectHandler2(int status,
2647 FdEventHandlerPtr event,
2648 StreamRequestPtr srequest)
2650 return httpServerDirectHandlerCommon(2, status, event, srequest);
2653 /* Add the data accumulated in connection->buf into the object in
2654 connection->request. Returns 0 in the normal case, 1 if the TE is
2655 self-terminating and we're done, -1 if there was a problem with
2656 objectAddData, -2 if there was a problem with the data. */
2658 connectionAddData(HTTPConnectionPtr connection, int skip)
2660 HTTPRequestPtr request = connection->request;
2661 ObjectPtr object = request->object;
2662 int rc;
2664 if(connection->te == TE_IDENTITY) {
2665 int len;
2667 len = connection->len - skip;
2668 if(object->length >= 0) {
2669 len = MIN(object->length - connection->offset, len);
2671 if(request->to >= 0)
2672 len = MIN(request->to - connection->offset, len);
2673 if(len > 0) {
2674 rc = objectAddData(object, connection->buf + skip,
2675 connection->offset, len);
2676 if(rc < 0)
2677 return -1;
2678 connection->offset += len;
2679 connection->len -= (len + skip);
2680 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2681 (unsigned long)connection, (unsigned long)object,
2682 connection->offset);
2685 if(connection->len > 0 && skip + len > 0) {
2686 memmove(connection->buf,
2687 connection->buf + skip + len, connection->len);
2690 if((object->length >= 0 && object->length <= connection->offset) ||
2691 (request->to >= 0 && request->to <= connection->offset)) {
2692 notifyObject(object);
2693 return 1;
2694 } else {
2695 if(len > 0)
2696 notifyObject(object);
2697 return 0;
2699 } else if(connection->te == TE_CHUNKED) {
2700 int i = skip, j, size;
2701 /* connection->chunk_remaining is 0 at the end of a chunk, -1
2702 after the CR/LF pair ending a chunk, and -2 after we've
2703 seen a chunk of length 0. */
2704 if(connection->chunk_remaining > -2) {
2705 while(1) {
2706 if(connection->chunk_remaining <= 0) {
2707 if(connection->chunk_remaining == 0) {
2708 if(connection->len < i + 2)
2709 break;
2710 if(connection->buf[i] != '\r' ||
2711 connection->buf[i + 1] != '\n')
2712 return -1;
2713 i += 2;
2714 connection->chunk_remaining = -1;
2716 if(connection->len < i + 2)
2717 break;
2718 j = parseChunkSize(connection->buf, i,
2719 connection->len, &size);
2720 if(j < 0)
2721 return -2;
2722 if(j == 0)
2723 break;
2724 else
2725 i = j;
2726 if(size == 0) {
2727 connection->chunk_remaining = -2;
2728 break;
2729 } else {
2730 connection->chunk_remaining = size;
2732 } else {
2733 /* connection->chunk_remaining > 0 */
2734 size = MIN(connection->chunk_remaining,
2735 connection->len - i);
2736 if(size <= 0)
2737 break;
2738 rc = objectAddData(object, connection->buf + i,
2739 connection->offset, size);
2740 connection->offset += size;
2741 if(rc < 0)
2742 return -1;
2743 i += size;
2744 connection->chunk_remaining -= size;
2745 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2746 (unsigned long)connection,
2747 (unsigned long)object,
2748 connection->offset);
2752 connection->len -= i;
2753 if(connection->len > 0)
2754 memmove(connection->buf, connection->buf + i, connection->len);
2755 if(i > 0 || connection->chunk_remaining == -2)
2756 notifyObject(object);
2757 if(connection->chunk_remaining == -2)
2758 return 1;
2759 else
2760 return 0;
2761 } else {
2762 abort();
2766 void
2767 listServers(FILE *out)
2769 HTTPServerPtr server;
2770 int i, n, m, entry;
2772 fprintf(out, "<!DOCTYPE HTML PUBLIC "
2773 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2774 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2775 "<html><head>\n"
2776 "\r\n<title>Known servers</title>\n"
2777 "</head><body>\n"
2778 "<h1>Known servers</h1>\n");
2780 alternatingHttpStyle(out, "servers");
2781 fprintf(out, "<table id=servers>\n");
2782 fprintf(out, "<thead><tr><th>Server</th>"
2783 "<th>Version</th>"
2784 "<th>Persistent</th>"
2785 "<th>Pipeline</th>"
2786 "<th>Connections</th>"
2787 "<th></th>"
2788 "<th>rtt</th>"
2789 "<th>rate</th>"
2790 "</tr></thead>\n");
2791 fprintf(out, "<tbody>\n");
2792 server = servers;
2793 entry = 0;
2794 while(server) {
2795 fprintf(out, "<tr class=\"%s\">", entry % 2 == 0 ? "even" : "odd");
2796 if(server->port == 80)
2797 fprintf(out, "<td>%s</td>", server->name);
2798 else
2799 fprintf(out, "<td>%s:%d</td>", server->name, server->port);
2801 if(server->version == HTTP_11)
2802 fprintf(out, "<td>1.1</td>");
2803 else if(server->version == HTTP_10)
2804 fprintf(out, "<td>1.0</td>");
2805 else
2806 fprintf(out, "<td>unknown</td>");
2808 if(server->persistent < 0)
2809 fprintf(out, "<td>no</td>");
2810 else if(server->persistent > 0)
2811 fprintf(out, "<td>yes</td>");
2812 else
2813 fprintf(out, "<td>unknown</td>");
2815 if(server->version != HTTP_11 || server->persistent <= 0)
2816 fprintf(out, "<td></td>");
2817 else if(server->pipeline < 0)
2818 fprintf(out, "<td>no</td>");
2819 else if(server->pipeline >= 0 && server->pipeline <= 1)
2820 fprintf(out, "<td>unknown</td>");
2821 else if(server->pipeline == 2 || server->pipeline == 3)
2822 fprintf(out, "<td>probing</td>");
2823 else
2824 fprintf(out, "<td>yes</td>");
2826 n = 0; m = 0;
2827 for(i = 0; i < server->maxslots; i++)
2828 if(server->connection[i] && !server->connection[i]->connecting) {
2829 if(i < server->numslots)
2830 n++;
2831 else
2832 m++;
2835 fprintf(out, "<td>%d/%d", n, server->numslots);
2836 if(m)
2837 fprintf(out, " + %d</td>", m);
2838 else
2839 fprintf(out, "</td>");
2841 if(server->lies > 0)
2842 fprintf(out, "<td>(%d lies)</td>", (server->lies + 9) / 10);
2843 else
2844 fprintf(out, "<td></td>");
2846 if(server->rtt > 0)
2847 fprintf(out, "<td>%.3f</td>", (double)server->rtt / 1000000.0);
2848 else
2849 fprintf(out, "<td></td>");
2850 if(server->rate > 0)
2851 fprintf(out, "<td>%d</td>", server->rate);
2852 else
2853 fprintf(out, "<td></td>");
2855 fprintf(out, "</tr>\n");
2856 server = server->next;
2857 entry++;
2859 fprintf(out, "</tbody>\n");
2860 fprintf(out, "</table>\n");
2861 fprintf(out, "<p><a href=\"/polipo/\">back</a></p>");
2862 fprintf(out, "</body></html>\n");