Don't use all of physical memory -- just one quarter. Oops.
[polipo.git] / server.c
blob2f12d47e533337171a34e5929261bf6fdd75efca
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 serverSlots1 = 4;
37 int serverMaxSlots = 8;
38 int dontCacheRedirects = 0;
39 int maxSideBuffering = 1500;
40 int maxConnectionAge = 1260;
41 int maxConnectionRequests = 400;
43 static HTTPServerPtr servers = 0;
45 static int httpServerContinueConditionHandler(int, ConditionHandlerPtr);
46 static int initParentProxy(void);
47 static int parentProxySetter(ConfigVariablePtr var, void *value);
48 static void httpServerDelayedFinish(HTTPConnectionPtr);
49 static int allowUnalignedRangeRequests = 0;
51 void
52 preinitServer(void)
54 CONFIG_VARIABLE_SETTABLE(parentProxy, CONFIG_ATOM_LOWER, parentProxySetter,
55 "Parent proxy (host:port).");
56 CONFIG_VARIABLE(serverExpireTime, CONFIG_TIME,
57 "Time during which server data is valid.");
58 CONFIG_VARIABLE_SETTABLE(smallRequestTime, CONFIG_TIME, configIntSetter,
59 "Estimated time for a small request.");
60 CONFIG_VARIABLE_SETTABLE(replyUnpipelineTime, CONFIG_TIME, configIntSetter,
61 "Estimated time for a pipeline break.");
62 CONFIG_VARIABLE_SETTABLE(replyUnpipelineSize, CONFIG_INT, configIntSetter,
63 "Size for a pipeline break.");
64 CONFIG_VARIABLE_SETTABLE(pipelineAdditionalRequests, CONFIG_TRISTATE,
65 configIntSetter,
66 "Pipeline requests on an active connection.");
67 CONFIG_VARIABLE_SETTABLE(maxPipelineTrain, CONFIG_INT,
68 configIntSetter,
69 "Maximum number of requests "
70 "pipelined at a time.");
71 CONFIG_VARIABLE(pmmFirstSize, CONFIG_INT,
72 "The size of the first PMM chunk.");
73 CONFIG_VARIABLE(pmmSize, CONFIG_INT,
74 "The size of a PMM chunk.");
75 CONFIG_VARIABLE(serverSlots, CONFIG_INT,
76 "Maximum number of connections per server.");
77 CONFIG_VARIABLE(serverSlots1, CONFIG_INT,
78 "Maximum number of connections per HTTP/1.0 server.");
79 CONFIG_VARIABLE(serverMaxSlots, CONFIG_INT,
80 "Maximum number of connections per broken server.");
81 CONFIG_VARIABLE(dontCacheRedirects, CONFIG_BOOLEAN,
82 "If true, don't cache redirects.");
83 CONFIG_VARIABLE_SETTABLE(allowUnalignedRangeRequests,
84 CONFIG_BOOLEAN, configIntSetter,
85 "Allow unaligned range requests (unreliable).");
86 CONFIG_VARIABLE_SETTABLE(maxSideBuffering,
87 CONFIG_INT, configIntSetter,
88 "Maximum buffering for PUT and POST requests.");
89 CONFIG_VARIABLE_SETTABLE(maxConnectionAge,
90 CONFIG_TIME, configIntSetter,
91 "Maximum age of a server-side connection.");
92 CONFIG_VARIABLE_SETTABLE(maxConnectionRequests,
93 CONFIG_INT, configIntSetter,
94 "Maximum number of requests on a server-side connection.");
97 static int
98 parentProxySetter(ConfigVariablePtr var, void *value)
100 configAtomSetter(var, value);
101 initParentProxy();
102 return 1;
105 static void
106 discardServer(HTTPServerPtr server)
108 HTTPServerPtr previous;
109 assert(!server->request);
111 if(server == servers)
112 servers = server->next;
113 else {
114 previous = servers;
115 while(previous->next != server)
116 previous = previous->next;
117 previous->next = server->next;
120 free(server);
123 static int
124 httpServerIdle(HTTPServerPtr server)
126 int i;
127 if(server->request)
128 return 0;
129 for(i = 0; i < server->maxslots; i++)
130 if(server->connection[i])
131 return 0;
132 return 1;
135 static int
136 expireServersHandler(TimeEventHandlerPtr event)
138 HTTPServerPtr server, next;
139 TimeEventHandlerPtr e;
140 server = servers;
141 while(server) {
142 next = server->next;
143 if(httpServerIdle(server) &&
144 server->time + serverExpireTime < current_time.tv_sec)
145 discardServer(server);
146 server = next;
148 e = scheduleTimeEvent(serverExpireTime / 60 + 60,
149 expireServersHandler, 0, NULL);
150 if(!e) {
151 do_log(L_ERROR, "Couldn't schedule server expiry.\n");
152 polipoExit();
154 return 1;
157 static int
158 roundSize(int size)
160 return (size + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE;
163 static int
164 initParentProxy()
166 AtomPtr host, port_atom;
167 int rc, port;
169 if(parentHost) {
170 releaseAtom(parentHost);
171 parentHost = NULL;
173 if(parentPort >= 0)
174 parentPort = -1;
176 if(parentProxy != NULL && parentProxy->length == 0) {
177 releaseAtom(parentProxy);
178 parentProxy = NULL;
181 if(parentProxy == NULL)
182 return 1;
184 rc = atomSplit(parentProxy, ':', &host, &port_atom);
185 if(rc <= 0) {
186 do_log(L_ERROR, "Couldn't parse parentProxy.");
187 releaseAtom(parentProxy);
188 parentProxy = NULL;
189 return -1;
192 port = atoi(port_atom->string);
193 if(port <= 0 || port >= 0x10000) {
194 releaseAtom(host);
195 releaseAtom(port_atom);
196 do_log(L_ERROR, "Couldn't parse parentProxy.");
197 releaseAtom(parentProxy);
198 parentProxy = NULL;
199 return -1;
202 parentHost = host;
203 parentPort = port;
204 return 1;
207 void
208 initServer(void)
210 TimeEventHandlerPtr event;
211 servers = NULL;
213 if(pmmFirstSize || pmmSize) {
214 if(pmmSize == 0) pmmSize = pmmFirstSize;
215 if(pmmFirstSize == 0) pmmFirstSize = pmmSize;
216 pmmSize = roundSize(pmmSize);
217 pmmFirstSize = roundSize(pmmFirstSize);
220 if(serverMaxSlots < 1)
221 serverMaxSlots = 1;
222 if(serverSlots < 1)
223 serverSlots = 1;
224 if(serverSlots > serverMaxSlots)
225 serverSlots = serverMaxSlots;
226 if(serverSlots1 < serverSlots)
227 serverSlots1 = serverSlots;
228 if(serverSlots1 > serverMaxSlots)
229 serverSlots1 = serverMaxSlots;
231 initParentProxy();
233 event = scheduleTimeEvent(serverExpireTime / 60 + 60, expireServersHandler,
234 0, NULL);
235 if(event == NULL) {
236 do_log(L_ERROR, "Couldn't schedule server expiry.\n");
237 exit(1);
241 static HTTPServerPtr
242 getServer(char *name, int port, int proxy)
244 HTTPServerPtr server;
245 int i;
247 server = servers;
248 while(server) {
249 if(strcmp(server->name, name) == 0 && server->port == port &&
250 server->isProxy == proxy) {
251 if(httpServerIdle(server) &&
252 server->time + serverExpireTime < current_time.tv_sec) {
253 discardServer(server);
254 server = NULL;
255 break;
256 } else {
257 server->time = current_time.tv_sec;
258 return server;
261 server = server->next;
264 server = malloc(sizeof(HTTPServerRec));
265 if(server == NULL) {
266 do_log(L_ERROR, "Couldn't allocate server.\n");
267 return NULL;
270 server->connection = malloc(serverMaxSlots * sizeof(HTTPConnectionPtr));
271 if(server->connection == NULL) {
272 do_log(L_ERROR, "Couldn't allocate server.\n");
273 free(server);
274 return NULL;
277 server->idleHandler = malloc(serverMaxSlots * sizeof(FdEventHandlerPtr));
278 if(server->connection == NULL) {
279 do_log(L_ERROR, "Couldn't allocate server.\n");
280 free(server->connection);
281 free(server);
282 return NULL;
285 server->maxslots = serverMaxSlots;
287 server->name = strdup(name);
288 if(server->name == NULL) {
289 do_log(L_ERROR, "Couldn't allocate server name.\n");
290 free(server);
291 return NULL;
294 server->port = port;
295 server->addrindex = 0;
296 server->isProxy = proxy;
297 server->version = HTTP_UNKNOWN;
298 server->persistent = 0;
299 server->pipeline = 0;
300 server->time = current_time.tv_sec;
301 server->rtt = -1;
302 server->rate = -1;
303 server->numslots = MIN(serverSlots, server->maxslots);
304 for(i = 0; i < server->maxslots; i++) {
305 server->connection[i] = NULL;
306 server->idleHandler[i] = NULL;
308 server->request = NULL;
309 server->request_last = NULL;
310 server->lies = 0;
312 server->next = servers;
313 servers = server;
314 return server;
318 httpServerQueueRequest(HTTPServerPtr server, HTTPRequestPtr request)
320 assert(request->request && request->request->request == request);
321 assert(request->connection == NULL);
322 if(server->request) {
323 server->request_last->next = request;
324 server->request_last = request;
325 } else {
326 server->request_last = request;
327 server->request = request;
329 return 1;
332 void
333 httpServerAbort(HTTPConnectionPtr connection, int fail,
334 int code, AtomPtr message)
336 HTTPRequestPtr request = connection->request;
337 if(request) {
338 if(request->request) {
339 httpClientError(request->request, code, retainAtom(message));
341 if(fail) {
342 request->object->flags |= OBJECT_FAILED;
343 if(request->object->flags & OBJECT_INITIAL)
344 abortObject(request->object, code, retainAtom(message));
345 notifyObject(request->object);
348 releaseAtom(message);
349 if(!connection->connecting)
350 httpServerFinish(connection, 1, 0);
353 void
354 httpServerAbortRequest(HTTPRequestPtr request, int fail,
355 int code, AtomPtr message)
357 if(request->connection && request == request->connection->request) {
358 httpServerAbort(request->connection, fail, code, message);
359 } else {
360 HTTPRequestPtr requestor = request->request;
361 if(requestor) {
362 requestor->request = NULL;
363 request->request = NULL;
364 httpClientError(requestor, code, retainAtom(message));
366 if(fail) {
367 request->object->flags |= OBJECT_FAILED;
368 if(request->object->flags & OBJECT_INITIAL)
369 abortObject(request->object, code, retainAtom(message));
370 notifyObject(request->object);
372 releaseAtom(message);
376 void
377 httpServerClientReset(HTTPRequestPtr request)
379 if(request->connection &&
380 request->connection->fd >= 0 &&
381 !request->connection->connecting &&
382 request->connection->request == request)
383 pokeFdEvent(request->connection->fd, -ECLIENTRESET, POLLIN | POLLOUT);
388 httpMakeServerRequest(char *name, int port, ObjectPtr object,
389 int method, int from, int to, HTTPRequestPtr requestor)
391 HTTPServerPtr server;
392 HTTPRequestPtr request;
393 int rc;
395 assert(!(object->flags & OBJECT_INPROGRESS));
397 if(parentHost) {
398 server = getServer(parentHost->string, parentPort, 1);
399 } else {
400 server = getServer(name, port, 0);
402 if(server == NULL) return -1;
404 object->flags |= OBJECT_INPROGRESS;
405 object->requestor = requestor;
407 request = httpMakeRequest();
408 if(!request) {
409 do_log(L_ERROR, "Couldn't allocate request.\n");
410 return -1;
413 /* Because we allocate objects in chunks, we cannot have data that
414 doesn't start at a chunk boundary. */
415 if(from % CHUNK_SIZE != 0) {
416 if(allowUnalignedRangeRequests) {
417 objectFillFromDisk(object, from / CHUNK_SIZE * CHUNK_SIZE, 1);
418 if(objectHoleSize(object, from - 1) != 0)
419 from = from / CHUNK_SIZE * CHUNK_SIZE;
420 } else {
421 from = from / CHUNK_SIZE * CHUNK_SIZE;
425 request->object = retainObject(object);
426 request->method = method;
427 if(method == METHOD_CONDITIONAL_GET) {
428 if(server->lies > 0)
429 request->method = METHOD_HEAD;
431 request->flags =
432 REQUEST_PERSISTENT |
433 (expectContinue ? (requestor->flags & REQUEST_WAIT_CONTINUE) : 0);
434 request->from = from;
435 request->to = to;
436 request->request = requestor;
437 requestor->request = request;
438 request->cache_control = requestor->cache_control;
439 request->time0 = null_time;
440 request->time1 = null_time;
442 rc = httpServerQueueRequest(server, request);
443 if(rc < 0) {
444 do_log(L_ERROR, "Couldn't queue request.\n");
445 request->request = NULL;
446 requestor->request = NULL;
447 object->flags &= ~(OBJECT_INPROGRESS | OBJECT_VALIDATING);
448 releaseNotifyObject(object);
449 httpDestroyRequest(request);
450 return 1;
453 if(request->flags & REQUEST_WAIT_CONTINUE) {
454 if(server->version == HTTP_10) {
455 httpServerAbortRequest(request, 1,
456 417, internAtom("Expectation failed"));
457 return 1;
459 } else if(expectContinue >= 2 && server->version == HTTP_11) {
460 if(request->method == METHOD_POST || request->method == METHOD_PUT)
461 request->flags |= REQUEST_WAIT_CONTINUE;
464 again:
465 rc = httpServerTrigger(server);
466 if(rc < 0) {
467 /* We must be very short on memory. If there are any requests
468 queued, we abort one and try again. If there aren't, we
469 give up. */
470 do_log(L_ERROR, "Couldn't trigger server -- out of memory?\n");
471 if(server->request) {
472 httpServerAbortRequest(server->request, 1, 503,
473 internAtom("Couldn't trigger server"));
474 goto again;
477 return 1;
481 httpServerConnection(HTTPServerPtr server)
483 HTTPConnectionPtr connection;
484 int i;
486 connection = httpMakeConnection();
487 if(connection == NULL) {
488 do_log(L_ERROR, "Couldn't allocate server connection.\n");
489 return -1;
491 connection->server = server;
493 for(i = 0; i < server->numslots; i++) {
494 if(!server->connection[i]) {
495 server->connection[i] = connection;
496 break;
499 assert(i < server->numslots);
501 connection->request = NULL;
502 connection->request_last = NULL;
504 do_log(D_SERVER_CONN, "C... %s:%d.\n",
505 connection->server->name, connection->server->port);
506 httpSetTimeout(connection, serverTimeout);
507 if(socksParentProxy) {
508 connection->connecting = CONNECTING_SOCKS;
509 do_socks_connect(server->name, connection->server->port,
510 httpServerSocksHandler, connection);
511 } else {
512 connection->connecting = CONNECTING_DNS;
513 do_gethostbyname(server->name, 0,
514 httpServerConnectionDnsHandler,
515 connection);
517 return 1;
521 httpServerConnectionDnsHandler(int status, GethostbynameRequestPtr request)
523 HTTPConnectionPtr connection = request->data;
525 httpSetTimeout(connection, -1);
527 if(status <= 0) {
528 AtomPtr message;
529 message = internAtomF("Host %s lookup failed: %s",
530 request->name ?
531 request->name->string : "(unknown)",
532 request->error_message ?
533 request->error_message->string :
534 pstrerror(-status));
535 do_log(L_ERROR, "Host %s lookup failed: %s (%d).\n",
536 request->name ?
537 request->name->string : "(unknown)",
538 request->error_message ?
539 request->error_message->string :
540 pstrerror(-status), -status);
541 connection->connecting = 0;
542 if(connection->server->request)
543 httpServerAbortRequest(connection->server->request, 1, 504,
544 retainAtom(message));
545 httpServerAbort(connection, 1, 502, message);
546 return 1;
549 if(request->addr->string[0] == DNS_CNAME) {
550 if(request->count > 10) {
551 AtomPtr message = internAtom("DNS CNAME loop");
552 do_log(L_ERROR, "DNS CNAME loop.\n");
553 connection->connecting = 0;
554 if(connection->server->request)
555 httpServerAbortRequest(connection->server->request, 1, 504,
556 retainAtom(message));
557 httpServerAbort(connection, 1, 504, message);
558 return 1;
561 httpSetTimeout(connection, serverTimeout);
562 do_gethostbyname(request->addr->string + 1, request->count + 1,
563 httpServerConnectionDnsHandler,
564 connection);
565 return 1;
568 connection->connecting = CONNECTING_CONNECT;
569 httpSetTimeout(connection, serverTimeout);
570 do_connect(retainAtom(request->addr), connection->server->addrindex,
571 connection->server->port,
572 httpServerConnectionHandler, connection);
573 return 1;
577 httpServerConnectionHandler(int status,
578 FdEventHandlerPtr event,
579 ConnectRequestPtr request)
581 HTTPConnectionPtr connection = request->data;
583 assert(connection->fd < 0);
584 if(request->fd >= 0) {
585 int rc;
586 connection->fd = request->fd;
587 connection->server->addrindex = request->index;
588 rc = setNodelay(connection->fd, 1);
589 if(rc < 0)
590 do_log_error(L_WARN, errno, "Couldn't disable Nagle's algorithm");
593 return httpServerConnectionHandlerCommon(status, connection);
597 httpServerSocksHandler(int status, SocksRequestPtr request)
599 HTTPConnectionPtr connection = request->data;
601 assert(connection->fd < 0);
602 if(request->fd >= 0) {
603 connection->fd = request->fd;
604 connection->server->addrindex = 0;
606 return httpServerConnectionHandlerCommon(status, connection);
610 httpServerConnectionHandlerCommon(int status, HTTPConnectionPtr connection)
612 httpSetTimeout(connection, -1);
614 if(status < 0) {
615 AtomPtr message =
616 internAtomError(-status, "Connect to %s:%d failed",
617 connection->server->name,
618 connection->server->port);
619 if(status != -ECLIENTRESET)
620 do_log_error(L_ERROR, -status, "Connect to %s:%d failed",
621 connection->server->name, connection->server->port);
622 connection->connecting = 0;
623 if(connection->server->request)
624 httpServerAbortRequest(connection->server->request,
625 status != -ECLIENTRESET, 504,
626 retainAtom(message));
627 httpServerAbort(connection, status != -ECLIENTRESET, 504, message);
628 return 1;
631 do_log(D_SERVER_CONN, "C %s:%d.\n",
632 connection->server->name, connection->server->port);
634 connection->connecting = 0;
635 /* serverTrigger will take care of inserting any timeouts */
636 httpServerTrigger(connection->server);
637 return 1;
641 httpServerIdleHandler(int a, FdEventHandlerPtr event)
643 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
644 HTTPServerPtr server = connection->server;
645 int i;
647 assert(!connection->request);
649 do_log(D_SERVER_CONN, "Idle connection to %s:%d died.\n",
650 connection->server->name, connection->server->port);
652 for(i = 0; i < server->maxslots; i++) {
653 if(connection == server->connection[i]) {
654 server->idleHandler[i] = NULL;
655 break;
658 assert(i < server->maxslots);
660 httpServerAbort(connection, 1, 504, internAtom("Timeout"));
661 return 1;
664 /* Discard aborted requests at the head of the queue. */
665 static void
666 httpServerDiscardRequests(HTTPServerPtr server)
668 HTTPRequestPtr request;
669 while(server->request && !server->request->request) {
670 request = server->request;
671 server->request = request->next;
672 request->next = NULL;
673 if(server->request == NULL)
674 server->request_last = NULL;
675 request->object->flags &= ~(OBJECT_INPROGRESS | OBJECT_VALIDATING);
676 releaseNotifyObject(request->object);
677 request->object = NULL;
678 httpDestroyRequest(request);
682 static int
683 pipelineIsSmall(HTTPConnectionPtr connection)
685 HTTPRequestPtr request = connection->request;
687 if(pipelineAdditionalRequests <= 0)
688 return 0;
689 else if(pipelineAdditionalRequests >= 2)
690 return 1;
692 if(!request)
693 return 1;
694 if(request->next || !(request->flags & REQUEST_PERSISTENT))
695 return 0;
696 if(request->method == METHOD_HEAD ||
697 request->method == METHOD_CONDITIONAL_GET)
698 return 1;
699 if(request->to >= 0 && connection->server->rate > 0 &&
700 request->to - request->from < connection->server->rate *
701 smallRequestTime)
702 return 1;
703 return 0;
706 static int
707 numRequests(HTTPServerPtr server)
709 int n = 0;
710 HTTPRequestPtr request = server->request;
711 while(request) {
712 n++;
713 request = request->next;
715 return n;
718 HTTPConnectionPtr
719 httpServerGetConnection(HTTPServerPtr server, int *idle_return)
721 int i, j;
722 int connecting = 0, empty = 0, idle = 0;
724 j = -1;
725 /* Try to find an idle connection */
726 for(i = 0; i < server->numslots; i++) {
727 if(server->connection[i]) {
728 if(!server->connection[i]->connecting) {
729 if(!server->connection[i]->request) {
730 if(server->idleHandler[i])
731 unregisterFdEvent(server->idleHandler[i]);
732 server->idleHandler[i] = NULL;
733 if(j < 0) j = i;
734 idle++;
736 } else
737 connecting++;
738 } else
739 empty++;
742 if(j >= 0) {
743 *idle_return = idle;
744 return server->connection[j];
747 /* If there's an empty slot, schedule connection creation */
748 if(empty) {
749 /* Don't open a connection if there are already enough in
750 progress, except if the server doesn't do persistent
751 connections and there's only one in progress. */
752 if((connecting == 0 || (server->persistent <= 0 && connecting <= 1)) ||
753 connecting < numRequests(server)) {
754 httpServerConnection(server);
758 /* Find a connection that can accept additional requests */
759 if(server->version == HTTP_11 && server->pipeline >= 4) {
760 for(i = 0; i < serverSlots; i++) {
761 if(server->connection[i] && !server->connection[i]->connecting &&
762 pipelineIsSmall(server->connection[i])) {
763 if(server->idleHandler[i])
764 unregisterFdEvent(server->idleHandler[i]);
765 server->idleHandler[i] = NULL;
766 *idle_return = 0;
767 return server->connection[i];
771 *idle_return = idle;
772 return NULL;
776 httpServerTrigger(HTTPServerPtr server)
778 HTTPConnectionPtr connection;
779 HTTPRequestPtr request;
780 int idle, n, i, rc, numidle;
782 while(server->request) {
783 httpServerDiscardRequests(server);
785 if(!server->request)
786 break;
788 if(REQUEST_SIDE(server->request)) {
789 rc = httpServerSideRequest(server);
790 /* If rc is 0, httpServerSideRequest didn't dequeue this
791 request. Go through the scheduling loop again, come
792 back later. */
793 if(rc <= 0) break;
794 continue;
796 connection = httpServerGetConnection(server, &numidle);
797 if(!connection) break;
799 /* If server->pipeline <= 0, we don't do pipelining. If
800 server->pipeline is 1, then we are ready to start probing
801 for pipelining on the server; we then send exactly two
802 requests in what is hopefully a single packet to check
803 whether the server has the nasty habit of discarding its
804 input buffers after each request.
805 If server->pipeline is 2 or 3, the pipelining probe is in
806 progress on this server, and we don't pipeline anything
807 until it succeeds. When server->pipeline >= 4, pipelining
808 is believed to work on this server. */
809 if(server->version != HTTP_11 || server->pipeline <= 0 ||
810 server->pipeline == 2 || server->pipeline == 3) {
811 if(connection->pipelined == 0)
812 n = 1;
813 else
814 n = 0;
815 } else if(server->pipeline == 1) {
816 if(connection->pipelined == 0)
817 n = MIN(2, maxPipelineTrain);
818 else
819 n = 0;
820 } else {
821 n = maxPipelineTrain;
824 /* Don't pipeline if there are more idle connections */
825 if(numidle >= 2)
826 n = MIN(n, 1);
828 idle = !connection->pipelined;
829 i = 0;
830 while(server->request && connection->pipelined < n) {
831 httpServerDiscardRequests(server);
832 if(!server->request) break;
833 request = server->request;
834 assert(request->request->request == request);
835 rc = httpWriteRequest(connection, request, -1);
836 if(rc < 0) {
837 if(i == 0)
838 httpServerAbortRequest(request, rc != -ECLIENTRESET, 503,
839 internAtom("Couldn't "
840 "write request"));
841 break;
843 do_log(D_SERVER_CONN, "W: ");
844 do_log_n(D_SERVER_CONN,
845 request->object->key, request->object->key_size);
846 do_log(D_SERVER_CONN, " (%d)\n", request->method);
847 if(connection->pipelined > 0)
848 request->flags |= REQUEST_PIPELINED;
849 request->time0 = current_time;
850 i++;
851 server->request = request->next;
852 request->next = NULL;
853 if(server->request == NULL)
854 server->request_last = NULL;
855 httpQueueRequest(connection, request);
856 connection->pipelined++;
858 if(server->persistent > 0 && server->pipeline == 1 && i >= 2)
859 server->pipeline = 2;
861 if(i > 0) httpServerSendRequest(connection);
863 if(idle && connection->pipelined > 0)
864 httpServerReply(connection, 0);
866 if(i == 0) break;
869 for(i = 0; i < server->maxslots; i++) {
870 if(server->connection[i] &&
871 !server->connection[i]->connecting &&
872 !server->connection[i]->request) {
873 /* Artificially age any fresh connections that aren't used
874 straight away; this is necessary for the logic for POST and
875 the logic that determines whether a given request should be
876 restarted. */
877 if(server->connection[i]->serviced == 0)
878 server->connection[i]->serviced = 1;
879 if(!server->idleHandler[i])
880 server->idleHandler[i] =
881 registerFdEvent(server->connection[i]->fd, POLLIN,
882 httpServerIdleHandler,
883 sizeof(HTTPConnectionPtr),
884 &server->connection[i]);
885 if(!server->idleHandler[i]) {
886 do_log(L_ERROR, "Couldn't register idle handler.\n");
887 httpServerFinish(server->connection[i], 1, 0);
889 httpSetTimeout(server->connection[i], serverIdleTimeout);
893 return 1;
897 httpServerSideRequest(HTTPServerPtr server)
899 HTTPRequestPtr request = server->request;
900 HTTPConnectionPtr connection;
901 HTTPRequestPtr requestor = request->request;
902 HTTPConnectionPtr client = requestor->connection;
903 int rc, i, freeslots, idle, connecting;
905 assert(REQUEST_SIDE(request));
907 connection = NULL;
908 freeslots = 0;
909 idle = -1;
910 connecting = 0;
912 /* Find a fresh connection */
913 for(i = 0; i < server->numslots; i++) {
914 if(!server->connection[i])
915 freeslots++;
916 else if(!server->connection[i]->connecting) {
917 if(!server->connection[i]->request) {
918 if(server->connection[i]->serviced == 0) {
919 if(server->idleHandler[i])
920 unregisterFdEvent(server->idleHandler[i]);
921 server->idleHandler[i] = NULL;
922 connection = server->connection[i];
923 break;
924 } else {
925 idle = i;
928 } else {
929 connecting++;
933 if(!connection) {
934 /* Make sure that a fresh connection will be established at some
935 point, then wait until httpServerTrigger calls us again. */
936 if(freeslots) {
937 httpServerConnection(server);
938 } else {
939 if(idle >= 0) {
940 /* Shutdown a random idle connection */
941 pokeFdEvent(server->connection[idle]->fd,
942 -EDOSHUTDOWN, POLLIN | POLLOUT);
945 return 0;
948 rc = httpWriteRequest(connection, request, client->bodylen);
949 if(rc < 0) {
950 do_log(L_ERROR, "Couldn't write POST or PUT request.\n");
951 httpServerAbortRequest(request, rc != -ECLIENTRESET, 503,
952 internAtom("Couldn't write request"));
953 return 0;
955 server->request = request->next;
956 request->next = NULL;
957 if(server->request == NULL)
958 server->request_last = NULL;
959 httpQueueRequest(connection, request);
960 connection->pipelined = 1;
961 request->time0 = current_time;
962 connection->reqoffset = 0;
963 connection->bodylen = client->bodylen;
964 httpServerDoSide(connection);
965 return 1;
968 int
969 httpServerDoSide(HTTPConnectionPtr connection)
971 HTTPRequestPtr request = connection->request;
972 HTTPRequestPtr requestor = request->request;
973 HTTPConnectionPtr client = requestor->connection;
974 int len = MIN(client->reqlen - client->reqbegin,
975 connection->bodylen - connection->reqoffset);
976 int doflush =
977 len > 0 &&
978 (len >= maxSideBuffering ||
979 client->reqbegin > 0 ||
980 (connection->reqoffset + client->reqlen - client->reqbegin) >=
981 connection->bodylen);
982 int done = connection->reqoffset >= connection->bodylen;
984 assert(connection->bodylen >= 0);
986 httpSetTimeout(connection, 60);
988 if(connection->reqlen > 0) {
989 /* Send the headers, but don't send any part of the body if
990 we're in wait_continue. */
991 do_stream_2(IO_WRITE,
992 connection->fd, 0,
993 connection->reqbuf, connection->reqlen,
994 client->reqbuf + client->reqbegin,
995 (request->flags & REQUEST_WAIT_CONTINUE) ? 0 : len,
996 httpServerSideHandler2, connection);
997 httpServerReply(connection, 0);
998 } else if(request->object->flags & OBJECT_ABORTED) {
999 if(connection->reqbuf)
1000 dispose_chunk(connection->reqbuf);
1001 connection->reqbuf = NULL;
1002 connection->reqlen = 0;
1003 pokeFdEvent(connection->fd, -ESHUTDOWN, POLLIN);
1004 if(client->flags & CONN_READER) {
1005 client->flags |= CONN_SIDE_READER;
1006 do_stream(IO_READ | IO_IMMEDIATE | IO_NOTNOW,
1007 client->fd, 0, NULL, 0,
1008 httpClientSideHandler, client);
1010 } else if(!(request->flags & REQUEST_WAIT_CONTINUE) && doflush) {
1011 /* Make sure there's a reqbuf, as httpServerFinish uses
1012 it to determine if there's a writer. */
1013 if(connection->reqbuf == NULL)
1014 connection->reqbuf = get_chunk();
1015 assert(connection->reqbuf != NULL);
1016 do_stream(IO_WRITE,
1017 connection->fd, 0,
1018 client->reqbuf + client->reqbegin, len,
1019 httpServerSideHandler, connection);
1020 } else {
1021 if(connection->reqbuf) {
1022 httpConnectionDestroyReqbuf(connection);
1023 connection->reqlen = 0;
1025 if(request->flags & REQUEST_WAIT_CONTINUE) {
1026 ConditionHandlerPtr chandler;
1027 do_log(D_SERVER_CONN, "W... %s:%d.\n",
1028 connection->server->name, connection->server->port);
1029 chandler =
1030 conditionWait(&request->object->condition,
1031 httpServerContinueConditionHandler,
1032 sizeof(connection), &connection);
1033 if(chandler)
1034 return 1;
1035 else
1036 do_log(L_ERROR, "Couldn't register condition handler.\n");
1037 /* Fall through -- the client side will clean up. */
1039 client->flags |= CONN_SIDE_READER;
1040 do_stream(IO_READ | (done ? IO_IMMEDIATE : 0 ) | IO_NOTNOW,
1041 client->fd, client->reqlen,
1042 client->reqbuf, CHUNK_SIZE,
1043 httpClientSideHandler, client);
1045 return 1;
1048 static int
1049 httpClientDelayedDoSideHandler(TimeEventHandlerPtr event)
1051 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1052 httpServerDoSide(connection);
1053 return 1;
1056 static int
1057 httpServerDelayedDoSide(HTTPConnectionPtr connection)
1059 TimeEventHandlerPtr handler;
1060 handler = scheduleTimeEvent(1, httpClientDelayedDoSideHandler,
1061 sizeof(connection), &connection);
1062 if(!handler) {
1063 do_log(L_ERROR, "Couldn't schedule DoSide -- freeing memory.\n");
1064 free_chunk_arenas();
1065 handler = scheduleTimeEvent(1, httpClientDelayedDoSideHandler,
1066 sizeof(connection), &connection);
1067 do_log(L_ERROR, "Couldn't schedule DoSide.\n");
1068 /* Somebody will hopefully end up timing out. */
1069 return 1;
1071 return 1;
1074 static int
1075 httpServerSideHandlerCommon(int kind, int status,
1076 FdEventHandlerPtr event,
1077 StreamRequestPtr srequest)
1079 HTTPConnectionPtr connection = srequest->data;
1080 HTTPRequestPtr request = connection->request;
1081 HTTPRequestPtr requestor = request->request;
1082 HTTPConnectionPtr client = requestor->connection;
1083 int bodylen;
1085 assert(request->object->flags & OBJECT_INPROGRESS);
1087 if(status) {
1088 do_log_error(L_ERROR, -status, "Couldn't write to server");
1089 httpConnectionDestroyReqbuf(connection);
1090 if(status != -ECLIENTRESET)
1091 shutdown(connection->fd, 2);
1092 abortObject(request->object, 503,
1093 internAtom("Couldn't write to server"));
1094 /* Let the read side handle the error */
1095 httpServerDoSide(connection);
1096 return 1;
1099 assert(srequest->offset > 0);
1101 if(kind == 2) {
1102 if(srequest->offset < connection->reqlen)
1103 return 0;
1104 bodylen = srequest->offset - connection->reqlen;
1105 connection->reqlen = 0;
1106 httpConnectionDestroyReqbuf(connection);
1107 } else {
1108 bodylen = srequest->offset;
1112 assert(client->reqbegin + bodylen <= client->reqlen);
1114 if(client->reqlen > client->reqbegin + bodylen)
1115 memmove(client->reqbuf, client->reqbuf + client->reqbegin + bodylen,
1116 client->reqlen - client->reqbegin - bodylen);
1117 client->reqlen -= bodylen + client->reqbegin;
1118 client->reqbegin = 0;
1119 connection->reqoffset += bodylen;
1120 httpServerDoSide(connection);
1121 return 1;
1125 httpServerSideHandler(int status,
1126 FdEventHandlerPtr event,
1127 StreamRequestPtr srequest)
1129 return httpServerSideHandlerCommon(1, status, event, srequest);
1133 httpServerSideHandler2(int status,
1134 FdEventHandlerPtr event,
1135 StreamRequestPtr srequest)
1137 return httpServerSideHandlerCommon(2, status, event, srequest);
1140 static int
1141 httpServerContinueConditionHandler(int status, ConditionHandlerPtr chandler)
1143 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)chandler->data;
1145 if(connection->request->flags & REQUEST_WAIT_CONTINUE)
1146 return 0;
1147 httpServerDelayedDoSide(connection);
1148 return 1;
1151 /* s is 0 to keep the connection alive, 1 to shutdown the connection,
1152 and -1 to keep the connection alive and keep the current request. */
1153 void
1154 httpServerFinish(HTTPConnectionPtr connection, int s, int offset)
1156 HTTPServerPtr server = connection->server;
1157 HTTPRequestPtr request = connection->request;
1158 int i;
1160 if(request) {
1161 assert(connection->pipelined >= 1);
1162 assert((connection->pipelined > 1) == (request->next != NULL));
1163 } else {
1164 assert(connection->pipelined == 0);
1167 if(s == 0 && (!connection->request ||
1168 !(connection->request->flags & REQUEST_PERSISTENT)))
1169 s = 1;
1171 if(connection->serviced >= maxConnectionRequests ||
1172 connection->time < current_time.tv_sec - maxConnectionAge)
1173 s = 1;
1175 if(connection->reqbuf) {
1176 /* As most normal requests go out in a single packet, this is
1177 extremely unlikely to happen. As for POST/PUT requests,
1178 they are not pipelined, so this can only happen if the
1179 server sent an error reply early. */
1180 assert(connection->fd >= 0);
1181 shutdown(connection->fd, 1);
1182 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLOUT);
1183 httpServerDelayedFinish(connection);
1184 goto done;
1187 if(s >= 0 && request) {
1188 /* Update statistics about the server */
1189 int size = -1, d = -1, rtt = -1, rate = -1;
1190 if(connection->offset > 0 && request->from >= 0)
1191 size = connection->offset - request->from;
1192 if(request->time1.tv_sec != null_time.tv_sec) {
1193 d = timeval_minus_usec(&current_time, &request->time1);
1194 if(!(request->flags & REQUEST_PIPELINED) &&
1195 request->time0.tv_sec != null_time.tv_sec)
1196 rtt = timeval_minus_usec(&request->time1, &request->time0);
1197 if(size >= 8192 && d > 50000)
1198 rate = ((double)size / (double)d) * 1000000.0 + 0.5;
1200 request->time0 = null_time;
1201 request->time1 = null_time;
1203 if(rtt >= 0) {
1204 if(server->rtt >= 0)
1205 server->rtt = (3 * server->rtt + rtt + 2) / 4;
1206 else
1207 server->rtt = rtt;
1209 if(rate >= 0) {
1210 if(server->rate >= 0)
1211 server->rate = (3 * server->rate + rate + 2) / 4;
1212 else
1213 server->rate = rate;
1216 httpDequeueRequest(connection);
1217 connection->pipelined--;
1218 request->object->flags &= ~(OBJECT_INPROGRESS | OBJECT_VALIDATING);
1219 if(request->request) {
1220 request->request->request = NULL;
1221 request->request = NULL;
1223 releaseNotifyObject(request->object);
1224 request->object = NULL;
1225 httpDestroyRequest(request);
1228 do_log(D_SERVER_CONN, "Done with server %s:%d connection (%d)\n",
1229 connection->server->name, connection->server->port, s);
1231 assert(offset <= connection->len);
1233 if(s <= 0) {
1234 if(offset < connection->len) {
1235 assert(connection->buf != NULL);
1236 if(!connection->pipelined) {
1237 do_log(L_WARN,
1238 "Closing connection to %s:%d: "
1239 "%d stray bytes of data.\n",
1240 server->name, server->port, connection->len - offset);
1241 s = 1;
1242 } else {
1243 memmove(connection->buf, connection->buf + offset,
1244 connection->len - offset);
1245 connection->len = connection->len - offset;
1246 if((connection->flags & CONN_BIGBUF) &&
1247 connection->len <= CHUNK_SIZE)
1248 httpConnectionUnbigify(connection);
1250 } else {
1251 connection->len = 0;
1255 connection->server->time = current_time.tv_sec;
1256 connection->serviced++;
1258 if(s > 0) {
1259 if(connection->timeout)
1260 cancelTimeEvent(connection->timeout);
1261 connection->timeout = NULL;
1262 httpConnectionDestroyBuf(connection);
1263 if(connection->fd >= 0)
1264 CLOSE(connection->fd);
1265 connection->fd = -1;
1266 server->persistent -= 1;
1267 if(server->persistent < -5)
1268 server->numslots = MIN(server->maxslots, serverMaxSlots);
1269 if(connection->request) {
1270 HTTPRequestPtr req;
1271 do_log(D_SERVER_CONN, "Restarting pipeline to %s:%d.\n",
1272 server->name, server->port);
1273 if(server->pipeline == 2)
1274 server->pipeline -= 20;
1275 else
1276 server->pipeline -= 5;
1277 req = connection->request;
1278 while(req) {
1279 req->connection = NULL;
1280 req = req->next;
1282 if(server->request)
1283 connection->request_last->next = server->request;
1284 else
1285 server->request_last = connection->request_last;
1286 server->request = connection->request;
1287 connection->request = NULL;
1288 connection->request_last = NULL;
1290 /* Make sure we don't get confused into thinking a probe
1291 is in progress. */
1292 if(server->pipeline == 2 || server->pipeline == 3)
1293 server->pipeline = 1;
1294 for(i = 0; i < server->maxslots; i++)
1295 if(connection == server->connection[i])
1296 break;
1297 assert(i < server->maxslots);
1298 if(server->idleHandler[i])
1299 unregisterFdEvent(server->idleHandler[i]);
1300 server->idleHandler[i] = NULL;
1301 server->connection[i] = NULL;
1302 free(connection);
1303 } else {
1304 server->persistent += 1;
1305 if(server->persistent > 0)
1306 server->numslots = MIN(server->maxslots,
1307 server->version == HTTP_10 ?
1308 serverSlots1 : serverSlots);
1309 httpSetTimeout(connection, serverTimeout);
1310 /* See httpServerTrigger */
1311 if(connection->pipelined ||
1312 (server->version == HTTP_11 && server->pipeline <= 0) ||
1313 (server->pipeline == 3)) {
1314 server->pipeline++;
1316 if(s < 0 || connection->pipelined) {
1317 httpServerReply(connection, 1);
1318 } else {
1319 httpConnectionDestroyBuf(connection);
1323 done:
1324 httpServerTrigger(server);
1327 static int
1328 httpServerDelayedFinishHandler(TimeEventHandlerPtr event)
1330 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1331 httpServerFinish(connection, 1, 0);
1332 return 1;
1335 static void
1336 httpServerDelayedFinish(HTTPConnectionPtr connection)
1338 TimeEventHandlerPtr handler;
1340 handler = scheduleTimeEvent(1, httpServerDelayedFinishHandler,
1341 sizeof(connection), &connection);
1342 if(!handler) {
1343 do_log(L_ERROR,
1344 "Couldn't schedule delayed finish -- freeing memory.");
1345 free_chunk_arenas();
1346 handler = scheduleTimeEvent(1, httpServerDelayedFinishHandler,
1347 sizeof(connection), &connection);
1348 if(!handler) {
1349 do_log(L_ERROR,
1350 "Couldn't schedule delayed finish -- aborting.\n");
1351 polipoExit();
1356 void
1357 httpServerReply(HTTPConnectionPtr connection, int immediate)
1359 assert(connection->pipelined > 0);
1361 if(connection->request->request == NULL) {
1362 do_log(L_WARN, "Aborting pipeline on %s:%d.\n",
1363 connection->server->name, connection->server->port);
1364 httpServerFinish(connection, 1, 0);
1365 return;
1368 do_log(D_SERVER_CONN, "R: ");
1369 do_log_n(D_SERVER_CONN, connection->request->object->key,
1370 connection->request->object->key_size);
1371 do_log(D_SERVER_CONN, " (%d)\n", connection->request->method);
1373 if(connection->len == 0)
1374 httpConnectionDestroyBuf(connection);
1376 httpSetTimeout(connection, serverTimeout);
1377 do_stream_buf(IO_READ | (immediate ? IO_IMMEDIATE : 0) | IO_NOTNOW,
1378 connection->fd, connection->len,
1379 &connection->buf, CHUNK_SIZE,
1380 httpServerReplyHandler, connection);
1384 httpConnectionPipelined(HTTPConnectionPtr connection)
1386 HTTPRequestPtr request = connection->request;
1387 int i = 0;
1388 while(request) {
1389 i++;
1390 request = request->next;
1392 return i;
1395 void
1396 httpServerUnpipeline(HTTPRequestPtr request)
1398 HTTPConnectionPtr connection = request->connection;
1399 HTTPServerPtr server = connection->server;
1401 request->flags &= ~REQUEST_PERSISTENT;
1402 if(request->next) {
1403 HTTPRequestPtr req;
1404 do_log(L_WARN,
1405 "Restarting pipeline to %s:%d.\n",
1406 connection->server->name, connection->server->port);
1407 req = request->next;
1408 while(req) {
1409 req->connection = NULL;
1410 req = req->next;
1412 if(server->request)
1413 connection->request_last->next = server->request;
1414 else
1415 server->request_last = connection->request_last;
1416 server->request = request->next;
1417 request->next = NULL;
1418 connection->request_last = request;
1420 connection->pipelined = httpConnectionPipelined(connection);
1423 void
1424 httpServerRestart(HTTPConnectionPtr connection)
1426 HTTPServerPtr server = connection->server;
1427 HTTPRequestPtr request = connection->request;
1429 if(request) {
1430 HTTPRequestPtr req;
1431 if(request->next)
1432 do_log(L_WARN,
1433 "Restarting pipeline to %s:%d.\n",
1434 connection->server->name, connection->server->port);
1435 req = request;
1436 while(req) {
1437 req->connection = NULL;
1438 req = req->next;
1440 if(server->request)
1441 connection->request_last->next = server->request;
1442 else
1443 server->request_last = connection->request_last;
1444 server->request = request;
1445 connection->request = NULL;
1446 connection->request_last = NULL;
1448 connection->pipelined = 0;
1449 httpServerFinish(connection, 1, 0);
1453 httpServerRequest(ObjectPtr object, int method, int from, int to,
1454 HTTPRequestPtr requestor, void *closure)
1456 int rc;
1457 char name[132];
1458 int port;
1459 int x, y, z;
1461 assert(from >= 0 && (to < 0 || to > from));
1462 assert(closure == NULL);
1463 assert(!(object->flags & OBJECT_LOCAL));
1464 assert(object->type == OBJECT_HTTP);
1466 if(object->flags & OBJECT_INPROGRESS)
1467 return 1;
1469 if(requestor->flags & REQUEST_REQUESTED)
1470 return 0;
1472 assert(requestor->request == NULL);
1474 if(proxyOffline)
1475 return -1;
1477 rc = parseUrl(object->key, object->key_size, &x, &y, &port, &z);
1479 if(rc < 0 || x < 0 || y < 0 || y - x > 131) {
1480 do_log(L_ERROR, "Couldn't parse URL: ");
1481 do_log_n(L_ERROR, object->key, object->key_size);
1482 do_log(L_ERROR, "\n");
1483 abortObject(object, 400, internAtom("Couldn't parse URL"));
1484 notifyObject(object);
1485 return 1;
1488 if(!intListMember(port, allowedPorts)) {
1489 do_log(L_ERROR, "Attempted connection to port %d.\n", port);
1490 abortObject(object, 403, internAtom("Forbidden port"));
1491 notifyObject(object);
1492 return 1;
1495 memcpy(name, ((char*)object->key) + x, y - x);
1496 name[y - x] = '\0';
1498 requestor->flags |= REQUEST_REQUESTED;
1499 rc = httpMakeServerRequest(name, port, object, method, from, to,
1500 requestor);
1502 if(rc < 0) {
1503 abortObject(object,
1504 503, internAtom("Couldn't schedule server request"));
1505 notifyObject(object);
1506 return 1;
1509 return 1;
1513 httpWriteRequest(HTTPConnectionPtr connection, HTTPRequestPtr request,
1514 int bodylen)
1516 ObjectPtr object = request->object;
1517 int from = request->from, to = request->to, method = request->method;
1518 char *url = object->key, *m;
1519 int url_size = object->key_size;
1520 int x, y, port, z, location_size;
1521 char *location;
1522 int l, n, rc, bufsize;
1524 assert(method != METHOD_NONE);
1526 if(request->method == METHOD_GET ||
1527 request->method == METHOD_CONDITIONAL_GET) {
1528 if(to >= 0) {
1529 assert(to >= from);
1530 if(to == from) {
1531 do_log(L_ERROR, "Requesting empty segment?\n");
1532 return -1;
1536 if(object->flags & OBJECT_DYNAMIC) {
1537 from = 0;
1538 to = -1;
1539 } else {
1540 objectFillFromDisk(object, from / CHUNK_SIZE * CHUNK_SIZE, 1);
1541 l = objectHoleSize(request->object, from);
1542 if(l > 0) {
1543 if(to <= 0 || to > from + l)
1544 to = from + l;
1547 if(pmmSize && connection->server->pipeline >= 4) {
1548 if(from == 0)
1549 to = to < 0 ? pmmFirstSize : MIN(to, pmmFirstSize);
1550 else
1551 to = to < 0 ? from + pmmSize : MIN(to, from + pmmSize);
1554 if(from % CHUNK_SIZE != 0)
1555 if(objectHoleSize(object, from - 1) != 0)
1556 from = from / CHUNK_SIZE * CHUNK_SIZE;
1560 rc = parseUrl(url, url_size, &x, &y, &port, &z);
1562 if(rc < 0 || x < 0 || y < 0) {
1563 return -1;
1566 if(connection->reqbuf == NULL) {
1567 connection->reqbuf = get_chunk();
1568 if(connection->reqbuf == NULL)
1569 return -1;
1570 connection->reqlen = 0;
1573 if(method == METHOD_CONDITIONAL_GET &&
1574 object->last_modified < 0 && object->etag == NULL)
1575 method = request->method = METHOD_GET;
1577 again:
1578 bufsize =
1579 (connection->flags & CONN_BIGREQBUF) ? bigBufferSize : CHUNK_SIZE;
1580 n = connection->reqlen;
1581 switch(method) {
1582 case METHOD_GET:
1583 case METHOD_CONDITIONAL_GET: m = "GET"; break;
1584 case METHOD_HEAD: m = "HEAD"; break;
1585 case METHOD_POST: m = "POST"; break;
1586 case METHOD_PUT: m = "PUT"; break;
1587 default: abort();
1589 n = snnprintf(connection->reqbuf, n, bufsize, "%s ", m);
1591 if(connection->server->isProxy) {
1592 n = snnprint_n(connection->reqbuf, n, bufsize,
1593 url, url_size);
1594 } else {
1595 if(url_size - z == 0) {
1596 location = "/";
1597 location_size = 1;
1598 } else {
1599 location = url + z;
1600 location_size = url_size - z;
1603 n = snnprint_n(connection->reqbuf, n, bufsize,
1604 location, location_size);
1607 do_log(D_SERVER_REQ, "Server request: ");
1608 do_log_n(D_SERVER_REQ, url + x, y - x);
1609 do_log(D_SERVER_REQ, ": ");
1610 do_log_n(D_SERVER_REQ, connection->reqbuf, n);
1611 do_log(D_SERVER_REQ, " (method %d from %d to %d, 0x%lx for 0x%lx)\n",
1612 method, from, to,
1613 (unsigned long)connection, (unsigned long)object);
1615 n = snnprintf(connection->reqbuf, n, bufsize, " HTTP/1.1");
1617 n = snnprintf(connection->reqbuf, n, bufsize, "\r\nHost: ");
1618 n = snnprint_n(connection->reqbuf, n, bufsize, url + x, y - x);
1619 if(port != 80)
1620 n = snnprintf(connection->reqbuf, n, bufsize, ":%d", port);
1622 if(connection->server->isProxy && parentAuthCredentials) {
1623 n = buildServerAuthHeaders(connection->reqbuf, n, bufsize,
1624 parentAuthCredentials);
1627 if(bodylen >= 0)
1628 n = snnprintf(connection->reqbuf, n, bufsize,
1629 "\r\nContent-Length: %d", bodylen);
1631 if(request->flags & REQUEST_WAIT_CONTINUE)
1632 n = snnprintf(connection->reqbuf, n, bufsize,
1633 "\r\nExpect: 100-continue");
1635 if(method != METHOD_HEAD && (from > 0 || to >= 0)) {
1636 if(to >= 0) {
1637 n = snnprintf(connection->reqbuf, n, bufsize,
1638 "\r\nRange: bytes=%d-%d", from, to - 1);
1639 } else {
1640 n = snnprintf(connection->reqbuf, n, bufsize,
1641 "\r\nRange: bytes=%d-", from);
1645 if(method == METHOD_GET && object->etag && (from > 0 || to >= 0)) {
1646 if(request->request && request->request->request == request &&
1647 request->request->from == 0 && request->request->to == -1 &&
1648 pmmSize == 0 && pmmFirstSize == 0)
1649 n = snnprintf(connection->reqbuf, n, bufsize,
1650 "\r\nIf-Range: \"%s\"", object->etag);
1653 if(method == METHOD_CONDITIONAL_GET) {
1654 if(object->last_modified >= 0) {
1655 n = snnprintf(connection->reqbuf, n, bufsize,
1656 "\r\nIf-Modified-Since: ");
1657 n = format_time(connection->reqbuf, n, bufsize,
1658 object->last_modified);
1660 if(object->etag) {
1661 n = snnprintf(connection->reqbuf, n, bufsize,
1662 "\r\nIf-None-Match: \"%s\"", object->etag);
1666 n = httpPrintCacheControl(connection->reqbuf, n, bufsize,
1667 0, &request->cache_control);
1668 if(n < 0)
1669 goto fail;
1671 if(request->request && request->request->headers) {
1672 n = snnprint_n(connection->reqbuf, n, bufsize,
1673 request->request->headers->string,
1674 request->request->headers->length);
1676 if(!disableVia) {
1677 if(request->request && request->request->via) {
1678 n = snnprintf(connection->reqbuf, n, bufsize,
1679 "\r\nVia: %s, 1.1 %s",
1680 request->request->via->string, proxyName->string);
1681 } else {
1682 n = snnprintf(connection->reqbuf, n, bufsize,
1683 "\r\nVia: 1.1 %s",
1684 proxyName->string);
1688 n = snnprintf(connection->reqbuf, n, bufsize,
1689 "\r\nConnection: %s\r\n\r\n",
1690 (request->flags & REQUEST_PERSISTENT) ?
1691 "keep-alive" : "close");
1692 if(n < 0 || n >= bufsize - 1)
1693 goto fail;
1694 connection->reqlen = n;
1695 return n;
1697 fail:
1698 rc = 0;
1699 if(!(connection->flags & CONN_BIGREQBUF))
1700 rc = httpConnectionBigifyReqbuf(connection);
1701 if(rc == 1)
1702 goto again;
1703 return -1;
1707 httpServerHandler(int status,
1708 FdEventHandlerPtr event,
1709 StreamRequestPtr srequest)
1711 HTTPConnectionPtr connection = srequest->data;
1712 AtomPtr message;
1714 assert(connection->request->object->flags & OBJECT_INPROGRESS);
1716 if(connection->reqlen == 0) {
1717 do_log(D_SERVER_REQ, "Writing aborted on 0x%lx\n",
1718 (unsigned long)connection);
1719 goto fail;
1722 if(status == 0 && !streamRequestDone(srequest)) {
1723 httpSetTimeout(connection, serverTimeout);
1724 return 0;
1727 httpConnectionDestroyReqbuf(connection);
1729 if(status) {
1730 if(connection->serviced >= 1) {
1731 httpServerRestart(connection);
1732 return 1;
1734 if(status >= 0 || status == ECONNRESET) {
1735 message = internAtom("Couldn't send request to server: "
1736 "short write");
1737 } else {
1738 if(status != -EPIPE)
1739 do_log_error(L_ERROR, -status,
1740 "Couldn't send request to server");
1741 message =
1742 internAtomError(-status, "Couldn't send request to server");
1744 goto fail;
1747 return 1;
1749 fail:
1750 dispose_chunk(connection->reqbuf);
1751 connection->reqbuf = NULL;
1752 shutdown(connection->fd, 2);
1753 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLIN);
1754 httpSetTimeout(connection, 60);
1755 return 1;
1759 httpServerSendRequest(HTTPConnectionPtr connection)
1761 assert(connection->server);
1763 if(connection->reqlen == 0) {
1764 do_log(D_SERVER_REQ,
1765 "Writing aborted on 0x%lx\n", (unsigned long)connection);
1766 httpConnectionDestroyReqbuf(connection);
1767 shutdown(connection->fd, 2);
1768 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLIN | POLLOUT);
1769 return -1;
1772 httpSetTimeout(connection, serverTimeout);
1773 do_stream(IO_WRITE, connection->fd, 0,
1774 connection->reqbuf, connection->reqlen,
1775 httpServerHandler, connection);
1776 return 1;
1780 httpServerReplyHandler(int status,
1781 FdEventHandlerPtr event,
1782 StreamRequestPtr srequest)
1784 HTTPConnectionPtr connection = srequest->data;
1785 HTTPRequestPtr request = connection->request;
1786 int i, body;
1787 int bufsize =
1788 (connection->flags & CONN_BIGBUF) ? bigBufferSize : CHUNK_SIZE;
1790 assert(request->object->flags & OBJECT_INPROGRESS);
1791 if(status < 0) {
1792 if(connection->serviced >= 1) {
1793 httpServerRestart(connection);
1794 return 1;
1796 if(status != -ECLIENTRESET)
1797 do_log_error(L_ERROR, -status, "Read from server failed");
1798 httpServerAbort(connection, status != -ECLIENTRESET, 502,
1799 internAtomError(-status, "Read from server failed"));
1800 return 1;
1803 i = findEndOfHeaders(connection->buf, 0, srequest->offset, &body);
1804 connection->len = srequest->offset;
1806 if(i >= 0) {
1807 request->time1 = current_time;
1808 return httpServerHandlerHeaders(status, event, srequest, connection);
1811 if(status) {
1812 if(connection->serviced >= 1) {
1813 httpServerRestart(connection);
1814 return 1;
1816 if(status < 0) {
1817 do_log(L_ERROR,
1818 "Error reading server headers: %d\n", -status);
1819 httpServerAbort(connection, status != -ECLIENTRESET, 502,
1820 internAtomError(-status,
1821 "Error reading server headers"));
1822 } else
1823 httpServerAbort(connection, 1, 502,
1824 internAtom("Server dropped connection"));
1825 return 1;
1828 if(connection->len >= bufsize) {
1829 int rc = 0;
1830 if(!(connection->flags & CONN_BIGBUF))
1831 rc = httpConnectionBigify(connection);
1832 if(rc == 0) {
1833 do_log(L_ERROR, "Couldn't find end of server's headers.\n");
1834 httpServerAbort(connection, 1, 502,
1835 internAtom("Couldn't find end "
1836 "of server's headers"));
1837 return 1;
1838 } else if(rc < 0) {
1839 do_log(L_ERROR, "Couldn't allocate big buffer.\n");
1840 httpServerAbort(connection, 1, 500,
1841 internAtom("Couldn't allocate big buffer"));
1842 return 1;
1844 /* Can't just return 0 -- buf has moved. */
1845 do_stream(IO_READ,
1846 connection->fd, connection->len,
1847 connection->buf, bigBufferSize,
1848 httpServerReplyHandler, connection);
1849 return 1;
1852 return 0;
1856 httpServerHandlerHeaders(int eof,
1857 FdEventHandlerPtr event,
1858 StreamRequestPtr srequest,
1859 HTTPConnectionPtr connection)
1861 HTTPRequestPtr request = connection->request;
1862 ObjectPtr object = request->object;
1863 int rc;
1864 int code, version;
1865 int full_len;
1866 AtomPtr headers;
1867 int len;
1868 int te;
1869 CacheControlRec cache_control;
1870 int age = -1;
1871 time_t date, last_modified, expires;
1872 struct timeval *init_time;
1873 char *etag;
1874 AtomPtr via, new_via;
1875 int expect_body;
1876 HTTPRangeRec content_range;
1877 ObjectPtr new_object = NULL, old_object = NULL;
1878 int supersede = 0;
1879 AtomPtr message = NULL;
1880 int suspectDynamic;
1881 AtomPtr url = NULL;
1883 assert(request->object->flags & OBJECT_INPROGRESS);
1884 assert(eof >= 0);
1886 httpSetTimeout(connection, -1);
1888 if(request->flags & REQUEST_WAIT_CONTINUE) {
1889 do_log(D_SERVER_CONN, "W %s:%d.\n",
1890 connection->server->name, connection->server->port);
1891 request->flags &= ~REQUEST_WAIT_CONTINUE;
1894 rc = httpParseServerFirstLine(connection->buf, &code, &version, &message);
1895 if(rc <= 0) {
1896 do_log(L_ERROR, "Couldn't parse server status line.\n");
1897 httpServerAbort(connection, 1, 502,
1898 internAtom("Couldn't parse server status line"));
1899 return 1;
1902 do_log(D_SERVER_REQ, "Server status: ");
1903 do_log_n(D_SERVER_REQ, connection->buf,
1904 connection->buf[rc - 1] == '\r' ? rc - 2 : rc - 2);
1905 do_log(D_SERVER_REQ, " (0x%lx for 0x%lx)\n",
1906 (unsigned long)connection, (unsigned long)object);
1908 if(version != HTTP_10 && version != HTTP_11) {
1909 do_log(L_ERROR, "Unknown server HTTP version\n");
1910 httpServerAbort(connection, 1, 502,
1911 internAtom("Unknown server HTTP version"));
1912 releaseAtom(message);
1913 return 1;
1916 connection->version = version;
1917 connection->server->version = version;
1918 request->flags |= REQUEST_PERSISTENT;
1920 url = internAtomN(object->key, object->key_size);
1921 rc = httpParseHeaders(0, url, connection->buf, rc, request,
1922 &headers, &len, &cache_control, NULL, &te,
1923 &date, &last_modified, &expires, NULL, NULL, NULL,
1924 &age, &etag, NULL, NULL, &content_range,
1925 NULL, &via, NULL);
1926 if(rc < 0) {
1927 do_log(L_ERROR, "Couldn't parse server headers\n");
1928 releaseAtom(url);
1929 releaseAtom(message);
1930 httpServerAbort(connection, 1, 502,
1931 internAtom("Couldn't parse server headers"));
1932 return 1;
1935 if(date < 0)
1936 date = current_time.tv_sec;
1938 if(code == 100) {
1939 releaseAtom(url);
1940 releaseAtom(message);
1941 /* We've already reset wait_continue above, but we must still
1942 ensure that the writer notices. */
1943 notifyObject(request->object);
1944 httpServerFinish(connection, -1, rc);
1945 return 1;
1948 if(code == 101) {
1949 httpServerAbort(connection, 1, 501,
1950 internAtom("Upgrade not implemented"));
1951 goto fail;
1954 if(via && !checkVia(proxyName, via)) {
1955 httpServerAbort(connection, 1, 504, internAtom("Proxy loop detected"));
1956 goto fail;
1958 full_len = content_range.full_length;
1960 if(code == 206) {
1961 if(content_range.from == -1 || content_range.to == -1) {
1962 do_log(L_ERROR, "Partial content without range.\n");
1963 httpServerAbort(connection, 1, 502,
1964 internAtom("Partial content without range"));
1965 goto fail;
1967 if(len >= 0 && len != content_range.to - content_range.from) {
1968 do_log(L_ERROR, "Inconsistent partial content.\n");
1969 httpServerAbort(connection, 1, 502,
1970 internAtom("Inconsistent partial content"));
1971 goto fail;
1973 } else if(code < 400 &&
1974 (content_range.from >= 0 || content_range.to >= 0 ||
1975 content_range.full_length >= 0)) {
1976 do_log(L_WARN, "Range without partial content.\n");
1977 /* Damn anakata. */
1978 content_range.from = -1;
1979 content_range.to = -1;
1980 content_range.full_length = -1;
1981 } else if(code != 304 && code != 412) {
1982 full_len = len;
1985 if(te != TE_IDENTITY && te != TE_CHUNKED) {
1986 do_log(L_ERROR, "Unsupported transfer-encoding\n");
1987 httpServerAbort(connection, 1, 502,
1988 internAtom("Unsupported transfer-encoding"));
1989 goto fail;
1992 if(code == 304) {
1993 if(request->method != METHOD_CONDITIONAL_GET) {
1994 do_log(L_ERROR, "Unexpected \"not changed\" reply from server\n");
1995 httpServerAbort(connection, 1, 502,
1996 internAtom("Unexpected \"not changed\" "
1997 "reply from server"));
1998 goto fail;
2000 if(object->etag && !etag) {
2001 /* RFC 2616 10.3.5. Violated by some front-end proxies. */
2002 do_log(L_WARN, "\"Not changed\" reply with no ETag.\n");
2006 if(code == 412) {
2007 if(request->method != METHOD_CONDITIONAL_GET ||
2008 (!object->etag && !object->last_modified)) {
2009 do_log(L_ERROR,
2010 "Unexpected \"precondition failed\" reply from server.\n");
2011 httpServerAbort(connection, 1, 502,
2012 internAtom("Unexpected \"precondition failed\" "
2013 "reply from server"));
2014 goto fail;
2018 releaseAtom(url);
2020 /* Okay, we're going to accept this reply. */
2022 if((code == 200 || code == 206 || code == 304 || code == 412) &&
2023 (cache_control.flags & (CACHE_NO | CACHE_NO_STORE) ||
2024 cache_control.max_age == 0 ||
2025 (cacheIsShared && cache_control.s_maxage == 0) ||
2026 (expires >= 0 && expires <= object->age))) {
2027 do_log(L_UNCACHEABLE, "Uncacheable object ");
2028 do_log_n(L_UNCACHEABLE, object->key, object->key_size);
2029 do_log(L_UNCACHEABLE, " (%d)\n", cache_control.flags);
2032 if(request->time0.tv_sec != null_time.tv_sec)
2033 init_time = &request->time0;
2034 else
2035 init_time = &current_time;
2036 age = MIN(init_time->tv_sec - age, init_time->tv_sec);
2038 if(request->method == METHOD_HEAD ||
2039 code < 200 || code == 204 || code == 304)
2040 expect_body = 0;
2041 else if(te == TE_IDENTITY)
2042 expect_body = (len != 0);
2043 else
2044 expect_body = 1;
2046 connection->chunk_remaining = -1;
2047 connection->te = te;
2049 old_object = object;
2051 connection->server->lies--;
2053 if(object->cache_control & CACHE_MISMATCH)
2054 supersede = 1;
2056 if(code == 304 || code == 412) {
2057 if((object->etag && etag && strcmp(object->etag, etag) != 0) ||
2058 (object->last_modified >= 0 && last_modified >= 0 &&
2059 object->last_modified != last_modified)) {
2060 do_log(L_ERROR, "Inconsistent \"%s\" reply for ",
2061 code == 304 ? "not changed":"precondition failed");
2062 do_log_n(L_ERROR, object->key, object->key_size);
2063 do_log(L_ERROR, "\n");
2064 object->flags |= OBJECT_DYNAMIC;
2065 supersede = 1;
2067 } else if(!(object->flags & OBJECT_INITIAL)) {
2068 if((object->last_modified < 0 || last_modified < 0) &&
2069 (!object->etag || !etag))
2070 supersede = 1;
2071 else if(object->last_modified != last_modified)
2072 supersede = 1;
2073 else if(object->etag || etag) {
2074 /* We need to be permissive here so as to deal with some
2075 front-end proxies that discard ETags on partial
2076 replies but not on full replies. */
2077 if(etag && object->etag && strcmp(object->etag, etag) != 0)
2078 supersede = 1;
2079 else if(!object->etag)
2080 supersede = 1;
2083 if(!supersede && (object->cache_control & CACHE_VARY) &&
2084 dontTrustVaryETag >= 1) {
2085 /* Check content-type to work around mod_gzip bugs */
2086 if(!httpHeaderMatch(atomContentType, object->headers, headers) ||
2087 !httpHeaderMatch(atomContentEncoding, object->headers, headers))
2088 supersede = 1;
2091 if(full_len < 0 && te == TE_IDENTITY) {
2092 /* It's an HTTP/1.0 CGI. Be afraid. */
2093 if(expect_body && content_range.from < 0 && content_range.to < 0)
2094 supersede = 1;
2097 if(!supersede && object->length >= 0 && full_len >= 0 &&
2098 object->length != full_len) {
2099 do_log(L_WARN, "Inconsistent length.\n");
2100 supersede = 1;
2103 if(!supersede &&
2104 ((object->last_modified >= 0 && last_modified >= 0) ||
2105 (object->etag && etag))) {
2106 if(request->method == METHOD_CONDITIONAL_GET) {
2107 do_log(L_WARN, "Server ignored conditional request.\n");
2108 connection->server->lies += 10;
2109 /* Drop the connection? */
2112 } else if(code == 416) {
2113 do_log(L_ERROR, "Unexpected \"range not satisfiable\" reply\n");
2114 httpServerAbort(connection, 1, 502,
2115 internAtom("Unexpected \"range not satisfiable\" "
2116 "reply"));
2117 /* The object may be superseded. Make sure the next request
2118 won't be partial. */
2119 abortObject(object, 502,
2120 internAtom("Unexpected \"range not satisfiable\" reply"));
2121 return 1;
2124 if(object->flags & OBJECT_INITIAL)
2125 supersede = 0;
2127 if(supersede) {
2128 do_log(L_SUPERSEDED, "Superseding object: ");
2129 do_log_n(L_SUPERSEDED, old_object->key, old_object->key_size);
2130 do_log(L_SUPERSEDED, " (%d %d %d %s -> %d %d %d %s)\n",
2131 object->code, object->length, (int)object->last_modified,
2132 object->etag?object->etag: "(none)",
2133 code, full_len, (int)last_modified,
2134 etag?etag:"(none)");
2135 privatiseObject(old_object, 0);
2136 new_object = makeObject(object->type, object->key,
2137 object->key_size, 1, 0,
2138 object->request, NULL);
2139 if(new_object == NULL) {
2140 do_log(L_ERROR, "Couldn't allocate object\n");
2141 httpServerAbort(connection, 1, 500,
2142 internAtom("Couldn't allocate object"));
2143 return 1;
2145 if(urlIsLocal(new_object->key, new_object->key_size))
2146 new_object->flags |= OBJECT_LOCAL;
2147 } else {
2148 new_object = object;
2151 suspectDynamic =
2152 (!etag && last_modified < 0) ||
2153 (cache_control.flags &
2154 (CACHE_NO_HIDDEN | CACHE_NO | CACHE_NO_STORE |
2155 (cacheIsShared ? CACHE_PRIVATE : 0))) ||
2156 (cache_control.max_age >= 0 && cache_control.max_age <= 2) ||
2157 (cacheIsShared &&
2158 cache_control.s_maxage >= 0 && cache_control.s_maxage <= 5) ||
2159 (old_object->last_modified >= 0 && old_object->expires >= 0 &&
2160 (old_object->expires - old_object->last_modified <= 1)) ||
2161 (supersede && (old_object->date - date <= 5));
2163 if(suspectDynamic)
2164 new_object->flags |= OBJECT_DYNAMIC;
2165 else if(!supersede)
2166 new_object->flags &= ~OBJECT_DYNAMIC;
2167 else if(old_object->flags & OBJECT_DYNAMIC)
2168 new_object->flags |= OBJECT_DYNAMIC;
2170 new_object->age = age;
2171 new_object->cache_control |= cache_control.flags;
2172 new_object->max_age = cache_control.max_age;
2173 new_object->s_maxage = cache_control.s_maxage;
2174 new_object->flags &= ~OBJECT_FAILED;
2176 if(date >= 0)
2177 new_object->date = date;
2178 if(last_modified >= 0)
2179 new_object->last_modified = last_modified;
2180 if(expires >= 0)
2181 new_object->expires = expires;
2182 if(new_object->etag == NULL)
2183 new_object->etag = etag;
2184 else
2185 free(etag);
2187 switch(code) {
2188 case 200:
2189 case 300: case 301: case 302: case 303: case 307:
2190 case 403: case 404: case 405: case 401:
2191 if(new_object->message) releaseAtom(new_object->message);
2192 new_object->code = code;
2193 new_object->message = message;
2194 break;
2195 case 206: case 304: case 412:
2196 if(new_object->code != 200 || !new_object->message) {
2197 if(new_object->message) releaseAtom(new_object->message);
2198 new_object->code = 200;
2199 new_object->message = internAtom("OK");
2201 releaseAtom(message);
2202 break;
2203 default:
2204 if(new_object->message) releaseAtom(new_object->message);
2205 new_object->code = code;
2206 new_object->message = retainAtom(message);
2207 break;
2210 httpTweakCachability(new_object);
2212 if(!via)
2213 new_via = internAtomF("%s %s",
2214 version == HTTP_11 ? "1.1" : "1.0",
2215 proxyName->string);
2216 else
2217 new_via = internAtomF("%s, %s %s", via->string,
2218 version == HTTP_11 ? "1.1" : "1.0",
2219 proxyName->string);
2220 if(new_via == NULL) {
2221 do_log(L_ERROR, "Couldn't allocate Via.\n");
2222 } else {
2223 if(new_object->via) releaseAtom(new_object->via);
2224 new_object->via = new_via;
2227 if(new_object->flags & OBJECT_INITIAL) {
2228 objectPartial(new_object, full_len, headers);
2229 } else {
2230 if(new_object->length < 0)
2231 new_object->length = full_len;
2232 /* XXX -- RFC 2616 13.5.3 */
2233 releaseAtom(headers);
2236 if(supersede) {
2237 assert(new_object != old_object);
2238 supersedeObject(old_object);
2241 if(new_object != old_object) {
2242 if(new_object->flags & OBJECT_INPROGRESS) {
2243 /* Make sure we don't fetch this object two times at the
2244 same time. Just drop the connection. */
2245 releaseObject(new_object);
2246 httpServerFinish(connection, 1, 0);
2247 return 1;
2249 old_object->flags &= ~OBJECT_VALIDATING;
2250 new_object->flags |= OBJECT_INPROGRESS;
2251 /* Signal the client side to switch to the new object -- see
2252 httpClientGetHandler. If it doesn't, we'll give up on this
2253 request below. */
2254 new_object->flags |= OBJECT_MUTATING;
2255 request->can_mutate = new_object;
2256 notifyObject(old_object);
2257 request->can_mutate = NULL;
2258 new_object->flags &= ~OBJECT_MUTATING;
2259 old_object->flags &= ~OBJECT_INPROGRESS;
2260 if(request->object == old_object) {
2261 if(request->request)
2262 request->request->request = NULL;
2263 request->request = NULL;
2264 request->object = new_object;
2265 } else {
2266 assert(request->object == new_object);
2268 releaseNotifyObject(old_object);
2269 old_object = NULL;
2270 object = new_object;
2271 } else {
2272 objectMetadataChanged(new_object, 0);
2275 if(object->flags & OBJECT_VALIDATING) {
2276 object->flags &= ~OBJECT_VALIDATING;
2277 notifyObject(object);
2280 if(!expect_body) {
2281 httpServerFinish(connection, 0, rc);
2282 return 1;
2285 if(request->request == NULL) {
2286 httpServerFinish(connection, 1, 0);
2287 return 1;
2290 if(code == 412) {
2291 /* 412 replies contain a useless body. For now, we
2292 drop the connection. */
2293 httpServerFinish(connection, 1, 0);
2294 return 1;
2298 if(request->flags & REQUEST_PERSISTENT) {
2299 if(request->method != METHOD_HEAD &&
2300 connection->te == TE_IDENTITY && len < 0) {
2301 do_log(L_ERROR, "Persistent reply with no Content-Length\n");
2302 /* That's potentially dangerous, as we could start reading
2303 arbitrary data into the object. Unfortunately, some
2304 servers do that. */
2305 request->flags &= ~REQUEST_PERSISTENT;
2309 /* we're getting a body */
2310 if(content_range.from > 0)
2311 connection->offset = content_range.from;
2312 else
2313 connection->offset = 0;
2315 if(content_range.to >= 0)
2316 request->to = content_range.to;
2318 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2319 (unsigned long)connection, (unsigned long)object,
2320 connection->offset);
2322 if(connection->len > rc) {
2323 rc = connectionAddData(connection, rc);
2324 if(rc) {
2325 if(rc < 0) {
2326 if(rc == -2) {
2327 do_log(L_ERROR, "Couldn't parse chunk size.\n");
2328 httpServerAbort(connection, 1, 502,
2329 internAtom("Couldn't parse chunk size"));
2330 } else {
2331 do_log(L_ERROR, "Couldn't add data to connection.\n");
2332 httpServerAbort(connection, 1, 500,
2333 internAtom("Couldn't add data "
2334 "to connection"));
2336 return 1;
2337 } else {
2338 if(code != 206) {
2339 if(object->length < 0) {
2340 object->length = object->size;
2341 objectMetadataChanged(object, 0);
2342 } else if(object->length != object->size) {
2343 httpServerAbort(connection, 1, 500,
2344 internAtom("Inconsistent "
2345 "object size"));
2346 object->length = -1;
2347 return 1;
2350 httpServerFinish(connection, 0, 0);
2351 return 1;
2354 } else {
2355 connection->len = 0;
2358 if(eof) {
2359 if(connection->te == TE_CHUNKED ||
2360 (object->length >= 0 &&
2361 connection->offset < object->length)) {
2362 do_log(L_ERROR, "Server closed connection.\n");
2363 httpServerAbort(connection, 1, 502,
2364 internAtom("Server closed connection"));
2365 return 1;
2366 } else {
2367 if(code != 206 && eof > 0 && object->length < 0) {
2368 object->length = object->size;
2369 objectMetadataChanged(object, 0);
2371 httpServerFinish(connection, 1, 0);
2372 return 1;
2374 } else {
2375 return httpServerReadData(connection, 1);
2377 return 0;
2379 fail:
2380 releaseAtom(url);
2381 releaseAtom(message);
2382 if(headers)
2383 releaseAtom(headers);
2384 if(etag)
2385 free(etag);
2386 if(via)
2387 releaseAtom(via);
2388 return 1;
2392 httpServerIndirectHandlerCommon(HTTPConnectionPtr connection, int eof)
2394 HTTPRequestPtr request = connection->request;
2396 assert(eof >= 0);
2397 assert(request->object->flags & OBJECT_INPROGRESS);
2399 if(connection->len > 0) {
2400 int rc;
2401 rc = connectionAddData(connection, 0);
2402 if(rc) {
2403 if(rc < 0) {
2404 if(rc == -2) {
2405 do_log(L_ERROR, "Couldn't parse chunk size.\n");
2406 httpServerAbort(connection, 1, 502,
2407 internAtom("Couldn't parse chunk size"));
2408 } else {
2409 do_log(L_ERROR, "Couldn't add data to connection.\n");
2410 httpServerAbort(connection, 1, 500,
2411 internAtom("Couldn't add data "
2412 "to connection"));
2414 return 1;
2415 } else {
2416 if(request->to < 0) {
2417 if(request->object->length < 0) {
2418 request->object->length = request->object->size;
2419 objectMetadataChanged(request->object, 0);
2420 } else if(request->object->length !=
2421 request->object->size) {
2422 request->object->length = -1;
2423 httpServerAbort(connection, 1, 502,
2424 internAtom("Inconsistent "
2425 "object size"));
2426 return 1;
2429 httpServerFinish(connection, 0, 0);
2431 return 1;
2435 if(eof && connection->len == 0) {
2436 if(connection->te == TE_CHUNKED ||
2437 (request->to >= 0 && connection->offset < request->to)) {
2438 do_log(L_ERROR, "Server dropped connection.\n");
2439 httpServerAbort(connection, 1, 502,
2440 internAtom("Server dropped connection"));
2441 return 1;
2442 } else {
2443 if(request->object->length < 0 && eof > 0 &&
2444 (request->to < 0 || request->to > request->object->size)) {
2445 request->object->length = request->object->size;
2446 objectMetadataChanged(request->object, 0);
2448 httpServerFinish(connection, 1, 0);
2449 return 1;
2451 } else {
2452 return httpServerReadData(connection, 0);
2457 httpServerIndirectHandler(int status,
2458 FdEventHandlerPtr event,
2459 StreamRequestPtr srequest)
2461 HTTPConnectionPtr connection = srequest->data;
2462 assert(connection->request->object->flags & OBJECT_INPROGRESS);
2464 httpSetTimeout(connection, -1);
2465 if(status < 0) {
2466 if(status != -ECLIENTRESET)
2467 do_log_error(L_ERROR, -status, "Read from server failed");
2468 httpServerAbort(connection, status != -ECLIENTRESET, 502,
2469 internAtomError(-status, "Read from server failed"));
2470 return 1;
2473 connection->len = srequest->offset;
2475 return httpServerIndirectHandlerCommon(connection, status);
2479 httpServerReadData(HTTPConnectionPtr connection, int immediate)
2481 HTTPRequestPtr request = connection->request;
2482 ObjectPtr object = request->object;
2483 int to = -1;
2485 assert(object->flags & OBJECT_INPROGRESS);
2487 if(request->request == NULL) {
2488 httpServerFinish(connection, 1, 0);
2489 return 1;
2492 if(request->to >= 0)
2493 to = request->to;
2494 else
2495 to = object->length;
2497 if(to >= 0 && to == connection->offset) {
2498 httpServerFinish(connection, 0, 0);
2499 return 1;
2502 if(connection->len == 0 &&
2503 ((connection->te == TE_IDENTITY && to > connection->offset) ||
2504 (connection->te == TE_CHUNKED && connection->chunk_remaining > 0))) {
2505 /* Read directly into the object */
2506 int i = connection->offset / CHUNK_SIZE;
2507 int j = connection->offset % CHUNK_SIZE;
2508 int end, len, more;
2509 /* See httpServerDirectHandlerCommon if you change this */
2510 if(connection->te == TE_CHUNKED) {
2511 len = connection->chunk_remaining;
2512 /* The logic here is that we want more to just fit the
2513 chunk header if we're doing a large read, but do a
2514 large read if we would otherwise do a small one. The
2515 magic constant 2000 comes from the assumption that the
2516 server uses chunks that have a size that are a power of
2517 two (possibly including the chunk header), and that we
2518 want a full ethernet packet to fit into our read. */
2519 more = (len >= 2000 ? 20 : MIN(2048 - len, CHUNK_SIZE));
2520 } else {
2521 len = to - connection->offset;
2522 /* We read more data only when there is a reasonable
2523 chance of there being another reply coming. */
2524 more = (connection->pipelined > 1) ? CHUNK_SIZE : 0;
2526 end = len + connection->offset;
2528 httpConnectionDestroyBuf(connection);
2530 /* The order of allocation is important in case we run out of
2531 memory. */
2532 lockChunk(object, i);
2533 if(object->chunks[i].data == NULL)
2534 object->chunks[i].data = get_chunk();
2535 if(object->chunks[i].data && object->chunks[i].size >= j) {
2536 if(len + j > CHUNK_SIZE) {
2537 lockChunk(object, i + 1);
2538 if(object->chunks[i + 1].data == NULL)
2539 object->chunks[i + 1].data = get_chunk();
2540 /* Unless we're grabbing all len of data, we do not
2541 want to do an indirect read immediately afterwards. */
2542 if(more && len + j <= 2 * CHUNK_SIZE) {
2543 if(!connection->buf)
2544 connection->buf = get_chunk(); /* checked below */
2546 if(object->chunks[i + 1].data) {
2547 do_stream_3(IO_READ | IO_NOTNOW, connection->fd, j,
2548 object->chunks[i].data, CHUNK_SIZE,
2549 object->chunks[i + 1].data,
2550 MIN(CHUNK_SIZE,
2551 end - (i + 1) * CHUNK_SIZE),
2552 connection->buf, connection->buf ? more : 0,
2553 httpServerDirectHandler2, connection);
2554 return 1;
2556 unlockChunk(object, i + 1);
2558 if(more && len + j <= CHUNK_SIZE) {
2559 if(!connection->buf)
2560 connection->buf = get_chunk();
2562 do_stream_2(IO_READ | IO_NOTNOW, connection->fd, j,
2563 object->chunks[i].data,
2564 MIN(CHUNK_SIZE, end - i * CHUNK_SIZE),
2565 connection->buf, connection->buf ? more : 0,
2566 httpServerDirectHandler, connection);
2567 return 1;
2568 } else {
2569 unlockChunk(object, i);
2573 if(connection->len == 0)
2574 httpConnectionDestroyBuf(connection);
2576 httpSetTimeout(connection, serverTimeout);
2577 do_stream_buf(IO_READ | IO_NOTNOW |
2578 ((immediate && connection->len) ? IO_IMMEDIATE : 0),
2579 connection->fd, connection->len,
2580 &connection->buf,
2581 (connection->te == TE_CHUNKED ?
2582 MIN(2048, CHUNK_SIZE) : CHUNK_SIZE),
2583 httpServerIndirectHandler, connection);
2584 return 1;
2588 httpServerDirectHandlerCommon(int kind, int status,
2589 FdEventHandlerPtr event,
2590 StreamRequestPtr srequest)
2592 HTTPConnectionPtr connection = srequest->data;
2593 HTTPRequestPtr request = connection->request;
2594 ObjectPtr object = request->object;
2595 int i = connection->offset / CHUNK_SIZE;
2596 int to, end, end1;
2598 assert(request->object->flags & OBJECT_INPROGRESS);
2600 httpSetTimeout(connection, -1);
2602 if(status < 0) {
2603 unlockChunk(object, i);
2604 if(kind == 2) unlockChunk(object, i + 1);
2605 if(status != -ECLIENTRESET)
2606 do_log_error(L_ERROR, -status, "Read from server failed");
2607 httpServerAbort(connection, status != -ECLIENTRESET, 502,
2608 internAtomError(-status, "Read from server failed"));
2609 return 1;
2612 /* We have incestuous knowledge of the decisions made in
2613 httpServerReadData */
2614 if(request->to >= 0)
2615 to = request->to;
2616 else
2617 to = object->length;
2618 if(connection->te == TE_CHUNKED)
2619 end = connection->offset + connection->chunk_remaining;
2620 else
2621 end = to;
2622 /* The amount of data actually read into the object */
2623 end1 = MIN(end, i * CHUNK_SIZE + MIN(kind * CHUNK_SIZE, srequest->offset));
2625 assert(end >= 0 && end1 >= i * CHUNK_SIZE && end1 <= (i + 2) * CHUNK_SIZE);
2627 object->chunks[i].size =
2628 MAX(object->chunks[i].size, MIN(end1 - i * CHUNK_SIZE, CHUNK_SIZE));
2629 if(kind == 2 && end1 > (i + 1) * CHUNK_SIZE) {
2630 object->chunks[i + 1].size =
2631 MAX(object->chunks[i + 1].size, end1 - (i + 1) * CHUNK_SIZE);
2633 if(connection->te == TE_CHUNKED) {
2634 connection->chunk_remaining -= (end1 - connection->offset);
2635 assert(connection->chunk_remaining >= 0);
2637 connection->offset = end1;
2638 object->size = MAX(object->size, end1);
2639 unlockChunk(object, i);
2640 if(kind == 2) unlockChunk(object, i + 1);
2642 if(i * CHUNK_SIZE + srequest->offset > end1) {
2643 connection->len = i * CHUNK_SIZE + srequest->offset - end1;
2644 return httpServerIndirectHandlerCommon(connection, status);
2645 } else {
2646 notifyObject(object);
2647 if(status) {
2648 httpServerFinish(connection, 1, 0);
2649 return 1;
2650 } else {
2651 return httpServerReadData(connection, 0);
2657 httpServerDirectHandler(int status,
2658 FdEventHandlerPtr event,
2659 StreamRequestPtr srequest)
2661 return httpServerDirectHandlerCommon(1, status, event, srequest);
2665 httpServerDirectHandler2(int status,
2666 FdEventHandlerPtr event,
2667 StreamRequestPtr srequest)
2669 return httpServerDirectHandlerCommon(2, status, event, srequest);
2672 /* Add the data accumulated in connection->buf into the object in
2673 connection->request. Returns 0 in the normal case, 1 if the TE is
2674 self-terminating and we're done, -1 if there was a problem with
2675 objectAddData, -2 if there was a problem with the data. */
2677 connectionAddData(HTTPConnectionPtr connection, int skip)
2679 HTTPRequestPtr request = connection->request;
2680 ObjectPtr object = request->object;
2681 int rc;
2683 if(connection->te == TE_IDENTITY) {
2684 int len;
2686 len = connection->len - skip;
2687 if(object->length >= 0) {
2688 len = MIN(object->length - connection->offset, len);
2690 if(request->to >= 0)
2691 len = MIN(request->to - connection->offset, len);
2692 if(len > 0) {
2693 rc = objectAddData(object, connection->buf + skip,
2694 connection->offset, len);
2695 if(rc < 0)
2696 return -1;
2697 connection->offset += len;
2698 connection->len -= (len + skip);
2699 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2700 (unsigned long)connection, (unsigned long)object,
2701 connection->offset);
2704 if(connection->len > 0 && skip + len > 0) {
2705 memmove(connection->buf,
2706 connection->buf + skip + len, connection->len);
2709 if((object->length >= 0 && object->length <= connection->offset) ||
2710 (request->to >= 0 && request->to <= connection->offset)) {
2711 notifyObject(object);
2712 return 1;
2713 } else {
2714 if(len > 0)
2715 notifyObject(object);
2716 return 0;
2718 } else if(connection->te == TE_CHUNKED) {
2719 int i = skip, j, size;
2720 /* connection->chunk_remaining is 0 at the end of a chunk, -1
2721 after the CR/LF pair ending a chunk, and -2 after we've
2722 seen a chunk of length 0. */
2723 if(connection->chunk_remaining > -2) {
2724 while(1) {
2725 if(connection->chunk_remaining <= 0) {
2726 if(connection->chunk_remaining == 0) {
2727 if(connection->len < i + 2)
2728 break;
2729 if(connection->buf[i] != '\r' ||
2730 connection->buf[i + 1] != '\n')
2731 return -1;
2732 i += 2;
2733 connection->chunk_remaining = -1;
2735 if(connection->len < i + 2)
2736 break;
2737 j = parseChunkSize(connection->buf, i,
2738 connection->len, &size);
2739 if(j < 0)
2740 return -2;
2741 if(j == 0)
2742 break;
2743 else
2744 i = j;
2745 if(size == 0) {
2746 connection->chunk_remaining = -2;
2747 break;
2748 } else {
2749 connection->chunk_remaining = size;
2751 } else {
2752 /* connection->chunk_remaining > 0 */
2753 size = MIN(connection->chunk_remaining,
2754 connection->len - i);
2755 if(size <= 0)
2756 break;
2757 rc = objectAddData(object, connection->buf + i,
2758 connection->offset, size);
2759 connection->offset += size;
2760 if(rc < 0)
2761 return -1;
2762 i += size;
2763 connection->chunk_remaining -= size;
2764 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2765 (unsigned long)connection,
2766 (unsigned long)object,
2767 connection->offset);
2771 connection->len -= i;
2772 if(connection->len > 0)
2773 memmove(connection->buf, connection->buf + i, connection->len);
2774 if(i > 0 || connection->chunk_remaining == -2)
2775 notifyObject(object);
2776 if(connection->chunk_remaining == -2)
2777 return 1;
2778 else
2779 return 0;
2780 } else {
2781 abort();
2785 void
2786 listServers(FILE *out)
2788 HTTPServerPtr server;
2789 int i, n, m, entry;
2791 fprintf(out, "<!DOCTYPE HTML PUBLIC "
2792 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2793 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2794 "<html><head>\n"
2795 "\r\n<title>Known servers</title>\n"
2796 "</head><body>\n"
2797 "<h1>Known servers</h1>\n");
2799 alternatingHttpStyle(out, "servers");
2800 fprintf(out, "<table id=servers>\n");
2801 fprintf(out, "<thead><tr><th>Server</th>"
2802 "<th>Version</th>"
2803 "<th>Persistent</th>"
2804 "<th>Pipeline</th>"
2805 "<th>Connections</th>"
2806 "<th></th>"
2807 "<th>rtt</th>"
2808 "<th>rate</th>"
2809 "</tr></thead>\n");
2810 fprintf(out, "<tbody>\n");
2811 server = servers;
2812 entry = 0;
2813 while(server) {
2814 fprintf(out, "<tr class=\"%s\">", entry % 2 == 0 ? "even" : "odd");
2815 if(server->port == 80)
2816 fprintf(out, "<td>%s</td>", server->name);
2817 else
2818 fprintf(out, "<td>%s:%d</td>", server->name, server->port);
2820 if(server->version == HTTP_11)
2821 fprintf(out, "<td>1.1</td>");
2822 else if(server->version == HTTP_10)
2823 fprintf(out, "<td>1.0</td>");
2824 else
2825 fprintf(out, "<td>unknown</td>");
2827 if(server->persistent < 0)
2828 fprintf(out, "<td>no</td>");
2829 else if(server->persistent > 0)
2830 fprintf(out, "<td>yes</td>");
2831 else
2832 fprintf(out, "<td>unknown</td>");
2834 if(server->version != HTTP_11 || server->persistent <= 0)
2835 fprintf(out, "<td></td>");
2836 else if(server->pipeline < 0)
2837 fprintf(out, "<td>no</td>");
2838 else if(server->pipeline >= 0 && server->pipeline <= 1)
2839 fprintf(out, "<td>unknown</td>");
2840 else if(server->pipeline == 2 || server->pipeline == 3)
2841 fprintf(out, "<td>probing</td>");
2842 else
2843 fprintf(out, "<td>yes</td>");
2845 n = 0; m = 0;
2846 for(i = 0; i < server->maxslots; i++)
2847 if(server->connection[i] && !server->connection[i]->connecting) {
2848 if(i < server->numslots)
2849 n++;
2850 else
2851 m++;
2854 fprintf(out, "<td>%d/%d", n, server->numslots);
2855 if(m)
2856 fprintf(out, " + %d</td>", m);
2857 else
2858 fprintf(out, "</td>");
2860 if(server->lies > 0)
2861 fprintf(out, "<td>(%d lies)</td>", (server->lies + 9) / 10);
2862 else
2863 fprintf(out, "<td></td>");
2865 if(server->rtt > 0)
2866 fprintf(out, "<td>%.3f</td>", (double)server->rtt / 1000000.0);
2867 else
2868 fprintf(out, "<td></td>");
2869 if(server->rate > 0)
2870 fprintf(out, "<td>%d</td>", server->rate);
2871 else
2872 fprintf(out, "<td></td>");
2874 fprintf(out, "</tr>\n");
2875 server = server->next;
2876 entry++;
2878 fprintf(out, "</tbody>\n");
2879 fprintf(out, "</table>\n");
2880 fprintf(out, "<p><a href=\"/polipo/\">back</a></p>");
2881 fprintf(out, "</body></html>\n");