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
) {
1971 "Unexpected \"precondition failed\" reply from server.\n");
1972 httpServerAbort(connection
, 1, 502,
1973 internAtom("Unexpected \"precondition failed\" "
1974 "reply from server"));
1977 assert(object
->etag
|| object
->last_modified
);
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(!supersede
&& object
->length
>= 0 && full_len
>= 0 &&
2043 object
->length
!= full_len
) {
2044 do_log(L_WARN
, "Inconsistent length.\n");
2049 ((object
->last_modified
>= 0 && last_modified
>= 0) ||
2050 (object
->etag
&& etag
))) {
2051 if(request
->method
== METHOD_CONDITIONAL_GET
) {
2052 do_log(L_WARN
, "Server ignored conditional request.\n");
2053 connection
->server
->lies
+= 10;
2054 /* Drop the connection? */
2057 } else if(code
== 416) {
2058 do_log(L_ERROR
, "Unexpected \"range not satisfiable\" reply\n");
2059 httpServerAbort(connection
, 1, 502,
2060 internAtom("Unexpected \"range not satisfiable\" "
2062 /* The object may be superseded. Make sure the next request
2063 won't be partial. */
2064 abortObject(object
, 502,
2065 internAtom("Unexpected \"range not satisfiable\" reply"));
2069 if(object
->flags
& OBJECT_INITIAL
)
2073 do_log(L_SUPERSEDED
, "Superseding object: ");
2074 do_log_n(L_SUPERSEDED
, old_object
->key
, old_object
->key_size
);
2075 do_log(L_SUPERSEDED
, " (%d %d %d %s -> %d %d %d %s)\n",
2076 object
->code
, object
->length
, (int)object
->last_modified
,
2077 object
->etag
?object
->etag
: "(none)",
2078 code
, full_len
, (int)last_modified
,
2079 etag
?etag
:"(none)");
2080 privatiseObject(old_object
, 0);
2081 new_object
= makeObject(object
->type
, object
->key
,
2082 object
->key_size
, 1, 0,
2083 object
->request
, NULL
);
2084 if(new_object
== NULL
) {
2085 do_log(L_ERROR
, "Couldn't allocate object\n");
2086 httpServerAbort(connection
, 1, 500,
2087 internAtom("Couldn't allocate object"));
2090 if(urlIsLocal(new_object
->key
, new_object
->key_size
))
2091 new_object
->flags
|= OBJECT_LOCAL
;
2093 new_object
= object
;
2097 (!etag
&& last_modified
< 0) ||
2098 (cache_control
.flags
&
2099 (CACHE_NO_HIDDEN
| CACHE_NO
| CACHE_NO_STORE
|
2100 (cacheIsShared
? CACHE_PRIVATE
: 0))) ||
2101 (cache_control
.max_age
>= 0 && cache_control
.max_age
<= 2) ||
2103 cache_control
.s_maxage
>= 0 && cache_control
.s_maxage
<= 5) ||
2104 (old_object
->last_modified
>= 0 && old_object
->expires
>= 0 &&
2105 (old_object
->expires
- old_object
->last_modified
<= 1)) ||
2106 (supersede
&& (old_object
->date
- date
<= 5));
2109 new_object
->flags
|= OBJECT_DYNAMIC
;
2111 new_object
->flags
&= ~OBJECT_DYNAMIC
;
2112 else if(old_object
->flags
& OBJECT_DYNAMIC
)
2113 new_object
->flags
|= OBJECT_DYNAMIC
;
2115 new_object
->age
= age
;
2116 new_object
->cache_control
|= cache_control
.flags
;
2117 new_object
->max_age
= cache_control
.max_age
;
2118 new_object
->s_maxage
= cache_control
.s_maxage
;
2119 new_object
->flags
&= ~OBJECT_FAILED
;
2122 new_object
->date
= date
;
2123 if(last_modified
>= 0)
2124 new_object
->last_modified
= last_modified
;
2126 new_object
->expires
= expires
;
2127 if(new_object
->etag
== NULL
)
2128 new_object
->etag
= etag
;
2134 case 300: case 301: case 302: case 303: case 307:
2135 case 403: case 404: case 405: case 401:
2136 if(new_object
->message
) releaseAtom(new_object
->message
);
2137 new_object
->code
= code
;
2138 new_object
->message
= message
;
2140 case 206: case 304: case 412:
2141 if(new_object
->code
!= 200 || !new_object
->message
) {
2142 if(new_object
->message
) releaseAtom(new_object
->message
);
2143 new_object
->code
= 200;
2144 new_object
->message
= internAtom("OK");
2146 releaseAtom(message
);
2149 if(new_object
->message
) releaseAtom(new_object
->message
);
2150 new_object
->code
= code
;
2151 new_object
->message
= retainAtom(message
);
2155 if((cache_control
.flags
& CACHE_AUTHORIZATION
) &&
2156 !(cache_control
.flags
& CACHE_PUBLIC
))
2157 new_object
->cache_control
|= (CACHE_NO_HIDDEN
| OBJECT_LINEAR
);
2159 /* This is not required by RFC 2616 -- but see RFC 3143 2.1.1. We
2160 manically avoid caching replies that we don't know how to
2161 handle, even if Expires or Cache-Control says otherwise. As to
2162 known uncacheable replies, we obey Cache-Control and default to
2163 allowing sharing but not caching. */
2164 if(code
!= 200 && code
!= 206 &&
2165 code
!= 300 && code
!= 301 && code
!= 302 && code
!= 303 &&
2166 code
!= 304 && code
!= 307 &&
2167 code
!= 403 && code
!= 404 && code
!= 405 && code
!= 416) {
2168 new_object
->cache_control
|= (CACHE_NO_HIDDEN
| OBJECT_LINEAR
);
2169 } else if(code
!= 200 && code
!= 206 &&
2170 code
!= 300 && code
!= 302 && code
!= 304 &&
2172 if(new_object
->expires
< 0 && !(cache_control
.flags
& CACHE_PUBLIC
)) {
2173 new_object
->cache_control
|= CACHE_NO_HIDDEN
;
2175 } else if(dontCacheRedirects
&& (code
== 301 || code
== 302)) {
2176 new_object
->cache_control
|= CACHE_NO_HIDDEN
;
2179 if(urlIsUncachable(new_object
->key
, new_object
->key_size
)) {
2180 new_object
->cache_control
|= CACHE_NO_HIDDEN
;
2184 new_via
= internAtomF("%s %s",
2185 version
== HTTP_11
? "1.1" : "1.0",
2188 new_via
= internAtomF("%s, %s %s", via
->string
,
2189 version
== HTTP_11
? "1.1" : "1.0",
2191 if(new_via
== NULL
) {
2192 do_log(L_ERROR
, "Couldn't allocate Via.\n");
2194 if(new_object
->via
) releaseAtom(new_object
->via
);
2195 new_object
->via
= new_via
;
2198 if((new_object
->cache_control
& CACHE_NO_STORE
) ||
2199 ((new_object
->cache_control
& CACHE_VARY
) && !new_object
->etag
))
2200 new_object
->cache_control
|= CACHE_NO_HIDDEN
;
2202 if(new_object
->flags
& OBJECT_INITIAL
) {
2203 objectPartial(new_object
, full_len
, headers
);
2205 if(new_object
->length
< 0)
2206 new_object
->length
= full_len
;
2207 /* XXX -- RFC 2616 13.5.3 */
2208 releaseAtom(headers
);
2212 assert(new_object
!= old_object
);
2213 supersedeObject(old_object
);
2216 if(new_object
!= old_object
) {
2217 if(new_object
->flags
& OBJECT_INPROGRESS
) {
2218 /* Make sure we don't fetch this object two times at the
2219 same time. Just drop the connection. */
2220 releaseObject(new_object
);
2221 httpServerFinish(connection
, 1, 0);
2224 old_object
->flags
&= ~OBJECT_VALIDATING
;
2225 new_object
->flags
|= OBJECT_INPROGRESS
;
2226 /* Signal the client side to switch to the new object -- see
2227 httpClientGetHandler. If it doesn't, we'll give up on this
2229 new_object
->flags
|= OBJECT_MUTATING
;
2230 request
->can_mutate
= new_object
;
2231 notifyObject(old_object
);
2232 request
->can_mutate
= NULL
;
2233 new_object
->flags
&= ~OBJECT_MUTATING
;
2234 old_object
->flags
&= ~OBJECT_INPROGRESS
;
2235 if(request
->object
== old_object
) {
2236 if(request
->request
)
2237 request
->request
->request
= NULL
;
2238 request
->request
= NULL
;
2239 request
->object
= new_object
;
2241 assert(request
->object
== new_object
);
2243 releaseNotifyObject(old_object
);
2245 object
= new_object
;
2247 objectMetadataChanged(new_object
, 0);
2250 if(object
->flags
& OBJECT_VALIDATING
) {
2251 object
->flags
&= ~OBJECT_VALIDATING
;
2252 notifyObject(object
);
2256 httpServerFinish(connection
, 0, rc
);
2260 if(request
->request
== NULL
) {
2261 httpServerFinish(connection
, 1, 0);
2266 /* 412 replies contain a useless body. For now, we
2267 drop the connection. */
2268 httpServerFinish(connection
, 1, 0);
2273 if(request
->flags
& REQUEST_PERSISTENT
) {
2274 if(request
->method
!= METHOD_HEAD
&&
2275 connection
->te
== TE_IDENTITY
&& len
< 0) {
2276 do_log(L_ERROR
, "Persistent reply with no Content-Length\n");
2277 /* That's potentially dangerous, as we could start reading
2278 arbitrary data into the object. Unfortunately, some
2280 request
->flags
&= ~REQUEST_PERSISTENT
;
2284 /* we're getting a body */
2285 if(content_range
.from
> 0)
2286 connection
->offset
= content_range
.from
;
2288 connection
->offset
= 0;
2290 if(content_range
.to
>= 0)
2291 request
->to
= content_range
.to
;
2293 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2294 (unsigned long)connection
, (unsigned long)object
,
2295 connection
->offset
);
2297 if(connection
->len
> rc
) {
2298 rc
= connectionAddData(connection
, rc
);
2302 do_log(L_ERROR
, "Couldn't parse chunk size.\n");
2303 httpServerAbort(connection
, 1, 502,
2304 internAtom("Couldn't parse chunk size"));
2306 do_log(L_ERROR
, "Couldn't add data to connection.\n");
2307 httpServerAbort(connection
, 1, 500,
2308 internAtom("Couldn't add data "
2314 if(object
->length
< 0) {
2315 object
->length
= object
->size
;
2316 objectMetadataChanged(object
, 0);
2317 } else if(object
->length
!= object
->size
) {
2318 httpServerAbort(connection
, 1, 500,
2319 internAtom("Inconsistent "
2321 object
->length
= -1;
2325 httpServerFinish(connection
, 0, 0);
2330 connection
->len
= 0;
2334 if(connection
->te
== TE_CHUNKED
||
2335 (object
->length
>= 0 &&
2336 connection
->offset
< object
->length
)) {
2337 do_log(L_ERROR
, "Server closed connection.\n");
2338 httpServerAbort(connection
, 1, 502,
2339 internAtom("Server closed connection"));
2342 if(code
!= 206 && eof
!= -ECLIENTRESET
&& object
->length
< 0) {
2343 object
->length
= object
->size
;
2344 objectMetadataChanged(object
, 0);
2346 httpServerFinish(connection
, 1, 0);
2350 return httpServerReadData(connection
, 1);
2356 releaseAtom(message
);
2358 releaseAtom(headers
);
2367 httpServerIndirectHandlerCommon(HTTPConnectionPtr connection
, int eof
)
2369 HTTPRequestPtr request
= connection
->request
;
2372 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
2374 if(connection
->len
> 0) {
2376 rc
= connectionAddData(connection
, 0);
2380 do_log(L_ERROR
, "Couldn't parse chunk size.\n");
2381 httpServerAbort(connection
, 1, 502,
2382 internAtom("Couldn't parse chunk size"));
2384 do_log(L_ERROR
, "Couldn't add data to connection.\n");
2385 httpServerAbort(connection
, 1, 500,
2386 internAtom("Couldn't add data "
2391 if(request
->to
< 0) {
2392 if(request
->object
->length
< 0) {
2393 request
->object
->length
= request
->object
->size
;
2394 objectMetadataChanged(request
->object
, 0);
2395 } else if(request
->object
->length
!=
2396 request
->object
->size
) {
2397 request
->object
->length
= -1;
2398 httpServerAbort(connection
, 1, 502,
2399 internAtom("Inconsistent "
2404 httpServerFinish(connection
, 0, 0);
2410 if(eof
&& connection
->len
== 0) {
2411 if(connection
->te
== TE_CHUNKED
||
2412 (request
->to
>= 0 && connection
->offset
< request
->to
)) {
2413 do_log(L_ERROR
, "Server dropped connection.\n");
2414 httpServerAbort(connection
, 1, 502,
2415 internAtom("Server dropped connection"));
2418 if(request
->object
->length
< 0 && eof
!= -ECLIENTRESET
&&
2419 (request
->to
< 0 || request
->to
> request
->object
->size
)) {
2420 request
->object
->length
= request
->object
->size
;
2421 objectMetadataChanged(request
->object
, 0);
2423 httpServerFinish(connection
, 1, 0);
2427 return httpServerReadData(connection
, 0);
2432 httpServerIndirectHandler(int status
,
2433 FdEventHandlerPtr event
,
2434 StreamRequestPtr srequest
)
2436 HTTPConnectionPtr connection
= srequest
->data
;
2437 assert(connection
->request
->object
->flags
& OBJECT_INPROGRESS
);
2439 httpSetTimeout(connection
, -1);
2441 if(status
!= -ECLIENTRESET
)
2442 do_log_error(L_ERROR
, -status
, "Read from server failed");
2443 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
2444 internAtomError(-status
, "Read from server failed"));
2448 connection
->len
= srequest
->offset
;
2450 return httpServerIndirectHandlerCommon(connection
, status
);
2454 httpServerReadData(HTTPConnectionPtr connection
, int immediate
)
2456 HTTPRequestPtr request
= connection
->request
;
2457 ObjectPtr object
= request
->object
;
2460 assert(object
->flags
& OBJECT_INPROGRESS
);
2462 if(request
->request
== NULL
) {
2463 httpServerFinish(connection
, 1, 0);
2467 if(request
->to
>= 0)
2470 to
= object
->length
;
2472 if(to
>= 0 && to
== connection
->offset
) {
2473 httpServerFinish(connection
, 0, 0);
2477 if(connection
->len
== 0 &&
2478 ((connection
->te
== TE_IDENTITY
&& to
> connection
->offset
) ||
2479 (connection
->te
== TE_CHUNKED
&& connection
->chunk_remaining
> 0))) {
2480 /* Read directly into the object */
2481 int i
= connection
->offset
/ CHUNK_SIZE
;
2482 int j
= connection
->offset
% CHUNK_SIZE
;
2484 /* See httpServerDirectHandlerCommon if you change this */
2485 if(connection
->te
== TE_CHUNKED
) {
2486 len
= connection
->chunk_remaining
;
2487 /* The logic here is that we want more to just fit the
2488 chunk header if we're doing a large read, but do a
2489 large read if we would otherwise do a small one. The
2490 magic constant 2000 comes from the assumption that the
2491 server uses chunks that have a size that are a power of
2492 two (possibly including the chunk header), and that we
2493 want a full ethernet packet to fit into our read. */
2494 more
= (len
>= 2000 ? 20 : MIN(2048 - len
, CHUNK_SIZE
));
2496 len
= to
- connection
->offset
;
2497 /* We read more data only when there is a reasonable
2498 chance of there being another reply coming. */
2499 more
= (connection
->pipelined
> 1) ? CHUNK_SIZE
: 0;
2501 end
= len
+ connection
->offset
;
2503 httpConnectionDestroyBuf(connection
);
2505 /* The order of allocation is important in case we run out of
2507 lockChunk(object
, i
);
2508 if(object
->chunks
[i
].data
== NULL
)
2509 object
->chunks
[i
].data
= get_chunk();
2510 if(object
->chunks
[i
].data
&& object
->chunks
[i
].size
>= j
) {
2511 if(len
+ j
> CHUNK_SIZE
) {
2512 lockChunk(object
, i
+ 1);
2513 if(object
->chunks
[i
+ 1].data
== NULL
)
2514 object
->chunks
[i
+ 1].data
= get_chunk();
2515 /* Unless we're grabbing all len of data, we do not
2516 want to do an indirect read immediately afterwards. */
2517 if(more
&& len
+ j
<= 2 * CHUNK_SIZE
) {
2518 if(!connection
->buf
)
2519 connection
->buf
= get_chunk(); /* checked below */
2521 if(object
->chunks
[i
+ 1].data
) {
2522 do_stream_3(IO_READ
| IO_NOTNOW
, connection
->fd
, j
,
2523 object
->chunks
[i
].data
, CHUNK_SIZE
,
2524 object
->chunks
[i
+ 1].data
,
2526 end
- (i
+ 1) * CHUNK_SIZE
),
2527 connection
->buf
, connection
->buf
? more
: 0,
2528 httpServerDirectHandler2
, connection
);
2531 unlockChunk(object
, i
+ 1);
2533 if(more
&& len
+ j
<= CHUNK_SIZE
) {
2534 if(!connection
->buf
)
2535 connection
->buf
= get_chunk();
2537 do_stream_2(IO_READ
| IO_NOTNOW
, connection
->fd
, j
,
2538 object
->chunks
[i
].data
,
2539 MIN(CHUNK_SIZE
, end
- i
* CHUNK_SIZE
),
2540 connection
->buf
, connection
->buf
? more
: 0,
2541 httpServerDirectHandler
, connection
);
2544 unlockChunk(object
, i
);
2548 if(connection
->len
== 0)
2549 httpConnectionDestroyBuf(connection
);
2551 httpSetTimeout(connection
, serverTimeout
);
2552 do_stream_buf(IO_READ
| IO_NOTNOW
|
2553 ((immediate
&& connection
->len
) ? IO_IMMEDIATE
: 0),
2554 connection
->fd
, connection
->len
,
2556 (connection
->te
== TE_CHUNKED
?
2557 MIN(2048, CHUNK_SIZE
) : CHUNK_SIZE
),
2558 httpServerIndirectHandler
, connection
);
2563 httpServerDirectHandlerCommon(int kind
, int status
,
2564 FdEventHandlerPtr event
,
2565 StreamRequestPtr srequest
)
2567 HTTPConnectionPtr connection
= srequest
->data
;
2568 HTTPRequestPtr request
= connection
->request
;
2569 ObjectPtr object
= request
->object
;
2570 int i
= connection
->offset
/ CHUNK_SIZE
;
2573 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
2575 httpSetTimeout(connection
, -1);
2578 unlockChunk(object
, i
);
2579 if(kind
== 2) unlockChunk(object
, i
+ 1);
2580 if(status
!= -ECLIENTRESET
)
2581 do_log_error(L_ERROR
, -status
, "Read from server failed");
2582 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
2583 internAtomError(-status
, "Read from server failed"));
2587 /* We have incestuous knowledge of the decisions made in
2588 httpServerReadData */
2589 if(request
->to
>= 0)
2592 to
= object
->length
;
2593 if(connection
->te
== TE_CHUNKED
)
2594 end
= connection
->offset
+ connection
->chunk_remaining
;
2597 /* The amount of data actually read into the object */
2598 end1
= MIN(end
, i
* CHUNK_SIZE
+ MIN(kind
* CHUNK_SIZE
, srequest
->offset
));
2600 assert(end
>= 0 && end1
>= i
* CHUNK_SIZE
&& end1
<= (i
+ 2) * CHUNK_SIZE
);
2602 object
->chunks
[i
].size
=
2603 MAX(object
->chunks
[i
].size
, MIN(end1
- i
* CHUNK_SIZE
, CHUNK_SIZE
));
2604 if(kind
== 2 && end1
> (i
+ 1) * CHUNK_SIZE
) {
2605 object
->chunks
[i
+ 1].size
=
2606 MAX(object
->chunks
[i
+ 1].size
, end1
- (i
+ 1) * CHUNK_SIZE
);
2608 if(connection
->te
== TE_CHUNKED
) {
2609 connection
->chunk_remaining
-= (end1
- connection
->offset
);
2610 assert(connection
->chunk_remaining
>= 0);
2612 connection
->offset
= end1
;
2613 object
->size
= MAX(object
->size
, end1
);
2614 unlockChunk(object
, i
);
2615 if(kind
== 2) unlockChunk(object
, i
+ 1);
2617 if(i
* CHUNK_SIZE
+ srequest
->offset
> end1
) {
2618 connection
->len
= i
* CHUNK_SIZE
+ srequest
->offset
- end1
;
2619 return httpServerIndirectHandlerCommon(connection
, status
);
2621 notifyObject(object
);
2623 httpServerFinish(connection
, 1, 0);
2626 return httpServerReadData(connection
, 0);
2632 httpServerDirectHandler(int status
,
2633 FdEventHandlerPtr event
,
2634 StreamRequestPtr srequest
)
2636 return httpServerDirectHandlerCommon(1, status
, event
, srequest
);
2640 httpServerDirectHandler2(int status
,
2641 FdEventHandlerPtr event
,
2642 StreamRequestPtr srequest
)
2644 return httpServerDirectHandlerCommon(2, status
, event
, srequest
);
2647 /* Add the data accumulated in connection->buf into the object in
2648 connection->request. Returns 0 in the normal case, 1 if the TE is
2649 self-terminating and we're done, -1 if there was a problem with
2650 objectAddData, -2 if there was a problem with the data. */
2652 connectionAddData(HTTPConnectionPtr connection
, int skip
)
2654 HTTPRequestPtr request
= connection
->request
;
2655 ObjectPtr object
= request
->object
;
2658 if(connection
->te
== TE_IDENTITY
) {
2661 len
= connection
->len
- skip
;
2662 if(object
->length
>= 0) {
2663 len
= MIN(object
->length
- connection
->offset
, len
);
2665 if(request
->to
>= 0)
2666 len
= MIN(request
->to
- connection
->offset
, len
);
2668 rc
= objectAddData(object
, connection
->buf
+ skip
,
2669 connection
->offset
, len
);
2672 connection
->offset
+= len
;
2673 connection
->len
-= (len
+ skip
);
2674 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2675 (unsigned long)connection
, (unsigned long)object
,
2676 connection
->offset
);
2679 if(connection
->len
> 0 && skip
+ len
> 0) {
2680 memmove(connection
->buf
,
2681 connection
->buf
+ skip
+ len
, connection
->len
);
2684 if((object
->length
>= 0 && object
->length
<= connection
->offset
) ||
2685 (request
->to
>= 0 && request
->to
<= connection
->offset
)) {
2686 notifyObject(object
);
2690 notifyObject(object
);
2693 } else if(connection
->te
== TE_CHUNKED
) {
2694 int i
= skip
, j
, size
;
2695 /* connection->chunk_remaining is 0 at the end of a chunk, -1
2696 after the CR/LF pair ending a chunk, and -2 after we've
2697 seen a chunk of length 0. */
2698 if(connection
->chunk_remaining
> -2) {
2700 if(connection
->chunk_remaining
<= 0) {
2701 if(connection
->chunk_remaining
== 0) {
2702 if(connection
->len
< i
+ 2)
2704 if(connection
->buf
[i
] != '\r' ||
2705 connection
->buf
[i
+ 1] != '\n')
2708 connection
->chunk_remaining
= -1;
2710 if(connection
->len
< i
+ 2)
2712 j
= parseChunkSize(connection
->buf
, i
,
2713 connection
->len
, &size
);
2721 connection
->chunk_remaining
= -2;
2724 connection
->chunk_remaining
= size
;
2727 /* connection->chunk_remaining > 0 */
2728 size
= MIN(connection
->chunk_remaining
,
2729 connection
->len
- i
);
2732 rc
= objectAddData(object
, connection
->buf
+ i
,
2733 connection
->offset
, size
);
2734 connection
->offset
+= size
;
2738 connection
->chunk_remaining
-= size
;
2739 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2740 (unsigned long)connection
,
2741 (unsigned long)object
,
2742 connection
->offset
);
2746 connection
->len
-= i
;
2747 if(connection
->len
> 0)
2748 memmove(connection
->buf
, connection
->buf
+ i
, connection
->len
);
2749 if(i
> 0 || connection
->chunk_remaining
== -2)
2750 notifyObject(object
);
2751 if(connection
->chunk_remaining
== -2)
2761 listServers(FILE *out
)
2763 HTTPServerPtr server
;
2766 fprintf(out
, "<!DOCTYPE HTML PUBLIC "
2767 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2768 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2770 "\r\n<title>Known servers</title>\n"
2772 "<h1>Known servers</h1>\n");
2774 alternatingHttpStyle(out
, "servers");
2775 fprintf(out
, "<table id=servers>\n");
2776 fprintf(out
, "<thead><tr><th>Server</th>"
2778 "<th>Persistent</th>"
2780 "<th>Connections</th>"
2785 fprintf(out
, "<tbody>\n");
2789 fprintf(out
, "<tr class=\"%s\">", entry
% 2 == 0 ? "even" : "odd");
2790 if(server
->port
== 80)
2791 fprintf(out
, "<td>%s</td>", server
->name
);
2793 fprintf(out
, "<td>%s:%d</td>", server
->name
, server
->port
);
2795 if(server
->version
== HTTP_11
)
2796 fprintf(out
, "<td>1.1</td>");
2797 else if(server
->version
== HTTP_10
)
2798 fprintf(out
, "<td>1.0</td>");
2800 fprintf(out
, "<td>unknown</td>");
2802 if(server
->persistent
< 0)
2803 fprintf(out
, "<td>no</td>");
2804 else if(server
->persistent
> 0)
2805 fprintf(out
, "<td>yes</td>");
2807 fprintf(out
, "<td>unknown</td>");
2809 if(server
->version
!= HTTP_11
|| server
->persistent
<= 0)
2810 fprintf(out
, "<td></td>");
2811 else if(server
->pipeline
< 0)
2812 fprintf(out
, "<td>no</td>");
2813 else if(server
->pipeline
>= 0 && server
->pipeline
<= 1)
2814 fprintf(out
, "<td>unknown</td>");
2815 else if(server
->pipeline
== 2 || server
->pipeline
== 3)
2816 fprintf(out
, "<td>probing</td>");
2818 fprintf(out
, "<td>yes</td>");
2821 for(i
= 0; i
< server
->maxslots
; i
++)
2822 if(server
->connection
[i
] && !server
->connection
[i
]->connecting
) {
2823 if(i
< server
->numslots
)
2829 fprintf(out
, "<td>%d/%d", n
, server
->numslots
);
2831 fprintf(out
, " + %d</td>", m
);
2833 fprintf(out
, "</td>");
2835 if(server
->lies
> 0)
2836 fprintf(out
, "<td>(%d lies)</td>", (server
->lies
+ 9) / 10);
2838 fprintf(out
, "<td></td>");
2841 fprintf(out
, "<td>%.3f</td>", (double)server
->rtt
/ 1000000.0);
2843 fprintf(out
, "<td></td>");
2844 if(server
->rate
> 0)
2845 fprintf(out
, "<td>%d</td>", server
->rate
);
2847 fprintf(out
, "<td></td>");
2849 fprintf(out
, "</tr>\n");
2850 server
= server
->next
;
2853 fprintf(out
, "</tbody>\n");
2854 fprintf(out
, "</table>\n");
2855 fprintf(out
, "<p><a href=\"/polipo/\">back</a></p>");
2856 fprintf(out
, "</body></html>\n");