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;
36 int serverMaxSlots
= 8;
37 int dontCacheRedirects
= 0;
39 static HTTPServerPtr servers
= 0;
41 static int httpServerContinueConditionHandler(int, ConditionHandlerPtr
);
42 static int initParentProxy(void);
43 static int parentProxySetter(ConfigVariablePtr var
, void *value
);
44 static void httpServerDelayedFinish(HTTPConnectionPtr
);
49 CONFIG_VARIABLE_SETTABLE(parentProxy
, CONFIG_ATOM_LOWER
, parentProxySetter
,
50 "Parent proxy (host:port).");
51 CONFIG_VARIABLE(serverExpireTime
, CONFIG_TIME
,
52 "Time during which server data is valid.");
53 CONFIG_VARIABLE_SETTABLE(smallRequestTime
, CONFIG_TIME
, configIntSetter
,
54 "Estimated time for a small request.");
55 CONFIG_VARIABLE_SETTABLE(replyUnpipelineTime
, CONFIG_TIME
, configIntSetter
,
56 "Estimated time for a pipeline break.");
57 CONFIG_VARIABLE_SETTABLE(replyUnpipelineSize
, CONFIG_INT
, configIntSetter
,
58 "Size for a pipeline break.");
59 CONFIG_VARIABLE_SETTABLE(pipelineAdditionalRequests
, CONFIG_TRISTATE
,
61 "Pipeline requests on an active connection.");
62 CONFIG_VARIABLE_SETTABLE(maxPipelineTrain
, CONFIG_INT
,
64 "Maximum number of requests "
65 "pipelined at a time.");
66 CONFIG_VARIABLE(pmmFirstSize
, CONFIG_INT
,
67 "The size of the first PMM chunk.");
68 CONFIG_VARIABLE(pmmSize
, CONFIG_INT
,
69 "The size of a PMM chunk.");
70 CONFIG_VARIABLE(serverSlots
, CONFIG_INT
,
71 "Maximum number of connections per server.");
72 CONFIG_VARIABLE(serverMaxSlots
, CONFIG_INT
,
73 "Maximum number of connections per broken server.");
74 CONFIG_VARIABLE(dontCacheRedirects
, CONFIG_BOOLEAN
,
75 "If true, don't cache redirects.");
79 parentProxySetter(ConfigVariablePtr var
, void *value
)
81 configAtomSetter(var
, value
);
87 discardServer(HTTPServerPtr server
)
89 HTTPServerPtr previous
;
90 assert(!server
->request
);
93 servers
= server
->next
;
96 while(previous
->next
!= server
)
97 previous
= previous
->next
;
98 previous
->next
= server
->next
;
105 httpServerIdle(HTTPServerPtr server
)
110 for(i
= 0; i
< server
->maxslots
; i
++)
111 if(server
->connection
[i
])
117 expireServersHandler(TimeEventHandlerPtr event
)
119 HTTPServerPtr server
, next
;
120 TimeEventHandlerPtr e
;
124 if(httpServerIdle(server
) &&
125 server
->time
+ serverExpireTime
< current_time
.tv_sec
)
126 discardServer(server
);
129 e
= scheduleTimeEvent(serverExpireTime
/ 60 + 60,
130 expireServersHandler
, 0, NULL
);
132 do_log(L_ERROR
, "Couldn't schedule server expiry.\n");
141 if(size
< CHUNK_SIZE
)
142 return 1 << log2_ceil(pmmSize
);
143 else if(size
> CHUNK_SIZE
)
144 return (size
+ CHUNK_SIZE
- 1) / CHUNK_SIZE
* CHUNK_SIZE
;
152 AtomPtr host
, port_atom
;
156 releaseAtom(parentHost
);
162 if(parentProxy
!= NULL
&& parentProxy
->length
== 0) {
163 releaseAtom(parentProxy
);
167 if(parentProxy
== NULL
)
170 rc
= atomSplit(parentProxy
, ':', &host
, &port_atom
);
172 do_log(L_ERROR
, "Couldn't parse parentProxy.");
173 releaseAtom(parentProxy
);
178 port
= atoi(port_atom
->string
);
179 if(port
<= 0 || port
>= 0x10000) {
181 releaseAtom(port_atom
);
182 do_log(L_ERROR
, "Couldn't parse parentProxy.");
183 releaseAtom(parentProxy
);
196 TimeEventHandlerPtr event
;
199 if(pmmFirstSize
|| pmmSize
) {
200 if(pmmSize
== 0) pmmSize
= pmmFirstSize
;
201 if(pmmFirstSize
== 0) pmmFirstSize
= pmmSize
;
202 pmmSize
= roundSize(pmmSize
);
203 pmmFirstSize
= roundSize(pmmFirstSize
);
206 if(serverMaxSlots
< 1)
210 if(serverSlots
> serverMaxSlots
)
211 serverSlots
= serverMaxSlots
;
215 event
= scheduleTimeEvent(serverExpireTime
/ 60 + 60, expireServersHandler
,
218 do_log(L_ERROR
, "Couldn't schedule server expiry.\n");
224 getServer(char *name
, int port
, int proxy
)
226 HTTPServerPtr server
;
231 if(strcmp(server
->name
, name
) == 0 && server
->port
== port
&&
232 server
->isProxy
== proxy
) {
233 if(httpServerIdle(server
) &&
234 server
->time
+ serverExpireTime
< current_time
.tv_sec
) {
235 discardServer(server
);
239 server
->time
= current_time
.tv_sec
;
243 server
= server
->next
;
246 server
= malloc(sizeof(HTTPServerRec
));
248 do_log(L_ERROR
, "Couldn't allocate server.\n");
252 server
->connection
= malloc(serverMaxSlots
* sizeof(HTTPConnectionPtr
));
253 if(server
->connection
== NULL
) {
254 do_log(L_ERROR
, "Couldn't allocate server.\n");
259 server
->idleHandler
= malloc(serverMaxSlots
* sizeof(FdEventHandlerPtr
));
260 if(server
->connection
== NULL
) {
261 do_log(L_ERROR
, "Couldn't allocate server.\n");
262 free(server
->connection
);
267 server
->maxslots
= serverMaxSlots
;
269 server
->name
= strdup(name
);
270 if(server
->name
== NULL
) {
271 do_log(L_ERROR
, "Couldn't allocate server name.\n");
277 server
->addrindex
= 0;
278 server
->isProxy
= proxy
;
279 server
->version
= HTTP_UNKNOWN
;
280 server
->persistent
= 0;
281 server
->pipeline
= 0;
282 server
->time
= current_time
.tv_sec
;
285 server
->numslots
= MIN(serverSlots
, server
->maxslots
);
286 for(i
= 0; i
< server
->maxslots
; i
++) {
287 server
->connection
[i
] = NULL
;
288 server
->idleHandler
[i
] = NULL
;
290 server
->request
= NULL
;
291 server
->request_last
= NULL
;
294 server
->next
= servers
;
300 httpServerQueueRequest(HTTPServerPtr server
, HTTPRequestPtr request
)
302 assert(request
->request
&& request
->request
->request
== request
);
303 assert(request
->connection
== NULL
);
304 if(server
->request
) {
305 server
->request_last
->next
= request
;
306 server
->request_last
= request
;
308 server
->request_last
= request
;
309 server
->request
= request
;
315 httpServerAbort(HTTPConnectionPtr connection
, int fail
,
316 int code
, AtomPtr message
)
318 HTTPRequestPtr request
= connection
->request
;
320 if(request
->request
) {
321 httpClientError(request
->request
, code
, retainAtom(message
));
324 request
->object
->flags
|= OBJECT_FAILED
;
325 if(request
->object
->flags
& OBJECT_INITIAL
)
326 abortObject(request
->object
, code
, retainAtom(message
));
327 notifyObject(request
->object
);
330 releaseAtom(message
);
331 if(!connection
->connecting
)
332 httpServerFinish(connection
, 1, 0);
336 httpServerAbortRequest(HTTPRequestPtr request
, int fail
,
337 int code
, AtomPtr message
)
339 if(request
->connection
&& request
== request
->connection
->request
) {
340 httpServerAbort(request
->connection
, fail
, code
, message
);
342 HTTPRequestPtr requestor
= request
->request
;
344 requestor
->request
= NULL
;
345 request
->request
= NULL
;
346 httpClientError(requestor
, code
, retainAtom(message
));
349 request
->object
->flags
|= OBJECT_FAILED
;
350 if(request
->object
->flags
& OBJECT_INITIAL
)
351 abortObject(request
->object
, code
, retainAtom(message
));
352 notifyObject(request
->object
);
354 releaseAtom(message
);
359 httpServerClientReset(HTTPRequestPtr request
)
361 if(request
->connection
&&
362 request
->connection
->fd
>= 0 &&
363 !request
->connection
->connecting
&&
364 request
->connection
->request
== request
)
365 pokeFdEvent(request
->connection
->fd
, -ECLIENTRESET
, POLLIN
| POLLOUT
);
370 httpMakeServerRequest(char *name
, int port
, ObjectPtr object
,
371 int method
, int from
, int to
, HTTPRequestPtr requestor
)
373 HTTPServerPtr server
;
374 HTTPRequestPtr request
;
377 assert(!(object
->flags
& OBJECT_INPROGRESS
));
380 server
= getServer(parentHost
->string
, parentPort
, 1);
382 server
= getServer(name
, port
, 0);
384 if(server
== NULL
) return -1;
386 object
->flags
|= OBJECT_INPROGRESS
;
387 object
->requestor
= requestor
;
389 request
= httpMakeRequest();
391 do_log(L_ERROR
, "Couldn't allocate request.\n");
395 /* Because we allocate objects in chunks, we cannot have data that
396 doesn't start at a chunk boundary. */
397 if(from
% CHUNK_SIZE
!= 0) {
398 objectFillFromDisk(object
, from
/ CHUNK_SIZE
* CHUNK_SIZE
, 1);
399 if(objectHoleSize(object
, from
- 1) != 0)
400 from
= from
/ CHUNK_SIZE
* CHUNK_SIZE
;
403 request
->object
= retainObject(object
);
404 request
->method
= method
;
405 if(method
== METHOD_CONDITIONAL_GET
) {
407 request
->method
= METHOD_HEAD
;
411 (expectContinue
? (requestor
->flags
& REQUEST_WAIT_CONTINUE
) : 0);
412 request
->from
= from
;
414 request
->request
= requestor
;
415 requestor
->request
= request
;
416 request
->cache_control
= requestor
->cache_control
;
417 request
->time0
= null_time
;
418 request
->time1
= null_time
;
420 rc
= httpServerQueueRequest(server
, request
);
422 do_log(L_ERROR
, "Couldn't queue request.\n");
423 request
->request
= NULL
;
424 requestor
->request
= NULL
;
425 object
->flags
&= ~(OBJECT_INPROGRESS
| OBJECT_VALIDATING
);
426 releaseNotifyObject(object
);
427 httpDestroyRequest(request
);
431 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
432 if(server
->version
== HTTP_10
) {
433 httpServerAbortRequest(request
, 1,
434 417, internAtom("Expectation failed"));
437 } else if(expectContinue
>= 2 && server
->version
== HTTP_11
) {
438 if(request
->method
== METHOD_POST
|| request
->method
== METHOD_PUT
)
439 request
->flags
|= REQUEST_WAIT_CONTINUE
;
443 rc
= httpServerTrigger(server
);
445 /* We must be very short on memory. If there are any requests
446 queued, we abort one and try again. If there aren't, we
448 do_log(L_ERROR
, "Couldn't trigger server -- out of memory?\n");
449 if(server
->request
) {
450 httpServerAbortRequest(server
->request
, 1, 503,
451 internAtom("Couldn't trigger server"));
459 httpServerConnection(HTTPServerPtr server
)
461 HTTPConnectionPtr connection
;
464 connection
= httpMakeConnection();
465 if(connection
== NULL
) {
466 do_log(L_ERROR
, "Couldn't allocate server connection.\n");
469 connection
->server
= server
;
471 for(i
= 0; i
< server
->numslots
; i
++) {
472 if(!server
->connection
[i
]) {
473 server
->connection
[i
] = connection
;
477 assert(i
< server
->numslots
);
479 connection
->request
= NULL
;
480 connection
->request_last
= NULL
;
482 do_log(D_SERVER_CONN
, "C... %s:%d.\n",
483 connection
->server
->name
, connection
->server
->port
);
484 httpSetTimeout(connection
, serverTimeout
);
485 if(socksParentProxy
) {
486 connection
->connecting
= CONNECTING_SOCKS
;
487 do_socks_connect(server
->name
, connection
->server
->port
,
488 httpServerSocksHandler
, connection
);
490 connection
->connecting
= CONNECTING_DNS
;
491 do_gethostbyname(server
->name
, 0,
492 httpServerConnectionDnsHandler
,
499 httpServerConnectionDnsHandler(int status
, GethostbynameRequestPtr request
)
501 HTTPConnectionPtr connection
= request
->data
;
503 httpSetTimeout(connection
, -1);
507 message
= internAtomF("Host %s lookup failed: %s",
509 request
->name
->string
: "(unknown)",
510 request
->error_message
?
511 request
->error_message
->string
:
513 do_log(L_ERROR
, "Host %s lookup failed: %s (%d).\n",
515 request
->name
->string
: "(unknown)",
516 request
->error_message
?
517 request
->error_message
->string
:
518 pstrerror(-status
), -status
);
519 connection
->connecting
= 0;
520 if(connection
->server
->request
)
521 httpServerAbortRequest(connection
->server
->request
, 1, 504,
522 retainAtom(message
));
523 httpServerAbort(connection
, 1, 502, message
);
527 if(request
->addr
->string
[0] == DNS_CNAME
) {
528 if(request
->count
> 10) {
529 AtomPtr message
= internAtom("DNS CNAME loop");
530 do_log(L_ERROR
, "DNS CNAME loop.\n");
531 connection
->connecting
= 0;
532 if(connection
->server
->request
)
533 httpServerAbortRequest(connection
->server
->request
, 1, 504,
534 retainAtom(message
));
535 httpServerAbort(connection
, 1, 504, message
);
539 httpSetTimeout(connection
, serverTimeout
);
540 do_gethostbyname(request
->addr
->string
+ 1, request
->count
+ 1,
541 httpServerConnectionDnsHandler
,
546 connection
->connecting
= CONNECTING_CONNECT
;
547 httpSetTimeout(connection
, serverTimeout
);
548 do_connect(retainAtom(request
->addr
), connection
->server
->addrindex
,
549 connection
->server
->port
,
550 httpServerConnectionHandler
, connection
);
555 httpServerConnectionHandler(int status
,
556 FdEventHandlerPtr event
,
557 ConnectRequestPtr request
)
559 HTTPConnectionPtr connection
= request
->data
;
561 assert(connection
->fd
< 0);
562 if(request
->fd
>= 0) {
564 connection
->fd
= request
->fd
;
565 connection
->server
->addrindex
= request
->index
;
566 rc
= setNodelay(connection
->fd
, 1);
568 do_log_error(L_WARN
, errno
, "Couldn't disable Nagle's algorithm");
571 return httpServerConnectionHandlerCommon(status
, connection
);
575 httpServerSocksHandler(int status
, SocksRequestPtr request
)
577 HTTPConnectionPtr connection
= request
->data
;
579 assert(connection
->fd
< 0);
580 if(request
->fd
>= 0) {
581 connection
->fd
= request
->fd
;
582 connection
->server
->addrindex
= 0;
584 return httpServerConnectionHandlerCommon(status
, connection
);
588 httpServerConnectionHandlerCommon(int status
, HTTPConnectionPtr connection
)
590 httpSetTimeout(connection
, -1);
594 internAtomError(-status
, "Connect to %s:%d failed",
595 connection
->server
->name
,
596 connection
->server
->port
);
597 if(status
!= -ECLIENTRESET
)
598 do_log_error(L_ERROR
, -status
, "Connect to %s:%d failed",
599 connection
->server
->name
, connection
->server
->port
);
600 connection
->connecting
= 0;
601 if(connection
->server
->request
)
602 httpServerAbortRequest(connection
->server
->request
,
603 status
!= -ECLIENTRESET
, 504,
604 retainAtom(message
));
605 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 504, message
);
609 do_log(D_SERVER_CONN
, "C %s:%d.\n",
610 connection
->server
->name
, connection
->server
->port
);
612 connection
->connecting
= 0;
613 httpServerTrigger(connection
->server
);
618 httpServerIdleHandler(int a
, FdEventHandlerPtr event
)
620 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
621 HTTPServerPtr server
= connection
->server
;
624 assert(!connection
->request
);
626 do_log(D_SERVER_CONN
, "Idle connection to %s:%d died.\n",
627 connection
->server
->name
, connection
->server
->port
);
629 for(i
= 0; i
< server
->maxslots
; i
++) {
630 if(connection
== server
->connection
[i
]) {
631 server
->idleHandler
[i
] = NULL
;
635 assert(i
< server
->maxslots
);
637 httpServerAbort(connection
, 1, 504, internAtom("Timeout"));
641 /* Discard aborted requests at the head of the queue. */
643 httpServerDiscardRequests(HTTPServerPtr server
)
645 HTTPRequestPtr request
;
646 while(server
->request
&& !server
->request
->request
) {
647 request
= server
->request
;
648 server
->request
= request
->next
;
649 request
->next
= NULL
;
650 if(server
->request
== NULL
)
651 server
->request_last
= NULL
;
652 request
->object
->flags
&= ~(OBJECT_INPROGRESS
| OBJECT_VALIDATING
);
653 releaseNotifyObject(request
->object
);
654 request
->object
= NULL
;
655 httpDestroyRequest(request
);
660 pipelineIsSmall(HTTPConnectionPtr connection
)
662 HTTPRequestPtr request
= connection
->request
;
664 if(pipelineAdditionalRequests
<= 0)
666 else if(pipelineAdditionalRequests
>= 2)
671 if(request
->next
|| !(request
->flags
& REQUEST_PERSISTENT
))
673 if(request
->method
== METHOD_HEAD
||
674 request
->method
== METHOD_CONDITIONAL_GET
)
676 if(request
->to
>= 0 && connection
->server
->rate
> 0 &&
677 request
->to
- request
->from
< connection
->server
->rate
*
684 numRequests(HTTPServerPtr server
)
687 HTTPRequestPtr request
= server
->request
;
690 request
= request
->next
;
696 httpServerGetConnection(HTTPServerPtr server
, int *idle_return
)
699 int connecting
= 0, empty
= 0, idle
= 0;
702 /* Try to find an idle connection */
703 for(i
= 0; i
< server
->numslots
; i
++) {
704 if(server
->connection
[i
]) {
705 if(!server
->connection
[i
]->connecting
) {
706 if(!server
->connection
[i
]->request
) {
707 if(server
->idleHandler
[i
])
708 unregisterFdEvent(server
->idleHandler
[i
]);
709 server
->idleHandler
[i
] = NULL
;
721 return server
->connection
[j
];
724 /* If there's an empty slot, schedule connection creation */
726 /* Don't open a connection if there are already enough in
727 progress, except if the server doesn't do persistent
728 connections and there's only one in progress. */
729 if((connecting
== 0 || (server
->persistent
<= 0 && connecting
<= 1)) ||
730 connecting
< numRequests(server
)) {
731 httpServerConnection(server
);
735 /* Find a connection that can accept additional requests */
736 if(server
->version
== HTTP_11
&& server
->pipeline
>= 4) {
737 for(i
= 0; i
< serverSlots
; i
++) {
738 if(server
->connection
[i
] && !server
->connection
[i
]->connecting
&&
739 pipelineIsSmall(server
->connection
[i
])) {
740 if(server
->idleHandler
[i
])
741 unregisterFdEvent(server
->idleHandler
[i
]);
742 server
->idleHandler
[i
] = NULL
;
744 return server
->connection
[i
];
753 httpServerTrigger(HTTPServerPtr server
)
755 HTTPConnectionPtr connection
;
756 HTTPRequestPtr request
;
757 int idle
, n
, i
, rc
, numidle
;
759 while(server
->request
) {
760 httpServerDiscardRequests(server
);
765 if(REQUEST_SIDE(server
->request
)) {
766 rc
= httpServerSideRequest(server
);
767 /* If rc is 0, httpServerSideRequest didn't dequeue this
768 request. Go through the scheduling loop again, come
773 connection
= httpServerGetConnection(server
, &numidle
);
774 if(!connection
) break;
776 /* If server->pipeline <= 0, we don't do pipelining. If
777 server->pipeline is 1, then we are ready to start probing
778 for pipelining on the server; we then send exactly two
779 requests in what is hopefully a single packet to check
780 whether the server has the nasty habit of discarding its
781 input buffers after each request.
782 If server->pipeline is 2 or 3, the pipelining probe is in
783 progress on this server, and we don't pipeline anything
784 until it succeeds. When server->pipeline >= 4, pipelining
785 is believed to work on this server. */
786 if(server
->version
!= HTTP_11
|| server
->pipeline
<= 0 ||
787 server
->pipeline
== 2 || server
->pipeline
== 3) {
788 if(connection
->pipelined
== 0)
792 } else if(server
->pipeline
== 1) {
793 if(connection
->pipelined
== 0)
794 n
= MIN(2, maxPipelineTrain
);
798 n
= maxPipelineTrain
;
801 /* Don't pipeline if there are more idle connections */
805 idle
= !connection
->pipelined
;
807 while(server
->request
&& connection
->pipelined
< n
) {
808 httpServerDiscardRequests(server
);
809 if(!server
->request
) break;
810 request
= server
->request
;
811 assert(request
->request
->request
== request
);
812 rc
= httpWriteRequest(connection
, request
, -1);
815 httpServerAbortRequest(request
, rc
!= -ECLIENTRESET
, 503,
816 internAtom("Couldn't "
820 do_log(D_SERVER_CONN
, "W: ");
821 do_log_n(D_SERVER_CONN
,
822 request
->object
->key
, request
->object
->key_size
);
823 do_log(D_SERVER_CONN
, " (%d)\n", request
->method
);
824 if(connection
->pipelined
== 0)
825 request
->time0
= current_time
;
827 server
->request
= request
->next
;
828 request
->next
= NULL
;
829 if(server
->request
== NULL
)
830 server
->request_last
= NULL
;
831 httpQueueRequest(connection
, request
);
832 connection
->pipelined
++;
834 if(server
->persistent
> 0 && server
->pipeline
== 1 && i
>= 2)
835 server
->pipeline
= 2;
837 if(i
> 0) httpServerSendRequest(connection
);
839 if(idle
&& connection
->pipelined
> 0)
840 httpServerReply(connection
, 0);
845 for(i
= 0; i
< server
->maxslots
; i
++) {
846 if(server
->connection
[i
] &&
847 !server
->connection
[i
]->connecting
&&
848 !server
->connection
[i
]->request
) {
849 /* Artificially age any fresh connections that aren't used
850 straight away; this is necessary for the logic for POST and
851 the logic that determines whether a given request should be
853 if(server
->connection
[i
]->serviced
== 0)
854 server
->connection
[i
]->serviced
= 1;
855 if(!server
->idleHandler
[i
])
856 server
->idleHandler
[i
] =
857 registerFdEvent(server
->connection
[i
]->fd
, POLLIN
,
858 httpServerIdleHandler
,
859 sizeof(HTTPConnectionPtr
),
860 &server
->connection
[i
]);
861 if(!server
->idleHandler
[i
]) {
862 do_log(L_ERROR
, "Couldn't register idle handler.\n");
863 httpServerFinish(server
->connection
[i
], 1, 0);
872 httpServerSideRequest(HTTPServerPtr server
)
874 HTTPRequestPtr request
= server
->request
;
875 HTTPConnectionPtr connection
;
876 HTTPRequestPtr requestor
= request
->request
;
877 HTTPConnectionPtr client
= requestor
->connection
;
878 int rc
, i
, freeslots
, idle
, connecting
;
880 assert(REQUEST_SIDE(request
));
887 /* Find a fresh connection */
888 for(i
= 0; i
< server
->numslots
; i
++) {
889 if(!server
->connection
[i
])
891 else if(!server
->connection
[i
]->connecting
) {
892 if(!server
->connection
[i
]->request
) {
893 if(server
->connection
[i
]->serviced
== 0) {
894 if(server
->idleHandler
[i
])
895 unregisterFdEvent(server
->idleHandler
[i
]);
896 server
->idleHandler
[i
] = NULL
;
897 connection
= server
->connection
[i
];
909 /* Make sure that a fresh connection will be established at some
910 point, then wait until httpServerTrigger calls us again. */
912 httpServerConnection(server
);
915 /* Shutdown a random idle connection */
916 pokeFdEvent(server
->connection
[idle
]->fd
,
917 -EDOSHUTDOWN
, POLLIN
| POLLOUT
);
923 rc
= httpWriteRequest(connection
, request
, client
->bodylen
);
925 do_log(L_ERROR
, "Couldn't write POST or PUT request.\n");
926 httpServerAbortRequest(request
, rc
!= -ECLIENTRESET
, 503,
927 internAtom("Couldn't write request"));
930 server
->request
= request
->next
;
931 request
->next
= NULL
;
932 if(server
->request
== NULL
)
933 server
->request_last
= NULL
;
934 httpQueueRequest(connection
, request
);
935 connection
->pipelined
= 1;
936 request
->time0
= current_time
;
937 connection
->reqoffset
= 0;
938 connection
->bodylen
= client
->bodylen
;
939 httpServerDoSide(connection
);
944 httpServerDoSide(HTTPConnectionPtr connection
)
946 HTTPRequestPtr request
= connection
->request
;
947 HTTPRequestPtr requestor
= request
->request
;
948 HTTPConnectionPtr client
= requestor
->connection
;
949 int len
= MIN(client
->reqlen
- client
->reqbegin
,
950 connection
->bodylen
- connection
->reqoffset
);
954 client
->reqbegin
> 0 ||
955 (connection
->reqoffset
+ client
->reqlen
- client
->reqbegin
) >=
956 connection
->bodylen
);
957 int done
= connection
->reqoffset
>= connection
->bodylen
;
959 assert(connection
->bodylen
>= 0);
961 httpSetTimeout(connection
, 60);
963 if(connection
->reqlen
> 0) {
964 /* Send the headers, but don't send any part of the body if
965 we're in wait_continue. */
966 do_stream_2(IO_WRITE
,
968 connection
->reqbuf
, connection
->reqlen
,
969 client
->reqbuf
+ client
->reqbegin
,
970 (request
->flags
& REQUEST_WAIT_CONTINUE
) ? 0 : len
,
971 httpServerSideHandler2
, connection
);
972 httpServerReply(connection
, 0);
973 } else if(request
->object
->flags
& OBJECT_ABORTED
) {
974 if(connection
->reqbuf
)
975 dispose_chunk(connection
->reqbuf
);
976 connection
->reqbuf
= NULL
;
977 connection
->reqlen
= 0;
978 pokeFdEvent(connection
->fd
, -ESHUTDOWN
, POLLIN
);
979 client
->flags
|= CONN_SIDE_READER
;
980 do_stream(IO_READ
| IO_IMMEDIATE
,
981 client
->fd
, 0, NULL
, 0,
982 httpClientSideHandler
, client
);
983 } else if(!(request
->flags
& REQUEST_WAIT_CONTINUE
) && doflush
) {
984 /* Make sure there's a reqbuf, as httpServerFinish uses
985 it to determine if there's a writer. */
986 if(connection
->reqbuf
== NULL
)
987 connection
->reqbuf
= get_chunk();
988 assert(connection
->reqbuf
!= NULL
);
991 client
->reqbuf
+ client
->reqbegin
, len
,
992 httpServerSideHandler
, connection
);
994 if(connection
->reqbuf
) {
995 httpConnectionDestroyReqbuf(connection
);
996 connection
->reqlen
= 0;
998 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
999 ConditionHandlerPtr chandler
;
1000 do_log(D_SERVER_CONN
, "W... %s:%d.\n",
1001 connection
->server
->name
, connection
->server
->port
);
1003 conditionWait(&request
->object
->condition
,
1004 httpServerContinueConditionHandler
,
1005 sizeof(connection
), &connection
);
1009 do_log(L_ERROR
, "Couldn't register condition handler.\n");
1010 /* Fall through -- the client side will clean up. */
1012 client
->flags
|= CONN_SIDE_READER
;
1013 do_stream(IO_READ
| (done
? IO_IMMEDIATE
: 0 ) | IO_NOTNOW
,
1014 client
->fd
, client
->reqlen
,
1015 client
->reqbuf
, CHUNK_SIZE
,
1016 httpClientSideHandler
, client
);
1022 httpClientDelayedDoSideHandler(TimeEventHandlerPtr event
)
1024 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1025 httpServerDoSide(connection
);
1030 httpServerDelayedDoSide(HTTPConnectionPtr connection
)
1032 TimeEventHandlerPtr handler
;
1033 handler
= scheduleTimeEvent(1, httpClientDelayedDoSideHandler
,
1034 sizeof(connection
), &connection
);
1036 do_log(L_ERROR
, "Couldn't schedule DoSide -- freeing memory.\n");
1037 free_chunk_arenas();
1038 handler
= scheduleTimeEvent(1, httpClientDelayedDoSideHandler
,
1039 sizeof(connection
), &connection
);
1040 do_log(L_ERROR
, "Couldn't schedule DoSide.\n");
1041 /* Somebody will hopefully end up timing out. */
1048 httpServerSideHandlerCommon(int kind
, int status
,
1049 FdEventHandlerPtr event
,
1050 StreamRequestPtr srequest
)
1052 HTTPConnectionPtr connection
= srequest
->data
;
1053 HTTPRequestPtr request
= connection
->request
;
1054 HTTPRequestPtr requestor
= request
->request
;
1055 HTTPConnectionPtr client
= requestor
->connection
;
1058 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1061 do_log_error(L_ERROR
, -status
, "Couldn't write to server");
1062 httpConnectionDestroyReqbuf(connection
);
1063 if(status
!= -ECLIENTRESET
)
1064 shutdown(connection
->fd
, 2);
1065 abortObject(request
->object
, 503,
1066 internAtom("Couldn't write to server"));
1067 /* Let the read side handle the error */
1068 httpServerDoSide(connection
);
1072 assert(srequest
->offset
> 0);
1075 if(srequest
->offset
< connection
->reqlen
)
1077 bodylen
= srequest
->offset
- connection
->reqlen
;
1078 connection
->reqlen
= 0;
1079 httpConnectionDestroyReqbuf(connection
);
1081 bodylen
= srequest
->offset
;
1085 assert(client
->reqbegin
+ bodylen
<= client
->reqlen
);
1087 if(client
->reqlen
> client
->reqbegin
+ bodylen
)
1088 memmove(client
->reqbuf
, client
->reqbuf
+ client
->reqbegin
+ bodylen
,
1089 client
->reqlen
- client
->reqbegin
- bodylen
);
1090 client
->reqlen
-= bodylen
+ client
->reqbegin
;
1091 client
->reqbegin
= 0;
1092 connection
->reqoffset
+= bodylen
;
1093 httpServerDoSide(connection
);
1098 httpServerSideHandler(int status
,
1099 FdEventHandlerPtr event
,
1100 StreamRequestPtr srequest
)
1102 return httpServerSideHandlerCommon(1, status
, event
, srequest
);
1106 httpServerSideHandler2(int status
,
1107 FdEventHandlerPtr event
,
1108 StreamRequestPtr srequest
)
1110 return httpServerSideHandlerCommon(2, status
, event
, srequest
);
1114 httpServerContinueConditionHandler(int status
, ConditionHandlerPtr chandler
)
1116 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)chandler
->data
;
1118 if(connection
->request
->flags
& REQUEST_WAIT_CONTINUE
)
1120 httpServerDelayedDoSide(connection
);
1124 /* s is 0 to keep the connection alive, 1 to shutdown the connection,
1125 and -1 to keep the connection alive and keep the current request. */
1127 httpServerFinish(HTTPConnectionPtr connection
, int s
, int offset
)
1129 HTTPServerPtr server
= connection
->server
;
1130 HTTPRequestPtr request
= connection
->request
;
1134 assert(connection
->pipelined
>= 1);
1135 assert((connection
->pipelined
> 1) == (request
->next
!= NULL
));
1137 assert(connection
->pipelined
== 0);
1140 if(s
== 0 && (!connection
->request
||
1141 !(connection
->request
->flags
& REQUEST_PERSISTENT
)))
1144 if(connection
->reqbuf
) {
1145 /* As most normal requests go out in a single packet, this is
1146 extremely unlikely to happen. As for POST/PUT requests,
1147 they are not pipelined, so this can only happen if the
1148 server sent an error reply early. */
1149 assert(connection
->fd
>= 0);
1150 shutdown(connection
->fd
, 1);
1151 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLOUT
);
1152 httpServerDelayedFinish(connection
);
1156 if(s
>= 0 && request
) {
1157 /* Update statistics about the server */
1158 int size
= -1, d
= -1, rtt
= -1, rate
= -1;
1159 if(connection
->offset
> 0 && request
->from
>= 0)
1160 size
= connection
->offset
- request
->from
;
1161 if(request
->time0
.tv_sec
> 0 && request
->time1
.tv_sec
> 0) {
1162 d
= timeval_minus_usec(¤t_time
, &request
->time1
);
1163 rtt
= timeval_minus_usec(&request
->time1
, &request
->time0
);
1164 if(size
>= 4096 && d
> 50000)
1165 rate
= ((double)size
/ (double)d
) * 1000000.0 + 0.5;
1167 request
->time0
= null_time
;
1168 request
->time1
= null_time
;
1172 server
->rtt
= (3 * server
->rtt
+ rtt
+ 2) / 4;
1177 if(server
->rate
> 0)
1178 server
->rate
= (3 * server
->rate
+ rate
+ 2) / 4;
1180 server
->rate
= rate
;
1183 httpDequeueRequest(connection
);
1184 connection
->pipelined
--;
1185 request
->object
->flags
&= ~(OBJECT_INPROGRESS
| OBJECT_VALIDATING
);
1186 if(request
->request
) {
1187 request
->request
->request
= NULL
;
1188 request
->request
= NULL
;
1190 releaseNotifyObject(request
->object
);
1191 request
->object
= NULL
;
1192 httpDestroyRequest(request
);
1195 do_log(D_SERVER_CONN
, "Done with server %s:%d connection (%d)\n",
1196 connection
->server
->name
, connection
->server
->port
, s
);
1198 assert(offset
<= connection
->len
);
1201 if(offset
< connection
->len
) {
1202 assert(connection
->buf
!= NULL
);
1203 if(!connection
->pipelined
) {
1205 "Closing connection to %s:%d: "
1206 "%d stray bytes of data.\n",
1207 server
->name
, server
->port
, connection
->len
- offset
);
1210 memmove(connection
->buf
, connection
->buf
+ offset
,
1211 connection
->len
- offset
);
1212 connection
->len
= connection
->len
- offset
;
1213 if((connection
->flags
& CONN_BIGBUF
) &&
1214 connection
->len
<= CHUNK_SIZE
)
1215 httpConnectionUnbigify(connection
);
1218 connection
->len
= 0;
1222 connection
->server
->time
= current_time
.tv_sec
;
1223 connection
->serviced
++;
1226 if(connection
->timeout
)
1227 cancelTimeEvent(connection
->timeout
);
1228 connection
->timeout
= NULL
;
1229 httpConnectionDestroyBuf(connection
);
1230 if(connection
->fd
>= 0)
1231 close(connection
->fd
);
1232 connection
->fd
= -1;
1233 server
->persistent
-= 1;
1234 if(server
->persistent
< -5)
1235 server
->numslots
= MIN(server
->maxslots
, serverMaxSlots
);
1236 if(connection
->request
) {
1238 do_log(D_SERVER_CONN
, "Restarting pipeline to %s:%d.\n",
1239 server
->name
, server
->port
);
1240 if(server
->pipeline
== 2)
1241 server
->pipeline
-= 20;
1243 server
->pipeline
-= 5;
1244 req
= connection
->request
;
1246 req
->connection
= NULL
;
1250 connection
->request_last
->next
= server
->request
;
1252 server
->request_last
= connection
->request_last
;
1253 server
->request
= connection
->request
;
1254 connection
->request
= NULL
;
1255 connection
->request_last
= NULL
;
1257 /* Make sure we don't get confused into thinking a probe
1259 if(server
->pipeline
== 2 || server
->pipeline
== 3)
1260 server
->pipeline
= 1;
1261 for(i
= 0; i
< server
->maxslots
; i
++)
1262 if(connection
== server
->connection
[i
])
1264 assert(i
< server
->maxslots
);
1265 if(server
->idleHandler
[i
])
1266 unregisterFdEvent(server
->idleHandler
[i
]);
1267 server
->idleHandler
[i
] = NULL
;
1268 server
->connection
[i
] = NULL
;
1271 server
->persistent
+= 1;
1272 if(server
->persistent
> 0)
1273 server
->numslots
= MIN(server
->maxslots
, serverSlots
);
1274 httpSetTimeout(connection
, serverTimeout
);
1275 /* See httpServerTrigger */
1276 if(connection
->pipelined
||
1277 (server
->version
== HTTP_11
&& server
->pipeline
<= 0) ||
1278 (server
->pipeline
== 3)) {
1281 if(s
< 0 || connection
->pipelined
) {
1282 httpServerReply(connection
, 1);
1284 httpConnectionDestroyBuf(connection
);
1289 httpServerTrigger(server
);
1293 httpServerDelayedFinishHandler(TimeEventHandlerPtr event
)
1295 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1296 httpServerFinish(connection
, 1, 0);
1301 httpServerDelayedFinish(HTTPConnectionPtr connection
)
1303 TimeEventHandlerPtr handler
;
1305 handler
= scheduleTimeEvent(1, httpServerDelayedFinishHandler
,
1306 sizeof(connection
), &connection
);
1309 "Couldn't schedule delayed finish -- freeing memory.");
1310 free_chunk_arenas();
1311 handler
= scheduleTimeEvent(1, httpServerDelayedFinishHandler
,
1312 sizeof(connection
), &connection
);
1315 "Couldn't schedule delayed finish -- aborting.\n");
1322 httpServerReply(HTTPConnectionPtr connection
, int immediate
)
1324 assert(connection
->pipelined
> 0);
1326 if(connection
->request
->request
== NULL
) {
1327 do_log(L_WARN
, "Aborting pipeline on %s:%d.\n",
1328 connection
->server
->name
, connection
->server
->port
);
1329 httpServerFinish(connection
, 1, 0);
1333 do_log(D_SERVER_CONN
, "R: ");
1334 do_log_n(D_SERVER_CONN
, connection
->request
->object
->key
,
1335 connection
->request
->object
->key_size
);
1336 do_log(D_SERVER_CONN
, " (%d)\n", connection
->request
->method
);
1338 if(connection
->len
== 0)
1339 httpConnectionDestroyBuf(connection
);
1341 httpSetTimeout(connection
, serverTimeout
);
1342 do_stream_buf(IO_READ
| (immediate
? IO_IMMEDIATE
: 0) | IO_NOTNOW
,
1343 connection
->fd
, connection
->len
,
1344 &connection
->buf
, CHUNK_SIZE
,
1345 httpServerReplyHandler
, connection
);
1349 httpConnectionPipelined(HTTPConnectionPtr connection
)
1351 HTTPRequestPtr request
= connection
->request
;
1355 request
= request
->next
;
1361 httpServerUnpipeline(HTTPRequestPtr request
)
1363 HTTPConnectionPtr connection
= request
->connection
;
1364 HTTPServerPtr server
= connection
->server
;
1366 request
->flags
&= ~REQUEST_PERSISTENT
;
1370 "Restarting pipeline to %s:%d.\n",
1371 connection
->server
->name
, connection
->server
->port
);
1372 req
= request
->next
;
1374 req
->connection
= NULL
;
1378 connection
->request_last
->next
= server
->request
;
1380 server
->request_last
= connection
->request_last
;
1381 server
->request
= request
->next
;
1382 request
->next
= NULL
;
1383 connection
->request_last
= request
;
1385 connection
->pipelined
= httpConnectionPipelined(connection
);
1389 httpServerRestart(HTTPConnectionPtr connection
)
1391 HTTPServerPtr server
= connection
->server
;
1392 HTTPRequestPtr request
= connection
->request
;
1398 "Restarting pipeline to %s:%d.\n",
1399 connection
->server
->name
, connection
->server
->port
);
1402 req
->connection
= NULL
;
1406 connection
->request_last
->next
= server
->request
;
1408 server
->request_last
= connection
->request_last
;
1409 server
->request
= request
;
1410 connection
->request
= NULL
;
1411 connection
->request_last
= NULL
;
1413 connection
->pipelined
= 0;
1414 httpServerFinish(connection
, 1, 0);
1418 httpServerRequest(ObjectPtr object
, int method
, int from
, int to
,
1419 HTTPRequestPtr requestor
, void *closure
)
1426 assert(from
>= 0 && (to
< 0 || to
> from
));
1427 assert(closure
== NULL
);
1428 assert(!(object
->flags
& OBJECT_LOCAL
));
1429 assert(object
->type
== OBJECT_HTTP
);
1431 if(object
->flags
& OBJECT_INPROGRESS
)
1434 if(requestor
->flags
& REQUEST_REQUESTED
)
1437 assert(requestor
->request
== NULL
);
1442 rc
= parseUrl(object
->key
, object
->key_size
, &x
, &y
, &port
, &z
);
1444 if(rc
< 0 || x
< 0 || y
< 0 || y
- x
> 131) {
1445 do_log(L_ERROR
, "Couldn't parse URL: ");
1446 do_log_n(L_ERROR
, object
->key
, object
->key_size
);
1447 do_log(L_ERROR
, "\n");
1448 abortObject(object
, 400, internAtom("Couldn't parse URL"));
1449 notifyObject(object
);
1453 if(!intListMember(port
, allowedPorts
)) {
1454 do_log(L_ERROR
, "Attempted connection to port %d.\n", port
);
1455 abortObject(object
, 403, internAtom("Forbidden port"));
1456 notifyObject(object
);
1460 memcpy(name
, ((char*)object
->key
) + x
, y
- x
);
1463 requestor
->flags
|= REQUEST_REQUESTED
;
1464 rc
= httpMakeServerRequest(name
, port
, object
, method
, from
, to
,
1469 503, internAtom("Couldn't schedule server request"));
1470 notifyObject(object
);
1478 httpWriteRequest(HTTPConnectionPtr connection
, HTTPRequestPtr request
,
1481 ObjectPtr object
= request
->object
;
1482 int from
= request
->from
, to
= request
->to
, method
= request
->method
;
1483 char *url
= object
->key
, *m
;
1484 int url_size
= object
->key_size
;
1485 int x
, y
, port
, z
, location_size
;
1487 int l
, n
, rc
, bufsize
;
1489 assert(method
!= METHOD_NONE
);
1491 if(request
->method
== METHOD_GET
||
1492 request
->method
== METHOD_CONDITIONAL_GET
) {
1496 do_log(L_ERROR
, "Requesting empty segment?\n");
1501 if(object
->flags
& OBJECT_DYNAMIC
) {
1505 objectFillFromDisk(object
, from
/ CHUNK_SIZE
* CHUNK_SIZE
, 1);
1506 l
= objectHoleSize(request
->object
, from
);
1508 if(to
<= 0 || to
> from
+ l
)
1512 if(pmmSize
&& connection
->server
->pipeline
>= 4) {
1514 to
= to
< 0 ? pmmFirstSize
: MIN(to
, pmmFirstSize
);
1516 to
= to
< 0 ? from
+ pmmSize
: MIN(to
, from
+ pmmSize
);
1519 if(from
% CHUNK_SIZE
!= 0)
1520 if(objectHoleSize(object
, from
- 1) != 0)
1521 from
= from
/ CHUNK_SIZE
* CHUNK_SIZE
;
1525 rc
= parseUrl(url
, url_size
, &x
, &y
, &port
, &z
);
1527 if(rc
< 0 || x
< 0 || y
< 0) {
1531 if(connection
->reqbuf
== NULL
) {
1532 connection
->reqbuf
= get_chunk();
1533 if(connection
->reqbuf
== NULL
)
1535 connection
->reqlen
= 0;
1538 if(method
== METHOD_CONDITIONAL_GET
&&
1539 object
->last_modified
< 0 && object
->etag
== NULL
)
1540 method
= request
->method
= METHOD_GET
;
1544 (connection
->flags
& CONN_BIGREQBUF
) ? bigBufferSize
: CHUNK_SIZE
;
1545 n
= connection
->reqlen
;
1548 case METHOD_CONDITIONAL_GET
: m
= "GET"; break;
1549 case METHOD_HEAD
: m
= "HEAD"; break;
1550 case METHOD_POST
: m
= "POST"; break;
1551 case METHOD_PUT
: m
= "PUT"; break;
1554 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, "%s ", m
);
1556 if(connection
->server
->isProxy
) {
1557 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
,
1560 if(url_size
- z
== 0) {
1565 location_size
= url_size
- z
;
1568 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
,
1569 location
, location_size
);
1572 do_log(D_SERVER_REQ
, "Server request: ");
1573 do_log_n(D_SERVER_REQ
, url
+ x
, y
- x
);
1574 do_log(D_SERVER_REQ
, ": ");
1575 do_log_n(D_SERVER_REQ
, connection
->reqbuf
, n
);
1576 do_log(D_SERVER_REQ
, " (method %d from %d to %d, 0x%lx for 0x%lx)\n",
1578 (unsigned long)connection
, (unsigned long)object
);
1580 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, " HTTP/1.1");
1582 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, "\r\nHost: ");
1583 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
, url
+ x
, y
- x
);
1585 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, ":%d", port
);
1587 if(connection
->server
->isProxy
&& parentAuthCredentials
) {
1588 n
= buildServerAuthHeaders(connection
->reqbuf
, n
, bufsize
,
1589 parentAuthCredentials
);
1593 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1594 "\r\nContent-Length: %d", bodylen
);
1596 if(request
->flags
& REQUEST_WAIT_CONTINUE
)
1597 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1598 "\r\nExpect: 100-continue");
1600 if(method
!= METHOD_HEAD
&& (from
> 0 || to
>= 0)) {
1602 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1603 "\r\nRange: bytes=%d-%d", from
, to
- 1);
1605 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1606 "\r\nRange: bytes=%d-", from
);
1610 if(method
== METHOD_GET
&& object
->etag
&& (from
> 0 || to
>= 0)) {
1611 if(request
->request
&& request
->request
->request
== request
&&
1612 request
->request
->from
== 0 && request
->request
->to
== -1 &&
1613 pmmSize
== 0 && pmmFirstSize
== 0)
1614 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1615 "\r\nIf-Range: \"%s\"", object
->etag
);
1618 if(method
== METHOD_CONDITIONAL_GET
) {
1619 if(object
->last_modified
>= 0) {
1620 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1621 "\r\nIf-Modified-Since: ");
1622 n
= format_time(connection
->reqbuf
, n
, bufsize
,
1623 object
->last_modified
);
1626 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1627 "\r\nIf-None-Match: \"%s\"", object
->etag
);
1631 n
= httpPrintCacheControl(connection
->reqbuf
, n
, bufsize
,
1632 0, &request
->cache_control
);
1636 if(request
->request
&& request
->request
->headers
) {
1637 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
,
1638 request
->request
->headers
->string
,
1639 request
->request
->headers
->length
);
1642 if(request
->request
&& request
->request
->via
) {
1643 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1644 "\r\nVia: %s, 1.1 %s",
1645 request
->request
->via
->string
, proxyName
->string
);
1647 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1653 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1654 "\r\nConnection: %s\r\n\r\n",
1655 (request
->flags
& REQUEST_PERSISTENT
) ?
1656 "keep-alive" : "close");
1657 if(n
< 0 || n
>= bufsize
- 1)
1659 connection
->reqlen
= n
;
1664 if(!(connection
->flags
& CONN_BIGREQBUF
))
1665 rc
= httpConnectionBigifyReqbuf(connection
);
1672 httpServerHandler(int status
,
1673 FdEventHandlerPtr event
,
1674 StreamRequestPtr srequest
)
1676 HTTPConnectionPtr connection
= srequest
->data
;
1679 assert(connection
->request
->object
->flags
& OBJECT_INPROGRESS
);
1681 if(connection
->reqlen
== 0) {
1682 do_log(D_SERVER_REQ
, "Writing aborted on 0x%lx\n",
1683 (unsigned long)connection
);
1687 if(status
== 0 && !streamRequestDone(srequest
)) {
1688 httpSetTimeout(connection
, serverTimeout
);
1692 httpConnectionDestroyReqbuf(connection
);
1695 if(connection
->serviced
>= 1) {
1696 httpServerRestart(connection
);
1699 if(status
>= 0 || status
== ECONNRESET
) {
1700 message
= internAtom("Couldn't send request to server: "
1703 if(status
!= -EPIPE
)
1704 do_log_error(L_ERROR
, -status
,
1705 "Couldn't send request to server");
1707 internAtomError(-status
, "Couldn't send request to server");
1715 dispose_chunk(connection
->reqbuf
);
1716 connection
->reqbuf
= NULL
;
1717 shutdown(connection
->fd
, 2);
1718 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLIN
);
1719 httpSetTimeout(connection
, 60);
1724 httpServerSendRequest(HTTPConnectionPtr connection
)
1726 assert(connection
->server
);
1728 if(connection
->reqlen
== 0) {
1729 do_log(D_SERVER_REQ
,
1730 "Writing aborted on 0x%lx\n", (unsigned long)connection
);
1731 httpConnectionDestroyReqbuf(connection
);
1732 shutdown(connection
->fd
, 2);
1733 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLIN
| POLLOUT
);
1737 httpSetTimeout(connection
, serverTimeout
);
1738 do_stream(IO_WRITE
, connection
->fd
, 0,
1739 connection
->reqbuf
, connection
->reqlen
,
1740 httpServerHandler
, connection
);
1745 httpServerReplyHandler(int status
,
1746 FdEventHandlerPtr event
,
1747 StreamRequestPtr srequest
)
1749 HTTPConnectionPtr connection
= srequest
->data
;
1750 HTTPRequestPtr request
= connection
->request
;
1753 (connection
->flags
& CONN_BIGBUF
) ? bigBufferSize
: CHUNK_SIZE
;
1755 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1757 if(connection
->serviced
>= 1) {
1758 httpServerRestart(connection
);
1761 if(status
!= -ECLIENTRESET
)
1762 do_log_error(L_ERROR
, -status
, "Read from server failed");
1763 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
1764 internAtomError(-status
, "Read from server failed"));
1768 i
= findEndOfHeaders(connection
->buf
, 0, srequest
->offset
, &body
);
1769 connection
->len
= srequest
->offset
;
1772 request
->time1
= current_time
;
1773 return httpServerHandlerHeaders(status
, event
, srequest
, connection
);
1777 if(connection
->serviced
>= 1) {
1778 httpServerRestart(connection
);
1783 "Error reading server headers: %d\n", -status
);
1784 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
1785 internAtomError(-status
,
1786 "Error reading server headers"));
1788 httpServerAbort(connection
, 1, 502,
1789 internAtom("Server dropped connection"));
1793 if(connection
->len
>= bufsize
) {
1795 if(!(connection
->flags
& CONN_BIGBUF
))
1796 rc
= httpConnectionBigify(connection
);
1798 do_log(L_ERROR
, "Couldn't find end of server's headers.\n");
1799 httpServerAbort(connection
, 1, 502,
1800 internAtom("Couldn't find end "
1801 "of server's headers"));
1804 do_log(L_ERROR
, "Couldn't allocate big buffer.\n");
1805 httpServerAbort(connection
, 1, 500,
1806 internAtom("Couldn't allocate big buffer"));
1809 /* Can't just return 0 -- buf has moved. */
1811 connection
->fd
, connection
->len
,
1812 connection
->buf
, bigBufferSize
,
1813 httpServerReplyHandler
, connection
);
1821 httpServerHandlerHeaders(int eof
,
1822 FdEventHandlerPtr event
,
1823 StreamRequestPtr srequest
,
1824 HTTPConnectionPtr connection
)
1826 HTTPRequestPtr request
= connection
->request
;
1827 ObjectPtr object
= request
->object
;
1834 CacheControlRec cache_control
;
1836 time_t date
, last_modified
, expires
;
1837 struct timeval
*init_time
;
1839 AtomPtr via
, new_via
;
1841 HTTPRangeRec content_range
;
1842 ObjectPtr new_object
= NULL
, old_object
= NULL
;
1844 AtomPtr message
= NULL
;
1848 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1851 httpSetTimeout(connection
, -1);
1853 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
1854 do_log(D_SERVER_CONN
, "W %s:%d.\n",
1855 connection
->server
->name
, connection
->server
->port
);
1856 request
->flags
&= ~REQUEST_WAIT_CONTINUE
;
1859 rc
= httpParseServerFirstLine(connection
->buf
, &code
, &version
, &message
);
1861 do_log(L_ERROR
, "Couldn't parse server status line.\n");
1862 httpServerAbort(connection
, 1, 502,
1863 internAtom("Couldn't parse server status line"));
1867 do_log(D_SERVER_REQ
, "Server status: ");
1868 do_log_n(D_SERVER_REQ
, connection
->buf
,
1869 connection
->buf
[rc
- 1] == '\r' ? rc
- 2 : rc
- 2);
1870 do_log(D_SERVER_REQ
, " (0x%lx for 0x%lx)\n",
1871 (unsigned long)connection
, (unsigned long)object
);
1873 if(version
!= HTTP_10
&& version
!= HTTP_11
) {
1874 do_log(L_ERROR
, "Unknown server HTTP version\n");
1875 httpServerAbort(connection
, 1, 502,
1876 internAtom("Unknown server HTTP version"));
1877 releaseAtom(message
);
1881 connection
->version
= version
;
1882 connection
->server
->version
= version
;
1883 request
->flags
|= REQUEST_PERSISTENT
;
1885 url
= internAtomN(object
->key
, object
->key_size
);
1886 rc
= httpParseHeaders(0, url
, connection
->buf
, rc
, request
,
1887 &headers
, &len
, &cache_control
, NULL
, &te
,
1888 &date
, &last_modified
, &expires
, NULL
, NULL
, NULL
,
1889 &age
, &etag
, NULL
, NULL
, &content_range
,
1892 do_log(L_ERROR
, "Couldn't parse server headers\n");
1894 releaseAtom(message
);
1895 httpServerAbort(connection
, 1, 502,
1896 internAtom("Couldn't parse server headers"));
1902 releaseAtom(message
);
1903 /* We've already reset wait_continue above, but we must still
1904 ensure that the writer notices. */
1905 notifyObject(request
->object
);
1906 httpServerFinish(connection
, -1, rc
);
1911 httpServerAbort(connection
, 1, 501,
1912 internAtom("Upgrade not implemented"));
1916 if(via
&& !checkVia(proxyName
, via
)) {
1917 httpServerAbort(connection
, 1, 504, internAtom("Proxy loop detected"));
1920 full_len
= content_range
.full_length
;
1923 if(content_range
.from
== -1 || content_range
.to
== -1) {
1924 do_log(L_ERROR
, "Partial content without range.\n");
1925 httpServerAbort(connection
, 1, 502,
1926 internAtom("Partial content without range"));
1929 if(len
>= 0 && len
!= content_range
.to
- content_range
.from
) {
1930 do_log(L_ERROR
, "Inconsistent partial content.\n");
1931 httpServerAbort(connection
, 1, 502,
1932 internAtom("Inconsistent partial content"));
1935 } else if(code
< 400 &&
1936 (content_range
.from
>= 0 || content_range
.to
>= 0 ||
1937 content_range
.full_length
>= 0)) {
1938 do_log(L_WARN
, "Range without partial content.\n");
1940 content_range
.from
= -1;
1941 content_range
.to
= -1;
1942 content_range
.full_length
= -1;
1943 } else if(code
!= 304 && code
!= 412) {
1947 if(te
!= TE_IDENTITY
&& te
!= TE_CHUNKED
) {
1948 do_log(L_ERROR
, "Unsupported transfer-encoding\n");
1949 httpServerAbort(connection
, 1, 502,
1950 internAtom("Unsupported transfer-encoding"));
1955 if(request
->method
!= METHOD_CONDITIONAL_GET
) {
1956 do_log(L_ERROR
, "Unexpected \"not changed\" reply from server\n");
1957 httpServerAbort(connection
, 1, 502,
1958 internAtom("Unexpected \"not changed\" "
1959 "reply from server"));
1962 if(object
->etag
&& !etag
) {
1963 /* RFC 2616 10.3.5. Violated by some front-end proxies. */
1964 do_log(L_WARN
, "\"Not changed\" reply with no ETag.\n");
1969 if(request
->method
!= METHOD_CONDITIONAL_GET
||
1970 (!object
->etag
&& !object
->last_modified
)) {
1972 "Unexpected \"precondition failed\" reply from server.\n");
1973 httpServerAbort(connection
, 1, 502,
1974 internAtom("Unexpected \"precondition failed\" "
1975 "reply from server"));
1982 /* Okay, we're going to accept this reply. */
1984 if((code
== 200 || code
== 206 || code
== 304 || code
== 412) &&
1985 (cache_control
.flags
& (CACHE_NO
| CACHE_NO_STORE
) ||
1986 cache_control
.max_age
== 0 ||
1987 (cacheIsShared
&& cache_control
.s_maxage
== 0) ||
1988 (expires
>= 0 && expires
<= object
->age
))) {
1989 do_log(L_UNCACHEABLE
, "Uncacheable object ");
1990 do_log_n(L_UNCACHEABLE
, object
->key
, object
->key_size
);
1991 do_log(L_UNCACHEABLE
, " (%d)\n", cache_control
.flags
);
1994 if(request
->time0
.tv_sec
!= null_time
.tv_sec
)
1995 init_time
= &request
->time0
;
1997 init_time
= ¤t_time
;
1998 age
= MIN(init_time
->tv_sec
- age
, init_time
->tv_sec
);
2000 if(request
->method
== METHOD_HEAD
||
2001 code
< 200 || code
== 204 || code
== 304)
2003 else if(te
== TE_IDENTITY
)
2004 expect_body
= (len
!= 0);
2008 connection
->chunk_remaining
= -1;
2009 connection
->te
= te
;
2011 old_object
= object
;
2013 connection
->server
->lies
--;
2015 if(code
== 304 || code
== 412) {
2016 if((object
->etag
&& etag
&& strcmp(object
->etag
, etag
) != 0) ||
2017 (object
->last_modified
>= 0 && last_modified
>= 0 &&
2018 object
->last_modified
!= last_modified
)) {
2019 do_log(L_ERROR
, "Inconsistent \"%s\" reply for ",
2020 code
== 304 ? "not changed":"precondition failed");
2021 do_log_n(L_ERROR
, object
->key
, object
->key_size
);
2022 do_log(L_ERROR
, "\n");
2023 object
->flags
|= OBJECT_DYNAMIC
;
2026 } else if(!(object
->flags
& OBJECT_INITIAL
)) {
2027 if((object
->last_modified
< 0 || last_modified
< 0) &&
2028 (!object
->etag
|| !etag
))
2030 else if(object
->last_modified
!= last_modified
)
2032 else if(object
->etag
|| etag
) {
2033 /* We need to be permissive here so as to deal with some
2034 front-end proxies that discard ETags on partial
2035 replies but not on full replies. */
2036 if(etag
&& object
->etag
&& strcmp(object
->etag
, etag
) != 0)
2038 else if(!object
->etag
)
2042 if(full_len
< 0 && te
== TE_IDENTITY
) {
2043 /* It's an HTTP/1.0 CGI. Be afraid. */
2044 if(expect_body
&& content_range
.from
< 0 && content_range
.to
< 0)
2048 if(!supersede
&& object
->length
>= 0 && full_len
>= 0 &&
2049 object
->length
!= full_len
) {
2050 do_log(L_WARN
, "Inconsistent length.\n");
2055 ((object
->last_modified
>= 0 && last_modified
>= 0) ||
2056 (object
->etag
&& etag
))) {
2057 if(request
->method
== METHOD_CONDITIONAL_GET
) {
2058 do_log(L_WARN
, "Server ignored conditional request.\n");
2059 connection
->server
->lies
+= 10;
2060 /* Drop the connection? */
2063 } else if(code
== 416) {
2064 do_log(L_ERROR
, "Unexpected \"range not satisfiable\" reply\n");
2065 httpServerAbort(connection
, 1, 502,
2066 internAtom("Unexpected \"range not satisfiable\" "
2068 /* The object may be superseded. Make sure the next request
2069 won't be partial. */
2070 abortObject(object
, 502,
2071 internAtom("Unexpected \"range not satisfiable\" reply"));
2075 if(object
->flags
& OBJECT_INITIAL
)
2079 do_log(L_SUPERSEDED
, "Superseding object: ");
2080 do_log_n(L_SUPERSEDED
, old_object
->key
, old_object
->key_size
);
2081 do_log(L_SUPERSEDED
, " (%d %d %d %s -> %d %d %d %s)\n",
2082 object
->code
, object
->length
, (int)object
->last_modified
,
2083 object
->etag
?object
->etag
: "(none)",
2084 code
, full_len
, (int)last_modified
,
2085 etag
?etag
:"(none)");
2086 privatiseObject(old_object
, 0);
2087 new_object
= makeObject(object
->type
, object
->key
,
2088 object
->key_size
, 1, 0,
2089 object
->request
, NULL
);
2090 if(new_object
== NULL
) {
2091 do_log(L_ERROR
, "Couldn't allocate object\n");
2092 httpServerAbort(connection
, 1, 500,
2093 internAtom("Couldn't allocate object"));
2096 if(urlIsLocal(new_object
->key
, new_object
->key_size
))
2097 new_object
->flags
|= OBJECT_LOCAL
;
2099 new_object
= object
;
2103 (!etag
&& last_modified
< 0) ||
2104 (cache_control
.flags
&
2105 (CACHE_NO_HIDDEN
| CACHE_NO
| CACHE_NO_STORE
|
2106 (cacheIsShared
? CACHE_PRIVATE
: 0))) ||
2107 (cache_control
.max_age
>= 0 && cache_control
.max_age
<= 2) ||
2109 cache_control
.s_maxage
>= 0 && cache_control
.s_maxage
<= 5) ||
2110 (old_object
->last_modified
>= 0 && old_object
->expires
>= 0 &&
2111 (old_object
->expires
- old_object
->last_modified
<= 1)) ||
2112 (supersede
&& (old_object
->date
- date
<= 5));
2115 new_object
->flags
|= OBJECT_DYNAMIC
;
2117 new_object
->flags
&= ~OBJECT_DYNAMIC
;
2118 else if(old_object
->flags
& OBJECT_DYNAMIC
)
2119 new_object
->flags
|= OBJECT_DYNAMIC
;
2121 new_object
->age
= age
;
2122 new_object
->cache_control
|= cache_control
.flags
;
2123 new_object
->max_age
= cache_control
.max_age
;
2124 new_object
->s_maxage
= cache_control
.s_maxage
;
2125 new_object
->flags
&= ~OBJECT_FAILED
;
2128 new_object
->date
= date
;
2129 if(last_modified
>= 0)
2130 new_object
->last_modified
= last_modified
;
2132 new_object
->expires
= expires
;
2133 if(new_object
->etag
== NULL
)
2134 new_object
->etag
= etag
;
2140 case 300: case 301: case 302: case 303: case 307:
2141 case 403: case 404: case 405: case 401:
2142 if(new_object
->message
) releaseAtom(new_object
->message
);
2143 new_object
->code
= code
;
2144 new_object
->message
= message
;
2146 case 206: case 304: case 412:
2147 if(new_object
->code
!= 200 || !new_object
->message
) {
2148 if(new_object
->message
) releaseAtom(new_object
->message
);
2149 new_object
->code
= 200;
2150 new_object
->message
= internAtom("OK");
2152 releaseAtom(message
);
2155 if(new_object
->message
) releaseAtom(new_object
->message
);
2156 new_object
->code
= code
;
2157 new_object
->message
= retainAtom(message
);
2161 if((cache_control
.flags
& CACHE_AUTHORIZATION
) &&
2162 !(cache_control
.flags
& CACHE_PUBLIC
))
2163 new_object
->cache_control
|= (CACHE_NO_HIDDEN
| OBJECT_LINEAR
);
2165 /* This is not required by RFC 2616 -- but see RFC 3143 2.1.1. We
2166 manically avoid caching replies that we don't know how to
2167 handle, even if Expires or Cache-Control says otherwise. As to
2168 known uncacheable replies, we obey Cache-Control and default to
2169 allowing sharing but not caching. */
2170 if(code
!= 200 && code
!= 206 &&
2171 code
!= 300 && code
!= 301 && code
!= 302 && code
!= 303 &&
2172 code
!= 304 && code
!= 307 &&
2173 code
!= 403 && code
!= 404 && code
!= 405 && code
!= 416) {
2174 new_object
->cache_control
|= (CACHE_NO_HIDDEN
| OBJECT_LINEAR
);
2175 } else if(code
!= 200 && code
!= 206 &&
2176 code
!= 300 && code
!= 302 && code
!= 304 &&
2178 if(new_object
->expires
< 0 && !(cache_control
.flags
& CACHE_PUBLIC
)) {
2179 new_object
->cache_control
|= CACHE_NO_HIDDEN
;
2181 } else if(dontCacheRedirects
&& (code
== 301 || code
== 302)) {
2182 new_object
->cache_control
|= CACHE_NO_HIDDEN
;
2185 if(urlIsUncachable(new_object
->key
, new_object
->key_size
)) {
2186 new_object
->cache_control
|= CACHE_NO_HIDDEN
;
2190 new_via
= internAtomF("%s %s",
2191 version
== HTTP_11
? "1.1" : "1.0",
2194 new_via
= internAtomF("%s, %s %s", via
->string
,
2195 version
== HTTP_11
? "1.1" : "1.0",
2197 if(new_via
== NULL
) {
2198 do_log(L_ERROR
, "Couldn't allocate Via.\n");
2200 if(new_object
->via
) releaseAtom(new_object
->via
);
2201 new_object
->via
= new_via
;
2204 if((new_object
->cache_control
& CACHE_NO_STORE
) ||
2205 ((new_object
->cache_control
& CACHE_VARY
) && !new_object
->etag
))
2206 new_object
->cache_control
|= CACHE_NO_HIDDEN
;
2208 if(new_object
->flags
& OBJECT_INITIAL
) {
2209 objectPartial(new_object
, full_len
, headers
);
2211 if(new_object
->length
< 0)
2212 new_object
->length
= full_len
;
2213 /* XXX -- RFC 2616 13.5.3 */
2214 releaseAtom(headers
);
2218 assert(new_object
!= old_object
);
2219 supersedeObject(old_object
);
2222 if(new_object
!= old_object
) {
2223 if(new_object
->flags
& OBJECT_INPROGRESS
) {
2224 /* Make sure we don't fetch this object two times at the
2225 same time. Just drop the connection. */
2226 releaseObject(new_object
);
2227 httpServerFinish(connection
, 1, 0);
2230 old_object
->flags
&= ~OBJECT_VALIDATING
;
2231 new_object
->flags
|= OBJECT_INPROGRESS
;
2232 /* Signal the client side to switch to the new object -- see
2233 httpClientGetHandler. If it doesn't, we'll give up on this
2235 new_object
->flags
|= OBJECT_MUTATING
;
2236 request
->can_mutate
= new_object
;
2237 notifyObject(old_object
);
2238 request
->can_mutate
= NULL
;
2239 new_object
->flags
&= ~OBJECT_MUTATING
;
2240 old_object
->flags
&= ~OBJECT_INPROGRESS
;
2241 if(request
->object
== old_object
) {
2242 if(request
->request
)
2243 request
->request
->request
= NULL
;
2244 request
->request
= NULL
;
2245 request
->object
= new_object
;
2247 assert(request
->object
== new_object
);
2249 releaseNotifyObject(old_object
);
2251 object
= new_object
;
2253 objectMetadataChanged(new_object
, 0);
2256 if(object
->flags
& OBJECT_VALIDATING
) {
2257 object
->flags
&= ~OBJECT_VALIDATING
;
2258 notifyObject(object
);
2262 httpServerFinish(connection
, 0, rc
);
2266 if(request
->request
== NULL
) {
2267 httpServerFinish(connection
, 1, 0);
2272 /* 412 replies contain a useless body. For now, we
2273 drop the connection. */
2274 httpServerFinish(connection
, 1, 0);
2279 if(request
->flags
& REQUEST_PERSISTENT
) {
2280 if(request
->method
!= METHOD_HEAD
&&
2281 connection
->te
== TE_IDENTITY
&& len
< 0) {
2282 do_log(L_ERROR
, "Persistent reply with no Content-Length\n");
2283 /* That's potentially dangerous, as we could start reading
2284 arbitrary data into the object. Unfortunately, some
2286 request
->flags
&= ~REQUEST_PERSISTENT
;
2290 /* we're getting a body */
2291 if(content_range
.from
> 0)
2292 connection
->offset
= content_range
.from
;
2294 connection
->offset
= 0;
2296 if(content_range
.to
>= 0)
2297 request
->to
= content_range
.to
;
2299 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2300 (unsigned long)connection
, (unsigned long)object
,
2301 connection
->offset
);
2303 if(connection
->len
> rc
) {
2304 rc
= connectionAddData(connection
, rc
);
2308 do_log(L_ERROR
, "Couldn't parse chunk size.\n");
2309 httpServerAbort(connection
, 1, 502,
2310 internAtom("Couldn't parse chunk size"));
2312 do_log(L_ERROR
, "Couldn't add data to connection.\n");
2313 httpServerAbort(connection
, 1, 500,
2314 internAtom("Couldn't add data "
2320 if(object
->length
< 0) {
2321 object
->length
= object
->size
;
2322 objectMetadataChanged(object
, 0);
2323 } else if(object
->length
!= object
->size
) {
2324 httpServerAbort(connection
, 1, 500,
2325 internAtom("Inconsistent "
2327 object
->length
= -1;
2331 httpServerFinish(connection
, 0, 0);
2336 connection
->len
= 0;
2340 if(connection
->te
== TE_CHUNKED
||
2341 (object
->length
>= 0 &&
2342 connection
->offset
< object
->length
)) {
2343 do_log(L_ERROR
, "Server closed connection.\n");
2344 httpServerAbort(connection
, 1, 502,
2345 internAtom("Server closed connection"));
2348 if(code
!= 206 && eof
> 0 && object
->length
< 0) {
2349 object
->length
= object
->size
;
2350 objectMetadataChanged(object
, 0);
2352 httpServerFinish(connection
, 1, 0);
2356 return httpServerReadData(connection
, 1);
2362 releaseAtom(message
);
2364 releaseAtom(headers
);
2373 httpServerIndirectHandlerCommon(HTTPConnectionPtr connection
, int eof
)
2375 HTTPRequestPtr request
= connection
->request
;
2378 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
2380 if(connection
->len
> 0) {
2382 rc
= connectionAddData(connection
, 0);
2386 do_log(L_ERROR
, "Couldn't parse chunk size.\n");
2387 httpServerAbort(connection
, 1, 502,
2388 internAtom("Couldn't parse chunk size"));
2390 do_log(L_ERROR
, "Couldn't add data to connection.\n");
2391 httpServerAbort(connection
, 1, 500,
2392 internAtom("Couldn't add data "
2397 if(request
->to
< 0) {
2398 if(request
->object
->length
< 0) {
2399 request
->object
->length
= request
->object
->size
;
2400 objectMetadataChanged(request
->object
, 0);
2401 } else if(request
->object
->length
!=
2402 request
->object
->size
) {
2403 request
->object
->length
= -1;
2404 httpServerAbort(connection
, 1, 502,
2405 internAtom("Inconsistent "
2410 httpServerFinish(connection
, 0, 0);
2416 if(eof
&& connection
->len
== 0) {
2417 if(connection
->te
== TE_CHUNKED
||
2418 (request
->to
>= 0 && connection
->offset
< request
->to
)) {
2419 do_log(L_ERROR
, "Server dropped connection.\n");
2420 httpServerAbort(connection
, 1, 502,
2421 internAtom("Server dropped connection"));
2424 if(request
->object
->length
< 0 && eof
> 0 &&
2425 (request
->to
< 0 || request
->to
> request
->object
->size
)) {
2426 request
->object
->length
= request
->object
->size
;
2427 objectMetadataChanged(request
->object
, 0);
2429 httpServerFinish(connection
, 1, 0);
2433 return httpServerReadData(connection
, 0);
2438 httpServerIndirectHandler(int status
,
2439 FdEventHandlerPtr event
,
2440 StreamRequestPtr srequest
)
2442 HTTPConnectionPtr connection
= srequest
->data
;
2443 assert(connection
->request
->object
->flags
& OBJECT_INPROGRESS
);
2445 httpSetTimeout(connection
, -1);
2447 if(status
!= -ECLIENTRESET
)
2448 do_log_error(L_ERROR
, -status
, "Read from server failed");
2449 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
2450 internAtomError(-status
, "Read from server failed"));
2454 connection
->len
= srequest
->offset
;
2456 return httpServerIndirectHandlerCommon(connection
, status
);
2460 httpServerReadData(HTTPConnectionPtr connection
, int immediate
)
2462 HTTPRequestPtr request
= connection
->request
;
2463 ObjectPtr object
= request
->object
;
2466 assert(object
->flags
& OBJECT_INPROGRESS
);
2468 if(request
->request
== NULL
) {
2469 httpServerFinish(connection
, 1, 0);
2473 if(request
->to
>= 0)
2476 to
= object
->length
;
2478 if(to
>= 0 && to
== connection
->offset
) {
2479 httpServerFinish(connection
, 0, 0);
2483 if(connection
->len
== 0 &&
2484 ((connection
->te
== TE_IDENTITY
&& to
> connection
->offset
) ||
2485 (connection
->te
== TE_CHUNKED
&& connection
->chunk_remaining
> 0))) {
2486 /* Read directly into the object */
2487 int i
= connection
->offset
/ CHUNK_SIZE
;
2488 int j
= connection
->offset
% CHUNK_SIZE
;
2490 /* See httpServerDirectHandlerCommon if you change this */
2491 if(connection
->te
== TE_CHUNKED
) {
2492 len
= connection
->chunk_remaining
;
2493 /* The logic here is that we want more to just fit the
2494 chunk header if we're doing a large read, but do a
2495 large read if we would otherwise do a small one. The
2496 magic constant 2000 comes from the assumption that the
2497 server uses chunks that have a size that are a power of
2498 two (possibly including the chunk header), and that we
2499 want a full ethernet packet to fit into our read. */
2500 more
= (len
>= 2000 ? 20 : MIN(2048 - len
, CHUNK_SIZE
));
2502 len
= to
- connection
->offset
;
2503 /* We read more data only when there is a reasonable
2504 chance of there being another reply coming. */
2505 more
= (connection
->pipelined
> 1) ? CHUNK_SIZE
: 0;
2507 end
= len
+ connection
->offset
;
2509 httpConnectionDestroyBuf(connection
);
2511 /* The order of allocation is important in case we run out of
2513 lockChunk(object
, i
);
2514 if(object
->chunks
[i
].data
== NULL
)
2515 object
->chunks
[i
].data
= get_chunk();
2516 if(object
->chunks
[i
].data
&& object
->chunks
[i
].size
>= j
) {
2517 if(len
+ j
> CHUNK_SIZE
) {
2518 lockChunk(object
, i
+ 1);
2519 if(object
->chunks
[i
+ 1].data
== NULL
)
2520 object
->chunks
[i
+ 1].data
= get_chunk();
2521 /* Unless we're grabbing all len of data, we do not
2522 want to do an indirect read immediately afterwards. */
2523 if(more
&& len
+ j
<= 2 * CHUNK_SIZE
) {
2524 if(!connection
->buf
)
2525 connection
->buf
= get_chunk(); /* checked below */
2527 if(object
->chunks
[i
+ 1].data
) {
2528 do_stream_3(IO_READ
| IO_NOTNOW
, connection
->fd
, j
,
2529 object
->chunks
[i
].data
, CHUNK_SIZE
,
2530 object
->chunks
[i
+ 1].data
,
2532 end
- (i
+ 1) * CHUNK_SIZE
),
2533 connection
->buf
, connection
->buf
? more
: 0,
2534 httpServerDirectHandler2
, connection
);
2537 unlockChunk(object
, i
+ 1);
2539 if(more
&& len
+ j
<= CHUNK_SIZE
) {
2540 if(!connection
->buf
)
2541 connection
->buf
= get_chunk();
2543 do_stream_2(IO_READ
| IO_NOTNOW
, connection
->fd
, j
,
2544 object
->chunks
[i
].data
,
2545 MIN(CHUNK_SIZE
, end
- i
* CHUNK_SIZE
),
2546 connection
->buf
, connection
->buf
? more
: 0,
2547 httpServerDirectHandler
, connection
);
2550 unlockChunk(object
, i
);
2554 if(connection
->len
== 0)
2555 httpConnectionDestroyBuf(connection
);
2557 httpSetTimeout(connection
, serverTimeout
);
2558 do_stream_buf(IO_READ
| IO_NOTNOW
|
2559 ((immediate
&& connection
->len
) ? IO_IMMEDIATE
: 0),
2560 connection
->fd
, connection
->len
,
2562 (connection
->te
== TE_CHUNKED
?
2563 MIN(2048, CHUNK_SIZE
) : CHUNK_SIZE
),
2564 httpServerIndirectHandler
, connection
);
2569 httpServerDirectHandlerCommon(int kind
, int status
,
2570 FdEventHandlerPtr event
,
2571 StreamRequestPtr srequest
)
2573 HTTPConnectionPtr connection
= srequest
->data
;
2574 HTTPRequestPtr request
= connection
->request
;
2575 ObjectPtr object
= request
->object
;
2576 int i
= connection
->offset
/ CHUNK_SIZE
;
2579 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
2581 httpSetTimeout(connection
, -1);
2584 unlockChunk(object
, i
);
2585 if(kind
== 2) unlockChunk(object
, i
+ 1);
2586 if(status
!= -ECLIENTRESET
)
2587 do_log_error(L_ERROR
, -status
, "Read from server failed");
2588 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
2589 internAtomError(-status
, "Read from server failed"));
2593 /* We have incestuous knowledge of the decisions made in
2594 httpServerReadData */
2595 if(request
->to
>= 0)
2598 to
= object
->length
;
2599 if(connection
->te
== TE_CHUNKED
)
2600 end
= connection
->offset
+ connection
->chunk_remaining
;
2603 /* The amount of data actually read into the object */
2604 end1
= MIN(end
, i
* CHUNK_SIZE
+ MIN(kind
* CHUNK_SIZE
, srequest
->offset
));
2606 assert(end
>= 0 && end1
>= i
* CHUNK_SIZE
&& end1
<= (i
+ 2) * CHUNK_SIZE
);
2608 object
->chunks
[i
].size
=
2609 MAX(object
->chunks
[i
].size
, MIN(end1
- i
* CHUNK_SIZE
, CHUNK_SIZE
));
2610 if(kind
== 2 && end1
> (i
+ 1) * CHUNK_SIZE
) {
2611 object
->chunks
[i
+ 1].size
=
2612 MAX(object
->chunks
[i
+ 1].size
, end1
- (i
+ 1) * CHUNK_SIZE
);
2614 if(connection
->te
== TE_CHUNKED
) {
2615 connection
->chunk_remaining
-= (end1
- connection
->offset
);
2616 assert(connection
->chunk_remaining
>= 0);
2618 connection
->offset
= end1
;
2619 object
->size
= MAX(object
->size
, end1
);
2620 unlockChunk(object
, i
);
2621 if(kind
== 2) unlockChunk(object
, i
+ 1);
2623 if(i
* CHUNK_SIZE
+ srequest
->offset
> end1
) {
2624 connection
->len
= i
* CHUNK_SIZE
+ srequest
->offset
- end1
;
2625 return httpServerIndirectHandlerCommon(connection
, status
);
2627 notifyObject(object
);
2629 httpServerFinish(connection
, 1, 0);
2632 return httpServerReadData(connection
, 0);
2638 httpServerDirectHandler(int status
,
2639 FdEventHandlerPtr event
,
2640 StreamRequestPtr srequest
)
2642 return httpServerDirectHandlerCommon(1, status
, event
, srequest
);
2646 httpServerDirectHandler2(int status
,
2647 FdEventHandlerPtr event
,
2648 StreamRequestPtr srequest
)
2650 return httpServerDirectHandlerCommon(2, status
, event
, srequest
);
2653 /* Add the data accumulated in connection->buf into the object in
2654 connection->request. Returns 0 in the normal case, 1 if the TE is
2655 self-terminating and we're done, -1 if there was a problem with
2656 objectAddData, -2 if there was a problem with the data. */
2658 connectionAddData(HTTPConnectionPtr connection
, int skip
)
2660 HTTPRequestPtr request
= connection
->request
;
2661 ObjectPtr object
= request
->object
;
2664 if(connection
->te
== TE_IDENTITY
) {
2667 len
= connection
->len
- skip
;
2668 if(object
->length
>= 0) {
2669 len
= MIN(object
->length
- connection
->offset
, len
);
2671 if(request
->to
>= 0)
2672 len
= MIN(request
->to
- connection
->offset
, len
);
2674 rc
= objectAddData(object
, connection
->buf
+ skip
,
2675 connection
->offset
, len
);
2678 connection
->offset
+= len
;
2679 connection
->len
-= (len
+ skip
);
2680 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2681 (unsigned long)connection
, (unsigned long)object
,
2682 connection
->offset
);
2685 if(connection
->len
> 0 && skip
+ len
> 0) {
2686 memmove(connection
->buf
,
2687 connection
->buf
+ skip
+ len
, connection
->len
);
2690 if((object
->length
>= 0 && object
->length
<= connection
->offset
) ||
2691 (request
->to
>= 0 && request
->to
<= connection
->offset
)) {
2692 notifyObject(object
);
2696 notifyObject(object
);
2699 } else if(connection
->te
== TE_CHUNKED
) {
2700 int i
= skip
, j
, size
;
2701 /* connection->chunk_remaining is 0 at the end of a chunk, -1
2702 after the CR/LF pair ending a chunk, and -2 after we've
2703 seen a chunk of length 0. */
2704 if(connection
->chunk_remaining
> -2) {
2706 if(connection
->chunk_remaining
<= 0) {
2707 if(connection
->chunk_remaining
== 0) {
2708 if(connection
->len
< i
+ 2)
2710 if(connection
->buf
[i
] != '\r' ||
2711 connection
->buf
[i
+ 1] != '\n')
2714 connection
->chunk_remaining
= -1;
2716 if(connection
->len
< i
+ 2)
2718 j
= parseChunkSize(connection
->buf
, i
,
2719 connection
->len
, &size
);
2727 connection
->chunk_remaining
= -2;
2730 connection
->chunk_remaining
= size
;
2733 /* connection->chunk_remaining > 0 */
2734 size
= MIN(connection
->chunk_remaining
,
2735 connection
->len
- i
);
2738 rc
= objectAddData(object
, connection
->buf
+ i
,
2739 connection
->offset
, size
);
2740 connection
->offset
+= size
;
2744 connection
->chunk_remaining
-= size
;
2745 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2746 (unsigned long)connection
,
2747 (unsigned long)object
,
2748 connection
->offset
);
2752 connection
->len
-= i
;
2753 if(connection
->len
> 0)
2754 memmove(connection
->buf
, connection
->buf
+ i
, connection
->len
);
2755 if(i
> 0 || connection
->chunk_remaining
== -2)
2756 notifyObject(object
);
2757 if(connection
->chunk_remaining
== -2)
2767 listServers(FILE *out
)
2769 HTTPServerPtr server
;
2772 fprintf(out
, "<!DOCTYPE HTML PUBLIC "
2773 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2774 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2776 "\r\n<title>Known servers</title>\n"
2778 "<h1>Known servers</h1>\n");
2780 alternatingHttpStyle(out
, "servers");
2781 fprintf(out
, "<table id=servers>\n");
2782 fprintf(out
, "<thead><tr><th>Server</th>"
2784 "<th>Persistent</th>"
2786 "<th>Connections</th>"
2791 fprintf(out
, "<tbody>\n");
2795 fprintf(out
, "<tr class=\"%s\">", entry
% 2 == 0 ? "even" : "odd");
2796 if(server
->port
== 80)
2797 fprintf(out
, "<td>%s</td>", server
->name
);
2799 fprintf(out
, "<td>%s:%d</td>", server
->name
, server
->port
);
2801 if(server
->version
== HTTP_11
)
2802 fprintf(out
, "<td>1.1</td>");
2803 else if(server
->version
== HTTP_10
)
2804 fprintf(out
, "<td>1.0</td>");
2806 fprintf(out
, "<td>unknown</td>");
2808 if(server
->persistent
< 0)
2809 fprintf(out
, "<td>no</td>");
2810 else if(server
->persistent
> 0)
2811 fprintf(out
, "<td>yes</td>");
2813 fprintf(out
, "<td>unknown</td>");
2815 if(server
->version
!= HTTP_11
|| server
->persistent
<= 0)
2816 fprintf(out
, "<td></td>");
2817 else if(server
->pipeline
< 0)
2818 fprintf(out
, "<td>no</td>");
2819 else if(server
->pipeline
>= 0 && server
->pipeline
<= 1)
2820 fprintf(out
, "<td>unknown</td>");
2821 else if(server
->pipeline
== 2 || server
->pipeline
== 3)
2822 fprintf(out
, "<td>probing</td>");
2824 fprintf(out
, "<td>yes</td>");
2827 for(i
= 0; i
< server
->maxslots
; i
++)
2828 if(server
->connection
[i
] && !server
->connection
[i
]->connecting
) {
2829 if(i
< server
->numslots
)
2835 fprintf(out
, "<td>%d/%d", n
, server
->numslots
);
2837 fprintf(out
, " + %d</td>", m
);
2839 fprintf(out
, "</td>");
2841 if(server
->lies
> 0)
2842 fprintf(out
, "<td>(%d lies)</td>", (server
->lies
+ 9) / 10);
2844 fprintf(out
, "<td></td>");
2847 fprintf(out
, "<td>%.3f</td>", (double)server
->rtt
/ 1000000.0);
2849 fprintf(out
, "<td></td>");
2850 if(server
->rate
> 0)
2851 fprintf(out
, "<td>%d</td>", server
->rate
);
2853 fprintf(out
, "<td></td>");
2855 fprintf(out
, "</tr>\n");
2856 server
= server
->next
;
2859 fprintf(out
, "</tbody>\n");
2860 fprintf(out
, "</table>\n");
2861 fprintf(out
, "<p><a href=\"/polipo/\">back</a></p>");
2862 fprintf(out
, "</body></html>\n");