2 Copyright (c) 2003-2006 by Juliusz Chroboczek
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 int serverExpireTime
= 24 * 60 * 60;
26 int smallRequestTime
= 10;
27 int replyUnpipelineTime
= 20;
28 int replyUnpipelineSize
= 1024 * 1024;
29 int pipelineAdditionalRequests
= 1;
30 int maxPipelineTrain
= 10;
31 AtomPtr parentProxy
= NULL
;
32 AtomPtr parentHost
= NULL
;
34 int pmmFirstSize
= 0, pmmSize
= 0;
37 int serverMaxSlots
= 8;
38 int dontCacheRedirects
= 0;
39 int maxSideBuffering
= 1500;
40 int maxConnectionAge
= 1260;
41 int maxConnectionRequests
= 400;
43 static HTTPServerPtr servers
= 0;
45 static int httpServerContinueConditionHandler(int, ConditionHandlerPtr
);
46 static int initParentProxy(void);
47 static int parentProxySetter(ConfigVariablePtr var
, void *value
);
48 static void httpServerDelayedFinish(HTTPConnectionPtr
);
49 static int allowUnalignedRangeRequests
= 0;
54 CONFIG_VARIABLE_SETTABLE(parentProxy
, CONFIG_ATOM_LOWER
, parentProxySetter
,
55 "Parent proxy (host:port).");
56 CONFIG_VARIABLE(serverExpireTime
, CONFIG_TIME
,
57 "Time during which server data is valid.");
58 CONFIG_VARIABLE_SETTABLE(smallRequestTime
, CONFIG_TIME
, configIntSetter
,
59 "Estimated time for a small request.");
60 CONFIG_VARIABLE_SETTABLE(replyUnpipelineTime
, CONFIG_TIME
, configIntSetter
,
61 "Estimated time for a pipeline break.");
62 CONFIG_VARIABLE_SETTABLE(replyUnpipelineSize
, CONFIG_INT
, configIntSetter
,
63 "Size for a pipeline break.");
64 CONFIG_VARIABLE_SETTABLE(pipelineAdditionalRequests
, CONFIG_TRISTATE
,
66 "Pipeline requests on an active connection.");
67 CONFIG_VARIABLE_SETTABLE(maxPipelineTrain
, CONFIG_INT
,
69 "Maximum number of requests "
70 "pipelined at a time.");
71 CONFIG_VARIABLE(pmmFirstSize
, CONFIG_INT
,
72 "The size of the first PMM chunk.");
73 CONFIG_VARIABLE(pmmSize
, CONFIG_INT
,
74 "The size of a PMM chunk.");
75 CONFIG_VARIABLE(serverSlots
, CONFIG_INT
,
76 "Maximum number of connections per server.");
77 CONFIG_VARIABLE(serverSlots1
, CONFIG_INT
,
78 "Maximum number of connections per HTTP/1.0 server.");
79 CONFIG_VARIABLE(serverMaxSlots
, CONFIG_INT
,
80 "Maximum number of connections per broken server.");
81 CONFIG_VARIABLE(dontCacheRedirects
, CONFIG_BOOLEAN
,
82 "If true, don't cache redirects.");
83 CONFIG_VARIABLE_SETTABLE(allowUnalignedRangeRequests
,
84 CONFIG_BOOLEAN
, configIntSetter
,
85 "Allow unaligned range requests (unreliable).");
86 CONFIG_VARIABLE_SETTABLE(maxSideBuffering
,
87 CONFIG_INT
, configIntSetter
,
88 "Maximum buffering for PUT and POST requests.");
89 CONFIG_VARIABLE_SETTABLE(maxConnectionAge
,
90 CONFIG_TIME
, configIntSetter
,
91 "Maximum age of a server-side connection.");
92 CONFIG_VARIABLE_SETTABLE(maxConnectionRequests
,
93 CONFIG_INT
, configIntSetter
,
94 "Maximum number of requests on a server-side connection.");
98 parentProxySetter(ConfigVariablePtr var
, void *value
)
100 configAtomSetter(var
, value
);
106 discardServer(HTTPServerPtr server
)
108 HTTPServerPtr previous
;
109 assert(!server
->request
);
111 if(server
== servers
)
112 servers
= server
->next
;
115 while(previous
->next
!= server
)
116 previous
= previous
->next
;
117 previous
->next
= server
->next
;
124 httpServerIdle(HTTPServerPtr server
)
129 for(i
= 0; i
< server
->maxslots
; i
++)
130 if(server
->connection
[i
])
136 expireServersHandler(TimeEventHandlerPtr event
)
138 HTTPServerPtr server
, next
;
139 TimeEventHandlerPtr e
;
143 if(httpServerIdle(server
) &&
144 server
->time
+ serverExpireTime
< current_time
.tv_sec
)
145 discardServer(server
);
148 e
= scheduleTimeEvent(serverExpireTime
/ 60 + 60,
149 expireServersHandler
, 0, NULL
);
151 do_log(L_ERROR
, "Couldn't schedule server expiry.\n");
160 return (size
+ CHUNK_SIZE
- 1) / CHUNK_SIZE
* CHUNK_SIZE
;
166 AtomPtr host
, port_atom
;
170 releaseAtom(parentHost
);
176 if(parentProxy
!= NULL
&& parentProxy
->length
== 0) {
177 releaseAtom(parentProxy
);
181 if(parentProxy
== NULL
)
184 rc
= atomSplit(parentProxy
, ':', &host
, &port_atom
);
186 do_log(L_ERROR
, "Couldn't parse parentProxy.");
187 releaseAtom(parentProxy
);
192 port
= atoi(port_atom
->string
);
193 if(port
<= 0 || port
>= 0x10000) {
195 releaseAtom(port_atom
);
196 do_log(L_ERROR
, "Couldn't parse parentProxy.");
197 releaseAtom(parentProxy
);
210 TimeEventHandlerPtr event
;
213 if(pmmFirstSize
|| pmmSize
) {
214 if(pmmSize
== 0) pmmSize
= pmmFirstSize
;
215 if(pmmFirstSize
== 0) pmmFirstSize
= pmmSize
;
216 pmmSize
= roundSize(pmmSize
);
217 pmmFirstSize
= roundSize(pmmFirstSize
);
220 if(serverMaxSlots
< 1)
224 if(serverSlots
> serverMaxSlots
)
225 serverSlots
= serverMaxSlots
;
226 if(serverSlots1
< serverSlots
)
227 serverSlots1
= serverSlots
;
228 if(serverSlots1
> serverMaxSlots
)
229 serverSlots1
= serverMaxSlots
;
233 event
= scheduleTimeEvent(serverExpireTime
/ 60 + 60, expireServersHandler
,
236 do_log(L_ERROR
, "Couldn't schedule server expiry.\n");
242 getServer(char *name
, int port
, int proxy
)
244 HTTPServerPtr server
;
249 if(strcmp(server
->name
, name
) == 0 && server
->port
== port
&&
250 server
->isProxy
== proxy
) {
251 if(httpServerIdle(server
) &&
252 server
->time
+ serverExpireTime
< current_time
.tv_sec
) {
253 discardServer(server
);
257 server
->time
= current_time
.tv_sec
;
261 server
= server
->next
;
264 server
= malloc(sizeof(HTTPServerRec
));
266 do_log(L_ERROR
, "Couldn't allocate server.\n");
270 server
->connection
= malloc(serverMaxSlots
* sizeof(HTTPConnectionPtr
));
271 if(server
->connection
== NULL
) {
272 do_log(L_ERROR
, "Couldn't allocate server.\n");
277 server
->idleHandler
= malloc(serverMaxSlots
* sizeof(FdEventHandlerPtr
));
278 if(server
->connection
== NULL
) {
279 do_log(L_ERROR
, "Couldn't allocate server.\n");
280 free(server
->connection
);
285 server
->maxslots
= serverMaxSlots
;
287 server
->name
= strdup(name
);
288 if(server
->name
== NULL
) {
289 do_log(L_ERROR
, "Couldn't allocate server name.\n");
295 server
->addrindex
= 0;
296 server
->isProxy
= proxy
;
297 server
->version
= HTTP_UNKNOWN
;
298 server
->persistent
= 0;
299 server
->pipeline
= 0;
300 server
->time
= current_time
.tv_sec
;
303 server
->numslots
= MIN(serverSlots
, server
->maxslots
);
304 for(i
= 0; i
< server
->maxslots
; i
++) {
305 server
->connection
[i
] = NULL
;
306 server
->idleHandler
[i
] = NULL
;
308 server
->request
= NULL
;
309 server
->request_last
= NULL
;
312 server
->next
= servers
;
318 httpServerQueueRequest(HTTPServerPtr server
, HTTPRequestPtr request
)
320 assert(request
->request
&& request
->request
->request
== request
);
321 assert(request
->connection
== NULL
);
322 if(server
->request
) {
323 server
->request_last
->next
= request
;
324 server
->request_last
= request
;
326 server
->request_last
= request
;
327 server
->request
= request
;
333 httpServerAbort(HTTPConnectionPtr connection
, int fail
,
334 int code
, AtomPtr message
)
336 HTTPRequestPtr request
= connection
->request
;
338 if(request
->request
) {
339 httpClientError(request
->request
, code
, retainAtom(message
));
342 request
->object
->flags
|= OBJECT_FAILED
;
343 if(request
->object
->flags
& OBJECT_INITIAL
)
344 abortObject(request
->object
, code
, retainAtom(message
));
345 notifyObject(request
->object
);
348 releaseAtom(message
);
349 if(!connection
->connecting
)
350 httpServerFinish(connection
, 1, 0);
354 httpServerAbortRequest(HTTPRequestPtr request
, int fail
,
355 int code
, AtomPtr message
)
357 if(request
->connection
&& request
== request
->connection
->request
) {
358 httpServerAbort(request
->connection
, fail
, code
, message
);
360 HTTPRequestPtr requestor
= request
->request
;
362 requestor
->request
= NULL
;
363 request
->request
= NULL
;
364 httpClientError(requestor
, code
, retainAtom(message
));
367 request
->object
->flags
|= OBJECT_FAILED
;
368 if(request
->object
->flags
& OBJECT_INITIAL
)
369 abortObject(request
->object
, code
, retainAtom(message
));
370 notifyObject(request
->object
);
372 releaseAtom(message
);
377 httpServerClientReset(HTTPRequestPtr request
)
379 if(request
->connection
&&
380 request
->connection
->fd
>= 0 &&
381 !request
->connection
->connecting
&&
382 request
->connection
->request
== request
)
383 pokeFdEvent(request
->connection
->fd
, -ECLIENTRESET
, POLLIN
| POLLOUT
);
388 httpMakeServerRequest(char *name
, int port
, ObjectPtr object
,
389 int method
, int from
, int to
, HTTPRequestPtr requestor
)
391 HTTPServerPtr server
;
392 HTTPRequestPtr request
;
395 assert(!(object
->flags
& OBJECT_INPROGRESS
));
398 server
= getServer(parentHost
->string
, parentPort
, 1);
400 server
= getServer(name
, port
, 0);
402 if(server
== NULL
) return -1;
404 object
->flags
|= OBJECT_INPROGRESS
;
405 object
->requestor
= requestor
;
407 request
= httpMakeRequest();
409 do_log(L_ERROR
, "Couldn't allocate request.\n");
413 /* Because we allocate objects in chunks, we cannot have data that
414 doesn't start at a chunk boundary. */
415 if(from
% CHUNK_SIZE
!= 0) {
416 if(allowUnalignedRangeRequests
) {
417 objectFillFromDisk(object
, from
/ CHUNK_SIZE
* CHUNK_SIZE
, 1);
418 if(objectHoleSize(object
, from
- 1) != 0)
419 from
= from
/ CHUNK_SIZE
* CHUNK_SIZE
;
421 from
= from
/ CHUNK_SIZE
* CHUNK_SIZE
;
425 request
->object
= retainObject(object
);
426 request
->method
= method
;
427 if(method
== METHOD_CONDITIONAL_GET
) {
429 request
->method
= METHOD_HEAD
;
433 (expectContinue
? (requestor
->flags
& REQUEST_WAIT_CONTINUE
) : 0);
434 request
->from
= from
;
436 request
->request
= requestor
;
437 requestor
->request
= request
;
438 request
->cache_control
= requestor
->cache_control
;
439 request
->time0
= null_time
;
440 request
->time1
= null_time
;
442 rc
= httpServerQueueRequest(server
, request
);
444 do_log(L_ERROR
, "Couldn't queue request.\n");
445 request
->request
= NULL
;
446 requestor
->request
= NULL
;
447 object
->flags
&= ~(OBJECT_INPROGRESS
| OBJECT_VALIDATING
);
448 releaseNotifyObject(object
);
449 httpDestroyRequest(request
);
453 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
454 if(server
->version
== HTTP_10
) {
455 httpServerAbortRequest(request
, 1,
456 417, internAtom("Expectation failed"));
459 } else if(expectContinue
>= 2 && server
->version
== HTTP_11
) {
460 if(request
->method
== METHOD_POST
|| request
->method
== METHOD_PUT
)
461 request
->flags
|= REQUEST_WAIT_CONTINUE
;
465 rc
= httpServerTrigger(server
);
467 /* We must be very short on memory. If there are any requests
468 queued, we abort one and try again. If there aren't, we
470 do_log(L_ERROR
, "Couldn't trigger server -- out of memory?\n");
471 if(server
->request
) {
472 httpServerAbortRequest(server
->request
, 1, 503,
473 internAtom("Couldn't trigger server"));
481 httpServerConnection(HTTPServerPtr server
)
483 HTTPConnectionPtr connection
;
486 connection
= httpMakeConnection();
487 if(connection
== NULL
) {
488 do_log(L_ERROR
, "Couldn't allocate server connection.\n");
491 connection
->server
= server
;
493 for(i
= 0; i
< server
->numslots
; i
++) {
494 if(!server
->connection
[i
]) {
495 server
->connection
[i
] = connection
;
499 assert(i
< server
->numslots
);
501 connection
->request
= NULL
;
502 connection
->request_last
= NULL
;
504 do_log(D_SERVER_CONN
, "C... %s:%d.\n",
505 scrub(connection
->server
->name
), connection
->server
->port
);
506 httpSetTimeout(connection
, serverTimeout
);
507 if(socksParentProxy
) {
508 connection
->connecting
= CONNECTING_SOCKS
;
509 do_socks_connect(server
->name
, connection
->server
->port
,
510 httpServerSocksHandler
, connection
);
512 connection
->connecting
= CONNECTING_DNS
;
513 do_gethostbyname(server
->name
, 0,
514 httpServerConnectionDnsHandler
,
521 httpServerConnectionDnsHandler(int status
, GethostbynameRequestPtr request
)
523 HTTPConnectionPtr connection
= request
->data
;
525 httpSetTimeout(connection
, -1);
529 message
= internAtomF("Host %s lookup failed: %s",
531 request
->name
->string
: "(unknown)",
532 request
->error_message
?
533 request
->error_message
->string
:
535 do_log(L_ERROR
, "Host %s lookup failed: %s (%d).\n",
537 scrub(request
->name
->string
) : "(unknown)",
538 request
->error_message
?
539 request
->error_message
->string
:
540 pstrerror(-status
), -status
);
541 connection
->connecting
= 0;
542 if(connection
->server
->request
)
543 httpServerAbortRequest(connection
->server
->request
, 1, 504,
544 retainAtom(message
));
545 httpServerAbort(connection
, 1, 502, message
);
549 if(request
->addr
->string
[0] == DNS_CNAME
) {
550 if(request
->count
> 10) {
551 AtomPtr message
= internAtom("DNS CNAME loop");
552 do_log(L_ERROR
, "DNS CNAME loop.\n");
553 connection
->connecting
= 0;
554 if(connection
->server
->request
)
555 httpServerAbortRequest(connection
->server
->request
, 1, 504,
556 retainAtom(message
));
557 httpServerAbort(connection
, 1, 504, message
);
561 httpSetTimeout(connection
, serverTimeout
);
562 do_gethostbyname(request
->addr
->string
+ 1, request
->count
+ 1,
563 httpServerConnectionDnsHandler
,
568 connection
->connecting
= CONNECTING_CONNECT
;
569 httpSetTimeout(connection
, serverTimeout
);
570 do_connect(retainAtom(request
->addr
), connection
->server
->addrindex
,
571 connection
->server
->port
,
572 httpServerConnectionHandler
, connection
);
577 httpServerConnectionHandler(int status
,
578 FdEventHandlerPtr event
,
579 ConnectRequestPtr request
)
581 HTTPConnectionPtr connection
= request
->data
;
583 assert(connection
->fd
< 0);
584 if(request
->fd
>= 0) {
586 connection
->fd
= request
->fd
;
587 connection
->server
->addrindex
= request
->index
;
588 rc
= setNodelay(connection
->fd
, 1);
590 do_log_error(L_WARN
, errno
, "Couldn't disable Nagle's algorithm");
593 return httpServerConnectionHandlerCommon(status
, connection
);
597 httpServerSocksHandler(int status
, SocksRequestPtr request
)
599 HTTPConnectionPtr connection
= request
->data
;
601 assert(connection
->fd
< 0);
602 if(request
->fd
>= 0) {
603 connection
->fd
= request
->fd
;
604 connection
->server
->addrindex
= 0;
606 return httpServerConnectionHandlerCommon(status
, connection
);
610 httpServerConnectionHandlerCommon(int status
, HTTPConnectionPtr connection
)
612 httpSetTimeout(connection
, -1);
616 internAtomError(-status
, "Connect to %s:%d failed",
617 connection
->server
->name
,
618 connection
->server
->port
);
619 if(status
!= -ECLIENTRESET
)
620 do_log_error(L_ERROR
, -status
, "Connect to %s:%d failed",
621 scrub(connection
->server
->name
),
622 connection
->server
->port
);
623 connection
->connecting
= 0;
624 if(connection
->server
->request
)
625 httpServerAbortRequest(connection
->server
->request
,
626 status
!= -ECLIENTRESET
, 504,
627 retainAtom(message
));
628 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 504, message
);
632 do_log(D_SERVER_CONN
, "C %s:%d.\n",
633 scrub(connection
->server
->name
), connection
->server
->port
);
635 connection
->connecting
= 0;
636 /* serverTrigger will take care of inserting any timeouts */
637 httpServerTrigger(connection
->server
);
642 httpServerIdleHandler(int a
, FdEventHandlerPtr event
)
644 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
645 HTTPServerPtr server
= connection
->server
;
648 assert(!connection
->request
);
650 do_log(D_SERVER_CONN
, "Idle connection to %s:%d died.\n",
651 scrub(connection
->server
->name
), connection
->server
->port
);
653 for(i
= 0; i
< server
->maxslots
; i
++) {
654 if(connection
== server
->connection
[i
]) {
655 server
->idleHandler
[i
] = NULL
;
659 assert(i
< server
->maxslots
);
661 httpServerAbort(connection
, 1, 504, internAtom("Timeout"));
665 /* Discard aborted requests at the head of the queue. */
667 httpServerDiscardRequests(HTTPServerPtr server
)
669 HTTPRequestPtr request
;
670 while(server
->request
&& !server
->request
->request
) {
671 request
= server
->request
;
672 server
->request
= request
->next
;
673 request
->next
= NULL
;
674 if(server
->request
== NULL
)
675 server
->request_last
= NULL
;
676 request
->object
->flags
&= ~(OBJECT_INPROGRESS
| OBJECT_VALIDATING
);
677 releaseNotifyObject(request
->object
);
678 request
->object
= NULL
;
679 httpDestroyRequest(request
);
684 pipelineIsSmall(HTTPConnectionPtr connection
)
686 HTTPRequestPtr request
= connection
->request
;
688 if(pipelineAdditionalRequests
<= 0)
690 else if(pipelineAdditionalRequests
>= 2)
695 if(request
->next
|| !(request
->flags
& REQUEST_PERSISTENT
))
697 if(request
->method
== METHOD_HEAD
||
698 request
->method
== METHOD_CONDITIONAL_GET
)
700 if(request
->to
>= 0 && connection
->server
->rate
> 0 &&
701 request
->to
- request
->from
< connection
->server
->rate
*
708 numRequests(HTTPServerPtr server
)
711 HTTPRequestPtr request
= server
->request
;
714 request
= request
->next
;
720 httpServerGetConnection(HTTPServerPtr server
, int *idle_return
)
723 int connecting
= 0, empty
= 0, idle
= 0;
726 /* Try to find an idle connection */
727 for(i
= 0; i
< server
->numslots
; i
++) {
728 if(server
->connection
[i
]) {
729 if(!server
->connection
[i
]->connecting
) {
730 if(!server
->connection
[i
]->request
) {
731 if(server
->idleHandler
[i
])
732 unregisterFdEvent(server
->idleHandler
[i
]);
733 server
->idleHandler
[i
] = NULL
;
745 return server
->connection
[j
];
748 /* If there's an empty slot, schedule connection creation */
750 /* Don't open a connection if there are already enough in
751 progress, except if the server doesn't do persistent
752 connections and there's only one in progress. */
753 if((connecting
== 0 || (server
->persistent
<= 0 && connecting
<= 1)) ||
754 connecting
< numRequests(server
)) {
755 httpServerConnection(server
);
759 /* Find a connection that can accept additional requests */
760 if(server
->version
== HTTP_11
&& server
->pipeline
>= 4) {
761 for(i
= 0; i
< serverSlots
; i
++) {
762 if(server
->connection
[i
] && !server
->connection
[i
]->connecting
&&
763 pipelineIsSmall(server
->connection
[i
])) {
764 if(server
->idleHandler
[i
])
765 unregisterFdEvent(server
->idleHandler
[i
]);
766 server
->idleHandler
[i
] = NULL
;
768 return server
->connection
[i
];
777 httpServerTrigger(HTTPServerPtr server
)
779 HTTPConnectionPtr connection
;
780 HTTPRequestPtr request
;
781 int idle
, n
, i
, rc
, numidle
;
783 while(server
->request
) {
784 httpServerDiscardRequests(server
);
789 if(REQUEST_SIDE(server
->request
)) {
790 rc
= httpServerSideRequest(server
);
791 /* If rc is 0, httpServerSideRequest didn't dequeue this
792 request. Go through the scheduling loop again, come
797 connection
= httpServerGetConnection(server
, &numidle
);
798 if(!connection
) break;
800 /* If server->pipeline <= 0, we don't do pipelining. If
801 server->pipeline is 1, then we are ready to start probing
802 for pipelining on the server; we then send exactly two
803 requests in what is hopefully a single packet to check
804 whether the server has the nasty habit of discarding its
805 input buffers after each request.
806 If server->pipeline is 2 or 3, the pipelining probe is in
807 progress on this server, and we don't pipeline anything
808 until it succeeds. When server->pipeline >= 4, pipelining
809 is believed to work on this server. */
810 if(server
->version
!= HTTP_11
|| server
->pipeline
<= 0 ||
811 server
->pipeline
== 2 || server
->pipeline
== 3) {
812 if(connection
->pipelined
== 0)
816 } else if(server
->pipeline
== 1) {
817 if(connection
->pipelined
== 0)
818 n
= MIN(2, maxPipelineTrain
);
822 n
= maxPipelineTrain
;
825 /* Don't pipeline if there are more idle connections */
829 idle
= !connection
->pipelined
;
831 while(server
->request
&& connection
->pipelined
< n
) {
832 httpServerDiscardRequests(server
);
833 if(!server
->request
) break;
834 request
= server
->request
;
835 assert(request
->request
->request
== request
);
836 rc
= httpWriteRequest(connection
, request
, -1);
839 httpServerAbortRequest(request
, rc
!= -ECLIENTRESET
, 502,
840 internAtom("Couldn't "
844 do_log(D_SERVER_CONN
, "W: ");
845 do_log_n(D_SERVER_CONN
,
846 request
->object
->key
, request
->object
->key_size
);
847 do_log(D_SERVER_CONN
, " (%d)\n", request
->method
);
848 if(connection
->pipelined
> 0)
849 request
->flags
|= REQUEST_PIPELINED
;
850 request
->time0
= current_time
;
852 server
->request
= request
->next
;
853 request
->next
= NULL
;
854 if(server
->request
== NULL
)
855 server
->request_last
= NULL
;
856 httpQueueRequest(connection
, request
);
857 connection
->pipelined
++;
859 if(server
->persistent
> 0 && server
->pipeline
== 1 && i
>= 2)
860 server
->pipeline
= 2;
862 if(i
> 0) httpServerSendRequest(connection
);
864 if(idle
&& connection
->pipelined
> 0)
865 httpServerReply(connection
, 0);
870 for(i
= 0; i
< server
->maxslots
; i
++) {
871 if(server
->connection
[i
] &&
872 !server
->connection
[i
]->connecting
&&
873 !server
->connection
[i
]->request
) {
874 /* Artificially age any fresh connections that aren't used
875 straight away; this is necessary for the logic for POST and
876 the logic that determines whether a given request should be
878 if(server
->connection
[i
]->serviced
== 0)
879 server
->connection
[i
]->serviced
= 1;
880 if(!server
->idleHandler
[i
])
881 server
->idleHandler
[i
] =
882 registerFdEvent(server
->connection
[i
]->fd
, POLLIN
,
883 httpServerIdleHandler
,
884 sizeof(HTTPConnectionPtr
),
885 &server
->connection
[i
]);
886 if(!server
->idleHandler
[i
]) {
887 do_log(L_ERROR
, "Couldn't register idle handler.\n");
888 httpServerFinish(server
->connection
[i
], 1, 0);
890 httpSetTimeout(server
->connection
[i
], serverIdleTimeout
);
898 httpServerSideRequest(HTTPServerPtr server
)
900 HTTPRequestPtr request
= server
->request
;
901 HTTPConnectionPtr connection
;
902 HTTPRequestPtr requestor
= request
->request
;
903 HTTPConnectionPtr client
= requestor
->connection
;
904 int rc
, i
, freeslots
, idle
, connecting
;
906 assert(REQUEST_SIDE(request
));
913 /* Find a fresh connection */
914 for(i
= 0; i
< server
->numslots
; i
++) {
915 if(!server
->connection
[i
])
917 else if(!server
->connection
[i
]->connecting
) {
918 if(!server
->connection
[i
]->request
) {
919 if(server
->connection
[i
]->serviced
== 0) {
920 if(server
->idleHandler
[i
])
921 unregisterFdEvent(server
->idleHandler
[i
]);
922 server
->idleHandler
[i
] = NULL
;
923 connection
= server
->connection
[i
];
935 /* Make sure that a fresh connection will be established at some
936 point, then wait until httpServerTrigger calls us again. */
938 httpServerConnection(server
);
941 /* Shutdown a random idle connection */
942 pokeFdEvent(server
->connection
[idle
]->fd
,
943 -EDOSHUTDOWN
, POLLIN
| POLLOUT
);
949 rc
= httpWriteRequest(connection
, request
, client
->bodylen
);
951 do_log(L_ERROR
, "Couldn't write POST or PUT request.\n");
952 httpServerAbortRequest(request
, rc
!= -ECLIENTRESET
, 502,
953 internAtom("Couldn't write request"));
956 server
->request
= request
->next
;
957 request
->next
= NULL
;
958 if(server
->request
== NULL
)
959 server
->request_last
= NULL
;
960 httpQueueRequest(connection
, request
);
961 connection
->pipelined
= 1;
962 request
->time0
= current_time
;
963 connection
->reqoffset
= 0;
964 connection
->bodylen
= client
->bodylen
;
965 httpServerDoSide(connection
);
970 httpServerDoSide(HTTPConnectionPtr connection
)
972 HTTPRequestPtr request
= connection
->request
;
973 HTTPRequestPtr requestor
= request
->request
;
974 HTTPConnectionPtr client
= requestor
->connection
;
975 int len
= MIN(client
->reqlen
- client
->reqbegin
,
976 connection
->bodylen
- connection
->reqoffset
);
979 (len
>= maxSideBuffering
||
980 client
->reqbegin
> 0 ||
981 (connection
->reqoffset
+ client
->reqlen
- client
->reqbegin
) >=
982 connection
->bodylen
);
983 int done
= connection
->reqoffset
>= connection
->bodylen
;
985 assert(connection
->bodylen
>= 0);
987 httpSetTimeout(connection
, 60);
989 if(connection
->reqlen
> 0) {
990 /* Send the headers, but don't send any part of the body if
991 we're in wait_continue. */
992 do_stream_2(IO_WRITE
,
994 connection
->reqbuf
, connection
->reqlen
,
995 client
->reqbuf
+ client
->reqbegin
,
996 (request
->flags
& REQUEST_WAIT_CONTINUE
) ? 0 : len
,
997 httpServerSideHandler2
, connection
);
998 httpServerReply(connection
, 0);
999 } else if(request
->object
->flags
& OBJECT_ABORTED
) {
1000 if(connection
->reqbuf
)
1001 dispose_chunk(connection
->reqbuf
);
1002 connection
->reqbuf
= NULL
;
1003 connection
->reqlen
= 0;
1004 pokeFdEvent(connection
->fd
, -ESHUTDOWN
, POLLIN
);
1005 if(client
->flags
& CONN_READER
) {
1006 client
->flags
|= CONN_SIDE_READER
;
1007 do_stream(IO_READ
| IO_IMMEDIATE
| IO_NOTNOW
,
1008 client
->fd
, 0, NULL
, 0,
1009 httpClientSideHandler
, client
);
1011 } else if(!(request
->flags
& REQUEST_WAIT_CONTINUE
) && doflush
) {
1012 /* Make sure there's a reqbuf, as httpServerFinish uses
1013 it to determine if there's a writer. */
1014 if(connection
->reqbuf
== NULL
)
1015 connection
->reqbuf
= get_chunk();
1016 assert(connection
->reqbuf
!= NULL
);
1019 client
->reqbuf
+ client
->reqbegin
, len
,
1020 httpServerSideHandler
, connection
);
1022 if(connection
->reqbuf
) {
1023 httpConnectionDestroyReqbuf(connection
);
1024 connection
->reqlen
= 0;
1026 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
1027 ConditionHandlerPtr chandler
;
1028 do_log(D_SERVER_CONN
, "W... %s:%d.\n",
1029 scrub(connection
->server
->name
), connection
->server
->port
);
1031 conditionWait(&request
->object
->condition
,
1032 httpServerContinueConditionHandler
,
1033 sizeof(connection
), &connection
);
1037 do_log(L_ERROR
, "Couldn't register condition handler.\n");
1038 /* Fall through -- the client side will clean up. */
1040 client
->flags
|= CONN_SIDE_READER
;
1041 do_stream(IO_READ
| (done
? IO_IMMEDIATE
: 0 ) | IO_NOTNOW
,
1042 client
->fd
, client
->reqlen
,
1043 client
->reqbuf
, CHUNK_SIZE
,
1044 httpClientSideHandler
, client
);
1050 httpClientDelayedDoSideHandler(TimeEventHandlerPtr event
)
1052 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1053 httpServerDoSide(connection
);
1058 httpServerDelayedDoSide(HTTPConnectionPtr connection
)
1060 TimeEventHandlerPtr handler
;
1061 handler
= scheduleTimeEvent(1, httpClientDelayedDoSideHandler
,
1062 sizeof(connection
), &connection
);
1064 do_log(L_ERROR
, "Couldn't schedule DoSide -- freeing memory.\n");
1065 free_chunk_arenas();
1066 handler
= scheduleTimeEvent(1, httpClientDelayedDoSideHandler
,
1067 sizeof(connection
), &connection
);
1068 do_log(L_ERROR
, "Couldn't schedule DoSide.\n");
1069 /* Somebody will hopefully end up timing out. */
1076 httpServerSideHandlerCommon(int kind
, int status
,
1077 FdEventHandlerPtr event
,
1078 StreamRequestPtr srequest
)
1080 HTTPConnectionPtr connection
= srequest
->data
;
1081 HTTPRequestPtr request
= connection
->request
;
1082 HTTPRequestPtr requestor
= request
->request
;
1083 HTTPConnectionPtr client
= requestor
->connection
;
1086 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1089 do_log_error(L_ERROR
, -status
, "Couldn't write to server");
1090 httpConnectionDestroyReqbuf(connection
);
1091 if(status
!= -ECLIENTRESET
)
1092 shutdown(connection
->fd
, 2);
1093 abortObject(request
->object
, 502,
1094 internAtom("Couldn't write to server"));
1095 /* Let the read side handle the error */
1096 httpServerDoSide(connection
);
1100 assert(srequest
->offset
> 0);
1103 if(srequest
->offset
< connection
->reqlen
)
1105 bodylen
= srequest
->offset
- connection
->reqlen
;
1106 connection
->reqlen
= 0;
1107 httpConnectionDestroyReqbuf(connection
);
1109 bodylen
= srequest
->offset
;
1113 assert(client
->reqbegin
+ bodylen
<= client
->reqlen
);
1115 if(client
->reqlen
> client
->reqbegin
+ bodylen
)
1116 memmove(client
->reqbuf
, client
->reqbuf
+ client
->reqbegin
+ bodylen
,
1117 client
->reqlen
- client
->reqbegin
- bodylen
);
1118 client
->reqlen
-= bodylen
+ client
->reqbegin
;
1119 client
->reqbegin
= 0;
1120 connection
->reqoffset
+= bodylen
;
1121 httpServerDoSide(connection
);
1126 httpServerSideHandler(int status
,
1127 FdEventHandlerPtr event
,
1128 StreamRequestPtr srequest
)
1130 return httpServerSideHandlerCommon(1, status
, event
, srequest
);
1134 httpServerSideHandler2(int status
,
1135 FdEventHandlerPtr event
,
1136 StreamRequestPtr srequest
)
1138 return httpServerSideHandlerCommon(2, status
, event
, srequest
);
1142 httpServerContinueConditionHandler(int status
, ConditionHandlerPtr chandler
)
1144 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)chandler
->data
;
1146 if(connection
->request
->flags
& REQUEST_WAIT_CONTINUE
)
1148 httpServerDelayedDoSide(connection
);
1152 /* s is 0 to keep the connection alive, 1 to shutdown the connection */
1154 httpServerFinish(HTTPConnectionPtr connection
, int s
, int offset
)
1156 HTTPServerPtr server
= connection
->server
;
1157 HTTPRequestPtr request
= connection
->request
;
1161 assert(connection
->pipelined
>= 1);
1162 assert((connection
->pipelined
> 1) == (request
->next
!= NULL
));
1164 assert(connection
->pipelined
== 0);
1167 if(!s
&& (!connection
->request
||
1168 !(connection
->request
->flags
& REQUEST_PERSISTENT
)))
1171 if(connection
->serviced
>= maxConnectionRequests
||
1172 connection
->time
< current_time
.tv_sec
- maxConnectionAge
)
1175 if(connection
->reqbuf
) {
1176 /* As most normal requests go out in a single packet, this is
1177 extremely unlikely to happen. As for POST/PUT requests,
1178 they are not pipelined, so this can only happen if the
1179 server sent an error reply early. */
1180 assert(connection
->fd
>= 0);
1181 shutdown(connection
->fd
, 1);
1182 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLOUT
);
1183 httpServerDelayedFinish(connection
);
1188 /* Update statistics about the server */
1189 int size
= -1, d
= -1, rtt
= -1, rate
= -1;
1190 if(connection
->offset
> 0 && request
->from
>= 0)
1191 size
= connection
->offset
- request
->from
;
1192 if(request
->time1
.tv_sec
!= null_time
.tv_sec
) {
1193 d
= timeval_minus_usec(¤t_time
, &request
->time1
);
1194 if(!(request
->flags
& REQUEST_PIPELINED
) &&
1195 request
->time0
.tv_sec
!= null_time
.tv_sec
)
1196 rtt
= timeval_minus_usec(&request
->time1
, &request
->time0
);
1197 if(size
>= 8192 && d
> 50000)
1198 rate
= ((double)size
/ (double)d
) * 1000000.0 + 0.5;
1200 request
->time0
= null_time
;
1201 request
->time1
= null_time
;
1204 if(server
->rtt
>= 0)
1205 server
->rtt
= (3 * server
->rtt
+ rtt
+ 2) / 4;
1210 if(server
->rate
>= 0)
1211 server
->rate
= (3 * server
->rate
+ rate
+ 2) / 4;
1213 server
->rate
= rate
;
1216 httpDequeueRequest(connection
);
1217 connection
->pipelined
--;
1218 request
->object
->flags
&= ~(OBJECT_INPROGRESS
| OBJECT_VALIDATING
);
1219 if(request
->request
) {
1220 request
->request
->request
= NULL
;
1221 request
->request
= NULL
;
1223 releaseNotifyObject(request
->object
);
1224 request
->object
= NULL
;
1225 httpDestroyRequest(request
);
1228 do_log(D_SERVER_CONN
, "Done with server %s:%d connection (%d)\n",
1229 scrub(connection
->server
->name
), connection
->server
->port
, s
);
1231 assert(offset
<= connection
->len
);
1234 if(offset
< connection
->len
) {
1235 assert(connection
->buf
!= NULL
);
1236 if(!connection
->pipelined
) {
1238 "Closing connection to %s:%d: "
1239 "%d stray bytes of data.\n",
1240 scrub(server
->name
), server
->port
,
1241 connection
->len
- offset
);
1244 memmove(connection
->buf
, connection
->buf
+ offset
,
1245 connection
->len
- offset
);
1246 connection
->len
= connection
->len
- offset
;
1247 if((connection
->flags
& CONN_BIGBUF
) &&
1248 connection
->len
<= CHUNK_SIZE
)
1249 httpConnectionUnbigify(connection
);
1252 connection
->len
= 0;
1256 connection
->server
->time
= current_time
.tv_sec
;
1257 connection
->serviced
++;
1260 if(connection
->timeout
)
1261 cancelTimeEvent(connection
->timeout
);
1262 connection
->timeout
= NULL
;
1263 httpConnectionDestroyBuf(connection
);
1264 if(connection
->fd
>= 0)
1265 CLOSE(connection
->fd
);
1266 connection
->fd
= -1;
1267 server
->persistent
-= 1;
1268 if(server
->persistent
< -5)
1269 server
->numslots
= MIN(server
->maxslots
, serverMaxSlots
);
1270 if(connection
->request
) {
1272 do_log(D_SERVER_CONN
, "Restarting pipeline to %s:%d.\n",
1273 scrub(server
->name
), server
->port
);
1274 if(server
->pipeline
== 2)
1275 server
->pipeline
-= 20;
1277 server
->pipeline
-= 5;
1278 req
= connection
->request
;
1280 req
->connection
= NULL
;
1284 connection
->request_last
->next
= server
->request
;
1286 server
->request_last
= connection
->request_last
;
1287 server
->request
= connection
->request
;
1288 connection
->request
= NULL
;
1289 connection
->request_last
= NULL
;
1291 /* Make sure we don't get confused into thinking a probe
1293 if(server
->pipeline
== 2 || server
->pipeline
== 3)
1294 server
->pipeline
= 1;
1295 for(i
= 0; i
< server
->maxslots
; i
++)
1296 if(connection
== server
->connection
[i
])
1298 assert(i
< server
->maxslots
);
1299 if(server
->idleHandler
[i
])
1300 unregisterFdEvent(server
->idleHandler
[i
]);
1301 server
->idleHandler
[i
] = NULL
;
1302 server
->connection
[i
] = NULL
;
1305 server
->persistent
+= 1;
1306 if(server
->persistent
> 0)
1307 server
->numslots
= MIN(server
->maxslots
,
1308 server
->version
== HTTP_10
?
1309 serverSlots1
: serverSlots
);
1310 httpSetTimeout(connection
, serverTimeout
);
1311 /* See httpServerTrigger */
1312 if(connection
->pipelined
||
1313 (server
->version
== HTTP_11
&& server
->pipeline
<= 0) ||
1314 (server
->pipeline
== 3)) {
1317 if(connection
->pipelined
) {
1318 httpServerReply(connection
, 1);
1320 httpConnectionDestroyBuf(connection
);
1325 httpServerTrigger(server
);
1329 httpServerDelayedFinishHandler(TimeEventHandlerPtr event
)
1331 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1332 httpServerFinish(connection
, 1, 0);
1337 httpServerDelayedFinish(HTTPConnectionPtr connection
)
1339 TimeEventHandlerPtr handler
;
1341 handler
= scheduleTimeEvent(1, httpServerDelayedFinishHandler
,
1342 sizeof(connection
), &connection
);
1345 "Couldn't schedule delayed finish -- freeing memory.");
1346 free_chunk_arenas();
1347 handler
= scheduleTimeEvent(1, httpServerDelayedFinishHandler
,
1348 sizeof(connection
), &connection
);
1351 "Couldn't schedule delayed finish -- aborting.\n");
1358 httpServerReply(HTTPConnectionPtr connection
, int immediate
)
1360 assert(connection
->pipelined
> 0);
1362 if(connection
->request
->request
== NULL
) {
1363 do_log(L_WARN
, "Aborting pipeline on %s:%d.\n",
1364 scrub(connection
->server
->name
), connection
->server
->port
);
1365 httpServerFinish(connection
, 1, 0);
1369 do_log(D_SERVER_CONN
, "R: %s (%d)\n",
1370 scrub(connection
->request
->object
->key
),
1371 connection
->request
->method
);
1373 if(connection
->len
== 0)
1374 httpConnectionDestroyBuf(connection
);
1376 httpSetTimeout(connection
, serverTimeout
);
1377 do_stream_buf(IO_READ
| (immediate
? IO_IMMEDIATE
: 0) | IO_NOTNOW
,
1378 connection
->fd
, connection
->len
,
1379 &connection
->buf
, CHUNK_SIZE
,
1380 httpServerReplyHandler
, connection
);
1384 httpConnectionPipelined(HTTPConnectionPtr connection
)
1386 HTTPRequestPtr request
= connection
->request
;
1390 request
= request
->next
;
1396 httpServerUnpipeline(HTTPRequestPtr request
)
1398 HTTPConnectionPtr connection
= request
->connection
;
1399 HTTPServerPtr server
= connection
->server
;
1401 request
->flags
&= ~REQUEST_PERSISTENT
;
1405 "Restarting pipeline to %s:%d.\n",
1406 scrub(connection
->server
->name
), connection
->server
->port
);
1407 req
= request
->next
;
1409 req
->connection
= NULL
;
1413 connection
->request_last
->next
= server
->request
;
1415 server
->request_last
= connection
->request_last
;
1416 server
->request
= request
->next
;
1417 request
->next
= NULL
;
1418 connection
->request_last
= request
;
1420 connection
->pipelined
= httpConnectionPipelined(connection
);
1424 httpServerRestart(HTTPConnectionPtr connection
)
1426 HTTPServerPtr server
= connection
->server
;
1427 HTTPRequestPtr request
= connection
->request
;
1433 "Restarting pipeline to %s:%d.\n",
1434 scrub(connection
->server
->name
), connection
->server
->port
);
1437 req
->connection
= NULL
;
1441 connection
->request_last
->next
= server
->request
;
1443 server
->request_last
= connection
->request_last
;
1444 server
->request
= request
;
1445 connection
->request
= NULL
;
1446 connection
->request_last
= NULL
;
1448 connection
->pipelined
= 0;
1449 httpServerFinish(connection
, 1, 0);
1453 httpServerRequest(ObjectPtr object
, int method
, int from
, int to
,
1454 HTTPRequestPtr requestor
, void *closure
)
1461 assert(from
>= 0 && (to
< 0 || to
> from
));
1462 assert(closure
== NULL
);
1463 assert(!(object
->flags
& OBJECT_LOCAL
));
1464 assert(object
->type
== OBJECT_HTTP
);
1466 if(object
->flags
& OBJECT_INPROGRESS
)
1469 if(requestor
->flags
& REQUEST_REQUESTED
)
1472 assert(requestor
->request
== NULL
);
1477 rc
= parseUrl(object
->key
, object
->key_size
, &x
, &y
, &port
, &z
);
1479 if(rc
< 0 || x
< 0 || y
< 0 || y
- x
> 131) {
1480 do_log(L_ERROR
, "Couldn't parse URL %s\n", scrub(object
->key
));
1481 abortObject(object
, 400, internAtom("Couldn't parse URL"));
1482 notifyObject(object
);
1486 if(!intListMember(port
, allowedPorts
)) {
1487 do_log(L_ERROR
, "Attempted connection to port %d.\n", port
);
1488 abortObject(object
, 403, internAtom("Forbidden port"));
1489 notifyObject(object
);
1493 memcpy(name
, ((char*)object
->key
) + x
, y
- x
);
1496 requestor
->flags
|= REQUEST_REQUESTED
;
1497 rc
= httpMakeServerRequest(name
, port
, object
, method
, from
, to
,
1502 503, internAtom("Couldn't schedule server request"));
1503 notifyObject(object
);
1511 httpWriteRequest(HTTPConnectionPtr connection
, HTTPRequestPtr request
,
1514 ObjectPtr object
= request
->object
;
1515 int from
= request
->from
, to
= request
->to
, method
= request
->method
;
1516 char *url
= object
->key
, *m
;
1517 int url_size
= object
->key_size
;
1518 int x
, y
, port
, z
, location_size
;
1520 int l
, n
, rc
, bufsize
;
1522 assert(method
!= METHOD_NONE
);
1524 if(request
->method
== METHOD_GET
||
1525 request
->method
== METHOD_CONDITIONAL_GET
) {
1529 do_log(L_ERROR
, "Requesting empty segment?\n");
1534 if(object
->flags
& OBJECT_DYNAMIC
) {
1538 objectFillFromDisk(object
, from
/ CHUNK_SIZE
* CHUNK_SIZE
, 1);
1539 l
= objectHoleSize(request
->object
, from
);
1541 if(to
<= 0 || to
> from
+ l
)
1545 if(pmmSize
&& connection
->server
->pipeline
>= 4) {
1547 to
= to
< 0 ? pmmFirstSize
: MIN(to
, pmmFirstSize
);
1549 to
= to
< 0 ? from
+ pmmSize
: MIN(to
, from
+ pmmSize
);
1552 if(from
% CHUNK_SIZE
!= 0)
1553 if(objectHoleSize(object
, from
- 1) != 0)
1554 from
= from
/ CHUNK_SIZE
* CHUNK_SIZE
;
1558 rc
= parseUrl(url
, url_size
, &x
, &y
, &port
, &z
);
1560 if(rc
< 0 || x
< 0 || y
< 0) {
1564 if(connection
->reqbuf
== NULL
) {
1565 connection
->reqbuf
= get_chunk();
1566 if(connection
->reqbuf
== NULL
)
1568 connection
->reqlen
= 0;
1571 if(method
== METHOD_CONDITIONAL_GET
&&
1572 object
->last_modified
< 0 && object
->etag
== NULL
)
1573 method
= request
->method
= METHOD_GET
;
1577 (connection
->flags
& CONN_BIGREQBUF
) ? bigBufferSize
: CHUNK_SIZE
;
1578 n
= connection
->reqlen
;
1581 case METHOD_CONDITIONAL_GET
: m
= "GET"; break;
1582 case METHOD_HEAD
: m
= "HEAD"; break;
1583 case METHOD_POST
: m
= "POST"; break;
1584 case METHOD_PUT
: m
= "PUT"; break;
1587 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, "%s ", m
);
1589 if(connection
->server
->isProxy
) {
1590 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
,
1593 if(url_size
- z
== 0) {
1598 location_size
= url_size
- z
;
1601 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
,
1602 location
, location_size
);
1605 do_log(D_SERVER_REQ
, "Server request: ");
1606 do_log_n(D_SERVER_REQ
, url
+ x
, y
- x
);
1607 do_log(D_SERVER_REQ
, ": ");
1608 do_log_n(D_SERVER_REQ
, connection
->reqbuf
, n
);
1609 do_log(D_SERVER_REQ
, " (method %d from %d to %d, 0x%lx for 0x%lx)\n",
1611 (unsigned long)connection
, (unsigned long)object
);
1613 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, " HTTP/1.1");
1615 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, "\r\nHost: ");
1616 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
, url
+ x
, y
- x
);
1618 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, ":%d", port
);
1620 if(connection
->server
->isProxy
&& parentAuthCredentials
) {
1621 n
= buildServerAuthHeaders(connection
->reqbuf
, n
, bufsize
,
1622 parentAuthCredentials
);
1626 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1627 "\r\nContent-Length: %d", bodylen
);
1629 if(request
->flags
& REQUEST_WAIT_CONTINUE
)
1630 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1631 "\r\nExpect: 100-continue");
1633 if(method
!= METHOD_HEAD
&& (from
> 0 || to
>= 0)) {
1635 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1636 "\r\nRange: bytes=%d-%d", from
, to
- 1);
1638 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1639 "\r\nRange: bytes=%d-", from
);
1643 if(method
== METHOD_GET
&& object
->etag
&& (from
> 0 || to
>= 0)) {
1644 if(request
->request
&& request
->request
->request
== request
&&
1645 request
->request
->from
== 0 && request
->request
->to
== -1 &&
1646 pmmSize
== 0 && pmmFirstSize
== 0)
1647 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1648 "\r\nIf-Range: \"%s\"", object
->etag
);
1651 if(method
== METHOD_CONDITIONAL_GET
) {
1652 if(object
->last_modified
>= 0) {
1653 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1654 "\r\nIf-Modified-Since: ");
1655 n
= format_time(connection
->reqbuf
, n
, bufsize
,
1656 object
->last_modified
);
1659 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1660 "\r\nIf-None-Match: \"%s\"", object
->etag
);
1664 n
= httpPrintCacheControl(connection
->reqbuf
, n
, bufsize
,
1665 0, &request
->cache_control
);
1669 if(request
->request
&& request
->request
->headers
) {
1670 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
,
1671 request
->request
->headers
->string
,
1672 request
->request
->headers
->length
);
1675 if(request
->request
&& request
->request
->via
) {
1676 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1677 "\r\nVia: %s, 1.1 %s",
1678 request
->request
->via
->string
, proxyName
->string
);
1680 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1686 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1687 "\r\nConnection: %s\r\n\r\n",
1688 (request
->flags
& REQUEST_PERSISTENT
) ?
1689 "keep-alive" : "close");
1690 if(n
< 0 || n
>= bufsize
- 1)
1692 connection
->reqlen
= n
;
1697 if(!(connection
->flags
& CONN_BIGREQBUF
))
1698 rc
= httpConnectionBigifyReqbuf(connection
);
1705 httpServerHandler(int status
,
1706 FdEventHandlerPtr event
,
1707 StreamRequestPtr srequest
)
1709 HTTPConnectionPtr connection
= srequest
->data
;
1712 assert(connection
->request
->object
->flags
& OBJECT_INPROGRESS
);
1714 if(connection
->reqlen
== 0) {
1715 do_log(D_SERVER_REQ
, "Writing aborted on 0x%lx\n",
1716 (unsigned long)connection
);
1720 if(status
== 0 && !streamRequestDone(srequest
)) {
1721 httpSetTimeout(connection
, serverTimeout
);
1725 httpConnectionDestroyReqbuf(connection
);
1728 if(connection
->serviced
>= 1) {
1729 httpServerRestart(connection
);
1732 if(status
>= 0 || status
== ECONNRESET
) {
1733 message
= internAtom("Couldn't send request to server: "
1736 if(status
!= -EPIPE
)
1737 do_log_error(L_ERROR
, -status
,
1738 "Couldn't send request to server");
1740 internAtomError(-status
, "Couldn't send request to server");
1748 httpConnectionDestroyReqbuf(connection
);
1749 shutdown(connection
->fd
, 2);
1750 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLIN
);
1751 httpSetTimeout(connection
, 60);
1756 httpServerSendRequest(HTTPConnectionPtr connection
)
1758 assert(connection
->server
);
1760 if(connection
->reqlen
== 0) {
1761 do_log(D_SERVER_REQ
,
1762 "Writing aborted on 0x%lx\n", (unsigned long)connection
);
1763 httpConnectionDestroyReqbuf(connection
);
1764 shutdown(connection
->fd
, 2);
1765 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLIN
| POLLOUT
);
1769 httpSetTimeout(connection
, serverTimeout
);
1770 do_stream(IO_WRITE
, connection
->fd
, 0,
1771 connection
->reqbuf
, connection
->reqlen
,
1772 httpServerHandler
, connection
);
1777 httpServerReplyHandler(int status
,
1778 FdEventHandlerPtr event
,
1779 StreamRequestPtr srequest
)
1781 HTTPConnectionPtr connection
= srequest
->data
;
1782 HTTPRequestPtr request
= connection
->request
;
1785 (connection
->flags
& CONN_BIGBUF
) ? bigBufferSize
: CHUNK_SIZE
;
1787 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1789 if(connection
->serviced
>= 1) {
1790 httpServerRestart(connection
);
1793 if(status
!= -ECLIENTRESET
)
1794 do_log_error(L_ERROR
, -status
, "Read from server failed");
1795 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
1796 internAtomError(-status
, "Read from server failed"));
1800 i
= findEndOfHeaders(connection
->buf
, 0, srequest
->offset
, &body
);
1801 connection
->len
= srequest
->offset
;
1804 request
->time1
= current_time
;
1805 return httpServerHandlerHeaders(status
, event
, srequest
, connection
);
1809 if(connection
->serviced
>= 1) {
1810 httpServerRestart(connection
);
1815 "Error reading server headers: %d\n", -status
);
1816 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
1817 internAtomError(-status
,
1818 "Error reading server headers"));
1820 httpServerAbort(connection
, 1, 502,
1821 internAtom("Server dropped connection"));
1825 if(connection
->len
>= bufsize
) {
1827 if(!(connection
->flags
& CONN_BIGBUF
))
1828 rc
= httpConnectionBigify(connection
);
1830 do_log(L_ERROR
, "Couldn't find end of server's headers.\n");
1831 httpServerAbort(connection
, 1, 502,
1832 internAtom("Couldn't find end "
1833 "of server's headers"));
1836 do_log(L_ERROR
, "Couldn't allocate big buffer.\n");
1837 httpServerAbort(connection
, 1, 500,
1838 internAtom("Couldn't allocate big buffer"));
1841 /* Can't just return 0 -- buf has moved. */
1843 connection
->fd
, connection
->len
,
1844 connection
->buf
, bigBufferSize
,
1845 httpServerReplyHandler
, connection
);
1853 httpServerHandlerHeaders(int eof
,
1854 FdEventHandlerPtr event
,
1855 StreamRequestPtr srequest
,
1856 HTTPConnectionPtr connection
)
1858 HTTPRequestPtr request
= connection
->request
;
1859 ObjectPtr object
= request
->object
;
1866 CacheControlRec cache_control
;
1868 time_t date
, last_modified
, expires
;
1869 struct timeval
*init_time
;
1871 AtomPtr via
, new_via
;
1873 HTTPRangeRec content_range
;
1874 ObjectPtr new_object
= NULL
, old_object
= NULL
;
1876 AtomPtr message
= NULL
;
1880 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1883 httpSetTimeout(connection
, -1);
1885 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
1886 do_log(D_SERVER_CONN
, "W %s:%d.\n",
1887 connection
->server
->name
, connection
->server
->port
);
1888 request
->flags
&= ~REQUEST_WAIT_CONTINUE
;
1891 rc
= httpParseServerFirstLine(connection
->buf
, &code
, &version
, &message
);
1893 do_log(L_ERROR
, "Couldn't parse server status line.\n");
1894 httpServerAbort(connection
, 1, 502,
1895 internAtom("Couldn't parse server status line"));
1899 do_log(D_SERVER_REQ
, "Server status: ");
1900 do_log_n(D_SERVER_REQ
, connection
->buf
,
1901 connection
->buf
[rc
- 1] == '\r' ? rc
- 2 : rc
- 2);
1902 do_log(D_SERVER_REQ
, " (0x%lx for 0x%lx)\n",
1903 (unsigned long)connection
, (unsigned long)object
);
1905 if(version
!= HTTP_10
&& version
!= HTTP_11
) {
1906 do_log(L_ERROR
, "Unknown server HTTP version\n");
1907 httpServerAbort(connection
, 1, 502,
1908 internAtom("Unknown server HTTP version"));
1909 releaseAtom(message
);
1913 connection
->version
= version
;
1914 connection
->server
->version
= version
;
1915 request
->flags
|= REQUEST_PERSISTENT
;
1917 url
= internAtomN(object
->key
, object
->key_size
);
1918 rc
= httpParseHeaders(0, url
, connection
->buf
, rc
, request
,
1919 &headers
, &len
, &cache_control
, NULL
, &te
,
1920 &date
, &last_modified
, &expires
, NULL
, NULL
, NULL
,
1921 &age
, &etag
, NULL
, NULL
, &content_range
,
1924 do_log(L_ERROR
, "Couldn't parse server headers\n");
1926 releaseAtom(message
);
1927 httpServerAbort(connection
, 1, 502,
1928 internAtom("Couldn't parse server headers"));
1933 date
= current_time
.tv_sec
;
1937 releaseAtom(message
);
1938 /* We've already reset wait_continue above, but we must still
1939 ensure that the writer notices. */
1940 notifyObject(request
->object
);
1941 connection
->len
-= rc
;
1942 if(connection
->len
> 0)
1943 memmove(connection
->buf
, connection
->buf
+ rc
, connection
->len
);
1944 httpServerReply(connection
, 1);
1949 httpServerAbort(connection
, 1, 501,
1950 internAtom("Upgrade not implemented"));
1954 if(via
&& !checkVia(proxyName
, via
)) {
1955 httpServerAbort(connection
, 1, 504, internAtom("Proxy loop detected"));
1958 full_len
= content_range
.full_length
;
1961 if(content_range
.from
== -1 || content_range
.to
== -1) {
1962 do_log(L_ERROR
, "Partial content without range.\n");
1963 httpServerAbort(connection
, 1, 502,
1964 internAtom("Partial content without range"));
1967 if(len
>= 0 && len
!= content_range
.to
- content_range
.from
) {
1968 do_log(L_ERROR
, "Inconsistent partial content.\n");
1969 httpServerAbort(connection
, 1, 502,
1970 internAtom("Inconsistent partial content"));
1973 } else if(code
< 400 &&
1974 (content_range
.from
>= 0 || content_range
.to
>= 0 ||
1975 content_range
.full_length
>= 0)) {
1976 do_log(L_WARN
, "Range without partial content.\n");
1978 content_range
.from
= -1;
1979 content_range
.to
= -1;
1980 content_range
.full_length
= -1;
1981 } else if(code
!= 304 && code
!= 412) {
1985 if(te
!= TE_IDENTITY
&& te
!= TE_CHUNKED
) {
1986 do_log(L_ERROR
, "Unsupported transfer-encoding\n");
1987 httpServerAbort(connection
, 1, 502,
1988 internAtom("Unsupported transfer-encoding"));
1993 if(request
->method
!= METHOD_CONDITIONAL_GET
) {
1994 do_log(L_ERROR
, "Unexpected \"not changed\" reply from server\n");
1995 httpServerAbort(connection
, 1, 502,
1996 internAtom("Unexpected \"not changed\" "
1997 "reply from server"));
2000 if(object
->etag
&& !etag
) {
2001 /* RFC 2616 10.3.5. Violated by some front-end proxies. */
2002 do_log(L_WARN
, "\"Not changed\" reply with no ETag.\n");
2007 if(request
->method
!= METHOD_CONDITIONAL_GET
||
2008 (!object
->etag
&& !object
->last_modified
)) {
2010 "Unexpected \"precondition failed\" reply from server.\n");
2011 httpServerAbort(connection
, 1, 502,
2012 internAtom("Unexpected \"precondition failed\" "
2013 "reply from server"));
2020 /* Okay, we're going to accept this reply. */
2022 if((code
== 200 || code
== 206 || code
== 304 || code
== 412) &&
2023 (cache_control
.flags
& (CACHE_NO
| CACHE_NO_STORE
) ||
2024 cache_control
.max_age
== 0 ||
2025 (cacheIsShared
&& cache_control
.s_maxage
== 0) ||
2026 (expires
>= 0 && expires
<= object
->age
))) {
2027 do_log(L_UNCACHEABLE
, "Uncacheable object %s (%d)\n",
2028 scrub(object
->key
), cache_control
.flags
);
2031 if(request
->time0
.tv_sec
!= null_time
.tv_sec
)
2032 init_time
= &request
->time0
;
2034 init_time
= ¤t_time
;
2035 age
= MIN(init_time
->tv_sec
- age
, init_time
->tv_sec
);
2037 if(request
->method
== METHOD_HEAD
||
2038 code
< 200 || code
== 204 || code
== 304)
2040 else if(te
== TE_IDENTITY
)
2041 expect_body
= (len
!= 0);
2045 connection
->chunk_remaining
= -1;
2046 connection
->te
= te
;
2048 old_object
= object
;
2050 connection
->server
->lies
--;
2052 if(object
->cache_control
& CACHE_MISMATCH
)
2055 if(code
== 304 || code
== 412) {
2056 if((object
->etag
&& etag
&& strcmp(object
->etag
, etag
) != 0) ||
2057 (object
->last_modified
>= 0 && last_modified
>= 0 &&
2058 object
->last_modified
!= last_modified
)) {
2059 do_log(L_ERROR
, "Inconsistent \"%s\" reply for %s\n",
2060 code
== 304 ? "not changed":"precondition failed",
2061 scrub(object
->key
));
2062 object
->flags
|= OBJECT_DYNAMIC
;
2065 } else if(!(object
->flags
& OBJECT_INITIAL
)) {
2066 if((object
->last_modified
< 0 || last_modified
< 0) &&
2067 (!object
->etag
|| !etag
))
2069 else if(object
->last_modified
!= last_modified
)
2071 else if(object
->etag
|| etag
) {
2072 /* We need to be permissive here so as to deal with some
2073 front-end proxies that discard ETags on partial
2074 replies but not on full replies. */
2075 if(etag
&& object
->etag
&& strcmp(object
->etag
, etag
) != 0)
2077 else if(!object
->etag
)
2081 if(!supersede
&& (object
->cache_control
& CACHE_VARY
) &&
2082 dontTrustVaryETag
>= 1) {
2083 /* Check content-type to work around mod_gzip bugs */
2084 if(!httpHeaderMatch(atomContentType
, object
->headers
, headers
) ||
2085 !httpHeaderMatch(atomContentEncoding
, object
->headers
, headers
))
2089 if(full_len
< 0 && te
== TE_IDENTITY
) {
2090 /* It's an HTTP/1.0 CGI. Be afraid. */
2091 if(expect_body
&& content_range
.from
< 0 && content_range
.to
< 0)
2095 if(!supersede
&& object
->length
>= 0 && full_len
>= 0 &&
2096 object
->length
!= full_len
) {
2097 do_log(L_WARN
, "Inconsistent length.\n");
2102 ((object
->last_modified
>= 0 && last_modified
>= 0) ||
2103 (object
->etag
&& etag
))) {
2104 if(request
->method
== METHOD_CONDITIONAL_GET
) {
2105 do_log(L_WARN
, "Server ignored conditional request.\n");
2106 connection
->server
->lies
+= 10;
2107 /* Drop the connection? */
2110 } else if(code
== 416) {
2111 do_log(L_ERROR
, "Unexpected \"range not satisfiable\" reply\n");
2112 httpServerAbort(connection
, 1, 502,
2113 internAtom("Unexpected \"range not satisfiable\" "
2115 /* The object may be superseded. Make sure the next request
2116 won't be partial. */
2117 abortObject(object
, 502,
2118 internAtom("Unexpected \"range not satisfiable\" reply"));
2122 if(object
->flags
& OBJECT_INITIAL
)
2126 do_log(L_SUPERSEDED
,
2127 "Superseding object %s (%d %d %d %s -> %d %d %d %s)\n",
2128 scrub(old_object
->key
),
2129 object
->code
, object
->length
, (int)object
->last_modified
,
2130 object
->etag
? object
->etag
: "(none)",
2131 code
, full_len
, (int)last_modified
,
2132 etag
? etag
: "(none)");
2133 privatiseObject(old_object
, 0);
2134 new_object
= makeObject(object
->type
, object
->key
,
2135 object
->key_size
, 1, 0,
2136 object
->request
, NULL
);
2137 if(new_object
== NULL
) {
2138 do_log(L_ERROR
, "Couldn't allocate object\n");
2139 httpServerAbort(connection
, 1, 500,
2140 internAtom("Couldn't allocate object"));
2143 if(urlIsLocal(new_object
->key
, new_object
->key_size
))
2144 new_object
->flags
|= OBJECT_LOCAL
;
2146 new_object
= object
;
2150 (!etag
&& last_modified
< 0) ||
2151 (cache_control
.flags
&
2152 (CACHE_NO_HIDDEN
| CACHE_NO
| CACHE_NO_STORE
|
2153 (cacheIsShared
? CACHE_PRIVATE
: 0))) ||
2154 (cache_control
.max_age
>= 0 && cache_control
.max_age
<= 2) ||
2156 cache_control
.s_maxage
>= 0 && cache_control
.s_maxage
<= 5) ||
2157 (old_object
->last_modified
>= 0 && old_object
->expires
>= 0 &&
2158 (old_object
->expires
- old_object
->last_modified
<= 1)) ||
2159 (supersede
&& (old_object
->date
- date
<= 5));
2162 new_object
->flags
|= OBJECT_DYNAMIC
;
2164 new_object
->flags
&= ~OBJECT_DYNAMIC
;
2165 else if(old_object
->flags
& OBJECT_DYNAMIC
)
2166 new_object
->flags
|= OBJECT_DYNAMIC
;
2168 new_object
->age
= age
;
2169 new_object
->cache_control
|= cache_control
.flags
;
2170 new_object
->max_age
= cache_control
.max_age
;
2171 new_object
->s_maxage
= cache_control
.s_maxage
;
2172 new_object
->flags
&= ~OBJECT_FAILED
;
2175 new_object
->date
= date
;
2176 if(last_modified
>= 0)
2177 new_object
->last_modified
= last_modified
;
2179 new_object
->expires
= expires
;
2180 if(new_object
->etag
== NULL
)
2181 new_object
->etag
= etag
;
2187 case 300: case 301: case 302: case 303: case 307:
2188 case 403: case 404: case 405: case 401:
2189 if(new_object
->message
) releaseAtom(new_object
->message
);
2190 new_object
->code
= code
;
2191 new_object
->message
= message
;
2193 case 206: case 304: case 412:
2194 if(new_object
->code
!= 200 || !new_object
->message
) {
2195 if(new_object
->message
) releaseAtom(new_object
->message
);
2196 new_object
->code
= 200;
2197 new_object
->message
= internAtom("OK");
2199 releaseAtom(message
);
2202 if(new_object
->message
) releaseAtom(new_object
->message
);
2203 new_object
->code
= code
;
2204 new_object
->message
= retainAtom(message
);
2208 httpTweakCachability(new_object
);
2211 new_via
= internAtomF("%s %s",
2212 version
== HTTP_11
? "1.1" : "1.0",
2215 new_via
= internAtomF("%s, %s %s", via
->string
,
2216 version
== HTTP_11
? "1.1" : "1.0",
2218 if(new_via
== NULL
) {
2219 do_log(L_ERROR
, "Couldn't allocate Via.\n");
2221 if(new_object
->via
) releaseAtom(new_object
->via
);
2222 new_object
->via
= new_via
;
2225 if(new_object
->flags
& OBJECT_INITIAL
) {
2226 objectPartial(new_object
, full_len
, headers
);
2228 if(new_object
->length
< 0)
2229 new_object
->length
= full_len
;
2230 /* XXX -- RFC 2616 13.5.3 */
2231 releaseAtom(headers
);
2235 assert(new_object
!= old_object
);
2236 supersedeObject(old_object
);
2239 if(new_object
!= old_object
) {
2240 if(new_object
->flags
& OBJECT_INPROGRESS
) {
2241 /* Make sure we don't fetch this object two times at the
2242 same time. Just drop the connection. */
2243 releaseObject(new_object
);
2244 httpServerFinish(connection
, 1, 0);
2247 old_object
->flags
&= ~OBJECT_VALIDATING
;
2248 new_object
->flags
|= OBJECT_INPROGRESS
;
2249 /* Signal the client side to switch to the new object -- see
2250 httpClientGetHandler. If it doesn't, we'll give up on this
2252 new_object
->flags
|= OBJECT_MUTATING
;
2253 request
->can_mutate
= new_object
;
2254 notifyObject(old_object
);
2255 request
->can_mutate
= NULL
;
2256 new_object
->flags
&= ~OBJECT_MUTATING
;
2257 old_object
->flags
&= ~OBJECT_INPROGRESS
;
2258 if(request
->object
== old_object
) {
2259 if(request
->request
)
2260 request
->request
->request
= NULL
;
2261 request
->request
= NULL
;
2262 request
->object
= new_object
;
2264 assert(request
->object
== new_object
);
2266 releaseNotifyObject(old_object
);
2268 object
= new_object
;
2270 objectMetadataChanged(new_object
, 0);
2273 if(object
->flags
& OBJECT_VALIDATING
) {
2274 object
->flags
&= ~OBJECT_VALIDATING
;
2275 notifyObject(object
);
2279 httpServerFinish(connection
, 0, rc
);
2283 if(request
->request
== NULL
) {
2284 httpServerFinish(connection
, 1, 0);
2289 /* 412 replies contain a useless body. For now, we
2290 drop the connection. */
2291 httpServerFinish(connection
, 1, 0);
2296 if(request
->flags
& REQUEST_PERSISTENT
) {
2297 if(request
->method
!= METHOD_HEAD
&&
2298 connection
->te
== TE_IDENTITY
&& len
< 0) {
2299 do_log(L_ERROR
, "Persistent reply with no Content-Length\n");
2300 /* That's potentially dangerous, as we could start reading
2301 arbitrary data into the object. Unfortunately, some
2303 request
->flags
&= ~REQUEST_PERSISTENT
;
2307 /* we're getting a body */
2308 if(content_range
.from
> 0)
2309 connection
->offset
= content_range
.from
;
2311 connection
->offset
= 0;
2313 if(content_range
.to
>= 0)
2314 request
->to
= content_range
.to
;
2316 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2317 (unsigned long)connection
, (unsigned long)object
,
2318 connection
->offset
);
2320 if(connection
->len
> rc
) {
2321 rc
= connectionAddData(connection
, rc
);
2325 do_log(L_ERROR
, "Couldn't parse chunk size.\n");
2326 httpServerAbort(connection
, 1, 502,
2327 internAtom("Couldn't parse chunk size"));
2329 do_log(L_ERROR
, "Couldn't add data to connection.\n");
2330 httpServerAbort(connection
, 1, 500,
2331 internAtom("Couldn't add data "
2337 if(object
->length
< 0) {
2338 object
->length
= object
->size
;
2339 objectMetadataChanged(object
, 0);
2340 } else if(object
->length
!= object
->size
) {
2341 httpServerAbort(connection
, 1, 500,
2342 internAtom("Inconsistent "
2344 object
->length
= -1;
2348 httpServerFinish(connection
, 0, 0);
2353 connection
->len
= 0;
2357 if(connection
->te
== TE_CHUNKED
||
2358 (object
->length
>= 0 &&
2359 connection
->offset
< object
->length
)) {
2360 do_log(L_ERROR
, "Server closed connection.\n");
2361 httpServerAbort(connection
, 1, 502,
2362 internAtom("Server closed connection"));
2365 if(code
!= 206 && eof
> 0 && object
->length
< 0) {
2366 object
->length
= object
->size
;
2367 objectMetadataChanged(object
, 0);
2369 httpServerFinish(connection
, 1, 0);
2373 return httpServerReadData(connection
, 1);
2379 releaseAtom(message
);
2381 releaseAtom(headers
);
2390 httpServerIndirectHandlerCommon(HTTPConnectionPtr connection
, int eof
)
2392 HTTPRequestPtr request
= connection
->request
;
2395 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
2397 if(connection
->len
> 0) {
2399 rc
= connectionAddData(connection
, 0);
2403 do_log(L_ERROR
, "Couldn't parse chunk size.\n");
2404 httpServerAbort(connection
, 1, 502,
2405 internAtom("Couldn't parse chunk size"));
2407 do_log(L_ERROR
, "Couldn't add data to connection.\n");
2408 httpServerAbort(connection
, 1, 500,
2409 internAtom("Couldn't add data "
2414 if(request
->to
< 0) {
2415 if(request
->object
->length
< 0) {
2416 request
->object
->length
= request
->object
->size
;
2417 objectMetadataChanged(request
->object
, 0);
2418 } else if(request
->object
->length
!=
2419 request
->object
->size
) {
2420 request
->object
->length
= -1;
2421 httpServerAbort(connection
, 1, 502,
2422 internAtom("Inconsistent "
2427 httpServerFinish(connection
, 0, 0);
2433 if(eof
&& connection
->len
== 0) {
2434 if(connection
->te
== TE_CHUNKED
||
2435 (request
->to
>= 0 && connection
->offset
< request
->to
)) {
2436 do_log(L_ERROR
, "Server dropped connection.\n");
2437 httpServerAbort(connection
, 1, 502,
2438 internAtom("Server dropped connection"));
2441 if(request
->object
->length
< 0 && eof
> 0 &&
2442 (request
->to
< 0 || request
->to
> request
->object
->size
)) {
2443 request
->object
->length
= request
->object
->size
;
2444 objectMetadataChanged(request
->object
, 0);
2446 httpServerFinish(connection
, 1, 0);
2450 return httpServerReadData(connection
, 0);
2455 httpServerIndirectHandler(int status
,
2456 FdEventHandlerPtr event
,
2457 StreamRequestPtr srequest
)
2459 HTTPConnectionPtr connection
= srequest
->data
;
2460 assert(connection
->request
->object
->flags
& OBJECT_INPROGRESS
);
2462 httpSetTimeout(connection
, -1);
2464 if(status
!= -ECLIENTRESET
)
2465 do_log_error(L_ERROR
, -status
, "Read from server failed");
2466 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
2467 internAtomError(-status
, "Read from server failed"));
2471 connection
->len
= srequest
->offset
;
2473 return httpServerIndirectHandlerCommon(connection
, status
);
2477 httpServerReadData(HTTPConnectionPtr connection
, int immediate
)
2479 HTTPRequestPtr request
= connection
->request
;
2480 ObjectPtr object
= request
->object
;
2483 assert(object
->flags
& OBJECT_INPROGRESS
);
2485 if(request
->request
== NULL
) {
2486 httpServerFinish(connection
, 1, 0);
2490 if(request
->to
>= 0)
2493 to
= object
->length
;
2495 if(to
>= 0 && to
== connection
->offset
) {
2496 httpServerFinish(connection
, 0, 0);
2500 if(connection
->len
== 0 &&
2501 ((connection
->te
== TE_IDENTITY
&& to
> connection
->offset
) ||
2502 (connection
->te
== TE_CHUNKED
&& connection
->chunk_remaining
> 0))) {
2503 /* Read directly into the object */
2504 int i
= connection
->offset
/ CHUNK_SIZE
;
2505 int j
= connection
->offset
% CHUNK_SIZE
;
2507 /* See httpServerDirectHandlerCommon if you change this */
2508 if(connection
->te
== TE_CHUNKED
) {
2509 len
= connection
->chunk_remaining
;
2510 /* The logic here is that we want more to just fit the
2511 chunk header if we're doing a large read, but do a
2512 large read if we would otherwise do a small one. The
2513 magic constant 2000 comes from the assumption that the
2514 server uses chunks that have a size that are a power of
2515 two (possibly including the chunk header), and that we
2516 want a full ethernet packet to fit into our read. */
2517 more
= (len
>= 2000 ? 20 : MIN(2048 - len
, CHUNK_SIZE
));
2519 len
= to
- connection
->offset
;
2520 /* We read more data only when there is a reasonable
2521 chance of there being another reply coming. */
2522 more
= (connection
->pipelined
> 1) ? CHUNK_SIZE
: 0;
2524 end
= len
+ connection
->offset
;
2526 httpConnectionDestroyBuf(connection
);
2528 /* The order of allocation is important in case we run out of
2530 lockChunk(object
, i
);
2531 if(object
->chunks
[i
].data
== NULL
)
2532 object
->chunks
[i
].data
= get_chunk();
2533 if(object
->chunks
[i
].data
&& object
->chunks
[i
].size
>= j
) {
2534 if(len
+ j
> CHUNK_SIZE
) {
2535 lockChunk(object
, i
+ 1);
2536 if(object
->chunks
[i
+ 1].data
== NULL
)
2537 object
->chunks
[i
+ 1].data
= get_chunk();
2538 /* Unless we're grabbing all len of data, we do not
2539 want to do an indirect read immediately afterwards. */
2540 if(more
&& len
+ j
<= 2 * CHUNK_SIZE
) {
2541 if(!connection
->buf
)
2542 connection
->buf
= get_chunk(); /* checked below */
2544 if(object
->chunks
[i
+ 1].data
) {
2545 do_stream_3(IO_READ
| IO_NOTNOW
, connection
->fd
, j
,
2546 object
->chunks
[i
].data
, CHUNK_SIZE
,
2547 object
->chunks
[i
+ 1].data
,
2549 end
- (i
+ 1) * CHUNK_SIZE
),
2550 connection
->buf
, connection
->buf
? more
: 0,
2551 httpServerDirectHandler2
, connection
);
2554 unlockChunk(object
, i
+ 1);
2556 if(more
&& len
+ j
<= CHUNK_SIZE
) {
2557 if(!connection
->buf
)
2558 connection
->buf
= get_chunk();
2560 do_stream_2(IO_READ
| IO_NOTNOW
, connection
->fd
, j
,
2561 object
->chunks
[i
].data
,
2562 MIN(CHUNK_SIZE
, end
- i
* CHUNK_SIZE
),
2563 connection
->buf
, connection
->buf
? more
: 0,
2564 httpServerDirectHandler
, connection
);
2567 unlockChunk(object
, i
);
2571 if(connection
->len
== 0)
2572 httpConnectionDestroyBuf(connection
);
2574 httpSetTimeout(connection
, serverTimeout
);
2575 do_stream_buf(IO_READ
| IO_NOTNOW
|
2576 ((immediate
&& connection
->len
) ? IO_IMMEDIATE
: 0),
2577 connection
->fd
, connection
->len
,
2579 (connection
->te
== TE_CHUNKED
?
2580 MIN(2048, CHUNK_SIZE
) : CHUNK_SIZE
),
2581 httpServerIndirectHandler
, connection
);
2586 httpServerDirectHandlerCommon(int kind
, int status
,
2587 FdEventHandlerPtr event
,
2588 StreamRequestPtr srequest
)
2590 HTTPConnectionPtr connection
= srequest
->data
;
2591 HTTPRequestPtr request
= connection
->request
;
2592 ObjectPtr object
= request
->object
;
2593 int i
= connection
->offset
/ CHUNK_SIZE
;
2596 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
2598 httpSetTimeout(connection
, -1);
2601 unlockChunk(object
, i
);
2602 if(kind
== 2) unlockChunk(object
, i
+ 1);
2603 if(status
!= -ECLIENTRESET
)
2604 do_log_error(L_ERROR
, -status
, "Read from server failed");
2605 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
2606 internAtomError(-status
, "Read from server failed"));
2610 /* We have incestuous knowledge of the decisions made in
2611 httpServerReadData */
2612 if(request
->to
>= 0)
2615 to
= object
->length
;
2616 if(connection
->te
== TE_CHUNKED
)
2617 end
= connection
->offset
+ connection
->chunk_remaining
;
2620 /* The amount of data actually read into the object */
2621 end1
= MIN(end
, i
* CHUNK_SIZE
+ MIN(kind
* CHUNK_SIZE
, srequest
->offset
));
2623 assert(end
>= 0 && end1
>= i
* CHUNK_SIZE
&& end1
<= (i
+ 2) * CHUNK_SIZE
);
2625 object
->chunks
[i
].size
=
2626 MAX(object
->chunks
[i
].size
, MIN(end1
- i
* CHUNK_SIZE
, CHUNK_SIZE
));
2627 if(kind
== 2 && end1
> (i
+ 1) * CHUNK_SIZE
) {
2628 object
->chunks
[i
+ 1].size
=
2629 MAX(object
->chunks
[i
+ 1].size
, end1
- (i
+ 1) * CHUNK_SIZE
);
2631 if(connection
->te
== TE_CHUNKED
) {
2632 connection
->chunk_remaining
-= (end1
- connection
->offset
);
2633 assert(connection
->chunk_remaining
>= 0);
2635 connection
->offset
= end1
;
2636 object
->size
= MAX(object
->size
, end1
);
2637 unlockChunk(object
, i
);
2638 if(kind
== 2) unlockChunk(object
, i
+ 1);
2640 if(i
* CHUNK_SIZE
+ srequest
->offset
> end1
) {
2641 connection
->len
= i
* CHUNK_SIZE
+ srequest
->offset
- end1
;
2642 return httpServerIndirectHandlerCommon(connection
, status
);
2644 notifyObject(object
);
2646 httpServerFinish(connection
, 1, 0);
2649 return httpServerReadData(connection
, 0);
2655 httpServerDirectHandler(int status
,
2656 FdEventHandlerPtr event
,
2657 StreamRequestPtr srequest
)
2659 return httpServerDirectHandlerCommon(1, status
, event
, srequest
);
2663 httpServerDirectHandler2(int status
,
2664 FdEventHandlerPtr event
,
2665 StreamRequestPtr srequest
)
2667 return httpServerDirectHandlerCommon(2, status
, event
, srequest
);
2670 /* Add the data accumulated in connection->buf into the object in
2671 connection->request. Returns 0 in the normal case, 1 if the TE is
2672 self-terminating and we're done, -1 if there was a problem with
2673 objectAddData, -2 if there was a problem with the data. */
2675 connectionAddData(HTTPConnectionPtr connection
, int skip
)
2677 HTTPRequestPtr request
= connection
->request
;
2678 ObjectPtr object
= request
->object
;
2681 if(connection
->te
== TE_IDENTITY
) {
2684 len
= connection
->len
- skip
;
2685 if(object
->length
>= 0) {
2686 len
= MIN(object
->length
- connection
->offset
, len
);
2688 if(request
->to
>= 0)
2689 len
= MIN(request
->to
- connection
->offset
, len
);
2691 rc
= objectAddData(object
, connection
->buf
+ skip
,
2692 connection
->offset
, len
);
2695 connection
->offset
+= len
;
2696 connection
->len
-= (len
+ skip
);
2697 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2698 (unsigned long)connection
, (unsigned long)object
,
2699 connection
->offset
);
2702 if(connection
->len
> 0 && skip
+ len
> 0) {
2703 memmove(connection
->buf
,
2704 connection
->buf
+ skip
+ len
, connection
->len
);
2707 if((object
->length
>= 0 && object
->length
<= connection
->offset
) ||
2708 (request
->to
>= 0 && request
->to
<= connection
->offset
)) {
2709 notifyObject(object
);
2713 notifyObject(object
);
2716 } else if(connection
->te
== TE_CHUNKED
) {
2717 int i
= skip
, j
, size
;
2718 /* connection->chunk_remaining is 0 at the end of a chunk, -1
2719 after the CR/LF pair ending a chunk, and -2 after we've
2720 seen a chunk of length 0. */
2721 if(connection
->chunk_remaining
> -2) {
2723 if(connection
->chunk_remaining
<= 0) {
2724 if(connection
->chunk_remaining
== 0) {
2725 if(connection
->len
< i
+ 2)
2727 if(connection
->buf
[i
] != '\r' ||
2728 connection
->buf
[i
+ 1] != '\n')
2731 connection
->chunk_remaining
= -1;
2733 if(connection
->len
< i
+ 2)
2735 j
= parseChunkSize(connection
->buf
, i
,
2736 connection
->len
, &size
);
2744 connection
->chunk_remaining
= -2;
2747 connection
->chunk_remaining
= size
;
2750 /* connection->chunk_remaining > 0 */
2751 size
= MIN(connection
->chunk_remaining
,
2752 connection
->len
- i
);
2755 rc
= objectAddData(object
, connection
->buf
+ i
,
2756 connection
->offset
, size
);
2757 connection
->offset
+= size
;
2761 connection
->chunk_remaining
-= size
;
2762 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2763 (unsigned long)connection
,
2764 (unsigned long)object
,
2765 connection
->offset
);
2769 connection
->len
-= i
;
2770 if(connection
->len
> 0)
2771 memmove(connection
->buf
, connection
->buf
+ i
, connection
->len
);
2772 if(i
> 0 || connection
->chunk_remaining
== -2)
2773 notifyObject(object
);
2774 if(connection
->chunk_remaining
== -2)
2784 listServers(FILE *out
)
2786 HTTPServerPtr server
;
2789 fprintf(out
, "<!DOCTYPE HTML PUBLIC "
2790 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2791 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2793 "\r\n<title>Known servers</title>\n"
2795 "<h1>Known servers</h1>\n");
2797 alternatingHttpStyle(out
, "servers");
2798 fprintf(out
, "<table id=servers>\n");
2799 fprintf(out
, "<thead><tr><th>Server</th>"
2801 "<th>Persistent</th>"
2803 "<th>Connections</th>"
2808 fprintf(out
, "<tbody>\n");
2812 fprintf(out
, "<tr class=\"%s\">", entry
% 2 == 0 ? "even" : "odd");
2813 if(server
->port
== 80)
2814 fprintf(out
, "<td>%s</td>", server
->name
);
2816 fprintf(out
, "<td>%s:%d</td>", server
->name
, server
->port
);
2818 if(server
->version
== HTTP_11
)
2819 fprintf(out
, "<td>1.1</td>");
2820 else if(server
->version
== HTTP_10
)
2821 fprintf(out
, "<td>1.0</td>");
2823 fprintf(out
, "<td>unknown</td>");
2825 if(server
->persistent
< 0)
2826 fprintf(out
, "<td>no</td>");
2827 else if(server
->persistent
> 0)
2828 fprintf(out
, "<td>yes</td>");
2830 fprintf(out
, "<td>unknown</td>");
2832 if(server
->version
!= HTTP_11
|| server
->persistent
<= 0)
2833 fprintf(out
, "<td></td>");
2834 else if(server
->pipeline
< 0)
2835 fprintf(out
, "<td>no</td>");
2836 else if(server
->pipeline
>= 0 && server
->pipeline
<= 1)
2837 fprintf(out
, "<td>unknown</td>");
2838 else if(server
->pipeline
== 2 || server
->pipeline
== 3)
2839 fprintf(out
, "<td>probing</td>");
2841 fprintf(out
, "<td>yes</td>");
2844 for(i
= 0; i
< server
->maxslots
; i
++)
2845 if(server
->connection
[i
] && !server
->connection
[i
]->connecting
) {
2846 if(i
< server
->numslots
)
2852 fprintf(out
, "<td>%d/%d", n
, server
->numslots
);
2854 fprintf(out
, " + %d</td>", m
);
2856 fprintf(out
, "</td>");
2858 if(server
->lies
> 0)
2859 fprintf(out
, "<td>(%d lies)</td>", (server
->lies
+ 9) / 10);
2861 fprintf(out
, "<td></td>");
2864 fprintf(out
, "<td>%.3f</td>", (double)server
->rtt
/ 1000000.0);
2866 fprintf(out
, "<td></td>");
2867 if(server
->rate
> 0)
2868 fprintf(out
, "<td>%d</td>", server
->rate
);
2870 fprintf(out
, "<td></td>");
2872 fprintf(out
, "</tr>\n");
2873 server
= server
->next
;
2876 fprintf(out
, "</tbody>\n");
2877 fprintf(out
, "</table>\n");
2878 fprintf(out
, "<p><a href=\"/polipo/\">back</a></p>");
2879 fprintf(out
, "</body></html>\n");