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 if(client
->flags
& CONN_READER
) {
1005 client
->flags
|= CONN_SIDE_READER
;
1006 do_stream(IO_READ
| IO_IMMEDIATE
| IO_NOTNOW
,
1007 client
->fd
, 0, NULL
, 0,
1008 httpClientSideHandler
, client
);
1010 } else if(!(request
->flags
& REQUEST_WAIT_CONTINUE
) && doflush
) {
1011 /* Make sure there's a reqbuf, as httpServerFinish uses
1012 it to determine if there's a writer. */
1013 if(connection
->reqbuf
== NULL
)
1014 connection
->reqbuf
= get_chunk();
1015 assert(connection
->reqbuf
!= NULL
);
1018 client
->reqbuf
+ client
->reqbegin
, len
,
1019 httpServerSideHandler
, connection
);
1021 if(connection
->reqbuf
) {
1022 httpConnectionDestroyReqbuf(connection
);
1023 connection
->reqlen
= 0;
1025 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
1026 ConditionHandlerPtr chandler
;
1027 do_log(D_SERVER_CONN
, "W... %s:%d.\n",
1028 connection
->server
->name
, connection
->server
->port
);
1030 conditionWait(&request
->object
->condition
,
1031 httpServerContinueConditionHandler
,
1032 sizeof(connection
), &connection
);
1036 do_log(L_ERROR
, "Couldn't register condition handler.\n");
1037 /* Fall through -- the client side will clean up. */
1039 client
->flags
|= CONN_SIDE_READER
;
1040 do_stream(IO_READ
| (done
? IO_IMMEDIATE
: 0 ) | IO_NOTNOW
,
1041 client
->fd
, client
->reqlen
,
1042 client
->reqbuf
, CHUNK_SIZE
,
1043 httpClientSideHandler
, client
);
1049 httpClientDelayedDoSideHandler(TimeEventHandlerPtr event
)
1051 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1052 httpServerDoSide(connection
);
1057 httpServerDelayedDoSide(HTTPConnectionPtr connection
)
1059 TimeEventHandlerPtr handler
;
1060 handler
= scheduleTimeEvent(1, httpClientDelayedDoSideHandler
,
1061 sizeof(connection
), &connection
);
1063 do_log(L_ERROR
, "Couldn't schedule DoSide -- freeing memory.\n");
1064 free_chunk_arenas();
1065 handler
= scheduleTimeEvent(1, httpClientDelayedDoSideHandler
,
1066 sizeof(connection
), &connection
);
1067 do_log(L_ERROR
, "Couldn't schedule DoSide.\n");
1068 /* Somebody will hopefully end up timing out. */
1075 httpServerSideHandlerCommon(int kind
, int status
,
1076 FdEventHandlerPtr event
,
1077 StreamRequestPtr srequest
)
1079 HTTPConnectionPtr connection
= srequest
->data
;
1080 HTTPRequestPtr request
= connection
->request
;
1081 HTTPRequestPtr requestor
= request
->request
;
1082 HTTPConnectionPtr client
= requestor
->connection
;
1085 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1088 do_log_error(L_ERROR
, -status
, "Couldn't write to server");
1089 httpConnectionDestroyReqbuf(connection
);
1090 if(status
!= -ECLIENTRESET
)
1091 shutdown(connection
->fd
, 2);
1092 abortObject(request
->object
, 503,
1093 internAtom("Couldn't write to server"));
1094 /* Let the read side handle the error */
1095 httpServerDoSide(connection
);
1099 assert(srequest
->offset
> 0);
1102 if(srequest
->offset
< connection
->reqlen
)
1104 bodylen
= srequest
->offset
- connection
->reqlen
;
1105 connection
->reqlen
= 0;
1106 httpConnectionDestroyReqbuf(connection
);
1108 bodylen
= srequest
->offset
;
1112 assert(client
->reqbegin
+ bodylen
<= client
->reqlen
);
1114 if(client
->reqlen
> client
->reqbegin
+ bodylen
)
1115 memmove(client
->reqbuf
, client
->reqbuf
+ client
->reqbegin
+ bodylen
,
1116 client
->reqlen
- client
->reqbegin
- bodylen
);
1117 client
->reqlen
-= bodylen
+ client
->reqbegin
;
1118 client
->reqbegin
= 0;
1119 connection
->reqoffset
+= bodylen
;
1120 httpServerDoSide(connection
);
1125 httpServerSideHandler(int status
,
1126 FdEventHandlerPtr event
,
1127 StreamRequestPtr srequest
)
1129 return httpServerSideHandlerCommon(1, status
, event
, srequest
);
1133 httpServerSideHandler2(int status
,
1134 FdEventHandlerPtr event
,
1135 StreamRequestPtr srequest
)
1137 return httpServerSideHandlerCommon(2, status
, event
, srequest
);
1141 httpServerContinueConditionHandler(int status
, ConditionHandlerPtr chandler
)
1143 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)chandler
->data
;
1145 if(connection
->request
->flags
& REQUEST_WAIT_CONTINUE
)
1147 httpServerDelayedDoSide(connection
);
1151 /* s is 0 to keep the connection alive, 1 to shutdown the connection,
1152 and -1 to keep the connection alive and keep the current request. */
1154 httpServerFinish(HTTPConnectionPtr connection
, int s
, int offset
)
1156 HTTPServerPtr server
= connection
->server
;
1157 HTTPRequestPtr request
= connection
->request
;
1161 assert(connection
->pipelined
>= 1);
1162 assert((connection
->pipelined
> 1) == (request
->next
!= NULL
));
1164 assert(connection
->pipelined
== 0);
1167 if(s
== 0 && (!connection
->request
||
1168 !(connection
->request
->flags
& REQUEST_PERSISTENT
)))
1171 if(connection
->serviced
>= maxConnectionRequests
||
1172 connection
->time
< current_time
.tv_sec
- maxConnectionAge
)
1175 if(connection
->reqbuf
) {
1176 /* As most normal requests go out in a single packet, this is
1177 extremely unlikely to happen. As for POST/PUT requests,
1178 they are not pipelined, so this can only happen if the
1179 server sent an error reply early. */
1180 assert(connection
->fd
>= 0);
1181 shutdown(connection
->fd
, 1);
1182 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLOUT
);
1183 httpServerDelayedFinish(connection
);
1187 if(s
>= 0 && request
) {
1188 /* Update statistics about the server */
1189 int size
= -1, d
= -1, rtt
= -1, rate
= -1;
1190 if(connection
->offset
> 0 && request
->from
>= 0)
1191 size
= connection
->offset
- request
->from
;
1192 if(request
->time1
.tv_sec
!= null_time
.tv_sec
) {
1193 d
= timeval_minus_usec(¤t_time
, &request
->time1
);
1194 if(!(request
->flags
& REQUEST_PIPELINED
) &&
1195 request
->time0
.tv_sec
!= null_time
.tv_sec
)
1196 rtt
= timeval_minus_usec(&request
->time1
, &request
->time0
);
1197 if(size
>= 8192 && d
> 50000)
1198 rate
= ((double)size
/ (double)d
) * 1000000.0 + 0.5;
1200 request
->time0
= null_time
;
1201 request
->time1
= null_time
;
1204 if(server
->rtt
>= 0)
1205 server
->rtt
= (3 * server
->rtt
+ rtt
+ 2) / 4;
1210 if(server
->rate
>= 0)
1211 server
->rate
= (3 * server
->rate
+ rate
+ 2) / 4;
1213 server
->rate
= rate
;
1216 httpDequeueRequest(connection
);
1217 connection
->pipelined
--;
1218 request
->object
->flags
&= ~(OBJECT_INPROGRESS
| OBJECT_VALIDATING
);
1219 if(request
->request
) {
1220 request
->request
->request
= NULL
;
1221 request
->request
= NULL
;
1223 releaseNotifyObject(request
->object
);
1224 request
->object
= NULL
;
1225 httpDestroyRequest(request
);
1228 do_log(D_SERVER_CONN
, "Done with server %s:%d connection (%d)\n",
1229 connection
->server
->name
, connection
->server
->port
, s
);
1231 assert(offset
<= connection
->len
);
1234 if(offset
< connection
->len
) {
1235 assert(connection
->buf
!= NULL
);
1236 if(!connection
->pipelined
) {
1238 "Closing connection to %s:%d: "
1239 "%d stray bytes of data.\n",
1240 server
->name
, server
->port
, connection
->len
- offset
);
1243 memmove(connection
->buf
, connection
->buf
+ offset
,
1244 connection
->len
- offset
);
1245 connection
->len
= connection
->len
- offset
;
1246 if((connection
->flags
& CONN_BIGBUF
) &&
1247 connection
->len
<= CHUNK_SIZE
)
1248 httpConnectionUnbigify(connection
);
1251 connection
->len
= 0;
1255 connection
->server
->time
= current_time
.tv_sec
;
1256 connection
->serviced
++;
1259 if(connection
->timeout
)
1260 cancelTimeEvent(connection
->timeout
);
1261 connection
->timeout
= NULL
;
1262 httpConnectionDestroyBuf(connection
);
1263 if(connection
->fd
>= 0)
1264 CLOSE(connection
->fd
);
1265 connection
->fd
= -1;
1266 server
->persistent
-= 1;
1267 if(server
->persistent
< -5)
1268 server
->numslots
= MIN(server
->maxslots
, serverMaxSlots
);
1269 if(connection
->request
) {
1271 do_log(D_SERVER_CONN
, "Restarting pipeline to %s:%d.\n",
1272 server
->name
, server
->port
);
1273 if(server
->pipeline
== 2)
1274 server
->pipeline
-= 20;
1276 server
->pipeline
-= 5;
1277 req
= connection
->request
;
1279 req
->connection
= NULL
;
1283 connection
->request_last
->next
= server
->request
;
1285 server
->request_last
= connection
->request_last
;
1286 server
->request
= connection
->request
;
1287 connection
->request
= NULL
;
1288 connection
->request_last
= NULL
;
1290 /* Make sure we don't get confused into thinking a probe
1292 if(server
->pipeline
== 2 || server
->pipeline
== 3)
1293 server
->pipeline
= 1;
1294 for(i
= 0; i
< server
->maxslots
; i
++)
1295 if(connection
== server
->connection
[i
])
1297 assert(i
< server
->maxslots
);
1298 if(server
->idleHandler
[i
])
1299 unregisterFdEvent(server
->idleHandler
[i
]);
1300 server
->idleHandler
[i
] = NULL
;
1301 server
->connection
[i
] = NULL
;
1304 server
->persistent
+= 1;
1305 if(server
->persistent
> 0)
1306 server
->numslots
= MIN(server
->maxslots
,
1307 server
->version
== HTTP_10
?
1308 serverSlots1
: serverSlots
);
1309 httpSetTimeout(connection
, serverTimeout
);
1310 /* See httpServerTrigger */
1311 if(connection
->pipelined
||
1312 (server
->version
== HTTP_11
&& server
->pipeline
<= 0) ||
1313 (server
->pipeline
== 3)) {
1316 if(s
< 0 || connection
->pipelined
) {
1317 httpServerReply(connection
, 1);
1319 httpConnectionDestroyBuf(connection
);
1324 httpServerTrigger(server
);
1328 httpServerDelayedFinishHandler(TimeEventHandlerPtr event
)
1330 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1331 httpServerFinish(connection
, 1, 0);
1336 httpServerDelayedFinish(HTTPConnectionPtr connection
)
1338 TimeEventHandlerPtr handler
;
1340 handler
= scheduleTimeEvent(1, httpServerDelayedFinishHandler
,
1341 sizeof(connection
), &connection
);
1344 "Couldn't schedule delayed finish -- freeing memory.");
1345 free_chunk_arenas();
1346 handler
= scheduleTimeEvent(1, httpServerDelayedFinishHandler
,
1347 sizeof(connection
), &connection
);
1350 "Couldn't schedule delayed finish -- aborting.\n");
1357 httpServerReply(HTTPConnectionPtr connection
, int immediate
)
1359 assert(connection
->pipelined
> 0);
1361 if(connection
->request
->request
== NULL
) {
1362 do_log(L_WARN
, "Aborting pipeline on %s:%d.\n",
1363 connection
->server
->name
, connection
->server
->port
);
1364 httpServerFinish(connection
, 1, 0);
1368 do_log(D_SERVER_CONN
, "R: ");
1369 do_log_n(D_SERVER_CONN
, connection
->request
->object
->key
,
1370 connection
->request
->object
->key_size
);
1371 do_log(D_SERVER_CONN
, " (%d)\n", connection
->request
->method
);
1373 if(connection
->len
== 0)
1374 httpConnectionDestroyBuf(connection
);
1376 httpSetTimeout(connection
, serverTimeout
);
1377 do_stream_buf(IO_READ
| (immediate
? IO_IMMEDIATE
: 0) | IO_NOTNOW
,
1378 connection
->fd
, connection
->len
,
1379 &connection
->buf
, CHUNK_SIZE
,
1380 httpServerReplyHandler
, connection
);
1384 httpConnectionPipelined(HTTPConnectionPtr connection
)
1386 HTTPRequestPtr request
= connection
->request
;
1390 request
= request
->next
;
1396 httpServerUnpipeline(HTTPRequestPtr request
)
1398 HTTPConnectionPtr connection
= request
->connection
;
1399 HTTPServerPtr server
= connection
->server
;
1401 request
->flags
&= ~REQUEST_PERSISTENT
;
1405 "Restarting pipeline to %s:%d.\n",
1406 connection
->server
->name
, connection
->server
->port
);
1407 req
= request
->next
;
1409 req
->connection
= NULL
;
1413 connection
->request_last
->next
= server
->request
;
1415 server
->request_last
= connection
->request_last
;
1416 server
->request
= request
->next
;
1417 request
->next
= NULL
;
1418 connection
->request_last
= request
;
1420 connection
->pipelined
= httpConnectionPipelined(connection
);
1424 httpServerRestart(HTTPConnectionPtr connection
)
1426 HTTPServerPtr server
= connection
->server
;
1427 HTTPRequestPtr request
= connection
->request
;
1433 "Restarting pipeline to %s:%d.\n",
1434 connection
->server
->name
, connection
->server
->port
);
1437 req
->connection
= NULL
;
1441 connection
->request_last
->next
= server
->request
;
1443 server
->request_last
= connection
->request_last
;
1444 server
->request
= request
;
1445 connection
->request
= NULL
;
1446 connection
->request_last
= NULL
;
1448 connection
->pipelined
= 0;
1449 httpServerFinish(connection
, 1, 0);
1453 httpServerRequest(ObjectPtr object
, int method
, int from
, int to
,
1454 HTTPRequestPtr requestor
, void *closure
)
1461 assert(from
>= 0 && (to
< 0 || to
> from
));
1462 assert(closure
== NULL
);
1463 assert(!(object
->flags
& OBJECT_LOCAL
));
1464 assert(object
->type
== OBJECT_HTTP
);
1466 if(object
->flags
& OBJECT_INPROGRESS
)
1469 if(requestor
->flags
& REQUEST_REQUESTED
)
1472 assert(requestor
->request
== NULL
);
1477 rc
= parseUrl(object
->key
, object
->key_size
, &x
, &y
, &port
, &z
);
1479 if(rc
< 0 || x
< 0 || y
< 0 || y
- x
> 131) {
1480 do_log(L_ERROR
, "Couldn't parse URL: ");
1481 do_log_n(L_ERROR
, object
->key
, object
->key_size
);
1482 do_log(L_ERROR
, "\n");
1483 abortObject(object
, 400, internAtom("Couldn't parse URL"));
1484 notifyObject(object
);
1488 if(!intListMember(port
, allowedPorts
)) {
1489 do_log(L_ERROR
, "Attempted connection to port %d.\n", port
);
1490 abortObject(object
, 403, internAtom("Forbidden port"));
1491 notifyObject(object
);
1495 memcpy(name
, ((char*)object
->key
) + x
, y
- x
);
1498 requestor
->flags
|= REQUEST_REQUESTED
;
1499 rc
= httpMakeServerRequest(name
, port
, object
, method
, from
, to
,
1504 503, internAtom("Couldn't schedule server request"));
1505 notifyObject(object
);
1513 httpWriteRequest(HTTPConnectionPtr connection
, HTTPRequestPtr request
,
1516 ObjectPtr object
= request
->object
;
1517 int from
= request
->from
, to
= request
->to
, method
= request
->method
;
1518 char *url
= object
->key
, *m
;
1519 int url_size
= object
->key_size
;
1520 int x
, y
, port
, z
, location_size
;
1522 int l
, n
, rc
, bufsize
;
1524 assert(method
!= METHOD_NONE
);
1526 if(request
->method
== METHOD_GET
||
1527 request
->method
== METHOD_CONDITIONAL_GET
) {
1531 do_log(L_ERROR
, "Requesting empty segment?\n");
1536 if(object
->flags
& OBJECT_DYNAMIC
) {
1540 objectFillFromDisk(object
, from
/ CHUNK_SIZE
* CHUNK_SIZE
, 1);
1541 l
= objectHoleSize(request
->object
, from
);
1543 if(to
<= 0 || to
> from
+ l
)
1547 if(pmmSize
&& connection
->server
->pipeline
>= 4) {
1549 to
= to
< 0 ? pmmFirstSize
: MIN(to
, pmmFirstSize
);
1551 to
= to
< 0 ? from
+ pmmSize
: MIN(to
, from
+ pmmSize
);
1554 if(from
% CHUNK_SIZE
!= 0)
1555 if(objectHoleSize(object
, from
- 1) != 0)
1556 from
= from
/ CHUNK_SIZE
* CHUNK_SIZE
;
1560 rc
= parseUrl(url
, url_size
, &x
, &y
, &port
, &z
);
1562 if(rc
< 0 || x
< 0 || y
< 0) {
1566 if(connection
->reqbuf
== NULL
) {
1567 connection
->reqbuf
= get_chunk();
1568 if(connection
->reqbuf
== NULL
)
1570 connection
->reqlen
= 0;
1573 if(method
== METHOD_CONDITIONAL_GET
&&
1574 object
->last_modified
< 0 && object
->etag
== NULL
)
1575 method
= request
->method
= METHOD_GET
;
1579 (connection
->flags
& CONN_BIGREQBUF
) ? bigBufferSize
: CHUNK_SIZE
;
1580 n
= connection
->reqlen
;
1583 case METHOD_CONDITIONAL_GET
: m
= "GET"; break;
1584 case METHOD_HEAD
: m
= "HEAD"; break;
1585 case METHOD_POST
: m
= "POST"; break;
1586 case METHOD_PUT
: m
= "PUT"; break;
1589 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, "%s ", m
);
1591 if(connection
->server
->isProxy
) {
1592 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
,
1595 if(url_size
- z
== 0) {
1600 location_size
= url_size
- z
;
1603 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
,
1604 location
, location_size
);
1607 do_log(D_SERVER_REQ
, "Server request: ");
1608 do_log_n(D_SERVER_REQ
, url
+ x
, y
- x
);
1609 do_log(D_SERVER_REQ
, ": ");
1610 do_log_n(D_SERVER_REQ
, connection
->reqbuf
, n
);
1611 do_log(D_SERVER_REQ
, " (method %d from %d to %d, 0x%lx for 0x%lx)\n",
1613 (unsigned long)connection
, (unsigned long)object
);
1615 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, " HTTP/1.1");
1617 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, "\r\nHost: ");
1618 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
, url
+ x
, y
- x
);
1620 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, ":%d", port
);
1622 if(connection
->server
->isProxy
&& parentAuthCredentials
) {
1623 n
= buildServerAuthHeaders(connection
->reqbuf
, n
, bufsize
,
1624 parentAuthCredentials
);
1628 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1629 "\r\nContent-Length: %d", bodylen
);
1631 if(request
->flags
& REQUEST_WAIT_CONTINUE
)
1632 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1633 "\r\nExpect: 100-continue");
1635 if(method
!= METHOD_HEAD
&& (from
> 0 || to
>= 0)) {
1637 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1638 "\r\nRange: bytes=%d-%d", from
, to
- 1);
1640 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1641 "\r\nRange: bytes=%d-", from
);
1645 if(method
== METHOD_GET
&& object
->etag
&& (from
> 0 || to
>= 0)) {
1646 if(request
->request
&& request
->request
->request
== request
&&
1647 request
->request
->from
== 0 && request
->request
->to
== -1 &&
1648 pmmSize
== 0 && pmmFirstSize
== 0)
1649 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1650 "\r\nIf-Range: \"%s\"", object
->etag
);
1653 if(method
== METHOD_CONDITIONAL_GET
) {
1654 if(object
->last_modified
>= 0) {
1655 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1656 "\r\nIf-Modified-Since: ");
1657 n
= format_time(connection
->reqbuf
, n
, bufsize
,
1658 object
->last_modified
);
1661 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1662 "\r\nIf-None-Match: \"%s\"", object
->etag
);
1666 n
= httpPrintCacheControl(connection
->reqbuf
, n
, bufsize
,
1667 0, &request
->cache_control
);
1671 if(request
->request
&& request
->request
->headers
) {
1672 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
,
1673 request
->request
->headers
->string
,
1674 request
->request
->headers
->length
);
1677 if(request
->request
&& request
->request
->via
) {
1678 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1679 "\r\nVia: %s, 1.1 %s",
1680 request
->request
->via
->string
, proxyName
->string
);
1682 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1688 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1689 "\r\nConnection: %s\r\n\r\n",
1690 (request
->flags
& REQUEST_PERSISTENT
) ?
1691 "keep-alive" : "close");
1692 if(n
< 0 || n
>= bufsize
- 1)
1694 connection
->reqlen
= n
;
1699 if(!(connection
->flags
& CONN_BIGREQBUF
))
1700 rc
= httpConnectionBigifyReqbuf(connection
);
1707 httpServerHandler(int status
,
1708 FdEventHandlerPtr event
,
1709 StreamRequestPtr srequest
)
1711 HTTPConnectionPtr connection
= srequest
->data
;
1714 assert(connection
->request
->object
->flags
& OBJECT_INPROGRESS
);
1716 if(connection
->reqlen
== 0) {
1717 do_log(D_SERVER_REQ
, "Writing aborted on 0x%lx\n",
1718 (unsigned long)connection
);
1722 if(status
== 0 && !streamRequestDone(srequest
)) {
1723 httpSetTimeout(connection
, serverTimeout
);
1727 httpConnectionDestroyReqbuf(connection
);
1730 if(connection
->serviced
>= 1) {
1731 httpServerRestart(connection
);
1734 if(status
>= 0 || status
== ECONNRESET
) {
1735 message
= internAtom("Couldn't send request to server: "
1738 if(status
!= -EPIPE
)
1739 do_log_error(L_ERROR
, -status
,
1740 "Couldn't send request to server");
1742 internAtomError(-status
, "Couldn't send request to server");
1750 dispose_chunk(connection
->reqbuf
);
1751 connection
->reqbuf
= NULL
;
1752 shutdown(connection
->fd
, 2);
1753 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLIN
);
1754 httpSetTimeout(connection
, 60);
1759 httpServerSendRequest(HTTPConnectionPtr connection
)
1761 assert(connection
->server
);
1763 if(connection
->reqlen
== 0) {
1764 do_log(D_SERVER_REQ
,
1765 "Writing aborted on 0x%lx\n", (unsigned long)connection
);
1766 httpConnectionDestroyReqbuf(connection
);
1767 shutdown(connection
->fd
, 2);
1768 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLIN
| POLLOUT
);
1772 httpSetTimeout(connection
, serverTimeout
);
1773 do_stream(IO_WRITE
, connection
->fd
, 0,
1774 connection
->reqbuf
, connection
->reqlen
,
1775 httpServerHandler
, connection
);
1780 httpServerReplyHandler(int status
,
1781 FdEventHandlerPtr event
,
1782 StreamRequestPtr srequest
)
1784 HTTPConnectionPtr connection
= srequest
->data
;
1785 HTTPRequestPtr request
= connection
->request
;
1788 (connection
->flags
& CONN_BIGBUF
) ? bigBufferSize
: CHUNK_SIZE
;
1790 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1792 if(connection
->serviced
>= 1) {
1793 httpServerRestart(connection
);
1796 if(status
!= -ECLIENTRESET
)
1797 do_log_error(L_ERROR
, -status
, "Read from server failed");
1798 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
1799 internAtomError(-status
, "Read from server failed"));
1803 i
= findEndOfHeaders(connection
->buf
, 0, srequest
->offset
, &body
);
1804 connection
->len
= srequest
->offset
;
1807 request
->time1
= current_time
;
1808 return httpServerHandlerHeaders(status
, event
, srequest
, connection
);
1812 if(connection
->serviced
>= 1) {
1813 httpServerRestart(connection
);
1818 "Error reading server headers: %d\n", -status
);
1819 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
1820 internAtomError(-status
,
1821 "Error reading server headers"));
1823 httpServerAbort(connection
, 1, 502,
1824 internAtom("Server dropped connection"));
1828 if(connection
->len
>= bufsize
) {
1830 if(!(connection
->flags
& CONN_BIGBUF
))
1831 rc
= httpConnectionBigify(connection
);
1833 do_log(L_ERROR
, "Couldn't find end of server's headers.\n");
1834 httpServerAbort(connection
, 1, 502,
1835 internAtom("Couldn't find end "
1836 "of server's headers"));
1839 do_log(L_ERROR
, "Couldn't allocate big buffer.\n");
1840 httpServerAbort(connection
, 1, 500,
1841 internAtom("Couldn't allocate big buffer"));
1844 /* Can't just return 0 -- buf has moved. */
1846 connection
->fd
, connection
->len
,
1847 connection
->buf
, bigBufferSize
,
1848 httpServerReplyHandler
, connection
);
1856 httpServerHandlerHeaders(int eof
,
1857 FdEventHandlerPtr event
,
1858 StreamRequestPtr srequest
,
1859 HTTPConnectionPtr connection
)
1861 HTTPRequestPtr request
= connection
->request
;
1862 ObjectPtr object
= request
->object
;
1869 CacheControlRec cache_control
;
1871 time_t date
, last_modified
, expires
;
1872 struct timeval
*init_time
;
1874 AtomPtr via
, new_via
;
1876 HTTPRangeRec content_range
;
1877 ObjectPtr new_object
= NULL
, old_object
= NULL
;
1879 AtomPtr message
= NULL
;
1883 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1886 httpSetTimeout(connection
, -1);
1888 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
1889 do_log(D_SERVER_CONN
, "W %s:%d.\n",
1890 connection
->server
->name
, connection
->server
->port
);
1891 request
->flags
&= ~REQUEST_WAIT_CONTINUE
;
1894 rc
= httpParseServerFirstLine(connection
->buf
, &code
, &version
, &message
);
1896 do_log(L_ERROR
, "Couldn't parse server status line.\n");
1897 httpServerAbort(connection
, 1, 502,
1898 internAtom("Couldn't parse server status line"));
1902 do_log(D_SERVER_REQ
, "Server status: ");
1903 do_log_n(D_SERVER_REQ
, connection
->buf
,
1904 connection
->buf
[rc
- 1] == '\r' ? rc
- 2 : rc
- 2);
1905 do_log(D_SERVER_REQ
, " (0x%lx for 0x%lx)\n",
1906 (unsigned long)connection
, (unsigned long)object
);
1908 if(version
!= HTTP_10
&& version
!= HTTP_11
) {
1909 do_log(L_ERROR
, "Unknown server HTTP version\n");
1910 httpServerAbort(connection
, 1, 502,
1911 internAtom("Unknown server HTTP version"));
1912 releaseAtom(message
);
1916 connection
->version
= version
;
1917 connection
->server
->version
= version
;
1918 request
->flags
|= REQUEST_PERSISTENT
;
1920 url
= internAtomN(object
->key
, object
->key_size
);
1921 rc
= httpParseHeaders(0, url
, connection
->buf
, rc
, request
,
1922 &headers
, &len
, &cache_control
, NULL
, &te
,
1923 &date
, &last_modified
, &expires
, NULL
, NULL
, NULL
,
1924 &age
, &etag
, NULL
, NULL
, &content_range
,
1927 do_log(L_ERROR
, "Couldn't parse server headers\n");
1929 releaseAtom(message
);
1930 httpServerAbort(connection
, 1, 502,
1931 internAtom("Couldn't parse server headers"));
1936 date
= current_time
.tv_sec
;
1940 releaseAtom(message
);
1941 /* We've already reset wait_continue above, but we must still
1942 ensure that the writer notices. */
1943 notifyObject(request
->object
);
1944 httpServerFinish(connection
, -1, rc
);
1949 httpServerAbort(connection
, 1, 501,
1950 internAtom("Upgrade not implemented"));
1954 if(via
&& !checkVia(proxyName
, via
)) {
1955 httpServerAbort(connection
, 1, 504, internAtom("Proxy loop detected"));
1958 full_len
= content_range
.full_length
;
1961 if(content_range
.from
== -1 || content_range
.to
== -1) {
1962 do_log(L_ERROR
, "Partial content without range.\n");
1963 httpServerAbort(connection
, 1, 502,
1964 internAtom("Partial content without range"));
1967 if(len
>= 0 && len
!= content_range
.to
- content_range
.from
) {
1968 do_log(L_ERROR
, "Inconsistent partial content.\n");
1969 httpServerAbort(connection
, 1, 502,
1970 internAtom("Inconsistent partial content"));
1973 } else if(code
< 400 &&
1974 (content_range
.from
>= 0 || content_range
.to
>= 0 ||
1975 content_range
.full_length
>= 0)) {
1976 do_log(L_WARN
, "Range without partial content.\n");
1978 content_range
.from
= -1;
1979 content_range
.to
= -1;
1980 content_range
.full_length
= -1;
1981 } else if(code
!= 304 && code
!= 412) {
1985 if(te
!= TE_IDENTITY
&& te
!= TE_CHUNKED
) {
1986 do_log(L_ERROR
, "Unsupported transfer-encoding\n");
1987 httpServerAbort(connection
, 1, 502,
1988 internAtom("Unsupported transfer-encoding"));
1993 if(request
->method
!= METHOD_CONDITIONAL_GET
) {
1994 do_log(L_ERROR
, "Unexpected \"not changed\" reply from server\n");
1995 httpServerAbort(connection
, 1, 502,
1996 internAtom("Unexpected \"not changed\" "
1997 "reply from server"));
2000 if(object
->etag
&& !etag
) {
2001 /* RFC 2616 10.3.5. Violated by some front-end proxies. */
2002 do_log(L_WARN
, "\"Not changed\" reply with no ETag.\n");
2007 if(request
->method
!= METHOD_CONDITIONAL_GET
||
2008 (!object
->etag
&& !object
->last_modified
)) {
2010 "Unexpected \"precondition failed\" reply from server.\n");
2011 httpServerAbort(connection
, 1, 502,
2012 internAtom("Unexpected \"precondition failed\" "
2013 "reply from server"));
2020 /* Okay, we're going to accept this reply. */
2022 if((code
== 200 || code
== 206 || code
== 304 || code
== 412) &&
2023 (cache_control
.flags
& (CACHE_NO
| CACHE_NO_STORE
) ||
2024 cache_control
.max_age
== 0 ||
2025 (cacheIsShared
&& cache_control
.s_maxage
== 0) ||
2026 (expires
>= 0 && expires
<= object
->age
))) {
2027 do_log(L_UNCACHEABLE
, "Uncacheable object ");
2028 do_log_n(L_UNCACHEABLE
, object
->key
, object
->key_size
);
2029 do_log(L_UNCACHEABLE
, " (%d)\n", cache_control
.flags
);
2032 if(request
->time0
.tv_sec
!= null_time
.tv_sec
)
2033 init_time
= &request
->time0
;
2035 init_time
= ¤t_time
;
2036 age
= MIN(init_time
->tv_sec
- age
, init_time
->tv_sec
);
2038 if(request
->method
== METHOD_HEAD
||
2039 code
< 200 || code
== 204 || code
== 304)
2041 else if(te
== TE_IDENTITY
)
2042 expect_body
= (len
!= 0);
2046 connection
->chunk_remaining
= -1;
2047 connection
->te
= te
;
2049 old_object
= object
;
2051 connection
->server
->lies
--;
2053 if(object
->cache_control
& CACHE_MISMATCH
)
2056 if(code
== 304 || code
== 412) {
2057 if((object
->etag
&& etag
&& strcmp(object
->etag
, etag
) != 0) ||
2058 (object
->last_modified
>= 0 && last_modified
>= 0 &&
2059 object
->last_modified
!= last_modified
)) {
2060 do_log(L_ERROR
, "Inconsistent \"%s\" reply for ",
2061 code
== 304 ? "not changed":"precondition failed");
2062 do_log_n(L_ERROR
, object
->key
, object
->key_size
);
2063 do_log(L_ERROR
, "\n");
2064 object
->flags
|= OBJECT_DYNAMIC
;
2067 } else if(!(object
->flags
& OBJECT_INITIAL
)) {
2068 if((object
->last_modified
< 0 || last_modified
< 0) &&
2069 (!object
->etag
|| !etag
))
2071 else if(object
->last_modified
!= last_modified
)
2073 else if(object
->etag
|| etag
) {
2074 /* We need to be permissive here so as to deal with some
2075 front-end proxies that discard ETags on partial
2076 replies but not on full replies. */
2077 if(etag
&& object
->etag
&& strcmp(object
->etag
, etag
) != 0)
2079 else if(!object
->etag
)
2083 if(!supersede
&& (object
->cache_control
& CACHE_VARY
) &&
2084 dontTrustVaryETag
>= 1) {
2085 /* Check content-type to work around mod_gzip bugs */
2086 if(!httpHeaderMatch(atomContentType
, object
->headers
, headers
) ||
2087 !httpHeaderMatch(atomContentEncoding
, object
->headers
, headers
))
2091 if(full_len
< 0 && te
== TE_IDENTITY
) {
2092 /* It's an HTTP/1.0 CGI. Be afraid. */
2093 if(expect_body
&& content_range
.from
< 0 && content_range
.to
< 0)
2097 if(!supersede
&& object
->length
>= 0 && full_len
>= 0 &&
2098 object
->length
!= full_len
) {
2099 do_log(L_WARN
, "Inconsistent length.\n");
2104 ((object
->last_modified
>= 0 && last_modified
>= 0) ||
2105 (object
->etag
&& etag
))) {
2106 if(request
->method
== METHOD_CONDITIONAL_GET
) {
2107 do_log(L_WARN
, "Server ignored conditional request.\n");
2108 connection
->server
->lies
+= 10;
2109 /* Drop the connection? */
2112 } else if(code
== 416) {
2113 do_log(L_ERROR
, "Unexpected \"range not satisfiable\" reply\n");
2114 httpServerAbort(connection
, 1, 502,
2115 internAtom("Unexpected \"range not satisfiable\" "
2117 /* The object may be superseded. Make sure the next request
2118 won't be partial. */
2119 abortObject(object
, 502,
2120 internAtom("Unexpected \"range not satisfiable\" reply"));
2124 if(object
->flags
& OBJECT_INITIAL
)
2128 do_log(L_SUPERSEDED
, "Superseding object: ");
2129 do_log_n(L_SUPERSEDED
, old_object
->key
, old_object
->key_size
);
2130 do_log(L_SUPERSEDED
, " (%d %d %d %s -> %d %d %d %s)\n",
2131 object
->code
, object
->length
, (int)object
->last_modified
,
2132 object
->etag
?object
->etag
: "(none)",
2133 code
, full_len
, (int)last_modified
,
2134 etag
?etag
:"(none)");
2135 privatiseObject(old_object
, 0);
2136 new_object
= makeObject(object
->type
, object
->key
,
2137 object
->key_size
, 1, 0,
2138 object
->request
, NULL
);
2139 if(new_object
== NULL
) {
2140 do_log(L_ERROR
, "Couldn't allocate object\n");
2141 httpServerAbort(connection
, 1, 500,
2142 internAtom("Couldn't allocate object"));
2145 if(urlIsLocal(new_object
->key
, new_object
->key_size
))
2146 new_object
->flags
|= OBJECT_LOCAL
;
2148 new_object
= object
;
2152 (!etag
&& last_modified
< 0) ||
2153 (cache_control
.flags
&
2154 (CACHE_NO_HIDDEN
| CACHE_NO
| CACHE_NO_STORE
|
2155 (cacheIsShared
? CACHE_PRIVATE
: 0))) ||
2156 (cache_control
.max_age
>= 0 && cache_control
.max_age
<= 2) ||
2158 cache_control
.s_maxage
>= 0 && cache_control
.s_maxage
<= 5) ||
2159 (old_object
->last_modified
>= 0 && old_object
->expires
>= 0 &&
2160 (old_object
->expires
- old_object
->last_modified
<= 1)) ||
2161 (supersede
&& (old_object
->date
- date
<= 5));
2164 new_object
->flags
|= OBJECT_DYNAMIC
;
2166 new_object
->flags
&= ~OBJECT_DYNAMIC
;
2167 else if(old_object
->flags
& OBJECT_DYNAMIC
)
2168 new_object
->flags
|= OBJECT_DYNAMIC
;
2170 new_object
->age
= age
;
2171 new_object
->cache_control
|= cache_control
.flags
;
2172 new_object
->max_age
= cache_control
.max_age
;
2173 new_object
->s_maxage
= cache_control
.s_maxage
;
2174 new_object
->flags
&= ~OBJECT_FAILED
;
2177 new_object
->date
= date
;
2178 if(last_modified
>= 0)
2179 new_object
->last_modified
= last_modified
;
2181 new_object
->expires
= expires
;
2182 if(new_object
->etag
== NULL
)
2183 new_object
->etag
= etag
;
2189 case 300: case 301: case 302: case 303: case 307:
2190 case 403: case 404: case 405: case 401:
2191 if(new_object
->message
) releaseAtom(new_object
->message
);
2192 new_object
->code
= code
;
2193 new_object
->message
= message
;
2195 case 206: case 304: case 412:
2196 if(new_object
->code
!= 200 || !new_object
->message
) {
2197 if(new_object
->message
) releaseAtom(new_object
->message
);
2198 new_object
->code
= 200;
2199 new_object
->message
= internAtom("OK");
2201 releaseAtom(message
);
2204 if(new_object
->message
) releaseAtom(new_object
->message
);
2205 new_object
->code
= code
;
2206 new_object
->message
= retainAtom(message
);
2210 httpTweakCachability(new_object
);
2213 new_via
= internAtomF("%s %s",
2214 version
== HTTP_11
? "1.1" : "1.0",
2217 new_via
= internAtomF("%s, %s %s", via
->string
,
2218 version
== HTTP_11
? "1.1" : "1.0",
2220 if(new_via
== NULL
) {
2221 do_log(L_ERROR
, "Couldn't allocate Via.\n");
2223 if(new_object
->via
) releaseAtom(new_object
->via
);
2224 new_object
->via
= new_via
;
2227 if(new_object
->flags
& OBJECT_INITIAL
) {
2228 objectPartial(new_object
, full_len
, headers
);
2230 if(new_object
->length
< 0)
2231 new_object
->length
= full_len
;
2232 /* XXX -- RFC 2616 13.5.3 */
2233 releaseAtom(headers
);
2237 assert(new_object
!= old_object
);
2238 supersedeObject(old_object
);
2241 if(new_object
!= old_object
) {
2242 if(new_object
->flags
& OBJECT_INPROGRESS
) {
2243 /* Make sure we don't fetch this object two times at the
2244 same time. Just drop the connection. */
2245 releaseObject(new_object
);
2246 httpServerFinish(connection
, 1, 0);
2249 old_object
->flags
&= ~OBJECT_VALIDATING
;
2250 new_object
->flags
|= OBJECT_INPROGRESS
;
2251 /* Signal the client side to switch to the new object -- see
2252 httpClientGetHandler. If it doesn't, we'll give up on this
2254 new_object
->flags
|= OBJECT_MUTATING
;
2255 request
->can_mutate
= new_object
;
2256 notifyObject(old_object
);
2257 request
->can_mutate
= NULL
;
2258 new_object
->flags
&= ~OBJECT_MUTATING
;
2259 old_object
->flags
&= ~OBJECT_INPROGRESS
;
2260 if(request
->object
== old_object
) {
2261 if(request
->request
)
2262 request
->request
->request
= NULL
;
2263 request
->request
= NULL
;
2264 request
->object
= new_object
;
2266 assert(request
->object
== new_object
);
2268 releaseNotifyObject(old_object
);
2270 object
= new_object
;
2272 objectMetadataChanged(new_object
, 0);
2275 if(object
->flags
& OBJECT_VALIDATING
) {
2276 object
->flags
&= ~OBJECT_VALIDATING
;
2277 notifyObject(object
);
2281 httpServerFinish(connection
, 0, rc
);
2285 if(request
->request
== NULL
) {
2286 httpServerFinish(connection
, 1, 0);
2291 /* 412 replies contain a useless body. For now, we
2292 drop the connection. */
2293 httpServerFinish(connection
, 1, 0);
2298 if(request
->flags
& REQUEST_PERSISTENT
) {
2299 if(request
->method
!= METHOD_HEAD
&&
2300 connection
->te
== TE_IDENTITY
&& len
< 0) {
2301 do_log(L_ERROR
, "Persistent reply with no Content-Length\n");
2302 /* That's potentially dangerous, as we could start reading
2303 arbitrary data into the object. Unfortunately, some
2305 request
->flags
&= ~REQUEST_PERSISTENT
;
2309 /* we're getting a body */
2310 if(content_range
.from
> 0)
2311 connection
->offset
= content_range
.from
;
2313 connection
->offset
= 0;
2315 if(content_range
.to
>= 0)
2316 request
->to
= content_range
.to
;
2318 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2319 (unsigned long)connection
, (unsigned long)object
,
2320 connection
->offset
);
2322 if(connection
->len
> rc
) {
2323 rc
= connectionAddData(connection
, rc
);
2327 do_log(L_ERROR
, "Couldn't parse chunk size.\n");
2328 httpServerAbort(connection
, 1, 502,
2329 internAtom("Couldn't parse chunk size"));
2331 do_log(L_ERROR
, "Couldn't add data to connection.\n");
2332 httpServerAbort(connection
, 1, 500,
2333 internAtom("Couldn't add data "
2339 if(object
->length
< 0) {
2340 object
->length
= object
->size
;
2341 objectMetadataChanged(object
, 0);
2342 } else if(object
->length
!= object
->size
) {
2343 httpServerAbort(connection
, 1, 500,
2344 internAtom("Inconsistent "
2346 object
->length
= -1;
2350 httpServerFinish(connection
, 0, 0);
2355 connection
->len
= 0;
2359 if(connection
->te
== TE_CHUNKED
||
2360 (object
->length
>= 0 &&
2361 connection
->offset
< object
->length
)) {
2362 do_log(L_ERROR
, "Server closed connection.\n");
2363 httpServerAbort(connection
, 1, 502,
2364 internAtom("Server closed connection"));
2367 if(code
!= 206 && eof
> 0 && object
->length
< 0) {
2368 object
->length
= object
->size
;
2369 objectMetadataChanged(object
, 0);
2371 httpServerFinish(connection
, 1, 0);
2375 return httpServerReadData(connection
, 1);
2381 releaseAtom(message
);
2383 releaseAtom(headers
);
2392 httpServerIndirectHandlerCommon(HTTPConnectionPtr connection
, int eof
)
2394 HTTPRequestPtr request
= connection
->request
;
2397 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
2399 if(connection
->len
> 0) {
2401 rc
= connectionAddData(connection
, 0);
2405 do_log(L_ERROR
, "Couldn't parse chunk size.\n");
2406 httpServerAbort(connection
, 1, 502,
2407 internAtom("Couldn't parse chunk size"));
2409 do_log(L_ERROR
, "Couldn't add data to connection.\n");
2410 httpServerAbort(connection
, 1, 500,
2411 internAtom("Couldn't add data "
2416 if(request
->to
< 0) {
2417 if(request
->object
->length
< 0) {
2418 request
->object
->length
= request
->object
->size
;
2419 objectMetadataChanged(request
->object
, 0);
2420 } else if(request
->object
->length
!=
2421 request
->object
->size
) {
2422 request
->object
->length
= -1;
2423 httpServerAbort(connection
, 1, 502,
2424 internAtom("Inconsistent "
2429 httpServerFinish(connection
, 0, 0);
2435 if(eof
&& connection
->len
== 0) {
2436 if(connection
->te
== TE_CHUNKED
||
2437 (request
->to
>= 0 && connection
->offset
< request
->to
)) {
2438 do_log(L_ERROR
, "Server dropped connection.\n");
2439 httpServerAbort(connection
, 1, 502,
2440 internAtom("Server dropped connection"));
2443 if(request
->object
->length
< 0 && eof
> 0 &&
2444 (request
->to
< 0 || request
->to
> request
->object
->size
)) {
2445 request
->object
->length
= request
->object
->size
;
2446 objectMetadataChanged(request
->object
, 0);
2448 httpServerFinish(connection
, 1, 0);
2452 return httpServerReadData(connection
, 0);
2457 httpServerIndirectHandler(int status
,
2458 FdEventHandlerPtr event
,
2459 StreamRequestPtr srequest
)
2461 HTTPConnectionPtr connection
= srequest
->data
;
2462 assert(connection
->request
->object
->flags
& OBJECT_INPROGRESS
);
2464 httpSetTimeout(connection
, -1);
2466 if(status
!= -ECLIENTRESET
)
2467 do_log_error(L_ERROR
, -status
, "Read from server failed");
2468 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
2469 internAtomError(-status
, "Read from server failed"));
2473 connection
->len
= srequest
->offset
;
2475 return httpServerIndirectHandlerCommon(connection
, status
);
2479 httpServerReadData(HTTPConnectionPtr connection
, int immediate
)
2481 HTTPRequestPtr request
= connection
->request
;
2482 ObjectPtr object
= request
->object
;
2485 assert(object
->flags
& OBJECT_INPROGRESS
);
2487 if(request
->request
== NULL
) {
2488 httpServerFinish(connection
, 1, 0);
2492 if(request
->to
>= 0)
2495 to
= object
->length
;
2497 if(to
>= 0 && to
== connection
->offset
) {
2498 httpServerFinish(connection
, 0, 0);
2502 if(connection
->len
== 0 &&
2503 ((connection
->te
== TE_IDENTITY
&& to
> connection
->offset
) ||
2504 (connection
->te
== TE_CHUNKED
&& connection
->chunk_remaining
> 0))) {
2505 /* Read directly into the object */
2506 int i
= connection
->offset
/ CHUNK_SIZE
;
2507 int j
= connection
->offset
% CHUNK_SIZE
;
2509 /* See httpServerDirectHandlerCommon if you change this */
2510 if(connection
->te
== TE_CHUNKED
) {
2511 len
= connection
->chunk_remaining
;
2512 /* The logic here is that we want more to just fit the
2513 chunk header if we're doing a large read, but do a
2514 large read if we would otherwise do a small one. The
2515 magic constant 2000 comes from the assumption that the
2516 server uses chunks that have a size that are a power of
2517 two (possibly including the chunk header), and that we
2518 want a full ethernet packet to fit into our read. */
2519 more
= (len
>= 2000 ? 20 : MIN(2048 - len
, CHUNK_SIZE
));
2521 len
= to
- connection
->offset
;
2522 /* We read more data only when there is a reasonable
2523 chance of there being another reply coming. */
2524 more
= (connection
->pipelined
> 1) ? CHUNK_SIZE
: 0;
2526 end
= len
+ connection
->offset
;
2528 httpConnectionDestroyBuf(connection
);
2530 /* The order of allocation is important in case we run out of
2532 lockChunk(object
, i
);
2533 if(object
->chunks
[i
].data
== NULL
)
2534 object
->chunks
[i
].data
= get_chunk();
2535 if(object
->chunks
[i
].data
&& object
->chunks
[i
].size
>= j
) {
2536 if(len
+ j
> CHUNK_SIZE
) {
2537 lockChunk(object
, i
+ 1);
2538 if(object
->chunks
[i
+ 1].data
== NULL
)
2539 object
->chunks
[i
+ 1].data
= get_chunk();
2540 /* Unless we're grabbing all len of data, we do not
2541 want to do an indirect read immediately afterwards. */
2542 if(more
&& len
+ j
<= 2 * CHUNK_SIZE
) {
2543 if(!connection
->buf
)
2544 connection
->buf
= get_chunk(); /* checked below */
2546 if(object
->chunks
[i
+ 1].data
) {
2547 do_stream_3(IO_READ
| IO_NOTNOW
, connection
->fd
, j
,
2548 object
->chunks
[i
].data
, CHUNK_SIZE
,
2549 object
->chunks
[i
+ 1].data
,
2551 end
- (i
+ 1) * CHUNK_SIZE
),
2552 connection
->buf
, connection
->buf
? more
: 0,
2553 httpServerDirectHandler2
, connection
);
2556 unlockChunk(object
, i
+ 1);
2558 if(more
&& len
+ j
<= CHUNK_SIZE
) {
2559 if(!connection
->buf
)
2560 connection
->buf
= get_chunk();
2562 do_stream_2(IO_READ
| IO_NOTNOW
, connection
->fd
, j
,
2563 object
->chunks
[i
].data
,
2564 MIN(CHUNK_SIZE
, end
- i
* CHUNK_SIZE
),
2565 connection
->buf
, connection
->buf
? more
: 0,
2566 httpServerDirectHandler
, connection
);
2569 unlockChunk(object
, i
);
2573 if(connection
->len
== 0)
2574 httpConnectionDestroyBuf(connection
);
2576 httpSetTimeout(connection
, serverTimeout
);
2577 do_stream_buf(IO_READ
| IO_NOTNOW
|
2578 ((immediate
&& connection
->len
) ? IO_IMMEDIATE
: 0),
2579 connection
->fd
, connection
->len
,
2581 (connection
->te
== TE_CHUNKED
?
2582 MIN(2048, CHUNK_SIZE
) : CHUNK_SIZE
),
2583 httpServerIndirectHandler
, connection
);
2588 httpServerDirectHandlerCommon(int kind
, int status
,
2589 FdEventHandlerPtr event
,
2590 StreamRequestPtr srequest
)
2592 HTTPConnectionPtr connection
= srequest
->data
;
2593 HTTPRequestPtr request
= connection
->request
;
2594 ObjectPtr object
= request
->object
;
2595 int i
= connection
->offset
/ CHUNK_SIZE
;
2598 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
2600 httpSetTimeout(connection
, -1);
2603 unlockChunk(object
, i
);
2604 if(kind
== 2) unlockChunk(object
, i
+ 1);
2605 if(status
!= -ECLIENTRESET
)
2606 do_log_error(L_ERROR
, -status
, "Read from server failed");
2607 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
2608 internAtomError(-status
, "Read from server failed"));
2612 /* We have incestuous knowledge of the decisions made in
2613 httpServerReadData */
2614 if(request
->to
>= 0)
2617 to
= object
->length
;
2618 if(connection
->te
== TE_CHUNKED
)
2619 end
= connection
->offset
+ connection
->chunk_remaining
;
2622 /* The amount of data actually read into the object */
2623 end1
= MIN(end
, i
* CHUNK_SIZE
+ MIN(kind
* CHUNK_SIZE
, srequest
->offset
));
2625 assert(end
>= 0 && end1
>= i
* CHUNK_SIZE
&& end1
<= (i
+ 2) * CHUNK_SIZE
);
2627 object
->chunks
[i
].size
=
2628 MAX(object
->chunks
[i
].size
, MIN(end1
- i
* CHUNK_SIZE
, CHUNK_SIZE
));
2629 if(kind
== 2 && end1
> (i
+ 1) * CHUNK_SIZE
) {
2630 object
->chunks
[i
+ 1].size
=
2631 MAX(object
->chunks
[i
+ 1].size
, end1
- (i
+ 1) * CHUNK_SIZE
);
2633 if(connection
->te
== TE_CHUNKED
) {
2634 connection
->chunk_remaining
-= (end1
- connection
->offset
);
2635 assert(connection
->chunk_remaining
>= 0);
2637 connection
->offset
= end1
;
2638 object
->size
= MAX(object
->size
, end1
);
2639 unlockChunk(object
, i
);
2640 if(kind
== 2) unlockChunk(object
, i
+ 1);
2642 if(i
* CHUNK_SIZE
+ srequest
->offset
> end1
) {
2643 connection
->len
= i
* CHUNK_SIZE
+ srequest
->offset
- end1
;
2644 return httpServerIndirectHandlerCommon(connection
, status
);
2646 notifyObject(object
);
2648 httpServerFinish(connection
, 1, 0);
2651 return httpServerReadData(connection
, 0);
2657 httpServerDirectHandler(int status
,
2658 FdEventHandlerPtr event
,
2659 StreamRequestPtr srequest
)
2661 return httpServerDirectHandlerCommon(1, status
, event
, srequest
);
2665 httpServerDirectHandler2(int status
,
2666 FdEventHandlerPtr event
,
2667 StreamRequestPtr srequest
)
2669 return httpServerDirectHandlerCommon(2, status
, event
, srequest
);
2672 /* Add the data accumulated in connection->buf into the object in
2673 connection->request. Returns 0 in the normal case, 1 if the TE is
2674 self-terminating and we're done, -1 if there was a problem with
2675 objectAddData, -2 if there was a problem with the data. */
2677 connectionAddData(HTTPConnectionPtr connection
, int skip
)
2679 HTTPRequestPtr request
= connection
->request
;
2680 ObjectPtr object
= request
->object
;
2683 if(connection
->te
== TE_IDENTITY
) {
2686 len
= connection
->len
- skip
;
2687 if(object
->length
>= 0) {
2688 len
= MIN(object
->length
- connection
->offset
, len
);
2690 if(request
->to
>= 0)
2691 len
= MIN(request
->to
- connection
->offset
, len
);
2693 rc
= objectAddData(object
, connection
->buf
+ skip
,
2694 connection
->offset
, len
);
2697 connection
->offset
+= len
;
2698 connection
->len
-= (len
+ skip
);
2699 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2700 (unsigned long)connection
, (unsigned long)object
,
2701 connection
->offset
);
2704 if(connection
->len
> 0 && skip
+ len
> 0) {
2705 memmove(connection
->buf
,
2706 connection
->buf
+ skip
+ len
, connection
->len
);
2709 if((object
->length
>= 0 && object
->length
<= connection
->offset
) ||
2710 (request
->to
>= 0 && request
->to
<= connection
->offset
)) {
2711 notifyObject(object
);
2715 notifyObject(object
);
2718 } else if(connection
->te
== TE_CHUNKED
) {
2719 int i
= skip
, j
, size
;
2720 /* connection->chunk_remaining is 0 at the end of a chunk, -1
2721 after the CR/LF pair ending a chunk, and -2 after we've
2722 seen a chunk of length 0. */
2723 if(connection
->chunk_remaining
> -2) {
2725 if(connection
->chunk_remaining
<= 0) {
2726 if(connection
->chunk_remaining
== 0) {
2727 if(connection
->len
< i
+ 2)
2729 if(connection
->buf
[i
] != '\r' ||
2730 connection
->buf
[i
+ 1] != '\n')
2733 connection
->chunk_remaining
= -1;
2735 if(connection
->len
< i
+ 2)
2737 j
= parseChunkSize(connection
->buf
, i
,
2738 connection
->len
, &size
);
2746 connection
->chunk_remaining
= -2;
2749 connection
->chunk_remaining
= size
;
2752 /* connection->chunk_remaining > 0 */
2753 size
= MIN(connection
->chunk_remaining
,
2754 connection
->len
- i
);
2757 rc
= objectAddData(object
, connection
->buf
+ i
,
2758 connection
->offset
, size
);
2759 connection
->offset
+= size
;
2763 connection
->chunk_remaining
-= size
;
2764 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2765 (unsigned long)connection
,
2766 (unsigned long)object
,
2767 connection
->offset
);
2771 connection
->len
-= i
;
2772 if(connection
->len
> 0)
2773 memmove(connection
->buf
, connection
->buf
+ i
, connection
->len
);
2774 if(i
> 0 || connection
->chunk_remaining
== -2)
2775 notifyObject(object
);
2776 if(connection
->chunk_remaining
== -2)
2786 listServers(FILE *out
)
2788 HTTPServerPtr server
;
2791 fprintf(out
, "<!DOCTYPE HTML PUBLIC "
2792 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2793 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2795 "\r\n<title>Known servers</title>\n"
2797 "<h1>Known servers</h1>\n");
2799 alternatingHttpStyle(out
, "servers");
2800 fprintf(out
, "<table id=servers>\n");
2801 fprintf(out
, "<thead><tr><th>Server</th>"
2803 "<th>Persistent</th>"
2805 "<th>Connections</th>"
2810 fprintf(out
, "<tbody>\n");
2814 fprintf(out
, "<tr class=\"%s\">", entry
% 2 == 0 ? "even" : "odd");
2815 if(server
->port
== 80)
2816 fprintf(out
, "<td>%s</td>", server
->name
);
2818 fprintf(out
, "<td>%s:%d</td>", server
->name
, server
->port
);
2820 if(server
->version
== HTTP_11
)
2821 fprintf(out
, "<td>1.1</td>");
2822 else if(server
->version
== HTTP_10
)
2823 fprintf(out
, "<td>1.0</td>");
2825 fprintf(out
, "<td>unknown</td>");
2827 if(server
->persistent
< 0)
2828 fprintf(out
, "<td>no</td>");
2829 else if(server
->persistent
> 0)
2830 fprintf(out
, "<td>yes</td>");
2832 fprintf(out
, "<td>unknown</td>");
2834 if(server
->version
!= HTTP_11
|| server
->persistent
<= 0)
2835 fprintf(out
, "<td></td>");
2836 else if(server
->pipeline
< 0)
2837 fprintf(out
, "<td>no</td>");
2838 else if(server
->pipeline
>= 0 && server
->pipeline
<= 1)
2839 fprintf(out
, "<td>unknown</td>");
2840 else if(server
->pipeline
== 2 || server
->pipeline
== 3)
2841 fprintf(out
, "<td>probing</td>");
2843 fprintf(out
, "<td>yes</td>");
2846 for(i
= 0; i
< server
->maxslots
; i
++)
2847 if(server
->connection
[i
] && !server
->connection
[i
]->connecting
) {
2848 if(i
< server
->numslots
)
2854 fprintf(out
, "<td>%d/%d", n
, server
->numslots
);
2856 fprintf(out
, " + %d</td>", m
);
2858 fprintf(out
, "</td>");
2860 if(server
->lies
> 0)
2861 fprintf(out
, "<td>(%d lies)</td>", (server
->lies
+ 9) / 10);
2863 fprintf(out
, "<td></td>");
2866 fprintf(out
, "<td>%.3f</td>", (double)server
->rtt
/ 1000000.0);
2868 fprintf(out
, "<td></td>");
2869 if(server
->rate
> 0)
2870 fprintf(out
, "<td>%d</td>", server
->rate
);
2872 fprintf(out
, "<td></td>");
2874 fprintf(out
, "</tr>\n");
2875 server
= server
->next
;
2878 fprintf(out
, "</tbody>\n");
2879 fprintf(out
, "</table>\n");
2880 fprintf(out
, "<p><a href=\"/polipo/\">back</a></p>");
2881 fprintf(out
, "</body></html>\n");