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
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
;
34 int pmmFirstSize
= 0, pmmSize
= 0;
37 int serverMaxSlots
= 8;
38 int dontCacheRedirects
= 0;
39 int maxSideBuffering
= 1500;
40 int maxConnectionAge
= 1260;
41 int maxConnectionRequests
= 400;
43 static HTTPServerPtr servers
= 0;
45 static int httpServerContinueConditionHandler(int, ConditionHandlerPtr
);
46 static int initParentProxy(void);
47 static int parentProxySetter(ConfigVariablePtr var
, void *value
);
48 static void httpServerDelayedFinish(HTTPConnectionPtr
);
49 static int allowUnalignedRangeRequests
= 0;
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
,
66 "Pipeline requests on an active connection.");
67 CONFIG_VARIABLE_SETTABLE(maxPipelineTrain
, CONFIG_INT
,
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.");
98 parentProxySetter(ConfigVariablePtr var
, void *value
)
100 configAtomSetter(var
, value
);
106 discardServer(HTTPServerPtr server
)
108 HTTPServerPtr previous
;
109 assert(!server
->request
);
111 if(server
== servers
)
112 servers
= server
->next
;
115 while(previous
->next
!= server
)
116 previous
= previous
->next
;
117 previous
->next
= server
->next
;
124 httpServerIdle(HTTPServerPtr server
)
129 for(i
= 0; i
< server
->maxslots
; i
++)
130 if(server
->connection
[i
])
136 expireServersHandler(TimeEventHandlerPtr event
)
138 HTTPServerPtr server
, next
;
139 TimeEventHandlerPtr e
;
143 if(httpServerIdle(server
) &&
144 server
->time
+ serverExpireTime
< current_time
.tv_sec
)
145 discardServer(server
);
148 e
= scheduleTimeEvent(serverExpireTime
/ 60 + 60,
149 expireServersHandler
, 0, NULL
);
151 do_log(L_ERROR
, "Couldn't schedule server expiry.\n");
160 return (size
+ CHUNK_SIZE
- 1) / CHUNK_SIZE
* CHUNK_SIZE
;
166 AtomPtr host
, port_atom
;
170 releaseAtom(parentHost
);
176 if(parentProxy
!= NULL
&& parentProxy
->length
== 0) {
177 releaseAtom(parentProxy
);
181 if(parentProxy
== NULL
)
184 rc
= atomSplit(parentProxy
, ':', &host
, &port_atom
);
186 do_log(L_ERROR
, "Couldn't parse parentProxy.");
187 releaseAtom(parentProxy
);
192 port
= atoi(port_atom
->string
);
193 if(port
<= 0 || port
>= 0x10000) {
195 releaseAtom(port_atom
);
196 do_log(L_ERROR
, "Couldn't parse parentProxy.");
197 releaseAtom(parentProxy
);
210 TimeEventHandlerPtr event
;
213 if(pmmFirstSize
|| pmmSize
) {
214 if(pmmSize
== 0) pmmSize
= pmmFirstSize
;
215 if(pmmFirstSize
== 0) pmmFirstSize
= pmmSize
;
216 pmmSize
= roundSize(pmmSize
);
217 pmmFirstSize
= roundSize(pmmFirstSize
);
220 if(serverMaxSlots
< 1)
224 if(serverSlots
> serverMaxSlots
)
225 serverSlots
= serverMaxSlots
;
226 if(serverSlots1
< serverSlots
)
227 serverSlots1
= serverSlots
;
228 if(serverSlots1
> serverMaxSlots
)
229 serverSlots1
= serverMaxSlots
;
233 event
= scheduleTimeEvent(serverExpireTime
/ 60 + 60, expireServersHandler
,
236 do_log(L_ERROR
, "Couldn't schedule server expiry.\n");
242 getServer(char *name
, int port
, int proxy
)
244 HTTPServerPtr server
;
249 if(strcmp(server
->name
, name
) == 0 && server
->port
== port
&&
250 server
->isProxy
== proxy
) {
251 if(httpServerIdle(server
) &&
252 server
->time
+ serverExpireTime
< current_time
.tv_sec
) {
253 discardServer(server
);
257 server
->time
= current_time
.tv_sec
;
261 server
= server
->next
;
264 server
= malloc(sizeof(HTTPServerRec
));
266 do_log(L_ERROR
, "Couldn't allocate server.\n");
270 server
->connection
= malloc(serverMaxSlots
* sizeof(HTTPConnectionPtr
));
271 if(server
->connection
== NULL
) {
272 do_log(L_ERROR
, "Couldn't allocate server.\n");
277 server
->idleHandler
= malloc(serverMaxSlots
* sizeof(FdEventHandlerPtr
));
278 if(server
->connection
== NULL
) {
279 do_log(L_ERROR
, "Couldn't allocate server.\n");
280 free(server
->connection
);
285 server
->maxslots
= serverMaxSlots
;
287 server
->name
= strdup(name
);
288 if(server
->name
== NULL
) {
289 do_log(L_ERROR
, "Couldn't allocate server name.\n");
295 server
->addrindex
= 0;
296 server
->isProxy
= proxy
;
297 server
->version
= HTTP_UNKNOWN
;
298 server
->persistent
= 0;
299 server
->pipeline
= 0;
300 server
->time
= current_time
.tv_sec
;
303 server
->numslots
= MIN(serverSlots
, server
->maxslots
);
304 for(i
= 0; i
< server
->maxslots
; i
++) {
305 server
->connection
[i
] = NULL
;
306 server
->idleHandler
[i
] = NULL
;
308 server
->request
= NULL
;
309 server
->request_last
= NULL
;
312 server
->next
= servers
;
318 httpServerQueueRequest(HTTPServerPtr server
, HTTPRequestPtr request
)
320 assert(request
->request
&& request
->request
->request
== request
);
321 assert(request
->connection
== NULL
);
322 if(server
->request
) {
323 server
->request_last
->next
= request
;
324 server
->request_last
= request
;
326 server
->request_last
= request
;
327 server
->request
= request
;
333 httpServerAbort(HTTPConnectionPtr connection
, int fail
,
334 int code
, AtomPtr message
)
336 HTTPRequestPtr request
= connection
->request
;
338 if(request
->request
) {
339 httpClientError(request
->request
, code
, retainAtom(message
));
342 request
->object
->flags
|= OBJECT_FAILED
;
343 if(request
->object
->flags
& OBJECT_INITIAL
)
344 abortObject(request
->object
, code
, retainAtom(message
));
345 notifyObject(request
->object
);
348 releaseAtom(message
);
349 if(!connection
->connecting
)
350 httpServerFinish(connection
, 1, 0);
354 httpServerAbortRequest(HTTPRequestPtr request
, int fail
,
355 int code
, AtomPtr message
)
357 if(request
->connection
&& request
== request
->connection
->request
) {
358 httpServerAbort(request
->connection
, fail
, code
, message
);
360 HTTPRequestPtr requestor
= request
->request
;
362 requestor
->request
= NULL
;
363 request
->request
= NULL
;
364 httpClientError(requestor
, code
, retainAtom(message
));
367 request
->object
->flags
|= OBJECT_FAILED
;
368 if(request
->object
->flags
& OBJECT_INITIAL
)
369 abortObject(request
->object
, code
, retainAtom(message
));
370 notifyObject(request
->object
);
372 releaseAtom(message
);
377 httpServerClientReset(HTTPRequestPtr request
)
379 if(request
->connection
&&
380 request
->connection
->fd
>= 0 &&
381 !request
->connection
->connecting
&&
382 request
->connection
->request
== request
)
383 pokeFdEvent(request
->connection
->fd
, -ECLIENTRESET
, POLLIN
| POLLOUT
);
388 httpMakeServerRequest(char *name
, int port
, ObjectPtr object
,
389 int method
, int from
, int to
, HTTPRequestPtr requestor
)
391 HTTPServerPtr server
;
392 HTTPRequestPtr request
;
395 assert(!(object
->flags
& OBJECT_INPROGRESS
));
398 server
= getServer(parentHost
->string
, parentPort
, 1);
400 server
= getServer(name
, port
, 0);
402 if(server
== NULL
) return -1;
404 object
->flags
|= OBJECT_INPROGRESS
;
405 object
->requestor
= requestor
;
407 request
= httpMakeRequest();
409 do_log(L_ERROR
, "Couldn't allocate request.\n");
413 /* Because we allocate objects in chunks, we cannot have data that
414 doesn't start at a chunk boundary. */
415 if(from
% CHUNK_SIZE
!= 0) {
416 if(allowUnalignedRangeRequests
) {
417 objectFillFromDisk(object
, from
/ CHUNK_SIZE
* CHUNK_SIZE
, 1);
418 if(objectHoleSize(object
, from
- 1) != 0)
419 from
= from
/ CHUNK_SIZE
* CHUNK_SIZE
;
421 from
= from
/ CHUNK_SIZE
* CHUNK_SIZE
;
425 request
->object
= retainObject(object
);
426 request
->method
= method
;
427 if(method
== METHOD_CONDITIONAL_GET
) {
429 request
->method
= METHOD_HEAD
;
433 (expectContinue
? (requestor
->flags
& REQUEST_WAIT_CONTINUE
) : 0);
434 request
->from
= from
;
436 request
->request
= requestor
;
437 requestor
->request
= request
;
438 request
->cache_control
= requestor
->cache_control
;
439 request
->time0
= null_time
;
440 request
->time1
= null_time
;
442 rc
= httpServerQueueRequest(server
, request
);
444 do_log(L_ERROR
, "Couldn't queue request.\n");
445 request
->request
= NULL
;
446 requestor
->request
= NULL
;
447 object
->flags
&= ~(OBJECT_INPROGRESS
| OBJECT_VALIDATING
);
448 releaseNotifyObject(object
);
449 httpDestroyRequest(request
);
453 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
454 if(server
->version
== HTTP_10
) {
455 httpServerAbortRequest(request
, 1,
456 417, internAtom("Expectation failed"));
459 } else if(expectContinue
>= 2 && server
->version
== HTTP_11
) {
460 if(request
->method
== METHOD_POST
|| request
->method
== METHOD_PUT
)
461 request
->flags
|= REQUEST_WAIT_CONTINUE
;
465 rc
= httpServerTrigger(server
);
467 /* We must be very short on memory. If there are any requests
468 queued, we abort one and try again. If there aren't, we
470 do_log(L_ERROR
, "Couldn't trigger server -- out of memory?\n");
471 if(server
->request
) {
472 httpServerAbortRequest(server
->request
, 1, 503,
473 internAtom("Couldn't trigger server"));
481 httpServerConnection(HTTPServerPtr server
)
483 HTTPConnectionPtr connection
;
486 connection
= httpMakeConnection();
487 if(connection
== NULL
) {
488 do_log(L_ERROR
, "Couldn't allocate server connection.\n");
491 connection
->server
= server
;
493 for(i
= 0; i
< server
->numslots
; i
++) {
494 if(!server
->connection
[i
]) {
495 server
->connection
[i
] = connection
;
499 assert(i
< server
->numslots
);
501 connection
->request
= NULL
;
502 connection
->request_last
= NULL
;
504 do_log(D_SERVER_CONN
, "C... %s:%d.\n",
505 connection
->server
->name
, connection
->server
->port
);
506 httpSetTimeout(connection
, serverTimeout
);
507 if(socksParentProxy
) {
508 connection
->connecting
= CONNECTING_SOCKS
;
509 do_socks_connect(server
->name
, connection
->server
->port
,
510 httpServerSocksHandler
, connection
);
512 connection
->connecting
= CONNECTING_DNS
;
513 do_gethostbyname(server
->name
, 0,
514 httpServerConnectionDnsHandler
,
521 httpServerConnectionDnsHandler(int status
, GethostbynameRequestPtr request
)
523 HTTPConnectionPtr connection
= request
->data
;
525 httpSetTimeout(connection
, -1);
529 message
= internAtomF("Host %s lookup failed: %s",
531 request
->name
->string
: "(unknown)",
532 request
->error_message
?
533 request
->error_message
->string
:
535 do_log(L_ERROR
, "Host %s lookup failed: %s (%d).\n",
537 request
->name
->string
: "(unknown)",
538 request
->error_message
?
539 request
->error_message
->string
:
540 pstrerror(-status
), -status
);
541 connection
->connecting
= 0;
542 if(connection
->server
->request
)
543 httpServerAbortRequest(connection
->server
->request
, 1, 504,
544 retainAtom(message
));
545 httpServerAbort(connection
, 1, 502, message
);
549 if(request
->addr
->string
[0] == DNS_CNAME
) {
550 if(request
->count
> 10) {
551 AtomPtr message
= internAtom("DNS CNAME loop");
552 do_log(L_ERROR
, "DNS CNAME loop.\n");
553 connection
->connecting
= 0;
554 if(connection
->server
->request
)
555 httpServerAbortRequest(connection
->server
->request
, 1, 504,
556 retainAtom(message
));
557 httpServerAbort(connection
, 1, 504, message
);
561 httpSetTimeout(connection
, serverTimeout
);
562 do_gethostbyname(request
->addr
->string
+ 1, request
->count
+ 1,
563 httpServerConnectionDnsHandler
,
568 connection
->connecting
= CONNECTING_CONNECT
;
569 httpSetTimeout(connection
, serverTimeout
);
570 do_connect(retainAtom(request
->addr
), connection
->server
->addrindex
,
571 connection
->server
->port
,
572 httpServerConnectionHandler
, connection
);
577 httpServerConnectionHandler(int status
,
578 FdEventHandlerPtr event
,
579 ConnectRequestPtr request
)
581 HTTPConnectionPtr connection
= request
->data
;
583 assert(connection
->fd
< 0);
584 if(request
->fd
>= 0) {
586 connection
->fd
= request
->fd
;
587 connection
->server
->addrindex
= request
->index
;
588 rc
= setNodelay(connection
->fd
, 1);
590 do_log_error(L_WARN
, errno
, "Couldn't disable Nagle's algorithm");
593 return httpServerConnectionHandlerCommon(status
, connection
);
597 httpServerSocksHandler(int status
, SocksRequestPtr request
)
599 HTTPConnectionPtr connection
= request
->data
;
601 assert(connection
->fd
< 0);
602 if(request
->fd
>= 0) {
603 connection
->fd
= request
->fd
;
604 connection
->server
->addrindex
= 0;
606 return httpServerConnectionHandlerCommon(status
, connection
);
610 httpServerConnectionHandlerCommon(int status
, HTTPConnectionPtr connection
)
612 httpSetTimeout(connection
, -1);
616 internAtomError(-status
, "Connect to %s:%d failed",
617 connection
->server
->name
,
618 connection
->server
->port
);
619 if(status
!= -ECLIENTRESET
)
620 do_log_error(L_ERROR
, -status
, "Connect to %s:%d failed",
621 connection
->server
->name
, connection
->server
->port
);
622 connection
->connecting
= 0;
623 if(connection
->server
->request
)
624 httpServerAbortRequest(connection
->server
->request
,
625 status
!= -ECLIENTRESET
, 504,
626 retainAtom(message
));
627 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 504, message
);
631 do_log(D_SERVER_CONN
, "C %s:%d.\n",
632 connection
->server
->name
, connection
->server
->port
);
634 connection
->connecting
= 0;
635 /* serverTrigger will take care of inserting any timeouts */
636 httpServerTrigger(connection
->server
);
641 httpServerIdleHandler(int a
, FdEventHandlerPtr event
)
643 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
644 HTTPServerPtr server
= connection
->server
;
647 assert(!connection
->request
);
649 do_log(D_SERVER_CONN
, "Idle connection to %s:%d died.\n",
650 connection
->server
->name
, connection
->server
->port
);
652 for(i
= 0; i
< server
->maxslots
; i
++) {
653 if(connection
== server
->connection
[i
]) {
654 server
->idleHandler
[i
] = NULL
;
658 assert(i
< server
->maxslots
);
660 httpServerAbort(connection
, 1, 504, internAtom("Timeout"));
664 /* Discard aborted requests at the head of the queue. */
666 httpServerDiscardRequests(HTTPServerPtr server
)
668 HTTPRequestPtr request
;
669 while(server
->request
&& !server
->request
->request
) {
670 request
= server
->request
;
671 server
->request
= request
->next
;
672 request
->next
= NULL
;
673 if(server
->request
== NULL
)
674 server
->request_last
= NULL
;
675 request
->object
->flags
&= ~(OBJECT_INPROGRESS
| OBJECT_VALIDATING
);
676 releaseNotifyObject(request
->object
);
677 request
->object
= NULL
;
678 httpDestroyRequest(request
);
683 pipelineIsSmall(HTTPConnectionPtr connection
)
685 HTTPRequestPtr request
= connection
->request
;
687 if(pipelineAdditionalRequests
<= 0)
689 else if(pipelineAdditionalRequests
>= 2)
694 if(request
->next
|| !(request
->flags
& REQUEST_PERSISTENT
))
696 if(request
->method
== METHOD_HEAD
||
697 request
->method
== METHOD_CONDITIONAL_GET
)
699 if(request
->to
>= 0 && connection
->server
->rate
> 0 &&
700 request
->to
- request
->from
< connection
->server
->rate
*
707 numRequests(HTTPServerPtr server
)
710 HTTPRequestPtr request
= server
->request
;
713 request
= request
->next
;
719 httpServerGetConnection(HTTPServerPtr server
, int *idle_return
)
722 int connecting
= 0, empty
= 0, idle
= 0;
725 /* Try to find an idle connection */
726 for(i
= 0; i
< server
->numslots
; i
++) {
727 if(server
->connection
[i
]) {
728 if(!server
->connection
[i
]->connecting
) {
729 if(!server
->connection
[i
]->request
) {
730 if(server
->idleHandler
[i
])
731 unregisterFdEvent(server
->idleHandler
[i
]);
732 server
->idleHandler
[i
] = NULL
;
744 return server
->connection
[j
];
747 /* If there's an empty slot, schedule connection creation */
749 /* Don't open a connection if there are already enough in
750 progress, except if the server doesn't do persistent
751 connections and there's only one in progress. */
752 if((connecting
== 0 || (server
->persistent
<= 0 && connecting
<= 1)) ||
753 connecting
< numRequests(server
)) {
754 httpServerConnection(server
);
758 /* Find a connection that can accept additional requests */
759 if(server
->version
== HTTP_11
&& server
->pipeline
>= 4) {
760 for(i
= 0; i
< serverSlots
; i
++) {
761 if(server
->connection
[i
] && !server
->connection
[i
]->connecting
&&
762 pipelineIsSmall(server
->connection
[i
])) {
763 if(server
->idleHandler
[i
])
764 unregisterFdEvent(server
->idleHandler
[i
]);
765 server
->idleHandler
[i
] = NULL
;
767 return server
->connection
[i
];
776 httpServerTrigger(HTTPServerPtr server
)
778 HTTPConnectionPtr connection
;
779 HTTPRequestPtr request
;
780 int idle
, n
, i
, rc
, numidle
;
782 while(server
->request
) {
783 httpServerDiscardRequests(server
);
788 if(REQUEST_SIDE(server
->request
)) {
789 rc
= httpServerSideRequest(server
);
790 /* If rc is 0, httpServerSideRequest didn't dequeue this
791 request. Go through the scheduling loop again, come
796 connection
= httpServerGetConnection(server
, &numidle
);
797 if(!connection
) break;
799 /* If server->pipeline <= 0, we don't do pipelining. If
800 server->pipeline is 1, then we are ready to start probing
801 for pipelining on the server; we then send exactly two
802 requests in what is hopefully a single packet to check
803 whether the server has the nasty habit of discarding its
804 input buffers after each request.
805 If server->pipeline is 2 or 3, the pipelining probe is in
806 progress on this server, and we don't pipeline anything
807 until it succeeds. When server->pipeline >= 4, pipelining
808 is believed to work on this server. */
809 if(server
->version
!= HTTP_11
|| server
->pipeline
<= 0 ||
810 server
->pipeline
== 2 || server
->pipeline
== 3) {
811 if(connection
->pipelined
== 0)
815 } else if(server
->pipeline
== 1) {
816 if(connection
->pipelined
== 0)
817 n
= MIN(2, maxPipelineTrain
);
821 n
= maxPipelineTrain
;
824 /* Don't pipeline if there are more idle connections */
828 idle
= !connection
->pipelined
;
830 while(server
->request
&& connection
->pipelined
< n
) {
831 httpServerDiscardRequests(server
);
832 if(!server
->request
) break;
833 request
= server
->request
;
834 assert(request
->request
->request
== request
);
835 rc
= httpWriteRequest(connection
, request
, -1);
838 httpServerAbortRequest(request
, rc
!= -ECLIENTRESET
, 503,
839 internAtom("Couldn't "
843 do_log(D_SERVER_CONN
, "W: ");
844 do_log_n(D_SERVER_CONN
,
845 request
->object
->key
, request
->object
->key_size
);
846 do_log(D_SERVER_CONN
, " (%d)\n", request
->method
);
847 if(connection
->pipelined
> 0)
848 request
->flags
|= REQUEST_PIPELINED
;
849 request
->time0
= current_time
;
851 server
->request
= request
->next
;
852 request
->next
= NULL
;
853 if(server
->request
== NULL
)
854 server
->request_last
= NULL
;
855 httpQueueRequest(connection
, request
);
856 connection
->pipelined
++;
858 if(server
->persistent
> 0 && server
->pipeline
== 1 && i
>= 2)
859 server
->pipeline
= 2;
861 if(i
> 0) httpServerSendRequest(connection
);
863 if(idle
&& connection
->pipelined
> 0)
864 httpServerReply(connection
, 0);
869 for(i
= 0; i
< server
->maxslots
; i
++) {
870 if(server
->connection
[i
] &&
871 !server
->connection
[i
]->connecting
&&
872 !server
->connection
[i
]->request
) {
873 /* Artificially age any fresh connections that aren't used
874 straight away; this is necessary for the logic for POST and
875 the logic that determines whether a given request should be
877 if(server
->connection
[i
]->serviced
== 0)
878 server
->connection
[i
]->serviced
= 1;
879 if(!server
->idleHandler
[i
])
880 server
->idleHandler
[i
] =
881 registerFdEvent(server
->connection
[i
]->fd
, POLLIN
,
882 httpServerIdleHandler
,
883 sizeof(HTTPConnectionPtr
),
884 &server
->connection
[i
]);
885 if(!server
->idleHandler
[i
]) {
886 do_log(L_ERROR
, "Couldn't register idle handler.\n");
887 httpServerFinish(server
->connection
[i
], 1, 0);
889 httpSetTimeout(server
->connection
[i
], serverIdleTimeout
);
897 httpServerSideRequest(HTTPServerPtr server
)
899 HTTPRequestPtr request
= server
->request
;
900 HTTPConnectionPtr connection
;
901 HTTPRequestPtr requestor
= request
->request
;
902 HTTPConnectionPtr client
= requestor
->connection
;
903 int rc
, i
, freeslots
, idle
, connecting
;
905 assert(REQUEST_SIDE(request
));
912 /* Find a fresh connection */
913 for(i
= 0; i
< server
->numslots
; i
++) {
914 if(!server
->connection
[i
])
916 else if(!server
->connection
[i
]->connecting
) {
917 if(!server
->connection
[i
]->request
) {
918 if(server
->connection
[i
]->serviced
== 0) {
919 if(server
->idleHandler
[i
])
920 unregisterFdEvent(server
->idleHandler
[i
]);
921 server
->idleHandler
[i
] = NULL
;
922 connection
= server
->connection
[i
];
934 /* Make sure that a fresh connection will be established at some
935 point, then wait until httpServerTrigger calls us again. */
937 httpServerConnection(server
);
940 /* Shutdown a random idle connection */
941 pokeFdEvent(server
->connection
[idle
]->fd
,
942 -EDOSHUTDOWN
, POLLIN
| POLLOUT
);
948 rc
= httpWriteRequest(connection
, request
, client
->bodylen
);
950 do_log(L_ERROR
, "Couldn't write POST or PUT request.\n");
951 httpServerAbortRequest(request
, rc
!= -ECLIENTRESET
, 503,
952 internAtom("Couldn't write request"));
955 server
->request
= request
->next
;
956 request
->next
= NULL
;
957 if(server
->request
== NULL
)
958 server
->request_last
= NULL
;
959 httpQueueRequest(connection
, request
);
960 connection
->pipelined
= 1;
961 request
->time0
= current_time
;
962 connection
->reqoffset
= 0;
963 connection
->bodylen
= client
->bodylen
;
964 httpServerDoSide(connection
);
969 httpServerDoSide(HTTPConnectionPtr connection
)
971 HTTPRequestPtr request
= connection
->request
;
972 HTTPRequestPtr requestor
= request
->request
;
973 HTTPConnectionPtr client
= requestor
->connection
;
974 int len
= MIN(client
->reqlen
- client
->reqbegin
,
975 connection
->bodylen
- connection
->reqoffset
);
978 (len
>= maxSideBuffering
||
979 client
->reqbegin
> 0 ||
980 (connection
->reqoffset
+ client
->reqlen
- client
->reqbegin
) >=
981 connection
->bodylen
);
982 int done
= connection
->reqoffset
>= connection
->bodylen
;
984 assert(connection
->bodylen
>= 0);
986 httpSetTimeout(connection
, 60);
988 if(connection
->reqlen
> 0) {
989 /* Send the headers, but don't send any part of the body if
990 we're in wait_continue. */
991 do_stream_2(IO_WRITE
,
993 connection
->reqbuf
, connection
->reqlen
,
994 client
->reqbuf
+ client
->reqbegin
,
995 (request
->flags
& REQUEST_WAIT_CONTINUE
) ? 0 : len
,
996 httpServerSideHandler2
, connection
);
997 httpServerReply(connection
, 0);
998 } else if(request
->object
->flags
& OBJECT_ABORTED
) {
999 if(connection
->reqbuf
)
1000 dispose_chunk(connection
->reqbuf
);
1001 connection
->reqbuf
= NULL
;
1002 connection
->reqlen
= 0;
1003 pokeFdEvent(connection
->fd
, -ESHUTDOWN
, POLLIN
);
1004 client
->flags
|= CONN_SIDE_READER
;
1005 do_stream(IO_READ
| IO_IMMEDIATE
,
1006 client
->fd
, 0, NULL
, 0,
1007 httpClientSideHandler
, client
);
1008 } else if(!(request
->flags
& REQUEST_WAIT_CONTINUE
) && doflush
) {
1009 /* Make sure there's a reqbuf, as httpServerFinish uses
1010 it to determine if there's a writer. */
1011 if(connection
->reqbuf
== NULL
)
1012 connection
->reqbuf
= get_chunk();
1013 assert(connection
->reqbuf
!= NULL
);
1016 client
->reqbuf
+ client
->reqbegin
, len
,
1017 httpServerSideHandler
, connection
);
1019 if(connection
->reqbuf
) {
1020 httpConnectionDestroyReqbuf(connection
);
1021 connection
->reqlen
= 0;
1023 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
1024 ConditionHandlerPtr chandler
;
1025 do_log(D_SERVER_CONN
, "W... %s:%d.\n",
1026 connection
->server
->name
, connection
->server
->port
);
1028 conditionWait(&request
->object
->condition
,
1029 httpServerContinueConditionHandler
,
1030 sizeof(connection
), &connection
);
1034 do_log(L_ERROR
, "Couldn't register condition handler.\n");
1035 /* Fall through -- the client side will clean up. */
1037 client
->flags
|= CONN_SIDE_READER
;
1038 do_stream(IO_READ
| (done
? IO_IMMEDIATE
: 0 ) | IO_NOTNOW
,
1039 client
->fd
, client
->reqlen
,
1040 client
->reqbuf
, CHUNK_SIZE
,
1041 httpClientSideHandler
, client
);
1047 httpClientDelayedDoSideHandler(TimeEventHandlerPtr event
)
1049 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1050 httpServerDoSide(connection
);
1055 httpServerDelayedDoSide(HTTPConnectionPtr connection
)
1057 TimeEventHandlerPtr handler
;
1058 handler
= scheduleTimeEvent(1, httpClientDelayedDoSideHandler
,
1059 sizeof(connection
), &connection
);
1061 do_log(L_ERROR
, "Couldn't schedule DoSide -- freeing memory.\n");
1062 free_chunk_arenas();
1063 handler
= scheduleTimeEvent(1, httpClientDelayedDoSideHandler
,
1064 sizeof(connection
), &connection
);
1065 do_log(L_ERROR
, "Couldn't schedule DoSide.\n");
1066 /* Somebody will hopefully end up timing out. */
1073 httpServerSideHandlerCommon(int kind
, int status
,
1074 FdEventHandlerPtr event
,
1075 StreamRequestPtr srequest
)
1077 HTTPConnectionPtr connection
= srequest
->data
;
1078 HTTPRequestPtr request
= connection
->request
;
1079 HTTPRequestPtr requestor
= request
->request
;
1080 HTTPConnectionPtr client
= requestor
->connection
;
1083 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1086 do_log_error(L_ERROR
, -status
, "Couldn't write to server");
1087 httpConnectionDestroyReqbuf(connection
);
1088 if(status
!= -ECLIENTRESET
)
1089 shutdown(connection
->fd
, 2);
1090 abortObject(request
->object
, 503,
1091 internAtom("Couldn't write to server"));
1092 /* Let the read side handle the error */
1093 httpServerDoSide(connection
);
1097 assert(srequest
->offset
> 0);
1100 if(srequest
->offset
< connection
->reqlen
)
1102 bodylen
= srequest
->offset
- connection
->reqlen
;
1103 connection
->reqlen
= 0;
1104 httpConnectionDestroyReqbuf(connection
);
1106 bodylen
= srequest
->offset
;
1110 assert(client
->reqbegin
+ bodylen
<= client
->reqlen
);
1112 if(client
->reqlen
> client
->reqbegin
+ bodylen
)
1113 memmove(client
->reqbuf
, client
->reqbuf
+ client
->reqbegin
+ bodylen
,
1114 client
->reqlen
- client
->reqbegin
- bodylen
);
1115 client
->reqlen
-= bodylen
+ client
->reqbegin
;
1116 client
->reqbegin
= 0;
1117 connection
->reqoffset
+= bodylen
;
1118 httpServerDoSide(connection
);
1123 httpServerSideHandler(int status
,
1124 FdEventHandlerPtr event
,
1125 StreamRequestPtr srequest
)
1127 return httpServerSideHandlerCommon(1, status
, event
, srequest
);
1131 httpServerSideHandler2(int status
,
1132 FdEventHandlerPtr event
,
1133 StreamRequestPtr srequest
)
1135 return httpServerSideHandlerCommon(2, status
, event
, srequest
);
1139 httpServerContinueConditionHandler(int status
, ConditionHandlerPtr chandler
)
1141 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)chandler
->data
;
1143 if(connection
->request
->flags
& REQUEST_WAIT_CONTINUE
)
1145 httpServerDelayedDoSide(connection
);
1149 /* s is 0 to keep the connection alive, 1 to shutdown the connection,
1150 and -1 to keep the connection alive and keep the current request. */
1152 httpServerFinish(HTTPConnectionPtr connection
, int s
, int offset
)
1154 HTTPServerPtr server
= connection
->server
;
1155 HTTPRequestPtr request
= connection
->request
;
1159 assert(connection
->pipelined
>= 1);
1160 assert((connection
->pipelined
> 1) == (request
->next
!= NULL
));
1162 assert(connection
->pipelined
== 0);
1165 if(s
== 0 && (!connection
->request
||
1166 !(connection
->request
->flags
& REQUEST_PERSISTENT
)))
1169 if(connection
->serviced
>= maxConnectionRequests
||
1170 connection
->time
< current_time
.tv_sec
- maxConnectionAge
)
1173 if(connection
->reqbuf
) {
1174 /* As most normal requests go out in a single packet, this is
1175 extremely unlikely to happen. As for POST/PUT requests,
1176 they are not pipelined, so this can only happen if the
1177 server sent an error reply early. */
1178 assert(connection
->fd
>= 0);
1179 shutdown(connection
->fd
, 1);
1180 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLOUT
);
1181 httpServerDelayedFinish(connection
);
1185 if(s
>= 0 && request
) {
1186 /* Update statistics about the server */
1187 int size
= -1, d
= -1, rtt
= -1, rate
= -1;
1188 if(connection
->offset
> 0 && request
->from
>= 0)
1189 size
= connection
->offset
- request
->from
;
1190 if(request
->time1
.tv_sec
!= null_time
.tv_sec
) {
1191 d
= timeval_minus_usec(¤t_time
, &request
->time1
);
1192 if(!(request
->flags
& REQUEST_PIPELINED
) &&
1193 request
->time0
.tv_sec
!= null_time
.tv_sec
)
1194 rtt
= timeval_minus_usec(&request
->time1
, &request
->time0
);
1195 if(size
>= 8192 && d
> 50000)
1196 rate
= ((double)size
/ (double)d
) * 1000000.0 + 0.5;
1198 request
->time0
= null_time
;
1199 request
->time1
= null_time
;
1202 if(server
->rtt
>= 0)
1203 server
->rtt
= (3 * server
->rtt
+ rtt
+ 2) / 4;
1208 if(server
->rate
>= 0)
1209 server
->rate
= (3 * server
->rate
+ rate
+ 2) / 4;
1211 server
->rate
= rate
;
1214 httpDequeueRequest(connection
);
1215 connection
->pipelined
--;
1216 request
->object
->flags
&= ~(OBJECT_INPROGRESS
| OBJECT_VALIDATING
);
1217 if(request
->request
) {
1218 request
->request
->request
= NULL
;
1219 request
->request
= NULL
;
1221 releaseNotifyObject(request
->object
);
1222 request
->object
= NULL
;
1223 httpDestroyRequest(request
);
1226 do_log(D_SERVER_CONN
, "Done with server %s:%d connection (%d)\n",
1227 connection
->server
->name
, connection
->server
->port
, s
);
1229 assert(offset
<= connection
->len
);
1232 if(offset
< connection
->len
) {
1233 assert(connection
->buf
!= NULL
);
1234 if(!connection
->pipelined
) {
1236 "Closing connection to %s:%d: "
1237 "%d stray bytes of data.\n",
1238 server
->name
, server
->port
, connection
->len
- offset
);
1241 memmove(connection
->buf
, connection
->buf
+ offset
,
1242 connection
->len
- offset
);
1243 connection
->len
= connection
->len
- offset
;
1244 if((connection
->flags
& CONN_BIGBUF
) &&
1245 connection
->len
<= CHUNK_SIZE
)
1246 httpConnectionUnbigify(connection
);
1249 connection
->len
= 0;
1253 connection
->server
->time
= current_time
.tv_sec
;
1254 connection
->serviced
++;
1257 if(connection
->timeout
)
1258 cancelTimeEvent(connection
->timeout
);
1259 connection
->timeout
= NULL
;
1260 httpConnectionDestroyBuf(connection
);
1261 if(connection
->fd
>= 0)
1262 CLOSE(connection
->fd
);
1263 connection
->fd
= -1;
1264 server
->persistent
-= 1;
1265 if(server
->persistent
< -5)
1266 server
->numslots
= MIN(server
->maxslots
, serverMaxSlots
);
1267 if(connection
->request
) {
1269 do_log(D_SERVER_CONN
, "Restarting pipeline to %s:%d.\n",
1270 server
->name
, server
->port
);
1271 if(server
->pipeline
== 2)
1272 server
->pipeline
-= 20;
1274 server
->pipeline
-= 5;
1275 req
= connection
->request
;
1277 req
->connection
= NULL
;
1281 connection
->request_last
->next
= server
->request
;
1283 server
->request_last
= connection
->request_last
;
1284 server
->request
= connection
->request
;
1285 connection
->request
= NULL
;
1286 connection
->request_last
= NULL
;
1288 /* Make sure we don't get confused into thinking a probe
1290 if(server
->pipeline
== 2 || server
->pipeline
== 3)
1291 server
->pipeline
= 1;
1292 for(i
= 0; i
< server
->maxslots
; i
++)
1293 if(connection
== server
->connection
[i
])
1295 assert(i
< server
->maxslots
);
1296 if(server
->idleHandler
[i
])
1297 unregisterFdEvent(server
->idleHandler
[i
]);
1298 server
->idleHandler
[i
] = NULL
;
1299 server
->connection
[i
] = NULL
;
1302 server
->persistent
+= 1;
1303 if(server
->persistent
> 0)
1304 server
->numslots
= MIN(server
->maxslots
,
1305 server
->version
== HTTP_10
?
1306 serverSlots1
: serverSlots
);
1307 httpSetTimeout(connection
, serverTimeout
);
1308 /* See httpServerTrigger */
1309 if(connection
->pipelined
||
1310 (server
->version
== HTTP_11
&& server
->pipeline
<= 0) ||
1311 (server
->pipeline
== 3)) {
1314 if(s
< 0 || connection
->pipelined
) {
1315 httpServerReply(connection
, 1);
1317 httpConnectionDestroyBuf(connection
);
1322 httpServerTrigger(server
);
1326 httpServerDelayedFinishHandler(TimeEventHandlerPtr event
)
1328 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1329 httpServerFinish(connection
, 1, 0);
1334 httpServerDelayedFinish(HTTPConnectionPtr connection
)
1336 TimeEventHandlerPtr handler
;
1338 handler
= scheduleTimeEvent(1, httpServerDelayedFinishHandler
,
1339 sizeof(connection
), &connection
);
1342 "Couldn't schedule delayed finish -- freeing memory.");
1343 free_chunk_arenas();
1344 handler
= scheduleTimeEvent(1, httpServerDelayedFinishHandler
,
1345 sizeof(connection
), &connection
);
1348 "Couldn't schedule delayed finish -- aborting.\n");
1355 httpServerReply(HTTPConnectionPtr connection
, int immediate
)
1357 assert(connection
->pipelined
> 0);
1359 if(connection
->request
->request
== NULL
) {
1360 do_log(L_WARN
, "Aborting pipeline on %s:%d.\n",
1361 connection
->server
->name
, connection
->server
->port
);
1362 httpServerFinish(connection
, 1, 0);
1366 do_log(D_SERVER_CONN
, "R: ");
1367 do_log_n(D_SERVER_CONN
, connection
->request
->object
->key
,
1368 connection
->request
->object
->key_size
);
1369 do_log(D_SERVER_CONN
, " (%d)\n", connection
->request
->method
);
1371 if(connection
->len
== 0)
1372 httpConnectionDestroyBuf(connection
);
1374 httpSetTimeout(connection
, serverTimeout
);
1375 do_stream_buf(IO_READ
| (immediate
? IO_IMMEDIATE
: 0) | IO_NOTNOW
,
1376 connection
->fd
, connection
->len
,
1377 &connection
->buf
, CHUNK_SIZE
,
1378 httpServerReplyHandler
, connection
);
1382 httpConnectionPipelined(HTTPConnectionPtr connection
)
1384 HTTPRequestPtr request
= connection
->request
;
1388 request
= request
->next
;
1394 httpServerUnpipeline(HTTPRequestPtr request
)
1396 HTTPConnectionPtr connection
= request
->connection
;
1397 HTTPServerPtr server
= connection
->server
;
1399 request
->flags
&= ~REQUEST_PERSISTENT
;
1403 "Restarting pipeline to %s:%d.\n",
1404 connection
->server
->name
, connection
->server
->port
);
1405 req
= request
->next
;
1407 req
->connection
= NULL
;
1411 connection
->request_last
->next
= server
->request
;
1413 server
->request_last
= connection
->request_last
;
1414 server
->request
= request
->next
;
1415 request
->next
= NULL
;
1416 connection
->request_last
= request
;
1418 connection
->pipelined
= httpConnectionPipelined(connection
);
1422 httpServerRestart(HTTPConnectionPtr connection
)
1424 HTTPServerPtr server
= connection
->server
;
1425 HTTPRequestPtr request
= connection
->request
;
1431 "Restarting pipeline to %s:%d.\n",
1432 connection
->server
->name
, connection
->server
->port
);
1435 req
->connection
= NULL
;
1439 connection
->request_last
->next
= server
->request
;
1441 server
->request_last
= connection
->request_last
;
1442 server
->request
= request
;
1443 connection
->request
= NULL
;
1444 connection
->request_last
= NULL
;
1446 connection
->pipelined
= 0;
1447 httpServerFinish(connection
, 1, 0);
1451 httpServerRequest(ObjectPtr object
, int method
, int from
, int to
,
1452 HTTPRequestPtr requestor
, void *closure
)
1459 assert(from
>= 0 && (to
< 0 || to
> from
));
1460 assert(closure
== NULL
);
1461 assert(!(object
->flags
& OBJECT_LOCAL
));
1462 assert(object
->type
== OBJECT_HTTP
);
1464 if(object
->flags
& OBJECT_INPROGRESS
)
1467 if(requestor
->flags
& REQUEST_REQUESTED
)
1470 assert(requestor
->request
== NULL
);
1475 rc
= parseUrl(object
->key
, object
->key_size
, &x
, &y
, &port
, &z
);
1477 if(rc
< 0 || x
< 0 || y
< 0 || y
- x
> 131) {
1478 do_log(L_ERROR
, "Couldn't parse URL: ");
1479 do_log_n(L_ERROR
, object
->key
, object
->key_size
);
1480 do_log(L_ERROR
, "\n");
1481 abortObject(object
, 400, internAtom("Couldn't parse URL"));
1482 notifyObject(object
);
1486 if(!intListMember(port
, allowedPorts
)) {
1487 do_log(L_ERROR
, "Attempted connection to port %d.\n", port
);
1488 abortObject(object
, 403, internAtom("Forbidden port"));
1489 notifyObject(object
);
1493 memcpy(name
, ((char*)object
->key
) + x
, y
- x
);
1496 requestor
->flags
|= REQUEST_REQUESTED
;
1497 rc
= httpMakeServerRequest(name
, port
, object
, method
, from
, to
,
1502 503, internAtom("Couldn't schedule server request"));
1503 notifyObject(object
);
1511 httpWriteRequest(HTTPConnectionPtr connection
, HTTPRequestPtr request
,
1514 ObjectPtr object
= request
->object
;
1515 int from
= request
->from
, to
= request
->to
, method
= request
->method
;
1516 char *url
= object
->key
, *m
;
1517 int url_size
= object
->key_size
;
1518 int x
, y
, port
, z
, location_size
;
1520 int l
, n
, rc
, bufsize
;
1522 assert(method
!= METHOD_NONE
);
1524 if(request
->method
== METHOD_GET
||
1525 request
->method
== METHOD_CONDITIONAL_GET
) {
1529 do_log(L_ERROR
, "Requesting empty segment?\n");
1534 if(object
->flags
& OBJECT_DYNAMIC
) {
1538 objectFillFromDisk(object
, from
/ CHUNK_SIZE
* CHUNK_SIZE
, 1);
1539 l
= objectHoleSize(request
->object
, from
);
1541 if(to
<= 0 || to
> from
+ l
)
1545 if(pmmSize
&& connection
->server
->pipeline
>= 4) {
1547 to
= to
< 0 ? pmmFirstSize
: MIN(to
, pmmFirstSize
);
1549 to
= to
< 0 ? from
+ pmmSize
: MIN(to
, from
+ pmmSize
);
1552 if(from
% CHUNK_SIZE
!= 0)
1553 if(objectHoleSize(object
, from
- 1) != 0)
1554 from
= from
/ CHUNK_SIZE
* CHUNK_SIZE
;
1558 rc
= parseUrl(url
, url_size
, &x
, &y
, &port
, &z
);
1560 if(rc
< 0 || x
< 0 || y
< 0) {
1564 if(connection
->reqbuf
== NULL
) {
1565 connection
->reqbuf
= get_chunk();
1566 if(connection
->reqbuf
== NULL
)
1568 connection
->reqlen
= 0;
1571 if(method
== METHOD_CONDITIONAL_GET
&&
1572 object
->last_modified
< 0 && object
->etag
== NULL
)
1573 method
= request
->method
= METHOD_GET
;
1577 (connection
->flags
& CONN_BIGREQBUF
) ? bigBufferSize
: CHUNK_SIZE
;
1578 n
= connection
->reqlen
;
1581 case METHOD_CONDITIONAL_GET
: m
= "GET"; break;
1582 case METHOD_HEAD
: m
= "HEAD"; break;
1583 case METHOD_POST
: m
= "POST"; break;
1584 case METHOD_PUT
: m
= "PUT"; break;
1587 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, "%s ", m
);
1589 if(connection
->server
->isProxy
) {
1590 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
,
1593 if(url_size
- z
== 0) {
1598 location_size
= url_size
- z
;
1601 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
,
1602 location
, location_size
);
1605 do_log(D_SERVER_REQ
, "Server request: ");
1606 do_log_n(D_SERVER_REQ
, url
+ x
, y
- x
);
1607 do_log(D_SERVER_REQ
, ": ");
1608 do_log_n(D_SERVER_REQ
, connection
->reqbuf
, n
);
1609 do_log(D_SERVER_REQ
, " (method %d from %d to %d, 0x%lx for 0x%lx)\n",
1611 (unsigned long)connection
, (unsigned long)object
);
1613 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, " HTTP/1.1");
1615 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, "\r\nHost: ");
1616 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
, url
+ x
, y
- x
);
1618 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, ":%d", port
);
1620 if(connection
->server
->isProxy
&& parentAuthCredentials
) {
1621 n
= buildServerAuthHeaders(connection
->reqbuf
, n
, bufsize
,
1622 parentAuthCredentials
);
1626 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1627 "\r\nContent-Length: %d", bodylen
);
1629 if(request
->flags
& REQUEST_WAIT_CONTINUE
)
1630 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1631 "\r\nExpect: 100-continue");
1633 if(method
!= METHOD_HEAD
&& (from
> 0 || to
>= 0)) {
1635 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1636 "\r\nRange: bytes=%d-%d", from
, to
- 1);
1638 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1639 "\r\nRange: bytes=%d-", from
);
1643 if(method
== METHOD_GET
&& object
->etag
&& (from
> 0 || to
>= 0)) {
1644 if(request
->request
&& request
->request
->request
== request
&&
1645 request
->request
->from
== 0 && request
->request
->to
== -1 &&
1646 pmmSize
== 0 && pmmFirstSize
== 0)
1647 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1648 "\r\nIf-Range: \"%s\"", object
->etag
);
1651 if(method
== METHOD_CONDITIONAL_GET
) {
1652 if(object
->last_modified
>= 0) {
1653 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1654 "\r\nIf-Modified-Since: ");
1655 n
= format_time(connection
->reqbuf
, n
, bufsize
,
1656 object
->last_modified
);
1659 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1660 "\r\nIf-None-Match: \"%s\"", object
->etag
);
1664 n
= httpPrintCacheControl(connection
->reqbuf
, n
, bufsize
,
1665 0, &request
->cache_control
);
1669 if(request
->request
&& request
->request
->headers
) {
1670 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
,
1671 request
->request
->headers
->string
,
1672 request
->request
->headers
->length
);
1675 if(request
->request
&& request
->request
->via
) {
1676 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1677 "\r\nVia: %s, 1.1 %s",
1678 request
->request
->via
->string
, proxyName
->string
);
1680 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1686 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1687 "\r\nConnection: %s\r\n\r\n",
1688 (request
->flags
& REQUEST_PERSISTENT
) ?
1689 "keep-alive" : "close");
1690 if(n
< 0 || n
>= bufsize
- 1)
1692 connection
->reqlen
= n
;
1697 if(!(connection
->flags
& CONN_BIGREQBUF
))
1698 rc
= httpConnectionBigifyReqbuf(connection
);
1705 httpServerHandler(int status
,
1706 FdEventHandlerPtr event
,
1707 StreamRequestPtr srequest
)
1709 HTTPConnectionPtr connection
= srequest
->data
;
1712 assert(connection
->request
->object
->flags
& OBJECT_INPROGRESS
);
1714 if(connection
->reqlen
== 0) {
1715 do_log(D_SERVER_REQ
, "Writing aborted on 0x%lx\n",
1716 (unsigned long)connection
);
1720 if(status
== 0 && !streamRequestDone(srequest
)) {
1721 httpSetTimeout(connection
, serverTimeout
);
1725 httpConnectionDestroyReqbuf(connection
);
1728 if(connection
->serviced
>= 1) {
1729 httpServerRestart(connection
);
1732 if(status
>= 0 || status
== ECONNRESET
) {
1733 message
= internAtom("Couldn't send request to server: "
1736 if(status
!= -EPIPE
)
1737 do_log_error(L_ERROR
, -status
,
1738 "Couldn't send request to server");
1740 internAtomError(-status
, "Couldn't send request to server");
1748 dispose_chunk(connection
->reqbuf
);
1749 connection
->reqbuf
= NULL
;
1750 shutdown(connection
->fd
, 2);
1751 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLIN
);
1752 httpSetTimeout(connection
, 60);
1757 httpServerSendRequest(HTTPConnectionPtr connection
)
1759 assert(connection
->server
);
1761 if(connection
->reqlen
== 0) {
1762 do_log(D_SERVER_REQ
,
1763 "Writing aborted on 0x%lx\n", (unsigned long)connection
);
1764 httpConnectionDestroyReqbuf(connection
);
1765 shutdown(connection
->fd
, 2);
1766 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLIN
| POLLOUT
);
1770 httpSetTimeout(connection
, serverTimeout
);
1771 do_stream(IO_WRITE
, connection
->fd
, 0,
1772 connection
->reqbuf
, connection
->reqlen
,
1773 httpServerHandler
, connection
);
1778 httpServerReplyHandler(int status
,
1779 FdEventHandlerPtr event
,
1780 StreamRequestPtr srequest
)
1782 HTTPConnectionPtr connection
= srequest
->data
;
1783 HTTPRequestPtr request
= connection
->request
;
1786 (connection
->flags
& CONN_BIGBUF
) ? bigBufferSize
: CHUNK_SIZE
;
1788 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1790 if(connection
->serviced
>= 1) {
1791 httpServerRestart(connection
);
1794 if(status
!= -ECLIENTRESET
)
1795 do_log_error(L_ERROR
, -status
, "Read from server failed");
1796 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
1797 internAtomError(-status
, "Read from server failed"));
1801 i
= findEndOfHeaders(connection
->buf
, 0, srequest
->offset
, &body
);
1802 connection
->len
= srequest
->offset
;
1805 request
->time1
= current_time
;
1806 return httpServerHandlerHeaders(status
, event
, srequest
, connection
);
1810 if(connection
->serviced
>= 1) {
1811 httpServerRestart(connection
);
1816 "Error reading server headers: %d\n", -status
);
1817 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
1818 internAtomError(-status
,
1819 "Error reading server headers"));
1821 httpServerAbort(connection
, 1, 502,
1822 internAtom("Server dropped connection"));
1826 if(connection
->len
>= bufsize
) {
1828 if(!(connection
->flags
& CONN_BIGBUF
))
1829 rc
= httpConnectionBigify(connection
);
1831 do_log(L_ERROR
, "Couldn't find end of server's headers.\n");
1832 httpServerAbort(connection
, 1, 502,
1833 internAtom("Couldn't find end "
1834 "of server's headers"));
1837 do_log(L_ERROR
, "Couldn't allocate big buffer.\n");
1838 httpServerAbort(connection
, 1, 500,
1839 internAtom("Couldn't allocate big buffer"));
1842 /* Can't just return 0 -- buf has moved. */
1844 connection
->fd
, connection
->len
,
1845 connection
->buf
, bigBufferSize
,
1846 httpServerReplyHandler
, connection
);
1854 httpServerHandlerHeaders(int eof
,
1855 FdEventHandlerPtr event
,
1856 StreamRequestPtr srequest
,
1857 HTTPConnectionPtr connection
)
1859 HTTPRequestPtr request
= connection
->request
;
1860 ObjectPtr object
= request
->object
;
1867 CacheControlRec cache_control
;
1869 time_t date
, last_modified
, expires
;
1870 struct timeval
*init_time
;
1872 AtomPtr via
, new_via
;
1874 HTTPRangeRec content_range
;
1875 ObjectPtr new_object
= NULL
, old_object
= NULL
;
1877 AtomPtr message
= NULL
;
1881 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1884 httpSetTimeout(connection
, -1);
1886 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
1887 do_log(D_SERVER_CONN
, "W %s:%d.\n",
1888 connection
->server
->name
, connection
->server
->port
);
1889 request
->flags
&= ~REQUEST_WAIT_CONTINUE
;
1892 rc
= httpParseServerFirstLine(connection
->buf
, &code
, &version
, &message
);
1894 do_log(L_ERROR
, "Couldn't parse server status line.\n");
1895 httpServerAbort(connection
, 1, 502,
1896 internAtom("Couldn't parse server status line"));
1900 do_log(D_SERVER_REQ
, "Server status: ");
1901 do_log_n(D_SERVER_REQ
, connection
->buf
,
1902 connection
->buf
[rc
- 1] == '\r' ? rc
- 2 : rc
- 2);
1903 do_log(D_SERVER_REQ
, " (0x%lx for 0x%lx)\n",
1904 (unsigned long)connection
, (unsigned long)object
);
1906 if(version
!= HTTP_10
&& version
!= HTTP_11
) {
1907 do_log(L_ERROR
, "Unknown server HTTP version\n");
1908 httpServerAbort(connection
, 1, 502,
1909 internAtom("Unknown server HTTP version"));
1910 releaseAtom(message
);
1914 connection
->version
= version
;
1915 connection
->server
->version
= version
;
1916 request
->flags
|= REQUEST_PERSISTENT
;
1918 url
= internAtomN(object
->key
, object
->key_size
);
1919 rc
= httpParseHeaders(0, url
, connection
->buf
, rc
, request
,
1920 &headers
, &len
, &cache_control
, NULL
, &te
,
1921 &date
, &last_modified
, &expires
, NULL
, NULL
, NULL
,
1922 &age
, &etag
, NULL
, NULL
, &content_range
,
1925 do_log(L_ERROR
, "Couldn't parse server headers\n");
1927 releaseAtom(message
);
1928 httpServerAbort(connection
, 1, 502,
1929 internAtom("Couldn't parse server headers"));
1935 releaseAtom(message
);
1936 /* We've already reset wait_continue above, but we must still
1937 ensure that the writer notices. */
1938 notifyObject(request
->object
);
1939 httpServerFinish(connection
, -1, rc
);
1944 httpServerAbort(connection
, 1, 501,
1945 internAtom("Upgrade not implemented"));
1949 if(via
&& !checkVia(proxyName
, via
)) {
1950 httpServerAbort(connection
, 1, 504, internAtom("Proxy loop detected"));
1953 full_len
= content_range
.full_length
;
1956 if(content_range
.from
== -1 || content_range
.to
== -1) {
1957 do_log(L_ERROR
, "Partial content without range.\n");
1958 httpServerAbort(connection
, 1, 502,
1959 internAtom("Partial content without range"));
1962 if(len
>= 0 && len
!= content_range
.to
- content_range
.from
) {
1963 do_log(L_ERROR
, "Inconsistent partial content.\n");
1964 httpServerAbort(connection
, 1, 502,
1965 internAtom("Inconsistent partial content"));
1968 } else if(code
< 400 &&
1969 (content_range
.from
>= 0 || content_range
.to
>= 0 ||
1970 content_range
.full_length
>= 0)) {
1971 do_log(L_WARN
, "Range without partial content.\n");
1973 content_range
.from
= -1;
1974 content_range
.to
= -1;
1975 content_range
.full_length
= -1;
1976 } else if(code
!= 304 && code
!= 412) {
1980 if(te
!= TE_IDENTITY
&& te
!= TE_CHUNKED
) {
1981 do_log(L_ERROR
, "Unsupported transfer-encoding\n");
1982 httpServerAbort(connection
, 1, 502,
1983 internAtom("Unsupported transfer-encoding"));
1988 if(request
->method
!= METHOD_CONDITIONAL_GET
) {
1989 do_log(L_ERROR
, "Unexpected \"not changed\" reply from server\n");
1990 httpServerAbort(connection
, 1, 502,
1991 internAtom("Unexpected \"not changed\" "
1992 "reply from server"));
1995 if(object
->etag
&& !etag
) {
1996 /* RFC 2616 10.3.5. Violated by some front-end proxies. */
1997 do_log(L_WARN
, "\"Not changed\" reply with no ETag.\n");
2002 if(request
->method
!= METHOD_CONDITIONAL_GET
||
2003 (!object
->etag
&& !object
->last_modified
)) {
2005 "Unexpected \"precondition failed\" reply from server.\n");
2006 httpServerAbort(connection
, 1, 502,
2007 internAtom("Unexpected \"precondition failed\" "
2008 "reply from server"));
2015 /* Okay, we're going to accept this reply. */
2017 if((code
== 200 || code
== 206 || code
== 304 || code
== 412) &&
2018 (cache_control
.flags
& (CACHE_NO
| CACHE_NO_STORE
) ||
2019 cache_control
.max_age
== 0 ||
2020 (cacheIsShared
&& cache_control
.s_maxage
== 0) ||
2021 (expires
>= 0 && expires
<= object
->age
))) {
2022 do_log(L_UNCACHEABLE
, "Uncacheable object ");
2023 do_log_n(L_UNCACHEABLE
, object
->key
, object
->key_size
);
2024 do_log(L_UNCACHEABLE
, " (%d)\n", cache_control
.flags
);
2027 if(request
->time0
.tv_sec
!= null_time
.tv_sec
)
2028 init_time
= &request
->time0
;
2030 init_time
= ¤t_time
;
2031 age
= MIN(init_time
->tv_sec
- age
, init_time
->tv_sec
);
2033 if(request
->method
== METHOD_HEAD
||
2034 code
< 200 || code
== 204 || code
== 304)
2036 else if(te
== TE_IDENTITY
)
2037 expect_body
= (len
!= 0);
2041 connection
->chunk_remaining
= -1;
2042 connection
->te
= te
;
2044 old_object
= object
;
2046 connection
->server
->lies
--;
2048 if(object
->cache_control
& CACHE_MISMATCH
)
2051 if(code
== 304 || code
== 412) {
2052 if((object
->etag
&& etag
&& strcmp(object
->etag
, etag
) != 0) ||
2053 (object
->last_modified
>= 0 && last_modified
>= 0 &&
2054 object
->last_modified
!= last_modified
)) {
2055 do_log(L_ERROR
, "Inconsistent \"%s\" reply for ",
2056 code
== 304 ? "not changed":"precondition failed");
2057 do_log_n(L_ERROR
, object
->key
, object
->key_size
);
2058 do_log(L_ERROR
, "\n");
2059 object
->flags
|= OBJECT_DYNAMIC
;
2062 } else if(!(object
->flags
& OBJECT_INITIAL
)) {
2063 if((object
->last_modified
< 0 || last_modified
< 0) &&
2064 (!object
->etag
|| !etag
))
2066 else if(object
->last_modified
!= last_modified
)
2068 else if(object
->etag
|| etag
) {
2069 /* We need to be permissive here so as to deal with some
2070 front-end proxies that discard ETags on partial
2071 replies but not on full replies. */
2072 if(etag
&& object
->etag
&& strcmp(object
->etag
, etag
) != 0)
2074 else if(!object
->etag
)
2078 if(!supersede
&& (object
->cache_control
& CACHE_VARY
) &&
2079 dontTrustVaryETag
>= 1) {
2080 /* Check content-type to work around mod_gzip bugs */
2081 if(!httpHeaderMatch(atomContentType
, object
->headers
, headers
) ||
2082 !httpHeaderMatch(atomContentEncoding
, object
->headers
, headers
))
2086 if(full_len
< 0 && te
== TE_IDENTITY
) {
2087 /* It's an HTTP/1.0 CGI. Be afraid. */
2088 if(expect_body
&& content_range
.from
< 0 && content_range
.to
< 0)
2092 if(!supersede
&& object
->length
>= 0 && full_len
>= 0 &&
2093 object
->length
!= full_len
) {
2094 do_log(L_WARN
, "Inconsistent length.\n");
2099 ((object
->last_modified
>= 0 && last_modified
>= 0) ||
2100 (object
->etag
&& etag
))) {
2101 if(request
->method
== METHOD_CONDITIONAL_GET
) {
2102 do_log(L_WARN
, "Server ignored conditional request.\n");
2103 connection
->server
->lies
+= 10;
2104 /* Drop the connection? */
2107 } else if(code
== 416) {
2108 do_log(L_ERROR
, "Unexpected \"range not satisfiable\" reply\n");
2109 httpServerAbort(connection
, 1, 502,
2110 internAtom("Unexpected \"range not satisfiable\" "
2112 /* The object may be superseded. Make sure the next request
2113 won't be partial. */
2114 abortObject(object
, 502,
2115 internAtom("Unexpected \"range not satisfiable\" reply"));
2119 if(object
->flags
& OBJECT_INITIAL
)
2123 do_log(L_SUPERSEDED
, "Superseding object: ");
2124 do_log_n(L_SUPERSEDED
, old_object
->key
, old_object
->key_size
);
2125 do_log(L_SUPERSEDED
, " (%d %d %d %s -> %d %d %d %s)\n",
2126 object
->code
, object
->length
, (int)object
->last_modified
,
2127 object
->etag
?object
->etag
: "(none)",
2128 code
, full_len
, (int)last_modified
,
2129 etag
?etag
:"(none)");
2130 privatiseObject(old_object
, 0);
2131 new_object
= makeObject(object
->type
, object
->key
,
2132 object
->key_size
, 1, 0,
2133 object
->request
, NULL
);
2134 if(new_object
== NULL
) {
2135 do_log(L_ERROR
, "Couldn't allocate object\n");
2136 httpServerAbort(connection
, 1, 500,
2137 internAtom("Couldn't allocate object"));
2140 if(urlIsLocal(new_object
->key
, new_object
->key_size
))
2141 new_object
->flags
|= OBJECT_LOCAL
;
2143 new_object
= object
;
2147 (!etag
&& last_modified
< 0) ||
2148 (cache_control
.flags
&
2149 (CACHE_NO_HIDDEN
| CACHE_NO
| CACHE_NO_STORE
|
2150 (cacheIsShared
? CACHE_PRIVATE
: 0))) ||
2151 (cache_control
.max_age
>= 0 && cache_control
.max_age
<= 2) ||
2153 cache_control
.s_maxage
>= 0 && cache_control
.s_maxage
<= 5) ||
2154 (old_object
->last_modified
>= 0 && old_object
->expires
>= 0 &&
2155 (old_object
->expires
- old_object
->last_modified
<= 1)) ||
2156 (supersede
&& (old_object
->date
- date
<= 5));
2159 new_object
->flags
|= OBJECT_DYNAMIC
;
2161 new_object
->flags
&= ~OBJECT_DYNAMIC
;
2162 else if(old_object
->flags
& OBJECT_DYNAMIC
)
2163 new_object
->flags
|= OBJECT_DYNAMIC
;
2165 new_object
->age
= age
;
2166 new_object
->cache_control
|= cache_control
.flags
;
2167 new_object
->max_age
= cache_control
.max_age
;
2168 new_object
->s_maxage
= cache_control
.s_maxage
;
2169 new_object
->flags
&= ~OBJECT_FAILED
;
2172 new_object
->date
= date
;
2173 if(last_modified
>= 0)
2174 new_object
->last_modified
= last_modified
;
2176 new_object
->expires
= expires
;
2177 if(new_object
->etag
== NULL
)
2178 new_object
->etag
= etag
;
2184 case 300: case 301: case 302: case 303: case 307:
2185 case 403: case 404: case 405: case 401:
2186 if(new_object
->message
) releaseAtom(new_object
->message
);
2187 new_object
->code
= code
;
2188 new_object
->message
= message
;
2190 case 206: case 304: case 412:
2191 if(new_object
->code
!= 200 || !new_object
->message
) {
2192 if(new_object
->message
) releaseAtom(new_object
->message
);
2193 new_object
->code
= 200;
2194 new_object
->message
= internAtom("OK");
2196 releaseAtom(message
);
2199 if(new_object
->message
) releaseAtom(new_object
->message
);
2200 new_object
->code
= code
;
2201 new_object
->message
= retainAtom(message
);
2205 httpTweakCachability(new_object
);
2208 new_via
= internAtomF("%s %s",
2209 version
== HTTP_11
? "1.1" : "1.0",
2212 new_via
= internAtomF("%s, %s %s", via
->string
,
2213 version
== HTTP_11
? "1.1" : "1.0",
2215 if(new_via
== NULL
) {
2216 do_log(L_ERROR
, "Couldn't allocate Via.\n");
2218 if(new_object
->via
) releaseAtom(new_object
->via
);
2219 new_object
->via
= new_via
;
2222 if(new_object
->flags
& OBJECT_INITIAL
) {
2223 objectPartial(new_object
, full_len
, headers
);
2225 if(new_object
->length
< 0)
2226 new_object
->length
= full_len
;
2227 /* XXX -- RFC 2616 13.5.3 */
2228 releaseAtom(headers
);
2232 assert(new_object
!= old_object
);
2233 supersedeObject(old_object
);
2236 if(new_object
!= old_object
) {
2237 if(new_object
->flags
& OBJECT_INPROGRESS
) {
2238 /* Make sure we don't fetch this object two times at the
2239 same time. Just drop the connection. */
2240 releaseObject(new_object
);
2241 httpServerFinish(connection
, 1, 0);
2244 old_object
->flags
&= ~OBJECT_VALIDATING
;
2245 new_object
->flags
|= OBJECT_INPROGRESS
;
2246 /* Signal the client side to switch to the new object -- see
2247 httpClientGetHandler. If it doesn't, we'll give up on this
2249 new_object
->flags
|= OBJECT_MUTATING
;
2250 request
->can_mutate
= new_object
;
2251 notifyObject(old_object
);
2252 request
->can_mutate
= NULL
;
2253 new_object
->flags
&= ~OBJECT_MUTATING
;
2254 old_object
->flags
&= ~OBJECT_INPROGRESS
;
2255 if(request
->object
== old_object
) {
2256 if(request
->request
)
2257 request
->request
->request
= NULL
;
2258 request
->request
= NULL
;
2259 request
->object
= new_object
;
2261 assert(request
->object
== new_object
);
2263 releaseNotifyObject(old_object
);
2265 object
= new_object
;
2267 objectMetadataChanged(new_object
, 0);
2270 if(object
->flags
& OBJECT_VALIDATING
) {
2271 object
->flags
&= ~OBJECT_VALIDATING
;
2272 notifyObject(object
);
2276 httpServerFinish(connection
, 0, rc
);
2280 if(request
->request
== NULL
) {
2281 httpServerFinish(connection
, 1, 0);
2286 /* 412 replies contain a useless body. For now, we
2287 drop the connection. */
2288 httpServerFinish(connection
, 1, 0);
2293 if(request
->flags
& REQUEST_PERSISTENT
) {
2294 if(request
->method
!= METHOD_HEAD
&&
2295 connection
->te
== TE_IDENTITY
&& len
< 0) {
2296 do_log(L_ERROR
, "Persistent reply with no Content-Length\n");
2297 /* That's potentially dangerous, as we could start reading
2298 arbitrary data into the object. Unfortunately, some
2300 request
->flags
&= ~REQUEST_PERSISTENT
;
2304 /* we're getting a body */
2305 if(content_range
.from
> 0)
2306 connection
->offset
= content_range
.from
;
2308 connection
->offset
= 0;
2310 if(content_range
.to
>= 0)
2311 request
->to
= content_range
.to
;
2313 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2314 (unsigned long)connection
, (unsigned long)object
,
2315 connection
->offset
);
2317 if(connection
->len
> rc
) {
2318 rc
= connectionAddData(connection
, rc
);
2322 do_log(L_ERROR
, "Couldn't parse chunk size.\n");
2323 httpServerAbort(connection
, 1, 502,
2324 internAtom("Couldn't parse chunk size"));
2326 do_log(L_ERROR
, "Couldn't add data to connection.\n");
2327 httpServerAbort(connection
, 1, 500,
2328 internAtom("Couldn't add data "
2334 if(object
->length
< 0) {
2335 object
->length
= object
->size
;
2336 objectMetadataChanged(object
, 0);
2337 } else if(object
->length
!= object
->size
) {
2338 httpServerAbort(connection
, 1, 500,
2339 internAtom("Inconsistent "
2341 object
->length
= -1;
2345 httpServerFinish(connection
, 0, 0);
2350 connection
->len
= 0;
2354 if(connection
->te
== TE_CHUNKED
||
2355 (object
->length
>= 0 &&
2356 connection
->offset
< object
->length
)) {
2357 do_log(L_ERROR
, "Server closed connection.\n");
2358 httpServerAbort(connection
, 1, 502,
2359 internAtom("Server closed connection"));
2362 if(code
!= 206 && eof
> 0 && object
->length
< 0) {
2363 object
->length
= object
->size
;
2364 objectMetadataChanged(object
, 0);
2366 httpServerFinish(connection
, 1, 0);
2370 return httpServerReadData(connection
, 1);
2376 releaseAtom(message
);
2378 releaseAtom(headers
);
2387 httpServerIndirectHandlerCommon(HTTPConnectionPtr connection
, int eof
)
2389 HTTPRequestPtr request
= connection
->request
;
2392 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
2394 if(connection
->len
> 0) {
2396 rc
= connectionAddData(connection
, 0);
2400 do_log(L_ERROR
, "Couldn't parse chunk size.\n");
2401 httpServerAbort(connection
, 1, 502,
2402 internAtom("Couldn't parse chunk size"));
2404 do_log(L_ERROR
, "Couldn't add data to connection.\n");
2405 httpServerAbort(connection
, 1, 500,
2406 internAtom("Couldn't add data "
2411 if(request
->to
< 0) {
2412 if(request
->object
->length
< 0) {
2413 request
->object
->length
= request
->object
->size
;
2414 objectMetadataChanged(request
->object
, 0);
2415 } else if(request
->object
->length
!=
2416 request
->object
->size
) {
2417 request
->object
->length
= -1;
2418 httpServerAbort(connection
, 1, 502,
2419 internAtom("Inconsistent "
2424 httpServerFinish(connection
, 0, 0);
2430 if(eof
&& connection
->len
== 0) {
2431 if(connection
->te
== TE_CHUNKED
||
2432 (request
->to
>= 0 && connection
->offset
< request
->to
)) {
2433 do_log(L_ERROR
, "Server dropped connection.\n");
2434 httpServerAbort(connection
, 1, 502,
2435 internAtom("Server dropped connection"));
2438 if(request
->object
->length
< 0 && eof
> 0 &&
2439 (request
->to
< 0 || request
->to
> request
->object
->size
)) {
2440 request
->object
->length
= request
->object
->size
;
2441 objectMetadataChanged(request
->object
, 0);
2443 httpServerFinish(connection
, 1, 0);
2447 return httpServerReadData(connection
, 0);
2452 httpServerIndirectHandler(int status
,
2453 FdEventHandlerPtr event
,
2454 StreamRequestPtr srequest
)
2456 HTTPConnectionPtr connection
= srequest
->data
;
2457 assert(connection
->request
->object
->flags
& OBJECT_INPROGRESS
);
2459 httpSetTimeout(connection
, -1);
2461 if(status
!= -ECLIENTRESET
)
2462 do_log_error(L_ERROR
, -status
, "Read from server failed");
2463 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
2464 internAtomError(-status
, "Read from server failed"));
2468 connection
->len
= srequest
->offset
;
2470 return httpServerIndirectHandlerCommon(connection
, status
);
2474 httpServerReadData(HTTPConnectionPtr connection
, int immediate
)
2476 HTTPRequestPtr request
= connection
->request
;
2477 ObjectPtr object
= request
->object
;
2480 assert(object
->flags
& OBJECT_INPROGRESS
);
2482 if(request
->request
== NULL
) {
2483 httpServerFinish(connection
, 1, 0);
2487 if(request
->to
>= 0)
2490 to
= object
->length
;
2492 if(to
>= 0 && to
== connection
->offset
) {
2493 httpServerFinish(connection
, 0, 0);
2497 if(connection
->len
== 0 &&
2498 ((connection
->te
== TE_IDENTITY
&& to
> connection
->offset
) ||
2499 (connection
->te
== TE_CHUNKED
&& connection
->chunk_remaining
> 0))) {
2500 /* Read directly into the object */
2501 int i
= connection
->offset
/ CHUNK_SIZE
;
2502 int j
= connection
->offset
% CHUNK_SIZE
;
2504 /* See httpServerDirectHandlerCommon if you change this */
2505 if(connection
->te
== TE_CHUNKED
) {
2506 len
= connection
->chunk_remaining
;
2507 /* The logic here is that we want more to just fit the
2508 chunk header if we're doing a large read, but do a
2509 large read if we would otherwise do a small one. The
2510 magic constant 2000 comes from the assumption that the
2511 server uses chunks that have a size that are a power of
2512 two (possibly including the chunk header), and that we
2513 want a full ethernet packet to fit into our read. */
2514 more
= (len
>= 2000 ? 20 : MIN(2048 - len
, CHUNK_SIZE
));
2516 len
= to
- connection
->offset
;
2517 /* We read more data only when there is a reasonable
2518 chance of there being another reply coming. */
2519 more
= (connection
->pipelined
> 1) ? CHUNK_SIZE
: 0;
2521 end
= len
+ connection
->offset
;
2523 httpConnectionDestroyBuf(connection
);
2525 /* The order of allocation is important in case we run out of
2527 lockChunk(object
, i
);
2528 if(object
->chunks
[i
].data
== NULL
)
2529 object
->chunks
[i
].data
= get_chunk();
2530 if(object
->chunks
[i
].data
&& object
->chunks
[i
].size
>= j
) {
2531 if(len
+ j
> CHUNK_SIZE
) {
2532 lockChunk(object
, i
+ 1);
2533 if(object
->chunks
[i
+ 1].data
== NULL
)
2534 object
->chunks
[i
+ 1].data
= get_chunk();
2535 /* Unless we're grabbing all len of data, we do not
2536 want to do an indirect read immediately afterwards. */
2537 if(more
&& len
+ j
<= 2 * CHUNK_SIZE
) {
2538 if(!connection
->buf
)
2539 connection
->buf
= get_chunk(); /* checked below */
2541 if(object
->chunks
[i
+ 1].data
) {
2542 do_stream_3(IO_READ
| IO_NOTNOW
, connection
->fd
, j
,
2543 object
->chunks
[i
].data
, CHUNK_SIZE
,
2544 object
->chunks
[i
+ 1].data
,
2546 end
- (i
+ 1) * CHUNK_SIZE
),
2547 connection
->buf
, connection
->buf
? more
: 0,
2548 httpServerDirectHandler2
, connection
);
2551 unlockChunk(object
, i
+ 1);
2553 if(more
&& len
+ j
<= CHUNK_SIZE
) {
2554 if(!connection
->buf
)
2555 connection
->buf
= get_chunk();
2557 do_stream_2(IO_READ
| IO_NOTNOW
, connection
->fd
, j
,
2558 object
->chunks
[i
].data
,
2559 MIN(CHUNK_SIZE
, end
- i
* CHUNK_SIZE
),
2560 connection
->buf
, connection
->buf
? more
: 0,
2561 httpServerDirectHandler
, connection
);
2564 unlockChunk(object
, i
);
2568 if(connection
->len
== 0)
2569 httpConnectionDestroyBuf(connection
);
2571 httpSetTimeout(connection
, serverTimeout
);
2572 do_stream_buf(IO_READ
| IO_NOTNOW
|
2573 ((immediate
&& connection
->len
) ? IO_IMMEDIATE
: 0),
2574 connection
->fd
, connection
->len
,
2576 (connection
->te
== TE_CHUNKED
?
2577 MIN(2048, CHUNK_SIZE
) : CHUNK_SIZE
),
2578 httpServerIndirectHandler
, connection
);
2583 httpServerDirectHandlerCommon(int kind
, int status
,
2584 FdEventHandlerPtr event
,
2585 StreamRequestPtr srequest
)
2587 HTTPConnectionPtr connection
= srequest
->data
;
2588 HTTPRequestPtr request
= connection
->request
;
2589 ObjectPtr object
= request
->object
;
2590 int i
= connection
->offset
/ CHUNK_SIZE
;
2593 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
2595 httpSetTimeout(connection
, -1);
2598 unlockChunk(object
, i
);
2599 if(kind
== 2) unlockChunk(object
, i
+ 1);
2600 if(status
!= -ECLIENTRESET
)
2601 do_log_error(L_ERROR
, -status
, "Read from server failed");
2602 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
2603 internAtomError(-status
, "Read from server failed"));
2607 /* We have incestuous knowledge of the decisions made in
2608 httpServerReadData */
2609 if(request
->to
>= 0)
2612 to
= object
->length
;
2613 if(connection
->te
== TE_CHUNKED
)
2614 end
= connection
->offset
+ connection
->chunk_remaining
;
2617 /* The amount of data actually read into the object */
2618 end1
= MIN(end
, i
* CHUNK_SIZE
+ MIN(kind
* CHUNK_SIZE
, srequest
->offset
));
2620 assert(end
>= 0 && end1
>= i
* CHUNK_SIZE
&& end1
<= (i
+ 2) * CHUNK_SIZE
);
2622 object
->chunks
[i
].size
=
2623 MAX(object
->chunks
[i
].size
, MIN(end1
- i
* CHUNK_SIZE
, CHUNK_SIZE
));
2624 if(kind
== 2 && end1
> (i
+ 1) * CHUNK_SIZE
) {
2625 object
->chunks
[i
+ 1].size
=
2626 MAX(object
->chunks
[i
+ 1].size
, end1
- (i
+ 1) * CHUNK_SIZE
);
2628 if(connection
->te
== TE_CHUNKED
) {
2629 connection
->chunk_remaining
-= (end1
- connection
->offset
);
2630 assert(connection
->chunk_remaining
>= 0);
2632 connection
->offset
= end1
;
2633 object
->size
= MAX(object
->size
, end1
);
2634 unlockChunk(object
, i
);
2635 if(kind
== 2) unlockChunk(object
, i
+ 1);
2637 if(i
* CHUNK_SIZE
+ srequest
->offset
> end1
) {
2638 connection
->len
= i
* CHUNK_SIZE
+ srequest
->offset
- end1
;
2639 return httpServerIndirectHandlerCommon(connection
, status
);
2641 notifyObject(object
);
2643 httpServerFinish(connection
, 1, 0);
2646 return httpServerReadData(connection
, 0);
2652 httpServerDirectHandler(int status
,
2653 FdEventHandlerPtr event
,
2654 StreamRequestPtr srequest
)
2656 return httpServerDirectHandlerCommon(1, status
, event
, srequest
);
2660 httpServerDirectHandler2(int status
,
2661 FdEventHandlerPtr event
,
2662 StreamRequestPtr srequest
)
2664 return httpServerDirectHandlerCommon(2, status
, event
, srequest
);
2667 /* Add the data accumulated in connection->buf into the object in
2668 connection->request. Returns 0 in the normal case, 1 if the TE is
2669 self-terminating and we're done, -1 if there was a problem with
2670 objectAddData, -2 if there was a problem with the data. */
2672 connectionAddData(HTTPConnectionPtr connection
, int skip
)
2674 HTTPRequestPtr request
= connection
->request
;
2675 ObjectPtr object
= request
->object
;
2678 if(connection
->te
== TE_IDENTITY
) {
2681 len
= connection
->len
- skip
;
2682 if(object
->length
>= 0) {
2683 len
= MIN(object
->length
- connection
->offset
, len
);
2685 if(request
->to
>= 0)
2686 len
= MIN(request
->to
- connection
->offset
, len
);
2688 rc
= objectAddData(object
, connection
->buf
+ skip
,
2689 connection
->offset
, len
);
2692 connection
->offset
+= len
;
2693 connection
->len
-= (len
+ skip
);
2694 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2695 (unsigned long)connection
, (unsigned long)object
,
2696 connection
->offset
);
2699 if(connection
->len
> 0 && skip
+ len
> 0) {
2700 memmove(connection
->buf
,
2701 connection
->buf
+ skip
+ len
, connection
->len
);
2704 if((object
->length
>= 0 && object
->length
<= connection
->offset
) ||
2705 (request
->to
>= 0 && request
->to
<= connection
->offset
)) {
2706 notifyObject(object
);
2710 notifyObject(object
);
2713 } else if(connection
->te
== TE_CHUNKED
) {
2714 int i
= skip
, j
, size
;
2715 /* connection->chunk_remaining is 0 at the end of a chunk, -1
2716 after the CR/LF pair ending a chunk, and -2 after we've
2717 seen a chunk of length 0. */
2718 if(connection
->chunk_remaining
> -2) {
2720 if(connection
->chunk_remaining
<= 0) {
2721 if(connection
->chunk_remaining
== 0) {
2722 if(connection
->len
< i
+ 2)
2724 if(connection
->buf
[i
] != '\r' ||
2725 connection
->buf
[i
+ 1] != '\n')
2728 connection
->chunk_remaining
= -1;
2730 if(connection
->len
< i
+ 2)
2732 j
= parseChunkSize(connection
->buf
, i
,
2733 connection
->len
, &size
);
2741 connection
->chunk_remaining
= -2;
2744 connection
->chunk_remaining
= size
;
2747 /* connection->chunk_remaining > 0 */
2748 size
= MIN(connection
->chunk_remaining
,
2749 connection
->len
- i
);
2752 rc
= objectAddData(object
, connection
->buf
+ i
,
2753 connection
->offset
, size
);
2754 connection
->offset
+= size
;
2758 connection
->chunk_remaining
-= size
;
2759 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2760 (unsigned long)connection
,
2761 (unsigned long)object
,
2762 connection
->offset
);
2766 connection
->len
-= i
;
2767 if(connection
->len
> 0)
2768 memmove(connection
->buf
, connection
->buf
+ i
, connection
->len
);
2769 if(i
> 0 || connection
->chunk_remaining
== -2)
2770 notifyObject(object
);
2771 if(connection
->chunk_remaining
== -2)
2781 listServers(FILE *out
)
2783 HTTPServerPtr server
;
2786 fprintf(out
, "<!DOCTYPE HTML PUBLIC "
2787 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2788 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2790 "\r\n<title>Known servers</title>\n"
2792 "<h1>Known servers</h1>\n");
2794 alternatingHttpStyle(out
, "servers");
2795 fprintf(out
, "<table id=servers>\n");
2796 fprintf(out
, "<thead><tr><th>Server</th>"
2798 "<th>Persistent</th>"
2800 "<th>Connections</th>"
2805 fprintf(out
, "<tbody>\n");
2809 fprintf(out
, "<tr class=\"%s\">", entry
% 2 == 0 ? "even" : "odd");
2810 if(server
->port
== 80)
2811 fprintf(out
, "<td>%s</td>", server
->name
);
2813 fprintf(out
, "<td>%s:%d</td>", server
->name
, server
->port
);
2815 if(server
->version
== HTTP_11
)
2816 fprintf(out
, "<td>1.1</td>");
2817 else if(server
->version
== HTTP_10
)
2818 fprintf(out
, "<td>1.0</td>");
2820 fprintf(out
, "<td>unknown</td>");
2822 if(server
->persistent
< 0)
2823 fprintf(out
, "<td>no</td>");
2824 else if(server
->persistent
> 0)
2825 fprintf(out
, "<td>yes</td>");
2827 fprintf(out
, "<td>unknown</td>");
2829 if(server
->version
!= HTTP_11
|| server
->persistent
<= 0)
2830 fprintf(out
, "<td></td>");
2831 else if(server
->pipeline
< 0)
2832 fprintf(out
, "<td>no</td>");
2833 else if(server
->pipeline
>= 0 && server
->pipeline
<= 1)
2834 fprintf(out
, "<td>unknown</td>");
2835 else if(server
->pipeline
== 2 || server
->pipeline
== 3)
2836 fprintf(out
, "<td>probing</td>");
2838 fprintf(out
, "<td>yes</td>");
2841 for(i
= 0; i
< server
->maxslots
; i
++)
2842 if(server
->connection
[i
] && !server
->connection
[i
]->connecting
) {
2843 if(i
< server
->numslots
)
2849 fprintf(out
, "<td>%d/%d", n
, server
->numslots
);
2851 fprintf(out
, " + %d</td>", m
);
2853 fprintf(out
, "</td>");
2855 if(server
->lies
> 0)
2856 fprintf(out
, "<td>(%d lies)</td>", (server
->lies
+ 9) / 10);
2858 fprintf(out
, "<td></td>");
2861 fprintf(out
, "<td>%.3f</td>", (double)server
->rtt
/ 1000000.0);
2863 fprintf(out
, "<td></td>");
2864 if(server
->rate
> 0)
2865 fprintf(out
, "<td>%d</td>", server
->rate
);
2867 fprintf(out
, "<td></td>");
2869 fprintf(out
, "</tr>\n");
2870 server
= server
->next
;
2873 fprintf(out
, "</tbody>\n");
2874 fprintf(out
, "</table>\n");
2875 fprintf(out
, "<p><a href=\"/polipo/\">back</a></p>");
2876 fprintf(out
, "</body></html>\n");