Remove dead store in cachePrintSeparator().
[polipo.git] / server.c
blob21beddbc2192fcd30453dcc82ce3123b3f4014ab
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;
42 int alwaysAddNoTransform = 0;
44 static HTTPServerPtr servers = 0;
46 static int httpServerContinueConditionHandler(int, ConditionHandlerPtr);
47 static int initParentProxy(void);
48 static int parentProxySetter(ConfigVariablePtr var, void *value);
49 static void httpServerDelayedFinish(HTTPConnectionPtr);
50 static int allowUnalignedRangeRequests = 0;
52 void
53 preinitServer(void)
55 CONFIG_VARIABLE_SETTABLE(parentProxy, CONFIG_ATOM_LOWER, parentProxySetter,
56 "Parent proxy (host:port).");
57 CONFIG_VARIABLE(serverExpireTime, CONFIG_TIME,
58 "Time during which server data is valid.");
59 CONFIG_VARIABLE_SETTABLE(smallRequestTime, CONFIG_TIME, configIntSetter,
60 "Estimated time for a small request.");
61 CONFIG_VARIABLE_SETTABLE(replyUnpipelineTime, CONFIG_TIME, configIntSetter,
62 "Estimated time for a pipeline break.");
63 CONFIG_VARIABLE_SETTABLE(replyUnpipelineSize, CONFIG_INT, configIntSetter,
64 "Size for a pipeline break.");
65 CONFIG_VARIABLE_SETTABLE(pipelineAdditionalRequests, CONFIG_TRISTATE,
66 configIntSetter,
67 "Pipeline requests on an active connection.");
68 CONFIG_VARIABLE_SETTABLE(maxPipelineTrain, CONFIG_INT,
69 configIntSetter,
70 "Maximum number of requests "
71 "pipelined at a time.");
72 CONFIG_VARIABLE(pmmFirstSize, CONFIG_INT,
73 "The size of the first PMM chunk.");
74 CONFIG_VARIABLE(pmmSize, CONFIG_INT,
75 "The size of a PMM chunk.");
76 CONFIG_VARIABLE(serverSlots, CONFIG_INT,
77 "Maximum number of connections per server.");
78 CONFIG_VARIABLE(serverSlots1, CONFIG_INT,
79 "Maximum number of connections per HTTP/1.0 server.");
80 CONFIG_VARIABLE(serverMaxSlots, CONFIG_INT,
81 "Maximum number of connections per broken server.");
82 CONFIG_VARIABLE(dontCacheRedirects, CONFIG_BOOLEAN,
83 "If true, don't cache redirects.");
84 CONFIG_VARIABLE_SETTABLE(allowUnalignedRangeRequests,
85 CONFIG_BOOLEAN, configIntSetter,
86 "Allow unaligned range requests (unreliable).");
87 CONFIG_VARIABLE_SETTABLE(maxSideBuffering,
88 CONFIG_INT, configIntSetter,
89 "Maximum buffering for PUT and POST requests.");
90 CONFIG_VARIABLE_SETTABLE(maxConnectionAge,
91 CONFIG_TIME, configIntSetter,
92 "Maximum age of a server-side connection.");
93 CONFIG_VARIABLE_SETTABLE(maxConnectionRequests,
94 CONFIG_INT, configIntSetter,
95 "Maximum number of requests on a server-side connection.");
96 CONFIG_VARIABLE(alwaysAddNoTransform, CONFIG_BOOLEAN,
97 "If true, add a no-transform directive to all requests.");
100 static int
101 parentProxySetter(ConfigVariablePtr var, void *value)
103 configAtomSetter(var, value);
104 initParentProxy();
105 return 1;
108 static void
109 discardServer(HTTPServerPtr server)
111 HTTPServerPtr previous;
112 assert(!server->request);
114 if(server == servers)
115 servers = server->next;
116 else {
117 previous = servers;
118 while(previous->next != server)
119 previous = previous->next;
120 previous->next = server->next;
123 if(server->connection)
124 free(server->connection);
125 if(server->idleHandler)
126 free(server->idleHandler);
127 if(server->name)
128 free(server->name);
130 free(server);
133 static int
134 httpServerIdle(HTTPServerPtr server)
136 int i;
137 if(server->request)
138 return 0;
139 for(i = 0; i < server->maxslots; i++)
140 if(server->connection[i])
141 return 0;
142 return 1;
145 static int
146 expireServersHandler(TimeEventHandlerPtr event)
148 HTTPServerPtr server, next;
149 TimeEventHandlerPtr e;
150 server = servers;
151 while(server) {
152 next = server->next;
153 if(httpServerIdle(server) &&
154 server->time + serverExpireTime < current_time.tv_sec)
155 discardServer(server);
156 server = next;
158 e = scheduleTimeEvent(serverExpireTime / 60 + 60,
159 expireServersHandler, 0, NULL);
160 if(!e) {
161 do_log(L_ERROR, "Couldn't schedule server expiry.\n");
162 polipoExit();
164 return 1;
167 static int
168 roundSize(int size)
170 return (size + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE;
173 static int
174 initParentProxy()
176 AtomPtr host, port_atom;
177 int rc, port;
179 if(parentHost) {
180 releaseAtom(parentHost);
181 parentHost = NULL;
183 if(parentPort >= 0)
184 parentPort = -1;
186 if(parentProxy != NULL && parentProxy->length == 0) {
187 releaseAtom(parentProxy);
188 parentProxy = NULL;
191 if(parentProxy == NULL)
192 return 1;
194 rc = atomSplit(parentProxy, ':', &host, &port_atom);
195 if(rc <= 0) {
196 do_log(L_ERROR, "Couldn't parse parentProxy.");
197 releaseAtom(parentProxy);
198 parentProxy = NULL;
199 return -1;
202 port = atoi(port_atom->string);
203 if(port <= 0 || port >= 0x10000) {
204 releaseAtom(host);
205 releaseAtom(port_atom);
206 do_log(L_ERROR, "Couldn't parse parentProxy.");
207 releaseAtom(parentProxy);
208 parentProxy = NULL;
209 return -1;
212 parentHost = host;
213 parentPort = port;
214 return 1;
217 void
218 initServer(void)
220 TimeEventHandlerPtr event;
221 servers = NULL;
223 if(pmmFirstSize || pmmSize) {
224 if(pmmSize == 0) pmmSize = pmmFirstSize;
225 if(pmmFirstSize == 0) pmmFirstSize = pmmSize;
226 pmmSize = roundSize(pmmSize);
227 pmmFirstSize = roundSize(pmmFirstSize);
230 if(serverMaxSlots < 1)
231 serverMaxSlots = 1;
232 if(serverSlots < 1)
233 serverSlots = 1;
234 if(serverSlots > serverMaxSlots)
235 serverSlots = serverMaxSlots;
236 if(serverSlots1 < serverSlots)
237 serverSlots1 = serverSlots;
238 if(serverSlots1 > serverMaxSlots)
239 serverSlots1 = serverMaxSlots;
241 initParentProxy();
243 event = scheduleTimeEvent(serverExpireTime / 60 + 60, expireServersHandler,
244 0, NULL);
245 if(event == NULL) {
246 do_log(L_ERROR, "Couldn't schedule server expiry.\n");
247 exit(1);
251 static HTTPServerPtr
252 getServer(char *name, int port, int proxy)
254 HTTPServerPtr server;
255 int i;
257 server = servers;
258 while(server) {
259 if(strcmp(server->name, name) == 0 && server->port == port &&
260 server->isProxy == proxy) {
261 if(httpServerIdle(server) &&
262 server->time + serverExpireTime < current_time.tv_sec) {
263 discardServer(server);
264 server = NULL;
265 break;
266 } else {
267 server->time = current_time.tv_sec;
268 return server;
271 server = server->next;
274 server = malloc(sizeof(HTTPServerRec));
275 if(server == NULL) {
276 do_log(L_ERROR, "Couldn't allocate server.\n");
277 return NULL;
280 server->connection = malloc(serverMaxSlots * sizeof(HTTPConnectionPtr));
281 if(server->connection == NULL) {
282 do_log(L_ERROR, "Couldn't allocate server.\n");
283 free(server);
284 return NULL;
287 server->idleHandler = malloc(serverMaxSlots * sizeof(FdEventHandlerPtr));
288 if(server->connection == NULL) {
289 do_log(L_ERROR, "Couldn't allocate server.\n");
290 free(server->connection);
291 free(server);
292 return NULL;
295 server->maxslots = serverMaxSlots;
297 server->name = strdup(name);
298 if(server->name == NULL) {
299 do_log(L_ERROR, "Couldn't allocate server name.\n");
300 free(server);
301 return NULL;
304 server->port = port;
305 server->addrindex = 0;
306 server->isProxy = proxy;
307 server->version = HTTP_UNKNOWN;
308 server->persistent = 0;
309 server->pipeline = 0;
310 server->time = current_time.tv_sec;
311 server->rtt = -1;
312 server->rate = -1;
313 server->numslots = MIN(serverSlots, server->maxslots);
314 for(i = 0; i < server->maxslots; i++) {
315 server->connection[i] = NULL;
316 server->idleHandler[i] = NULL;
318 server->request = NULL;
319 server->request_last = NULL;
320 server->lies = 0;
322 server->next = servers;
323 servers = server;
324 return server;
328 httpServerQueueRequest(HTTPServerPtr server, HTTPRequestPtr request)
330 assert(request->request && request->request->request == request);
331 assert(request->connection == NULL);
332 if(server->request) {
333 server->request_last->next = request;
334 server->request_last = request;
335 } else {
336 server->request_last = request;
337 server->request = request;
339 return 1;
342 void
343 httpServerAbort(HTTPConnectionPtr connection, int fail,
344 int code, AtomPtr message)
346 HTTPRequestPtr request = connection->request;
347 if(request) {
348 if(request->request) {
349 httpClientError(request->request, code, retainAtom(message));
351 if(fail) {
352 request->object->flags |= OBJECT_FAILED;
353 if(request->object->flags & OBJECT_INITIAL)
354 abortObject(request->object, code, retainAtom(message));
355 notifyObject(request->object);
358 releaseAtom(message);
359 if(!connection->connecting)
360 httpServerFinish(connection, 1, 0);
363 void
364 httpServerAbortRequest(HTTPRequestPtr request, int fail,
365 int code, AtomPtr message)
367 if(request->connection && request == request->connection->request) {
368 httpServerAbort(request->connection, fail, code, message);
369 } else {
370 HTTPRequestPtr requestor = request->request;
371 if(requestor) {
372 requestor->request = NULL;
373 request->request = NULL;
374 httpClientError(requestor, code, retainAtom(message));
376 if(fail) {
377 request->object->flags |= OBJECT_FAILED;
378 if(request->object->flags & OBJECT_INITIAL)
379 abortObject(request->object, code, retainAtom(message));
380 notifyObject(request->object);
382 releaseAtom(message);
386 void
387 httpServerClientReset(HTTPRequestPtr request)
389 if(request->connection &&
390 request->connection->fd >= 0 &&
391 !request->connection->connecting &&
392 request->connection->request == request)
393 pokeFdEvent(request->connection->fd, -ECLIENTRESET, POLLIN | POLLOUT);
398 httpMakeServerRequest(char *name, int port, ObjectPtr object,
399 int method, int from, int to, HTTPRequestPtr requestor)
401 HTTPServerPtr server;
402 HTTPRequestPtr request;
403 int rc;
405 assert(!(object->flags & OBJECT_INPROGRESS));
407 if(parentHost) {
408 server = getServer(parentHost->string, parentPort, 1);
409 } else {
410 server = getServer(name, port, 0);
412 if(server == NULL) return -1;
414 object->flags |= OBJECT_INPROGRESS;
415 object->requestor = requestor;
417 request = httpMakeRequest();
418 if(!request) {
419 do_log(L_ERROR, "Couldn't allocate request.\n");
420 return -1;
423 /* Because we allocate objects in chunks, we cannot have data that
424 doesn't start at a chunk boundary. */
425 if(from % CHUNK_SIZE != 0) {
426 if(allowUnalignedRangeRequests) {
427 objectFillFromDisk(object, from / CHUNK_SIZE * CHUNK_SIZE, 1);
428 if(objectHoleSize(object, from - 1) != 0)
429 from = from / CHUNK_SIZE * CHUNK_SIZE;
430 } else {
431 from = from / CHUNK_SIZE * CHUNK_SIZE;
435 request->object = retainObject(object);
436 request->method = method;
437 if(method == METHOD_CONDITIONAL_GET) {
438 if(server->lies > 0)
439 request->method = METHOD_HEAD;
441 request->flags =
442 REQUEST_PERSISTENT |
443 (expectContinue ? (requestor->flags & REQUEST_WAIT_CONTINUE) : 0);
444 request->from = from;
445 request->to = to;
446 request->request = requestor;
447 requestor->request = request;
448 request->cache_control = requestor->cache_control;
449 request->time0 = null_time;
450 request->time1 = null_time;
452 rc = httpServerQueueRequest(server, request);
453 if(rc < 0) {
454 do_log(L_ERROR, "Couldn't queue request.\n");
455 request->request = NULL;
456 requestor->request = NULL;
457 object->flags &= ~(OBJECT_INPROGRESS | OBJECT_VALIDATING);
458 releaseNotifyObject(object);
459 httpDestroyRequest(request);
460 return 1;
463 if(request->flags & REQUEST_WAIT_CONTINUE) {
464 if(server->version == HTTP_10) {
465 httpServerAbortRequest(request, 1,
466 417, internAtom("Expectation failed"));
467 return 1;
469 } else if(expectContinue >= 2 && server->version == HTTP_11) {
470 if(request->method == METHOD_POST || request->method == METHOD_PUT)
471 request->flags |= REQUEST_WAIT_CONTINUE;
474 again:
475 rc = httpServerTrigger(server);
476 if(rc < 0) {
477 /* We must be very short on memory. If there are any requests
478 queued, we abort one and try again. If there aren't, we
479 give up. */
480 do_log(L_ERROR, "Couldn't trigger server -- out of memory?\n");
481 if(server->request) {
482 httpServerAbortRequest(server->request, 1, 503,
483 internAtom("Couldn't trigger server"));
484 goto again;
487 return 1;
491 httpServerConnection(HTTPServerPtr server)
493 HTTPConnectionPtr connection;
494 int i;
496 connection = httpMakeConnection();
497 if(connection == NULL) {
498 do_log(L_ERROR, "Couldn't allocate server connection.\n");
499 return -1;
501 connection->server = server;
503 for(i = 0; i < server->numslots; i++) {
504 if(!server->connection[i]) {
505 server->connection[i] = connection;
506 break;
509 assert(i < server->numslots);
511 connection->request = NULL;
512 connection->request_last = NULL;
514 do_log(D_SERVER_CONN, "C... %s:%d.\n",
515 scrub(connection->server->name), connection->server->port);
516 httpSetTimeout(connection, serverTimeout);
517 if(socksParentProxy) {
518 connection->connecting = CONNECTING_SOCKS;
519 do_socks_connect(server->name, connection->server->port,
520 httpServerSocksHandler, connection);
521 } else {
522 connection->connecting = CONNECTING_DNS;
523 do_gethostbyname(server->name, 0,
524 httpServerConnectionDnsHandler,
525 connection);
527 return 1;
531 httpServerConnectionDnsHandler(int status, GethostbynameRequestPtr request)
533 HTTPConnectionPtr connection = request->data;
535 httpSetTimeout(connection, -1);
537 if(status <= 0) {
538 AtomPtr message;
539 message = internAtomF("Host %s lookup failed: %s",
540 request->name ?
541 request->name->string : "(unknown)",
542 request->error_message ?
543 request->error_message->string :
544 pstrerror(-status));
545 do_log(L_ERROR, "Host %s lookup failed: %s (%d).\n",
546 request->name ?
547 scrub(request->name->string) : "(unknown)",
548 request->error_message ?
549 request->error_message->string :
550 pstrerror(-status), -status);
551 connection->connecting = 0;
552 if(connection->server->request)
553 httpServerAbortRequest(connection->server->request, 1, 504,
554 retainAtom(message));
555 httpServerAbort(connection, 1, 502, message);
556 return 1;
559 if(request->addr->string[0] == DNS_CNAME) {
560 if(request->count > 10) {
561 AtomPtr message = internAtom("DNS CNAME loop");
562 do_log(L_ERROR, "DNS CNAME loop.\n");
563 connection->connecting = 0;
564 if(connection->server->request)
565 httpServerAbortRequest(connection->server->request, 1, 504,
566 retainAtom(message));
567 httpServerAbort(connection, 1, 504, message);
568 return 1;
571 httpSetTimeout(connection, serverTimeout);
572 do_gethostbyname(request->addr->string + 1, request->count + 1,
573 httpServerConnectionDnsHandler,
574 connection);
575 return 1;
578 connection->connecting = CONNECTING_CONNECT;
579 httpSetTimeout(connection, serverTimeout);
580 do_connect(retainAtom(request->addr), connection->server->addrindex,
581 connection->server->port,
582 httpServerConnectionHandler, connection);
583 return 1;
587 httpServerConnectionHandler(int status,
588 FdEventHandlerPtr event,
589 ConnectRequestPtr request)
591 HTTPConnectionPtr connection = request->data;
593 assert(connection->fd < 0);
594 if(request->fd >= 0) {
595 int rc;
596 connection->fd = request->fd;
597 connection->server->addrindex = request->index;
598 rc = setNodelay(connection->fd, 1);
599 if(rc < 0)
600 do_log_error(L_WARN, errno, "Couldn't disable Nagle's algorithm");
603 return httpServerConnectionHandlerCommon(status, connection);
607 httpServerSocksHandler(int status, SocksRequestPtr request)
609 HTTPConnectionPtr connection = request->data;
611 assert(connection->fd < 0);
612 if(request->fd >= 0) {
613 connection->fd = request->fd;
614 connection->server->addrindex = 0;
616 return httpServerConnectionHandlerCommon(status, connection);
620 httpServerConnectionHandlerCommon(int status, HTTPConnectionPtr connection)
622 httpSetTimeout(connection, -1);
624 if(status < 0) {
625 AtomPtr message =
626 internAtomError(-status, "Connect to %s:%d failed",
627 connection->server->name,
628 connection->server->port);
629 if(status != -ECLIENTRESET)
630 do_log_error(L_ERROR, -status, "Connect to %s:%d failed",
631 scrub(connection->server->name),
632 connection->server->port);
633 connection->connecting = 0;
634 if(connection->server->request)
635 httpServerAbortRequest(connection->server->request,
636 status != -ECLIENTRESET, 504,
637 retainAtom(message));
638 httpServerAbort(connection, status != -ECLIENTRESET, 504, message);
639 return 1;
642 do_log(D_SERVER_CONN, "C %s:%d.\n",
643 scrub(connection->server->name), connection->server->port);
645 connection->connecting = 0;
646 /* serverTrigger will take care of inserting any timeouts */
647 httpServerTrigger(connection->server);
648 return 1;
652 httpServerIdleHandler(int a, FdEventHandlerPtr event)
654 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
655 HTTPServerPtr server = connection->server;
656 int i;
658 assert(!connection->request);
660 do_log(D_SERVER_CONN, "Idle connection to %s:%d died.\n",
661 scrub(connection->server->name), connection->server->port);
663 for(i = 0; i < server->maxslots; i++) {
664 if(connection == server->connection[i]) {
665 server->idleHandler[i] = NULL;
666 break;
669 assert(i < server->maxslots);
671 httpServerAbort(connection, 1, 504, internAtom("Timeout"));
672 return 1;
675 /* Discard aborted requests at the head of the queue. */
676 static void
677 httpServerDiscardRequests(HTTPServerPtr server)
679 HTTPRequestPtr request;
680 while(server->request && !server->request->request) {
681 request = server->request;
682 server->request = request->next;
683 request->next = NULL;
684 if(server->request == NULL)
685 server->request_last = NULL;
686 request->object->flags &= ~(OBJECT_INPROGRESS | OBJECT_VALIDATING);
687 releaseNotifyObject(request->object);
688 request->object = NULL;
689 httpDestroyRequest(request);
693 static int
694 pipelineIsSmall(HTTPConnectionPtr connection)
696 HTTPRequestPtr request = connection->request;
698 if(pipelineAdditionalRequests <= 0)
699 return 0;
700 else if(pipelineAdditionalRequests >= 2)
701 return 1;
703 if(!request)
704 return 1;
705 if(request->next || !(request->flags & REQUEST_PERSISTENT))
706 return 0;
707 if(request->method == METHOD_HEAD ||
708 request->method == METHOD_CONDITIONAL_GET)
709 return 1;
710 if(request->to >= 0 && connection->server->rate > 0 &&
711 request->to - request->from < connection->server->rate *
712 smallRequestTime)
713 return 1;
714 return 0;
717 static int
718 numRequests(HTTPServerPtr server)
720 int n = 0;
721 HTTPRequestPtr request = server->request;
722 while(request) {
723 n++;
724 request = request->next;
726 return n;
729 HTTPConnectionPtr
730 httpServerGetConnection(HTTPServerPtr server, int *idle_return)
732 int i, j;
733 int connecting = 0, empty = 0, idle = 0;
735 j = -1;
736 /* Try to find an idle connection */
737 for(i = 0; i < server->numslots; i++) {
738 if(server->connection[i]) {
739 if(!server->connection[i]->connecting) {
740 if(!server->connection[i]->request) {
741 if(server->idleHandler[i])
742 unregisterFdEvent(server->idleHandler[i]);
743 server->idleHandler[i] = NULL;
744 if(j < 0) j = i;
745 idle++;
747 } else
748 connecting++;
749 } else
750 empty++;
753 if(j >= 0) {
754 *idle_return = idle;
755 return server->connection[j];
758 /* If there's an empty slot, schedule connection creation */
759 if(empty) {
760 /* Don't open a connection if there are already enough in
761 progress, except if the server doesn't do persistent
762 connections and there's only one in progress. */
763 if((connecting == 0 || (server->persistent <= 0 && connecting <= 1)) ||
764 connecting < numRequests(server)) {
765 httpServerConnection(server);
769 /* Find a connection that can accept additional requests */
770 if(server->version == HTTP_11 && server->pipeline >= 4) {
771 for(i = 0; i < serverSlots; i++) {
772 if(server->connection[i] && !server->connection[i]->connecting &&
773 pipelineIsSmall(server->connection[i])) {
774 if(server->idleHandler[i])
775 unregisterFdEvent(server->idleHandler[i]);
776 server->idleHandler[i] = NULL;
777 *idle_return = 0;
778 return server->connection[i];
782 *idle_return = idle;
783 return NULL;
787 httpServerTrigger(HTTPServerPtr server)
789 HTTPConnectionPtr connection;
790 HTTPRequestPtr request;
791 int idle, n, i, rc, numidle;
793 while(server->request) {
794 httpServerDiscardRequests(server);
796 if(!server->request)
797 break;
799 if(REQUEST_SIDE(server->request)) {
800 rc = httpServerSideRequest(server);
801 /* If rc is 0, httpServerSideRequest didn't dequeue this
802 request. Go through the scheduling loop again, come
803 back later. */
804 if(rc <= 0) break;
805 continue;
807 connection = httpServerGetConnection(server, &numidle);
808 if(!connection) break;
810 /* If server->pipeline <= 0, we don't do pipelining. If
811 server->pipeline is 1, then we are ready to start probing
812 for pipelining on the server; we then send exactly two
813 requests in what is hopefully a single packet to check
814 whether the server has the nasty habit of discarding its
815 input buffers after each request.
816 If server->pipeline is 2 or 3, the pipelining probe is in
817 progress on this server, and we don't pipeline anything
818 until it succeeds. When server->pipeline >= 4, pipelining
819 is believed to work on this server. */
820 if(server->version != HTTP_11 || server->pipeline <= 0 ||
821 server->pipeline == 2 || server->pipeline == 3) {
822 if(connection->pipelined == 0)
823 n = 1;
824 else
825 n = 0;
826 } else if(server->pipeline == 1) {
827 if(connection->pipelined == 0)
828 n = MIN(2, maxPipelineTrain);
829 else
830 n = 0;
831 } else {
832 n = maxPipelineTrain;
835 /* Don't pipeline if there are more idle connections */
836 if(numidle >= 2)
837 n = MIN(n, 1);
839 idle = !connection->pipelined;
840 i = 0;
841 while(server->request && connection->pipelined < n) {
842 httpServerDiscardRequests(server);
843 if(!server->request) break;
844 request = server->request;
845 assert(request->request->request == request);
846 rc = httpWriteRequest(connection, request, -1);
847 if(rc < 0) {
848 if(i == 0)
849 httpServerAbortRequest(request, rc != -ECLIENTRESET, 502,
850 internAtom("Couldn't "
851 "write request"));
852 break;
854 do_log(D_SERVER_CONN, "W: ");
855 do_log_n(D_SERVER_CONN,
856 request->object->key, request->object->key_size);
857 do_log(D_SERVER_CONN, " (%d)\n", request->method);
858 if(connection->pipelined > 0)
859 request->flags |= REQUEST_PIPELINED;
860 request->time0 = current_time;
861 i++;
862 server->request = request->next;
863 request->next = NULL;
864 if(server->request == NULL)
865 server->request_last = NULL;
866 httpQueueRequest(connection, request);
867 connection->pipelined++;
869 if(server->persistent > 0 && server->pipeline == 1 && i >= 2)
870 server->pipeline = 2;
872 if(i > 0) httpServerSendRequest(connection);
874 if(idle && connection->pipelined > 0)
875 httpServerReply(connection, 0);
877 if(i == 0) break;
880 for(i = 0; i < server->maxslots; i++) {
881 if(server->connection[i] &&
882 !server->connection[i]->connecting &&
883 !server->connection[i]->request) {
884 /* Artificially age any fresh connections that aren't used
885 straight away; this is necessary for the logic for POST and
886 the logic that determines whether a given request should be
887 restarted. */
888 if(server->connection[i]->serviced == 0)
889 server->connection[i]->serviced = 1;
890 if(!server->idleHandler[i])
891 server->idleHandler[i] =
892 registerFdEvent(server->connection[i]->fd, POLLIN,
893 httpServerIdleHandler,
894 sizeof(HTTPConnectionPtr),
895 &server->connection[i]);
896 if(!server->idleHandler[i]) {
897 do_log(L_ERROR, "Couldn't register idle handler.\n");
898 httpServerFinish(server->connection[i], 1, 0);
900 httpSetTimeout(server->connection[i], serverIdleTimeout);
904 return 1;
908 httpServerSideRequest(HTTPServerPtr server)
910 HTTPRequestPtr request = server->request;
911 HTTPConnectionPtr connection;
912 HTTPRequestPtr requestor = request->request;
913 HTTPConnectionPtr client = requestor->connection;
914 int rc, i, freeslots, idle, connecting;
916 assert(REQUEST_SIDE(request));
918 connection = NULL;
919 freeslots = 0;
920 idle = -1;
921 connecting = 0;
923 /* Find a fresh connection */
924 for(i = 0; i < server->numslots; i++) {
925 if(!server->connection[i])
926 freeslots++;
927 else if(!server->connection[i]->connecting) {
928 if(!server->connection[i]->request) {
929 if(server->connection[i]->serviced == 0) {
930 if(server->idleHandler[i])
931 unregisterFdEvent(server->idleHandler[i]);
932 server->idleHandler[i] = NULL;
933 connection = server->connection[i];
934 break;
935 } else {
936 idle = i;
939 } else {
940 connecting++;
944 if(!connection) {
945 /* Make sure that a fresh connection will be established at some
946 point, then wait until httpServerTrigger calls us again. */
947 if(freeslots) {
948 httpServerConnection(server);
949 } else {
950 if(idle >= 0) {
951 /* Shutdown a random idle connection */
952 pokeFdEvent(server->connection[idle]->fd,
953 -EDOSHUTDOWN, POLLIN | POLLOUT);
956 return 0;
959 rc = httpWriteRequest(connection, request, client->bodylen);
960 if(rc < 0) {
961 do_log(L_ERROR, "Couldn't write POST or PUT request.\n");
962 httpServerAbortRequest(request, rc != -ECLIENTRESET, 502,
963 internAtom("Couldn't write request"));
964 return 0;
966 server->request = request->next;
967 request->next = NULL;
968 if(server->request == NULL)
969 server->request_last = NULL;
970 httpQueueRequest(connection, request);
971 connection->pipelined = 1;
972 request->time0 = current_time;
973 connection->reqoffset = 0;
974 connection->bodylen = client->bodylen;
975 httpServerDoSide(connection);
976 return 1;
979 int
980 httpServerDoSide(HTTPConnectionPtr connection)
982 HTTPRequestPtr request = connection->request;
983 HTTPRequestPtr requestor = request->request;
984 HTTPConnectionPtr client = requestor->connection;
985 int len = MIN(client->reqlen - client->reqbegin,
986 connection->bodylen - connection->reqoffset);
987 int doflush =
988 len > 0 &&
989 (len >= maxSideBuffering ||
990 client->reqbegin > 0 ||
991 (connection->reqoffset + client->reqlen - client->reqbegin) >=
992 connection->bodylen);
993 int done = connection->reqoffset >= connection->bodylen;
995 assert(connection->bodylen >= 0);
997 httpSetTimeout(connection, 60);
999 if(connection->reqlen > 0) {
1000 /* Send the headers, but don't send any part of the body if
1001 we're in wait_continue. */
1002 do_stream_2(IO_WRITE,
1003 connection->fd, 0,
1004 connection->reqbuf, connection->reqlen,
1005 client->reqbuf + client->reqbegin,
1006 (request->flags & REQUEST_WAIT_CONTINUE) ? 0 : len,
1007 httpServerSideHandler2, connection);
1008 httpServerReply(connection, 0);
1009 } else if(request->object->flags & OBJECT_ABORTED) {
1010 if(connection->reqbuf)
1011 dispose_chunk(connection->reqbuf);
1012 connection->reqbuf = NULL;
1013 connection->reqlen = 0;
1014 pokeFdEvent(connection->fd, -ESHUTDOWN, POLLIN);
1015 if(client->flags & CONN_READER) {
1016 client->flags |= CONN_SIDE_READER;
1017 do_stream(IO_READ | IO_IMMEDIATE | IO_NOTNOW,
1018 client->fd, 0, NULL, 0,
1019 httpClientSideHandler, client);
1021 } else if(!(request->flags & REQUEST_WAIT_CONTINUE) && doflush) {
1022 /* Make sure there's a reqbuf, as httpServerFinish uses
1023 it to determine if there's a writer. */
1024 if(connection->reqbuf == NULL)
1025 connection->reqbuf = get_chunk();
1026 assert(connection->reqbuf != NULL);
1027 do_stream(IO_WRITE,
1028 connection->fd, 0,
1029 client->reqbuf + client->reqbegin, len,
1030 httpServerSideHandler, connection);
1031 } else {
1032 if(connection->reqbuf) {
1033 httpConnectionDestroyReqbuf(connection);
1034 connection->reqlen = 0;
1036 if(request->flags & REQUEST_WAIT_CONTINUE) {
1037 ConditionHandlerPtr chandler;
1038 do_log(D_SERVER_CONN, "W... %s:%d.\n",
1039 scrub(connection->server->name), connection->server->port);
1040 chandler =
1041 conditionWait(&request->object->condition,
1042 httpServerContinueConditionHandler,
1043 sizeof(connection), &connection);
1044 if(chandler)
1045 return 1;
1046 else
1047 do_log(L_ERROR, "Couldn't register condition handler.\n");
1048 /* Fall through -- the client side will clean up. */
1050 client->flags |= CONN_SIDE_READER;
1051 do_stream(IO_READ | (done ? IO_IMMEDIATE : 0 ) | IO_NOTNOW,
1052 client->fd, client->reqlen,
1053 client->reqbuf, CHUNK_SIZE,
1054 httpClientSideHandler, client);
1056 return 1;
1059 static int
1060 httpClientDelayedDoSideHandler(TimeEventHandlerPtr event)
1062 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1063 httpServerDoSide(connection);
1064 return 1;
1067 static int
1068 httpServerDelayedDoSide(HTTPConnectionPtr connection)
1070 TimeEventHandlerPtr handler;
1071 handler = scheduleTimeEvent(0, httpClientDelayedDoSideHandler,
1072 sizeof(connection), &connection);
1073 if(!handler) {
1074 do_log(L_ERROR, "Couldn't schedule DoSide -- freeing memory.\n");
1075 free_chunk_arenas();
1076 handler = scheduleTimeEvent(0, httpClientDelayedDoSideHandler,
1077 sizeof(connection), &connection);
1078 do_log(L_ERROR, "Couldn't schedule DoSide.\n");
1079 /* Somebody will hopefully end up timing out. */
1080 return 1;
1082 return 1;
1085 static int
1086 httpServerSideHandlerCommon(int kind, int status,
1087 FdEventHandlerPtr event,
1088 StreamRequestPtr srequest)
1090 HTTPConnectionPtr connection = srequest->data;
1091 HTTPRequestPtr request = connection->request;
1092 HTTPRequestPtr requestor = request->request;
1093 HTTPConnectionPtr client = requestor->connection;
1094 int bodylen;
1096 assert(request->object->flags & OBJECT_INPROGRESS);
1098 if(status) {
1099 do_log_error(L_ERROR, -status, "Couldn't write to server");
1100 httpConnectionDestroyReqbuf(connection);
1101 if(status != -ECLIENTRESET)
1102 shutdown(connection->fd, 2);
1103 abortObject(request->object, 502,
1104 internAtom("Couldn't write to server"));
1105 /* Let the read side handle the error */
1106 httpServerDoSide(connection);
1107 return 1;
1110 assert(srequest->offset > 0);
1112 if(kind == 2) {
1113 if(srequest->offset < connection->reqlen)
1114 return 0;
1115 bodylen = srequest->offset - connection->reqlen;
1116 connection->reqlen = 0;
1117 httpConnectionDestroyReqbuf(connection);
1118 } else {
1119 bodylen = srequest->offset;
1123 assert(client->reqbegin + bodylen <= client->reqlen);
1125 if(client->reqlen > client->reqbegin + bodylen)
1126 memmove(client->reqbuf, client->reqbuf + client->reqbegin + bodylen,
1127 client->reqlen - client->reqbegin - bodylen);
1128 client->reqlen -= bodylen + client->reqbegin;
1129 client->reqbegin = 0;
1130 connection->reqoffset += bodylen;
1131 httpServerDoSide(connection);
1132 return 1;
1136 httpServerSideHandler(int status,
1137 FdEventHandlerPtr event,
1138 StreamRequestPtr srequest)
1140 return httpServerSideHandlerCommon(1, status, event, srequest);
1144 httpServerSideHandler2(int status,
1145 FdEventHandlerPtr event,
1146 StreamRequestPtr srequest)
1148 return httpServerSideHandlerCommon(2, status, event, srequest);
1151 static int
1152 httpServerContinueConditionHandler(int status, ConditionHandlerPtr chandler)
1154 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)chandler->data;
1156 if(connection->request->flags & REQUEST_WAIT_CONTINUE)
1157 return 0;
1158 httpServerDelayedDoSide(connection);
1159 return 1;
1162 /* s is 0 to keep the connection alive, 1 to shutdown the connection */
1163 void
1164 httpServerFinish(HTTPConnectionPtr connection, int s, int offset)
1166 HTTPServerPtr server = connection->server;
1167 HTTPRequestPtr request = connection->request;
1168 int i;
1170 if(request) {
1171 assert(connection->pipelined >= 1);
1172 assert((connection->pipelined > 1) == (request->next != NULL));
1173 } else {
1174 assert(connection->pipelined == 0);
1177 if(!s && (!connection->request ||
1178 !(connection->request->flags & REQUEST_PERSISTENT)))
1179 s = 1;
1181 if(connection->serviced >= maxConnectionRequests ||
1182 connection->time < current_time.tv_sec - maxConnectionAge)
1183 s = 1;
1185 if(connection->reqbuf) {
1186 /* As most normal requests go out in a single packet, this is
1187 extremely unlikely to happen. As for POST/PUT requests,
1188 they are not pipelined, so this can only happen if the
1189 server sent an error reply early. */
1190 assert(connection->fd >= 0);
1191 shutdown(connection->fd, 1);
1192 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLOUT);
1193 httpServerDelayedFinish(connection);
1194 goto done;
1197 if(request) {
1198 /* Update statistics about the server */
1199 int size = -1, d = -1, rtt = -1, rate = -1;
1200 if(connection->offset > 0 && request->from >= 0)
1201 size = connection->offset - request->from;
1202 if(request->time1.tv_sec != null_time.tv_sec) {
1203 d = timeval_minus_usec(&current_time, &request->time1);
1204 if(!(request->flags & REQUEST_PIPELINED) &&
1205 request->time0.tv_sec != null_time.tv_sec)
1206 rtt = timeval_minus_usec(&request->time1, &request->time0);
1207 if(size >= 8192 && d > 50000)
1208 rate = ((double)size / (double)d) * 1000000.0 + 0.5;
1210 request->time0 = null_time;
1211 request->time1 = null_time;
1213 if(rtt >= 0) {
1214 if(server->rtt >= 0)
1215 server->rtt = (3 * server->rtt + rtt + 2) / 4;
1216 else
1217 server->rtt = rtt;
1219 if(rate >= 0) {
1220 if(server->rate >= 0)
1221 server->rate = (3 * server->rate + rate + 2) / 4;
1222 else
1223 server->rate = rate;
1226 httpDequeueRequest(connection);
1227 connection->pipelined--;
1228 request->object->flags &= ~(OBJECT_INPROGRESS | OBJECT_VALIDATING);
1229 if(request->request) {
1230 request->request->request = NULL;
1231 request->request = NULL;
1233 releaseNotifyObject(request->object);
1234 request->object = NULL;
1235 httpDestroyRequest(request);
1238 do_log(D_SERVER_CONN, "Done with server %s:%d connection (%d)\n",
1239 scrub(connection->server->name), connection->server->port, s);
1241 assert(offset <= connection->len);
1243 if(!s) {
1244 if(offset < connection->len) {
1245 assert(connection->buf != NULL);
1246 if(!connection->pipelined) {
1247 do_log(L_WARN,
1248 "Closing connection to %s:%d: "
1249 "%d stray bytes of data.\n",
1250 scrub(server->name), server->port,
1251 connection->len - offset);
1252 s = 1;
1253 } else {
1254 memmove(connection->buf, connection->buf + offset,
1255 connection->len - offset);
1256 connection->len = connection->len - offset;
1257 if((connection->flags & CONN_BIGBUF) &&
1258 connection->len <= CHUNK_SIZE)
1259 httpConnectionUnbigify(connection);
1261 } else {
1262 connection->len = 0;
1266 connection->server->time = current_time.tv_sec;
1267 connection->serviced++;
1269 if(s) {
1270 if(connection->timeout)
1271 cancelTimeEvent(connection->timeout);
1272 connection->timeout = NULL;
1273 httpConnectionDestroyBuf(connection);
1274 if(connection->fd >= 0)
1275 CLOSE(connection->fd);
1276 connection->fd = -1;
1277 server->persistent -= 1;
1278 if(server->persistent < -5)
1279 server->numslots = MIN(server->maxslots, serverMaxSlots);
1280 if(connection->request) {
1281 HTTPRequestPtr req;
1282 do_log(D_SERVER_CONN, "Restarting pipeline to %s:%d.\n",
1283 scrub(server->name), server->port);
1284 if(server->pipeline == 2)
1285 server->pipeline -= 20;
1286 else
1287 server->pipeline -= 5;
1288 req = connection->request;
1289 while(req) {
1290 req->connection = NULL;
1291 req = req->next;
1293 if(server->request)
1294 connection->request_last->next = server->request;
1295 else
1296 server->request_last = connection->request_last;
1297 server->request = connection->request;
1298 connection->request = NULL;
1299 connection->request_last = NULL;
1301 /* Make sure we don't get confused into thinking a probe
1302 is in progress. */
1303 if(server->pipeline == 2 || server->pipeline == 3)
1304 server->pipeline = 1;
1305 for(i = 0; i < server->maxslots; i++)
1306 if(connection == server->connection[i])
1307 break;
1308 assert(i < server->maxslots);
1309 if(server->idleHandler[i])
1310 unregisterFdEvent(server->idleHandler[i]);
1311 server->idleHandler[i] = NULL;
1312 server->connection[i] = NULL;
1313 free(connection);
1314 } else {
1315 server->persistent += 1;
1316 if(server->persistent > 0)
1317 server->numslots = MIN(server->maxslots,
1318 server->version == HTTP_10 ?
1319 serverSlots1 : serverSlots);
1320 httpSetTimeout(connection, serverTimeout);
1321 /* See httpServerTrigger */
1322 if(connection->pipelined ||
1323 (server->version == HTTP_11 && server->pipeline <= 0) ||
1324 (server->pipeline == 3)) {
1325 server->pipeline++;
1327 if(connection->pipelined) {
1328 httpServerReply(connection, 1);
1329 } else {
1330 httpConnectionDestroyBuf(connection);
1334 done:
1335 httpServerTrigger(server);
1338 static int
1339 httpServerDelayedFinishHandler(TimeEventHandlerPtr event)
1341 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1342 httpServerFinish(connection, 1, 0);
1343 return 1;
1346 static void
1347 httpServerDelayedFinish(HTTPConnectionPtr connection)
1349 TimeEventHandlerPtr handler;
1351 handler = scheduleTimeEvent(1, httpServerDelayedFinishHandler,
1352 sizeof(connection), &connection);
1353 if(!handler) {
1354 do_log(L_ERROR,
1355 "Couldn't schedule delayed finish -- freeing memory.");
1356 free_chunk_arenas();
1357 handler = scheduleTimeEvent(1, httpServerDelayedFinishHandler,
1358 sizeof(connection), &connection);
1359 if(!handler) {
1360 do_log(L_ERROR,
1361 "Couldn't schedule delayed finish -- aborting.\n");
1362 polipoExit();
1367 void
1368 httpServerReply(HTTPConnectionPtr connection, int immediate)
1370 assert(connection->pipelined > 0);
1372 if(connection->request->request == NULL) {
1373 do_log(L_WARN, "Aborting pipeline on %s:%d.\n",
1374 scrub(connection->server->name), connection->server->port);
1375 httpServerFinish(connection, 1, 0);
1376 return;
1379 do_log(D_SERVER_CONN, "R: %s (%d)\n",
1380 scrub(connection->request->object->key),
1381 connection->request->method);
1383 if(connection->len == 0)
1384 httpConnectionDestroyBuf(connection);
1386 httpSetTimeout(connection, serverTimeout);
1387 do_stream_buf(IO_READ | (immediate ? IO_IMMEDIATE : 0) | IO_NOTNOW,
1388 connection->fd, connection->len,
1389 &connection->buf, CHUNK_SIZE,
1390 httpServerReplyHandler, connection);
1394 httpConnectionPipelined(HTTPConnectionPtr connection)
1396 HTTPRequestPtr request = connection->request;
1397 int i = 0;
1398 while(request) {
1399 i++;
1400 request = request->next;
1402 return i;
1405 void
1406 httpServerUnpipeline(HTTPRequestPtr request)
1408 HTTPConnectionPtr connection = request->connection;
1409 HTTPServerPtr server = connection->server;
1411 request->flags &= ~REQUEST_PERSISTENT;
1412 if(request->next) {
1413 HTTPRequestPtr req;
1414 do_log(L_WARN,
1415 "Restarting pipeline to %s:%d.\n",
1416 scrub(connection->server->name), connection->server->port);
1417 req = request->next;
1418 while(req) {
1419 req->connection = NULL;
1420 req = req->next;
1422 if(server->request)
1423 connection->request_last->next = server->request;
1424 else
1425 server->request_last = connection->request_last;
1426 server->request = request->next;
1427 request->next = NULL;
1428 connection->request_last = request;
1430 connection->pipelined = httpConnectionPipelined(connection);
1433 void
1434 httpServerRestart(HTTPConnectionPtr connection)
1436 HTTPServerPtr server = connection->server;
1437 HTTPRequestPtr request = connection->request;
1439 if(request) {
1440 HTTPRequestPtr req;
1441 if(request->next)
1442 do_log(L_WARN,
1443 "Restarting pipeline to %s:%d.\n",
1444 scrub(connection->server->name), connection->server->port);
1445 req = request;
1446 while(req) {
1447 req->connection = NULL;
1448 req = req->next;
1450 if(server->request)
1451 connection->request_last->next = server->request;
1452 else
1453 server->request_last = connection->request_last;
1454 server->request = request;
1455 connection->request = NULL;
1456 connection->request_last = NULL;
1458 connection->pipelined = 0;
1459 httpServerFinish(connection, 1, 0);
1463 httpServerRequest(ObjectPtr object, int method, int from, int to,
1464 HTTPRequestPtr requestor, void *closure)
1466 int rc;
1467 char name[132];
1468 int port;
1469 int x, y, z;
1471 assert(from >= 0 && (to < 0 || to > from));
1472 assert(closure == NULL);
1473 assert(!(object->flags & OBJECT_LOCAL));
1474 assert(object->type == OBJECT_HTTP);
1476 if(object->flags & OBJECT_INPROGRESS)
1477 return 1;
1479 if(requestor->flags & REQUEST_REQUESTED)
1480 return 0;
1482 assert(requestor->request == NULL);
1484 if(proxyOffline)
1485 return -1;
1487 rc = parseUrl(object->key, object->key_size, &x, &y, &port, &z);
1489 if(rc < 0 || x < 0 || y < 0 || y - x > 131) {
1490 do_log(L_ERROR, "Couldn't parse URL %s\n", scrub(object->key));
1491 abortObject(object, 400, internAtom("Couldn't parse URL"));
1492 notifyObject(object);
1493 return 1;
1496 if(!intListMember(port, allowedPorts)) {
1497 do_log(L_ERROR, "Attempted connection to port %d.\n", port);
1498 abortObject(object, 403, internAtom("Forbidden port"));
1499 notifyObject(object);
1500 return 1;
1503 memcpy(name, ((char*)object->key) + x, y - x);
1504 name[y - x] = '\0';
1506 requestor->flags |= REQUEST_REQUESTED;
1507 rc = httpMakeServerRequest(name, port, object, method, from, to,
1508 requestor);
1510 if(rc < 0) {
1511 abortObject(object,
1512 503, internAtom("Couldn't schedule server request"));
1513 notifyObject(object);
1514 return 1;
1517 return 1;
1521 httpWriteRequest(HTTPConnectionPtr connection, HTTPRequestPtr request,
1522 int bodylen)
1524 ObjectPtr object = request->object;
1525 int from = request->from, to = request->to, method = request->method;
1526 char *url = object->key, *m;
1527 int url_size = object->key_size;
1528 int x, y, port, z, location_size;
1529 char *location;
1530 int l, n, rc, bufsize;
1532 assert(method != METHOD_NONE);
1534 if(request->method == METHOD_GET ||
1535 request->method == METHOD_CONDITIONAL_GET) {
1536 if(to >= 0) {
1537 assert(to >= from);
1538 if(to == from) {
1539 do_log(L_ERROR, "Requesting empty segment?\n");
1540 return -1;
1544 if(object->flags & OBJECT_DYNAMIC) {
1545 from = 0;
1546 to = -1;
1547 } else {
1548 objectFillFromDisk(object, from / CHUNK_SIZE * CHUNK_SIZE, 1);
1549 l = objectHoleSize(request->object, from);
1550 if(l > 0) {
1551 if(to <= 0 || to > from + l)
1552 to = from + l;
1555 if(pmmSize && connection->server->pipeline >= 4) {
1556 if(from == 0)
1557 to = to < 0 ? pmmFirstSize : MIN(to, pmmFirstSize);
1558 else
1559 to = to < 0 ? from + pmmSize : MIN(to, from + pmmSize);
1562 if(from % CHUNK_SIZE != 0)
1563 if(objectHoleSize(object, from - 1) != 0)
1564 from = from / CHUNK_SIZE * CHUNK_SIZE;
1568 rc = parseUrl(url, url_size, &x, &y, &port, &z);
1570 if(rc < 0 || x < 0 || y < 0) {
1571 return -1;
1574 if(connection->reqbuf == NULL) {
1575 connection->reqbuf = get_chunk();
1576 if(connection->reqbuf == NULL)
1577 return -1;
1578 connection->reqlen = 0;
1581 if(method == METHOD_CONDITIONAL_GET &&
1582 object->last_modified < 0 && object->etag == NULL)
1583 method = request->method = METHOD_GET;
1585 again:
1586 bufsize =
1587 (connection->flags & CONN_BIGREQBUF) ? bigBufferSize : CHUNK_SIZE;
1588 n = connection->reqlen;
1589 switch(method) {
1590 case METHOD_GET:
1591 case METHOD_CONDITIONAL_GET: m = "GET"; break;
1592 case METHOD_HEAD: m = "HEAD"; break;
1593 case METHOD_POST: m = "POST"; break;
1594 case METHOD_PUT: m = "PUT"; break;
1595 default: abort();
1597 n = snnprintf(connection->reqbuf, n, bufsize, "%s ", m);
1599 if(connection->server->isProxy) {
1600 n = snnprint_n(connection->reqbuf, n, bufsize,
1601 url, url_size);
1602 } else {
1603 if(url_size - z == 0) {
1604 location = "/";
1605 location_size = 1;
1606 } else {
1607 location = url + z;
1608 location_size = url_size - z;
1611 n = snnprint_n(connection->reqbuf, n, bufsize,
1612 location, location_size);
1615 do_log(D_SERVER_REQ, "Server request: ");
1616 do_log_n(D_SERVER_REQ, url + x, y - x);
1617 do_log(D_SERVER_REQ, ": ");
1618 do_log_n(D_SERVER_REQ, connection->reqbuf, n);
1619 do_log(D_SERVER_REQ, " (method %d from %d to %d, 0x%lx for 0x%lx)\n",
1620 method, from, to,
1621 (unsigned long)connection, (unsigned long)object);
1623 n = snnprintf(connection->reqbuf, n, bufsize, " HTTP/1.1");
1625 n = snnprintf(connection->reqbuf, n, bufsize, "\r\nHost: ");
1626 n = snnprint_n(connection->reqbuf, n, bufsize, url + x, y - x);
1627 if(port != 80)
1628 n = snnprintf(connection->reqbuf, n, bufsize, ":%d", port);
1630 if(connection->server->isProxy && parentAuthCredentials) {
1631 n = buildServerAuthHeaders(connection->reqbuf, n, bufsize,
1632 parentAuthCredentials);
1635 if(bodylen >= 0)
1636 n = snnprintf(connection->reqbuf, n, bufsize,
1637 "\r\nContent-Length: %d", bodylen);
1639 if(request->flags & REQUEST_WAIT_CONTINUE)
1640 n = snnprintf(connection->reqbuf, n, bufsize,
1641 "\r\nExpect: 100-continue");
1643 if(method != METHOD_HEAD && (from > 0 || to >= 0)) {
1644 if(to >= 0) {
1645 n = snnprintf(connection->reqbuf, n, bufsize,
1646 "\r\nRange: bytes=%d-%d", from, to - 1);
1647 } else {
1648 n = snnprintf(connection->reqbuf, n, bufsize,
1649 "\r\nRange: bytes=%d-", from);
1653 if(method == METHOD_GET && object->etag && (from > 0 || to >= 0)) {
1654 if(request->request && request->request->request == request &&
1655 request->request->from == 0 && request->request->to == -1 &&
1656 pmmSize == 0 && pmmFirstSize == 0)
1657 n = snnprintf(connection->reqbuf, n, bufsize,
1658 "\r\nIf-Range: \"%s\"", object->etag);
1661 if(method == METHOD_CONDITIONAL_GET) {
1662 if(object->last_modified >= 0) {
1663 n = snnprintf(connection->reqbuf, n, bufsize,
1664 "\r\nIf-Modified-Since: ");
1665 n = format_time(connection->reqbuf, n, bufsize,
1666 object->last_modified);
1668 if(object->etag) {
1669 n = snnprintf(connection->reqbuf, n, bufsize,
1670 "\r\nIf-None-Match: \"%s\"", object->etag);
1674 n = httpPrintCacheControl(connection->reqbuf, n, bufsize,
1675 alwaysAddNoTransform ? CACHE_NO_TRANSFORM : 0,
1676 &request->cache_control);
1677 if(n < 0)
1678 goto fail;
1680 if(request->request && request->request->headers) {
1681 n = snnprint_n(connection->reqbuf, n, bufsize,
1682 request->request->headers->string,
1683 request->request->headers->length);
1685 if(!disableVia) {
1686 if(request->request && request->request->via) {
1687 n = snnprintf(connection->reqbuf, n, bufsize,
1688 "\r\nVia: %s, 1.1 %s",
1689 request->request->via->string, proxyName->string);
1690 } else {
1691 n = snnprintf(connection->reqbuf, n, bufsize,
1692 "\r\nVia: 1.1 %s",
1693 proxyName->string);
1697 n = snnprintf(connection->reqbuf, n, bufsize,
1698 "\r\nConnection: %s\r\n\r\n",
1699 (request->flags & REQUEST_PERSISTENT) ?
1700 "keep-alive" : "close");
1701 if(n < 0 || n >= bufsize - 1)
1702 goto fail;
1703 connection->reqlen = n;
1704 return n;
1706 fail:
1707 rc = 0;
1708 if(!(connection->flags & CONN_BIGREQBUF))
1709 rc = httpConnectionBigifyReqbuf(connection);
1710 if(rc == 1)
1711 goto again;
1712 return -1;
1716 httpServerHandler(int status,
1717 FdEventHandlerPtr event,
1718 StreamRequestPtr srequest)
1720 HTTPConnectionPtr connection = srequest->data;
1721 AtomPtr message;
1723 assert(connection->request->object->flags & OBJECT_INPROGRESS);
1725 if(connection->reqlen == 0) {
1726 do_log(D_SERVER_REQ, "Writing aborted on 0x%lx\n",
1727 (unsigned long)connection);
1728 goto fail;
1731 if(status == 0 && !streamRequestDone(srequest)) {
1732 httpSetTimeout(connection, serverTimeout);
1733 return 0;
1736 httpConnectionDestroyReqbuf(connection);
1738 if(status) {
1739 if(connection->serviced >= 1) {
1740 httpServerRestart(connection);
1741 return 1;
1743 if(status >= 0 || status == ECONNRESET) {
1744 message = internAtom("Couldn't send request to server: "
1745 "short write");
1746 } else {
1747 if(status != -EPIPE)
1748 do_log_error(L_ERROR, -status,
1749 "Couldn't send request to server");
1750 message =
1751 internAtomError(-status, "Couldn't send request to server");
1753 goto fail;
1756 return 1;
1758 fail:
1759 httpConnectionDestroyReqbuf(connection);
1760 shutdown(connection->fd, 2);
1761 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLIN);
1762 httpSetTimeout(connection, 60);
1763 return 1;
1767 httpServerSendRequest(HTTPConnectionPtr connection)
1769 assert(connection->server);
1771 if(connection->reqlen == 0) {
1772 do_log(D_SERVER_REQ,
1773 "Writing aborted on 0x%lx\n", (unsigned long)connection);
1774 httpConnectionDestroyReqbuf(connection);
1775 shutdown(connection->fd, 2);
1776 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLIN | POLLOUT);
1777 return -1;
1780 httpSetTimeout(connection, serverTimeout);
1781 do_stream(IO_WRITE, connection->fd, 0,
1782 connection->reqbuf, connection->reqlen,
1783 httpServerHandler, connection);
1784 return 1;
1788 httpServerReplyHandler(int status,
1789 FdEventHandlerPtr event,
1790 StreamRequestPtr srequest)
1792 HTTPConnectionPtr connection = srequest->data;
1793 HTTPRequestPtr request = connection->request;
1794 int i, body;
1795 int bufsize =
1796 (connection->flags & CONN_BIGBUF) ? bigBufferSize : CHUNK_SIZE;
1798 assert(request->object->flags & OBJECT_INPROGRESS);
1799 if(status < 0) {
1800 if(connection->serviced >= 1) {
1801 httpServerRestart(connection);
1802 return 1;
1804 if(status != -ECLIENTRESET)
1805 do_log_error(L_ERROR, -status, "Read from server failed");
1806 httpServerAbort(connection, status != -ECLIENTRESET, 502,
1807 internAtomError(-status, "Read from server failed"));
1808 return 1;
1811 i = findEndOfHeaders(connection->buf, 0, srequest->offset, &body);
1812 connection->len = srequest->offset;
1814 if(i >= 0) {
1815 request->time1 = current_time;
1816 return httpServerHandlerHeaders(status, event, srequest, connection);
1819 if(status) {
1820 if(connection->serviced >= 1) {
1821 httpServerRestart(connection);
1822 return 1;
1824 if(status < 0) {
1825 do_log(L_ERROR,
1826 "Error reading server headers: %d\n", -status);
1827 httpServerAbort(connection, status != -ECLIENTRESET, 502,
1828 internAtomError(-status,
1829 "Error reading server headers"));
1830 } else
1831 httpServerAbort(connection, 1, 502,
1832 internAtom("Server dropped connection"));
1833 return 1;
1836 if(connection->len >= bufsize) {
1837 int rc = 0;
1838 if(!(connection->flags & CONN_BIGBUF))
1839 rc = httpConnectionBigify(connection);
1840 if(rc == 0) {
1841 do_log(L_ERROR, "Couldn't find end of server's headers.\n");
1842 httpServerAbort(connection, 1, 502,
1843 internAtom("Couldn't find end "
1844 "of server's headers"));
1845 return 1;
1846 } else if(rc < 0) {
1847 do_log(L_ERROR, "Couldn't allocate big buffer.\n");
1848 httpServerAbort(connection, 1, 500,
1849 internAtom("Couldn't allocate big buffer"));
1850 return 1;
1852 /* Can't just return 0 -- buf has moved. */
1853 do_stream(IO_READ,
1854 connection->fd, connection->len,
1855 connection->buf, bigBufferSize,
1856 httpServerReplyHandler, connection);
1857 return 1;
1860 return 0;
1864 httpServerHandlerHeaders(int eof,
1865 FdEventHandlerPtr event,
1866 StreamRequestPtr srequest,
1867 HTTPConnectionPtr connection)
1869 HTTPRequestPtr request = connection->request;
1870 ObjectPtr object = request->object;
1871 int rc;
1872 int code, version;
1873 int full_len;
1874 AtomPtr headers;
1875 int len;
1876 int te;
1877 CacheControlRec cache_control;
1878 int age = -1;
1879 time_t date, last_modified, expires;
1880 struct timeval *init_time;
1881 char *etag;
1882 AtomPtr via, new_via;
1883 int expect_body;
1884 HTTPRangeRec content_range;
1885 ObjectPtr new_object = NULL, old_object = NULL;
1886 int supersede = 0;
1887 AtomPtr message = NULL;
1888 int suspectDynamic;
1889 AtomPtr url = NULL;
1891 assert(request->object->flags & OBJECT_INPROGRESS);
1892 assert(eof >= 0);
1894 httpSetTimeout(connection, -1);
1896 if(request->flags & REQUEST_WAIT_CONTINUE) {
1897 do_log(D_SERVER_CONN, "W %s:%d.\n",
1898 connection->server->name, connection->server->port);
1899 request->flags &= ~REQUEST_WAIT_CONTINUE;
1902 rc = httpParseServerFirstLine(connection->buf, &code, &version, &message);
1903 if(rc <= 0) {
1904 do_log(L_ERROR, "Couldn't parse server status line.\n");
1905 httpServerAbort(connection, 1, 502,
1906 internAtom("Couldn't parse server status line"));
1907 return 1;
1910 do_log(D_SERVER_REQ, "Server status: ");
1911 do_log_n(D_SERVER_REQ, connection->buf,
1912 connection->buf[rc - 1] == '\r' ? rc - 2 : rc - 2);
1913 do_log(D_SERVER_REQ, " (0x%lx for 0x%lx)\n",
1914 (unsigned long)connection, (unsigned long)object);
1916 if(version != HTTP_10 && version != HTTP_11) {
1917 do_log(L_ERROR, "Unknown server HTTP version\n");
1918 httpServerAbort(connection, 1, 502,
1919 internAtom("Unknown server HTTP version"));
1920 releaseAtom(message);
1921 return 1;
1924 connection->version = version;
1925 connection->server->version = version;
1926 request->flags |= REQUEST_PERSISTENT;
1928 url = internAtomN(object->key, object->key_size);
1929 rc = httpParseHeaders(0, url, connection->buf, rc, request,
1930 &headers, &len, &cache_control, NULL, &te,
1931 &date, &last_modified, &expires, NULL, NULL, NULL,
1932 &age, &etag, NULL, NULL, &content_range,
1933 NULL, &via, NULL);
1934 if(rc < 0) {
1935 do_log(L_ERROR, "Couldn't parse server headers\n");
1936 releaseAtom(url);
1937 releaseAtom(message);
1938 httpServerAbort(connection, 1, 502,
1939 internAtom("Couldn't parse server headers"));
1940 return 1;
1943 if(date < 0)
1944 date = current_time.tv_sec;
1946 if(code == 100) {
1947 releaseAtom(url);
1948 releaseAtom(message);
1949 /* We've already reset wait_continue above, but we must still
1950 ensure that the writer notices. */
1951 notifyObject(request->object);
1952 connection->len -= rc;
1953 if(connection->len > 0)
1954 memmove(connection->buf, connection->buf + rc, connection->len);
1955 httpServerReply(connection, 1);
1956 return 1;
1959 if(code == 101) {
1960 httpServerAbort(connection, 1, 501,
1961 internAtom("Upgrade not implemented"));
1962 goto fail;
1965 if(via && !checkVia(proxyName, via)) {
1966 httpServerAbort(connection, 1, 504, internAtom("Proxy loop detected"));
1967 goto fail;
1969 full_len = content_range.full_length;
1971 if(code == 206) {
1972 if(content_range.from == -1 || content_range.to == -1) {
1973 do_log(L_ERROR, "Partial content without range.\n");
1974 httpServerAbort(connection, 1, 502,
1975 internAtom("Partial content without range"));
1976 goto fail;
1978 if(len >= 0 && len != content_range.to - content_range.from) {
1979 do_log(L_ERROR, "Inconsistent partial content.\n");
1980 httpServerAbort(connection, 1, 502,
1981 internAtom("Inconsistent partial content"));
1982 goto fail;
1984 } else if(code < 400 &&
1985 (content_range.from >= 0 || content_range.to >= 0 ||
1986 content_range.full_length >= 0)) {
1987 do_log(L_WARN, "Range without partial content.\n");
1988 /* Damn anakata. */
1989 content_range.from = -1;
1990 content_range.to = -1;
1991 content_range.full_length = -1;
1992 } else if(code != 304 && code != 412) {
1993 full_len = len;
1996 if(te != TE_IDENTITY && te != TE_CHUNKED) {
1997 do_log(L_ERROR, "Unsupported transfer-encoding\n");
1998 httpServerAbort(connection, 1, 502,
1999 internAtom("Unsupported transfer-encoding"));
2000 goto fail;
2003 if(code == 304) {
2004 if(request->method != METHOD_CONDITIONAL_GET) {
2005 do_log(L_ERROR, "Unexpected \"not changed\" reply from server\n");
2006 httpServerAbort(connection, 1, 502,
2007 internAtom("Unexpected \"not changed\" "
2008 "reply from server"));
2009 goto fail;
2011 if(object->etag && !etag) {
2012 /* RFC 2616 10.3.5. Violated by some front-end proxies. */
2013 do_log(L_WARN, "\"Not changed\" reply with no ETag.\n");
2017 if(code == 412) {
2018 if(request->method != METHOD_CONDITIONAL_GET ||
2019 (!object->etag && !object->last_modified)) {
2020 do_log(L_ERROR,
2021 "Unexpected \"precondition failed\" reply from server.\n");
2022 httpServerAbort(connection, 1, 502,
2023 internAtom("Unexpected \"precondition failed\" "
2024 "reply from server"));
2025 goto fail;
2029 releaseAtom(url);
2031 /* Okay, we're going to accept this reply. */
2033 if((code == 200 || code == 206 || code == 304 || code == 412) &&
2034 (cache_control.flags & (CACHE_NO | CACHE_NO_STORE) ||
2035 cache_control.max_age == 0 ||
2036 (cacheIsShared && cache_control.s_maxage == 0) ||
2037 (expires >= 0 && expires <= object->age))) {
2038 do_log(L_UNCACHEABLE, "Uncacheable object %s (%d)\n",
2039 scrub(object->key), cache_control.flags);
2042 if(request->time0.tv_sec != null_time.tv_sec)
2043 init_time = &request->time0;
2044 else
2045 init_time = &current_time;
2046 age = MIN(init_time->tv_sec - age, init_time->tv_sec);
2048 if(request->method == METHOD_HEAD ||
2049 code < 200 || code == 204 || code == 304)
2050 expect_body = 0;
2051 else if(te == TE_IDENTITY)
2052 expect_body = (len != 0);
2053 else
2054 expect_body = 1;
2056 connection->chunk_remaining = -1;
2057 connection->te = te;
2059 old_object = object;
2061 connection->server->lies--;
2063 if(object->cache_control & CACHE_MISMATCH)
2064 supersede = 1;
2066 if(code == 304 || code == 412) {
2067 if((object->etag && etag && strcmp(object->etag, etag) != 0) ||
2068 (object->last_modified >= 0 && last_modified >= 0 &&
2069 object->last_modified != last_modified)) {
2070 do_log(L_ERROR, "Inconsistent \"%s\" reply for %s\n",
2071 code == 304 ? "not changed":"precondition failed",
2072 scrub(object->key));
2073 object->flags |= OBJECT_DYNAMIC;
2074 supersede = 1;
2076 } else if(!(object->flags & OBJECT_INITIAL)) {
2077 if((object->last_modified < 0 || last_modified < 0) &&
2078 (!object->etag || !etag))
2079 supersede = 1;
2080 else if(object->last_modified != last_modified)
2081 supersede = 1;
2082 else if(object->etag || etag) {
2083 /* We need to be permissive here so as to deal with some
2084 front-end proxies that discard ETags on partial
2085 replies but not on full replies. */
2086 if(etag && object->etag && strcmp(object->etag, etag) != 0)
2087 supersede = 1;
2088 else if(!object->etag)
2089 supersede = 1;
2092 if(!supersede && (object->cache_control & CACHE_VARY) &&
2093 dontTrustVaryETag >= 1) {
2094 /* Check content-type to work around mod_gzip bugs */
2095 if(!httpHeaderMatch(atomContentType, object->headers, headers) ||
2096 !httpHeaderMatch(atomContentEncoding, object->headers, headers))
2097 supersede = 1;
2100 if(full_len < 0 && te == TE_IDENTITY) {
2101 /* It's an HTTP/1.0 CGI. Be afraid. */
2102 if(expect_body && content_range.from < 0 && content_range.to < 0)
2103 supersede = 1;
2106 if(!supersede && object->length >= 0 && full_len >= 0 &&
2107 object->length != full_len) {
2108 do_log(L_WARN, "Inconsistent length.\n");
2109 supersede = 1;
2112 if(!supersede &&
2113 ((object->last_modified >= 0 && last_modified >= 0) ||
2114 (object->etag && etag))) {
2115 if(request->method == METHOD_CONDITIONAL_GET) {
2116 do_log(L_WARN, "Server ignored conditional request.\n");
2117 connection->server->lies += 10;
2118 /* Drop the connection? */
2121 } else if(code == 416) {
2122 do_log(L_ERROR, "Unexpected \"range not satisfiable\" reply\n");
2123 httpServerAbort(connection, 1, 502,
2124 internAtom("Unexpected \"range not satisfiable\" "
2125 "reply"));
2126 /* The object may be superseded. Make sure the next request
2127 won't be partial. */
2128 abortObject(object, 502,
2129 internAtom("Unexpected \"range not satisfiable\" reply"));
2130 return 1;
2133 if(object->flags & OBJECT_INITIAL)
2134 supersede = 0;
2136 if(supersede) {
2137 do_log(L_SUPERSEDED,
2138 "Superseding object %s (%d %d %d %s -> %d %d %d %s)\n",
2139 scrub(old_object->key),
2140 object->code, object->length, (int)object->last_modified,
2141 object->etag ? object->etag : "(none)",
2142 code, full_len, (int)last_modified,
2143 etag ? etag : "(none)");
2144 privatiseObject(old_object, 0);
2145 new_object = makeObject(object->type, object->key,
2146 object->key_size, 1, 0,
2147 object->request, NULL);
2148 if(new_object == NULL) {
2149 do_log(L_ERROR, "Couldn't allocate object\n");
2150 httpServerAbort(connection, 1, 500,
2151 internAtom("Couldn't allocate object"));
2152 return 1;
2154 if(urlIsLocal(new_object->key, new_object->key_size))
2155 new_object->flags |= OBJECT_LOCAL;
2156 } else {
2157 new_object = object;
2160 suspectDynamic =
2161 (!etag && last_modified < 0) ||
2162 (cache_control.flags &
2163 (CACHE_NO_HIDDEN | CACHE_NO | CACHE_NO_STORE |
2164 (cacheIsShared ? CACHE_PRIVATE : 0))) ||
2165 (cache_control.max_age >= 0 && cache_control.max_age <= 2) ||
2166 (cacheIsShared &&
2167 cache_control.s_maxage >= 0 && cache_control.s_maxage <= 5) ||
2168 (old_object->last_modified >= 0 && old_object->expires >= 0 &&
2169 (old_object->expires - old_object->last_modified <= 1)) ||
2170 (supersede && (old_object->date - date <= 5));
2172 if(suspectDynamic)
2173 new_object->flags |= OBJECT_DYNAMIC;
2174 else if(!supersede)
2175 new_object->flags &= ~OBJECT_DYNAMIC;
2176 else if(old_object->flags & OBJECT_DYNAMIC)
2177 new_object->flags |= OBJECT_DYNAMIC;
2179 new_object->age = age;
2180 new_object->cache_control |= cache_control.flags;
2181 new_object->max_age = cache_control.max_age;
2182 new_object->s_maxage = cache_control.s_maxage;
2183 new_object->flags &= ~OBJECT_FAILED;
2185 if(date >= 0)
2186 new_object->date = date;
2187 if(last_modified >= 0)
2188 new_object->last_modified = last_modified;
2189 if(expires >= 0)
2190 new_object->expires = expires;
2191 if(new_object->etag == NULL)
2192 new_object->etag = etag;
2193 else
2194 free(etag);
2196 switch(code) {
2197 case 200:
2198 case 300: case 301: case 302: case 303: case 307:
2199 case 403: case 404: case 405: case 401:
2200 if(new_object->message) releaseAtom(new_object->message);
2201 new_object->code = code;
2202 new_object->message = message;
2203 break;
2204 case 206: case 304: case 412:
2205 if(new_object->code != 200 || !new_object->message) {
2206 if(new_object->message) releaseAtom(new_object->message);
2207 new_object->code = 200;
2208 new_object->message = internAtom("OK");
2210 releaseAtom(message);
2211 break;
2212 default:
2213 if(new_object->message) releaseAtom(new_object->message);
2214 new_object->code = code;
2215 new_object->message = retainAtom(message);
2216 break;
2219 httpTweakCachability(new_object);
2221 if(!via)
2222 new_via = internAtomF("%s %s",
2223 version == HTTP_11 ? "1.1" : "1.0",
2224 proxyName->string);
2225 else
2226 new_via = internAtomF("%s, %s %s", via->string,
2227 version == HTTP_11 ? "1.1" : "1.0",
2228 proxyName->string);
2229 if(new_via == NULL) {
2230 do_log(L_ERROR, "Couldn't allocate Via.\n");
2231 } else {
2232 if(new_object->via) releaseAtom(new_object->via);
2233 new_object->via = new_via;
2236 if(new_object->flags & OBJECT_INITIAL) {
2237 objectPartial(new_object, full_len, headers);
2238 } else {
2239 if(new_object->length < 0)
2240 new_object->length = full_len;
2241 /* XXX -- RFC 2616 13.5.3 */
2242 releaseAtom(headers);
2245 if(supersede) {
2246 assert(new_object != old_object);
2247 supersedeObject(old_object);
2250 if(new_object != old_object) {
2251 if(new_object->flags & OBJECT_INPROGRESS) {
2252 /* Make sure we don't fetch this object two times at the
2253 same time. Just drop the connection. */
2254 releaseObject(new_object);
2255 httpServerFinish(connection, 1, 0);
2256 return 1;
2258 old_object->flags &= ~OBJECT_VALIDATING;
2259 new_object->flags |= OBJECT_INPROGRESS;
2260 /* Signal the client side to switch to the new object -- see
2261 httpClientGetHandler. If it doesn't, we'll give up on this
2262 request below. */
2263 new_object->flags |= OBJECT_MUTATING;
2264 request->can_mutate = new_object;
2265 notifyObject(old_object);
2266 request->can_mutate = NULL;
2267 new_object->flags &= ~OBJECT_MUTATING;
2268 old_object->flags &= ~OBJECT_INPROGRESS;
2269 if(request->object == old_object) {
2270 if(request->request)
2271 request->request->request = NULL;
2272 request->request = NULL;
2273 request->object = new_object;
2274 } else {
2275 assert(request->object == new_object);
2277 releaseNotifyObject(old_object);
2278 old_object = NULL;
2279 object = new_object;
2280 } else {
2281 objectMetadataChanged(new_object, 0);
2284 if(object->flags & OBJECT_VALIDATING) {
2285 object->flags &= ~OBJECT_VALIDATING;
2286 notifyObject(object);
2289 if(!expect_body) {
2290 httpServerFinish(connection, 0, rc);
2291 return 1;
2294 if(request->request == NULL) {
2295 httpServerFinish(connection, 1, 0);
2296 return 1;
2299 if(code == 412) {
2300 /* 412 replies contain a useless body. For now, we
2301 drop the connection. */
2302 httpServerFinish(connection, 1, 0);
2303 return 1;
2307 if(request->flags & REQUEST_PERSISTENT) {
2308 if(request->method != METHOD_HEAD &&
2309 connection->te == TE_IDENTITY && len < 0) {
2310 do_log(L_ERROR, "Persistent reply with no Content-Length\n");
2311 /* That's potentially dangerous, as we could start reading
2312 arbitrary data into the object. Unfortunately, some
2313 servers do that. */
2314 request->flags &= ~REQUEST_PERSISTENT;
2318 /* we're getting a body */
2319 if(content_range.from > 0)
2320 connection->offset = content_range.from;
2321 else
2322 connection->offset = 0;
2324 if(content_range.to >= 0)
2325 request->to = content_range.to;
2327 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2328 (unsigned long)connection, (unsigned long)object,
2329 connection->offset);
2331 if(connection->len > rc) {
2332 rc = connectionAddData(connection, rc);
2333 if(rc) {
2334 if(rc < 0) {
2335 if(rc == -2) {
2336 do_log(L_ERROR, "Couldn't parse chunk size.\n");
2337 httpServerAbort(connection, 1, 502,
2338 internAtom("Couldn't parse chunk size"));
2339 } else {
2340 do_log(L_ERROR, "Couldn't add data to connection.\n");
2341 httpServerAbort(connection, 1, 500,
2342 internAtom("Couldn't add data "
2343 "to connection"));
2345 return 1;
2346 } else {
2347 if(code != 206) {
2348 if(object->length < 0) {
2349 object->length = object->size;
2350 objectMetadataChanged(object, 0);
2351 } else if(object->length != object->size) {
2352 httpServerAbort(connection, 1, 500,
2353 internAtom("Inconsistent "
2354 "object size"));
2355 object->length = -1;
2356 return 1;
2359 httpServerFinish(connection, 0, 0);
2360 return 1;
2363 } else {
2364 connection->len = 0;
2367 if(eof) {
2368 if(connection->te == TE_CHUNKED ||
2369 (object->length >= 0 &&
2370 connection->offset < object->length)) {
2371 do_log(L_ERROR, "Server closed connection.\n");
2372 httpServerAbort(connection, 1, 502,
2373 internAtom("Server closed connection"));
2374 return 1;
2375 } else {
2376 if(code != 206 && eof > 0 && object->length < 0) {
2377 object->length = object->size;
2378 objectMetadataChanged(object, 0);
2380 httpServerFinish(connection, 1, 0);
2381 return 1;
2383 } else {
2384 return httpServerReadData(connection, 1);
2386 return 0;
2388 fail:
2389 releaseAtom(url);
2390 releaseAtom(message);
2391 if(headers)
2392 releaseAtom(headers);
2393 if(etag)
2394 free(etag);
2395 if(via)
2396 releaseAtom(via);
2397 return 1;
2401 httpServerIndirectHandlerCommon(HTTPConnectionPtr connection, int eof)
2403 HTTPRequestPtr request = connection->request;
2405 assert(eof >= 0);
2406 assert(request->object->flags & OBJECT_INPROGRESS);
2408 if(connection->len > 0) {
2409 int rc;
2410 rc = connectionAddData(connection, 0);
2411 if(rc) {
2412 if(rc < 0) {
2413 if(rc == -2) {
2414 do_log(L_ERROR, "Couldn't parse chunk size.\n");
2415 httpServerAbort(connection, 1, 502,
2416 internAtom("Couldn't parse chunk size"));
2417 } else {
2418 do_log(L_ERROR, "Couldn't add data to connection.\n");
2419 httpServerAbort(connection, 1, 500,
2420 internAtom("Couldn't add data "
2421 "to connection"));
2423 return 1;
2424 } else {
2425 if(request->to < 0) {
2426 if(request->object->length < 0) {
2427 request->object->length = request->object->size;
2428 objectMetadataChanged(request->object, 0);
2429 } else if(request->object->length !=
2430 request->object->size) {
2431 request->object->length = -1;
2432 httpServerAbort(connection, 1, 502,
2433 internAtom("Inconsistent "
2434 "object size"));
2435 return 1;
2438 httpServerFinish(connection, 0, 0);
2440 return 1;
2444 if(eof && connection->len == 0) {
2445 if(connection->te == TE_CHUNKED ||
2446 (request->to >= 0 && connection->offset < request->to)) {
2447 do_log(L_ERROR, "Server dropped connection.\n");
2448 httpServerAbort(connection, 1, 502,
2449 internAtom("Server dropped connection"));
2450 return 1;
2451 } else {
2452 if(request->object->length < 0 && eof > 0 &&
2453 (request->to < 0 || request->to > request->object->size)) {
2454 request->object->length = request->object->size;
2455 objectMetadataChanged(request->object, 0);
2457 httpServerFinish(connection, 1, 0);
2458 return 1;
2460 } else {
2461 return httpServerReadData(connection, 0);
2466 httpServerIndirectHandler(int status,
2467 FdEventHandlerPtr event,
2468 StreamRequestPtr srequest)
2470 HTTPConnectionPtr connection = srequest->data;
2471 assert(connection->request->object->flags & OBJECT_INPROGRESS);
2473 httpSetTimeout(connection, -1);
2474 if(status < 0) {
2475 if(status != -ECLIENTRESET)
2476 do_log_error(L_ERROR, -status, "Read from server failed");
2477 httpServerAbort(connection, status != -ECLIENTRESET, 502,
2478 internAtomError(-status, "Read from server failed"));
2479 return 1;
2482 connection->len = srequest->offset;
2484 return httpServerIndirectHandlerCommon(connection, status);
2488 httpServerReadData(HTTPConnectionPtr connection, int immediate)
2490 HTTPRequestPtr request = connection->request;
2491 ObjectPtr object = request->object;
2492 int to = -1;
2494 assert(object->flags & OBJECT_INPROGRESS);
2496 if(request->request == NULL) {
2497 httpServerFinish(connection, 1, 0);
2498 return 1;
2501 if(request->to >= 0)
2502 to = request->to;
2503 else
2504 to = object->length;
2506 if(to >= 0 && to == connection->offset) {
2507 httpServerFinish(connection, 0, 0);
2508 return 1;
2511 if(connection->len == 0 &&
2512 ((connection->te == TE_IDENTITY && to > connection->offset) ||
2513 (connection->te == TE_CHUNKED && connection->chunk_remaining > 0))) {
2514 /* Read directly into the object */
2515 int i = connection->offset / CHUNK_SIZE;
2516 int j = connection->offset % CHUNK_SIZE;
2517 int end, len, more;
2518 /* See httpServerDirectHandlerCommon if you change this */
2519 if(connection->te == TE_CHUNKED) {
2520 len = connection->chunk_remaining;
2521 /* The logic here is that we want more to just fit the
2522 chunk header if we're doing a large read, but do a
2523 large read if we would otherwise do a small one. The
2524 magic constant 2000 comes from the assumption that the
2525 server uses chunks that have a size that are a power of
2526 two (possibly including the chunk header), and that we
2527 want a full ethernet packet to fit into our read. */
2528 more = (len >= 2000 ? 20 : MIN(2048 - len, CHUNK_SIZE));
2529 } else {
2530 len = to - connection->offset;
2531 /* We read more data only when there is a reasonable
2532 chance of there being another reply coming. */
2533 more = (connection->pipelined > 1) ? CHUNK_SIZE : 0;
2535 end = len + connection->offset;
2537 httpConnectionDestroyBuf(connection);
2539 /* The order of allocation is important in case we run out of
2540 memory. */
2541 lockChunk(object, i);
2542 if(object->chunks[i].data == NULL)
2543 object->chunks[i].data = get_chunk();
2544 if(object->chunks[i].data && object->chunks[i].size >= j) {
2545 if(len + j > CHUNK_SIZE) {
2546 lockChunk(object, i + 1);
2547 if(object->chunks[i + 1].data == NULL)
2548 object->chunks[i + 1].data = get_chunk();
2549 /* Unless we're grabbing all len of data, we do not
2550 want to do an indirect read immediately afterwards. */
2551 if(more && len + j <= 2 * CHUNK_SIZE) {
2552 if(!connection->buf)
2553 connection->buf = get_chunk(); /* checked below */
2555 if(object->chunks[i + 1].data) {
2556 do_stream_3(IO_READ | IO_NOTNOW, connection->fd, j,
2557 object->chunks[i].data, CHUNK_SIZE,
2558 object->chunks[i + 1].data,
2559 MIN(CHUNK_SIZE,
2560 end - (i + 1) * CHUNK_SIZE),
2561 connection->buf, connection->buf ? more : 0,
2562 httpServerDirectHandler2, connection);
2563 return 1;
2565 unlockChunk(object, i + 1);
2567 if(more && len + j <= CHUNK_SIZE) {
2568 if(!connection->buf)
2569 connection->buf = get_chunk();
2571 do_stream_2(IO_READ | IO_NOTNOW, connection->fd, j,
2572 object->chunks[i].data,
2573 MIN(CHUNK_SIZE, end - i * CHUNK_SIZE),
2574 connection->buf, connection->buf ? more : 0,
2575 httpServerDirectHandler, connection);
2576 return 1;
2577 } else {
2578 unlockChunk(object, i);
2582 if(connection->len == 0)
2583 httpConnectionDestroyBuf(connection);
2585 httpSetTimeout(connection, serverTimeout);
2586 do_stream_buf(IO_READ | IO_NOTNOW |
2587 ((immediate && connection->len) ? IO_IMMEDIATE : 0),
2588 connection->fd, connection->len,
2589 &connection->buf,
2590 (connection->te == TE_CHUNKED ?
2591 MIN(2048, CHUNK_SIZE) : CHUNK_SIZE),
2592 httpServerIndirectHandler, connection);
2593 return 1;
2597 httpServerDirectHandlerCommon(int kind, int status,
2598 FdEventHandlerPtr event,
2599 StreamRequestPtr srequest)
2601 HTTPConnectionPtr connection = srequest->data;
2602 HTTPRequestPtr request = connection->request;
2603 ObjectPtr object = request->object;
2604 int i = connection->offset / CHUNK_SIZE;
2605 int to, end, end1;
2607 assert(request->object->flags & OBJECT_INPROGRESS);
2609 httpSetTimeout(connection, -1);
2611 if(status < 0) {
2612 unlockChunk(object, i);
2613 if(kind == 2) unlockChunk(object, i + 1);
2614 if(status != -ECLIENTRESET)
2615 do_log_error(L_ERROR, -status, "Read from server failed");
2616 httpServerAbort(connection, status != -ECLIENTRESET, 502,
2617 internAtomError(-status, "Read from server failed"));
2618 return 1;
2621 /* We have incestuous knowledge of the decisions made in
2622 httpServerReadData */
2623 if(request->to >= 0)
2624 to = request->to;
2625 else
2626 to = object->length;
2627 if(connection->te == TE_CHUNKED)
2628 end = connection->offset + connection->chunk_remaining;
2629 else
2630 end = to;
2631 /* The amount of data actually read into the object */
2632 end1 = MIN(end, i * CHUNK_SIZE + MIN(kind * CHUNK_SIZE, srequest->offset));
2634 assert(end >= 0);
2635 assert(end1 >= i * CHUNK_SIZE);
2636 assert(end1 - 2 * CHUNK_SIZE <= i * CHUNK_SIZE);
2638 object->chunks[i].size =
2639 MAX(object->chunks[i].size, MIN(end1 - i * CHUNK_SIZE, CHUNK_SIZE));
2640 if(kind == 2 && end1 > (i + 1) * CHUNK_SIZE) {
2641 object->chunks[i + 1].size =
2642 MAX(object->chunks[i + 1].size, end1 - (i + 1) * CHUNK_SIZE);
2644 if(connection->te == TE_CHUNKED) {
2645 connection->chunk_remaining -= (end1 - connection->offset);
2646 assert(connection->chunk_remaining >= 0);
2648 connection->offset = end1;
2649 object->size = MAX(object->size, end1);
2650 unlockChunk(object, i);
2651 if(kind == 2) unlockChunk(object, i + 1);
2653 if(i * CHUNK_SIZE + srequest->offset > end1) {
2654 connection->len = i * CHUNK_SIZE + srequest->offset - end1;
2655 return httpServerIndirectHandlerCommon(connection, status);
2656 } else {
2657 notifyObject(object);
2658 if(status) {
2659 httpServerFinish(connection, 1, 0);
2660 return 1;
2661 } else {
2662 return httpServerReadData(connection, 0);
2668 httpServerDirectHandler(int status,
2669 FdEventHandlerPtr event,
2670 StreamRequestPtr srequest)
2672 return httpServerDirectHandlerCommon(1, status, event, srequest);
2676 httpServerDirectHandler2(int status,
2677 FdEventHandlerPtr event,
2678 StreamRequestPtr srequest)
2680 return httpServerDirectHandlerCommon(2, status, event, srequest);
2683 /* Add the data accumulated in connection->buf into the object in
2684 connection->request. Returns 0 in the normal case, 1 if the TE is
2685 self-terminating and we're done, -1 if there was a problem with
2686 objectAddData, -2 if there was a problem with the data. */
2688 connectionAddData(HTTPConnectionPtr connection, int skip)
2690 HTTPRequestPtr request = connection->request;
2691 ObjectPtr object = request->object;
2692 int rc;
2694 if(connection->te == TE_IDENTITY) {
2695 int len;
2697 len = connection->len - skip;
2698 if(object->length >= 0) {
2699 len = MIN(object->length - connection->offset, len);
2701 if(request->to >= 0)
2702 len = MIN(request->to - connection->offset, len);
2703 if(len > 0) {
2704 rc = objectAddData(object, connection->buf + skip,
2705 connection->offset, len);
2706 if(rc < 0)
2707 return -1;
2708 connection->offset += len;
2709 connection->len -= (len + skip);
2710 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2711 (unsigned long)connection, (unsigned long)object,
2712 connection->offset);
2715 if(connection->len > 0 && skip + len > 0) {
2716 memmove(connection->buf,
2717 connection->buf + skip + len, connection->len);
2720 if((object->length >= 0 && object->length <= connection->offset) ||
2721 (request->to >= 0 && request->to <= connection->offset)) {
2722 notifyObject(object);
2723 return 1;
2724 } else {
2725 if(len > 0)
2726 notifyObject(object);
2727 return 0;
2729 } else if(connection->te == TE_CHUNKED) {
2730 int i = skip, j, size;
2731 /* connection->chunk_remaining is 0 at the end of a chunk, -1
2732 after the CR/LF pair ending a chunk, and -2 after we've
2733 seen a chunk of length 0. */
2734 if(connection->chunk_remaining > -2) {
2735 while(1) {
2736 if(connection->chunk_remaining <= 0) {
2737 if(connection->chunk_remaining == 0) {
2738 if(connection->len < i + 2)
2739 break;
2740 if(connection->buf[i] != '\r' ||
2741 connection->buf[i + 1] != '\n')
2742 return -1;
2743 i += 2;
2744 connection->chunk_remaining = -1;
2746 if(connection->len < i + 2)
2747 break;
2748 j = parseChunkSize(connection->buf, i,
2749 connection->len, &size);
2750 if(j < 0)
2751 return -2;
2752 if(j == 0)
2753 break;
2754 else
2755 i = j;
2756 if(size == 0) {
2757 connection->chunk_remaining = -2;
2758 break;
2759 } else {
2760 connection->chunk_remaining = size;
2762 } else {
2763 /* connection->chunk_remaining > 0 */
2764 size = MIN(connection->chunk_remaining,
2765 connection->len - i);
2766 if(size <= 0)
2767 break;
2768 rc = objectAddData(object, connection->buf + i,
2769 connection->offset, size);
2770 connection->offset += size;
2771 if(rc < 0)
2772 return -1;
2773 i += size;
2774 connection->chunk_remaining -= size;
2775 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2776 (unsigned long)connection,
2777 (unsigned long)object,
2778 connection->offset);
2782 connection->len -= i;
2783 if(connection->len > 0)
2784 memmove(connection->buf, connection->buf + i, connection->len);
2785 if(i > 0 || connection->chunk_remaining == -2)
2786 notifyObject(object);
2787 if(connection->chunk_remaining == -2)
2788 return 1;
2789 else
2790 return 0;
2791 } else {
2792 abort();
2796 void
2797 listServers(FILE *out)
2799 HTTPServerPtr server;
2800 int i, n, m, entry;
2802 fprintf(out, "<!DOCTYPE HTML PUBLIC "
2803 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2804 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2805 "<html><head>\n"
2806 "\r\n<title>Known servers</title>\n"
2807 "</head><body>\n"
2808 "<h1>Known servers</h1>\n");
2810 alternatingHttpStyle(out, "servers");
2811 fprintf(out, "<table id=servers>\n");
2812 fprintf(out, "<thead><tr><th>Server</th>"
2813 "<th>Version</th>"
2814 "<th>Persistent</th>"
2815 "<th>Pipeline</th>"
2816 "<th>Connections</th>"
2817 "<th></th>"
2818 "<th>rtt</th>"
2819 "<th>rate</th>"
2820 "</tr></thead>\n");
2821 fprintf(out, "<tbody>\n");
2822 server = servers;
2823 entry = 0;
2824 while(server) {
2825 fprintf(out, "<tr class=\"%s\">", entry % 2 == 0 ? "even" : "odd");
2826 if(server->port == 80)
2827 fprintf(out, "<td>%s</td>", server->name);
2828 else
2829 fprintf(out, "<td>%s:%d</td>", server->name, server->port);
2831 if(server->version == HTTP_11)
2832 fprintf(out, "<td>1.1</td>");
2833 else if(server->version == HTTP_10)
2834 fprintf(out, "<td>1.0</td>");
2835 else
2836 fprintf(out, "<td>unknown</td>");
2838 if(server->persistent < 0)
2839 fprintf(out, "<td>no</td>");
2840 else if(server->persistent > 0)
2841 fprintf(out, "<td>yes</td>");
2842 else
2843 fprintf(out, "<td>unknown</td>");
2845 if(server->version != HTTP_11 || server->persistent <= 0)
2846 fprintf(out, "<td></td>");
2847 else if(server->pipeline < 0)
2848 fprintf(out, "<td>no</td>");
2849 else if(server->pipeline >= 0 && server->pipeline <= 1)
2850 fprintf(out, "<td>unknown</td>");
2851 else if(server->pipeline == 2 || server->pipeline == 3)
2852 fprintf(out, "<td>probing</td>");
2853 else
2854 fprintf(out, "<td>yes</td>");
2856 n = 0; m = 0;
2857 for(i = 0; i < server->maxslots; i++)
2858 if(server->connection[i] && !server->connection[i]->connecting) {
2859 if(i < server->numslots)
2860 n++;
2861 else
2862 m++;
2865 fprintf(out, "<td>%d/%d", n, server->numslots);
2866 if(m)
2867 fprintf(out, " + %d</td>", m);
2868 else
2869 fprintf(out, "</td>");
2871 if(server->lies > 0)
2872 fprintf(out, "<td>(%d lies)</td>", (server->lies + 9) / 10);
2873 else
2874 fprintf(out, "<td></td>");
2876 if(server->rtt > 0)
2877 fprintf(out, "<td>%.3f</td>", (double)server->rtt / 1000000.0);
2878 else
2879 fprintf(out, "<td></td>");
2880 if(server->rate > 0)
2881 fprintf(out, "<td>%d</td>", server->rate);
2882 else
2883 fprintf(out, "<td></td>");
2885 fprintf(out, "</tr>\n");
2886 server = server->next;
2887 entry++;
2889 fprintf(out, "</tbody>\n");
2890 fprintf(out, "</table>\n");
2891 fprintf(out, "<p><a href=\"/polipo/\">back</a></p>");
2892 fprintf(out, "</body></html>\n");