Doc update.
[polipo.git] / server.c
blob76f793e964bb2fa905be2f002ca612ba0f2defdb
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 scrub(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 scrub(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 scrub(connection->server->name),
622 connection->server->port);
623 connection->connecting = 0;
624 if(connection->server->request)
625 httpServerAbortRequest(connection->server->request,
626 status != -ECLIENTRESET, 504,
627 retainAtom(message));
628 httpServerAbort(connection, status != -ECLIENTRESET, 504, message);
629 return 1;
632 do_log(D_SERVER_CONN, "C %s:%d.\n",
633 scrub(connection->server->name), connection->server->port);
635 connection->connecting = 0;
636 /* serverTrigger will take care of inserting any timeouts */
637 httpServerTrigger(connection->server);
638 return 1;
642 httpServerIdleHandler(int a, FdEventHandlerPtr event)
644 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
645 HTTPServerPtr server = connection->server;
646 int i;
648 assert(!connection->request);
650 do_log(D_SERVER_CONN, "Idle connection to %s:%d died.\n",
651 scrub(connection->server->name), connection->server->port);
653 for(i = 0; i < server->maxslots; i++) {
654 if(connection == server->connection[i]) {
655 server->idleHandler[i] = NULL;
656 break;
659 assert(i < server->maxslots);
661 httpServerAbort(connection, 1, 504, internAtom("Timeout"));
662 return 1;
665 /* Discard aborted requests at the head of the queue. */
666 static void
667 httpServerDiscardRequests(HTTPServerPtr server)
669 HTTPRequestPtr request;
670 while(server->request && !server->request->request) {
671 request = server->request;
672 server->request = request->next;
673 request->next = NULL;
674 if(server->request == NULL)
675 server->request_last = NULL;
676 request->object->flags &= ~(OBJECT_INPROGRESS | OBJECT_VALIDATING);
677 releaseNotifyObject(request->object);
678 request->object = NULL;
679 httpDestroyRequest(request);
683 static int
684 pipelineIsSmall(HTTPConnectionPtr connection)
686 HTTPRequestPtr request = connection->request;
688 if(pipelineAdditionalRequests <= 0)
689 return 0;
690 else if(pipelineAdditionalRequests >= 2)
691 return 1;
693 if(!request)
694 return 1;
695 if(request->next || !(request->flags & REQUEST_PERSISTENT))
696 return 0;
697 if(request->method == METHOD_HEAD ||
698 request->method == METHOD_CONDITIONAL_GET)
699 return 1;
700 if(request->to >= 0 && connection->server->rate > 0 &&
701 request->to - request->from < connection->server->rate *
702 smallRequestTime)
703 return 1;
704 return 0;
707 static int
708 numRequests(HTTPServerPtr server)
710 int n = 0;
711 HTTPRequestPtr request = server->request;
712 while(request) {
713 n++;
714 request = request->next;
716 return n;
719 HTTPConnectionPtr
720 httpServerGetConnection(HTTPServerPtr server, int *idle_return)
722 int i, j;
723 int connecting = 0, empty = 0, idle = 0;
725 j = -1;
726 /* Try to find an idle connection */
727 for(i = 0; i < server->numslots; i++) {
728 if(server->connection[i]) {
729 if(!server->connection[i]->connecting) {
730 if(!server->connection[i]->request) {
731 if(server->idleHandler[i])
732 unregisterFdEvent(server->idleHandler[i]);
733 server->idleHandler[i] = NULL;
734 if(j < 0) j = i;
735 idle++;
737 } else
738 connecting++;
739 } else
740 empty++;
743 if(j >= 0) {
744 *idle_return = idle;
745 return server->connection[j];
748 /* If there's an empty slot, schedule connection creation */
749 if(empty) {
750 /* Don't open a connection if there are already enough in
751 progress, except if the server doesn't do persistent
752 connections and there's only one in progress. */
753 if((connecting == 0 || (server->persistent <= 0 && connecting <= 1)) ||
754 connecting < numRequests(server)) {
755 httpServerConnection(server);
759 /* Find a connection that can accept additional requests */
760 if(server->version == HTTP_11 && server->pipeline >= 4) {
761 for(i = 0; i < serverSlots; i++) {
762 if(server->connection[i] && !server->connection[i]->connecting &&
763 pipelineIsSmall(server->connection[i])) {
764 if(server->idleHandler[i])
765 unregisterFdEvent(server->idleHandler[i]);
766 server->idleHandler[i] = NULL;
767 *idle_return = 0;
768 return server->connection[i];
772 *idle_return = idle;
773 return NULL;
777 httpServerTrigger(HTTPServerPtr server)
779 HTTPConnectionPtr connection;
780 HTTPRequestPtr request;
781 int idle, n, i, rc, numidle;
783 while(server->request) {
784 httpServerDiscardRequests(server);
786 if(!server->request)
787 break;
789 if(REQUEST_SIDE(server->request)) {
790 rc = httpServerSideRequest(server);
791 /* If rc is 0, httpServerSideRequest didn't dequeue this
792 request. Go through the scheduling loop again, come
793 back later. */
794 if(rc <= 0) break;
795 continue;
797 connection = httpServerGetConnection(server, &numidle);
798 if(!connection) break;
800 /* If server->pipeline <= 0, we don't do pipelining. If
801 server->pipeline is 1, then we are ready to start probing
802 for pipelining on the server; we then send exactly two
803 requests in what is hopefully a single packet to check
804 whether the server has the nasty habit of discarding its
805 input buffers after each request.
806 If server->pipeline is 2 or 3, the pipelining probe is in
807 progress on this server, and we don't pipeline anything
808 until it succeeds. When server->pipeline >= 4, pipelining
809 is believed to work on this server. */
810 if(server->version != HTTP_11 || server->pipeline <= 0 ||
811 server->pipeline == 2 || server->pipeline == 3) {
812 if(connection->pipelined == 0)
813 n = 1;
814 else
815 n = 0;
816 } else if(server->pipeline == 1) {
817 if(connection->pipelined == 0)
818 n = MIN(2, maxPipelineTrain);
819 else
820 n = 0;
821 } else {
822 n = maxPipelineTrain;
825 /* Don't pipeline if there are more idle connections */
826 if(numidle >= 2)
827 n = MIN(n, 1);
829 idle = !connection->pipelined;
830 i = 0;
831 while(server->request && connection->pipelined < n) {
832 httpServerDiscardRequests(server);
833 if(!server->request) break;
834 request = server->request;
835 assert(request->request->request == request);
836 rc = httpWriteRequest(connection, request, -1);
837 if(rc < 0) {
838 if(i == 0)
839 httpServerAbortRequest(request, rc != -ECLIENTRESET, 502,
840 internAtom("Couldn't "
841 "write request"));
842 break;
844 do_log(D_SERVER_CONN, "W: ");
845 do_log_n(D_SERVER_CONN,
846 request->object->key, request->object->key_size);
847 do_log(D_SERVER_CONN, " (%d)\n", request->method);
848 if(connection->pipelined > 0)
849 request->flags |= REQUEST_PIPELINED;
850 request->time0 = current_time;
851 i++;
852 server->request = request->next;
853 request->next = NULL;
854 if(server->request == NULL)
855 server->request_last = NULL;
856 httpQueueRequest(connection, request);
857 connection->pipelined++;
859 if(server->persistent > 0 && server->pipeline == 1 && i >= 2)
860 server->pipeline = 2;
862 if(i > 0) httpServerSendRequest(connection);
864 if(idle && connection->pipelined > 0)
865 httpServerReply(connection, 0);
867 if(i == 0) break;
870 for(i = 0; i < server->maxslots; i++) {
871 if(server->connection[i] &&
872 !server->connection[i]->connecting &&
873 !server->connection[i]->request) {
874 /* Artificially age any fresh connections that aren't used
875 straight away; this is necessary for the logic for POST and
876 the logic that determines whether a given request should be
877 restarted. */
878 if(server->connection[i]->serviced == 0)
879 server->connection[i]->serviced = 1;
880 if(!server->idleHandler[i])
881 server->idleHandler[i] =
882 registerFdEvent(server->connection[i]->fd, POLLIN,
883 httpServerIdleHandler,
884 sizeof(HTTPConnectionPtr),
885 &server->connection[i]);
886 if(!server->idleHandler[i]) {
887 do_log(L_ERROR, "Couldn't register idle handler.\n");
888 httpServerFinish(server->connection[i], 1, 0);
890 httpSetTimeout(server->connection[i], serverIdleTimeout);
894 return 1;
898 httpServerSideRequest(HTTPServerPtr server)
900 HTTPRequestPtr request = server->request;
901 HTTPConnectionPtr connection;
902 HTTPRequestPtr requestor = request->request;
903 HTTPConnectionPtr client = requestor->connection;
904 int rc, i, freeslots, idle, connecting;
906 assert(REQUEST_SIDE(request));
908 connection = NULL;
909 freeslots = 0;
910 idle = -1;
911 connecting = 0;
913 /* Find a fresh connection */
914 for(i = 0; i < server->numslots; i++) {
915 if(!server->connection[i])
916 freeslots++;
917 else if(!server->connection[i]->connecting) {
918 if(!server->connection[i]->request) {
919 if(server->connection[i]->serviced == 0) {
920 if(server->idleHandler[i])
921 unregisterFdEvent(server->idleHandler[i]);
922 server->idleHandler[i] = NULL;
923 connection = server->connection[i];
924 break;
925 } else {
926 idle = i;
929 } else {
930 connecting++;
934 if(!connection) {
935 /* Make sure that a fresh connection will be established at some
936 point, then wait until httpServerTrigger calls us again. */
937 if(freeslots) {
938 httpServerConnection(server);
939 } else {
940 if(idle >= 0) {
941 /* Shutdown a random idle connection */
942 pokeFdEvent(server->connection[idle]->fd,
943 -EDOSHUTDOWN, POLLIN | POLLOUT);
946 return 0;
949 rc = httpWriteRequest(connection, request, client->bodylen);
950 if(rc < 0) {
951 do_log(L_ERROR, "Couldn't write POST or PUT request.\n");
952 httpServerAbortRequest(request, rc != -ECLIENTRESET, 502,
953 internAtom("Couldn't write request"));
954 return 0;
956 server->request = request->next;
957 request->next = NULL;
958 if(server->request == NULL)
959 server->request_last = NULL;
960 httpQueueRequest(connection, request);
961 connection->pipelined = 1;
962 request->time0 = current_time;
963 connection->reqoffset = 0;
964 connection->bodylen = client->bodylen;
965 httpServerDoSide(connection);
966 return 1;
969 int
970 httpServerDoSide(HTTPConnectionPtr connection)
972 HTTPRequestPtr request = connection->request;
973 HTTPRequestPtr requestor = request->request;
974 HTTPConnectionPtr client = requestor->connection;
975 int len = MIN(client->reqlen - client->reqbegin,
976 connection->bodylen - connection->reqoffset);
977 int doflush =
978 len > 0 &&
979 (len >= maxSideBuffering ||
980 client->reqbegin > 0 ||
981 (connection->reqoffset + client->reqlen - client->reqbegin) >=
982 connection->bodylen);
983 int done = connection->reqoffset >= connection->bodylen;
985 assert(connection->bodylen >= 0);
987 httpSetTimeout(connection, 60);
989 if(connection->reqlen > 0) {
990 /* Send the headers, but don't send any part of the body if
991 we're in wait_continue. */
992 do_stream_2(IO_WRITE,
993 connection->fd, 0,
994 connection->reqbuf, connection->reqlen,
995 client->reqbuf + client->reqbegin,
996 (request->flags & REQUEST_WAIT_CONTINUE) ? 0 : len,
997 httpServerSideHandler2, connection);
998 httpServerReply(connection, 0);
999 } else if(request->object->flags & OBJECT_ABORTED) {
1000 if(connection->reqbuf)
1001 dispose_chunk(connection->reqbuf);
1002 connection->reqbuf = NULL;
1003 connection->reqlen = 0;
1004 pokeFdEvent(connection->fd, -ESHUTDOWN, POLLIN);
1005 if(client->flags & CONN_READER) {
1006 client->flags |= CONN_SIDE_READER;
1007 do_stream(IO_READ | IO_IMMEDIATE | IO_NOTNOW,
1008 client->fd, 0, NULL, 0,
1009 httpClientSideHandler, client);
1011 } else if(!(request->flags & REQUEST_WAIT_CONTINUE) && doflush) {
1012 /* Make sure there's a reqbuf, as httpServerFinish uses
1013 it to determine if there's a writer. */
1014 if(connection->reqbuf == NULL)
1015 connection->reqbuf = get_chunk();
1016 assert(connection->reqbuf != NULL);
1017 do_stream(IO_WRITE,
1018 connection->fd, 0,
1019 client->reqbuf + client->reqbegin, len,
1020 httpServerSideHandler, connection);
1021 } else {
1022 if(connection->reqbuf) {
1023 httpConnectionDestroyReqbuf(connection);
1024 connection->reqlen = 0;
1026 if(request->flags & REQUEST_WAIT_CONTINUE) {
1027 ConditionHandlerPtr chandler;
1028 do_log(D_SERVER_CONN, "W... %s:%d.\n",
1029 scrub(connection->server->name), connection->server->port);
1030 chandler =
1031 conditionWait(&request->object->condition,
1032 httpServerContinueConditionHandler,
1033 sizeof(connection), &connection);
1034 if(chandler)
1035 return 1;
1036 else
1037 do_log(L_ERROR, "Couldn't register condition handler.\n");
1038 /* Fall through -- the client side will clean up. */
1040 client->flags |= CONN_SIDE_READER;
1041 do_stream(IO_READ | (done ? IO_IMMEDIATE : 0 ) | IO_NOTNOW,
1042 client->fd, client->reqlen,
1043 client->reqbuf, CHUNK_SIZE,
1044 httpClientSideHandler, client);
1046 return 1;
1049 static int
1050 httpClientDelayedDoSideHandler(TimeEventHandlerPtr event)
1052 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1053 httpServerDoSide(connection);
1054 return 1;
1057 static int
1058 httpServerDelayedDoSide(HTTPConnectionPtr connection)
1060 TimeEventHandlerPtr handler;
1061 handler = scheduleTimeEvent(1, httpClientDelayedDoSideHandler,
1062 sizeof(connection), &connection);
1063 if(!handler) {
1064 do_log(L_ERROR, "Couldn't schedule DoSide -- freeing memory.\n");
1065 free_chunk_arenas();
1066 handler = scheduleTimeEvent(1, httpClientDelayedDoSideHandler,
1067 sizeof(connection), &connection);
1068 do_log(L_ERROR, "Couldn't schedule DoSide.\n");
1069 /* Somebody will hopefully end up timing out. */
1070 return 1;
1072 return 1;
1075 static int
1076 httpServerSideHandlerCommon(int kind, int status,
1077 FdEventHandlerPtr event,
1078 StreamRequestPtr srequest)
1080 HTTPConnectionPtr connection = srequest->data;
1081 HTTPRequestPtr request = connection->request;
1082 HTTPRequestPtr requestor = request->request;
1083 HTTPConnectionPtr client = requestor->connection;
1084 int bodylen;
1086 assert(request->object->flags & OBJECT_INPROGRESS);
1088 if(status) {
1089 do_log_error(L_ERROR, -status, "Couldn't write to server");
1090 httpConnectionDestroyReqbuf(connection);
1091 if(status != -ECLIENTRESET)
1092 shutdown(connection->fd, 2);
1093 abortObject(request->object, 502,
1094 internAtom("Couldn't write to server"));
1095 /* Let the read side handle the error */
1096 httpServerDoSide(connection);
1097 return 1;
1100 assert(srequest->offset > 0);
1102 if(kind == 2) {
1103 if(srequest->offset < connection->reqlen)
1104 return 0;
1105 bodylen = srequest->offset - connection->reqlen;
1106 connection->reqlen = 0;
1107 httpConnectionDestroyReqbuf(connection);
1108 } else {
1109 bodylen = srequest->offset;
1113 assert(client->reqbegin + bodylen <= client->reqlen);
1115 if(client->reqlen > client->reqbegin + bodylen)
1116 memmove(client->reqbuf, client->reqbuf + client->reqbegin + bodylen,
1117 client->reqlen - client->reqbegin - bodylen);
1118 client->reqlen -= bodylen + client->reqbegin;
1119 client->reqbegin = 0;
1120 connection->reqoffset += bodylen;
1121 httpServerDoSide(connection);
1122 return 1;
1126 httpServerSideHandler(int status,
1127 FdEventHandlerPtr event,
1128 StreamRequestPtr srequest)
1130 return httpServerSideHandlerCommon(1, status, event, srequest);
1134 httpServerSideHandler2(int status,
1135 FdEventHandlerPtr event,
1136 StreamRequestPtr srequest)
1138 return httpServerSideHandlerCommon(2, status, event, srequest);
1141 static int
1142 httpServerContinueConditionHandler(int status, ConditionHandlerPtr chandler)
1144 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)chandler->data;
1146 if(connection->request->flags & REQUEST_WAIT_CONTINUE)
1147 return 0;
1148 httpServerDelayedDoSide(connection);
1149 return 1;
1152 /* s is 0 to keep the connection alive, 1 to shutdown the connection */
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 && (!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(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 scrub(connection->server->name), connection->server->port, s);
1231 assert(offset <= connection->len);
1233 if(!s) {
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 scrub(server->name), server->port,
1241 connection->len - offset);
1242 s = 1;
1243 } else {
1244 memmove(connection->buf, connection->buf + offset,
1245 connection->len - offset);
1246 connection->len = connection->len - offset;
1247 if((connection->flags & CONN_BIGBUF) &&
1248 connection->len <= CHUNK_SIZE)
1249 httpConnectionUnbigify(connection);
1251 } else {
1252 connection->len = 0;
1256 connection->server->time = current_time.tv_sec;
1257 connection->serviced++;
1259 if(s) {
1260 if(connection->timeout)
1261 cancelTimeEvent(connection->timeout);
1262 connection->timeout = NULL;
1263 httpConnectionDestroyBuf(connection);
1264 if(connection->fd >= 0)
1265 CLOSE(connection->fd);
1266 connection->fd = -1;
1267 server->persistent -= 1;
1268 if(server->persistent < -5)
1269 server->numslots = MIN(server->maxslots, serverMaxSlots);
1270 if(connection->request) {
1271 HTTPRequestPtr req;
1272 do_log(D_SERVER_CONN, "Restarting pipeline to %s:%d.\n",
1273 scrub(server->name), server->port);
1274 if(server->pipeline == 2)
1275 server->pipeline -= 20;
1276 else
1277 server->pipeline -= 5;
1278 req = connection->request;
1279 while(req) {
1280 req->connection = NULL;
1281 req = req->next;
1283 if(server->request)
1284 connection->request_last->next = server->request;
1285 else
1286 server->request_last = connection->request_last;
1287 server->request = connection->request;
1288 connection->request = NULL;
1289 connection->request_last = NULL;
1291 /* Make sure we don't get confused into thinking a probe
1292 is in progress. */
1293 if(server->pipeline == 2 || server->pipeline == 3)
1294 server->pipeline = 1;
1295 for(i = 0; i < server->maxslots; i++)
1296 if(connection == server->connection[i])
1297 break;
1298 assert(i < server->maxslots);
1299 if(server->idleHandler[i])
1300 unregisterFdEvent(server->idleHandler[i]);
1301 server->idleHandler[i] = NULL;
1302 server->connection[i] = NULL;
1303 free(connection);
1304 } else {
1305 server->persistent += 1;
1306 if(server->persistent > 0)
1307 server->numslots = MIN(server->maxslots,
1308 server->version == HTTP_10 ?
1309 serverSlots1 : serverSlots);
1310 httpSetTimeout(connection, serverTimeout);
1311 /* See httpServerTrigger */
1312 if(connection->pipelined ||
1313 (server->version == HTTP_11 && server->pipeline <= 0) ||
1314 (server->pipeline == 3)) {
1315 server->pipeline++;
1317 if(connection->pipelined) {
1318 httpServerReply(connection, 1);
1319 } else {
1320 httpConnectionDestroyBuf(connection);
1324 done:
1325 httpServerTrigger(server);
1328 static int
1329 httpServerDelayedFinishHandler(TimeEventHandlerPtr event)
1331 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1332 httpServerFinish(connection, 1, 0);
1333 return 1;
1336 static void
1337 httpServerDelayedFinish(HTTPConnectionPtr connection)
1339 TimeEventHandlerPtr handler;
1341 handler = scheduleTimeEvent(1, httpServerDelayedFinishHandler,
1342 sizeof(connection), &connection);
1343 if(!handler) {
1344 do_log(L_ERROR,
1345 "Couldn't schedule delayed finish -- freeing memory.");
1346 free_chunk_arenas();
1347 handler = scheduleTimeEvent(1, httpServerDelayedFinishHandler,
1348 sizeof(connection), &connection);
1349 if(!handler) {
1350 do_log(L_ERROR,
1351 "Couldn't schedule delayed finish -- aborting.\n");
1352 polipoExit();
1357 void
1358 httpServerReply(HTTPConnectionPtr connection, int immediate)
1360 assert(connection->pipelined > 0);
1362 if(connection->request->request == NULL) {
1363 do_log(L_WARN, "Aborting pipeline on %s:%d.\n",
1364 scrub(connection->server->name), connection->server->port);
1365 httpServerFinish(connection, 1, 0);
1366 return;
1369 do_log(D_SERVER_CONN, "R: %s (%d)\n",
1370 scrub(connection->request->object->key),
1371 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 scrub(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 scrub(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 %s\n", scrub(object->key));
1481 abortObject(object, 400, internAtom("Couldn't parse URL"));
1482 notifyObject(object);
1483 return 1;
1486 if(!intListMember(port, allowedPorts)) {
1487 do_log(L_ERROR, "Attempted connection to port %d.\n", port);
1488 abortObject(object, 403, internAtom("Forbidden port"));
1489 notifyObject(object);
1490 return 1;
1493 memcpy(name, ((char*)object->key) + x, y - x);
1494 name[y - x] = '\0';
1496 requestor->flags |= REQUEST_REQUESTED;
1497 rc = httpMakeServerRequest(name, port, object, method, from, to,
1498 requestor);
1500 if(rc < 0) {
1501 abortObject(object,
1502 503, internAtom("Couldn't schedule server request"));
1503 notifyObject(object);
1504 return 1;
1507 return 1;
1511 httpWriteRequest(HTTPConnectionPtr connection, HTTPRequestPtr request,
1512 int bodylen)
1514 ObjectPtr object = request->object;
1515 int from = request->from, to = request->to, method = request->method;
1516 char *url = object->key, *m;
1517 int url_size = object->key_size;
1518 int x, y, port, z, location_size;
1519 char *location;
1520 int l, n, rc, bufsize;
1522 assert(method != METHOD_NONE);
1524 if(request->method == METHOD_GET ||
1525 request->method == METHOD_CONDITIONAL_GET) {
1526 if(to >= 0) {
1527 assert(to >= from);
1528 if(to == from) {
1529 do_log(L_ERROR, "Requesting empty segment?\n");
1530 return -1;
1534 if(object->flags & OBJECT_DYNAMIC) {
1535 from = 0;
1536 to = -1;
1537 } else {
1538 objectFillFromDisk(object, from / CHUNK_SIZE * CHUNK_SIZE, 1);
1539 l = objectHoleSize(request->object, from);
1540 if(l > 0) {
1541 if(to <= 0 || to > from + l)
1542 to = from + l;
1545 if(pmmSize && connection->server->pipeline >= 4) {
1546 if(from == 0)
1547 to = to < 0 ? pmmFirstSize : MIN(to, pmmFirstSize);
1548 else
1549 to = to < 0 ? from + pmmSize : MIN(to, from + pmmSize);
1552 if(from % CHUNK_SIZE != 0)
1553 if(objectHoleSize(object, from - 1) != 0)
1554 from = from / CHUNK_SIZE * CHUNK_SIZE;
1558 rc = parseUrl(url, url_size, &x, &y, &port, &z);
1560 if(rc < 0 || x < 0 || y < 0) {
1561 return -1;
1564 if(connection->reqbuf == NULL) {
1565 connection->reqbuf = get_chunk();
1566 if(connection->reqbuf == NULL)
1567 return -1;
1568 connection->reqlen = 0;
1571 if(method == METHOD_CONDITIONAL_GET &&
1572 object->last_modified < 0 && object->etag == NULL)
1573 method = request->method = METHOD_GET;
1575 again:
1576 bufsize =
1577 (connection->flags & CONN_BIGREQBUF) ? bigBufferSize : CHUNK_SIZE;
1578 n = connection->reqlen;
1579 switch(method) {
1580 case METHOD_GET:
1581 case METHOD_CONDITIONAL_GET: m = "GET"; break;
1582 case METHOD_HEAD: m = "HEAD"; break;
1583 case METHOD_POST: m = "POST"; break;
1584 case METHOD_PUT: m = "PUT"; break;
1585 default: abort();
1587 n = snnprintf(connection->reqbuf, n, bufsize, "%s ", m);
1589 if(connection->server->isProxy) {
1590 n = snnprint_n(connection->reqbuf, n, bufsize,
1591 url, url_size);
1592 } else {
1593 if(url_size - z == 0) {
1594 location = "/";
1595 location_size = 1;
1596 } else {
1597 location = url + z;
1598 location_size = url_size - z;
1601 n = snnprint_n(connection->reqbuf, n, bufsize,
1602 location, location_size);
1605 do_log(D_SERVER_REQ, "Server request: ");
1606 do_log_n(D_SERVER_REQ, url + x, y - x);
1607 do_log(D_SERVER_REQ, ": ");
1608 do_log_n(D_SERVER_REQ, connection->reqbuf, n);
1609 do_log(D_SERVER_REQ, " (method %d from %d to %d, 0x%lx for 0x%lx)\n",
1610 method, from, to,
1611 (unsigned long)connection, (unsigned long)object);
1613 n = snnprintf(connection->reqbuf, n, bufsize, " HTTP/1.1");
1615 n = snnprintf(connection->reqbuf, n, bufsize, "\r\nHost: ");
1616 n = snnprint_n(connection->reqbuf, n, bufsize, url + x, y - x);
1617 if(port != 80)
1618 n = snnprintf(connection->reqbuf, n, bufsize, ":%d", port);
1620 if(connection->server->isProxy && parentAuthCredentials) {
1621 n = buildServerAuthHeaders(connection->reqbuf, n, bufsize,
1622 parentAuthCredentials);
1625 if(bodylen >= 0)
1626 n = snnprintf(connection->reqbuf, n, bufsize,
1627 "\r\nContent-Length: %d", bodylen);
1629 if(request->flags & REQUEST_WAIT_CONTINUE)
1630 n = snnprintf(connection->reqbuf, n, bufsize,
1631 "\r\nExpect: 100-continue");
1633 if(method != METHOD_HEAD && (from > 0 || to >= 0)) {
1634 if(to >= 0) {
1635 n = snnprintf(connection->reqbuf, n, bufsize,
1636 "\r\nRange: bytes=%d-%d", from, to - 1);
1637 } else {
1638 n = snnprintf(connection->reqbuf, n, bufsize,
1639 "\r\nRange: bytes=%d-", from);
1643 if(method == METHOD_GET && object->etag && (from > 0 || to >= 0)) {
1644 if(request->request && request->request->request == request &&
1645 request->request->from == 0 && request->request->to == -1 &&
1646 pmmSize == 0 && pmmFirstSize == 0)
1647 n = snnprintf(connection->reqbuf, n, bufsize,
1648 "\r\nIf-Range: \"%s\"", object->etag);
1651 if(method == METHOD_CONDITIONAL_GET) {
1652 if(object->last_modified >= 0) {
1653 n = snnprintf(connection->reqbuf, n, bufsize,
1654 "\r\nIf-Modified-Since: ");
1655 n = format_time(connection->reqbuf, n, bufsize,
1656 object->last_modified);
1658 if(object->etag) {
1659 n = snnprintf(connection->reqbuf, n, bufsize,
1660 "\r\nIf-None-Match: \"%s\"", object->etag);
1664 n = httpPrintCacheControl(connection->reqbuf, n, bufsize,
1665 0, &request->cache_control);
1666 if(n < 0)
1667 goto fail;
1669 if(request->request && request->request->headers) {
1670 n = snnprint_n(connection->reqbuf, n, bufsize,
1671 request->request->headers->string,
1672 request->request->headers->length);
1674 if(!disableVia) {
1675 if(request->request && request->request->via) {
1676 n = snnprintf(connection->reqbuf, n, bufsize,
1677 "\r\nVia: %s, 1.1 %s",
1678 request->request->via->string, proxyName->string);
1679 } else {
1680 n = snnprintf(connection->reqbuf, n, bufsize,
1681 "\r\nVia: 1.1 %s",
1682 proxyName->string);
1686 n = snnprintf(connection->reqbuf, n, bufsize,
1687 "\r\nConnection: %s\r\n\r\n",
1688 (request->flags & REQUEST_PERSISTENT) ?
1689 "keep-alive" : "close");
1690 if(n < 0 || n >= bufsize - 1)
1691 goto fail;
1692 connection->reqlen = n;
1693 return n;
1695 fail:
1696 rc = 0;
1697 if(!(connection->flags & CONN_BIGREQBUF))
1698 rc = httpConnectionBigifyReqbuf(connection);
1699 if(rc == 1)
1700 goto again;
1701 return -1;
1705 httpServerHandler(int status,
1706 FdEventHandlerPtr event,
1707 StreamRequestPtr srequest)
1709 HTTPConnectionPtr connection = srequest->data;
1710 AtomPtr message;
1712 assert(connection->request->object->flags & OBJECT_INPROGRESS);
1714 if(connection->reqlen == 0) {
1715 do_log(D_SERVER_REQ, "Writing aborted on 0x%lx\n",
1716 (unsigned long)connection);
1717 goto fail;
1720 if(status == 0 && !streamRequestDone(srequest)) {
1721 httpSetTimeout(connection, serverTimeout);
1722 return 0;
1725 httpConnectionDestroyReqbuf(connection);
1727 if(status) {
1728 if(connection->serviced >= 1) {
1729 httpServerRestart(connection);
1730 return 1;
1732 if(status >= 0 || status == ECONNRESET) {
1733 message = internAtom("Couldn't send request to server: "
1734 "short write");
1735 } else {
1736 if(status != -EPIPE)
1737 do_log_error(L_ERROR, -status,
1738 "Couldn't send request to server");
1739 message =
1740 internAtomError(-status, "Couldn't send request to server");
1742 goto fail;
1745 return 1;
1747 fail:
1748 httpConnectionDestroyReqbuf(connection);
1749 shutdown(connection->fd, 2);
1750 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLIN);
1751 httpSetTimeout(connection, 60);
1752 return 1;
1756 httpServerSendRequest(HTTPConnectionPtr connection)
1758 assert(connection->server);
1760 if(connection->reqlen == 0) {
1761 do_log(D_SERVER_REQ,
1762 "Writing aborted on 0x%lx\n", (unsigned long)connection);
1763 httpConnectionDestroyReqbuf(connection);
1764 shutdown(connection->fd, 2);
1765 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLIN | POLLOUT);
1766 return -1;
1769 httpSetTimeout(connection, serverTimeout);
1770 do_stream(IO_WRITE, connection->fd, 0,
1771 connection->reqbuf, connection->reqlen,
1772 httpServerHandler, connection);
1773 return 1;
1777 httpServerReplyHandler(int status,
1778 FdEventHandlerPtr event,
1779 StreamRequestPtr srequest)
1781 HTTPConnectionPtr connection = srequest->data;
1782 HTTPRequestPtr request = connection->request;
1783 int i, body;
1784 int bufsize =
1785 (connection->flags & CONN_BIGBUF) ? bigBufferSize : CHUNK_SIZE;
1787 assert(request->object->flags & OBJECT_INPROGRESS);
1788 if(status < 0) {
1789 if(connection->serviced >= 1) {
1790 httpServerRestart(connection);
1791 return 1;
1793 if(status != -ECLIENTRESET)
1794 do_log_error(L_ERROR, -status, "Read from server failed");
1795 httpServerAbort(connection, status != -ECLIENTRESET, 502,
1796 internAtomError(-status, "Read from server failed"));
1797 return 1;
1800 i = findEndOfHeaders(connection->buf, 0, srequest->offset, &body);
1801 connection->len = srequest->offset;
1803 if(i >= 0) {
1804 request->time1 = current_time;
1805 return httpServerHandlerHeaders(status, event, srequest, connection);
1808 if(status) {
1809 if(connection->serviced >= 1) {
1810 httpServerRestart(connection);
1811 return 1;
1813 if(status < 0) {
1814 do_log(L_ERROR,
1815 "Error reading server headers: %d\n", -status);
1816 httpServerAbort(connection, status != -ECLIENTRESET, 502,
1817 internAtomError(-status,
1818 "Error reading server headers"));
1819 } else
1820 httpServerAbort(connection, 1, 502,
1821 internAtom("Server dropped connection"));
1822 return 1;
1825 if(connection->len >= bufsize) {
1826 int rc = 0;
1827 if(!(connection->flags & CONN_BIGBUF))
1828 rc = httpConnectionBigify(connection);
1829 if(rc == 0) {
1830 do_log(L_ERROR, "Couldn't find end of server's headers.\n");
1831 httpServerAbort(connection, 1, 502,
1832 internAtom("Couldn't find end "
1833 "of server's headers"));
1834 return 1;
1835 } else if(rc < 0) {
1836 do_log(L_ERROR, "Couldn't allocate big buffer.\n");
1837 httpServerAbort(connection, 1, 500,
1838 internAtom("Couldn't allocate big buffer"));
1839 return 1;
1841 /* Can't just return 0 -- buf has moved. */
1842 do_stream(IO_READ,
1843 connection->fd, connection->len,
1844 connection->buf, bigBufferSize,
1845 httpServerReplyHandler, connection);
1846 return 1;
1849 return 0;
1853 httpServerHandlerHeaders(int eof,
1854 FdEventHandlerPtr event,
1855 StreamRequestPtr srequest,
1856 HTTPConnectionPtr connection)
1858 HTTPRequestPtr request = connection->request;
1859 ObjectPtr object = request->object;
1860 int rc;
1861 int code, version;
1862 int full_len;
1863 AtomPtr headers;
1864 int len;
1865 int te;
1866 CacheControlRec cache_control;
1867 int age = -1;
1868 time_t date, last_modified, expires;
1869 struct timeval *init_time;
1870 char *etag;
1871 AtomPtr via, new_via;
1872 int expect_body;
1873 HTTPRangeRec content_range;
1874 ObjectPtr new_object = NULL, old_object = NULL;
1875 int supersede = 0;
1876 AtomPtr message = NULL;
1877 int suspectDynamic;
1878 AtomPtr url = NULL;
1880 assert(request->object->flags & OBJECT_INPROGRESS);
1881 assert(eof >= 0);
1883 httpSetTimeout(connection, -1);
1885 if(request->flags & REQUEST_WAIT_CONTINUE) {
1886 do_log(D_SERVER_CONN, "W %s:%d.\n",
1887 connection->server->name, connection->server->port);
1888 request->flags &= ~REQUEST_WAIT_CONTINUE;
1891 rc = httpParseServerFirstLine(connection->buf, &code, &version, &message);
1892 if(rc <= 0) {
1893 do_log(L_ERROR, "Couldn't parse server status line.\n");
1894 httpServerAbort(connection, 1, 502,
1895 internAtom("Couldn't parse server status line"));
1896 return 1;
1899 do_log(D_SERVER_REQ, "Server status: ");
1900 do_log_n(D_SERVER_REQ, connection->buf,
1901 connection->buf[rc - 1] == '\r' ? rc - 2 : rc - 2);
1902 do_log(D_SERVER_REQ, " (0x%lx for 0x%lx)\n",
1903 (unsigned long)connection, (unsigned long)object);
1905 if(version != HTTP_10 && version != HTTP_11) {
1906 do_log(L_ERROR, "Unknown server HTTP version\n");
1907 httpServerAbort(connection, 1, 502,
1908 internAtom("Unknown server HTTP version"));
1909 releaseAtom(message);
1910 return 1;
1913 connection->version = version;
1914 connection->server->version = version;
1915 request->flags |= REQUEST_PERSISTENT;
1917 url = internAtomN(object->key, object->key_size);
1918 rc = httpParseHeaders(0, url, connection->buf, rc, request,
1919 &headers, &len, &cache_control, NULL, &te,
1920 &date, &last_modified, &expires, NULL, NULL, NULL,
1921 &age, &etag, NULL, NULL, &content_range,
1922 NULL, &via, NULL);
1923 if(rc < 0) {
1924 do_log(L_ERROR, "Couldn't parse server headers\n");
1925 releaseAtom(url);
1926 releaseAtom(message);
1927 httpServerAbort(connection, 1, 502,
1928 internAtom("Couldn't parse server headers"));
1929 return 1;
1932 if(date < 0)
1933 date = current_time.tv_sec;
1935 if(code == 100) {
1936 releaseAtom(url);
1937 releaseAtom(message);
1938 /* We've already reset wait_continue above, but we must still
1939 ensure that the writer notices. */
1940 notifyObject(request->object);
1941 connection->len -= rc;
1942 if(connection->len > 0)
1943 memmove(connection->buf, connection->buf + rc, connection->len);
1944 httpServerReply(connection, 1);
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 %s (%d)\n",
2028 scrub(object->key), cache_control.flags);
2031 if(request->time0.tv_sec != null_time.tv_sec)
2032 init_time = &request->time0;
2033 else
2034 init_time = &current_time;
2035 age = MIN(init_time->tv_sec - age, init_time->tv_sec);
2037 if(request->method == METHOD_HEAD ||
2038 code < 200 || code == 204 || code == 304)
2039 expect_body = 0;
2040 else if(te == TE_IDENTITY)
2041 expect_body = (len != 0);
2042 else
2043 expect_body = 1;
2045 connection->chunk_remaining = -1;
2046 connection->te = te;
2048 old_object = object;
2050 connection->server->lies--;
2052 if(object->cache_control & CACHE_MISMATCH)
2053 supersede = 1;
2055 if(code == 304 || code == 412) {
2056 if((object->etag && etag && strcmp(object->etag, etag) != 0) ||
2057 (object->last_modified >= 0 && last_modified >= 0 &&
2058 object->last_modified != last_modified)) {
2059 do_log(L_ERROR, "Inconsistent \"%s\" reply for %s\n",
2060 code == 304 ? "not changed":"precondition failed",
2061 scrub(object->key));
2062 object->flags |= OBJECT_DYNAMIC;
2063 supersede = 1;
2065 } else if(!(object->flags & OBJECT_INITIAL)) {
2066 if((object->last_modified < 0 || last_modified < 0) &&
2067 (!object->etag || !etag))
2068 supersede = 1;
2069 else if(object->last_modified != last_modified)
2070 supersede = 1;
2071 else if(object->etag || etag) {
2072 /* We need to be permissive here so as to deal with some
2073 front-end proxies that discard ETags on partial
2074 replies but not on full replies. */
2075 if(etag && object->etag && strcmp(object->etag, etag) != 0)
2076 supersede = 1;
2077 else if(!object->etag)
2078 supersede = 1;
2081 if(!supersede && (object->cache_control & CACHE_VARY) &&
2082 dontTrustVaryETag >= 1) {
2083 /* Check content-type to work around mod_gzip bugs */
2084 if(!httpHeaderMatch(atomContentType, object->headers, headers) ||
2085 !httpHeaderMatch(atomContentEncoding, object->headers, headers))
2086 supersede = 1;
2089 if(full_len < 0 && te == TE_IDENTITY) {
2090 /* It's an HTTP/1.0 CGI. Be afraid. */
2091 if(expect_body && content_range.from < 0 && content_range.to < 0)
2092 supersede = 1;
2095 if(!supersede && object->length >= 0 && full_len >= 0 &&
2096 object->length != full_len) {
2097 do_log(L_WARN, "Inconsistent length.\n");
2098 supersede = 1;
2101 if(!supersede &&
2102 ((object->last_modified >= 0 && last_modified >= 0) ||
2103 (object->etag && etag))) {
2104 if(request->method == METHOD_CONDITIONAL_GET) {
2105 do_log(L_WARN, "Server ignored conditional request.\n");
2106 connection->server->lies += 10;
2107 /* Drop the connection? */
2110 } else if(code == 416) {
2111 do_log(L_ERROR, "Unexpected \"range not satisfiable\" reply\n");
2112 httpServerAbort(connection, 1, 502,
2113 internAtom("Unexpected \"range not satisfiable\" "
2114 "reply"));
2115 /* The object may be superseded. Make sure the next request
2116 won't be partial. */
2117 abortObject(object, 502,
2118 internAtom("Unexpected \"range not satisfiable\" reply"));
2119 return 1;
2122 if(object->flags & OBJECT_INITIAL)
2123 supersede = 0;
2125 if(supersede) {
2126 do_log(L_SUPERSEDED,
2127 "Superseding object %s (%d %d %d %s -> %d %d %d %s)\n",
2128 scrub(old_object->key),
2129 object->code, object->length, (int)object->last_modified,
2130 object->etag ? object->etag : "(none)",
2131 code, full_len, (int)last_modified,
2132 etag ? etag : "(none)");
2133 privatiseObject(old_object, 0);
2134 new_object = makeObject(object->type, object->key,
2135 object->key_size, 1, 0,
2136 object->request, NULL);
2137 if(new_object == NULL) {
2138 do_log(L_ERROR, "Couldn't allocate object\n");
2139 httpServerAbort(connection, 1, 500,
2140 internAtom("Couldn't allocate object"));
2141 return 1;
2143 if(urlIsLocal(new_object->key, new_object->key_size))
2144 new_object->flags |= OBJECT_LOCAL;
2145 } else {
2146 new_object = object;
2149 suspectDynamic =
2150 (!etag && last_modified < 0) ||
2151 (cache_control.flags &
2152 (CACHE_NO_HIDDEN | CACHE_NO | CACHE_NO_STORE |
2153 (cacheIsShared ? CACHE_PRIVATE : 0))) ||
2154 (cache_control.max_age >= 0 && cache_control.max_age <= 2) ||
2155 (cacheIsShared &&
2156 cache_control.s_maxage >= 0 && cache_control.s_maxage <= 5) ||
2157 (old_object->last_modified >= 0 && old_object->expires >= 0 &&
2158 (old_object->expires - old_object->last_modified <= 1)) ||
2159 (supersede && (old_object->date - date <= 5));
2161 if(suspectDynamic)
2162 new_object->flags |= OBJECT_DYNAMIC;
2163 else if(!supersede)
2164 new_object->flags &= ~OBJECT_DYNAMIC;
2165 else if(old_object->flags & OBJECT_DYNAMIC)
2166 new_object->flags |= OBJECT_DYNAMIC;
2168 new_object->age = age;
2169 new_object->cache_control |= cache_control.flags;
2170 new_object->max_age = cache_control.max_age;
2171 new_object->s_maxage = cache_control.s_maxage;
2172 new_object->flags &= ~OBJECT_FAILED;
2174 if(date >= 0)
2175 new_object->date = date;
2176 if(last_modified >= 0)
2177 new_object->last_modified = last_modified;
2178 if(expires >= 0)
2179 new_object->expires = expires;
2180 if(new_object->etag == NULL)
2181 new_object->etag = etag;
2182 else
2183 free(etag);
2185 switch(code) {
2186 case 200:
2187 case 300: case 301: case 302: case 303: case 307:
2188 case 403: case 404: case 405: case 401:
2189 if(new_object->message) releaseAtom(new_object->message);
2190 new_object->code = code;
2191 new_object->message = message;
2192 break;
2193 case 206: case 304: case 412:
2194 if(new_object->code != 200 || !new_object->message) {
2195 if(new_object->message) releaseAtom(new_object->message);
2196 new_object->code = 200;
2197 new_object->message = internAtom("OK");
2199 releaseAtom(message);
2200 break;
2201 default:
2202 if(new_object->message) releaseAtom(new_object->message);
2203 new_object->code = code;
2204 new_object->message = retainAtom(message);
2205 break;
2208 httpTweakCachability(new_object);
2210 if(!via)
2211 new_via = internAtomF("%s %s",
2212 version == HTTP_11 ? "1.1" : "1.0",
2213 proxyName->string);
2214 else
2215 new_via = internAtomF("%s, %s %s", via->string,
2216 version == HTTP_11 ? "1.1" : "1.0",
2217 proxyName->string);
2218 if(new_via == NULL) {
2219 do_log(L_ERROR, "Couldn't allocate Via.\n");
2220 } else {
2221 if(new_object->via) releaseAtom(new_object->via);
2222 new_object->via = new_via;
2225 if(new_object->flags & OBJECT_INITIAL) {
2226 objectPartial(new_object, full_len, headers);
2227 } else {
2228 if(new_object->length < 0)
2229 new_object->length = full_len;
2230 /* XXX -- RFC 2616 13.5.3 */
2231 releaseAtom(headers);
2234 if(supersede) {
2235 assert(new_object != old_object);
2236 supersedeObject(old_object);
2239 if(new_object != old_object) {
2240 if(new_object->flags & OBJECT_INPROGRESS) {
2241 /* Make sure we don't fetch this object two times at the
2242 same time. Just drop the connection. */
2243 releaseObject(new_object);
2244 httpServerFinish(connection, 1, 0);
2245 return 1;
2247 old_object->flags &= ~OBJECT_VALIDATING;
2248 new_object->flags |= OBJECT_INPROGRESS;
2249 /* Signal the client side to switch to the new object -- see
2250 httpClientGetHandler. If it doesn't, we'll give up on this
2251 request below. */
2252 new_object->flags |= OBJECT_MUTATING;
2253 request->can_mutate = new_object;
2254 notifyObject(old_object);
2255 request->can_mutate = NULL;
2256 new_object->flags &= ~OBJECT_MUTATING;
2257 old_object->flags &= ~OBJECT_INPROGRESS;
2258 if(request->object == old_object) {
2259 if(request->request)
2260 request->request->request = NULL;
2261 request->request = NULL;
2262 request->object = new_object;
2263 } else {
2264 assert(request->object == new_object);
2266 releaseNotifyObject(old_object);
2267 old_object = NULL;
2268 object = new_object;
2269 } else {
2270 objectMetadataChanged(new_object, 0);
2273 if(object->flags & OBJECT_VALIDATING) {
2274 object->flags &= ~OBJECT_VALIDATING;
2275 notifyObject(object);
2278 if(!expect_body) {
2279 httpServerFinish(connection, 0, rc);
2280 return 1;
2283 if(request->request == NULL) {
2284 httpServerFinish(connection, 1, 0);
2285 return 1;
2288 if(code == 412) {
2289 /* 412 replies contain a useless body. For now, we
2290 drop the connection. */
2291 httpServerFinish(connection, 1, 0);
2292 return 1;
2296 if(request->flags & REQUEST_PERSISTENT) {
2297 if(request->method != METHOD_HEAD &&
2298 connection->te == TE_IDENTITY && len < 0) {
2299 do_log(L_ERROR, "Persistent reply with no Content-Length\n");
2300 /* That's potentially dangerous, as we could start reading
2301 arbitrary data into the object. Unfortunately, some
2302 servers do that. */
2303 request->flags &= ~REQUEST_PERSISTENT;
2307 /* we're getting a body */
2308 if(content_range.from > 0)
2309 connection->offset = content_range.from;
2310 else
2311 connection->offset = 0;
2313 if(content_range.to >= 0)
2314 request->to = content_range.to;
2316 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2317 (unsigned long)connection, (unsigned long)object,
2318 connection->offset);
2320 if(connection->len > rc) {
2321 rc = connectionAddData(connection, rc);
2322 if(rc) {
2323 if(rc < 0) {
2324 if(rc == -2) {
2325 do_log(L_ERROR, "Couldn't parse chunk size.\n");
2326 httpServerAbort(connection, 1, 502,
2327 internAtom("Couldn't parse chunk size"));
2328 } else {
2329 do_log(L_ERROR, "Couldn't add data to connection.\n");
2330 httpServerAbort(connection, 1, 500,
2331 internAtom("Couldn't add data "
2332 "to connection"));
2334 return 1;
2335 } else {
2336 if(code != 206) {
2337 if(object->length < 0) {
2338 object->length = object->size;
2339 objectMetadataChanged(object, 0);
2340 } else if(object->length != object->size) {
2341 httpServerAbort(connection, 1, 500,
2342 internAtom("Inconsistent "
2343 "object size"));
2344 object->length = -1;
2345 return 1;
2348 httpServerFinish(connection, 0, 0);
2349 return 1;
2352 } else {
2353 connection->len = 0;
2356 if(eof) {
2357 if(connection->te == TE_CHUNKED ||
2358 (object->length >= 0 &&
2359 connection->offset < object->length)) {
2360 do_log(L_ERROR, "Server closed connection.\n");
2361 httpServerAbort(connection, 1, 502,
2362 internAtom("Server closed connection"));
2363 return 1;
2364 } else {
2365 if(code != 206 && eof > 0 && object->length < 0) {
2366 object->length = object->size;
2367 objectMetadataChanged(object, 0);
2369 httpServerFinish(connection, 1, 0);
2370 return 1;
2372 } else {
2373 return httpServerReadData(connection, 1);
2375 return 0;
2377 fail:
2378 releaseAtom(url);
2379 releaseAtom(message);
2380 if(headers)
2381 releaseAtom(headers);
2382 if(etag)
2383 free(etag);
2384 if(via)
2385 releaseAtom(via);
2386 return 1;
2390 httpServerIndirectHandlerCommon(HTTPConnectionPtr connection, int eof)
2392 HTTPRequestPtr request = connection->request;
2394 assert(eof >= 0);
2395 assert(request->object->flags & OBJECT_INPROGRESS);
2397 if(connection->len > 0) {
2398 int rc;
2399 rc = connectionAddData(connection, 0);
2400 if(rc) {
2401 if(rc < 0) {
2402 if(rc == -2) {
2403 do_log(L_ERROR, "Couldn't parse chunk size.\n");
2404 httpServerAbort(connection, 1, 502,
2405 internAtom("Couldn't parse chunk size"));
2406 } else {
2407 do_log(L_ERROR, "Couldn't add data to connection.\n");
2408 httpServerAbort(connection, 1, 500,
2409 internAtom("Couldn't add data "
2410 "to connection"));
2412 return 1;
2413 } else {
2414 if(request->to < 0) {
2415 if(request->object->length < 0) {
2416 request->object->length = request->object->size;
2417 objectMetadataChanged(request->object, 0);
2418 } else if(request->object->length !=
2419 request->object->size) {
2420 request->object->length = -1;
2421 httpServerAbort(connection, 1, 502,
2422 internAtom("Inconsistent "
2423 "object size"));
2424 return 1;
2427 httpServerFinish(connection, 0, 0);
2429 return 1;
2433 if(eof && connection->len == 0) {
2434 if(connection->te == TE_CHUNKED ||
2435 (request->to >= 0 && connection->offset < request->to)) {
2436 do_log(L_ERROR, "Server dropped connection.\n");
2437 httpServerAbort(connection, 1, 502,
2438 internAtom("Server dropped connection"));
2439 return 1;
2440 } else {
2441 if(request->object->length < 0 && eof > 0 &&
2442 (request->to < 0 || request->to > request->object->size)) {
2443 request->object->length = request->object->size;
2444 objectMetadataChanged(request->object, 0);
2446 httpServerFinish(connection, 1, 0);
2447 return 1;
2449 } else {
2450 return httpServerReadData(connection, 0);
2455 httpServerIndirectHandler(int status,
2456 FdEventHandlerPtr event,
2457 StreamRequestPtr srequest)
2459 HTTPConnectionPtr connection = srequest->data;
2460 assert(connection->request->object->flags & OBJECT_INPROGRESS);
2462 httpSetTimeout(connection, -1);
2463 if(status < 0) {
2464 if(status != -ECLIENTRESET)
2465 do_log_error(L_ERROR, -status, "Read from server failed");
2466 httpServerAbort(connection, status != -ECLIENTRESET, 502,
2467 internAtomError(-status, "Read from server failed"));
2468 return 1;
2471 connection->len = srequest->offset;
2473 return httpServerIndirectHandlerCommon(connection, status);
2477 httpServerReadData(HTTPConnectionPtr connection, int immediate)
2479 HTTPRequestPtr request = connection->request;
2480 ObjectPtr object = request->object;
2481 int to = -1;
2483 assert(object->flags & OBJECT_INPROGRESS);
2485 if(request->request == NULL) {
2486 httpServerFinish(connection, 1, 0);
2487 return 1;
2490 if(request->to >= 0)
2491 to = request->to;
2492 else
2493 to = object->length;
2495 if(to >= 0 && to == connection->offset) {
2496 httpServerFinish(connection, 0, 0);
2497 return 1;
2500 if(connection->len == 0 &&
2501 ((connection->te == TE_IDENTITY && to > connection->offset) ||
2502 (connection->te == TE_CHUNKED && connection->chunk_remaining > 0))) {
2503 /* Read directly into the object */
2504 int i = connection->offset / CHUNK_SIZE;
2505 int j = connection->offset % CHUNK_SIZE;
2506 int end, len, more;
2507 /* See httpServerDirectHandlerCommon if you change this */
2508 if(connection->te == TE_CHUNKED) {
2509 len = connection->chunk_remaining;
2510 /* The logic here is that we want more to just fit the
2511 chunk header if we're doing a large read, but do a
2512 large read if we would otherwise do a small one. The
2513 magic constant 2000 comes from the assumption that the
2514 server uses chunks that have a size that are a power of
2515 two (possibly including the chunk header), and that we
2516 want a full ethernet packet to fit into our read. */
2517 more = (len >= 2000 ? 20 : MIN(2048 - len, CHUNK_SIZE));
2518 } else {
2519 len = to - connection->offset;
2520 /* We read more data only when there is a reasonable
2521 chance of there being another reply coming. */
2522 more = (connection->pipelined > 1) ? CHUNK_SIZE : 0;
2524 end = len + connection->offset;
2526 httpConnectionDestroyBuf(connection);
2528 /* The order of allocation is important in case we run out of
2529 memory. */
2530 lockChunk(object, i);
2531 if(object->chunks[i].data == NULL)
2532 object->chunks[i].data = get_chunk();
2533 if(object->chunks[i].data && object->chunks[i].size >= j) {
2534 if(len + j > CHUNK_SIZE) {
2535 lockChunk(object, i + 1);
2536 if(object->chunks[i + 1].data == NULL)
2537 object->chunks[i + 1].data = get_chunk();
2538 /* Unless we're grabbing all len of data, we do not
2539 want to do an indirect read immediately afterwards. */
2540 if(more && len + j <= 2 * CHUNK_SIZE) {
2541 if(!connection->buf)
2542 connection->buf = get_chunk(); /* checked below */
2544 if(object->chunks[i + 1].data) {
2545 do_stream_3(IO_READ | IO_NOTNOW, connection->fd, j,
2546 object->chunks[i].data, CHUNK_SIZE,
2547 object->chunks[i + 1].data,
2548 MIN(CHUNK_SIZE,
2549 end - (i + 1) * CHUNK_SIZE),
2550 connection->buf, connection->buf ? more : 0,
2551 httpServerDirectHandler2, connection);
2552 return 1;
2554 unlockChunk(object, i + 1);
2556 if(more && len + j <= CHUNK_SIZE) {
2557 if(!connection->buf)
2558 connection->buf = get_chunk();
2560 do_stream_2(IO_READ | IO_NOTNOW, connection->fd, j,
2561 object->chunks[i].data,
2562 MIN(CHUNK_SIZE, end - i * CHUNK_SIZE),
2563 connection->buf, connection->buf ? more : 0,
2564 httpServerDirectHandler, connection);
2565 return 1;
2566 } else {
2567 unlockChunk(object, i);
2571 if(connection->len == 0)
2572 httpConnectionDestroyBuf(connection);
2574 httpSetTimeout(connection, serverTimeout);
2575 do_stream_buf(IO_READ | IO_NOTNOW |
2576 ((immediate && connection->len) ? IO_IMMEDIATE : 0),
2577 connection->fd, connection->len,
2578 &connection->buf,
2579 (connection->te == TE_CHUNKED ?
2580 MIN(2048, CHUNK_SIZE) : CHUNK_SIZE),
2581 httpServerIndirectHandler, connection);
2582 return 1;
2586 httpServerDirectHandlerCommon(int kind, int status,
2587 FdEventHandlerPtr event,
2588 StreamRequestPtr srequest)
2590 HTTPConnectionPtr connection = srequest->data;
2591 HTTPRequestPtr request = connection->request;
2592 ObjectPtr object = request->object;
2593 int i = connection->offset / CHUNK_SIZE;
2594 int to, end, end1;
2596 assert(request->object->flags & OBJECT_INPROGRESS);
2598 httpSetTimeout(connection, -1);
2600 if(status < 0) {
2601 unlockChunk(object, i);
2602 if(kind == 2) unlockChunk(object, i + 1);
2603 if(status != -ECLIENTRESET)
2604 do_log_error(L_ERROR, -status, "Read from server failed");
2605 httpServerAbort(connection, status != -ECLIENTRESET, 502,
2606 internAtomError(-status, "Read from server failed"));
2607 return 1;
2610 /* We have incestuous knowledge of the decisions made in
2611 httpServerReadData */
2612 if(request->to >= 0)
2613 to = request->to;
2614 else
2615 to = object->length;
2616 if(connection->te == TE_CHUNKED)
2617 end = connection->offset + connection->chunk_remaining;
2618 else
2619 end = to;
2620 /* The amount of data actually read into the object */
2621 end1 = MIN(end, i * CHUNK_SIZE + MIN(kind * CHUNK_SIZE, srequest->offset));
2623 assert(end >= 0 && end1 >= i * CHUNK_SIZE && end1 <= (i + 2) * CHUNK_SIZE);
2625 object->chunks[i].size =
2626 MAX(object->chunks[i].size, MIN(end1 - i * CHUNK_SIZE, CHUNK_SIZE));
2627 if(kind == 2 && end1 > (i + 1) * CHUNK_SIZE) {
2628 object->chunks[i + 1].size =
2629 MAX(object->chunks[i + 1].size, end1 - (i + 1) * CHUNK_SIZE);
2631 if(connection->te == TE_CHUNKED) {
2632 connection->chunk_remaining -= (end1 - connection->offset);
2633 assert(connection->chunk_remaining >= 0);
2635 connection->offset = end1;
2636 object->size = MAX(object->size, end1);
2637 unlockChunk(object, i);
2638 if(kind == 2) unlockChunk(object, i + 1);
2640 if(i * CHUNK_SIZE + srequest->offset > end1) {
2641 connection->len = i * CHUNK_SIZE + srequest->offset - end1;
2642 return httpServerIndirectHandlerCommon(connection, status);
2643 } else {
2644 notifyObject(object);
2645 if(status) {
2646 httpServerFinish(connection, 1, 0);
2647 return 1;
2648 } else {
2649 return httpServerReadData(connection, 0);
2655 httpServerDirectHandler(int status,
2656 FdEventHandlerPtr event,
2657 StreamRequestPtr srequest)
2659 return httpServerDirectHandlerCommon(1, status, event, srequest);
2663 httpServerDirectHandler2(int status,
2664 FdEventHandlerPtr event,
2665 StreamRequestPtr srequest)
2667 return httpServerDirectHandlerCommon(2, status, event, srequest);
2670 /* Add the data accumulated in connection->buf into the object in
2671 connection->request. Returns 0 in the normal case, 1 if the TE is
2672 self-terminating and we're done, -1 if there was a problem with
2673 objectAddData, -2 if there was a problem with the data. */
2675 connectionAddData(HTTPConnectionPtr connection, int skip)
2677 HTTPRequestPtr request = connection->request;
2678 ObjectPtr object = request->object;
2679 int rc;
2681 if(connection->te == TE_IDENTITY) {
2682 int len;
2684 len = connection->len - skip;
2685 if(object->length >= 0) {
2686 len = MIN(object->length - connection->offset, len);
2688 if(request->to >= 0)
2689 len = MIN(request->to - connection->offset, len);
2690 if(len > 0) {
2691 rc = objectAddData(object, connection->buf + skip,
2692 connection->offset, len);
2693 if(rc < 0)
2694 return -1;
2695 connection->offset += len;
2696 connection->len -= (len + skip);
2697 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2698 (unsigned long)connection, (unsigned long)object,
2699 connection->offset);
2702 if(connection->len > 0 && skip + len > 0) {
2703 memmove(connection->buf,
2704 connection->buf + skip + len, connection->len);
2707 if((object->length >= 0 && object->length <= connection->offset) ||
2708 (request->to >= 0 && request->to <= connection->offset)) {
2709 notifyObject(object);
2710 return 1;
2711 } else {
2712 if(len > 0)
2713 notifyObject(object);
2714 return 0;
2716 } else if(connection->te == TE_CHUNKED) {
2717 int i = skip, j, size;
2718 /* connection->chunk_remaining is 0 at the end of a chunk, -1
2719 after the CR/LF pair ending a chunk, and -2 after we've
2720 seen a chunk of length 0. */
2721 if(connection->chunk_remaining > -2) {
2722 while(1) {
2723 if(connection->chunk_remaining <= 0) {
2724 if(connection->chunk_remaining == 0) {
2725 if(connection->len < i + 2)
2726 break;
2727 if(connection->buf[i] != '\r' ||
2728 connection->buf[i + 1] != '\n')
2729 return -1;
2730 i += 2;
2731 connection->chunk_remaining = -1;
2733 if(connection->len < i + 2)
2734 break;
2735 j = parseChunkSize(connection->buf, i,
2736 connection->len, &size);
2737 if(j < 0)
2738 return -2;
2739 if(j == 0)
2740 break;
2741 else
2742 i = j;
2743 if(size == 0) {
2744 connection->chunk_remaining = -2;
2745 break;
2746 } else {
2747 connection->chunk_remaining = size;
2749 } else {
2750 /* connection->chunk_remaining > 0 */
2751 size = MIN(connection->chunk_remaining,
2752 connection->len - i);
2753 if(size <= 0)
2754 break;
2755 rc = objectAddData(object, connection->buf + i,
2756 connection->offset, size);
2757 connection->offset += size;
2758 if(rc < 0)
2759 return -1;
2760 i += size;
2761 connection->chunk_remaining -= size;
2762 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2763 (unsigned long)connection,
2764 (unsigned long)object,
2765 connection->offset);
2769 connection->len -= i;
2770 if(connection->len > 0)
2771 memmove(connection->buf, connection->buf + i, connection->len);
2772 if(i > 0 || connection->chunk_remaining == -2)
2773 notifyObject(object);
2774 if(connection->chunk_remaining == -2)
2775 return 1;
2776 else
2777 return 0;
2778 } else {
2779 abort();
2783 void
2784 listServers(FILE *out)
2786 HTTPServerPtr server;
2787 int i, n, m, entry;
2789 fprintf(out, "<!DOCTYPE HTML PUBLIC "
2790 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2791 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2792 "<html><head>\n"
2793 "\r\n<title>Known servers</title>\n"
2794 "</head><body>\n"
2795 "<h1>Known servers</h1>\n");
2797 alternatingHttpStyle(out, "servers");
2798 fprintf(out, "<table id=servers>\n");
2799 fprintf(out, "<thead><tr><th>Server</th>"
2800 "<th>Version</th>"
2801 "<th>Persistent</th>"
2802 "<th>Pipeline</th>"
2803 "<th>Connections</th>"
2804 "<th></th>"
2805 "<th>rtt</th>"
2806 "<th>rate</th>"
2807 "</tr></thead>\n");
2808 fprintf(out, "<tbody>\n");
2809 server = servers;
2810 entry = 0;
2811 while(server) {
2812 fprintf(out, "<tr class=\"%s\">", entry % 2 == 0 ? "even" : "odd");
2813 if(server->port == 80)
2814 fprintf(out, "<td>%s</td>", server->name);
2815 else
2816 fprintf(out, "<td>%s:%d</td>", server->name, server->port);
2818 if(server->version == HTTP_11)
2819 fprintf(out, "<td>1.1</td>");
2820 else if(server->version == HTTP_10)
2821 fprintf(out, "<td>1.0</td>");
2822 else
2823 fprintf(out, "<td>unknown</td>");
2825 if(server->persistent < 0)
2826 fprintf(out, "<td>no</td>");
2827 else if(server->persistent > 0)
2828 fprintf(out, "<td>yes</td>");
2829 else
2830 fprintf(out, "<td>unknown</td>");
2832 if(server->version != HTTP_11 || server->persistent <= 0)
2833 fprintf(out, "<td></td>");
2834 else if(server->pipeline < 0)
2835 fprintf(out, "<td>no</td>");
2836 else if(server->pipeline >= 0 && server->pipeline <= 1)
2837 fprintf(out, "<td>unknown</td>");
2838 else if(server->pipeline == 2 || server->pipeline == 3)
2839 fprintf(out, "<td>probing</td>");
2840 else
2841 fprintf(out, "<td>yes</td>");
2843 n = 0; m = 0;
2844 for(i = 0; i < server->maxslots; i++)
2845 if(server->connection[i] && !server->connection[i]->connecting) {
2846 if(i < server->numslots)
2847 n++;
2848 else
2849 m++;
2852 fprintf(out, "<td>%d/%d", n, server->numslots);
2853 if(m)
2854 fprintf(out, " + %d</td>", m);
2855 else
2856 fprintf(out, "</td>");
2858 if(server->lies > 0)
2859 fprintf(out, "<td>(%d lies)</td>", (server->lies + 9) / 10);
2860 else
2861 fprintf(out, "<td></td>");
2863 if(server->rtt > 0)
2864 fprintf(out, "<td>%.3f</td>", (double)server->rtt / 1000000.0);
2865 else
2866 fprintf(out, "<td></td>");
2867 if(server->rate > 0)
2868 fprintf(out, "<td>%d</td>", server->rate);
2869 else
2870 fprintf(out, "<td></td>");
2872 fprintf(out, "</tr>\n");
2873 server = server->next;
2874 entry++;
2876 fprintf(out, "</tbody>\n");
2877 fprintf(out, "</table>\n");
2878 fprintf(out, "<p><a href=\"/polipo/\">back</a></p>");
2879 fprintf(out, "</body></html>\n");