Add -fno-strict-aliasing to prevent compile warnings on some systems.
[polipo.git] / server.c
blobd59d7723e5c0b038becf3a98374e140441799efc
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 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.");
95 CONFIG_VARIABLE(alwaysAddNoTransform, CONFIG_BOOLEAN,
96 "If true, add a no-transform directive to all requests.");
99 static int
100 parentProxySetter(ConfigVariablePtr var, void *value)
102 configAtomSetter(var, value);
103 initParentProxy();
104 return 1;
107 static void
108 discardServer(HTTPServerPtr server)
110 HTTPServerPtr previous;
111 assert(!server->request);
113 if(server == servers)
114 servers = server->next;
115 else {
116 previous = servers;
117 while(previous->next != server)
118 previous = previous->next;
119 previous->next = server->next;
122 if(server->connection)
123 free(server->connection);
124 if(server->idleHandler)
125 free(server->idleHandler);
126 if(server->name)
127 free(server->name);
129 free(server);
132 static int
133 httpServerIdle(HTTPServerPtr server)
135 int i;
136 if(server->request)
137 return 0;
138 for(i = 0; i < server->maxslots; i++)
139 if(server->connection[i])
140 return 0;
141 return 1;
144 static int
145 expireServersHandler(TimeEventHandlerPtr event)
147 HTTPServerPtr server, next;
148 TimeEventHandlerPtr e;
149 server = servers;
150 while(server) {
151 next = server->next;
152 if(httpServerIdle(server) &&
153 server->time + serverExpireTime < current_time.tv_sec)
154 discardServer(server);
155 server = next;
157 e = scheduleTimeEvent(serverExpireTime / 60 + 60,
158 expireServersHandler, 0, NULL);
159 if(!e) {
160 do_log(L_ERROR, "Couldn't schedule server expiry.\n");
161 polipoExit();
163 return 1;
166 static int
167 roundSize(int size)
169 return (size + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE;
172 static int
173 initParentProxy()
175 AtomPtr host, port_atom;
176 int rc, port;
178 if(parentHost) {
179 releaseAtom(parentHost);
180 parentHost = NULL;
182 if(parentPort >= 0)
183 parentPort = -1;
185 if(parentProxy != NULL && parentProxy->length == 0) {
186 releaseAtom(parentProxy);
187 parentProxy = NULL;
190 if(parentProxy == NULL)
191 return 1;
193 rc = atomSplit(parentProxy, ':', &host, &port_atom);
194 if(rc <= 0) {
195 do_log(L_ERROR, "Couldn't parse parentProxy.");
196 releaseAtom(parentProxy);
197 parentProxy = NULL;
198 return -1;
201 port = atoi(port_atom->string);
202 if(port <= 0 || port >= 0x10000) {
203 releaseAtom(host);
204 releaseAtom(port_atom);
205 do_log(L_ERROR, "Couldn't parse parentProxy.");
206 releaseAtom(parentProxy);
207 parentProxy = NULL;
208 return -1;
211 parentHost = host;
212 parentPort = port;
213 return 1;
216 void
217 initServer(void)
219 TimeEventHandlerPtr event;
220 servers = NULL;
222 if(pmmFirstSize || pmmSize) {
223 if(pmmSize == 0) pmmSize = pmmFirstSize;
224 if(pmmFirstSize == 0) pmmFirstSize = pmmSize;
225 pmmSize = roundSize(pmmSize);
226 pmmFirstSize = roundSize(pmmFirstSize);
229 if(serverMaxSlots < 1)
230 serverMaxSlots = 1;
231 if(serverSlots < 1)
232 serverSlots = 1;
233 if(serverSlots > serverMaxSlots)
234 serverSlots = serverMaxSlots;
235 if(serverSlots1 < serverSlots)
236 serverSlots1 = serverSlots;
237 if(serverSlots1 > serverMaxSlots)
238 serverSlots1 = serverMaxSlots;
240 initParentProxy();
242 event = scheduleTimeEvent(serverExpireTime / 60 + 60, expireServersHandler,
243 0, NULL);
244 if(event == NULL) {
245 do_log(L_ERROR, "Couldn't schedule server expiry.\n");
246 exit(1);
250 static HTTPServerPtr
251 getServer(char *name, int port, int proxy)
253 HTTPServerPtr server;
254 int i;
256 server = servers;
257 while(server) {
258 if(strcmp(server->name, name) == 0 && server->port == port &&
259 server->isProxy == proxy) {
260 if(httpServerIdle(server) &&
261 server->time + serverExpireTime < current_time.tv_sec) {
262 discardServer(server);
263 server = NULL;
264 break;
265 } else {
266 server->time = current_time.tv_sec;
267 return server;
270 server = server->next;
273 server = malloc(sizeof(HTTPServerRec));
274 if(server == NULL) {
275 do_log(L_ERROR, "Couldn't allocate server.\n");
276 return NULL;
279 server->connection = malloc(serverMaxSlots * sizeof(HTTPConnectionPtr));
280 if(server->connection == NULL) {
281 do_log(L_ERROR, "Couldn't allocate server.\n");
282 free(server);
283 return NULL;
286 server->idleHandler = malloc(serverMaxSlots * sizeof(FdEventHandlerPtr));
287 if(server->connection == NULL) {
288 do_log(L_ERROR, "Couldn't allocate server.\n");
289 free(server->connection);
290 free(server);
291 return NULL;
294 server->maxslots = serverMaxSlots;
296 server->name = strdup(name);
297 if(server->name == NULL) {
298 do_log(L_ERROR, "Couldn't allocate server name.\n");
299 free(server);
300 return NULL;
303 server->port = port;
304 server->addrindex = 0;
305 server->isProxy = proxy;
306 server->version = HTTP_UNKNOWN;
307 server->persistent = 0;
308 server->pipeline = 0;
309 server->time = current_time.tv_sec;
310 server->rtt = -1;
311 server->rate = -1;
312 server->numslots = MIN(serverSlots, server->maxslots);
313 for(i = 0; i < server->maxslots; i++) {
314 server->connection[i] = NULL;
315 server->idleHandler[i] = NULL;
317 server->request = NULL;
318 server->request_last = NULL;
319 server->lies = 0;
321 server->next = servers;
322 servers = server;
323 return server;
327 httpServerQueueRequest(HTTPServerPtr server, HTTPRequestPtr request)
329 assert(request->request && request->request->request == request);
330 assert(request->connection == NULL);
331 if(server->request) {
332 server->request_last->next = request;
333 server->request_last = request;
334 } else {
335 server->request_last = request;
336 server->request = request;
338 return 1;
341 void
342 httpServerAbort(HTTPConnectionPtr connection, int fail,
343 int code, AtomPtr message)
345 HTTPRequestPtr request = connection->request;
346 if(request) {
347 if(request->request) {
348 httpClientError(request->request, code, retainAtom(message));
350 if(fail) {
351 request->object->flags |= OBJECT_FAILED;
352 /* Abort dynamic objects because we won't be able to fill in
353 missing data with a range request later. */
354 if(request->object->flags & (OBJECT_INITIAL | OBJECT_DYNAMIC))
355 abortObject(request->object, code, retainAtom(message));
356 notifyObject(request->object);
359 releaseAtom(message);
360 if(!connection->connecting)
361 httpServerFinish(connection, 1, 0);
364 void
365 httpServerAbortRequest(HTTPRequestPtr request, int fail,
366 int code, AtomPtr message)
368 if(request->connection && request == request->connection->request) {
369 httpServerAbort(request->connection, fail, code, message);
370 } else {
371 HTTPRequestPtr requestor = request->request;
372 if(requestor) {
373 requestor->request = NULL;
374 request->request = NULL;
375 httpClientError(requestor, code, retainAtom(message));
377 if(fail) {
378 request->object->flags |= OBJECT_FAILED;
379 if(request->object->flags & OBJECT_INITIAL)
380 abortObject(request->object, code, retainAtom(message));
381 notifyObject(request->object);
383 releaseAtom(message);
387 void
388 httpServerClientReset(HTTPRequestPtr request)
390 if(request->connection &&
391 request->connection->fd >= 0 &&
392 !request->connection->connecting &&
393 request->connection->request == request)
394 pokeFdEvent(request->connection->fd, -ECLIENTRESET, POLLIN | POLLOUT);
399 httpMakeServerRequest(char *name, int port, ObjectPtr object,
400 int method, int from, int to, HTTPRequestPtr requestor)
402 HTTPServerPtr server;
403 HTTPRequestPtr request;
404 int rc;
406 assert(!(object->flags & OBJECT_INPROGRESS));
408 if(parentHost) {
409 server = getServer(parentHost->string, parentPort, 1);
410 } else {
411 server = getServer(name, port, 0);
413 if(server == NULL) return -1;
415 object->flags |= OBJECT_INPROGRESS;
416 object->requestor = requestor;
418 request = httpMakeRequest();
419 if(!request) {
420 do_log(L_ERROR, "Couldn't allocate request.\n");
421 return -1;
424 /* Because we allocate objects in chunks, we cannot have data that
425 doesn't start at a chunk boundary. */
426 if(from % CHUNK_SIZE != 0) {
427 if(allowUnalignedRangeRequests) {
428 objectFillFromDisk(object, from / CHUNK_SIZE * CHUNK_SIZE, 1);
429 if(objectHoleSize(object, from - 1) != 0)
430 from = from / CHUNK_SIZE * CHUNK_SIZE;
431 } else {
432 from = from / CHUNK_SIZE * CHUNK_SIZE;
436 request->object = retainObject(object);
437 request->method = method;
438 if(method == METHOD_CONDITIONAL_GET) {
439 if(server->lies > 0)
440 request->method = METHOD_HEAD;
442 request->flags =
443 REQUEST_PERSISTENT |
444 (expectContinue ? (requestor->flags & REQUEST_WAIT_CONTINUE) : 0);
445 request->from = from;
446 request->to = to;
447 request->request = requestor;
448 requestor->request = request;
449 request->cache_control = requestor->cache_control;
450 request->time0 = null_time;
451 request->time1 = null_time;
453 rc = httpServerQueueRequest(server, request);
454 if(rc < 0) {
455 do_log(L_ERROR, "Couldn't queue request.\n");
456 request->request = NULL;
457 requestor->request = NULL;
458 object->flags &= ~(OBJECT_INPROGRESS | OBJECT_VALIDATING);
459 releaseNotifyObject(object);
460 httpDestroyRequest(request);
461 return 1;
464 if(request->flags & REQUEST_WAIT_CONTINUE) {
465 if(server->version == HTTP_10) {
466 httpServerAbortRequest(request, 1,
467 417, internAtom("Expectation failed"));
468 return 1;
470 } else if(expectContinue >= 2 && server->version == HTTP_11) {
471 if(request->method == METHOD_POST || request->method == METHOD_PUT)
472 request->flags |= REQUEST_WAIT_CONTINUE;
475 again:
476 rc = httpServerTrigger(server);
477 if(rc < 0) {
478 /* We must be very short on memory. If there are any requests
479 queued, we abort one and try again. If there aren't, we
480 give up. */
481 do_log(L_ERROR, "Couldn't trigger server -- out of memory?\n");
482 if(server->request) {
483 httpServerAbortRequest(server->request, 1, 503,
484 internAtom("Couldn't trigger server"));
485 goto again;
488 return 1;
492 httpServerConnection(HTTPServerPtr server)
494 HTTPConnectionPtr connection;
495 int i;
497 connection = httpMakeConnection();
498 if(connection == NULL) {
499 do_log(L_ERROR, "Couldn't allocate server connection.\n");
500 return -1;
502 connection->server = server;
504 for(i = 0; i < server->numslots; i++) {
505 if(!server->connection[i]) {
506 server->connection[i] = connection;
507 break;
510 assert(i < server->numslots);
512 connection->request = NULL;
513 connection->request_last = NULL;
515 do_log(D_SERVER_CONN, "C... %s:%d.\n",
516 scrub(connection->server->name), connection->server->port);
517 httpSetTimeout(connection, serverTimeout);
518 if(socksParentProxy) {
519 connection->connecting = CONNECTING_SOCKS;
520 do_socks_connect(server->name, connection->server->port,
521 httpServerSocksHandler, connection);
522 } else {
523 connection->connecting = CONNECTING_DNS;
524 do_gethostbyname(server->name, 0,
525 httpServerConnectionDnsHandler,
526 connection);
528 return 1;
532 httpServerConnectionDnsHandler(int status, GethostbynameRequestPtr request)
534 HTTPConnectionPtr connection = request->data;
536 httpSetTimeout(connection, -1);
538 if(status <= 0) {
539 AtomPtr message;
540 message = internAtomF("Host %s lookup failed: %s",
541 request->name ?
542 request->name->string : "(unknown)",
543 request->error_message ?
544 request->error_message->string :
545 pstrerror(-status));
546 do_log(L_ERROR, "Host %s lookup failed: %s (%d).\n",
547 request->name ?
548 scrub(request->name->string) : "(unknown)",
549 request->error_message ?
550 request->error_message->string :
551 pstrerror(-status), -status);
552 connection->connecting = 0;
553 if(connection->server->request)
554 httpServerAbortRequest(connection->server->request, 1, 504,
555 retainAtom(message));
556 httpServerAbort(connection, 1, 502, message);
557 return 1;
560 if(request->addr->string[0] == DNS_CNAME) {
561 if(request->count > 10) {
562 AtomPtr message = internAtom("DNS CNAME loop");
563 do_log(L_ERROR, "DNS CNAME loop.\n");
564 connection->connecting = 0;
565 if(connection->server->request)
566 httpServerAbortRequest(connection->server->request, 1, 504,
567 retainAtom(message));
568 httpServerAbort(connection, 1, 504, message);
569 return 1;
572 httpSetTimeout(connection, serverTimeout);
573 do_gethostbyname(request->addr->string + 1, request->count + 1,
574 httpServerConnectionDnsHandler,
575 connection);
576 return 1;
579 connection->connecting = CONNECTING_CONNECT;
580 httpSetTimeout(connection, serverTimeout);
581 do_connect(retainAtom(request->addr), connection->server->addrindex,
582 connection->server->port,
583 httpServerConnectionHandler, connection);
584 return 1;
588 httpServerConnectionHandler(int status,
589 FdEventHandlerPtr event,
590 ConnectRequestPtr request)
592 HTTPConnectionPtr connection = request->data;
594 assert(connection->fd < 0);
595 if(request->fd >= 0) {
596 int rc;
597 connection->fd = request->fd;
598 connection->server->addrindex = request->index;
599 rc = setNodelay(connection->fd, 1);
600 if(rc < 0)
601 do_log_error(L_WARN, errno, "Couldn't disable Nagle's algorithm");
604 return httpServerConnectionHandlerCommon(status, connection);
608 httpServerSocksHandler(int status, SocksRequestPtr request)
610 HTTPConnectionPtr connection = request->data;
612 assert(connection->fd < 0);
613 if(request->fd >= 0) {
614 connection->fd = request->fd;
615 connection->server->addrindex = 0;
617 return httpServerConnectionHandlerCommon(status, connection);
621 httpServerConnectionHandlerCommon(int status, HTTPConnectionPtr connection)
623 httpSetTimeout(connection, -1);
625 if(status < 0) {
626 AtomPtr message =
627 internAtomError(-status, "Connect to %s:%d failed",
628 connection->server->name,
629 connection->server->port);
630 if(status != -ECLIENTRESET)
631 do_log_error(L_ERROR, -status, "Connect to %s:%d failed",
632 scrub(connection->server->name),
633 connection->server->port);
634 connection->connecting = 0;
635 if(connection->server->request)
636 httpServerAbortRequest(connection->server->request,
637 status != -ECLIENTRESET, 504,
638 retainAtom(message));
639 httpServerAbort(connection, status != -ECLIENTRESET, 504, message);
640 return 1;
643 do_log(D_SERVER_CONN, "C %s:%d.\n",
644 scrub(connection->server->name), connection->server->port);
646 connection->connecting = 0;
647 /* serverTrigger will take care of inserting any timeouts */
648 httpServerTrigger(connection->server);
649 return 1;
653 httpServerIdleHandler(int a, FdEventHandlerPtr event)
655 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
656 HTTPServerPtr server = connection->server;
657 int i;
659 assert(!connection->request);
661 do_log(D_SERVER_CONN, "Idle connection to %s:%d died.\n",
662 scrub(connection->server->name), connection->server->port);
664 for(i = 0; i < server->maxslots; i++) {
665 if(connection == server->connection[i]) {
666 server->idleHandler[i] = NULL;
667 break;
670 assert(i < server->maxslots);
672 httpServerAbort(connection, 1, 504, internAtom("Timeout"));
673 return 1;
676 /* Discard aborted requests at the head of the queue. */
677 static void
678 httpServerDiscardRequests(HTTPServerPtr server)
680 HTTPRequestPtr request;
681 while(server->request && !server->request->request) {
682 request = server->request;
683 server->request = request->next;
684 request->next = NULL;
685 if(server->request == NULL)
686 server->request_last = NULL;
687 request->object->flags &= ~(OBJECT_INPROGRESS | OBJECT_VALIDATING);
688 releaseNotifyObject(request->object);
689 request->object = NULL;
690 httpDestroyRequest(request);
694 static int
695 pipelineIsSmall(HTTPConnectionPtr connection)
697 HTTPRequestPtr request = connection->request;
699 if(pipelineAdditionalRequests <= 0)
700 return 0;
701 else if(pipelineAdditionalRequests >= 2)
702 return 1;
704 if(!request)
705 return 1;
706 if(request->next || !(request->flags & REQUEST_PERSISTENT))
707 return 0;
708 if(request->method == METHOD_HEAD ||
709 request->method == METHOD_CONDITIONAL_GET)
710 return 1;
711 if(request->to >= 0 && connection->server->rate > 0 &&
712 request->to - request->from < connection->server->rate *
713 smallRequestTime)
714 return 1;
715 return 0;
718 static int
719 numRequests(HTTPServerPtr server)
721 int n = 0;
722 HTTPRequestPtr request = server->request;
723 while(request) {
724 n++;
725 request = request->next;
727 return n;
730 HTTPConnectionPtr
731 httpServerGetConnection(HTTPServerPtr server, int *idle_return)
733 int i, j;
734 int connecting = 0, empty = 0, idle = 0;
736 j = -1;
737 /* Try to find an idle connection */
738 for(i = 0; i < server->numslots; i++) {
739 if(server->connection[i]) {
740 if(!server->connection[i]->connecting) {
741 if(!server->connection[i]->request) {
742 if(server->idleHandler[i])
743 unregisterFdEvent(server->idleHandler[i]);
744 server->idleHandler[i] = NULL;
745 if(j < 0) j = i;
746 idle++;
748 } else
749 connecting++;
750 } else
751 empty++;
754 if(j >= 0) {
755 *idle_return = idle;
756 return server->connection[j];
759 /* If there's an empty slot, schedule connection creation */
760 if(empty) {
761 /* Don't open a connection if there are already enough in
762 progress, except if the server doesn't do persistent
763 connections and there's only one in progress. */
764 if((connecting == 0 || (server->persistent <= 0 && connecting <= 1)) ||
765 connecting < numRequests(server)) {
766 httpServerConnection(server);
770 /* Find a connection that can accept additional requests */
771 if(server->version == HTTP_11 && server->pipeline >= 4) {
772 for(i = 0; i < serverSlots; i++) {
773 if(server->connection[i] && !server->connection[i]->connecting &&
774 pipelineIsSmall(server->connection[i])) {
775 if(server->idleHandler[i])
776 unregisterFdEvent(server->idleHandler[i]);
777 server->idleHandler[i] = NULL;
778 *idle_return = 0;
779 return server->connection[i];
783 *idle_return = idle;
784 return NULL;
788 httpServerTrigger(HTTPServerPtr server)
790 HTTPConnectionPtr connection;
791 HTTPRequestPtr request;
792 int idle, n, i, rc, numidle;
794 while(server->request) {
795 httpServerDiscardRequests(server);
797 if(!server->request)
798 break;
800 if(REQUEST_SIDE(server->request)) {
801 rc = httpServerSideRequest(server);
802 /* If rc is 0, httpServerSideRequest didn't dequeue this
803 request. Go through the scheduling loop again, come
804 back later. */
805 if(rc <= 0) break;
806 continue;
808 connection = httpServerGetConnection(server, &numidle);
809 if(!connection) break;
811 /* If server->pipeline <= 0, we don't do pipelining. If
812 server->pipeline is 1, then we are ready to start probing
813 for pipelining on the server; we then send exactly two
814 requests in what is hopefully a single packet to check
815 whether the server has the nasty habit of discarding its
816 input buffers after each request.
817 If server->pipeline is 2 or 3, the pipelining probe is in
818 progress on this server, and we don't pipeline anything
819 until it succeeds. When server->pipeline >= 4, pipelining
820 is believed to work on this server. */
821 if(server->version != HTTP_11 || server->pipeline <= 0 ||
822 server->pipeline == 2 || server->pipeline == 3) {
823 if(connection->pipelined == 0)
824 n = 1;
825 else
826 n = 0;
827 } else if(server->pipeline == 1) {
828 if(connection->pipelined == 0)
829 n = MIN(2, maxPipelineTrain);
830 else
831 n = 0;
832 } else {
833 n = maxPipelineTrain;
836 /* Don't pipeline if there are more idle connections */
837 if(numidle >= 2)
838 n = MIN(n, 1);
840 idle = !connection->pipelined;
841 i = 0;
842 while(server->request && connection->pipelined < n) {
843 httpServerDiscardRequests(server);
844 if(!server->request) break;
845 request = server->request;
846 assert(request->request->request == request);
847 rc = httpWriteRequest(connection, request, -1);
848 if(rc < 0) {
849 if(i == 0)
850 httpServerAbortRequest(request, rc != -ECLIENTRESET, 502,
851 internAtom("Couldn't "
852 "write request"));
853 break;
855 do_log(D_SERVER_CONN, "W: ");
856 do_log_n(D_SERVER_CONN,
857 request->object->key, request->object->key_size);
858 do_log(D_SERVER_CONN, " (%d)\n", request->method);
859 if(connection->pipelined > 0)
860 request->flags |= REQUEST_PIPELINED;
861 request->time0 = current_time;
862 i++;
863 server->request = request->next;
864 request->next = NULL;
865 if(server->request == NULL)
866 server->request_last = NULL;
867 httpQueueRequest(connection, request);
868 connection->pipelined++;
870 if(server->persistent > 0 && server->pipeline == 1 && i >= 2)
871 server->pipeline = 2;
873 if(i > 0) httpServerSendRequest(connection);
875 if(idle && connection->pipelined > 0)
876 httpServerReply(connection, 0);
878 if(i == 0) break;
881 for(i = 0; i < server->maxslots; i++) {
882 if(server->connection[i] &&
883 !server->connection[i]->connecting &&
884 !server->connection[i]->request) {
885 /* Artificially age any fresh connections that aren't used
886 straight away; this is necessary for the logic for POST and
887 the logic that determines whether a given request should be
888 restarted. */
889 if(server->connection[i]->serviced == 0)
890 server->connection[i]->serviced = 1;
891 if(!server->idleHandler[i])
892 server->idleHandler[i] =
893 registerFdEvent(server->connection[i]->fd, POLLIN,
894 httpServerIdleHandler,
895 sizeof(HTTPConnectionPtr),
896 &server->connection[i]);
897 if(!server->idleHandler[i]) {
898 do_log(L_ERROR, "Couldn't register idle handler.\n");
899 httpServerFinish(server->connection[i], 1, 0);
901 httpSetTimeout(server->connection[i], serverIdleTimeout);
905 return 1;
909 httpServerSideRequest(HTTPServerPtr server)
911 HTTPRequestPtr request = server->request;
912 HTTPConnectionPtr connection;
913 HTTPRequestPtr requestor = request->request;
914 HTTPConnectionPtr client = requestor->connection;
915 int rc, i, freeslots, idle, connecting;
917 assert(REQUEST_SIDE(request));
919 connection = NULL;
920 freeslots = 0;
921 idle = -1;
922 connecting = 0;
924 /* Find a fresh connection */
925 for(i = 0; i < server->numslots; i++) {
926 if(!server->connection[i])
927 freeslots++;
928 else if(!server->connection[i]->connecting) {
929 if(!server->connection[i]->request) {
930 if(server->connection[i]->serviced == 0) {
931 if(server->idleHandler[i])
932 unregisterFdEvent(server->idleHandler[i]);
933 server->idleHandler[i] = NULL;
934 connection = server->connection[i];
935 break;
936 } else {
937 idle = i;
940 } else {
941 connecting++;
945 if(!connection) {
946 /* Make sure that a fresh connection will be established at some
947 point, then wait until httpServerTrigger calls us again. */
948 if(freeslots) {
949 httpServerConnection(server);
950 } else {
951 if(idle >= 0) {
952 /* Shutdown a random idle connection */
953 pokeFdEvent(server->connection[idle]->fd,
954 -EDOSHUTDOWN, POLLIN | POLLOUT);
957 return 0;
960 rc = httpWriteRequest(connection, request, client->bodylen);
961 if(rc < 0) {
962 do_log(L_ERROR, "Couldn't write POST or PUT request.\n");
963 httpServerAbortRequest(request, rc != -ECLIENTRESET, 502,
964 internAtom("Couldn't write request"));
965 return 0;
967 server->request = request->next;
968 request->next = NULL;
969 if(server->request == NULL)
970 server->request_last = NULL;
971 httpQueueRequest(connection, request);
972 connection->pipelined = 1;
973 request->time0 = current_time;
974 connection->reqoffset = 0;
975 connection->bodylen = client->bodylen;
976 httpServerDoSide(connection);
977 return 1;
980 int
981 httpServerDoSide(HTTPConnectionPtr connection)
983 HTTPRequestPtr request = connection->request;
984 HTTPRequestPtr requestor = request->request;
985 HTTPConnectionPtr client = requestor->connection;
986 int len = MIN(client->reqlen - client->reqbegin,
987 connection->bodylen - connection->reqoffset);
988 int doflush =
989 len > 0 &&
990 (len >= maxSideBuffering ||
991 client->reqbegin > 0 ||
992 (connection->reqoffset + client->reqlen - client->reqbegin) >=
993 connection->bodylen);
994 int done = connection->reqoffset >= connection->bodylen;
996 assert(connection->bodylen >= 0);
998 httpSetTimeout(connection, 60);
1000 if(connection->reqlen > 0) {
1001 /* Send the headers, but don't send any part of the body if
1002 we're in wait_continue. */
1003 do_stream_2(IO_WRITE,
1004 connection->fd, 0,
1005 connection->reqbuf, connection->reqlen,
1006 client->reqbuf + client->reqbegin,
1007 (request->flags & REQUEST_WAIT_CONTINUE) ? 0 : len,
1008 httpServerSideHandler2, connection);
1009 httpServerReply(connection, 0);
1010 } else if(request->object->flags & OBJECT_ABORTED) {
1011 if(connection->reqbuf)
1012 dispose_chunk(connection->reqbuf);
1013 connection->reqbuf = NULL;
1014 connection->reqlen = 0;
1015 pokeFdEvent(connection->fd, -ESHUTDOWN, POLLIN);
1016 if(client->flags & CONN_READER) {
1017 client->flags |= CONN_SIDE_READER;
1018 do_stream(IO_READ | IO_IMMEDIATE | IO_NOTNOW,
1019 client->fd, 0, NULL, 0,
1020 httpClientSideHandler, client);
1022 } else if(!(request->flags & REQUEST_WAIT_CONTINUE) && doflush) {
1023 /* Make sure there's a reqbuf, as httpServerFinish uses
1024 it to determine if there's a writer. */
1025 if(connection->reqbuf == NULL)
1026 connection->reqbuf = get_chunk();
1027 assert(connection->reqbuf != NULL);
1028 do_stream(IO_WRITE,
1029 connection->fd, 0,
1030 client->reqbuf + client->reqbegin, len,
1031 httpServerSideHandler, connection);
1032 } else {
1033 if(connection->reqbuf) {
1034 httpConnectionDestroyReqbuf(connection);
1035 connection->reqlen = 0;
1037 if(request->flags & REQUEST_WAIT_CONTINUE) {
1038 do_log(D_SERVER_CONN, "W... %s:%d.\n",
1039 scrub(connection->server->name), connection->server->port);
1040 return 1;
1042 client->flags |= CONN_SIDE_READER;
1043 do_stream(IO_READ | (done ? IO_IMMEDIATE : 0 ) | IO_NOTNOW,
1044 client->fd, client->reqlen,
1045 client->reqbuf, CHUNK_SIZE,
1046 httpClientSideHandler, client);
1048 return 1;
1051 static int
1052 httpClientDelayedDoSideHandler(TimeEventHandlerPtr event)
1054 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1055 httpServerDoSide(connection);
1056 return 1;
1059 static int
1060 httpServerDelayedDoSide(HTTPConnectionPtr connection)
1062 TimeEventHandlerPtr handler;
1063 handler = scheduleTimeEvent(0, httpClientDelayedDoSideHandler,
1064 sizeof(connection), &connection);
1065 if(!handler) {
1066 do_log(L_ERROR, "Couldn't schedule DoSide -- freeing memory.\n");
1067 free_chunk_arenas();
1068 handler = scheduleTimeEvent(0, httpClientDelayedDoSideHandler,
1069 sizeof(connection), &connection);
1070 do_log(L_ERROR, "Couldn't schedule DoSide.\n");
1071 /* Somebody will hopefully end up timing out. */
1072 return 1;
1074 return 1;
1077 static int
1078 httpServerSideHandlerCommon(int kind, int status,
1079 FdEventHandlerPtr event,
1080 StreamRequestPtr srequest)
1082 HTTPConnectionPtr connection = srequest->data;
1083 HTTPRequestPtr request = connection->request;
1084 HTTPRequestPtr requestor = request->request;
1085 HTTPConnectionPtr client = requestor->connection;
1086 int bodylen;
1088 assert(request->object->flags & OBJECT_INPROGRESS);
1090 if(status) {
1091 do_log_error(L_ERROR, -status, "Couldn't write to server");
1092 httpConnectionDestroyReqbuf(connection);
1093 if(status != -ECLIENTRESET)
1094 shutdown(connection->fd, 2);
1095 abortObject(request->object, 502,
1096 internAtom("Couldn't write to server"));
1097 /* Let the read side handle the error */
1098 httpServerDoSide(connection);
1099 return 1;
1102 assert(srequest->offset > 0);
1104 if(kind == 2) {
1105 if(srequest->offset < connection->reqlen)
1106 return 0;
1107 bodylen = srequest->offset - connection->reqlen;
1108 connection->reqlen = 0;
1109 httpConnectionDestroyReqbuf(connection);
1110 } else {
1111 bodylen = srequest->offset;
1115 assert(client->reqbegin + bodylen <= client->reqlen);
1117 if(client->reqlen > client->reqbegin + bodylen)
1118 memmove(client->reqbuf, client->reqbuf + client->reqbegin + bodylen,
1119 client->reqlen - client->reqbegin - bodylen);
1120 client->reqlen -= bodylen + client->reqbegin;
1121 client->reqbegin = 0;
1122 connection->reqoffset += bodylen;
1123 httpServerDoSide(connection);
1124 return 1;
1128 httpServerSideHandler(int status,
1129 FdEventHandlerPtr event,
1130 StreamRequestPtr srequest)
1132 return httpServerSideHandlerCommon(1, status, event, srequest);
1136 httpServerSideHandler2(int status,
1137 FdEventHandlerPtr event,
1138 StreamRequestPtr srequest)
1140 return httpServerSideHandlerCommon(2, status, event, srequest);
1143 /* s is 0 to keep the connection alive, 1 to shutdown the connection */
1144 void
1145 httpServerFinish(HTTPConnectionPtr connection, int s, int offset)
1147 HTTPServerPtr server = connection->server;
1148 HTTPRequestPtr request = connection->request;
1149 int i;
1151 if(request) {
1152 assert(connection->pipelined >= 1);
1153 assert((connection->pipelined > 1) == (request->next != NULL));
1154 } else {
1155 assert(connection->pipelined == 0);
1158 if(!s && (!connection->request ||
1159 !(connection->request->flags & REQUEST_PERSISTENT)))
1160 s = 1;
1162 if(connection->serviced >= maxConnectionRequests ||
1163 connection->time < current_time.tv_sec - maxConnectionAge)
1164 s = 1;
1166 if(connection->reqbuf) {
1167 /* As most normal requests go out in a single packet, this is
1168 extremely unlikely to happen. As for POST/PUT requests,
1169 they are not pipelined, so this can only happen if the
1170 server sent an error reply early. */
1171 assert(connection->fd >= 0);
1172 shutdown(connection->fd, 1);
1173 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLOUT);
1174 httpServerDelayedFinish(connection);
1175 goto done;
1178 if(request) {
1179 /* Update statistics about the server */
1180 int size = -1, d = -1, rtt = -1, rate = -1;
1181 if(connection->offset > 0 && request->from >= 0)
1182 size = connection->offset - request->from;
1183 if(request->time1.tv_sec != null_time.tv_sec) {
1184 d = timeval_minus_usec(&current_time, &request->time1);
1185 if(!(request->flags & REQUEST_PIPELINED) &&
1186 request->time0.tv_sec != null_time.tv_sec)
1187 rtt = timeval_minus_usec(&request->time1, &request->time0);
1188 if(size >= 8192 && d > 50000)
1189 rate = ((double)size / (double)d) * 1000000.0 + 0.5;
1191 request->time0 = null_time;
1192 request->time1 = null_time;
1194 if(rtt >= 0) {
1195 if(server->rtt >= 0)
1196 server->rtt = (3 * server->rtt + rtt + 2) / 4;
1197 else
1198 server->rtt = rtt;
1200 if(rate >= 0) {
1201 if(server->rate >= 0)
1202 server->rate = (3 * server->rate + rate + 2) / 4;
1203 else
1204 server->rate = rate;
1207 httpDequeueRequest(connection);
1208 connection->pipelined--;
1209 request->object->flags &= ~(OBJECT_INPROGRESS | OBJECT_VALIDATING);
1210 if(request->request) {
1211 request->request->request = NULL;
1212 request->request = NULL;
1214 releaseNotifyObject(request->object);
1215 request->object = NULL;
1216 httpDestroyRequest(request);
1219 do_log(D_SERVER_CONN, "Done with server %s:%d connection (%d)\n",
1220 scrub(connection->server->name), connection->server->port, s);
1222 assert(offset <= connection->len);
1224 if(!s) {
1225 if(offset < connection->len) {
1226 assert(connection->buf != NULL);
1227 if(!connection->pipelined) {
1228 do_log(L_WARN,
1229 "Closing connection to %s:%d: "
1230 "%d stray bytes of data.\n",
1231 scrub(server->name), server->port,
1232 connection->len - offset);
1233 s = 1;
1234 } else {
1235 memmove(connection->buf, connection->buf + offset,
1236 connection->len - offset);
1237 connection->len = connection->len - offset;
1238 if((connection->flags & CONN_BIGBUF) &&
1239 connection->len <= CHUNK_SIZE)
1240 httpConnectionUnbigify(connection);
1242 } else {
1243 connection->len = 0;
1247 connection->server->time = current_time.tv_sec;
1248 connection->serviced++;
1250 if(s) {
1251 if(connection->timeout)
1252 cancelTimeEvent(connection->timeout);
1253 connection->timeout = NULL;
1254 httpConnectionDestroyBuf(connection);
1255 if(connection->fd >= 0)
1256 CLOSE(connection->fd);
1257 connection->fd = -1;
1258 server->persistent -= 1;
1259 if(server->persistent < -5)
1260 server->numslots = MIN(server->maxslots, serverMaxSlots);
1261 if(connection->request) {
1262 HTTPRequestPtr req;
1263 do_log(D_SERVER_CONN, "Restarting pipeline to %s:%d.\n",
1264 scrub(server->name), server->port);
1265 if(server->pipeline == 2)
1266 server->pipeline -= 20;
1267 else
1268 server->pipeline -= 5;
1269 req = connection->request;
1270 while(req) {
1271 req->connection = NULL;
1272 req = req->next;
1274 if(server->request)
1275 connection->request_last->next = server->request;
1276 else
1277 server->request_last = connection->request_last;
1278 server->request = connection->request;
1279 connection->request = NULL;
1280 connection->request_last = NULL;
1282 /* Make sure we don't get confused into thinking a probe
1283 is in progress. */
1284 if(server->pipeline == 2 || server->pipeline == 3)
1285 server->pipeline = 1;
1286 for(i = 0; i < server->maxslots; i++)
1287 if(connection == server->connection[i])
1288 break;
1289 assert(i < server->maxslots);
1290 if(server->idleHandler[i])
1291 unregisterFdEvent(server->idleHandler[i]);
1292 server->idleHandler[i] = NULL;
1293 server->connection[i] = NULL;
1294 free(connection);
1295 } else {
1296 server->persistent += 1;
1297 if(server->persistent > 0)
1298 server->numslots = MIN(server->maxslots,
1299 server->version == HTTP_10 ?
1300 serverSlots1 : serverSlots);
1301 httpSetTimeout(connection, serverTimeout);
1302 /* See httpServerTrigger */
1303 if(connection->pipelined ||
1304 (server->version == HTTP_11 && server->pipeline <= 0) ||
1305 (server->pipeline == 3)) {
1306 server->pipeline++;
1308 if(connection->pipelined) {
1309 httpServerReply(connection, 1);
1310 } else {
1311 httpConnectionDestroyBuf(connection);
1315 done:
1316 httpServerTrigger(server);
1319 static int
1320 httpServerDelayedFinishHandler(TimeEventHandlerPtr event)
1322 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1323 httpServerFinish(connection, 1, 0);
1324 return 1;
1327 static void
1328 httpServerDelayedFinish(HTTPConnectionPtr connection)
1330 TimeEventHandlerPtr handler;
1332 handler = scheduleTimeEvent(1, httpServerDelayedFinishHandler,
1333 sizeof(connection), &connection);
1334 if(!handler) {
1335 do_log(L_ERROR,
1336 "Couldn't schedule delayed finish -- freeing memory.");
1337 free_chunk_arenas();
1338 handler = scheduleTimeEvent(1, httpServerDelayedFinishHandler,
1339 sizeof(connection), &connection);
1340 if(!handler) {
1341 do_log(L_ERROR,
1342 "Couldn't schedule delayed finish -- aborting.\n");
1343 polipoExit();
1348 void
1349 httpServerReply(HTTPConnectionPtr connection, int immediate)
1351 assert(connection->pipelined > 0);
1353 if(connection->request->request == NULL) {
1354 do_log(L_WARN, "Aborting pipeline on %s:%d.\n",
1355 scrub(connection->server->name), connection->server->port);
1356 httpServerFinish(connection, 1, 0);
1357 return;
1360 do_log(D_SERVER_CONN, "R: %s (%d)\n",
1361 scrub(connection->request->object->key),
1362 connection->request->method);
1364 if(connection->len == 0)
1365 httpConnectionDestroyBuf(connection);
1367 httpSetTimeout(connection, serverTimeout);
1368 do_stream_buf(IO_READ | (immediate ? IO_IMMEDIATE : 0) | IO_NOTNOW,
1369 connection->fd, connection->len,
1370 &connection->buf, CHUNK_SIZE,
1371 httpServerReplyHandler, connection);
1375 httpConnectionPipelined(HTTPConnectionPtr connection)
1377 HTTPRequestPtr request = connection->request;
1378 int i = 0;
1379 while(request) {
1380 i++;
1381 request = request->next;
1383 return i;
1386 void
1387 httpServerUnpipeline(HTTPRequestPtr request)
1389 HTTPConnectionPtr connection = request->connection;
1390 HTTPServerPtr server = connection->server;
1392 request->flags &= ~REQUEST_PERSISTENT;
1393 if(request->next) {
1394 HTTPRequestPtr req;
1395 do_log(L_WARN,
1396 "Restarting pipeline to %s:%d.\n",
1397 scrub(connection->server->name), connection->server->port);
1398 req = request->next;
1399 while(req) {
1400 req->connection = NULL;
1401 req = req->next;
1403 if(server->request)
1404 connection->request_last->next = server->request;
1405 else
1406 server->request_last = connection->request_last;
1407 server->request = request->next;
1408 request->next = NULL;
1409 connection->request_last = request;
1411 connection->pipelined = httpConnectionPipelined(connection);
1414 void
1415 httpServerRestart(HTTPConnectionPtr connection)
1417 HTTPServerPtr server = connection->server;
1418 HTTPRequestPtr request = connection->request;
1420 if(request) {
1421 HTTPRequestPtr req;
1422 if(request->next)
1423 do_log(L_WARN,
1424 "Restarting pipeline to %s:%d.\n",
1425 scrub(connection->server->name), connection->server->port);
1426 req = request;
1427 while(req) {
1428 req->connection = NULL;
1429 req = req->next;
1431 if(server->request)
1432 connection->request_last->next = server->request;
1433 else
1434 server->request_last = connection->request_last;
1435 server->request = request;
1436 connection->request = NULL;
1437 connection->request_last = NULL;
1439 connection->pipelined = 0;
1440 httpServerFinish(connection, 1, 0);
1444 httpServerRequest(ObjectPtr object, int method, int from, int to,
1445 HTTPRequestPtr requestor, void *closure)
1447 int rc;
1448 char name[132];
1449 int port;
1450 int x, y, z;
1452 assert(from >= 0 && (to < 0 || to > from));
1453 assert(closure == NULL);
1454 assert(!(object->flags & OBJECT_LOCAL));
1455 assert(object->type == OBJECT_HTTP);
1457 if(object->flags & OBJECT_INPROGRESS)
1458 return 1;
1460 if(requestor->flags & REQUEST_REQUESTED)
1461 return 0;
1463 assert(requestor->request == NULL);
1465 if(proxyOffline)
1466 return -1;
1468 rc = parseUrl(object->key, object->key_size, &x, &y, &port, &z);
1470 if(rc < 0 || x < 0 || y < 0 || y - x > 131) {
1471 do_log(L_ERROR, "Couldn't parse URL %s\n", scrub(object->key));
1472 abortObject(object, 400, internAtom("Couldn't parse URL"));
1473 notifyObject(object);
1474 return 1;
1477 if(!intListMember(port, allowedPorts)) {
1478 do_log(L_ERROR, "Attempted connection to port %d.\n", port);
1479 abortObject(object, 403, internAtom("Forbidden port"));
1480 notifyObject(object);
1481 return 1;
1484 memcpy(name, ((char*)object->key) + x, y - x);
1485 name[y - x] = '\0';
1487 requestor->flags |= REQUEST_REQUESTED;
1488 rc = httpMakeServerRequest(name, port, object, method, from, to,
1489 requestor);
1491 if(rc < 0) {
1492 abortObject(object,
1493 503, internAtom("Couldn't schedule server request"));
1494 notifyObject(object);
1495 return 1;
1498 return 1;
1502 httpWriteRequest(HTTPConnectionPtr connection, HTTPRequestPtr request,
1503 int bodylen)
1505 ObjectPtr object = request->object;
1506 int from = request->from, to = request->to, method = request->method;
1507 char *url = object->key, *m;
1508 int url_size = object->key_size;
1509 int x, y, port, z, location_size;
1510 char *location;
1511 int l, n, rc, bufsize;
1513 assert(method != METHOD_NONE);
1515 if(request->method == METHOD_GET ||
1516 request->method == METHOD_CONDITIONAL_GET) {
1517 if(to >= 0) {
1518 assert(to >= from);
1519 if(to == from) {
1520 do_log(L_ERROR, "Requesting empty segment?\n");
1521 return -1;
1525 if(object->flags & OBJECT_DYNAMIC) {
1526 from = 0;
1527 to = -1;
1528 } else {
1529 objectFillFromDisk(object, from / CHUNK_SIZE * CHUNK_SIZE, 1);
1530 l = objectHoleSize(request->object, from);
1531 if(l > 0) {
1532 if(to <= 0 || to > from + l)
1533 to = from + l;
1536 if(pmmSize && connection->server->pipeline >= 4) {
1537 if(from == 0)
1538 to = to < 0 ? pmmFirstSize : MIN(to, pmmFirstSize);
1539 else
1540 to = to < 0 ? from + pmmSize : MIN(to, from + pmmSize);
1543 if(from % CHUNK_SIZE != 0)
1544 if(objectHoleSize(object, from - 1) != 0)
1545 from = from / CHUNK_SIZE * CHUNK_SIZE;
1549 rc = parseUrl(url, url_size, &x, &y, &port, &z);
1551 if(rc < 0 || x < 0 || y < 0) {
1552 return -1;
1555 if(connection->reqbuf == NULL) {
1556 connection->reqbuf = get_chunk();
1557 if(connection->reqbuf == NULL)
1558 return -1;
1559 connection->reqlen = 0;
1562 if(method == METHOD_CONDITIONAL_GET &&
1563 object->last_modified < 0 && object->etag == NULL)
1564 method = request->method = METHOD_GET;
1566 again:
1567 bufsize =
1568 (connection->flags & CONN_BIGREQBUF) ? bigBufferSize : CHUNK_SIZE;
1569 n = connection->reqlen;
1570 switch(method) {
1571 case METHOD_GET:
1572 case METHOD_CONDITIONAL_GET: m = "GET"; break;
1573 case METHOD_HEAD: m = "HEAD"; break;
1574 case METHOD_POST: m = "POST"; break;
1575 case METHOD_PUT: m = "PUT"; break;
1576 default: abort();
1578 n = snnprintf(connection->reqbuf, n, bufsize, "%s ", m);
1580 if(connection->server->isProxy) {
1581 n = snnprint_n(connection->reqbuf, n, bufsize,
1582 url, url_size);
1583 } else {
1584 if(url_size - z == 0) {
1585 location = "/";
1586 location_size = 1;
1587 } else {
1588 location = url + z;
1589 location_size = url_size - z;
1592 n = snnprint_n(connection->reqbuf, n, bufsize,
1593 location, location_size);
1596 do_log(D_SERVER_REQ, "Server request: ");
1597 do_log_n(D_SERVER_REQ, url + x, y - x);
1598 do_log(D_SERVER_REQ, ": ");
1599 do_log_n(D_SERVER_REQ, connection->reqbuf, n);
1600 do_log(D_SERVER_REQ, " (method %d from %d to %d, 0x%lx for 0x%lx)\n",
1601 method, from, to,
1602 (unsigned long)connection, (unsigned long)object);
1604 n = snnprintf(connection->reqbuf, n, bufsize, " HTTP/1.1");
1606 n = snnprintf(connection->reqbuf, n, bufsize, "\r\nHost: ");
1607 n = snnprint_n(connection->reqbuf, n, bufsize, url + x, y - x);
1608 if(port != 80)
1609 n = snnprintf(connection->reqbuf, n, bufsize, ":%d", port);
1611 if(connection->server->isProxy && parentAuthCredentials) {
1612 n = buildServerAuthHeaders(connection->reqbuf, n, bufsize,
1613 parentAuthCredentials);
1616 if(bodylen >= 0)
1617 n = snnprintf(connection->reqbuf, n, bufsize,
1618 "\r\nContent-Length: %d", bodylen);
1620 if(request->flags & REQUEST_WAIT_CONTINUE)
1621 n = snnprintf(connection->reqbuf, n, bufsize,
1622 "\r\nExpect: 100-continue");
1624 if(method != METHOD_HEAD && (from > 0 || to >= 0)) {
1625 if(to >= 0) {
1626 n = snnprintf(connection->reqbuf, n, bufsize,
1627 "\r\nRange: bytes=%d-%d", from, to - 1);
1628 } else {
1629 n = snnprintf(connection->reqbuf, n, bufsize,
1630 "\r\nRange: bytes=%d-", from);
1634 if(method == METHOD_GET && object->etag && (from > 0 || to >= 0)) {
1635 if(request->request && request->request->request == request &&
1636 request->request->from == 0 && request->request->to == -1 &&
1637 pmmSize == 0 && pmmFirstSize == 0)
1638 n = snnprintf(connection->reqbuf, n, bufsize,
1639 "\r\nIf-Range: \"%s\"", object->etag);
1642 if(method == METHOD_CONDITIONAL_GET) {
1643 if(object->last_modified >= 0) {
1644 n = snnprintf(connection->reqbuf, n, bufsize,
1645 "\r\nIf-Modified-Since: ");
1646 n = format_time(connection->reqbuf, n, bufsize,
1647 object->last_modified);
1649 if(object->etag) {
1650 n = snnprintf(connection->reqbuf, n, bufsize,
1651 "\r\nIf-None-Match: \"%s\"", object->etag);
1655 n = httpPrintCacheControl(connection->reqbuf, n, bufsize,
1656 alwaysAddNoTransform ? CACHE_NO_TRANSFORM : 0,
1657 &request->cache_control);
1658 if(n < 0)
1659 goto fail;
1661 if(request->request && request->request->headers) {
1662 n = snnprint_n(connection->reqbuf, n, bufsize,
1663 request->request->headers->string,
1664 request->request->headers->length);
1666 if(!disableVia) {
1667 if(request->request && request->request->via) {
1668 n = snnprintf(connection->reqbuf, n, bufsize,
1669 "\r\nVia: %s, 1.1 %s",
1670 request->request->via->string, proxyName->string);
1671 } else {
1672 n = snnprintf(connection->reqbuf, n, bufsize,
1673 "\r\nVia: 1.1 %s",
1674 proxyName->string);
1678 n = snnprintf(connection->reqbuf, n, bufsize,
1679 "\r\nConnection: %s\r\n\r\n",
1680 (request->flags & REQUEST_PERSISTENT) ?
1681 "keep-alive" : "close");
1682 if(n < 0 || n >= bufsize - 1)
1683 goto fail;
1684 connection->reqlen = n;
1685 return n;
1687 fail:
1688 rc = 0;
1689 if(!(connection->flags & CONN_BIGREQBUF))
1690 rc = httpConnectionBigifyReqbuf(connection);
1691 if(rc == 1)
1692 goto again;
1693 return -1;
1697 httpServerHandler(int status,
1698 FdEventHandlerPtr event,
1699 StreamRequestPtr srequest)
1701 HTTPConnectionPtr connection = srequest->data;
1702 AtomPtr message;
1704 assert(connection->request->object->flags & OBJECT_INPROGRESS);
1706 if(connection->reqlen == 0) {
1707 do_log(D_SERVER_REQ, "Writing aborted on 0x%lx\n",
1708 (unsigned long)connection);
1709 goto fail;
1712 if(status == 0 && !streamRequestDone(srequest)) {
1713 httpSetTimeout(connection, serverTimeout);
1714 return 0;
1717 httpConnectionDestroyReqbuf(connection);
1719 if(status) {
1720 if(connection->serviced >= 1) {
1721 httpServerRestart(connection);
1722 return 1;
1724 if(status >= 0 || status == ECONNRESET) {
1725 message = internAtom("Couldn't send request to server: "
1726 "short write");
1727 } else {
1728 if(status != -EPIPE)
1729 do_log_error(L_ERROR, -status,
1730 "Couldn't send request to server");
1731 message =
1732 internAtomError(-status, "Couldn't send request to server");
1734 goto fail;
1737 return 1;
1739 fail:
1740 httpConnectionDestroyReqbuf(connection);
1741 shutdown(connection->fd, 2);
1742 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLIN);
1743 httpSetTimeout(connection, 60);
1744 return 1;
1748 httpServerSendRequest(HTTPConnectionPtr connection)
1750 assert(connection->server);
1752 if(connection->reqlen == 0) {
1753 do_log(D_SERVER_REQ,
1754 "Writing aborted on 0x%lx\n", (unsigned long)connection);
1755 httpConnectionDestroyReqbuf(connection);
1756 shutdown(connection->fd, 2);
1757 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLIN | POLLOUT);
1758 return -1;
1761 httpSetTimeout(connection, serverTimeout);
1762 do_stream(IO_WRITE, connection->fd, 0,
1763 connection->reqbuf, connection->reqlen,
1764 httpServerHandler, connection);
1765 return 1;
1769 httpServerReplyHandler(int status,
1770 FdEventHandlerPtr event,
1771 StreamRequestPtr srequest)
1773 HTTPConnectionPtr connection = srequest->data;
1774 HTTPRequestPtr request = connection->request;
1775 int i, body;
1776 int bufsize =
1777 (connection->flags & CONN_BIGBUF) ? bigBufferSize : CHUNK_SIZE;
1779 assert(request->object->flags & OBJECT_INPROGRESS);
1780 if(status < 0) {
1781 if(connection->serviced >= 1) {
1782 httpServerRestart(connection);
1783 return 1;
1785 if(status != -ECLIENTRESET)
1786 do_log_error(L_ERROR, -status, "Read from server failed");
1787 httpServerAbort(connection, status != -ECLIENTRESET, 502,
1788 internAtomError(-status, "Read from server failed"));
1789 return 1;
1792 i = findEndOfHeaders(connection->buf, 0, srequest->offset, &body);
1793 connection->len = srequest->offset;
1795 if(i >= 0) {
1796 request->time1 = current_time;
1797 return httpServerHandlerHeaders(status, event, srequest, connection);
1800 if(status) {
1801 if(connection->serviced >= 1) {
1802 httpServerRestart(connection);
1803 return 1;
1805 if(status < 0) {
1806 do_log(L_ERROR,
1807 "Error reading server headers: %d\n", -status);
1808 httpServerAbort(connection, status != -ECLIENTRESET, 502,
1809 internAtomError(-status,
1810 "Error reading server headers"));
1811 } else
1812 httpServerAbort(connection, 1, 502,
1813 internAtom("Server dropped connection"));
1814 return 1;
1817 if(connection->len >= bufsize) {
1818 int rc = 0;
1819 if(!(connection->flags & CONN_BIGBUF))
1820 rc = httpConnectionBigify(connection);
1821 if(rc == 0) {
1822 do_log(L_ERROR, "Couldn't find end of server's headers.\n");
1823 httpServerAbort(connection, 1, 502,
1824 internAtom("Couldn't find end "
1825 "of server's headers"));
1826 return 1;
1827 } else if(rc < 0) {
1828 do_log(L_ERROR, "Couldn't allocate big buffer.\n");
1829 httpServerAbort(connection, 1, 500,
1830 internAtom("Couldn't allocate big buffer"));
1831 return 1;
1833 /* Can't just return 0 -- buf has moved. */
1834 do_stream(IO_READ,
1835 connection->fd, connection->len,
1836 connection->buf, bigBufferSize,
1837 httpServerReplyHandler, connection);
1838 return 1;
1841 return 0;
1845 httpServerHandlerHeaders(int eof,
1846 FdEventHandlerPtr event,
1847 StreamRequestPtr srequest,
1848 HTTPConnectionPtr connection)
1850 HTTPRequestPtr request = connection->request;
1851 ObjectPtr object = request->object;
1852 int rc;
1853 int code, version;
1854 int full_len;
1855 AtomPtr headers;
1856 int len;
1857 int te;
1858 CacheControlRec cache_control;
1859 int age = -1;
1860 time_t date, last_modified, expires;
1861 struct timeval *init_time;
1862 char *etag;
1863 AtomPtr via, new_via;
1864 int expect_body;
1865 HTTPRangeRec content_range;
1866 ObjectPtr new_object = NULL, old_object = NULL;
1867 int supersede = 0;
1868 AtomPtr message = NULL;
1869 int suspectDynamic;
1870 AtomPtr url = NULL;
1871 int waiting = 0;
1873 assert(request->object->flags & OBJECT_INPROGRESS);
1874 assert(eof >= 0);
1876 httpSetTimeout(connection, -1);
1878 if(request->flags & REQUEST_WAIT_CONTINUE) {
1879 waiting = 1;
1880 do_log(D_SERVER_CONN, "W %s:%d.\n",
1881 scrub(connection->server->name), connection->server->port);
1882 request->flags &= ~REQUEST_WAIT_CONTINUE;
1885 rc = httpParseServerFirstLine(connection->buf, &code, &version, &message);
1886 if(rc <= 0) {
1887 do_log(L_ERROR, "Couldn't parse server status line.\n");
1888 httpServerAbort(connection, 1, 502,
1889 internAtom("Couldn't parse server status line"));
1890 return 1;
1893 do_log(D_SERVER_REQ, "Server status: ");
1894 do_log_n(D_SERVER_REQ, connection->buf,
1895 connection->buf[rc - 1] == '\r' ? rc - 2 : rc - 2);
1896 do_log(D_SERVER_REQ, " (0x%lx for 0x%lx)\n",
1897 (unsigned long)connection, (unsigned long)object);
1899 if(version != HTTP_10 && version != HTTP_11) {
1900 do_log(L_ERROR, "Unknown server HTTP version\n");
1901 httpServerAbort(connection, 1, 502,
1902 internAtom("Unknown server HTTP version"));
1903 releaseAtom(message);
1904 return 1;
1907 connection->version = version;
1908 connection->server->version = version;
1909 request->flags |= REQUEST_PERSISTENT;
1911 url = internAtomN(object->key, object->key_size);
1912 rc = httpParseHeaders(0, url, connection->buf, rc, request,
1913 &headers, &len, &cache_control, NULL, &te,
1914 &date, &last_modified, &expires, NULL, NULL, NULL,
1915 &age, &etag, NULL, NULL, &content_range,
1916 NULL, &via, NULL);
1917 if(rc < 0) {
1918 do_log(L_ERROR, "Couldn't parse server headers\n");
1919 releaseAtom(url);
1920 releaseAtom(message);
1921 httpServerAbort(connection, 1, 502,
1922 internAtom("Couldn't parse server headers"));
1923 return 1;
1926 if(date < 0)
1927 date = current_time.tv_sec;
1929 object->code = code;
1930 if(code == 100) {
1931 if(!REQUEST_SIDE(request)) {
1932 httpServerAbort(connection, 1, 502,
1933 internAtom("Unexpected continue status"));
1934 goto fail;
1936 releaseAtom(url);
1937 releaseAtom(message);
1938 /* We've already reset wait_continue above, but we must still
1939 ensure that the writer notices if it is waiting. The server
1940 may send continue status for POST or PUT requests, even when
1941 we don't expect it. */
1942 if(waiting) {
1943 httpServerDelayedDoSide(connection);
1944 notifyObject(object);
1946 connection->len -= rc;
1947 if(connection->len > 0)
1948 memmove(connection->buf, connection->buf + rc, connection->len);
1949 httpServerReply(connection, 1);
1950 return 1;
1951 } else if(waiting) {
1952 /* The server responded with something other than 100 Continue,
1953 but the client side is still has its flag set. Tell it to clear
1954 it now. */
1955 notifyObject(object);
1958 if(code == 101) {
1959 httpServerAbort(connection, 1, 501,
1960 internAtom("Upgrade not implemented"));
1961 goto fail;
1964 if(via && !checkVia(proxyName, via)) {
1965 httpServerAbort(connection, 1, 504, internAtom("Proxy loop detected"));
1966 goto fail;
1968 full_len = content_range.full_length;
1970 if(code == 206) {
1971 if(content_range.from == -1 || content_range.to == -1) {
1972 do_log(L_ERROR, "Partial content without range.\n");
1973 httpServerAbort(connection, 1, 502,
1974 internAtom("Partial content without range"));
1975 goto fail;
1977 if(len >= 0 && len != content_range.to - content_range.from) {
1978 do_log(L_ERROR, "Inconsistent partial content.\n");
1979 httpServerAbort(connection, 1, 502,
1980 internAtom("Inconsistent partial content"));
1981 goto fail;
1983 } else if(code < 400 &&
1984 (content_range.from >= 0 || content_range.to >= 0 ||
1985 content_range.full_length >= 0)) {
1986 do_log(L_WARN, "Range without partial content.\n");
1987 /* Damn anakata. */
1988 content_range.from = -1;
1989 content_range.to = -1;
1990 content_range.full_length = -1;
1991 } else if(code != 304 && code != 412) {
1992 full_len = len;
1995 if(te != TE_IDENTITY && te != TE_CHUNKED) {
1996 do_log(L_ERROR, "Unsupported transfer-encoding\n");
1997 httpServerAbort(connection, 1, 502,
1998 internAtom("Unsupported transfer-encoding"));
1999 goto fail;
2002 if(code == 304) {
2003 if(request->method != METHOD_CONDITIONAL_GET) {
2004 do_log(L_ERROR, "Unexpected \"not changed\" reply from server\n");
2005 httpServerAbort(connection, 1, 502,
2006 internAtom("Unexpected \"not changed\" "
2007 "reply from server"));
2008 goto fail;
2010 if(object->etag && !etag) {
2011 /* RFC 2616 10.3.5. Violated by some front-end proxies. */
2012 do_log(L_WARN, "\"Not changed\" reply with no ETag.\n");
2016 if(code == 412) {
2017 if(request->method != METHOD_CONDITIONAL_GET ||
2018 (!object->etag && !object->last_modified)) {
2019 do_log(L_ERROR,
2020 "Unexpected \"precondition failed\" reply from server.\n");
2021 httpServerAbort(connection, 1, 502,
2022 internAtom("Unexpected \"precondition failed\" "
2023 "reply from server"));
2024 goto fail;
2028 releaseAtom(url);
2030 /* Okay, we're going to accept this reply. */
2032 if((code == 200 || code == 206 || code == 304 || code == 412) &&
2033 (cache_control.flags & (CACHE_NO | CACHE_NO_STORE) ||
2034 cache_control.max_age == 0 ||
2035 (cacheIsShared && cache_control.s_maxage == 0) ||
2036 (expires >= 0 && expires <= object->age))) {
2037 do_log(L_UNCACHEABLE, "Uncacheable object %s (%d)\n",
2038 scrub(object->key), cache_control.flags);
2041 if(request->time0.tv_sec != null_time.tv_sec)
2042 init_time = &request->time0;
2043 else
2044 init_time = &current_time;
2045 age = MIN(init_time->tv_sec - age, init_time->tv_sec);
2047 if(request->method == METHOD_HEAD ||
2048 code < 200 || code == 204 || code == 304)
2049 expect_body = 0;
2050 else if(te == TE_IDENTITY)
2051 expect_body = (len != 0);
2052 else
2053 expect_body = 1;
2055 connection->chunk_remaining = -1;
2056 connection->te = te;
2058 old_object = object;
2060 connection->server->lies--;
2062 if(object->cache_control & CACHE_MISMATCH)
2063 supersede = 1;
2065 if(code == 304 || code == 412) {
2066 if((object->etag && etag && strcmp(object->etag, etag) != 0) ||
2067 (object->last_modified >= 0 && last_modified >= 0 &&
2068 object->last_modified != last_modified)) {
2069 do_log(L_ERROR, "Inconsistent \"%s\" reply for %s\n",
2070 code == 304 ? "not changed":"precondition failed",
2071 scrub(object->key));
2072 object->flags |= OBJECT_DYNAMIC;
2073 supersede = 1;
2075 } else if(!(object->flags & OBJECT_INITIAL)) {
2076 if((object->last_modified < 0 || last_modified < 0) &&
2077 (!object->etag || !etag))
2078 supersede = 1;
2079 else if(object->last_modified != last_modified)
2080 supersede = 1;
2081 else if(object->etag || etag) {
2082 /* We need to be permissive here so as to deal with some
2083 front-end proxies that discard ETags on partial
2084 replies but not on full replies. */
2085 if(etag && object->etag && strcmp(object->etag, etag) != 0)
2086 supersede = 1;
2087 else if(!object->etag)
2088 supersede = 1;
2091 if(!supersede && (object->cache_control & CACHE_VARY) &&
2092 dontTrustVaryETag >= 1) {
2093 /* Check content-type to work around mod_gzip bugs */
2094 if(!httpHeaderMatch(atomContentType, object->headers, headers) ||
2095 !httpHeaderMatch(atomContentEncoding, object->headers, headers))
2096 supersede = 1;
2099 if(full_len < 0 && te == TE_IDENTITY) {
2100 /* It's an HTTP/1.0 CGI. Be afraid. */
2101 if(expect_body && content_range.from < 0 && content_range.to < 0)
2102 supersede = 1;
2105 if(!supersede && object->length >= 0 && full_len >= 0 &&
2106 object->length != full_len) {
2107 do_log(L_WARN, "Inconsistent length.\n");
2108 supersede = 1;
2111 if(!supersede &&
2112 ((object->last_modified >= 0 && last_modified >= 0) ||
2113 (object->etag && etag))) {
2114 if(request->method == METHOD_CONDITIONAL_GET) {
2115 do_log(L_WARN, "Server ignored conditional request.\n");
2116 connection->server->lies += 10;
2117 /* Drop the connection? */
2120 } else if(code == 416) {
2121 do_log(L_ERROR, "Unexpected \"range not satisfiable\" reply\n");
2122 httpServerAbort(connection, 1, 502,
2123 internAtom("Unexpected \"range not satisfiable\" "
2124 "reply"));
2125 /* The object may be superseded. Make sure the next request
2126 won't be partial. */
2127 abortObject(object, 502,
2128 internAtom("Unexpected \"range not satisfiable\" reply"));
2129 return 1;
2132 if(object->flags & OBJECT_INITIAL)
2133 supersede = 0;
2135 if(supersede) {
2136 do_log(L_SUPERSEDED,
2137 "Superseding object %s (%d %d %d %s -> %d %d %d %s)\n",
2138 scrub(old_object->key),
2139 object->code, object->length, (int)object->last_modified,
2140 object->etag ? object->etag : "(none)",
2141 code, full_len, (int)last_modified,
2142 etag ? etag : "(none)");
2143 privatiseObject(old_object, 0);
2144 new_object = makeObject(object->type, object->key,
2145 object->key_size, 1, 0,
2146 object->request, NULL);
2147 if(new_object == NULL) {
2148 do_log(L_ERROR, "Couldn't allocate object\n");
2149 httpServerAbort(connection, 1, 500,
2150 internAtom("Couldn't allocate object"));
2151 return 1;
2153 if(urlIsLocal(new_object->key, new_object->key_size))
2154 new_object->flags |= OBJECT_LOCAL;
2155 } else {
2156 new_object = object;
2159 suspectDynamic =
2160 (!etag && last_modified < 0) ||
2161 (cache_control.flags &
2162 (CACHE_NO_HIDDEN | CACHE_NO | CACHE_NO_STORE |
2163 (cacheIsShared ? CACHE_PRIVATE : 0))) ||
2164 (cache_control.max_age >= 0 && cache_control.max_age <= 2) ||
2165 (cacheIsShared &&
2166 cache_control.s_maxage >= 0 && cache_control.s_maxage <= 5) ||
2167 (old_object->last_modified >= 0 && old_object->expires >= 0 &&
2168 (old_object->expires - old_object->last_modified <= 1)) ||
2169 (supersede && (old_object->date - date <= 5));
2171 if(suspectDynamic)
2172 new_object->flags |= OBJECT_DYNAMIC;
2173 else if(!supersede)
2174 new_object->flags &= ~OBJECT_DYNAMIC;
2175 else if(old_object->flags & OBJECT_DYNAMIC)
2176 new_object->flags |= OBJECT_DYNAMIC;
2178 new_object->age = age;
2179 new_object->cache_control |= cache_control.flags;
2180 new_object->max_age = cache_control.max_age;
2181 new_object->s_maxage = cache_control.s_maxage;
2182 new_object->flags &= ~OBJECT_FAILED;
2184 if(date >= 0)
2185 new_object->date = date;
2186 if(last_modified >= 0)
2187 new_object->last_modified = last_modified;
2188 if(expires >= 0)
2189 new_object->expires = expires;
2190 if(new_object->etag == NULL)
2191 new_object->etag = etag;
2192 else
2193 free(etag);
2195 switch(code) {
2196 case 200:
2197 case 300: case 301: case 302: case 303: case 307:
2198 case 403: case 404: case 405: case 401:
2199 if(new_object->message) releaseAtom(new_object->message);
2200 new_object->code = code;
2201 new_object->message = message;
2202 break;
2203 case 206: case 304: case 412:
2204 if(new_object->code != 200 || !new_object->message) {
2205 if(new_object->message) releaseAtom(new_object->message);
2206 new_object->code = 200;
2207 new_object->message = internAtom("OK");
2209 releaseAtom(message);
2210 break;
2211 default:
2212 if(new_object->message) releaseAtom(new_object->message);
2213 new_object->code = code;
2214 new_object->message = retainAtom(message);
2215 break;
2218 httpTweakCachability(new_object);
2220 if(!via)
2221 new_via = internAtomF("%s %s",
2222 version == HTTP_11 ? "1.1" : "1.0",
2223 proxyName->string);
2224 else
2225 new_via = internAtomF("%s, %s %s", via->string,
2226 version == HTTP_11 ? "1.1" : "1.0",
2227 proxyName->string);
2228 if(new_via == NULL) {
2229 do_log(L_ERROR, "Couldn't allocate Via.\n");
2230 } else {
2231 if(new_object->via) releaseAtom(new_object->via);
2232 new_object->via = new_via;
2235 if(new_object->flags & OBJECT_INITIAL) {
2236 objectPartial(new_object, full_len, headers);
2237 } else {
2238 if(new_object->length < 0)
2239 new_object->length = full_len;
2240 /* XXX -- RFC 2616 13.5.3 */
2241 releaseAtom(headers);
2244 if(supersede) {
2245 assert(new_object != old_object);
2246 supersedeObject(old_object);
2249 if(new_object != old_object) {
2250 if(new_object->flags & OBJECT_INPROGRESS) {
2251 /* Make sure we don't fetch this object two times at the
2252 same time. Just drop the connection. */
2253 releaseObject(new_object);
2254 httpServerFinish(connection, 1, 0);
2255 return 1;
2257 old_object->flags &= ~OBJECT_VALIDATING;
2258 new_object->flags |= OBJECT_INPROGRESS;
2259 /* Signal the client side to switch to the new object -- see
2260 httpClientGetHandler. If it doesn't, we'll give up on this
2261 request below. */
2262 new_object->flags |= OBJECT_MUTATING;
2263 request->can_mutate = new_object;
2264 notifyObject(old_object);
2265 request->can_mutate = NULL;
2266 new_object->flags &= ~OBJECT_MUTATING;
2267 old_object->flags &= ~OBJECT_INPROGRESS;
2268 if(request->object == old_object) {
2269 if(request->request)
2270 request->request->request = NULL;
2271 request->request = NULL;
2272 request->object = new_object;
2273 } else {
2274 assert(request->object == new_object);
2276 releaseNotifyObject(old_object);
2277 old_object = NULL;
2278 object = new_object;
2279 } else {
2280 objectMetadataChanged(new_object, 0);
2283 if(object->flags & OBJECT_VALIDATING) {
2284 object->flags &= ~OBJECT_VALIDATING;
2285 notifyObject(object);
2288 if(!expect_body) {
2289 httpServerFinish(connection, 0, rc);
2290 return 1;
2293 if(request->request == NULL) {
2294 httpServerFinish(connection, 1, 0);
2295 return 1;
2298 if(code == 412) {
2299 /* 412 replies contain a useless body. For now, we
2300 drop the connection. */
2301 httpServerFinish(connection, 1, 0);
2302 return 1;
2306 if(request->flags & REQUEST_PERSISTENT) {
2307 if(request->method != METHOD_HEAD &&
2308 connection->te == TE_IDENTITY && len < 0) {
2309 do_log(L_ERROR, "Persistent reply with no Content-Length\n");
2310 /* That's potentially dangerous, as we could start reading
2311 arbitrary data into the object. Unfortunately, some
2312 servers do that. */
2313 request->flags &= ~REQUEST_PERSISTENT;
2317 /* we're getting a body */
2318 if(content_range.from > 0)
2319 connection->offset = content_range.from;
2320 else
2321 connection->offset = 0;
2323 if(content_range.to >= 0)
2324 request->to = content_range.to;
2326 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2327 (unsigned long)connection, (unsigned long)object,
2328 connection->offset);
2330 if(connection->len > rc) {
2331 rc = connectionAddData(connection, rc);
2332 if(rc) {
2333 if(rc < 0) {
2334 if(rc == -2) {
2335 do_log(L_ERROR, "Couldn't parse chunk size.\n");
2336 httpServerAbort(connection, 1, 502,
2337 internAtom("Couldn't parse chunk size"));
2338 } else {
2339 do_log(L_ERROR, "Couldn't add data to connection.\n");
2340 httpServerAbort(connection, 1, 500,
2341 internAtom("Couldn't add data "
2342 "to connection"));
2344 return 1;
2345 } else {
2346 if(code != 206) {
2347 if(object->length < 0) {
2348 object->length = object->size;
2349 objectMetadataChanged(object, 0);
2350 } else if(object->length != object->size) {
2351 httpServerAbort(connection, 1, 500,
2352 internAtom("Inconsistent "
2353 "object size"));
2354 object->length = -1;
2355 return 1;
2358 httpServerFinish(connection, 0, 0);
2359 return 1;
2362 } else {
2363 connection->len = 0;
2366 if(eof) {
2367 if(connection->te == TE_CHUNKED ||
2368 (object->length >= 0 &&
2369 connection->offset < object->length)) {
2370 do_log(L_ERROR, "Server closed connection.\n");
2371 httpServerAbort(connection, 1, 502,
2372 internAtom("Server closed connection"));
2373 return 1;
2374 } else {
2375 if(code != 206 && eof > 0 && object->length < 0) {
2376 object->length = object->size;
2377 objectMetadataChanged(object, 0);
2379 httpServerFinish(connection, 1, 0);
2380 return 1;
2382 } else {
2383 return httpServerReadData(connection, 1);
2385 return 0;
2387 fail:
2388 releaseAtom(url);
2389 releaseAtom(message);
2390 if(headers)
2391 releaseAtom(headers);
2392 if(etag)
2393 free(etag);
2394 if(via)
2395 releaseAtom(via);
2396 return 1;
2400 httpServerIndirectHandlerCommon(HTTPConnectionPtr connection, int eof)
2402 HTTPRequestPtr request = connection->request;
2404 assert(eof >= 0);
2405 assert(request->object->flags & OBJECT_INPROGRESS);
2407 if(connection->len > 0) {
2408 int rc;
2409 rc = connectionAddData(connection, 0);
2410 if(rc) {
2411 if(rc < 0) {
2412 if(rc == -2) {
2413 do_log(L_ERROR, "Couldn't parse chunk size.\n");
2414 httpServerAbort(connection, 1, 502,
2415 internAtom("Couldn't parse chunk size"));
2416 } else {
2417 do_log(L_ERROR, "Couldn't add data to connection.\n");
2418 httpServerAbort(connection, 1, 500,
2419 internAtom("Couldn't add data "
2420 "to connection"));
2422 return 1;
2423 } else {
2424 if(request->to < 0) {
2425 if(request->object->length < 0) {
2426 request->object->length = request->object->size;
2427 objectMetadataChanged(request->object, 0);
2428 } else if(request->object->length !=
2429 request->object->size) {
2430 request->object->length = -1;
2431 httpServerAbort(connection, 1, 502,
2432 internAtom("Inconsistent "
2433 "object size"));
2434 return 1;
2437 httpServerFinish(connection, 0, 0);
2439 return 1;
2443 if(eof && connection->len == 0) {
2444 if(connection->te == TE_CHUNKED ||
2445 (request->to >= 0 && connection->offset < request->to)) {
2446 do_log(L_ERROR, "Server dropped connection.\n");
2447 httpServerAbort(connection, 1, 502,
2448 internAtom("Server dropped connection"));
2449 return 1;
2450 } else {
2451 if(request->object->length < 0 && eof > 0 &&
2452 (request->to < 0 || request->to > request->object->size)) {
2453 request->object->length = request->object->size;
2454 objectMetadataChanged(request->object, 0);
2456 httpServerFinish(connection, 1, 0);
2457 return 1;
2459 } else {
2460 return httpServerReadData(connection, 0);
2465 httpServerIndirectHandler(int status,
2466 FdEventHandlerPtr event,
2467 StreamRequestPtr srequest)
2469 HTTPConnectionPtr connection = srequest->data;
2470 assert(connection->request->object->flags & OBJECT_INPROGRESS);
2472 httpSetTimeout(connection, -1);
2473 if(status < 0) {
2474 if(status != -ECLIENTRESET)
2475 do_log_error(L_ERROR, -status, "Read from server failed");
2476 httpServerAbort(connection, status != -ECLIENTRESET, 502,
2477 internAtomError(-status, "Read from server failed"));
2478 return 1;
2481 connection->len = srequest->offset;
2483 return httpServerIndirectHandlerCommon(connection, status);
2487 httpServerReadData(HTTPConnectionPtr connection, int immediate)
2489 HTTPRequestPtr request = connection->request;
2490 ObjectPtr object = request->object;
2491 int to = -1;
2493 assert(object->flags & OBJECT_INPROGRESS);
2495 if(request->request == NULL) {
2496 httpServerFinish(connection, 1, 0);
2497 return 1;
2500 if(request->to >= 0)
2501 to = request->to;
2502 else
2503 to = object->length;
2505 if(to >= 0 && to == connection->offset) {
2506 httpServerFinish(connection, 0, 0);
2507 return 1;
2510 if(connection->len == 0 &&
2511 ((connection->te == TE_IDENTITY && to > connection->offset) ||
2512 (connection->te == TE_CHUNKED && connection->chunk_remaining > 0))) {
2513 /* Read directly into the object */
2514 int i = connection->offset / CHUNK_SIZE;
2515 int j = connection->offset % CHUNK_SIZE;
2516 int end, len, more;
2517 /* See httpServerDirectHandlerCommon if you change this */
2518 if(connection->te == TE_CHUNKED) {
2519 len = connection->chunk_remaining;
2520 /* The logic here is that we want more to just fit the
2521 chunk header if we're doing a large read, but do a
2522 large read if we would otherwise do a small one. The
2523 magic constant 2000 comes from the assumption that the
2524 server uses chunks that have a size that are a power of
2525 two (possibly including the chunk header), and that we
2526 want a full ethernet packet to fit into our read. */
2527 more = (len >= 2000 ? 20 : MIN(2048 - len, CHUNK_SIZE));
2528 } else {
2529 len = to - connection->offset;
2530 /* We read more data only when there is a reasonable
2531 chance of there being another reply coming. */
2532 more = (connection->pipelined > 1) ? CHUNK_SIZE : 0;
2534 end = len + connection->offset;
2536 httpConnectionDestroyBuf(connection);
2538 /* The order of allocation is important in case we run out of
2539 memory. */
2540 lockChunk(object, i);
2541 if(object->chunks[i].data == NULL)
2542 object->chunks[i].data = get_chunk();
2543 if(object->chunks[i].data && object->chunks[i].size >= j) {
2544 if(len + j > CHUNK_SIZE) {
2545 lockChunk(object, i + 1);
2546 if(object->chunks[i + 1].data == NULL)
2547 object->chunks[i + 1].data = get_chunk();
2548 /* Unless we're grabbing all len of data, we do not
2549 want to do an indirect read immediately afterwards. */
2550 if(more && len + j <= 2 * CHUNK_SIZE) {
2551 if(!connection->buf)
2552 connection->buf = get_chunk(); /* checked below */
2554 if(object->chunks[i + 1].data) {
2555 do_stream_3(IO_READ | IO_NOTNOW, connection->fd, j,
2556 object->chunks[i].data, CHUNK_SIZE,
2557 object->chunks[i + 1].data,
2558 MIN(CHUNK_SIZE,
2559 end - (i + 1) * CHUNK_SIZE),
2560 connection->buf, connection->buf ? more : 0,
2561 httpServerDirectHandler2, connection);
2562 return 1;
2564 unlockChunk(object, i + 1);
2566 if(more && len + j <= CHUNK_SIZE) {
2567 if(!connection->buf)
2568 connection->buf = get_chunk();
2570 do_stream_2(IO_READ | IO_NOTNOW, connection->fd, j,
2571 object->chunks[i].data,
2572 MIN(CHUNK_SIZE, end - i * CHUNK_SIZE),
2573 connection->buf, connection->buf ? more : 0,
2574 httpServerDirectHandler, connection);
2575 return 1;
2576 } else {
2577 unlockChunk(object, i);
2581 if(connection->len == 0)
2582 httpConnectionDestroyBuf(connection);
2584 httpSetTimeout(connection, serverTimeout);
2585 do_stream_buf(IO_READ | IO_NOTNOW |
2586 ((immediate && connection->len) ? IO_IMMEDIATE : 0),
2587 connection->fd, connection->len,
2588 &connection->buf,
2589 (connection->te == TE_CHUNKED ?
2590 MIN(2048, CHUNK_SIZE) : CHUNK_SIZE),
2591 httpServerIndirectHandler, connection);
2592 return 1;
2596 httpServerDirectHandlerCommon(int kind, int status,
2597 FdEventHandlerPtr event,
2598 StreamRequestPtr srequest)
2600 HTTPConnectionPtr connection = srequest->data;
2601 HTTPRequestPtr request = connection->request;
2602 ObjectPtr object = request->object;
2603 int i = connection->offset / CHUNK_SIZE;
2604 int to, end, end1;
2606 assert(request->object->flags & OBJECT_INPROGRESS);
2608 httpSetTimeout(connection, -1);
2610 if(status < 0) {
2611 unlockChunk(object, i);
2612 if(kind == 2) unlockChunk(object, i + 1);
2613 if(status != -ECLIENTRESET)
2614 do_log_error(L_ERROR, -status, "Read from server failed");
2615 httpServerAbort(connection, status != -ECLIENTRESET, 502,
2616 internAtomError(-status, "Read from server failed"));
2617 return 1;
2620 /* We have incestuous knowledge of the decisions made in
2621 httpServerReadData */
2622 if(request->to >= 0)
2623 to = request->to;
2624 else
2625 to = object->length;
2626 if(connection->te == TE_CHUNKED)
2627 end = connection->offset + connection->chunk_remaining;
2628 else
2629 end = to;
2630 /* The amount of data actually read into the object */
2631 end1 = MIN(end, i * CHUNK_SIZE + MIN(kind * CHUNK_SIZE, srequest->offset));
2633 assert(end >= 0);
2634 assert(end1 >= i * CHUNK_SIZE);
2635 assert(end1 - 2 * CHUNK_SIZE <= i * CHUNK_SIZE);
2637 object->chunks[i].size =
2638 MAX(object->chunks[i].size, MIN(end1 - i * CHUNK_SIZE, CHUNK_SIZE));
2639 if(kind == 2 && end1 > (i + 1) * CHUNK_SIZE) {
2640 object->chunks[i + 1].size =
2641 MAX(object->chunks[i + 1].size, end1 - (i + 1) * CHUNK_SIZE);
2643 if(connection->te == TE_CHUNKED) {
2644 connection->chunk_remaining -= (end1 - connection->offset);
2645 assert(connection->chunk_remaining >= 0);
2647 connection->offset = end1;
2648 object->size = MAX(object->size, end1);
2649 unlockChunk(object, i);
2650 if(kind == 2) unlockChunk(object, i + 1);
2652 if(i * CHUNK_SIZE + srequest->offset > end1) {
2653 connection->len = i * CHUNK_SIZE + srequest->offset - end1;
2654 return httpServerIndirectHandlerCommon(connection, status);
2655 } else {
2656 notifyObject(object);
2657 if(status) {
2658 if(connection->te == TE_CHUNKED ||
2659 (end >= 0 && connection->offset < end)) {
2660 do_log(L_ERROR, "Server dropped connection.\n");
2661 httpServerAbort(connection, 1, 502,
2662 internAtom("Server dropped connection"));
2663 } else
2664 httpServerFinish(connection, 1, 0);
2665 return 1;
2666 } else {
2667 return httpServerReadData(connection, 0);
2673 httpServerDirectHandler(int status,
2674 FdEventHandlerPtr event,
2675 StreamRequestPtr srequest)
2677 return httpServerDirectHandlerCommon(1, status, event, srequest);
2681 httpServerDirectHandler2(int status,
2682 FdEventHandlerPtr event,
2683 StreamRequestPtr srequest)
2685 return httpServerDirectHandlerCommon(2, status, event, srequest);
2688 /* Add the data accumulated in connection->buf into the object in
2689 connection->request. Returns 0 in the normal case, 1 if the TE is
2690 self-terminating and we're done, -1 if there was a problem with
2691 objectAddData, -2 if there was a problem with the data. */
2693 connectionAddData(HTTPConnectionPtr connection, int skip)
2695 HTTPRequestPtr request = connection->request;
2696 ObjectPtr object = request->object;
2697 int rc;
2699 if(connection->te == TE_IDENTITY) {
2700 int len;
2702 len = connection->len - skip;
2703 if(object->length >= 0) {
2704 len = MIN(object->length - connection->offset, len);
2706 if(request->to >= 0)
2707 len = MIN(request->to - connection->offset, len);
2708 if(len > 0) {
2709 rc = objectAddData(object, connection->buf + skip,
2710 connection->offset, len);
2711 if(rc < 0)
2712 return -1;
2713 connection->offset += len;
2714 connection->len -= (len + skip);
2715 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2716 (unsigned long)connection, (unsigned long)object,
2717 connection->offset);
2720 if(connection->len > 0 && skip + len > 0) {
2721 memmove(connection->buf,
2722 connection->buf + skip + len, connection->len);
2725 if((object->length >= 0 && object->length <= connection->offset) ||
2726 (request->to >= 0 && request->to <= connection->offset)) {
2727 notifyObject(object);
2728 return 1;
2729 } else {
2730 if(len > 0)
2731 notifyObject(object);
2732 return 0;
2734 } else if(connection->te == TE_CHUNKED) {
2735 int i = skip, j, size;
2736 /* connection->chunk_remaining is 0 at the end of a chunk, -1
2737 after the CR/LF pair ending a chunk, and -2 after we've
2738 seen a chunk of length 0. */
2739 if(connection->chunk_remaining > -2) {
2740 while(1) {
2741 if(connection->chunk_remaining <= 0) {
2742 if(connection->chunk_remaining == 0) {
2743 if(connection->len < i + 2)
2744 break;
2745 if(connection->buf[i] != '\r' ||
2746 connection->buf[i + 1] != '\n')
2747 return -1;
2748 i += 2;
2749 connection->chunk_remaining = -1;
2751 if(connection->len < i + 2)
2752 break;
2753 j = parseChunkSize(connection->buf, i,
2754 connection->len, &size);
2755 if(j < 0)
2756 return -2;
2757 if(j == 0)
2758 break;
2759 else
2760 i = j;
2761 if(size == 0) {
2762 connection->chunk_remaining = -2;
2763 break;
2764 } else {
2765 connection->chunk_remaining = size;
2767 } else {
2768 /* connection->chunk_remaining > 0 */
2769 size = MIN(connection->chunk_remaining,
2770 connection->len - i);
2771 if(size <= 0)
2772 break;
2773 rc = objectAddData(object, connection->buf + i,
2774 connection->offset, size);
2775 connection->offset += size;
2776 if(rc < 0)
2777 return -1;
2778 i += size;
2779 connection->chunk_remaining -= size;
2780 do_log(D_SERVER_OFFSET, "0x%lx(0x%lx): offset = %d\n",
2781 (unsigned long)connection,
2782 (unsigned long)object,
2783 connection->offset);
2787 connection->len -= i;
2788 if(connection->len > 0)
2789 memmove(connection->buf, connection->buf + i, connection->len);
2790 if(i > 0 || connection->chunk_remaining == -2)
2791 notifyObject(object);
2792 if(connection->chunk_remaining == -2)
2793 return 1;
2794 else
2795 return 0;
2796 } else {
2797 abort();
2801 void
2802 listServers(FILE *out)
2804 HTTPServerPtr server;
2805 int i, n, m, entry;
2807 fprintf(out, "<!DOCTYPE HTML PUBLIC "
2808 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2809 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2810 "<html><head>\n"
2811 "\r\n<title>Known servers</title>\n"
2812 "</head><body>\n"
2813 "<h1>Known servers</h1>\n");
2815 alternatingHttpStyle(out, "servers");
2816 fprintf(out, "<table id=servers>\n");
2817 fprintf(out, "<thead><tr><th>Server</th>"
2818 "<th>Version</th>"
2819 "<th>Persistent</th>"
2820 "<th>Pipeline</th>"
2821 "<th>Connections</th>"
2822 "<th></th>"
2823 "<th>rtt</th>"
2824 "<th>rate</th>"
2825 "</tr></thead>\n");
2826 fprintf(out, "<tbody>\n");
2827 server = servers;
2828 entry = 0;
2829 while(server) {
2830 fprintf(out, "<tr class=\"%s\">", entry % 2 == 0 ? "even" : "odd");
2831 if(server->port == 80)
2832 fprintf(out, "<td>%s</td>", server->name);
2833 else
2834 fprintf(out, "<td>%s:%d</td>", server->name, server->port);
2836 if(server->version == HTTP_11)
2837 fprintf(out, "<td>1.1</td>");
2838 else if(server->version == HTTP_10)
2839 fprintf(out, "<td>1.0</td>");
2840 else
2841 fprintf(out, "<td>unknown</td>");
2843 if(server->persistent < 0)
2844 fprintf(out, "<td>no</td>");
2845 else if(server->persistent > 0)
2846 fprintf(out, "<td>yes</td>");
2847 else
2848 fprintf(out, "<td>unknown</td>");
2850 if(server->version != HTTP_11 || server->persistent <= 0)
2851 fprintf(out, "<td></td>");
2852 else if(server->pipeline < 0)
2853 fprintf(out, "<td>no</td>");
2854 else if(server->pipeline >= 0 && server->pipeline <= 1)
2855 fprintf(out, "<td>unknown</td>");
2856 else if(server->pipeline == 2 || server->pipeline == 3)
2857 fprintf(out, "<td>probing</td>");
2858 else
2859 fprintf(out, "<td>yes</td>");
2861 n = 0; m = 0;
2862 for(i = 0; i < server->maxslots; i++)
2863 if(server->connection[i] && !server->connection[i]->connecting) {
2864 if(i < server->numslots)
2865 n++;
2866 else
2867 m++;
2870 fprintf(out, "<td>%d/%d", n, server->numslots);
2871 if(m)
2872 fprintf(out, " + %d</td>", m);
2873 else
2874 fprintf(out, "</td>");
2876 if(server->lies > 0)
2877 fprintf(out, "<td>(%d lies)</td>", (server->lies + 9) / 10);
2878 else
2879 fprintf(out, "<td></td>");
2881 if(server->rtt > 0)
2882 fprintf(out, "<td>%.3f</td>", (double)server->rtt / 1000000.0);
2883 else
2884 fprintf(out, "<td></td>");
2885 if(server->rate > 0)
2886 fprintf(out, "<td>%d</td>", server->rate);
2887 else
2888 fprintf(out, "<td></td>");
2890 fprintf(out, "</tr>\n");
2891 server = server->next;
2892 entry++;
2894 fprintf(out, "</tbody>\n");
2895 fprintf(out, "</table>\n");
2896 fprintf(out, "<p><a href=\"/polipo/\">back</a></p>");
2897 fprintf(out, "</body></html>\n");