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
->flags
|= REQUEST_PIPELINED
;
826 request
->time0
= current_time
;
828 server
->request
= request
->next
;
829 request
->next
= NULL
;
830 if(server
->request
== NULL
)
831 server
->request_last
= NULL
;
832 httpQueueRequest(connection
, request
);
833 connection
->pipelined
++;
835 if(server
->persistent
> 0 && server
->pipeline
== 1 && i
>= 2)
836 server
->pipeline
= 2;
838 if(i
> 0) httpServerSendRequest(connection
);
840 if(idle
&& connection
->pipelined
> 0)
841 httpServerReply(connection
, 0);
846 for(i
= 0; i
< server
->maxslots
; i
++) {
847 if(server
->connection
[i
] &&
848 !server
->connection
[i
]->connecting
&&
849 !server
->connection
[i
]->request
) {
850 /* Artificially age any fresh connections that aren't used
851 straight away; this is necessary for the logic for POST and
852 the logic that determines whether a given request should be
854 if(server
->connection
[i
]->serviced
== 0)
855 server
->connection
[i
]->serviced
= 1;
856 if(!server
->idleHandler
[i
])
857 server
->idleHandler
[i
] =
858 registerFdEvent(server
->connection
[i
]->fd
, POLLIN
,
859 httpServerIdleHandler
,
860 sizeof(HTTPConnectionPtr
),
861 &server
->connection
[i
]);
862 if(!server
->idleHandler
[i
]) {
863 do_log(L_ERROR
, "Couldn't register idle handler.\n");
864 httpServerFinish(server
->connection
[i
], 1, 0);
873 httpServerSideRequest(HTTPServerPtr server
)
875 HTTPRequestPtr request
= server
->request
;
876 HTTPConnectionPtr connection
;
877 HTTPRequestPtr requestor
= request
->request
;
878 HTTPConnectionPtr client
= requestor
->connection
;
879 int rc
, i
, freeslots
, idle
, connecting
;
881 assert(REQUEST_SIDE(request
));
888 /* Find a fresh connection */
889 for(i
= 0; i
< server
->numslots
; i
++) {
890 if(!server
->connection
[i
])
892 else if(!server
->connection
[i
]->connecting
) {
893 if(!server
->connection
[i
]->request
) {
894 if(server
->connection
[i
]->serviced
== 0) {
895 if(server
->idleHandler
[i
])
896 unregisterFdEvent(server
->idleHandler
[i
]);
897 server
->idleHandler
[i
] = NULL
;
898 connection
= server
->connection
[i
];
910 /* Make sure that a fresh connection will be established at some
911 point, then wait until httpServerTrigger calls us again. */
913 httpServerConnection(server
);
916 /* Shutdown a random idle connection */
917 pokeFdEvent(server
->connection
[idle
]->fd
,
918 -EDOSHUTDOWN
, POLLIN
| POLLOUT
);
924 rc
= httpWriteRequest(connection
, request
, client
->bodylen
);
926 do_log(L_ERROR
, "Couldn't write POST or PUT request.\n");
927 httpServerAbortRequest(request
, rc
!= -ECLIENTRESET
, 503,
928 internAtom("Couldn't write request"));
931 server
->request
= request
->next
;
932 request
->next
= NULL
;
933 if(server
->request
== NULL
)
934 server
->request_last
= NULL
;
935 httpQueueRequest(connection
, request
);
936 connection
->pipelined
= 1;
937 request
->time0
= current_time
;
938 connection
->reqoffset
= 0;
939 connection
->bodylen
= client
->bodylen
;
940 httpServerDoSide(connection
);
945 httpServerDoSide(HTTPConnectionPtr connection
)
947 HTTPRequestPtr request
= connection
->request
;
948 HTTPRequestPtr requestor
= request
->request
;
949 HTTPConnectionPtr client
= requestor
->connection
;
950 int len
= MIN(client
->reqlen
- client
->reqbegin
,
951 connection
->bodylen
- connection
->reqoffset
);
955 client
->reqbegin
> 0 ||
956 (connection
->reqoffset
+ client
->reqlen
- client
->reqbegin
) >=
957 connection
->bodylen
);
958 int done
= connection
->reqoffset
>= connection
->bodylen
;
960 assert(connection
->bodylen
>= 0);
962 httpSetTimeout(connection
, 60);
964 if(connection
->reqlen
> 0) {
965 /* Send the headers, but don't send any part of the body if
966 we're in wait_continue. */
967 do_stream_2(IO_WRITE
,
969 connection
->reqbuf
, connection
->reqlen
,
970 client
->reqbuf
+ client
->reqbegin
,
971 (request
->flags
& REQUEST_WAIT_CONTINUE
) ? 0 : len
,
972 httpServerSideHandler2
, connection
);
973 httpServerReply(connection
, 0);
974 } else if(request
->object
->flags
& OBJECT_ABORTED
) {
975 if(connection
->reqbuf
)
976 dispose_chunk(connection
->reqbuf
);
977 connection
->reqbuf
= NULL
;
978 connection
->reqlen
= 0;
979 pokeFdEvent(connection
->fd
, -ESHUTDOWN
, POLLIN
);
980 client
->flags
|= CONN_SIDE_READER
;
981 do_stream(IO_READ
| IO_IMMEDIATE
,
982 client
->fd
, 0, NULL
, 0,
983 httpClientSideHandler
, client
);
984 } else if(!(request
->flags
& REQUEST_WAIT_CONTINUE
) && doflush
) {
985 /* Make sure there's a reqbuf, as httpServerFinish uses
986 it to determine if there's a writer. */
987 if(connection
->reqbuf
== NULL
)
988 connection
->reqbuf
= get_chunk();
989 assert(connection
->reqbuf
!= NULL
);
992 client
->reqbuf
+ client
->reqbegin
, len
,
993 httpServerSideHandler
, connection
);
995 if(connection
->reqbuf
) {
996 httpConnectionDestroyReqbuf(connection
);
997 connection
->reqlen
= 0;
999 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
1000 ConditionHandlerPtr chandler
;
1001 do_log(D_SERVER_CONN
, "W... %s:%d.\n",
1002 connection
->server
->name
, connection
->server
->port
);
1004 conditionWait(&request
->object
->condition
,
1005 httpServerContinueConditionHandler
,
1006 sizeof(connection
), &connection
);
1010 do_log(L_ERROR
, "Couldn't register condition handler.\n");
1011 /* Fall through -- the client side will clean up. */
1013 client
->flags
|= CONN_SIDE_READER
;
1014 do_stream(IO_READ
| (done
? IO_IMMEDIATE
: 0 ) | IO_NOTNOW
,
1015 client
->fd
, client
->reqlen
,
1016 client
->reqbuf
, CHUNK_SIZE
,
1017 httpClientSideHandler
, client
);
1023 httpClientDelayedDoSideHandler(TimeEventHandlerPtr event
)
1025 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1026 httpServerDoSide(connection
);
1031 httpServerDelayedDoSide(HTTPConnectionPtr connection
)
1033 TimeEventHandlerPtr handler
;
1034 handler
= scheduleTimeEvent(1, httpClientDelayedDoSideHandler
,
1035 sizeof(connection
), &connection
);
1037 do_log(L_ERROR
, "Couldn't schedule DoSide -- freeing memory.\n");
1038 free_chunk_arenas();
1039 handler
= scheduleTimeEvent(1, httpClientDelayedDoSideHandler
,
1040 sizeof(connection
), &connection
);
1041 do_log(L_ERROR
, "Couldn't schedule DoSide.\n");
1042 /* Somebody will hopefully end up timing out. */
1049 httpServerSideHandlerCommon(int kind
, int status
,
1050 FdEventHandlerPtr event
,
1051 StreamRequestPtr srequest
)
1053 HTTPConnectionPtr connection
= srequest
->data
;
1054 HTTPRequestPtr request
= connection
->request
;
1055 HTTPRequestPtr requestor
= request
->request
;
1056 HTTPConnectionPtr client
= requestor
->connection
;
1059 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1062 do_log_error(L_ERROR
, -status
, "Couldn't write to server");
1063 httpConnectionDestroyReqbuf(connection
);
1064 if(status
!= -ECLIENTRESET
)
1065 shutdown(connection
->fd
, 2);
1066 abortObject(request
->object
, 503,
1067 internAtom("Couldn't write to server"));
1068 /* Let the read side handle the error */
1069 httpServerDoSide(connection
);
1073 assert(srequest
->offset
> 0);
1076 if(srequest
->offset
< connection
->reqlen
)
1078 bodylen
= srequest
->offset
- connection
->reqlen
;
1079 connection
->reqlen
= 0;
1080 httpConnectionDestroyReqbuf(connection
);
1082 bodylen
= srequest
->offset
;
1086 assert(client
->reqbegin
+ bodylen
<= client
->reqlen
);
1088 if(client
->reqlen
> client
->reqbegin
+ bodylen
)
1089 memmove(client
->reqbuf
, client
->reqbuf
+ client
->reqbegin
+ bodylen
,
1090 client
->reqlen
- client
->reqbegin
- bodylen
);
1091 client
->reqlen
-= bodylen
+ client
->reqbegin
;
1092 client
->reqbegin
= 0;
1093 connection
->reqoffset
+= bodylen
;
1094 httpServerDoSide(connection
);
1099 httpServerSideHandler(int status
,
1100 FdEventHandlerPtr event
,
1101 StreamRequestPtr srequest
)
1103 return httpServerSideHandlerCommon(1, status
, event
, srequest
);
1107 httpServerSideHandler2(int status
,
1108 FdEventHandlerPtr event
,
1109 StreamRequestPtr srequest
)
1111 return httpServerSideHandlerCommon(2, status
, event
, srequest
);
1115 httpServerContinueConditionHandler(int status
, ConditionHandlerPtr chandler
)
1117 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)chandler
->data
;
1119 if(connection
->request
->flags
& REQUEST_WAIT_CONTINUE
)
1121 httpServerDelayedDoSide(connection
);
1125 /* s is 0 to keep the connection alive, 1 to shutdown the connection,
1126 and -1 to keep the connection alive and keep the current request. */
1128 httpServerFinish(HTTPConnectionPtr connection
, int s
, int offset
)
1130 HTTPServerPtr server
= connection
->server
;
1131 HTTPRequestPtr request
= connection
->request
;
1135 assert(connection
->pipelined
>= 1);
1136 assert((connection
->pipelined
> 1) == (request
->next
!= NULL
));
1138 assert(connection
->pipelined
== 0);
1141 if(s
== 0 && (!connection
->request
||
1142 !(connection
->request
->flags
& REQUEST_PERSISTENT
)))
1145 if(connection
->reqbuf
) {
1146 /* As most normal requests go out in a single packet, this is
1147 extremely unlikely to happen. As for POST/PUT requests,
1148 they are not pipelined, so this can only happen if the
1149 server sent an error reply early. */
1150 assert(connection
->fd
>= 0);
1151 shutdown(connection
->fd
, 1);
1152 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLOUT
);
1153 httpServerDelayedFinish(connection
);
1157 if(s
>= 0 && request
) {
1158 /* Update statistics about the server */
1159 int size
= -1, d
= -1, rtt
= -1, rate
= -1;
1160 if(connection
->offset
> 0 && request
->from
>= 0)
1161 size
= connection
->offset
- request
->from
;
1162 if(request
->time1
.tv_sec
!= null_time
.tv_sec
) {
1163 d
= timeval_minus_usec(¤t_time
, &request
->time1
);
1164 if(!(request
->flags
& REQUEST_PIPELINED
) &&
1165 request
->time0
.tv_sec
!= null_time
.tv_sec
)
1166 rtt
= timeval_minus_usec(&request
->time1
, &request
->time0
);
1167 if(size
>= 8192 && d
> 50000)
1168 rate
= ((double)size
/ (double)d
) * 1000000.0 + 0.5;
1170 request
->time0
= null_time
;
1171 request
->time1
= null_time
;
1174 if(server
->rtt
>= 0)
1175 server
->rtt
= (3 * server
->rtt
+ rtt
+ 2) / 4;
1180 if(server
->rate
>= 0)
1181 server
->rate
= (3 * server
->rate
+ rate
+ 2) / 4;
1183 server
->rate
= rate
;
1186 httpDequeueRequest(connection
);
1187 connection
->pipelined
--;
1188 request
->object
->flags
&= ~(OBJECT_INPROGRESS
| OBJECT_VALIDATING
);
1189 if(request
->request
) {
1190 request
->request
->request
= NULL
;
1191 request
->request
= NULL
;
1193 releaseNotifyObject(request
->object
);
1194 request
->object
= NULL
;
1195 httpDestroyRequest(request
);
1198 do_log(D_SERVER_CONN
, "Done with server %s:%d connection (%d)\n",
1199 connection
->server
->name
, connection
->server
->port
, s
);
1201 assert(offset
<= connection
->len
);
1204 if(offset
< connection
->len
) {
1205 assert(connection
->buf
!= NULL
);
1206 if(!connection
->pipelined
) {
1208 "Closing connection to %s:%d: "
1209 "%d stray bytes of data.\n",
1210 server
->name
, server
->port
, connection
->len
- offset
);
1213 memmove(connection
->buf
, connection
->buf
+ offset
,
1214 connection
->len
- offset
);
1215 connection
->len
= connection
->len
- offset
;
1216 if((connection
->flags
& CONN_BIGBUF
) &&
1217 connection
->len
<= CHUNK_SIZE
)
1218 httpConnectionUnbigify(connection
);
1221 connection
->len
= 0;
1225 connection
->server
->time
= current_time
.tv_sec
;
1226 connection
->serviced
++;
1229 if(connection
->timeout
)
1230 cancelTimeEvent(connection
->timeout
);
1231 connection
->timeout
= NULL
;
1232 httpConnectionDestroyBuf(connection
);
1233 if(connection
->fd
>= 0)
1234 close(connection
->fd
);
1235 connection
->fd
= -1;
1236 server
->persistent
-= 1;
1237 if(server
->persistent
< -5)
1238 server
->numslots
= MIN(server
->maxslots
, serverMaxSlots
);
1239 if(connection
->request
) {
1241 do_log(D_SERVER_CONN
, "Restarting pipeline to %s:%d.\n",
1242 server
->name
, server
->port
);
1243 if(server
->pipeline
== 2)
1244 server
->pipeline
-= 20;
1246 server
->pipeline
-= 5;
1247 req
= connection
->request
;
1249 req
->connection
= NULL
;
1253 connection
->request_last
->next
= server
->request
;
1255 server
->request_last
= connection
->request_last
;
1256 server
->request
= connection
->request
;
1257 connection
->request
= NULL
;
1258 connection
->request_last
= NULL
;
1260 /* Make sure we don't get confused into thinking a probe
1262 if(server
->pipeline
== 2 || server
->pipeline
== 3)
1263 server
->pipeline
= 1;
1264 for(i
= 0; i
< server
->maxslots
; i
++)
1265 if(connection
== server
->connection
[i
])
1267 assert(i
< server
->maxslots
);
1268 if(server
->idleHandler
[i
])
1269 unregisterFdEvent(server
->idleHandler
[i
]);
1270 server
->idleHandler
[i
] = NULL
;
1271 server
->connection
[i
] = NULL
;
1274 server
->persistent
+= 1;
1275 if(server
->persistent
> 0)
1276 server
->numslots
= MIN(server
->maxslots
, serverSlots
);
1277 httpSetTimeout(connection
, serverTimeout
);
1278 /* See httpServerTrigger */
1279 if(connection
->pipelined
||
1280 (server
->version
== HTTP_11
&& server
->pipeline
<= 0) ||
1281 (server
->pipeline
== 3)) {
1284 if(s
< 0 || connection
->pipelined
) {
1285 httpServerReply(connection
, 1);
1287 httpConnectionDestroyBuf(connection
);
1292 httpServerTrigger(server
);
1296 httpServerDelayedFinishHandler(TimeEventHandlerPtr event
)
1298 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1299 httpServerFinish(connection
, 1, 0);
1304 httpServerDelayedFinish(HTTPConnectionPtr connection
)
1306 TimeEventHandlerPtr handler
;
1308 handler
= scheduleTimeEvent(1, httpServerDelayedFinishHandler
,
1309 sizeof(connection
), &connection
);
1312 "Couldn't schedule delayed finish -- freeing memory.");
1313 free_chunk_arenas();
1314 handler
= scheduleTimeEvent(1, httpServerDelayedFinishHandler
,
1315 sizeof(connection
), &connection
);
1318 "Couldn't schedule delayed finish -- aborting.\n");
1325 httpServerReply(HTTPConnectionPtr connection
, int immediate
)
1327 assert(connection
->pipelined
> 0);
1329 if(connection
->request
->request
== NULL
) {
1330 do_log(L_WARN
, "Aborting pipeline on %s:%d.\n",
1331 connection
->server
->name
, connection
->server
->port
);
1332 httpServerFinish(connection
, 1, 0);
1336 do_log(D_SERVER_CONN
, "R: ");
1337 do_log_n(D_SERVER_CONN
, connection
->request
->object
->key
,
1338 connection
->request
->object
->key_size
);
1339 do_log(D_SERVER_CONN
, " (%d)\n", connection
->request
->method
);
1341 if(connection
->len
== 0)
1342 httpConnectionDestroyBuf(connection
);
1344 httpSetTimeout(connection
, serverTimeout
);
1345 do_stream_buf(IO_READ
| (immediate
? IO_IMMEDIATE
: 0) | IO_NOTNOW
,
1346 connection
->fd
, connection
->len
,
1347 &connection
->buf
, CHUNK_SIZE
,
1348 httpServerReplyHandler
, connection
);
1352 httpConnectionPipelined(HTTPConnectionPtr connection
)
1354 HTTPRequestPtr request
= connection
->request
;
1358 request
= request
->next
;
1364 httpServerUnpipeline(HTTPRequestPtr request
)
1366 HTTPConnectionPtr connection
= request
->connection
;
1367 HTTPServerPtr server
= connection
->server
;
1369 request
->flags
&= ~REQUEST_PERSISTENT
;
1373 "Restarting pipeline to %s:%d.\n",
1374 connection
->server
->name
, connection
->server
->port
);
1375 req
= request
->next
;
1377 req
->connection
= NULL
;
1381 connection
->request_last
->next
= server
->request
;
1383 server
->request_last
= connection
->request_last
;
1384 server
->request
= request
->next
;
1385 request
->next
= NULL
;
1386 connection
->request_last
= request
;
1388 connection
->pipelined
= httpConnectionPipelined(connection
);
1392 httpServerRestart(HTTPConnectionPtr connection
)
1394 HTTPServerPtr server
= connection
->server
;
1395 HTTPRequestPtr request
= connection
->request
;
1401 "Restarting pipeline to %s:%d.\n",
1402 connection
->server
->name
, connection
->server
->port
);
1405 req
->connection
= NULL
;
1409 connection
->request_last
->next
= server
->request
;
1411 server
->request_last
= connection
->request_last
;
1412 server
->request
= request
;
1413 connection
->request
= NULL
;
1414 connection
->request_last
= NULL
;
1416 connection
->pipelined
= 0;
1417 httpServerFinish(connection
, 1, 0);
1421 httpServerRequest(ObjectPtr object
, int method
, int from
, int to
,
1422 HTTPRequestPtr requestor
, void *closure
)
1429 assert(from
>= 0 && (to
< 0 || to
> from
));
1430 assert(closure
== NULL
);
1431 assert(!(object
->flags
& OBJECT_LOCAL
));
1432 assert(object
->type
== OBJECT_HTTP
);
1434 if(object
->flags
& OBJECT_INPROGRESS
)
1437 if(requestor
->flags
& REQUEST_REQUESTED
)
1440 assert(requestor
->request
== NULL
);
1445 rc
= parseUrl(object
->key
, object
->key_size
, &x
, &y
, &port
, &z
);
1447 if(rc
< 0 || x
< 0 || y
< 0 || y
- x
> 131) {
1448 do_log(L_ERROR
, "Couldn't parse URL: ");
1449 do_log_n(L_ERROR
, object
->key
, object
->key_size
);
1450 do_log(L_ERROR
, "\n");
1451 abortObject(object
, 400, internAtom("Couldn't parse URL"));
1452 notifyObject(object
);
1456 if(!intListMember(port
, allowedPorts
)) {
1457 do_log(L_ERROR
, "Attempted connection to port %d.\n", port
);
1458 abortObject(object
, 403, internAtom("Forbidden port"));
1459 notifyObject(object
);
1463 memcpy(name
, ((char*)object
->key
) + x
, y
- x
);
1466 requestor
->flags
|= REQUEST_REQUESTED
;
1467 rc
= httpMakeServerRequest(name
, port
, object
, method
, from
, to
,
1472 503, internAtom("Couldn't schedule server request"));
1473 notifyObject(object
);
1481 httpWriteRequest(HTTPConnectionPtr connection
, HTTPRequestPtr request
,
1484 ObjectPtr object
= request
->object
;
1485 int from
= request
->from
, to
= request
->to
, method
= request
->method
;
1486 char *url
= object
->key
, *m
;
1487 int url_size
= object
->key_size
;
1488 int x
, y
, port
, z
, location_size
;
1490 int l
, n
, rc
, bufsize
;
1492 assert(method
!= METHOD_NONE
);
1494 if(request
->method
== METHOD_GET
||
1495 request
->method
== METHOD_CONDITIONAL_GET
) {
1499 do_log(L_ERROR
, "Requesting empty segment?\n");
1504 if(object
->flags
& OBJECT_DYNAMIC
) {
1508 objectFillFromDisk(object
, from
/ CHUNK_SIZE
* CHUNK_SIZE
, 1);
1509 l
= objectHoleSize(request
->object
, from
);
1511 if(to
<= 0 || to
> from
+ l
)
1515 if(pmmSize
&& connection
->server
->pipeline
>= 4) {
1517 to
= to
< 0 ? pmmFirstSize
: MIN(to
, pmmFirstSize
);
1519 to
= to
< 0 ? from
+ pmmSize
: MIN(to
, from
+ pmmSize
);
1522 if(from
% CHUNK_SIZE
!= 0)
1523 if(objectHoleSize(object
, from
- 1) != 0)
1524 from
= from
/ CHUNK_SIZE
* CHUNK_SIZE
;
1528 rc
= parseUrl(url
, url_size
, &x
, &y
, &port
, &z
);
1530 if(rc
< 0 || x
< 0 || y
< 0) {
1534 if(connection
->reqbuf
== NULL
) {
1535 connection
->reqbuf
= get_chunk();
1536 if(connection
->reqbuf
== NULL
)
1538 connection
->reqlen
= 0;
1541 if(method
== METHOD_CONDITIONAL_GET
&&
1542 object
->last_modified
< 0 && object
->etag
== NULL
)
1543 method
= request
->method
= METHOD_GET
;
1547 (connection
->flags
& CONN_BIGREQBUF
) ? bigBufferSize
: CHUNK_SIZE
;
1548 n
= connection
->reqlen
;
1551 case METHOD_CONDITIONAL_GET
: m
= "GET"; break;
1552 case METHOD_HEAD
: m
= "HEAD"; break;
1553 case METHOD_POST
: m
= "POST"; break;
1554 case METHOD_PUT
: m
= "PUT"; break;
1557 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, "%s ", m
);
1559 if(connection
->server
->isProxy
) {
1560 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
,
1563 if(url_size
- z
== 0) {
1568 location_size
= url_size
- z
;
1571 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
,
1572 location
, location_size
);
1575 do_log(D_SERVER_REQ
, "Server request: ");
1576 do_log_n(D_SERVER_REQ
, url
+ x
, y
- x
);
1577 do_log(D_SERVER_REQ
, ": ");
1578 do_log_n(D_SERVER_REQ
, connection
->reqbuf
, n
);
1579 do_log(D_SERVER_REQ
, " (method %d from %d to %d, 0x%lx for 0x%lx)\n",
1581 (unsigned long)connection
, (unsigned long)object
);
1583 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, " HTTP/1.1");
1585 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, "\r\nHost: ");
1586 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
, url
+ x
, y
- x
);
1588 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, ":%d", port
);
1590 if(connection
->server
->isProxy
&& parentAuthCredentials
) {
1591 n
= buildServerAuthHeaders(connection
->reqbuf
, n
, bufsize
,
1592 parentAuthCredentials
);
1596 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1597 "\r\nContent-Length: %d", bodylen
);
1599 if(request
->flags
& REQUEST_WAIT_CONTINUE
)
1600 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1601 "\r\nExpect: 100-continue");
1603 if(method
!= METHOD_HEAD
&& (from
> 0 || to
>= 0)) {
1605 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1606 "\r\nRange: bytes=%d-%d", from
, to
- 1);
1608 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1609 "\r\nRange: bytes=%d-", from
);
1613 if(method
== METHOD_GET
&& object
->etag
&& (from
> 0 || to
>= 0)) {
1614 if(request
->request
&& request
->request
->request
== request
&&
1615 request
->request
->from
== 0 && request
->request
->to
== -1 &&
1616 pmmSize
== 0 && pmmFirstSize
== 0)
1617 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1618 "\r\nIf-Range: \"%s\"", object
->etag
);
1621 if(method
== METHOD_CONDITIONAL_GET
) {
1622 if(object
->last_modified
>= 0) {
1623 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1624 "\r\nIf-Modified-Since: ");
1625 n
= format_time(connection
->reqbuf
, n
, bufsize
,
1626 object
->last_modified
);
1629 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1630 "\r\nIf-None-Match: \"%s\"", object
->etag
);
1634 n
= httpPrintCacheControl(connection
->reqbuf
, n
, bufsize
,
1635 0, &request
->cache_control
);
1639 if(request
->request
&& request
->request
->headers
) {
1640 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
,
1641 request
->request
->headers
->string
,
1642 request
->request
->headers
->length
);
1645 if(request
->request
&& request
->request
->via
) {
1646 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1647 "\r\nVia: %s, 1.1 %s",
1648 request
->request
->via
->string
, proxyName
->string
);
1650 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1656 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1657 "\r\nConnection: %s\r\n\r\n",
1658 (request
->flags
& REQUEST_PERSISTENT
) ?
1659 "keep-alive" : "close");
1660 if(n
< 0 || n
>= bufsize
- 1)
1662 connection
->reqlen
= n
;
1667 if(!(connection
->flags
& CONN_BIGREQBUF
))
1668 rc
= httpConnectionBigifyReqbuf(connection
);
1675 httpServerHandler(int status
,
1676 FdEventHandlerPtr event
,
1677 StreamRequestPtr srequest
)
1679 HTTPConnectionPtr connection
= srequest
->data
;
1682 assert(connection
->request
->object
->flags
& OBJECT_INPROGRESS
);
1684 if(connection
->reqlen
== 0) {
1685 do_log(D_SERVER_REQ
, "Writing aborted on 0x%lx\n",
1686 (unsigned long)connection
);
1690 if(status
== 0 && !streamRequestDone(srequest
)) {
1691 httpSetTimeout(connection
, serverTimeout
);
1695 httpConnectionDestroyReqbuf(connection
);
1698 if(connection
->serviced
>= 1) {
1699 httpServerRestart(connection
);
1702 if(status
>= 0 || status
== ECONNRESET
) {
1703 message
= internAtom("Couldn't send request to server: "
1706 if(status
!= -EPIPE
)
1707 do_log_error(L_ERROR
, -status
,
1708 "Couldn't send request to server");
1710 internAtomError(-status
, "Couldn't send request to server");
1718 dispose_chunk(connection
->reqbuf
);
1719 connection
->reqbuf
= NULL
;
1720 shutdown(connection
->fd
, 2);
1721 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLIN
);
1722 httpSetTimeout(connection
, 60);
1727 httpServerSendRequest(HTTPConnectionPtr connection
)
1729 assert(connection
->server
);
1731 if(connection
->reqlen
== 0) {
1732 do_log(D_SERVER_REQ
,
1733 "Writing aborted on 0x%lx\n", (unsigned long)connection
);
1734 httpConnectionDestroyReqbuf(connection
);
1735 shutdown(connection
->fd
, 2);
1736 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLIN
| POLLOUT
);
1740 httpSetTimeout(connection
, serverTimeout
);
1741 do_stream(IO_WRITE
, connection
->fd
, 0,
1742 connection
->reqbuf
, connection
->reqlen
,
1743 httpServerHandler
, connection
);
1748 httpServerReplyHandler(int status
,
1749 FdEventHandlerPtr event
,
1750 StreamRequestPtr srequest
)
1752 HTTPConnectionPtr connection
= srequest
->data
;
1753 HTTPRequestPtr request
= connection
->request
;
1756 (connection
->flags
& CONN_BIGBUF
) ? bigBufferSize
: CHUNK_SIZE
;
1758 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1760 if(connection
->serviced
>= 1) {
1761 httpServerRestart(connection
);
1764 if(status
!= -ECLIENTRESET
)
1765 do_log_error(L_ERROR
, -status
, "Read from server failed");
1766 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
1767 internAtomError(-status
, "Read from server failed"));
1771 i
= findEndOfHeaders(connection
->buf
, 0, srequest
->offset
, &body
);
1772 connection
->len
= srequest
->offset
;
1775 request
->time1
= current_time
;
1776 return httpServerHandlerHeaders(status
, event
, srequest
, connection
);
1780 if(connection
->serviced
>= 1) {
1781 httpServerRestart(connection
);
1786 "Error reading server headers: %d\n", -status
);
1787 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
1788 internAtomError(-status
,
1789 "Error reading server headers"));
1791 httpServerAbort(connection
, 1, 502,
1792 internAtom("Server dropped connection"));
1796 if(connection
->len
>= bufsize
) {
1798 if(!(connection
->flags
& CONN_BIGBUF
))
1799 rc
= httpConnectionBigify(connection
);
1801 do_log(L_ERROR
, "Couldn't find end of server's headers.\n");
1802 httpServerAbort(connection
, 1, 502,
1803 internAtom("Couldn't find end "
1804 "of server's headers"));
1807 do_log(L_ERROR
, "Couldn't allocate big buffer.\n");
1808 httpServerAbort(connection
, 1, 500,
1809 internAtom("Couldn't allocate big buffer"));
1812 /* Can't just return 0 -- buf has moved. */
1814 connection
->fd
, connection
->len
,
1815 connection
->buf
, bigBufferSize
,
1816 httpServerReplyHandler
, connection
);
1824 httpServerHandlerHeaders(int eof
,
1825 FdEventHandlerPtr event
,
1826 StreamRequestPtr srequest
,
1827 HTTPConnectionPtr connection
)
1829 HTTPRequestPtr request
= connection
->request
;
1830 ObjectPtr object
= request
->object
;
1837 CacheControlRec cache_control
;
1839 time_t date
, last_modified
, expires
;
1840 struct timeval
*init_time
;
1842 AtomPtr via
, new_via
;
1844 HTTPRangeRec content_range
;
1845 ObjectPtr new_object
= NULL
, old_object
= NULL
;
1847 AtomPtr message
= NULL
;
1851 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1854 httpSetTimeout(connection
, -1);
1856 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
1857 do_log(D_SERVER_CONN
, "W %s:%d.\n",
1858 connection
->server
->name
, connection
->server
->port
);
1859 request
->flags
&= ~REQUEST_WAIT_CONTINUE
;
1862 rc
= httpParseServerFirstLine(connection
->buf
, &code
, &version
, &message
);
1864 do_log(L_ERROR
, "Couldn't parse server status line.\n");
1865 httpServerAbort(connection
, 1, 502,
1866 internAtom("Couldn't parse server status line"));
1870 do_log(D_SERVER_REQ
, "Server status: ");
1871 do_log_n(D_SERVER_REQ
, connection
->buf
,
1872 connection
->buf
[rc
- 1] == '\r' ? rc
- 2 : rc
- 2);
1873 do_log(D_SERVER_REQ
, " (0x%lx for 0x%lx)\n",
1874 (unsigned long)connection
, (unsigned long)object
);
1876 if(version
!= HTTP_10
&& version
!= HTTP_11
) {
1877 do_log(L_ERROR
, "Unknown server HTTP version\n");
1878 httpServerAbort(connection
, 1, 502,
1879 internAtom("Unknown server HTTP version"));
1880 releaseAtom(message
);
1884 connection
->version
= version
;
1885 connection
->server
->version
= version
;
1886 request
->flags
|= REQUEST_PERSISTENT
;
1888 url
= internAtomN(object
->key
, object
->key_size
);
1889 rc
= httpParseHeaders(0, url
, connection
->buf
, rc
, request
,
1890 &headers
, &len
, &cache_control
, NULL
, &te
,
1891 &date
, &last_modified
, &expires
, NULL
, NULL
, NULL
,
1892 &age
, &etag
, NULL
, NULL
, &content_range
,
1895 do_log(L_ERROR
, "Couldn't parse server headers\n");
1897 releaseAtom(message
);
1898 httpServerAbort(connection
, 1, 502,
1899 internAtom("Couldn't parse server headers"));
1905 releaseAtom(message
);
1906 /* We've already reset wait_continue above, but we must still
1907 ensure that the writer notices. */
1908 notifyObject(request
->object
);
1909 httpServerFinish(connection
, -1, rc
);
1914 httpServerAbort(connection
, 1, 501,
1915 internAtom("Upgrade not implemented"));
1919 if(via
&& !checkVia(proxyName
, via
)) {
1920 httpServerAbort(connection
, 1, 504, internAtom("Proxy loop detected"));
1923 full_len
= content_range
.full_length
;
1926 if(content_range
.from
== -1 || content_range
.to
== -1) {
1927 do_log(L_ERROR
, "Partial content without range.\n");
1928 httpServerAbort(connection
, 1, 502,
1929 internAtom("Partial content without range"));
1932 if(len
>= 0 && len
!= content_range
.to
- content_range
.from
) {
1933 do_log(L_ERROR
, "Inconsistent partial content.\n");
1934 httpServerAbort(connection
, 1, 502,
1935 internAtom("Inconsistent partial content"));
1938 } else if(code
< 400 &&
1939 (content_range
.from
>= 0 || content_range
.to
>= 0 ||
1940 content_range
.full_length
>= 0)) {
1941 do_log(L_WARN
, "Range without partial content.\n");
1943 content_range
.from
= -1;
1944 content_range
.to
= -1;
1945 content_range
.full_length
= -1;
1946 } else if(code
!= 304 && code
!= 412) {
1950 if(te
!= TE_IDENTITY
&& te
!= TE_CHUNKED
) {
1951 do_log(L_ERROR
, "Unsupported transfer-encoding\n");
1952 httpServerAbort(connection
, 1, 502,
1953 internAtom("Unsupported transfer-encoding"));
1958 if(request
->method
!= METHOD_CONDITIONAL_GET
) {
1959 do_log(L_ERROR
, "Unexpected \"not changed\" reply from server\n");
1960 httpServerAbort(connection
, 1, 502,
1961 internAtom("Unexpected \"not changed\" "
1962 "reply from server"));
1965 if(object
->etag
&& !etag
) {
1966 /* RFC 2616 10.3.5. Violated by some front-end proxies. */
1967 do_log(L_WARN
, "\"Not changed\" reply with no ETag.\n");
1972 if(request
->method
!= METHOD_CONDITIONAL_GET
||
1973 (!object
->etag
&& !object
->last_modified
)) {
1975 "Unexpected \"precondition failed\" reply from server.\n");
1976 httpServerAbort(connection
, 1, 502,
1977 internAtom("Unexpected \"precondition failed\" "
1978 "reply from server"));
1985 /* Okay, we're going to accept this reply. */
1987 if((code
== 200 || code
== 206 || code
== 304 || code
== 412) &&
1988 (cache_control
.flags
& (CACHE_NO
| CACHE_NO_STORE
) ||
1989 cache_control
.max_age
== 0 ||
1990 (cacheIsShared
&& cache_control
.s_maxage
== 0) ||
1991 (expires
>= 0 && expires
<= object
->age
))) {
1992 do_log(L_UNCACHEABLE
, "Uncacheable object ");
1993 do_log_n(L_UNCACHEABLE
, object
->key
, object
->key_size
);
1994 do_log(L_UNCACHEABLE
, " (%d)\n", cache_control
.flags
);
1997 if(request
->time0
.tv_sec
!= null_time
.tv_sec
)
1998 init_time
= &request
->time0
;
2000 init_time
= ¤t_time
;
2001 age
= MIN(init_time
->tv_sec
- age
, init_time
->tv_sec
);
2003 if(request
->method
== METHOD_HEAD
||
2004 code
< 200 || code
== 204 || code
== 304)
2006 else if(te
== TE_IDENTITY
)
2007 expect_body
= (len
!= 0);
2011 connection
->chunk_remaining
= -1;
2012 connection
->te
= te
;
2014 old_object
= object
;
2016 connection
->server
->lies
--;
2018 if(object
->cache_control
& CACHE_MISMATCH
)
2021 if(code
== 304 || code
== 412) {
2022 if((object
->etag
&& etag
&& strcmp(object
->etag
, etag
) != 0) ||
2023 (object
->last_modified
>= 0 && last_modified
>= 0 &&
2024 object
->last_modified
!= last_modified
)) {
2025 do_log(L_ERROR
, "Inconsistent \"%s\" reply for ",
2026 code
== 304 ? "not changed":"precondition failed");
2027 do_log_n(L_ERROR
, object
->key
, object
->key_size
);
2028 do_log(L_ERROR
, "\n");
2029 object
->flags
|= OBJECT_DYNAMIC
;
2032 } else if(!(object
->flags
& OBJECT_INITIAL
)) {
2033 if((object
->last_modified
< 0 || last_modified
< 0) &&
2034 (!object
->etag
|| !etag
))
2036 else if(object
->last_modified
!= last_modified
)
2038 else if(object
->etag
|| etag
) {
2039 /* We need to be permissive here so as to deal with some
2040 front-end proxies that discard ETags on partial
2041 replies but not on full replies. */
2042 if(etag
&& object
->etag
&& strcmp(object
->etag
, etag
) != 0)
2044 else if(!object
->etag
)
2048 if(!supersede
&& (object
->cache_control
& CACHE_VARY
) &&
2049 dontTrustVaryETag
>= 1) {
2050 /* Check content-type to work around mod_gzip bugs */
2051 if(!httpHeaderMatch(atomContentType
, object
->headers
, headers
) ||
2052 !httpHeaderMatch(atomContentEncoding
, object
->headers
, headers
))
2056 if(full_len
< 0 && te
== TE_IDENTITY
) {
2057 /* It's an HTTP/1.0 CGI. Be afraid. */
2058 if(expect_body
&& content_range
.from
< 0 && content_range
.to
< 0)
2062 if(!supersede
&& object
->length
>= 0 && full_len
>= 0 &&
2063 object
->length
!= full_len
) {
2064 do_log(L_WARN
, "Inconsistent length.\n");
2069 ((object
->last_modified
>= 0 && last_modified
>= 0) ||
2070 (object
->etag
&& etag
))) {
2071 if(request
->method
== METHOD_CONDITIONAL_GET
) {
2072 do_log(L_WARN
, "Server ignored conditional request.\n");
2073 connection
->server
->lies
+= 10;
2074 /* Drop the connection? */
2077 } else if(code
== 416) {
2078 do_log(L_ERROR
, "Unexpected \"range not satisfiable\" reply\n");
2079 httpServerAbort(connection
, 1, 502,
2080 internAtom("Unexpected \"range not satisfiable\" "
2082 /* The object may be superseded. Make sure the next request
2083 won't be partial. */
2084 abortObject(object
, 502,
2085 internAtom("Unexpected \"range not satisfiable\" reply"));
2089 if(object
->flags
& OBJECT_INITIAL
)
2093 do_log(L_SUPERSEDED
, "Superseding object: ");
2094 do_log_n(L_SUPERSEDED
, old_object
->key
, old_object
->key_size
);
2095 do_log(L_SUPERSEDED
, " (%d %d %d %s -> %d %d %d %s)\n",
2096 object
->code
, object
->length
, (int)object
->last_modified
,
2097 object
->etag
?object
->etag
: "(none)",
2098 code
, full_len
, (int)last_modified
,
2099 etag
?etag
:"(none)");
2100 privatiseObject(old_object
, 0);
2101 new_object
= makeObject(object
->type
, object
->key
,
2102 object
->key_size
, 1, 0,
2103 object
->request
, NULL
);
2104 if(new_object
== NULL
) {
2105 do_log(L_ERROR
, "Couldn't allocate object\n");
2106 httpServerAbort(connection
, 1, 500,
2107 internAtom("Couldn't allocate object"));
2110 if(urlIsLocal(new_object
->key
, new_object
->key_size
))
2111 new_object
->flags
|= OBJECT_LOCAL
;
2113 new_object
= object
;
2117 (!etag
&& last_modified
< 0) ||
2118 (cache_control
.flags
&
2119 (CACHE_NO_HIDDEN
| CACHE_NO
| CACHE_NO_STORE
|
2120 (cacheIsShared
? CACHE_PRIVATE
: 0))) ||
2121 (cache_control
.max_age
>= 0 && cache_control
.max_age
<= 2) ||
2123 cache_control
.s_maxage
>= 0 && cache_control
.s_maxage
<= 5) ||
2124 (old_object
->last_modified
>= 0 && old_object
->expires
>= 0 &&
2125 (old_object
->expires
- old_object
->last_modified
<= 1)) ||
2126 (supersede
&& (old_object
->date
- date
<= 5));
2129 new_object
->flags
|= OBJECT_DYNAMIC
;
2131 new_object
->flags
&= ~OBJECT_DYNAMIC
;
2132 else if(old_object
->flags
& OBJECT_DYNAMIC
)
2133 new_object
->flags
|= OBJECT_DYNAMIC
;
2135 new_object
->age
= age
;
2136 new_object
->cache_control
|= cache_control
.flags
;
2137 new_object
->max_age
= cache_control
.max_age
;
2138 new_object
->s_maxage
= cache_control
.s_maxage
;
2139 new_object
->flags
&= ~OBJECT_FAILED
;
2142 new_object
->date
= date
;
2143 if(last_modified
>= 0)
2144 new_object
->last_modified
= last_modified
;
2146 new_object
->expires
= expires
;
2147 if(new_object
->etag
== NULL
)
2148 new_object
->etag
= etag
;
2154 case 300: case 301: case 302: case 303: case 307:
2155 case 403: case 404: case 405: case 401:
2156 if(new_object
->message
) releaseAtom(new_object
->message
);
2157 new_object
->code
= code
;
2158 new_object
->message
= message
;
2160 case 206: case 304: case 412:
2161 if(new_object
->code
!= 200 || !new_object
->message
) {
2162 if(new_object
->message
) releaseAtom(new_object
->message
);
2163 new_object
->code
= 200;
2164 new_object
->message
= internAtom("OK");
2166 releaseAtom(message
);
2169 if(new_object
->message
) releaseAtom(new_object
->message
);
2170 new_object
->code
= code
;
2171 new_object
->message
= retainAtom(message
);
2175 httpTweakCachability(new_object
);
2178 new_via
= internAtomF("%s %s",
2179 version
== HTTP_11
? "1.1" : "1.0",
2182 new_via
= internAtomF("%s, %s %s", via
->string
,
2183 version
== HTTP_11
? "1.1" : "1.0",
2185 if(new_via
== NULL
) {
2186 do_log(L_ERROR
, "Couldn't allocate Via.\n");
2188 if(new_object
->via
) releaseAtom(new_object
->via
);
2189 new_object
->via
= new_via
;
2192 if(new_object
->flags
& OBJECT_INITIAL
) {
2193 objectPartial(new_object
, full_len
, headers
);
2195 if(new_object
->length
< 0)
2196 new_object
->length
= full_len
;
2197 /* XXX -- RFC 2616 13.5.3 */
2198 releaseAtom(headers
);
2202 assert(new_object
!= old_object
);
2203 supersedeObject(old_object
);
2206 if(new_object
!= old_object
) {
2207 if(new_object
->flags
& OBJECT_INPROGRESS
) {
2208 /* Make sure we don't fetch this object two times at the
2209 same time. Just drop the connection. */
2210 releaseObject(new_object
);
2211 httpServerFinish(connection
, 1, 0);
2214 old_object
->flags
&= ~OBJECT_VALIDATING
;
2215 new_object
->flags
|= OBJECT_INPROGRESS
;
2216 /* Signal the client side to switch to the new object -- see
2217 httpClientGetHandler. If it doesn't, we'll give up on this
2219 new_object
->flags
|= OBJECT_MUTATING
;
2220 request
->can_mutate
= new_object
;
2221 notifyObject(old_object
);
2222 request
->can_mutate
= NULL
;
2223 new_object
->flags
&= ~OBJECT_MUTATING
;
2224 old_object
->flags
&= ~OBJECT_INPROGRESS
;
2225 if(request
->object
== old_object
) {
2226 if(request
->request
)
2227 request
->request
->request
= NULL
;
2228 request
->request
= NULL
;
2229 request
->object
= new_object
;
2231 assert(request
->object
== new_object
);
2233 releaseNotifyObject(old_object
);
2235 object
= new_object
;
2237 objectMetadataChanged(new_object
, 0);
2240 if(object
->flags
& OBJECT_VALIDATING
) {
2241 object
->flags
&= ~OBJECT_VALIDATING
;
2242 notifyObject(object
);
2246 httpServerFinish(connection
, 0, rc
);
2250 if(request
->request
== NULL
) {
2251 httpServerFinish(connection
, 1, 0);
2256 /* 412 replies contain a useless body. For now, we
2257 drop the connection. */
2258 httpServerFinish(connection
, 1, 0);
2263 if(request
->flags
& REQUEST_PERSISTENT
) {
2264 if(request
->method
!= METHOD_HEAD
&&
2265 connection
->te
== TE_IDENTITY
&& len
< 0) {
2266 do_log(L_ERROR
, "Persistent reply with no Content-Length\n");
2267 /* That's potentially dangerous, as we could start reading
2268 arbitrary data into the object. Unfortunately, some
2270 request
->flags
&= ~REQUEST_PERSISTENT
;
2274 /* we're getting a body */
2275 if(content_range
.from
> 0)
2276 connection
->offset
= content_range
.from
;
2278 connection
->offset
= 0;
2280 if(content_range
.to
>= 0)
2281 request
->to
= content_range
.to
;
2283 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2284 (unsigned long)connection
, (unsigned long)object
,
2285 connection
->offset
);
2287 if(connection
->len
> rc
) {
2288 rc
= connectionAddData(connection
, rc
);
2292 do_log(L_ERROR
, "Couldn't parse chunk size.\n");
2293 httpServerAbort(connection
, 1, 502,
2294 internAtom("Couldn't parse chunk size"));
2296 do_log(L_ERROR
, "Couldn't add data to connection.\n");
2297 httpServerAbort(connection
, 1, 500,
2298 internAtom("Couldn't add data "
2304 if(object
->length
< 0) {
2305 object
->length
= object
->size
;
2306 objectMetadataChanged(object
, 0);
2307 } else if(object
->length
!= object
->size
) {
2308 httpServerAbort(connection
, 1, 500,
2309 internAtom("Inconsistent "
2311 object
->length
= -1;
2315 httpServerFinish(connection
, 0, 0);
2320 connection
->len
= 0;
2324 if(connection
->te
== TE_CHUNKED
||
2325 (object
->length
>= 0 &&
2326 connection
->offset
< object
->length
)) {
2327 do_log(L_ERROR
, "Server closed connection.\n");
2328 httpServerAbort(connection
, 1, 502,
2329 internAtom("Server closed connection"));
2332 if(code
!= 206 && eof
> 0 && object
->length
< 0) {
2333 object
->length
= object
->size
;
2334 objectMetadataChanged(object
, 0);
2336 httpServerFinish(connection
, 1, 0);
2340 return httpServerReadData(connection
, 1);
2346 releaseAtom(message
);
2348 releaseAtom(headers
);
2357 httpServerIndirectHandlerCommon(HTTPConnectionPtr connection
, int eof
)
2359 HTTPRequestPtr request
= connection
->request
;
2362 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
2364 if(connection
->len
> 0) {
2366 rc
= connectionAddData(connection
, 0);
2370 do_log(L_ERROR
, "Couldn't parse chunk size.\n");
2371 httpServerAbort(connection
, 1, 502,
2372 internAtom("Couldn't parse chunk size"));
2374 do_log(L_ERROR
, "Couldn't add data to connection.\n");
2375 httpServerAbort(connection
, 1, 500,
2376 internAtom("Couldn't add data "
2381 if(request
->to
< 0) {
2382 if(request
->object
->length
< 0) {
2383 request
->object
->length
= request
->object
->size
;
2384 objectMetadataChanged(request
->object
, 0);
2385 } else if(request
->object
->length
!=
2386 request
->object
->size
) {
2387 request
->object
->length
= -1;
2388 httpServerAbort(connection
, 1, 502,
2389 internAtom("Inconsistent "
2394 httpServerFinish(connection
, 0, 0);
2400 if(eof
&& connection
->len
== 0) {
2401 if(connection
->te
== TE_CHUNKED
||
2402 (request
->to
>= 0 && connection
->offset
< request
->to
)) {
2403 do_log(L_ERROR
, "Server dropped connection.\n");
2404 httpServerAbort(connection
, 1, 502,
2405 internAtom("Server dropped connection"));
2408 if(request
->object
->length
< 0 && eof
> 0 &&
2409 (request
->to
< 0 || request
->to
> request
->object
->size
)) {
2410 request
->object
->length
= request
->object
->size
;
2411 objectMetadataChanged(request
->object
, 0);
2413 httpServerFinish(connection
, 1, 0);
2417 return httpServerReadData(connection
, 0);
2422 httpServerIndirectHandler(int status
,
2423 FdEventHandlerPtr event
,
2424 StreamRequestPtr srequest
)
2426 HTTPConnectionPtr connection
= srequest
->data
;
2427 assert(connection
->request
->object
->flags
& OBJECT_INPROGRESS
);
2429 httpSetTimeout(connection
, -1);
2431 if(status
!= -ECLIENTRESET
)
2432 do_log_error(L_ERROR
, -status
, "Read from server failed");
2433 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
2434 internAtomError(-status
, "Read from server failed"));
2438 connection
->len
= srequest
->offset
;
2440 return httpServerIndirectHandlerCommon(connection
, status
);
2444 httpServerReadData(HTTPConnectionPtr connection
, int immediate
)
2446 HTTPRequestPtr request
= connection
->request
;
2447 ObjectPtr object
= request
->object
;
2450 assert(object
->flags
& OBJECT_INPROGRESS
);
2452 if(request
->request
== NULL
) {
2453 httpServerFinish(connection
, 1, 0);
2457 if(request
->to
>= 0)
2460 to
= object
->length
;
2462 if(to
>= 0 && to
== connection
->offset
) {
2463 httpServerFinish(connection
, 0, 0);
2467 if(connection
->len
== 0 &&
2468 ((connection
->te
== TE_IDENTITY
&& to
> connection
->offset
) ||
2469 (connection
->te
== TE_CHUNKED
&& connection
->chunk_remaining
> 0))) {
2470 /* Read directly into the object */
2471 int i
= connection
->offset
/ CHUNK_SIZE
;
2472 int j
= connection
->offset
% CHUNK_SIZE
;
2474 /* See httpServerDirectHandlerCommon if you change this */
2475 if(connection
->te
== TE_CHUNKED
) {
2476 len
= connection
->chunk_remaining
;
2477 /* The logic here is that we want more to just fit the
2478 chunk header if we're doing a large read, but do a
2479 large read if we would otherwise do a small one. The
2480 magic constant 2000 comes from the assumption that the
2481 server uses chunks that have a size that are a power of
2482 two (possibly including the chunk header), and that we
2483 want a full ethernet packet to fit into our read. */
2484 more
= (len
>= 2000 ? 20 : MIN(2048 - len
, CHUNK_SIZE
));
2486 len
= to
- connection
->offset
;
2487 /* We read more data only when there is a reasonable
2488 chance of there being another reply coming. */
2489 more
= (connection
->pipelined
> 1) ? CHUNK_SIZE
: 0;
2491 end
= len
+ connection
->offset
;
2493 httpConnectionDestroyBuf(connection
);
2495 /* The order of allocation is important in case we run out of
2497 lockChunk(object
, i
);
2498 if(object
->chunks
[i
].data
== NULL
)
2499 object
->chunks
[i
].data
= get_chunk();
2500 if(object
->chunks
[i
].data
&& object
->chunks
[i
].size
>= j
) {
2501 if(len
+ j
> CHUNK_SIZE
) {
2502 lockChunk(object
, i
+ 1);
2503 if(object
->chunks
[i
+ 1].data
== NULL
)
2504 object
->chunks
[i
+ 1].data
= get_chunk();
2505 /* Unless we're grabbing all len of data, we do not
2506 want to do an indirect read immediately afterwards. */
2507 if(more
&& len
+ j
<= 2 * CHUNK_SIZE
) {
2508 if(!connection
->buf
)
2509 connection
->buf
= get_chunk(); /* checked below */
2511 if(object
->chunks
[i
+ 1].data
) {
2512 do_stream_3(IO_READ
| IO_NOTNOW
, connection
->fd
, j
,
2513 object
->chunks
[i
].data
, CHUNK_SIZE
,
2514 object
->chunks
[i
+ 1].data
,
2516 end
- (i
+ 1) * CHUNK_SIZE
),
2517 connection
->buf
, connection
->buf
? more
: 0,
2518 httpServerDirectHandler2
, connection
);
2521 unlockChunk(object
, i
+ 1);
2523 if(more
&& len
+ j
<= CHUNK_SIZE
) {
2524 if(!connection
->buf
)
2525 connection
->buf
= get_chunk();
2527 do_stream_2(IO_READ
| IO_NOTNOW
, connection
->fd
, j
,
2528 object
->chunks
[i
].data
,
2529 MIN(CHUNK_SIZE
, end
- i
* CHUNK_SIZE
),
2530 connection
->buf
, connection
->buf
? more
: 0,
2531 httpServerDirectHandler
, connection
);
2534 unlockChunk(object
, i
);
2538 if(connection
->len
== 0)
2539 httpConnectionDestroyBuf(connection
);
2541 httpSetTimeout(connection
, serverTimeout
);
2542 do_stream_buf(IO_READ
| IO_NOTNOW
|
2543 ((immediate
&& connection
->len
) ? IO_IMMEDIATE
: 0),
2544 connection
->fd
, connection
->len
,
2546 (connection
->te
== TE_CHUNKED
?
2547 MIN(2048, CHUNK_SIZE
) : CHUNK_SIZE
),
2548 httpServerIndirectHandler
, connection
);
2553 httpServerDirectHandlerCommon(int kind
, int status
,
2554 FdEventHandlerPtr event
,
2555 StreamRequestPtr srequest
)
2557 HTTPConnectionPtr connection
= srequest
->data
;
2558 HTTPRequestPtr request
= connection
->request
;
2559 ObjectPtr object
= request
->object
;
2560 int i
= connection
->offset
/ CHUNK_SIZE
;
2563 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
2565 httpSetTimeout(connection
, -1);
2568 unlockChunk(object
, i
);
2569 if(kind
== 2) unlockChunk(object
, i
+ 1);
2570 if(status
!= -ECLIENTRESET
)
2571 do_log_error(L_ERROR
, -status
, "Read from server failed");
2572 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
2573 internAtomError(-status
, "Read from server failed"));
2577 /* We have incestuous knowledge of the decisions made in
2578 httpServerReadData */
2579 if(request
->to
>= 0)
2582 to
= object
->length
;
2583 if(connection
->te
== TE_CHUNKED
)
2584 end
= connection
->offset
+ connection
->chunk_remaining
;
2587 /* The amount of data actually read into the object */
2588 end1
= MIN(end
, i
* CHUNK_SIZE
+ MIN(kind
* CHUNK_SIZE
, srequest
->offset
));
2590 assert(end
>= 0 && end1
>= i
* CHUNK_SIZE
&& end1
<= (i
+ 2) * CHUNK_SIZE
);
2592 object
->chunks
[i
].size
=
2593 MAX(object
->chunks
[i
].size
, MIN(end1
- i
* CHUNK_SIZE
, CHUNK_SIZE
));
2594 if(kind
== 2 && end1
> (i
+ 1) * CHUNK_SIZE
) {
2595 object
->chunks
[i
+ 1].size
=
2596 MAX(object
->chunks
[i
+ 1].size
, end1
- (i
+ 1) * CHUNK_SIZE
);
2598 if(connection
->te
== TE_CHUNKED
) {
2599 connection
->chunk_remaining
-= (end1
- connection
->offset
);
2600 assert(connection
->chunk_remaining
>= 0);
2602 connection
->offset
= end1
;
2603 object
->size
= MAX(object
->size
, end1
);
2604 unlockChunk(object
, i
);
2605 if(kind
== 2) unlockChunk(object
, i
+ 1);
2607 if(i
* CHUNK_SIZE
+ srequest
->offset
> end1
) {
2608 connection
->len
= i
* CHUNK_SIZE
+ srequest
->offset
- end1
;
2609 return httpServerIndirectHandlerCommon(connection
, status
);
2611 notifyObject(object
);
2613 httpServerFinish(connection
, 1, 0);
2616 return httpServerReadData(connection
, 0);
2622 httpServerDirectHandler(int status
,
2623 FdEventHandlerPtr event
,
2624 StreamRequestPtr srequest
)
2626 return httpServerDirectHandlerCommon(1, status
, event
, srequest
);
2630 httpServerDirectHandler2(int status
,
2631 FdEventHandlerPtr event
,
2632 StreamRequestPtr srequest
)
2634 return httpServerDirectHandlerCommon(2, status
, event
, srequest
);
2637 /* Add the data accumulated in connection->buf into the object in
2638 connection->request. Returns 0 in the normal case, 1 if the TE is
2639 self-terminating and we're done, -1 if there was a problem with
2640 objectAddData, -2 if there was a problem with the data. */
2642 connectionAddData(HTTPConnectionPtr connection
, int skip
)
2644 HTTPRequestPtr request
= connection
->request
;
2645 ObjectPtr object
= request
->object
;
2648 if(connection
->te
== TE_IDENTITY
) {
2651 len
= connection
->len
- skip
;
2652 if(object
->length
>= 0) {
2653 len
= MIN(object
->length
- connection
->offset
, len
);
2655 if(request
->to
>= 0)
2656 len
= MIN(request
->to
- connection
->offset
, len
);
2658 rc
= objectAddData(object
, connection
->buf
+ skip
,
2659 connection
->offset
, len
);
2662 connection
->offset
+= len
;
2663 connection
->len
-= (len
+ skip
);
2664 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2665 (unsigned long)connection
, (unsigned long)object
,
2666 connection
->offset
);
2669 if(connection
->len
> 0 && skip
+ len
> 0) {
2670 memmove(connection
->buf
,
2671 connection
->buf
+ skip
+ len
, connection
->len
);
2674 if((object
->length
>= 0 && object
->length
<= connection
->offset
) ||
2675 (request
->to
>= 0 && request
->to
<= connection
->offset
)) {
2676 notifyObject(object
);
2680 notifyObject(object
);
2683 } else if(connection
->te
== TE_CHUNKED
) {
2684 int i
= skip
, j
, size
;
2685 /* connection->chunk_remaining is 0 at the end of a chunk, -1
2686 after the CR/LF pair ending a chunk, and -2 after we've
2687 seen a chunk of length 0. */
2688 if(connection
->chunk_remaining
> -2) {
2690 if(connection
->chunk_remaining
<= 0) {
2691 if(connection
->chunk_remaining
== 0) {
2692 if(connection
->len
< i
+ 2)
2694 if(connection
->buf
[i
] != '\r' ||
2695 connection
->buf
[i
+ 1] != '\n')
2698 connection
->chunk_remaining
= -1;
2700 if(connection
->len
< i
+ 2)
2702 j
= parseChunkSize(connection
->buf
, i
,
2703 connection
->len
, &size
);
2711 connection
->chunk_remaining
= -2;
2714 connection
->chunk_remaining
= size
;
2717 /* connection->chunk_remaining > 0 */
2718 size
= MIN(connection
->chunk_remaining
,
2719 connection
->len
- i
);
2722 rc
= objectAddData(object
, connection
->buf
+ i
,
2723 connection
->offset
, size
);
2724 connection
->offset
+= size
;
2728 connection
->chunk_remaining
-= size
;
2729 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2730 (unsigned long)connection
,
2731 (unsigned long)object
,
2732 connection
->offset
);
2736 connection
->len
-= i
;
2737 if(connection
->len
> 0)
2738 memmove(connection
->buf
, connection
->buf
+ i
, connection
->len
);
2739 if(i
> 0 || connection
->chunk_remaining
== -2)
2740 notifyObject(object
);
2741 if(connection
->chunk_remaining
== -2)
2751 listServers(FILE *out
)
2753 HTTPServerPtr server
;
2756 fprintf(out
, "<!DOCTYPE HTML PUBLIC "
2757 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2758 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2760 "\r\n<title>Known servers</title>\n"
2762 "<h1>Known servers</h1>\n");
2764 alternatingHttpStyle(out
, "servers");
2765 fprintf(out
, "<table id=servers>\n");
2766 fprintf(out
, "<thead><tr><th>Server</th>"
2768 "<th>Persistent</th>"
2770 "<th>Connections</th>"
2775 fprintf(out
, "<tbody>\n");
2779 fprintf(out
, "<tr class=\"%s\">", entry
% 2 == 0 ? "even" : "odd");
2780 if(server
->port
== 80)
2781 fprintf(out
, "<td>%s</td>", server
->name
);
2783 fprintf(out
, "<td>%s:%d</td>", server
->name
, server
->port
);
2785 if(server
->version
== HTTP_11
)
2786 fprintf(out
, "<td>1.1</td>");
2787 else if(server
->version
== HTTP_10
)
2788 fprintf(out
, "<td>1.0</td>");
2790 fprintf(out
, "<td>unknown</td>");
2792 if(server
->persistent
< 0)
2793 fprintf(out
, "<td>no</td>");
2794 else if(server
->persistent
> 0)
2795 fprintf(out
, "<td>yes</td>");
2797 fprintf(out
, "<td>unknown</td>");
2799 if(server
->version
!= HTTP_11
|| server
->persistent
<= 0)
2800 fprintf(out
, "<td></td>");
2801 else if(server
->pipeline
< 0)
2802 fprintf(out
, "<td>no</td>");
2803 else if(server
->pipeline
>= 0 && server
->pipeline
<= 1)
2804 fprintf(out
, "<td>unknown</td>");
2805 else if(server
->pipeline
== 2 || server
->pipeline
== 3)
2806 fprintf(out
, "<td>probing</td>");
2808 fprintf(out
, "<td>yes</td>");
2811 for(i
= 0; i
< server
->maxslots
; i
++)
2812 if(server
->connection
[i
] && !server
->connection
[i
]->connecting
) {
2813 if(i
< server
->numslots
)
2819 fprintf(out
, "<td>%d/%d", n
, server
->numslots
);
2821 fprintf(out
, " + %d</td>", m
);
2823 fprintf(out
, "</td>");
2825 if(server
->lies
> 0)
2826 fprintf(out
, "<td>(%d lies)</td>", (server
->lies
+ 9) / 10);
2828 fprintf(out
, "<td></td>");
2831 fprintf(out
, "<td>%.3f</td>", (double)server
->rtt
/ 1000000.0);
2833 fprintf(out
, "<td></td>");
2834 if(server
->rate
> 0)
2835 fprintf(out
, "<td>%d</td>", server
->rate
);
2837 fprintf(out
, "<td></td>");
2839 fprintf(out
, "</tr>\n");
2840 server
= server
->next
;
2843 fprintf(out
, "</tbody>\n");
2844 fprintf(out
, "</table>\n");
2845 fprintf(out
, "<p><a href=\"/polipo/\">back</a></p>");
2846 fprintf(out
, "</body></html>\n");