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 dispose_chunk(connection
->reqbuf
);
1749 connection
->reqbuf
= NULL
;
1750 shutdown(connection
->fd
, 2);
1751 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLIN
);
1752 httpSetTimeout(connection
, 60);
1757 httpServerSendRequest(HTTPConnectionPtr connection
)
1759 assert(connection
->server
);
1761 if(connection
->reqlen
== 0) {
1762 do_log(D_SERVER_REQ
,
1763 "Writing aborted on 0x%lx\n", (unsigned long)connection
);
1764 httpConnectionDestroyReqbuf(connection
);
1765 shutdown(connection
->fd
, 2);
1766 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLIN
| POLLOUT
);
1770 httpSetTimeout(connection
, serverTimeout
);
1771 do_stream(IO_WRITE
, connection
->fd
, 0,
1772 connection
->reqbuf
, connection
->reqlen
,
1773 httpServerHandler
, connection
);
1778 httpServerReplyHandler(int status
,
1779 FdEventHandlerPtr event
,
1780 StreamRequestPtr srequest
)
1782 HTTPConnectionPtr connection
= srequest
->data
;
1783 HTTPRequestPtr request
= connection
->request
;
1786 (connection
->flags
& CONN_BIGBUF
) ? bigBufferSize
: CHUNK_SIZE
;
1788 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1790 if(connection
->serviced
>= 1) {
1791 httpServerRestart(connection
);
1794 if(status
!= -ECLIENTRESET
)
1795 do_log_error(L_ERROR
, -status
, "Read from server failed");
1796 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
1797 internAtomError(-status
, "Read from server failed"));
1801 i
= findEndOfHeaders(connection
->buf
, 0, srequest
->offset
, &body
);
1802 connection
->len
= srequest
->offset
;
1805 request
->time1
= current_time
;
1806 return httpServerHandlerHeaders(status
, event
, srequest
, connection
);
1810 if(connection
->serviced
>= 1) {
1811 httpServerRestart(connection
);
1816 "Error reading server headers: %d\n", -status
);
1817 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
1818 internAtomError(-status
,
1819 "Error reading server headers"));
1821 httpServerAbort(connection
, 1, 502,
1822 internAtom("Server dropped connection"));
1826 if(connection
->len
>= bufsize
) {
1828 if(!(connection
->flags
& CONN_BIGBUF
))
1829 rc
= httpConnectionBigify(connection
);
1831 do_log(L_ERROR
, "Couldn't find end of server's headers.\n");
1832 httpServerAbort(connection
, 1, 502,
1833 internAtom("Couldn't find end "
1834 "of server's headers"));
1837 do_log(L_ERROR
, "Couldn't allocate big buffer.\n");
1838 httpServerAbort(connection
, 1, 500,
1839 internAtom("Couldn't allocate big buffer"));
1842 /* Can't just return 0 -- buf has moved. */
1844 connection
->fd
, connection
->len
,
1845 connection
->buf
, bigBufferSize
,
1846 httpServerReplyHandler
, connection
);
1854 httpServerHandlerHeaders(int eof
,
1855 FdEventHandlerPtr event
,
1856 StreamRequestPtr srequest
,
1857 HTTPConnectionPtr connection
)
1859 HTTPRequestPtr request
= connection
->request
;
1860 ObjectPtr object
= request
->object
;
1867 CacheControlRec cache_control
;
1869 time_t date
, last_modified
, expires
;
1870 struct timeval
*init_time
;
1872 AtomPtr via
, new_via
;
1874 HTTPRangeRec content_range
;
1875 ObjectPtr new_object
= NULL
, old_object
= NULL
;
1877 AtomPtr message
= NULL
;
1881 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1884 httpSetTimeout(connection
, -1);
1886 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
1887 do_log(D_SERVER_CONN
, "W %s:%d.\n",
1888 connection
->server
->name
, connection
->server
->port
);
1889 request
->flags
&= ~REQUEST_WAIT_CONTINUE
;
1892 rc
= httpParseServerFirstLine(connection
->buf
, &code
, &version
, &message
);
1894 do_log(L_ERROR
, "Couldn't parse server status line.\n");
1895 httpServerAbort(connection
, 1, 502,
1896 internAtom("Couldn't parse server status line"));
1900 do_log(D_SERVER_REQ
, "Server status: ");
1901 do_log_n(D_SERVER_REQ
, connection
->buf
,
1902 connection
->buf
[rc
- 1] == '\r' ? rc
- 2 : rc
- 2);
1903 do_log(D_SERVER_REQ
, " (0x%lx for 0x%lx)\n",
1904 (unsigned long)connection
, (unsigned long)object
);
1906 if(version
!= HTTP_10
&& version
!= HTTP_11
) {
1907 do_log(L_ERROR
, "Unknown server HTTP version\n");
1908 httpServerAbort(connection
, 1, 502,
1909 internAtom("Unknown server HTTP version"));
1910 releaseAtom(message
);
1914 connection
->version
= version
;
1915 connection
->server
->version
= version
;
1916 request
->flags
|= REQUEST_PERSISTENT
;
1918 url
= internAtomN(object
->key
, object
->key_size
);
1919 rc
= httpParseHeaders(0, url
, connection
->buf
, rc
, request
,
1920 &headers
, &len
, &cache_control
, NULL
, &te
,
1921 &date
, &last_modified
, &expires
, NULL
, NULL
, NULL
,
1922 &age
, &etag
, NULL
, NULL
, &content_range
,
1925 do_log(L_ERROR
, "Couldn't parse server headers\n");
1927 releaseAtom(message
);
1928 httpServerAbort(connection
, 1, 502,
1929 internAtom("Couldn't parse server headers"));
1934 date
= current_time
.tv_sec
;
1938 releaseAtom(message
);
1939 /* We've already reset wait_continue above, but we must still
1940 ensure that the writer notices. */
1941 notifyObject(request
->object
);
1942 connection
->len
-= rc
;
1943 if(connection
->len
> 0)
1944 memmove(connection
->buf
, connection
->buf
+ rc
, connection
->len
);
1945 httpServerReply(connection
, 1);
1950 httpServerAbort(connection
, 1, 501,
1951 internAtom("Upgrade not implemented"));
1955 if(via
&& !checkVia(proxyName
, via
)) {
1956 httpServerAbort(connection
, 1, 504, internAtom("Proxy loop detected"));
1959 full_len
= content_range
.full_length
;
1962 if(content_range
.from
== -1 || content_range
.to
== -1) {
1963 do_log(L_ERROR
, "Partial content without range.\n");
1964 httpServerAbort(connection
, 1, 502,
1965 internAtom("Partial content without range"));
1968 if(len
>= 0 && len
!= content_range
.to
- content_range
.from
) {
1969 do_log(L_ERROR
, "Inconsistent partial content.\n");
1970 httpServerAbort(connection
, 1, 502,
1971 internAtom("Inconsistent partial content"));
1974 } else if(code
< 400 &&
1975 (content_range
.from
>= 0 || content_range
.to
>= 0 ||
1976 content_range
.full_length
>= 0)) {
1977 do_log(L_WARN
, "Range without partial content.\n");
1979 content_range
.from
= -1;
1980 content_range
.to
= -1;
1981 content_range
.full_length
= -1;
1982 } else if(code
!= 304 && code
!= 412) {
1986 if(te
!= TE_IDENTITY
&& te
!= TE_CHUNKED
) {
1987 do_log(L_ERROR
, "Unsupported transfer-encoding\n");
1988 httpServerAbort(connection
, 1, 502,
1989 internAtom("Unsupported transfer-encoding"));
1994 if(request
->method
!= METHOD_CONDITIONAL_GET
) {
1995 do_log(L_ERROR
, "Unexpected \"not changed\" reply from server\n");
1996 httpServerAbort(connection
, 1, 502,
1997 internAtom("Unexpected \"not changed\" "
1998 "reply from server"));
2001 if(object
->etag
&& !etag
) {
2002 /* RFC 2616 10.3.5. Violated by some front-end proxies. */
2003 do_log(L_WARN
, "\"Not changed\" reply with no ETag.\n");
2008 if(request
->method
!= METHOD_CONDITIONAL_GET
||
2009 (!object
->etag
&& !object
->last_modified
)) {
2011 "Unexpected \"precondition failed\" reply from server.\n");
2012 httpServerAbort(connection
, 1, 502,
2013 internAtom("Unexpected \"precondition failed\" "
2014 "reply from server"));
2021 /* Okay, we're going to accept this reply. */
2023 if((code
== 200 || code
== 206 || code
== 304 || code
== 412) &&
2024 (cache_control
.flags
& (CACHE_NO
| CACHE_NO_STORE
) ||
2025 cache_control
.max_age
== 0 ||
2026 (cacheIsShared
&& cache_control
.s_maxage
== 0) ||
2027 (expires
>= 0 && expires
<= object
->age
))) {
2028 do_log(L_UNCACHEABLE
, "Uncacheable object %s (%d)\n",
2029 scrub(object
->key
), cache_control
.flags
);
2032 if(request
->time0
.tv_sec
!= null_time
.tv_sec
)
2033 init_time
= &request
->time0
;
2035 init_time
= ¤t_time
;
2036 age
= MIN(init_time
->tv_sec
- age
, init_time
->tv_sec
);
2038 if(request
->method
== METHOD_HEAD
||
2039 code
< 200 || code
== 204 || code
== 304)
2041 else if(te
== TE_IDENTITY
)
2042 expect_body
= (len
!= 0);
2046 connection
->chunk_remaining
= -1;
2047 connection
->te
= te
;
2049 old_object
= object
;
2051 connection
->server
->lies
--;
2053 if(object
->cache_control
& CACHE_MISMATCH
)
2056 if(code
== 304 || code
== 412) {
2057 if((object
->etag
&& etag
&& strcmp(object
->etag
, etag
) != 0) ||
2058 (object
->last_modified
>= 0 && last_modified
>= 0 &&
2059 object
->last_modified
!= last_modified
)) {
2060 do_log(L_ERROR
, "Inconsistent \"%s\" reply for %s\n",
2061 code
== 304 ? "not changed":"precondition failed",
2062 scrub(object
->key
));
2063 object
->flags
|= OBJECT_DYNAMIC
;
2066 } else if(!(object
->flags
& OBJECT_INITIAL
)) {
2067 if((object
->last_modified
< 0 || last_modified
< 0) &&
2068 (!object
->etag
|| !etag
))
2070 else if(object
->last_modified
!= last_modified
)
2072 else if(object
->etag
|| etag
) {
2073 /* We need to be permissive here so as to deal with some
2074 front-end proxies that discard ETags on partial
2075 replies but not on full replies. */
2076 if(etag
&& object
->etag
&& strcmp(object
->etag
, etag
) != 0)
2078 else if(!object
->etag
)
2082 if(!supersede
&& (object
->cache_control
& CACHE_VARY
) &&
2083 dontTrustVaryETag
>= 1) {
2084 /* Check content-type to work around mod_gzip bugs */
2085 if(!httpHeaderMatch(atomContentType
, object
->headers
, headers
) ||
2086 !httpHeaderMatch(atomContentEncoding
, object
->headers
, headers
))
2090 if(full_len
< 0 && te
== TE_IDENTITY
) {
2091 /* It's an HTTP/1.0 CGI. Be afraid. */
2092 if(expect_body
&& content_range
.from
< 0 && content_range
.to
< 0)
2096 if(!supersede
&& object
->length
>= 0 && full_len
>= 0 &&
2097 object
->length
!= full_len
) {
2098 do_log(L_WARN
, "Inconsistent length.\n");
2103 ((object
->last_modified
>= 0 && last_modified
>= 0) ||
2104 (object
->etag
&& etag
))) {
2105 if(request
->method
== METHOD_CONDITIONAL_GET
) {
2106 do_log(L_WARN
, "Server ignored conditional request.\n");
2107 connection
->server
->lies
+= 10;
2108 /* Drop the connection? */
2111 } else if(code
== 416) {
2112 do_log(L_ERROR
, "Unexpected \"range not satisfiable\" reply\n");
2113 httpServerAbort(connection
, 1, 502,
2114 internAtom("Unexpected \"range not satisfiable\" "
2116 /* The object may be superseded. Make sure the next request
2117 won't be partial. */
2118 abortObject(object
, 502,
2119 internAtom("Unexpected \"range not satisfiable\" reply"));
2123 if(object
->flags
& OBJECT_INITIAL
)
2127 do_log(L_SUPERSEDED
,
2128 "Superseding object %s (%d %d %d %s -> %d %d %d %s)\n",
2129 scrub(old_object
->key
),
2130 object
->code
, object
->length
, (int)object
->last_modified
,
2131 object
->etag
? object
->etag
: "(none)",
2132 code
, full_len
, (int)last_modified
,
2133 etag
? etag
: "(none)");
2134 privatiseObject(old_object
, 0);
2135 new_object
= makeObject(object
->type
, object
->key
,
2136 object
->key_size
, 1, 0,
2137 object
->request
, NULL
);
2138 if(new_object
== NULL
) {
2139 do_log(L_ERROR
, "Couldn't allocate object\n");
2140 httpServerAbort(connection
, 1, 500,
2141 internAtom("Couldn't allocate object"));
2144 if(urlIsLocal(new_object
->key
, new_object
->key_size
))
2145 new_object
->flags
|= OBJECT_LOCAL
;
2147 new_object
= object
;
2151 (!etag
&& last_modified
< 0) ||
2152 (cache_control
.flags
&
2153 (CACHE_NO_HIDDEN
| CACHE_NO
| CACHE_NO_STORE
|
2154 (cacheIsShared
? CACHE_PRIVATE
: 0))) ||
2155 (cache_control
.max_age
>= 0 && cache_control
.max_age
<= 2) ||
2157 cache_control
.s_maxage
>= 0 && cache_control
.s_maxage
<= 5) ||
2158 (old_object
->last_modified
>= 0 && old_object
->expires
>= 0 &&
2159 (old_object
->expires
- old_object
->last_modified
<= 1)) ||
2160 (supersede
&& (old_object
->date
- date
<= 5));
2163 new_object
->flags
|= OBJECT_DYNAMIC
;
2165 new_object
->flags
&= ~OBJECT_DYNAMIC
;
2166 else if(old_object
->flags
& OBJECT_DYNAMIC
)
2167 new_object
->flags
|= OBJECT_DYNAMIC
;
2169 new_object
->age
= age
;
2170 new_object
->cache_control
|= cache_control
.flags
;
2171 new_object
->max_age
= cache_control
.max_age
;
2172 new_object
->s_maxage
= cache_control
.s_maxage
;
2173 new_object
->flags
&= ~OBJECT_FAILED
;
2176 new_object
->date
= date
;
2177 if(last_modified
>= 0)
2178 new_object
->last_modified
= last_modified
;
2180 new_object
->expires
= expires
;
2181 if(new_object
->etag
== NULL
)
2182 new_object
->etag
= etag
;
2188 case 300: case 301: case 302: case 303: case 307:
2189 case 403: case 404: case 405: case 401:
2190 if(new_object
->message
) releaseAtom(new_object
->message
);
2191 new_object
->code
= code
;
2192 new_object
->message
= message
;
2194 case 206: case 304: case 412:
2195 if(new_object
->code
!= 200 || !new_object
->message
) {
2196 if(new_object
->message
) releaseAtom(new_object
->message
);
2197 new_object
->code
= 200;
2198 new_object
->message
= internAtom("OK");
2200 releaseAtom(message
);
2203 if(new_object
->message
) releaseAtom(new_object
->message
);
2204 new_object
->code
= code
;
2205 new_object
->message
= retainAtom(message
);
2209 httpTweakCachability(new_object
);
2212 new_via
= internAtomF("%s %s",
2213 version
== HTTP_11
? "1.1" : "1.0",
2216 new_via
= internAtomF("%s, %s %s", via
->string
,
2217 version
== HTTP_11
? "1.1" : "1.0",
2219 if(new_via
== NULL
) {
2220 do_log(L_ERROR
, "Couldn't allocate Via.\n");
2222 if(new_object
->via
) releaseAtom(new_object
->via
);
2223 new_object
->via
= new_via
;
2226 if(new_object
->flags
& OBJECT_INITIAL
) {
2227 objectPartial(new_object
, full_len
, headers
);
2229 if(new_object
->length
< 0)
2230 new_object
->length
= full_len
;
2231 /* XXX -- RFC 2616 13.5.3 */
2232 releaseAtom(headers
);
2236 assert(new_object
!= old_object
);
2237 supersedeObject(old_object
);
2240 if(new_object
!= old_object
) {
2241 if(new_object
->flags
& OBJECT_INPROGRESS
) {
2242 /* Make sure we don't fetch this object two times at the
2243 same time. Just drop the connection. */
2244 releaseObject(new_object
);
2245 httpServerFinish(connection
, 1, 0);
2248 old_object
->flags
&= ~OBJECT_VALIDATING
;
2249 new_object
->flags
|= OBJECT_INPROGRESS
;
2250 /* Signal the client side to switch to the new object -- see
2251 httpClientGetHandler. If it doesn't, we'll give up on this
2253 new_object
->flags
|= OBJECT_MUTATING
;
2254 request
->can_mutate
= new_object
;
2255 notifyObject(old_object
);
2256 request
->can_mutate
= NULL
;
2257 new_object
->flags
&= ~OBJECT_MUTATING
;
2258 old_object
->flags
&= ~OBJECT_INPROGRESS
;
2259 if(request
->object
== old_object
) {
2260 if(request
->request
)
2261 request
->request
->request
= NULL
;
2262 request
->request
= NULL
;
2263 request
->object
= new_object
;
2265 assert(request
->object
== new_object
);
2267 releaseNotifyObject(old_object
);
2269 object
= new_object
;
2271 objectMetadataChanged(new_object
, 0);
2274 if(object
->flags
& OBJECT_VALIDATING
) {
2275 object
->flags
&= ~OBJECT_VALIDATING
;
2276 notifyObject(object
);
2280 httpServerFinish(connection
, 0, rc
);
2284 if(request
->request
== NULL
) {
2285 httpServerFinish(connection
, 1, 0);
2290 /* 412 replies contain a useless body. For now, we
2291 drop the connection. */
2292 httpServerFinish(connection
, 1, 0);
2297 if(request
->flags
& REQUEST_PERSISTENT
) {
2298 if(request
->method
!= METHOD_HEAD
&&
2299 connection
->te
== TE_IDENTITY
&& len
< 0) {
2300 do_log(L_ERROR
, "Persistent reply with no Content-Length\n");
2301 /* That's potentially dangerous, as we could start reading
2302 arbitrary data into the object. Unfortunately, some
2304 request
->flags
&= ~REQUEST_PERSISTENT
;
2308 /* we're getting a body */
2309 if(content_range
.from
> 0)
2310 connection
->offset
= content_range
.from
;
2312 connection
->offset
= 0;
2314 if(content_range
.to
>= 0)
2315 request
->to
= content_range
.to
;
2317 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2318 (unsigned long)connection
, (unsigned long)object
,
2319 connection
->offset
);
2321 if(connection
->len
> rc
) {
2322 rc
= connectionAddData(connection
, rc
);
2326 do_log(L_ERROR
, "Couldn't parse chunk size.\n");
2327 httpServerAbort(connection
, 1, 502,
2328 internAtom("Couldn't parse chunk size"));
2330 do_log(L_ERROR
, "Couldn't add data to connection.\n");
2331 httpServerAbort(connection
, 1, 500,
2332 internAtom("Couldn't add data "
2338 if(object
->length
< 0) {
2339 object
->length
= object
->size
;
2340 objectMetadataChanged(object
, 0);
2341 } else if(object
->length
!= object
->size
) {
2342 httpServerAbort(connection
, 1, 500,
2343 internAtom("Inconsistent "
2345 object
->length
= -1;
2349 httpServerFinish(connection
, 0, 0);
2354 connection
->len
= 0;
2358 if(connection
->te
== TE_CHUNKED
||
2359 (object
->length
>= 0 &&
2360 connection
->offset
< object
->length
)) {
2361 do_log(L_ERROR
, "Server closed connection.\n");
2362 httpServerAbort(connection
, 1, 502,
2363 internAtom("Server closed connection"));
2366 if(code
!= 206 && eof
> 0 && object
->length
< 0) {
2367 object
->length
= object
->size
;
2368 objectMetadataChanged(object
, 0);
2370 httpServerFinish(connection
, 1, 0);
2374 return httpServerReadData(connection
, 1);
2380 releaseAtom(message
);
2382 releaseAtom(headers
);
2391 httpServerIndirectHandlerCommon(HTTPConnectionPtr connection
, int eof
)
2393 HTTPRequestPtr request
= connection
->request
;
2396 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
2398 if(connection
->len
> 0) {
2400 rc
= connectionAddData(connection
, 0);
2404 do_log(L_ERROR
, "Couldn't parse chunk size.\n");
2405 httpServerAbort(connection
, 1, 502,
2406 internAtom("Couldn't parse chunk size"));
2408 do_log(L_ERROR
, "Couldn't add data to connection.\n");
2409 httpServerAbort(connection
, 1, 500,
2410 internAtom("Couldn't add data "
2415 if(request
->to
< 0) {
2416 if(request
->object
->length
< 0) {
2417 request
->object
->length
= request
->object
->size
;
2418 objectMetadataChanged(request
->object
, 0);
2419 } else if(request
->object
->length
!=
2420 request
->object
->size
) {
2421 request
->object
->length
= -1;
2422 httpServerAbort(connection
, 1, 502,
2423 internAtom("Inconsistent "
2428 httpServerFinish(connection
, 0, 0);
2434 if(eof
&& connection
->len
== 0) {
2435 if(connection
->te
== TE_CHUNKED
||
2436 (request
->to
>= 0 && connection
->offset
< request
->to
)) {
2437 do_log(L_ERROR
, "Server dropped connection.\n");
2438 httpServerAbort(connection
, 1, 502,
2439 internAtom("Server dropped connection"));
2442 if(request
->object
->length
< 0 && eof
> 0 &&
2443 (request
->to
< 0 || request
->to
> request
->object
->size
)) {
2444 request
->object
->length
= request
->object
->size
;
2445 objectMetadataChanged(request
->object
, 0);
2447 httpServerFinish(connection
, 1, 0);
2451 return httpServerReadData(connection
, 0);
2456 httpServerIndirectHandler(int status
,
2457 FdEventHandlerPtr event
,
2458 StreamRequestPtr srequest
)
2460 HTTPConnectionPtr connection
= srequest
->data
;
2461 assert(connection
->request
->object
->flags
& OBJECT_INPROGRESS
);
2463 httpSetTimeout(connection
, -1);
2465 if(status
!= -ECLIENTRESET
)
2466 do_log_error(L_ERROR
, -status
, "Read from server failed");
2467 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
2468 internAtomError(-status
, "Read from server failed"));
2472 connection
->len
= srequest
->offset
;
2474 return httpServerIndirectHandlerCommon(connection
, status
);
2478 httpServerReadData(HTTPConnectionPtr connection
, int immediate
)
2480 HTTPRequestPtr request
= connection
->request
;
2481 ObjectPtr object
= request
->object
;
2484 assert(object
->flags
& OBJECT_INPROGRESS
);
2486 if(request
->request
== NULL
) {
2487 httpServerFinish(connection
, 1, 0);
2491 if(request
->to
>= 0)
2494 to
= object
->length
;
2496 if(to
>= 0 && to
== connection
->offset
) {
2497 httpServerFinish(connection
, 0, 0);
2501 if(connection
->len
== 0 &&
2502 ((connection
->te
== TE_IDENTITY
&& to
> connection
->offset
) ||
2503 (connection
->te
== TE_CHUNKED
&& connection
->chunk_remaining
> 0))) {
2504 /* Read directly into the object */
2505 int i
= connection
->offset
/ CHUNK_SIZE
;
2506 int j
= connection
->offset
% CHUNK_SIZE
;
2508 /* See httpServerDirectHandlerCommon if you change this */
2509 if(connection
->te
== TE_CHUNKED
) {
2510 len
= connection
->chunk_remaining
;
2511 /* The logic here is that we want more to just fit the
2512 chunk header if we're doing a large read, but do a
2513 large read if we would otherwise do a small one. The
2514 magic constant 2000 comes from the assumption that the
2515 server uses chunks that have a size that are a power of
2516 two (possibly including the chunk header), and that we
2517 want a full ethernet packet to fit into our read. */
2518 more
= (len
>= 2000 ? 20 : MIN(2048 - len
, CHUNK_SIZE
));
2520 len
= to
- connection
->offset
;
2521 /* We read more data only when there is a reasonable
2522 chance of there being another reply coming. */
2523 more
= (connection
->pipelined
> 1) ? CHUNK_SIZE
: 0;
2525 end
= len
+ connection
->offset
;
2527 httpConnectionDestroyBuf(connection
);
2529 /* The order of allocation is important in case we run out of
2531 lockChunk(object
, i
);
2532 if(object
->chunks
[i
].data
== NULL
)
2533 object
->chunks
[i
].data
= get_chunk();
2534 if(object
->chunks
[i
].data
&& object
->chunks
[i
].size
>= j
) {
2535 if(len
+ j
> CHUNK_SIZE
) {
2536 lockChunk(object
, i
+ 1);
2537 if(object
->chunks
[i
+ 1].data
== NULL
)
2538 object
->chunks
[i
+ 1].data
= get_chunk();
2539 /* Unless we're grabbing all len of data, we do not
2540 want to do an indirect read immediately afterwards. */
2541 if(more
&& len
+ j
<= 2 * CHUNK_SIZE
) {
2542 if(!connection
->buf
)
2543 connection
->buf
= get_chunk(); /* checked below */
2545 if(object
->chunks
[i
+ 1].data
) {
2546 do_stream_3(IO_READ
| IO_NOTNOW
, connection
->fd
, j
,
2547 object
->chunks
[i
].data
, CHUNK_SIZE
,
2548 object
->chunks
[i
+ 1].data
,
2550 end
- (i
+ 1) * CHUNK_SIZE
),
2551 connection
->buf
, connection
->buf
? more
: 0,
2552 httpServerDirectHandler2
, connection
);
2555 unlockChunk(object
, i
+ 1);
2557 if(more
&& len
+ j
<= CHUNK_SIZE
) {
2558 if(!connection
->buf
)
2559 connection
->buf
= get_chunk();
2561 do_stream_2(IO_READ
| IO_NOTNOW
, connection
->fd
, j
,
2562 object
->chunks
[i
].data
,
2563 MIN(CHUNK_SIZE
, end
- i
* CHUNK_SIZE
),
2564 connection
->buf
, connection
->buf
? more
: 0,
2565 httpServerDirectHandler
, connection
);
2568 unlockChunk(object
, i
);
2572 if(connection
->len
== 0)
2573 httpConnectionDestroyBuf(connection
);
2575 httpSetTimeout(connection
, serverTimeout
);
2576 do_stream_buf(IO_READ
| IO_NOTNOW
|
2577 ((immediate
&& connection
->len
) ? IO_IMMEDIATE
: 0),
2578 connection
->fd
, connection
->len
,
2580 (connection
->te
== TE_CHUNKED
?
2581 MIN(2048, CHUNK_SIZE
) : CHUNK_SIZE
),
2582 httpServerIndirectHandler
, connection
);
2587 httpServerDirectHandlerCommon(int kind
, int status
,
2588 FdEventHandlerPtr event
,
2589 StreamRequestPtr srequest
)
2591 HTTPConnectionPtr connection
= srequest
->data
;
2592 HTTPRequestPtr request
= connection
->request
;
2593 ObjectPtr object
= request
->object
;
2594 int i
= connection
->offset
/ CHUNK_SIZE
;
2597 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
2599 httpSetTimeout(connection
, -1);
2602 unlockChunk(object
, i
);
2603 if(kind
== 2) unlockChunk(object
, i
+ 1);
2604 if(status
!= -ECLIENTRESET
)
2605 do_log_error(L_ERROR
, -status
, "Read from server failed");
2606 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
2607 internAtomError(-status
, "Read from server failed"));
2611 /* We have incestuous knowledge of the decisions made in
2612 httpServerReadData */
2613 if(request
->to
>= 0)
2616 to
= object
->length
;
2617 if(connection
->te
== TE_CHUNKED
)
2618 end
= connection
->offset
+ connection
->chunk_remaining
;
2621 /* The amount of data actually read into the object */
2622 end1
= MIN(end
, i
* CHUNK_SIZE
+ MIN(kind
* CHUNK_SIZE
, srequest
->offset
));
2624 assert(end
>= 0 && end1
>= i
* CHUNK_SIZE
&& end1
<= (i
+ 2) * CHUNK_SIZE
);
2626 object
->chunks
[i
].size
=
2627 MAX(object
->chunks
[i
].size
, MIN(end1
- i
* CHUNK_SIZE
, CHUNK_SIZE
));
2628 if(kind
== 2 && end1
> (i
+ 1) * CHUNK_SIZE
) {
2629 object
->chunks
[i
+ 1].size
=
2630 MAX(object
->chunks
[i
+ 1].size
, end1
- (i
+ 1) * CHUNK_SIZE
);
2632 if(connection
->te
== TE_CHUNKED
) {
2633 connection
->chunk_remaining
-= (end1
- connection
->offset
);
2634 assert(connection
->chunk_remaining
>= 0);
2636 connection
->offset
= end1
;
2637 object
->size
= MAX(object
->size
, end1
);
2638 unlockChunk(object
, i
);
2639 if(kind
== 2) unlockChunk(object
, i
+ 1);
2641 if(i
* CHUNK_SIZE
+ srequest
->offset
> end1
) {
2642 connection
->len
= i
* CHUNK_SIZE
+ srequest
->offset
- end1
;
2643 return httpServerIndirectHandlerCommon(connection
, status
);
2645 notifyObject(object
);
2647 httpServerFinish(connection
, 1, 0);
2650 return httpServerReadData(connection
, 0);
2656 httpServerDirectHandler(int status
,
2657 FdEventHandlerPtr event
,
2658 StreamRequestPtr srequest
)
2660 return httpServerDirectHandlerCommon(1, status
, event
, srequest
);
2664 httpServerDirectHandler2(int status
,
2665 FdEventHandlerPtr event
,
2666 StreamRequestPtr srequest
)
2668 return httpServerDirectHandlerCommon(2, status
, event
, srequest
);
2671 /* Add the data accumulated in connection->buf into the object in
2672 connection->request. Returns 0 in the normal case, 1 if the TE is
2673 self-terminating and we're done, -1 if there was a problem with
2674 objectAddData, -2 if there was a problem with the data. */
2676 connectionAddData(HTTPConnectionPtr connection
, int skip
)
2678 HTTPRequestPtr request
= connection
->request
;
2679 ObjectPtr object
= request
->object
;
2682 if(connection
->te
== TE_IDENTITY
) {
2685 len
= connection
->len
- skip
;
2686 if(object
->length
>= 0) {
2687 len
= MIN(object
->length
- connection
->offset
, len
);
2689 if(request
->to
>= 0)
2690 len
= MIN(request
->to
- connection
->offset
, len
);
2692 rc
= objectAddData(object
, connection
->buf
+ skip
,
2693 connection
->offset
, len
);
2696 connection
->offset
+= len
;
2697 connection
->len
-= (len
+ skip
);
2698 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2699 (unsigned long)connection
, (unsigned long)object
,
2700 connection
->offset
);
2703 if(connection
->len
> 0 && skip
+ len
> 0) {
2704 memmove(connection
->buf
,
2705 connection
->buf
+ skip
+ len
, connection
->len
);
2708 if((object
->length
>= 0 && object
->length
<= connection
->offset
) ||
2709 (request
->to
>= 0 && request
->to
<= connection
->offset
)) {
2710 notifyObject(object
);
2714 notifyObject(object
);
2717 } else if(connection
->te
== TE_CHUNKED
) {
2718 int i
= skip
, j
, size
;
2719 /* connection->chunk_remaining is 0 at the end of a chunk, -1
2720 after the CR/LF pair ending a chunk, and -2 after we've
2721 seen a chunk of length 0. */
2722 if(connection
->chunk_remaining
> -2) {
2724 if(connection
->chunk_remaining
<= 0) {
2725 if(connection
->chunk_remaining
== 0) {
2726 if(connection
->len
< i
+ 2)
2728 if(connection
->buf
[i
] != '\r' ||
2729 connection
->buf
[i
+ 1] != '\n')
2732 connection
->chunk_remaining
= -1;
2734 if(connection
->len
< i
+ 2)
2736 j
= parseChunkSize(connection
->buf
, i
,
2737 connection
->len
, &size
);
2745 connection
->chunk_remaining
= -2;
2748 connection
->chunk_remaining
= size
;
2751 /* connection->chunk_remaining > 0 */
2752 size
= MIN(connection
->chunk_remaining
,
2753 connection
->len
- i
);
2756 rc
= objectAddData(object
, connection
->buf
+ i
,
2757 connection
->offset
, size
);
2758 connection
->offset
+= size
;
2762 connection
->chunk_remaining
-= size
;
2763 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2764 (unsigned long)connection
,
2765 (unsigned long)object
,
2766 connection
->offset
);
2770 connection
->len
-= i
;
2771 if(connection
->len
> 0)
2772 memmove(connection
->buf
, connection
->buf
+ i
, connection
->len
);
2773 if(i
> 0 || connection
->chunk_remaining
== -2)
2774 notifyObject(object
);
2775 if(connection
->chunk_remaining
== -2)
2785 listServers(FILE *out
)
2787 HTTPServerPtr server
;
2790 fprintf(out
, "<!DOCTYPE HTML PUBLIC "
2791 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2792 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2794 "\r\n<title>Known servers</title>\n"
2796 "<h1>Known servers</h1>\n");
2798 alternatingHttpStyle(out
, "servers");
2799 fprintf(out
, "<table id=servers>\n");
2800 fprintf(out
, "<thead><tr><th>Server</th>"
2802 "<th>Persistent</th>"
2804 "<th>Connections</th>"
2809 fprintf(out
, "<tbody>\n");
2813 fprintf(out
, "<tr class=\"%s\">", entry
% 2 == 0 ? "even" : "odd");
2814 if(server
->port
== 80)
2815 fprintf(out
, "<td>%s</td>", server
->name
);
2817 fprintf(out
, "<td>%s:%d</td>", server
->name
, server
->port
);
2819 if(server
->version
== HTTP_11
)
2820 fprintf(out
, "<td>1.1</td>");
2821 else if(server
->version
== HTTP_10
)
2822 fprintf(out
, "<td>1.0</td>");
2824 fprintf(out
, "<td>unknown</td>");
2826 if(server
->persistent
< 0)
2827 fprintf(out
, "<td>no</td>");
2828 else if(server
->persistent
> 0)
2829 fprintf(out
, "<td>yes</td>");
2831 fprintf(out
, "<td>unknown</td>");
2833 if(server
->version
!= HTTP_11
|| server
->persistent
<= 0)
2834 fprintf(out
, "<td></td>");
2835 else if(server
->pipeline
< 0)
2836 fprintf(out
, "<td>no</td>");
2837 else if(server
->pipeline
>= 0 && server
->pipeline
<= 1)
2838 fprintf(out
, "<td>unknown</td>");
2839 else if(server
->pipeline
== 2 || server
->pipeline
== 3)
2840 fprintf(out
, "<td>probing</td>");
2842 fprintf(out
, "<td>yes</td>");
2845 for(i
= 0; i
< server
->maxslots
; i
++)
2846 if(server
->connection
[i
] && !server
->connection
[i
]->connecting
) {
2847 if(i
< server
->numslots
)
2853 fprintf(out
, "<td>%d/%d", n
, server
->numslots
);
2855 fprintf(out
, " + %d</td>", m
);
2857 fprintf(out
, "</td>");
2859 if(server
->lies
> 0)
2860 fprintf(out
, "<td>(%d lies)</td>", (server
->lies
+ 9) / 10);
2862 fprintf(out
, "<td></td>");
2865 fprintf(out
, "<td>%.3f</td>", (double)server
->rtt
/ 1000000.0);
2867 fprintf(out
, "<td></td>");
2868 if(server
->rate
> 0)
2869 fprintf(out
, "<td>%d</td>", server
->rate
);
2871 fprintf(out
, "<td></td>");
2873 fprintf(out
, "</tr>\n");
2874 server
= server
->next
;
2877 fprintf(out
, "</tbody>\n");
2878 fprintf(out
, "</table>\n");
2879 fprintf(out
, "<p><a href=\"/polipo/\">back</a></p>");
2880 fprintf(out
, "</body></html>\n");