Use DO_NTOHS when parsing DNS id.
[polipo.git] / server.c
blob42b07bb43bc2c0c546c4b8e48a21c5823c6a0673
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 client->flags |= CONN_SIDE_READER;
1005 do_stream(IO_READ | IO_IMMEDIATE,
1006 client->fd, 0, NULL, 0,
1007 httpClientSideHandler, client);
1008 } else if(!(request->flags & REQUEST_WAIT_CONTINUE) && doflush) {
1009 /* Make sure there's a reqbuf, as httpServerFinish uses
1010 it to determine if there's a writer. */
1011 if(connection->reqbuf == NULL)
1012 connection->reqbuf = get_chunk();
1013 assert(connection->reqbuf != NULL);
1014 do_stream(IO_WRITE,
1015 connection->fd, 0,
1016 client->reqbuf + client->reqbegin, len,
1017 httpServerSideHandler, connection);
1018 } else {
1019 if(connection->reqbuf) {
1020 httpConnectionDestroyReqbuf(connection);
1021 connection->reqlen = 0;
1023 if(request->flags & REQUEST_WAIT_CONTINUE) {
1024 ConditionHandlerPtr chandler;
1025 do_log(D_SERVER_CONN, "W... %s:%d.\n",
1026 connection->server->name, connection->server->port);
1027 chandler =
1028 conditionWait(&request->object->condition,
1029 httpServerContinueConditionHandler,
1030 sizeof(connection), &connection);
1031 if(chandler)
1032 return 1;
1033 else
1034 do_log(L_ERROR, "Couldn't register condition handler.\n");
1035 /* Fall through -- the client side will clean up. */
1037 client->flags |= CONN_SIDE_READER;
1038 do_stream(IO_READ | (done ? IO_IMMEDIATE : 0 ) | IO_NOTNOW,
1039 client->fd, client->reqlen,
1040 client->reqbuf, CHUNK_SIZE,
1041 httpClientSideHandler, client);
1043 return 1;
1046 static int
1047 httpClientDelayedDoSideHandler(TimeEventHandlerPtr event)
1049 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1050 httpServerDoSide(connection);
1051 return 1;
1054 static int
1055 httpServerDelayedDoSide(HTTPConnectionPtr connection)
1057 TimeEventHandlerPtr handler;
1058 handler = scheduleTimeEvent(1, httpClientDelayedDoSideHandler,
1059 sizeof(connection), &connection);
1060 if(!handler) {
1061 do_log(L_ERROR, "Couldn't schedule DoSide -- freeing memory.\n");
1062 free_chunk_arenas();
1063 handler = scheduleTimeEvent(1, httpClientDelayedDoSideHandler,
1064 sizeof(connection), &connection);
1065 do_log(L_ERROR, "Couldn't schedule DoSide.\n");
1066 /* Somebody will hopefully end up timing out. */
1067 return 1;
1069 return 1;
1072 static int
1073 httpServerSideHandlerCommon(int kind, int status,
1074 FdEventHandlerPtr event,
1075 StreamRequestPtr srequest)
1077 HTTPConnectionPtr connection = srequest->data;
1078 HTTPRequestPtr request = connection->request;
1079 HTTPRequestPtr requestor = request->request;
1080 HTTPConnectionPtr client = requestor->connection;
1081 int bodylen;
1083 assert(request->object->flags & OBJECT_INPROGRESS);
1085 if(status) {
1086 do_log_error(L_ERROR, -status, "Couldn't write to server");
1087 httpConnectionDestroyReqbuf(connection);
1088 if(status != -ECLIENTRESET)
1089 shutdown(connection->fd, 2);
1090 abortObject(request->object, 503,
1091 internAtom("Couldn't write to server"));
1092 /* Let the read side handle the error */
1093 httpServerDoSide(connection);
1094 return 1;
1097 assert(srequest->offset > 0);
1099 if(kind == 2) {
1100 if(srequest->offset < connection->reqlen)
1101 return 0;
1102 bodylen = srequest->offset - connection->reqlen;
1103 connection->reqlen = 0;
1104 httpConnectionDestroyReqbuf(connection);
1105 } else {
1106 bodylen = srequest->offset;
1110 assert(client->reqbegin + bodylen <= client->reqlen);
1112 if(client->reqlen > client->reqbegin + bodylen)
1113 memmove(client->reqbuf, client->reqbuf + client->reqbegin + bodylen,
1114 client->reqlen - client->reqbegin - bodylen);
1115 client->reqlen -= bodylen + client->reqbegin;
1116 client->reqbegin = 0;
1117 connection->reqoffset += bodylen;
1118 httpServerDoSide(connection);
1119 return 1;
1123 httpServerSideHandler(int status,
1124 FdEventHandlerPtr event,
1125 StreamRequestPtr srequest)
1127 return httpServerSideHandlerCommon(1, status, event, srequest);
1131 httpServerSideHandler2(int status,
1132 FdEventHandlerPtr event,
1133 StreamRequestPtr srequest)
1135 return httpServerSideHandlerCommon(2, status, event, srequest);
1138 static int
1139 httpServerContinueConditionHandler(int status, ConditionHandlerPtr chandler)
1141 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)chandler->data;
1143 if(connection->request->flags & REQUEST_WAIT_CONTINUE)
1144 return 0;
1145 httpServerDelayedDoSide(connection);
1146 return 1;
1149 /* s is 0 to keep the connection alive, 1 to shutdown the connection,
1150 and -1 to keep the connection alive and keep the current request. */
1151 void
1152 httpServerFinish(HTTPConnectionPtr connection, int s, int offset)
1154 HTTPServerPtr server = connection->server;
1155 HTTPRequestPtr request = connection->request;
1156 int i;
1158 if(request) {
1159 assert(connection->pipelined >= 1);
1160 assert((connection->pipelined > 1) == (request->next != NULL));
1161 } else {
1162 assert(connection->pipelined == 0);
1165 if(s == 0 && (!connection->request ||
1166 !(connection->request->flags & REQUEST_PERSISTENT)))
1167 s = 1;
1169 if(connection->serviced >= maxConnectionRequests ||
1170 connection->time < current_time.tv_sec - maxConnectionAge)
1171 s = 1;
1173 if(connection->reqbuf) {
1174 /* As most normal requests go out in a single packet, this is
1175 extremely unlikely to happen. As for POST/PUT requests,
1176 they are not pipelined, so this can only happen if the
1177 server sent an error reply early. */
1178 assert(connection->fd >= 0);
1179 shutdown(connection->fd, 1);
1180 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLOUT);
1181 httpServerDelayedFinish(connection);
1182 goto done;
1185 if(s >= 0 && request) {
1186 /* Update statistics about the server */
1187 int size = -1, d = -1, rtt = -1, rate = -1;
1188 if(connection->offset > 0 && request->from >= 0)
1189 size = connection->offset - request->from;
1190 if(request->time1.tv_sec != null_time.tv_sec) {
1191 d = timeval_minus_usec(&current_time, &request->time1);
1192 if(!(request->flags & REQUEST_PIPELINED) &&
1193 request->time0.tv_sec != null_time.tv_sec)
1194 rtt = timeval_minus_usec(&request->time1, &request->time0);
1195 if(size >= 8192 && d > 50000)
1196 rate = ((double)size / (double)d) * 1000000.0 + 0.5;
1198 request->time0 = null_time;
1199 request->time1 = null_time;
1201 if(rtt >= 0) {
1202 if(server->rtt >= 0)
1203 server->rtt = (3 * server->rtt + rtt + 2) / 4;
1204 else
1205 server->rtt = rtt;
1207 if(rate >= 0) {
1208 if(server->rate >= 0)
1209 server->rate = (3 * server->rate + rate + 2) / 4;
1210 else
1211 server->rate = rate;
1214 httpDequeueRequest(connection);
1215 connection->pipelined--;
1216 request->object->flags &= ~(OBJECT_INPROGRESS | OBJECT_VALIDATING);
1217 if(request->request) {
1218 request->request->request = NULL;
1219 request->request = NULL;
1221 releaseNotifyObject(request->object);
1222 request->object = NULL;
1223 httpDestroyRequest(request);
1226 do_log(D_SERVER_CONN, "Done with server %s:%d connection (%d)\n",
1227 connection->server->name, connection->server->port, s);
1229 assert(offset <= connection->len);
1231 if(s <= 0) {
1232 if(offset < connection->len) {
1233 assert(connection->buf != NULL);
1234 if(!connection->pipelined) {
1235 do_log(L_WARN,
1236 "Closing connection to %s:%d: "
1237 "%d stray bytes of data.\n",
1238 server->name, server->port, connection->len - offset);
1239 s = 1;
1240 } else {
1241 memmove(connection->buf, connection->buf + offset,
1242 connection->len - offset);
1243 connection->len = connection->len - offset;
1244 if((connection->flags & CONN_BIGBUF) &&
1245 connection->len <= CHUNK_SIZE)
1246 httpConnectionUnbigify(connection);
1248 } else {
1249 connection->len = 0;
1253 connection->server->time = current_time.tv_sec;
1254 connection->serviced++;
1256 if(s > 0) {
1257 if(connection->timeout)
1258 cancelTimeEvent(connection->timeout);
1259 connection->timeout = NULL;
1260 httpConnectionDestroyBuf(connection);
1261 if(connection->fd >= 0)
1262 CLOSE(connection->fd);
1263 connection->fd = -1;
1264 server->persistent -= 1;
1265 if(server->persistent < -5)
1266 server->numslots = MIN(server->maxslots, serverMaxSlots);
1267 if(connection->request) {
1268 HTTPRequestPtr req;
1269 do_log(D_SERVER_CONN, "Restarting pipeline to %s:%d.\n",
1270 server->name, server->port);
1271 if(server->pipeline == 2)
1272 server->pipeline -= 20;
1273 else
1274 server->pipeline -= 5;
1275 req = connection->request;
1276 while(req) {
1277 req->connection = NULL;
1278 req = req->next;
1280 if(server->request)
1281 connection->request_last->next = server->request;
1282 else
1283 server->request_last = connection->request_last;
1284 server->request = connection->request;
1285 connection->request = NULL;
1286 connection->request_last = NULL;
1288 /* Make sure we don't get confused into thinking a probe
1289 is in progress. */
1290 if(server->pipeline == 2 || server->pipeline == 3)
1291 server->pipeline = 1;
1292 for(i = 0; i < server->maxslots; i++)
1293 if(connection == server->connection[i])
1294 break;
1295 assert(i < server->maxslots);
1296 if(server->idleHandler[i])
1297 unregisterFdEvent(server->idleHandler[i]);
1298 server->idleHandler[i] = NULL;
1299 server->connection[i] = NULL;
1300 free(connection);
1301 } else {
1302 server->persistent += 1;
1303 if(server->persistent > 0)
1304 server->numslots = MIN(server->maxslots,
1305 server->version == HTTP_10 ?
1306 serverSlots1 : serverSlots);
1307 httpSetTimeout(connection, serverTimeout);
1308 /* See httpServerTrigger */
1309 if(connection->pipelined ||
1310 (server->version == HTTP_11 && server->pipeline <= 0) ||
1311 (server->pipeline == 3)) {
1312 server->pipeline++;
1314 if(s < 0 || connection->pipelined) {
1315 httpServerReply(connection, 1);
1316 } else {
1317 httpConnectionDestroyBuf(connection);
1321 done:
1322 httpServerTrigger(server);
1325 static int
1326 httpServerDelayedFinishHandler(TimeEventHandlerPtr event)
1328 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1329 httpServerFinish(connection, 1, 0);
1330 return 1;
1333 static void
1334 httpServerDelayedFinish(HTTPConnectionPtr connection)
1336 TimeEventHandlerPtr handler;
1338 handler = scheduleTimeEvent(1, httpServerDelayedFinishHandler,
1339 sizeof(connection), &connection);
1340 if(!handler) {
1341 do_log(L_ERROR,
1342 "Couldn't schedule delayed finish -- freeing memory.");
1343 free_chunk_arenas();
1344 handler = scheduleTimeEvent(1, httpServerDelayedFinishHandler,
1345 sizeof(connection), &connection);
1346 if(!handler) {
1347 do_log(L_ERROR,
1348 "Couldn't schedule delayed finish -- aborting.\n");
1349 polipoExit();
1354 void
1355 httpServerReply(HTTPConnectionPtr connection, int immediate)
1357 assert(connection->pipelined > 0);
1359 if(connection->request->request == NULL) {
1360 do_log(L_WARN, "Aborting pipeline on %s:%d.\n",
1361 connection->server->name, connection->server->port);
1362 httpServerFinish(connection, 1, 0);
1363 return;
1366 do_log(D_SERVER_CONN, "R: ");
1367 do_log_n(D_SERVER_CONN, connection->request->object->key,
1368 connection->request->object->key_size);
1369 do_log(D_SERVER_CONN, " (%d)\n", connection->request->method);
1371 if(connection->len == 0)
1372 httpConnectionDestroyBuf(connection);
1374 httpSetTimeout(connection, serverTimeout);
1375 do_stream_buf(IO_READ | (immediate ? IO_IMMEDIATE : 0) | IO_NOTNOW,
1376 connection->fd, connection->len,
1377 &connection->buf, CHUNK_SIZE,
1378 httpServerReplyHandler, connection);
1382 httpConnectionPipelined(HTTPConnectionPtr connection)
1384 HTTPRequestPtr request = connection->request;
1385 int i = 0;
1386 while(request) {
1387 i++;
1388 request = request->next;
1390 return i;
1393 void
1394 httpServerUnpipeline(HTTPRequestPtr request)
1396 HTTPConnectionPtr connection = request->connection;
1397 HTTPServerPtr server = connection->server;
1399 request->flags &= ~REQUEST_PERSISTENT;
1400 if(request->next) {
1401 HTTPRequestPtr req;
1402 do_log(L_WARN,
1403 "Restarting pipeline to %s:%d.\n",
1404 connection->server->name, connection->server->port);
1405 req = request->next;
1406 while(req) {
1407 req->connection = NULL;
1408 req = req->next;
1410 if(server->request)
1411 connection->request_last->next = server->request;
1412 else
1413 server->request_last = connection->request_last;
1414 server->request = request->next;
1415 request->next = NULL;
1416 connection->request_last = request;
1418 connection->pipelined = httpConnectionPipelined(connection);
1421 void
1422 httpServerRestart(HTTPConnectionPtr connection)
1424 HTTPServerPtr server = connection->server;
1425 HTTPRequestPtr request = connection->request;
1427 if(request) {
1428 HTTPRequestPtr req;
1429 if(request->next)
1430 do_log(L_WARN,
1431 "Restarting pipeline to %s:%d.\n",
1432 connection->server->name, connection->server->port);
1433 req = request;
1434 while(req) {
1435 req->connection = NULL;
1436 req = req->next;
1438 if(server->request)
1439 connection->request_last->next = server->request;
1440 else
1441 server->request_last = connection->request_last;
1442 server->request = request;
1443 connection->request = NULL;
1444 connection->request_last = NULL;
1446 connection->pipelined = 0;
1447 httpServerFinish(connection, 1, 0);
1451 httpServerRequest(ObjectPtr object, int method, int from, int to,
1452 HTTPRequestPtr requestor, void *closure)
1454 int rc;
1455 char name[132];
1456 int port;
1457 int x, y, z;
1459 assert(from >= 0 && (to < 0 || to > from));
1460 assert(closure == NULL);
1461 assert(!(object->flags & OBJECT_LOCAL));
1462 assert(object->type == OBJECT_HTTP);
1464 if(object->flags & OBJECT_INPROGRESS)
1465 return 1;
1467 if(requestor->flags & REQUEST_REQUESTED)
1468 return 0;
1470 assert(requestor->request == NULL);
1472 if(proxyOffline)
1473 return -1;
1475 rc = parseUrl(object->key, object->key_size, &x, &y, &port, &z);
1477 if(rc < 0 || x < 0 || y < 0 || y - x > 131) {
1478 do_log(L_ERROR, "Couldn't parse URL: ");
1479 do_log_n(L_ERROR, object->key, object->key_size);
1480 do_log(L_ERROR, "\n");
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 dispose_chunk(connection->reqbuf);
1749 connection->reqbuf = NULL;
1750 shutdown(connection->fd, 2);
1751 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLIN);
1752 httpSetTimeout(connection, 60);
1753 return 1;
1757 httpServerSendRequest(HTTPConnectionPtr connection)
1759 assert(connection->server);
1761 if(connection->reqlen == 0) {
1762 do_log(D_SERVER_REQ,
1763 "Writing aborted on 0x%lx\n", (unsigned long)connection);
1764 httpConnectionDestroyReqbuf(connection);
1765 shutdown(connection->fd, 2);
1766 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLIN | POLLOUT);
1767 return -1;
1770 httpSetTimeout(connection, serverTimeout);
1771 do_stream(IO_WRITE, connection->fd, 0,
1772 connection->reqbuf, connection->reqlen,
1773 httpServerHandler, connection);
1774 return 1;
1778 httpServerReplyHandler(int status,
1779 FdEventHandlerPtr event,
1780 StreamRequestPtr srequest)
1782 HTTPConnectionPtr connection = srequest->data;
1783 HTTPRequestPtr request = connection->request;
1784 int i, body;
1785 int bufsize =
1786 (connection->flags & CONN_BIGBUF) ? bigBufferSize : CHUNK_SIZE;
1788 assert(request->object->flags & OBJECT_INPROGRESS);
1789 if(status < 0) {
1790 if(connection->serviced >= 1) {
1791 httpServerRestart(connection);
1792 return 1;
1794 if(status != -ECLIENTRESET)
1795 do_log_error(L_ERROR, -status, "Read from server failed");
1796 httpServerAbort(connection, status != -ECLIENTRESET, 502,
1797 internAtomError(-status, "Read from server failed"));
1798 return 1;
1801 i = findEndOfHeaders(connection->buf, 0, srequest->offset, &body);
1802 connection->len = srequest->offset;
1804 if(i >= 0) {
1805 request->time1 = current_time;
1806 return httpServerHandlerHeaders(status, event, srequest, connection);
1809 if(status) {
1810 if(connection->serviced >= 1) {
1811 httpServerRestart(connection);
1812 return 1;
1814 if(status < 0) {
1815 do_log(L_ERROR,
1816 "Error reading server headers: %d\n", -status);
1817 httpServerAbort(connection, status != -ECLIENTRESET, 502,
1818 internAtomError(-status,
1819 "Error reading server headers"));
1820 } else
1821 httpServerAbort(connection, 1, 502,
1822 internAtom("Server dropped connection"));
1823 return 1;
1826 if(connection->len >= bufsize) {
1827 int rc = 0;
1828 if(!(connection->flags & CONN_BIGBUF))
1829 rc = httpConnectionBigify(connection);
1830 if(rc == 0) {
1831 do_log(L_ERROR, "Couldn't find end of server's headers.\n");
1832 httpServerAbort(connection, 1, 502,
1833 internAtom("Couldn't find end "
1834 "of server's headers"));
1835 return 1;
1836 } else if(rc < 0) {
1837 do_log(L_ERROR, "Couldn't allocate big buffer.\n");
1838 httpServerAbort(connection, 1, 500,
1839 internAtom("Couldn't allocate big buffer"));
1840 return 1;
1842 /* Can't just return 0 -- buf has moved. */
1843 do_stream(IO_READ,
1844 connection->fd, connection->len,
1845 connection->buf, bigBufferSize,
1846 httpServerReplyHandler, connection);
1847 return 1;
1850 return 0;
1854 httpServerHandlerHeaders(int eof,
1855 FdEventHandlerPtr event,
1856 StreamRequestPtr srequest,
1857 HTTPConnectionPtr connection)
1859 HTTPRequestPtr request = connection->request;
1860 ObjectPtr object = request->object;
1861 int rc;
1862 int code, version;
1863 int full_len;
1864 AtomPtr headers;
1865 int len;
1866 int te;
1867 CacheControlRec cache_control;
1868 int age = -1;
1869 time_t date, last_modified, expires;
1870 struct timeval *init_time;
1871 char *etag;
1872 AtomPtr via, new_via;
1873 int expect_body;
1874 HTTPRangeRec content_range;
1875 ObjectPtr new_object = NULL, old_object = NULL;
1876 int supersede = 0;
1877 AtomPtr message = NULL;
1878 int suspectDynamic;
1879 AtomPtr url = NULL;
1881 assert(request->object->flags & OBJECT_INPROGRESS);
1882 assert(eof >= 0);
1884 httpSetTimeout(connection, -1);
1886 if(request->flags & REQUEST_WAIT_CONTINUE) {
1887 do_log(D_SERVER_CONN, "W %s:%d.\n",
1888 connection->server->name, connection->server->port);
1889 request->flags &= ~REQUEST_WAIT_CONTINUE;
1892 rc = httpParseServerFirstLine(connection->buf, &code, &version, &message);
1893 if(rc <= 0) {
1894 do_log(L_ERROR, "Couldn't parse server status line.\n");
1895 httpServerAbort(connection, 1, 502,
1896 internAtom("Couldn't parse server status line"));
1897 return 1;
1900 do_log(D_SERVER_REQ, "Server status: ");
1901 do_log_n(D_SERVER_REQ, connection->buf,
1902 connection->buf[rc - 1] == '\r' ? rc - 2 : rc - 2);
1903 do_log(D_SERVER_REQ, " (0x%lx for 0x%lx)\n",
1904 (unsigned long)connection, (unsigned long)object);
1906 if(version != HTTP_10 && version != HTTP_11) {
1907 do_log(L_ERROR, "Unknown server HTTP version\n");
1908 httpServerAbort(connection, 1, 502,
1909 internAtom("Unknown server HTTP version"));
1910 releaseAtom(message);
1911 return 1;
1914 connection->version = version;
1915 connection->server->version = version;
1916 request->flags |= REQUEST_PERSISTENT;
1918 url = internAtomN(object->key, object->key_size);
1919 rc = httpParseHeaders(0, url, connection->buf, rc, request,
1920 &headers, &len, &cache_control, NULL, &te,
1921 &date, &last_modified, &expires, NULL, NULL, NULL,
1922 &age, &etag, NULL, NULL, &content_range,
1923 NULL, &via, NULL);
1924 if(rc < 0) {
1925 do_log(L_ERROR, "Couldn't parse server headers\n");
1926 releaseAtom(url);
1927 releaseAtom(message);
1928 httpServerAbort(connection, 1, 502,
1929 internAtom("Couldn't parse server headers"));
1930 return 1;
1933 if(code == 100) {
1934 releaseAtom(url);
1935 releaseAtom(message);
1936 /* We've already reset wait_continue above, but we must still
1937 ensure that the writer notices. */
1938 notifyObject(request->object);
1939 httpServerFinish(connection, -1, rc);
1940 return 1;
1943 if(code == 101) {
1944 httpServerAbort(connection, 1, 501,
1945 internAtom("Upgrade not implemented"));
1946 goto fail;
1949 if(via && !checkVia(proxyName, via)) {
1950 httpServerAbort(connection, 1, 504, internAtom("Proxy loop detected"));
1951 goto fail;
1953 full_len = content_range.full_length;
1955 if(code == 206) {
1956 if(content_range.from == -1 || content_range.to == -1) {
1957 do_log(L_ERROR, "Partial content without range.\n");
1958 httpServerAbort(connection, 1, 502,
1959 internAtom("Partial content without range"));
1960 goto fail;
1962 if(len >= 0 && len != content_range.to - content_range.from) {
1963 do_log(L_ERROR, "Inconsistent partial content.\n");
1964 httpServerAbort(connection, 1, 502,
1965 internAtom("Inconsistent partial content"));
1966 goto fail;
1968 } else if(code < 400 &&
1969 (content_range.from >= 0 || content_range.to >= 0 ||
1970 content_range.full_length >= 0)) {
1971 do_log(L_WARN, "Range without partial content.\n");
1972 /* Damn anakata. */
1973 content_range.from = -1;
1974 content_range.to = -1;
1975 content_range.full_length = -1;
1976 } else if(code != 304 && code != 412) {
1977 full_len = len;
1980 if(te != TE_IDENTITY && te != TE_CHUNKED) {
1981 do_log(L_ERROR, "Unsupported transfer-encoding\n");
1982 httpServerAbort(connection, 1, 502,
1983 internAtom("Unsupported transfer-encoding"));
1984 goto fail;
1987 if(code == 304) {
1988 if(request->method != METHOD_CONDITIONAL_GET) {
1989 do_log(L_ERROR, "Unexpected \"not changed\" reply from server\n");
1990 httpServerAbort(connection, 1, 502,
1991 internAtom("Unexpected \"not changed\" "
1992 "reply from server"));
1993 goto fail;
1995 if(object->etag && !etag) {
1996 /* RFC 2616 10.3.5. Violated by some front-end proxies. */
1997 do_log(L_WARN, "\"Not changed\" reply with no ETag.\n");
2001 if(code == 412) {
2002 if(request->method != METHOD_CONDITIONAL_GET ||
2003 (!object->etag && !object->last_modified)) {
2004 do_log(L_ERROR,
2005 "Unexpected \"precondition failed\" reply from server.\n");
2006 httpServerAbort(connection, 1, 502,
2007 internAtom("Unexpected \"precondition failed\" "
2008 "reply from server"));
2009 goto fail;
2013 releaseAtom(url);
2015 /* Okay, we're going to accept this reply. */
2017 if((code == 200 || code == 206 || code == 304 || code == 412) &&
2018 (cache_control.flags & (CACHE_NO | CACHE_NO_STORE) ||
2019 cache_control.max_age == 0 ||
2020 (cacheIsShared && cache_control.s_maxage == 0) ||
2021 (expires >= 0 && expires <= object->age))) {
2022 do_log(L_UNCACHEABLE, "Uncacheable object ");
2023 do_log_n(L_UNCACHEABLE, object->key, object->key_size);
2024 do_log(L_UNCACHEABLE, " (%d)\n", cache_control.flags);
2027 if(request->time0.tv_sec != null_time.tv_sec)
2028 init_time = &request->time0;
2029 else
2030 init_time = &current_time;
2031 age = MIN(init_time->tv_sec - age, init_time->tv_sec);
2033 if(request->method == METHOD_HEAD ||
2034 code < 200 || code == 204 || code == 304)
2035 expect_body = 0;
2036 else if(te == TE_IDENTITY)
2037 expect_body = (len != 0);
2038 else
2039 expect_body = 1;
2041 connection->chunk_remaining = -1;
2042 connection->te = te;
2044 old_object = object;
2046 connection->server->lies--;
2048 if(object->cache_control & CACHE_MISMATCH)
2049 supersede = 1;
2051 if(code == 304 || code == 412) {
2052 if((object->etag && etag && strcmp(object->etag, etag) != 0) ||
2053 (object->last_modified >= 0 && last_modified >= 0 &&
2054 object->last_modified != last_modified)) {
2055 do_log(L_ERROR, "Inconsistent \"%s\" reply for ",
2056 code == 304 ? "not changed":"precondition failed");
2057 do_log_n(L_ERROR, object->key, object->key_size);
2058 do_log(L_ERROR, "\n");
2059 object->flags |= OBJECT_DYNAMIC;
2060 supersede = 1;
2062 } else if(!(object->flags & OBJECT_INITIAL)) {
2063 if((object->last_modified < 0 || last_modified < 0) &&
2064 (!object->etag || !etag))
2065 supersede = 1;
2066 else if(object->last_modified != last_modified)
2067 supersede = 1;
2068 else if(object->etag || etag) {
2069 /* We need to be permissive here so as to deal with some
2070 front-end proxies that discard ETags on partial
2071 replies but not on full replies. */
2072 if(etag && object->etag && strcmp(object->etag, etag) != 0)
2073 supersede = 1;
2074 else if(!object->etag)
2075 supersede = 1;
2078 if(!supersede && (object->cache_control & CACHE_VARY) &&
2079 dontTrustVaryETag >= 1) {
2080 /* Check content-type to work around mod_gzip bugs */
2081 if(!httpHeaderMatch(atomContentType, object->headers, headers) ||
2082 !httpHeaderMatch(atomContentEncoding, object->headers, headers))
2083 supersede = 1;
2086 if(full_len < 0 && te == TE_IDENTITY) {
2087 /* It's an HTTP/1.0 CGI. Be afraid. */
2088 if(expect_body && content_range.from < 0 && content_range.to < 0)
2089 supersede = 1;
2092 if(!supersede && object->length >= 0 && full_len >= 0 &&
2093 object->length != full_len) {
2094 do_log(L_WARN, "Inconsistent length.\n");
2095 supersede = 1;
2098 if(!supersede &&
2099 ((object->last_modified >= 0 && last_modified >= 0) ||
2100 (object->etag && etag))) {
2101 if(request->method == METHOD_CONDITIONAL_GET) {
2102 do_log(L_WARN, "Server ignored conditional request.\n");
2103 connection->server->lies += 10;
2104 /* Drop the connection? */
2107 } else if(code == 416) {
2108 do_log(L_ERROR, "Unexpected \"range not satisfiable\" reply\n");
2109 httpServerAbort(connection, 1, 502,
2110 internAtom("Unexpected \"range not satisfiable\" "
2111 "reply"));
2112 /* The object may be superseded. Make sure the next request
2113 won't be partial. */
2114 abortObject(object, 502,
2115 internAtom("Unexpected \"range not satisfiable\" reply"));
2116 return 1;
2119 if(object->flags & OBJECT_INITIAL)
2120 supersede = 0;
2122 if(supersede) {
2123 do_log(L_SUPERSEDED, "Superseding object: ");
2124 do_log_n(L_SUPERSEDED, old_object->key, old_object->key_size);
2125 do_log(L_SUPERSEDED, " (%d %d %d %s -> %d %d %d %s)\n",
2126 object->code, object->length, (int)object->last_modified,
2127 object->etag?object->etag: "(none)",
2128 code, full_len, (int)last_modified,
2129 etag?etag:"(none)");
2130 privatiseObject(old_object, 0);
2131 new_object = makeObject(object->type, object->key,
2132 object->key_size, 1, 0,
2133 object->request, NULL);
2134 if(new_object == NULL) {
2135 do_log(L_ERROR, "Couldn't allocate object\n");
2136 httpServerAbort(connection, 1, 500,
2137 internAtom("Couldn't allocate object"));
2138 return 1;
2140 if(urlIsLocal(new_object->key, new_object->key_size))
2141 new_object->flags |= OBJECT_LOCAL;
2142 } else {
2143 new_object = object;
2146 suspectDynamic =
2147 (!etag && last_modified < 0) ||
2148 (cache_control.flags &
2149 (CACHE_NO_HIDDEN | CACHE_NO | CACHE_NO_STORE |
2150 (cacheIsShared ? CACHE_PRIVATE : 0))) ||
2151 (cache_control.max_age >= 0 && cache_control.max_age <= 2) ||
2152 (cacheIsShared &&
2153 cache_control.s_maxage >= 0 && cache_control.s_maxage <= 5) ||
2154 (old_object->last_modified >= 0 && old_object->expires >= 0 &&
2155 (old_object->expires - old_object->last_modified <= 1)) ||
2156 (supersede && (old_object->date - date <= 5));
2158 if(suspectDynamic)
2159 new_object->flags |= OBJECT_DYNAMIC;
2160 else if(!supersede)
2161 new_object->flags &= ~OBJECT_DYNAMIC;
2162 else if(old_object->flags & OBJECT_DYNAMIC)
2163 new_object->flags |= OBJECT_DYNAMIC;
2165 new_object->age = age;
2166 new_object->cache_control |= cache_control.flags;
2167 new_object->max_age = cache_control.max_age;
2168 new_object->s_maxage = cache_control.s_maxage;
2169 new_object->flags &= ~OBJECT_FAILED;
2171 if(date >= 0)
2172 new_object->date = date;
2173 if(last_modified >= 0)
2174 new_object->last_modified = last_modified;
2175 if(expires >= 0)
2176 new_object->expires = expires;
2177 if(new_object->etag == NULL)
2178 new_object->etag = etag;
2179 else
2180 free(etag);
2182 switch(code) {
2183 case 200:
2184 case 300: case 301: case 302: case 303: case 307:
2185 case 403: case 404: case 405: case 401:
2186 if(new_object->message) releaseAtom(new_object->message);
2187 new_object->code = code;
2188 new_object->message = message;
2189 break;
2190 case 206: case 304: case 412:
2191 if(new_object->code != 200 || !new_object->message) {
2192 if(new_object->message) releaseAtom(new_object->message);
2193 new_object->code = 200;
2194 new_object->message = internAtom("OK");
2196 releaseAtom(message);
2197 break;
2198 default:
2199 if(new_object->message) releaseAtom(new_object->message);
2200 new_object->code = code;
2201 new_object->message = retainAtom(message);
2202 break;
2205 httpTweakCachability(new_object);
2207 if(!via)
2208 new_via = internAtomF("%s %s",
2209 version == HTTP_11 ? "1.1" : "1.0",
2210 proxyName->string);
2211 else
2212 new_via = internAtomF("%s, %s %s", via->string,
2213 version == HTTP_11 ? "1.1" : "1.0",
2214 proxyName->string);
2215 if(new_via == NULL) {
2216 do_log(L_ERROR, "Couldn't allocate Via.\n");
2217 } else {
2218 if(new_object->via) releaseAtom(new_object->via);
2219 new_object->via = new_via;
2222 if(new_object->flags & OBJECT_INITIAL) {
2223 objectPartial(new_object, full_len, headers);
2224 } else {
2225 if(new_object->length < 0)
2226 new_object->length = full_len;
2227 /* XXX -- RFC 2616 13.5.3 */
2228 releaseAtom(headers);
2231 if(supersede) {
2232 assert(new_object != old_object);
2233 supersedeObject(old_object);
2236 if(new_object != old_object) {
2237 if(new_object->flags & OBJECT_INPROGRESS) {
2238 /* Make sure we don't fetch this object two times at the
2239 same time. Just drop the connection. */
2240 releaseObject(new_object);
2241 httpServerFinish(connection, 1, 0);
2242 return 1;
2244 old_object->flags &= ~OBJECT_VALIDATING;
2245 new_object->flags |= OBJECT_INPROGRESS;
2246 /* Signal the client side to switch to the new object -- see
2247 httpClientGetHandler. If it doesn't, we'll give up on this
2248 request below. */
2249 new_object->flags |= OBJECT_MUTATING;
2250 request->can_mutate = new_object;
2251 notifyObject(old_object);
2252 request->can_mutate = NULL;
2253 new_object->flags &= ~OBJECT_MUTATING;
2254 old_object->flags &= ~OBJECT_INPROGRESS;
2255 if(request->object == old_object) {
2256 if(request->request)
2257 request->request->request = NULL;
2258 request->request = NULL;
2259 request->object = new_object;
2260 } else {
2261 assert(request->object == new_object);
2263 releaseNotifyObject(old_object);
2264 old_object = NULL;
2265 object = new_object;
2266 } else {
2267 objectMetadataChanged(new_object, 0);
2270 if(object->flags & OBJECT_VALIDATING) {
2271 object->flags &= ~OBJECT_VALIDATING;
2272 notifyObject(object);
2275 if(!expect_body) {
2276 httpServerFinish(connection, 0, rc);
2277 return 1;
2280 if(request->request == NULL) {
2281 httpServerFinish(connection, 1, 0);
2282 return 1;
2285 if(code == 412) {
2286 /* 412 replies contain a useless body. For now, we
2287 drop the connection. */
2288 httpServerFinish(connection, 1, 0);
2289 return 1;
2293 if(request->flags & REQUEST_PERSISTENT) {
2294 if(request->method != METHOD_HEAD &&
2295 connection->te == TE_IDENTITY && len < 0) {
2296 do_log(L_ERROR, "Persistent reply with no Content-Length\n");
2297 /* That's potentially dangerous, as we could start reading
2298 arbitrary data into the object. Unfortunately, some
2299 servers do that. */
2300 request->flags &= ~REQUEST_PERSISTENT;
2304 /* we're getting a body */
2305 if(content_range.from > 0)
2306 connection->offset = content_range.from;
2307 else
2308 connection->offset = 0;
2310 if(content_range.to >= 0)
2311 request->to = content_range.to;
2313 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2314 (unsigned long)connection, (unsigned long)object,
2315 connection->offset);
2317 if(connection->len > rc) {
2318 rc = connectionAddData(connection, rc);
2319 if(rc) {
2320 if(rc < 0) {
2321 if(rc == -2) {
2322 do_log(L_ERROR, "Couldn't parse chunk size.\n");
2323 httpServerAbort(connection, 1, 502,
2324 internAtom("Couldn't parse chunk size"));
2325 } else {
2326 do_log(L_ERROR, "Couldn't add data to connection.\n");
2327 httpServerAbort(connection, 1, 500,
2328 internAtom("Couldn't add data "
2329 "to connection"));
2331 return 1;
2332 } else {
2333 if(code != 206) {
2334 if(object->length < 0) {
2335 object->length = object->size;
2336 objectMetadataChanged(object, 0);
2337 } else if(object->length != object->size) {
2338 httpServerAbort(connection, 1, 500,
2339 internAtom("Inconsistent "
2340 "object size"));
2341 object->length = -1;
2342 return 1;
2345 httpServerFinish(connection, 0, 0);
2346 return 1;
2349 } else {
2350 connection->len = 0;
2353 if(eof) {
2354 if(connection->te == TE_CHUNKED ||
2355 (object->length >= 0 &&
2356 connection->offset < object->length)) {
2357 do_log(L_ERROR, "Server closed connection.\n");
2358 httpServerAbort(connection, 1, 502,
2359 internAtom("Server closed connection"));
2360 return 1;
2361 } else {
2362 if(code != 206 && eof > 0 && object->length < 0) {
2363 object->length = object->size;
2364 objectMetadataChanged(object, 0);
2366 httpServerFinish(connection, 1, 0);
2367 return 1;
2369 } else {
2370 return httpServerReadData(connection, 1);
2372 return 0;
2374 fail:
2375 releaseAtom(url);
2376 releaseAtom(message);
2377 if(headers)
2378 releaseAtom(headers);
2379 if(etag)
2380 free(etag);
2381 if(via)
2382 releaseAtom(via);
2383 return 1;
2387 httpServerIndirectHandlerCommon(HTTPConnectionPtr connection, int eof)
2389 HTTPRequestPtr request = connection->request;
2391 assert(eof >= 0);
2392 assert(request->object->flags & OBJECT_INPROGRESS);
2394 if(connection->len > 0) {
2395 int rc;
2396 rc = connectionAddData(connection, 0);
2397 if(rc) {
2398 if(rc < 0) {
2399 if(rc == -2) {
2400 do_log(L_ERROR, "Couldn't parse chunk size.\n");
2401 httpServerAbort(connection, 1, 502,
2402 internAtom("Couldn't parse chunk size"));
2403 } else {
2404 do_log(L_ERROR, "Couldn't add data to connection.\n");
2405 httpServerAbort(connection, 1, 500,
2406 internAtom("Couldn't add data "
2407 "to connection"));
2409 return 1;
2410 } else {
2411 if(request->to < 0) {
2412 if(request->object->length < 0) {
2413 request->object->length = request->object->size;
2414 objectMetadataChanged(request->object, 0);
2415 } else if(request->object->length !=
2416 request->object->size) {
2417 request->object->length = -1;
2418 httpServerAbort(connection, 1, 502,
2419 internAtom("Inconsistent "
2420 "object size"));
2421 return 1;
2424 httpServerFinish(connection, 0, 0);
2426 return 1;
2430 if(eof && connection->len == 0) {
2431 if(connection->te == TE_CHUNKED ||
2432 (request->to >= 0 && connection->offset < request->to)) {
2433 do_log(L_ERROR, "Server dropped connection.\n");
2434 httpServerAbort(connection, 1, 502,
2435 internAtom("Server dropped connection"));
2436 return 1;
2437 } else {
2438 if(request->object->length < 0 && eof > 0 &&
2439 (request->to < 0 || request->to > request->object->size)) {
2440 request->object->length = request->object->size;
2441 objectMetadataChanged(request->object, 0);
2443 httpServerFinish(connection, 1, 0);
2444 return 1;
2446 } else {
2447 return httpServerReadData(connection, 0);
2452 httpServerIndirectHandler(int status,
2453 FdEventHandlerPtr event,
2454 StreamRequestPtr srequest)
2456 HTTPConnectionPtr connection = srequest->data;
2457 assert(connection->request->object->flags & OBJECT_INPROGRESS);
2459 httpSetTimeout(connection, -1);
2460 if(status < 0) {
2461 if(status != -ECLIENTRESET)
2462 do_log_error(L_ERROR, -status, "Read from server failed");
2463 httpServerAbort(connection, status != -ECLIENTRESET, 502,
2464 internAtomError(-status, "Read from server failed"));
2465 return 1;
2468 connection->len = srequest->offset;
2470 return httpServerIndirectHandlerCommon(connection, status);
2474 httpServerReadData(HTTPConnectionPtr connection, int immediate)
2476 HTTPRequestPtr request = connection->request;
2477 ObjectPtr object = request->object;
2478 int to = -1;
2480 assert(object->flags & OBJECT_INPROGRESS);
2482 if(request->request == NULL) {
2483 httpServerFinish(connection, 1, 0);
2484 return 1;
2487 if(request->to >= 0)
2488 to = request->to;
2489 else
2490 to = object->length;
2492 if(to >= 0 && to == connection->offset) {
2493 httpServerFinish(connection, 0, 0);
2494 return 1;
2497 if(connection->len == 0 &&
2498 ((connection->te == TE_IDENTITY && to > connection->offset) ||
2499 (connection->te == TE_CHUNKED && connection->chunk_remaining > 0))) {
2500 /* Read directly into the object */
2501 int i = connection->offset / CHUNK_SIZE;
2502 int j = connection->offset % CHUNK_SIZE;
2503 int end, len, more;
2504 /* See httpServerDirectHandlerCommon if you change this */
2505 if(connection->te == TE_CHUNKED) {
2506 len = connection->chunk_remaining;
2507 /* The logic here is that we want more to just fit the
2508 chunk header if we're doing a large read, but do a
2509 large read if we would otherwise do a small one. The
2510 magic constant 2000 comes from the assumption that the
2511 server uses chunks that have a size that are a power of
2512 two (possibly including the chunk header), and that we
2513 want a full ethernet packet to fit into our read. */
2514 more = (len >= 2000 ? 20 : MIN(2048 - len, CHUNK_SIZE));
2515 } else {
2516 len = to - connection->offset;
2517 /* We read more data only when there is a reasonable
2518 chance of there being another reply coming. */
2519 more = (connection->pipelined > 1) ? CHUNK_SIZE : 0;
2521 end = len + connection->offset;
2523 httpConnectionDestroyBuf(connection);
2525 /* The order of allocation is important in case we run out of
2526 memory. */
2527 lockChunk(object, i);
2528 if(object->chunks[i].data == NULL)
2529 object->chunks[i].data = get_chunk();
2530 if(object->chunks[i].data && object->chunks[i].size >= j) {
2531 if(len + j > CHUNK_SIZE) {
2532 lockChunk(object, i + 1);
2533 if(object->chunks[i + 1].data == NULL)
2534 object->chunks[i + 1].data = get_chunk();
2535 /* Unless we're grabbing all len of data, we do not
2536 want to do an indirect read immediately afterwards. */
2537 if(more && len + j <= 2 * CHUNK_SIZE) {
2538 if(!connection->buf)
2539 connection->buf = get_chunk(); /* checked below */
2541 if(object->chunks[i + 1].data) {
2542 do_stream_3(IO_READ | IO_NOTNOW, connection->fd, j,
2543 object->chunks[i].data, CHUNK_SIZE,
2544 object->chunks[i + 1].data,
2545 MIN(CHUNK_SIZE,
2546 end - (i + 1) * CHUNK_SIZE),
2547 connection->buf, connection->buf ? more : 0,
2548 httpServerDirectHandler2, connection);
2549 return 1;
2551 unlockChunk(object, i + 1);
2553 if(more && len + j <= CHUNK_SIZE) {
2554 if(!connection->buf)
2555 connection->buf = get_chunk();
2557 do_stream_2(IO_READ | IO_NOTNOW, connection->fd, j,
2558 object->chunks[i].data,
2559 MIN(CHUNK_SIZE, end - i * CHUNK_SIZE),
2560 connection->buf, connection->buf ? more : 0,
2561 httpServerDirectHandler, connection);
2562 return 1;
2563 } else {
2564 unlockChunk(object, i);
2568 if(connection->len == 0)
2569 httpConnectionDestroyBuf(connection);
2571 httpSetTimeout(connection, serverTimeout);
2572 do_stream_buf(IO_READ | IO_NOTNOW |
2573 ((immediate && connection->len) ? IO_IMMEDIATE : 0),
2574 connection->fd, connection->len,
2575 &connection->buf,
2576 (connection->te == TE_CHUNKED ?
2577 MIN(2048, CHUNK_SIZE) : CHUNK_SIZE),
2578 httpServerIndirectHandler, connection);
2579 return 1;
2583 httpServerDirectHandlerCommon(int kind, int status,
2584 FdEventHandlerPtr event,
2585 StreamRequestPtr srequest)
2587 HTTPConnectionPtr connection = srequest->data;
2588 HTTPRequestPtr request = connection->request;
2589 ObjectPtr object = request->object;
2590 int i = connection->offset / CHUNK_SIZE;
2591 int to, end, end1;
2593 assert(request->object->flags & OBJECT_INPROGRESS);
2595 httpSetTimeout(connection, -1);
2597 if(status < 0) {
2598 unlockChunk(object, i);
2599 if(kind == 2) unlockChunk(object, i + 1);
2600 if(status != -ECLIENTRESET)
2601 do_log_error(L_ERROR, -status, "Read from server failed");
2602 httpServerAbort(connection, status != -ECLIENTRESET, 502,
2603 internAtomError(-status, "Read from server failed"));
2604 return 1;
2607 /* We have incestuous knowledge of the decisions made in
2608 httpServerReadData */
2609 if(request->to >= 0)
2610 to = request->to;
2611 else
2612 to = object->length;
2613 if(connection->te == TE_CHUNKED)
2614 end = connection->offset + connection->chunk_remaining;
2615 else
2616 end = to;
2617 /* The amount of data actually read into the object */
2618 end1 = MIN(end, i * CHUNK_SIZE + MIN(kind * CHUNK_SIZE, srequest->offset));
2620 assert(end >= 0 && end1 >= i * CHUNK_SIZE && end1 <= (i + 2) * CHUNK_SIZE);
2622 object->chunks[i].size =
2623 MAX(object->chunks[i].size, MIN(end1 - i * CHUNK_SIZE, CHUNK_SIZE));
2624 if(kind == 2 && end1 > (i + 1) * CHUNK_SIZE) {
2625 object->chunks[i + 1].size =
2626 MAX(object->chunks[i + 1].size, end1 - (i + 1) * CHUNK_SIZE);
2628 if(connection->te == TE_CHUNKED) {
2629 connection->chunk_remaining -= (end1 - connection->offset);
2630 assert(connection->chunk_remaining >= 0);
2632 connection->offset = end1;
2633 object->size = MAX(object->size, end1);
2634 unlockChunk(object, i);
2635 if(kind == 2) unlockChunk(object, i + 1);
2637 if(i * CHUNK_SIZE + srequest->offset > end1) {
2638 connection->len = i * CHUNK_SIZE + srequest->offset - end1;
2639 return httpServerIndirectHandlerCommon(connection, status);
2640 } else {
2641 notifyObject(object);
2642 if(status) {
2643 httpServerFinish(connection, 1, 0);
2644 return 1;
2645 } else {
2646 return httpServerReadData(connection, 0);
2652 httpServerDirectHandler(int status,
2653 FdEventHandlerPtr event,
2654 StreamRequestPtr srequest)
2656 return httpServerDirectHandlerCommon(1, status, event, srequest);
2660 httpServerDirectHandler2(int status,
2661 FdEventHandlerPtr event,
2662 StreamRequestPtr srequest)
2664 return httpServerDirectHandlerCommon(2, status, event, srequest);
2667 /* Add the data accumulated in connection->buf into the object in
2668 connection->request. Returns 0 in the normal case, 1 if the TE is
2669 self-terminating and we're done, -1 if there was a problem with
2670 objectAddData, -2 if there was a problem with the data. */
2672 connectionAddData(HTTPConnectionPtr connection, int skip)
2674 HTTPRequestPtr request = connection->request;
2675 ObjectPtr object = request->object;
2676 int rc;
2678 if(connection->te == TE_IDENTITY) {
2679 int len;
2681 len = connection->len - skip;
2682 if(object->length >= 0) {
2683 len = MIN(object->length - connection->offset, len);
2685 if(request->to >= 0)
2686 len = MIN(request->to - connection->offset, len);
2687 if(len > 0) {
2688 rc = objectAddData(object, connection->buf + skip,
2689 connection->offset, len);
2690 if(rc < 0)
2691 return -1;
2692 connection->offset += len;
2693 connection->len -= (len + skip);
2694 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2695 (unsigned long)connection, (unsigned long)object,
2696 connection->offset);
2699 if(connection->len > 0 && skip + len > 0) {
2700 memmove(connection->buf,
2701 connection->buf + skip + len, connection->len);
2704 if((object->length >= 0 && object->length <= connection->offset) ||
2705 (request->to >= 0 && request->to <= connection->offset)) {
2706 notifyObject(object);
2707 return 1;
2708 } else {
2709 if(len > 0)
2710 notifyObject(object);
2711 return 0;
2713 } else if(connection->te == TE_CHUNKED) {
2714 int i = skip, j, size;
2715 /* connection->chunk_remaining is 0 at the end of a chunk, -1
2716 after the CR/LF pair ending a chunk, and -2 after we've
2717 seen a chunk of length 0. */
2718 if(connection->chunk_remaining > -2) {
2719 while(1) {
2720 if(connection->chunk_remaining <= 0) {
2721 if(connection->chunk_remaining == 0) {
2722 if(connection->len < i + 2)
2723 break;
2724 if(connection->buf[i] != '\r' ||
2725 connection->buf[i + 1] != '\n')
2726 return -1;
2727 i += 2;
2728 connection->chunk_remaining = -1;
2730 if(connection->len < i + 2)
2731 break;
2732 j = parseChunkSize(connection->buf, i,
2733 connection->len, &size);
2734 if(j < 0)
2735 return -2;
2736 if(j == 0)
2737 break;
2738 else
2739 i = j;
2740 if(size == 0) {
2741 connection->chunk_remaining = -2;
2742 break;
2743 } else {
2744 connection->chunk_remaining = size;
2746 } else {
2747 /* connection->chunk_remaining > 0 */
2748 size = MIN(connection->chunk_remaining,
2749 connection->len - i);
2750 if(size <= 0)
2751 break;
2752 rc = objectAddData(object, connection->buf + i,
2753 connection->offset, size);
2754 connection->offset += size;
2755 if(rc < 0)
2756 return -1;
2757 i += size;
2758 connection->chunk_remaining -= size;
2759 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2760 (unsigned long)connection,
2761 (unsigned long)object,
2762 connection->offset);
2766 connection->len -= i;
2767 if(connection->len > 0)
2768 memmove(connection->buf, connection->buf + i, connection->len);
2769 if(i > 0 || connection->chunk_remaining == -2)
2770 notifyObject(object);
2771 if(connection->chunk_remaining == -2)
2772 return 1;
2773 else
2774 return 0;
2775 } else {
2776 abort();
2780 void
2781 listServers(FILE *out)
2783 HTTPServerPtr server;
2784 int i, n, m, entry;
2786 fprintf(out, "<!DOCTYPE HTML PUBLIC "
2787 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2788 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2789 "<html><head>\n"
2790 "\r\n<title>Known servers</title>\n"
2791 "</head><body>\n"
2792 "<h1>Known servers</h1>\n");
2794 alternatingHttpStyle(out, "servers");
2795 fprintf(out, "<table id=servers>\n");
2796 fprintf(out, "<thead><tr><th>Server</th>"
2797 "<th>Version</th>"
2798 "<th>Persistent</th>"
2799 "<th>Pipeline</th>"
2800 "<th>Connections</th>"
2801 "<th></th>"
2802 "<th>rtt</th>"
2803 "<th>rate</th>"
2804 "</tr></thead>\n");
2805 fprintf(out, "<tbody>\n");
2806 server = servers;
2807 entry = 0;
2808 while(server) {
2809 fprintf(out, "<tr class=\"%s\">", entry % 2 == 0 ? "even" : "odd");
2810 if(server->port == 80)
2811 fprintf(out, "<td>%s</td>", server->name);
2812 else
2813 fprintf(out, "<td>%s:%d</td>", server->name, server->port);
2815 if(server->version == HTTP_11)
2816 fprintf(out, "<td>1.1</td>");
2817 else if(server->version == HTTP_10)
2818 fprintf(out, "<td>1.0</td>");
2819 else
2820 fprintf(out, "<td>unknown</td>");
2822 if(server->persistent < 0)
2823 fprintf(out, "<td>no</td>");
2824 else if(server->persistent > 0)
2825 fprintf(out, "<td>yes</td>");
2826 else
2827 fprintf(out, "<td>unknown</td>");
2829 if(server->version != HTTP_11 || server->persistent <= 0)
2830 fprintf(out, "<td></td>");
2831 else if(server->pipeline < 0)
2832 fprintf(out, "<td>no</td>");
2833 else if(server->pipeline >= 0 && server->pipeline <= 1)
2834 fprintf(out, "<td>unknown</td>");
2835 else if(server->pipeline == 2 || server->pipeline == 3)
2836 fprintf(out, "<td>probing</td>");
2837 else
2838 fprintf(out, "<td>yes</td>");
2840 n = 0; m = 0;
2841 for(i = 0; i < server->maxslots; i++)
2842 if(server->connection[i] && !server->connection[i]->connecting) {
2843 if(i < server->numslots)
2844 n++;
2845 else
2846 m++;
2849 fprintf(out, "<td>%d/%d", n, server->numslots);
2850 if(m)
2851 fprintf(out, " + %d</td>", m);
2852 else
2853 fprintf(out, "</td>");
2855 if(server->lies > 0)
2856 fprintf(out, "<td>(%d lies)</td>", (server->lies + 9) / 10);
2857 else
2858 fprintf(out, "<td></td>");
2860 if(server->rtt > 0)
2861 fprintf(out, "<td>%.3f</td>", (double)server->rtt / 1000000.0);
2862 else
2863 fprintf(out, "<td></td>");
2864 if(server->rate > 0)
2865 fprintf(out, "<td>%d</td>", server->rate);
2866 else
2867 fprintf(out, "<td></td>");
2869 fprintf(out, "</tr>\n");
2870 server = server->next;
2871 entry++;
2873 fprintf(out, "</tbody>\n");
2874 fprintf(out, "</table>\n");
2875 fprintf(out, "<p><a href=\"/polipo/\">back</a></p>");
2876 fprintf(out, "</body></html>\n");