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;
42 int alwaysAddNoTransform
= 0;
44 static HTTPServerPtr servers
= 0;
46 static int httpServerContinueConditionHandler(int, ConditionHandlerPtr
);
47 static int initParentProxy(void);
48 static int parentProxySetter(ConfigVariablePtr var
, void *value
);
49 static void httpServerDelayedFinish(HTTPConnectionPtr
);
50 static int allowUnalignedRangeRequests
= 0;
55 CONFIG_VARIABLE_SETTABLE(parentProxy
, CONFIG_ATOM_LOWER
, parentProxySetter
,
56 "Parent proxy (host:port).");
57 CONFIG_VARIABLE(serverExpireTime
, CONFIG_TIME
,
58 "Time during which server data is valid.");
59 CONFIG_VARIABLE_SETTABLE(smallRequestTime
, CONFIG_TIME
, configIntSetter
,
60 "Estimated time for a small request.");
61 CONFIG_VARIABLE_SETTABLE(replyUnpipelineTime
, CONFIG_TIME
, configIntSetter
,
62 "Estimated time for a pipeline break.");
63 CONFIG_VARIABLE_SETTABLE(replyUnpipelineSize
, CONFIG_INT
, configIntSetter
,
64 "Size for a pipeline break.");
65 CONFIG_VARIABLE_SETTABLE(pipelineAdditionalRequests
, CONFIG_TRISTATE
,
67 "Pipeline requests on an active connection.");
68 CONFIG_VARIABLE_SETTABLE(maxPipelineTrain
, CONFIG_INT
,
70 "Maximum number of requests "
71 "pipelined at a time.");
72 CONFIG_VARIABLE(pmmFirstSize
, CONFIG_INT
,
73 "The size of the first PMM chunk.");
74 CONFIG_VARIABLE(pmmSize
, CONFIG_INT
,
75 "The size of a PMM chunk.");
76 CONFIG_VARIABLE(serverSlots
, CONFIG_INT
,
77 "Maximum number of connections per server.");
78 CONFIG_VARIABLE(serverSlots1
, CONFIG_INT
,
79 "Maximum number of connections per HTTP/1.0 server.");
80 CONFIG_VARIABLE(serverMaxSlots
, CONFIG_INT
,
81 "Maximum number of connections per broken server.");
82 CONFIG_VARIABLE(dontCacheRedirects
, CONFIG_BOOLEAN
,
83 "If true, don't cache redirects.");
84 CONFIG_VARIABLE_SETTABLE(allowUnalignedRangeRequests
,
85 CONFIG_BOOLEAN
, configIntSetter
,
86 "Allow unaligned range requests (unreliable).");
87 CONFIG_VARIABLE_SETTABLE(maxSideBuffering
,
88 CONFIG_INT
, configIntSetter
,
89 "Maximum buffering for PUT and POST requests.");
90 CONFIG_VARIABLE_SETTABLE(maxConnectionAge
,
91 CONFIG_TIME
, configIntSetter
,
92 "Maximum age of a server-side connection.");
93 CONFIG_VARIABLE_SETTABLE(maxConnectionRequests
,
94 CONFIG_INT
, configIntSetter
,
95 "Maximum number of requests on a server-side connection.");
96 CONFIG_VARIABLE(alwaysAddNoTransform
, CONFIG_BOOLEAN
,
97 "If true, add a no-transform directive to all requests.");
101 parentProxySetter(ConfigVariablePtr var
, void *value
)
103 configAtomSetter(var
, value
);
109 discardServer(HTTPServerPtr server
)
111 HTTPServerPtr previous
;
112 assert(!server
->request
);
114 if(server
== servers
)
115 servers
= server
->next
;
118 while(previous
->next
!= server
)
119 previous
= previous
->next
;
120 previous
->next
= server
->next
;
123 if(server
->connection
)
124 free(server
->connection
);
125 if(server
->idleHandler
)
126 free(server
->idleHandler
);
134 httpServerIdle(HTTPServerPtr server
)
139 for(i
= 0; i
< server
->maxslots
; i
++)
140 if(server
->connection
[i
])
146 expireServersHandler(TimeEventHandlerPtr event
)
148 HTTPServerPtr server
, next
;
149 TimeEventHandlerPtr e
;
153 if(httpServerIdle(server
) &&
154 server
->time
+ serverExpireTime
< current_time
.tv_sec
)
155 discardServer(server
);
158 e
= scheduleTimeEvent(serverExpireTime
/ 60 + 60,
159 expireServersHandler
, 0, NULL
);
161 do_log(L_ERROR
, "Couldn't schedule server expiry.\n");
170 return (size
+ CHUNK_SIZE
- 1) / CHUNK_SIZE
* CHUNK_SIZE
;
176 AtomPtr host
, port_atom
;
180 releaseAtom(parentHost
);
186 if(parentProxy
!= NULL
&& parentProxy
->length
== 0) {
187 releaseAtom(parentProxy
);
191 if(parentProxy
== NULL
)
194 rc
= atomSplit(parentProxy
, ':', &host
, &port_atom
);
196 do_log(L_ERROR
, "Couldn't parse parentProxy.");
197 releaseAtom(parentProxy
);
202 port
= atoi(port_atom
->string
);
203 if(port
<= 0 || port
>= 0x10000) {
205 releaseAtom(port_atom
);
206 do_log(L_ERROR
, "Couldn't parse parentProxy.");
207 releaseAtom(parentProxy
);
220 TimeEventHandlerPtr event
;
223 if(pmmFirstSize
|| pmmSize
) {
224 if(pmmSize
== 0) pmmSize
= pmmFirstSize
;
225 if(pmmFirstSize
== 0) pmmFirstSize
= pmmSize
;
226 pmmSize
= roundSize(pmmSize
);
227 pmmFirstSize
= roundSize(pmmFirstSize
);
230 if(serverMaxSlots
< 1)
234 if(serverSlots
> serverMaxSlots
)
235 serverSlots
= serverMaxSlots
;
236 if(serverSlots1
< serverSlots
)
237 serverSlots1
= serverSlots
;
238 if(serverSlots1
> serverMaxSlots
)
239 serverSlots1
= serverMaxSlots
;
243 event
= scheduleTimeEvent(serverExpireTime
/ 60 + 60, expireServersHandler
,
246 do_log(L_ERROR
, "Couldn't schedule server expiry.\n");
252 getServer(char *name
, int port
, int proxy
)
254 HTTPServerPtr server
;
259 if(strcmp(server
->name
, name
) == 0 && server
->port
== port
&&
260 server
->isProxy
== proxy
) {
261 if(httpServerIdle(server
) &&
262 server
->time
+ serverExpireTime
< current_time
.tv_sec
) {
263 discardServer(server
);
267 server
->time
= current_time
.tv_sec
;
271 server
= server
->next
;
274 server
= malloc(sizeof(HTTPServerRec
));
276 do_log(L_ERROR
, "Couldn't allocate server.\n");
280 server
->connection
= malloc(serverMaxSlots
* sizeof(HTTPConnectionPtr
));
281 if(server
->connection
== NULL
) {
282 do_log(L_ERROR
, "Couldn't allocate server.\n");
287 server
->idleHandler
= malloc(serverMaxSlots
* sizeof(FdEventHandlerPtr
));
288 if(server
->connection
== NULL
) {
289 do_log(L_ERROR
, "Couldn't allocate server.\n");
290 free(server
->connection
);
295 server
->maxslots
= serverMaxSlots
;
297 server
->name
= strdup(name
);
298 if(server
->name
== NULL
) {
299 do_log(L_ERROR
, "Couldn't allocate server name.\n");
305 server
->addrindex
= 0;
306 server
->isProxy
= proxy
;
307 server
->version
= HTTP_UNKNOWN
;
308 server
->persistent
= 0;
309 server
->pipeline
= 0;
310 server
->time
= current_time
.tv_sec
;
313 server
->numslots
= MIN(serverSlots
, server
->maxslots
);
314 for(i
= 0; i
< server
->maxslots
; i
++) {
315 server
->connection
[i
] = NULL
;
316 server
->idleHandler
[i
] = NULL
;
318 server
->request
= NULL
;
319 server
->request_last
= NULL
;
322 server
->next
= servers
;
328 httpServerQueueRequest(HTTPServerPtr server
, HTTPRequestPtr request
)
330 assert(request
->request
&& request
->request
->request
== request
);
331 assert(request
->connection
== NULL
);
332 if(server
->request
) {
333 server
->request_last
->next
= request
;
334 server
->request_last
= request
;
336 server
->request_last
= request
;
337 server
->request
= request
;
343 httpServerAbort(HTTPConnectionPtr connection
, int fail
,
344 int code
, AtomPtr message
)
346 HTTPRequestPtr request
= connection
->request
;
348 if(request
->request
) {
349 httpClientError(request
->request
, code
, retainAtom(message
));
352 request
->object
->flags
|= OBJECT_FAILED
;
353 if(request
->object
->flags
& OBJECT_INITIAL
)
354 abortObject(request
->object
, code
, retainAtom(message
));
355 notifyObject(request
->object
);
358 releaseAtom(message
);
359 if(!connection
->connecting
)
360 httpServerFinish(connection
, 1, 0);
364 httpServerAbortRequest(HTTPRequestPtr request
, int fail
,
365 int code
, AtomPtr message
)
367 if(request
->connection
&& request
== request
->connection
->request
) {
368 httpServerAbort(request
->connection
, fail
, code
, message
);
370 HTTPRequestPtr requestor
= request
->request
;
372 requestor
->request
= NULL
;
373 request
->request
= NULL
;
374 httpClientError(requestor
, code
, retainAtom(message
));
377 request
->object
->flags
|= OBJECT_FAILED
;
378 if(request
->object
->flags
& OBJECT_INITIAL
)
379 abortObject(request
->object
, code
, retainAtom(message
));
380 notifyObject(request
->object
);
382 releaseAtom(message
);
387 httpServerClientReset(HTTPRequestPtr request
)
389 if(request
->connection
&&
390 request
->connection
->fd
>= 0 &&
391 !request
->connection
->connecting
&&
392 request
->connection
->request
== request
)
393 pokeFdEvent(request
->connection
->fd
, -ECLIENTRESET
, POLLIN
| POLLOUT
);
398 httpMakeServerRequest(char *name
, int port
, ObjectPtr object
,
399 int method
, int from
, int to
, HTTPRequestPtr requestor
)
401 HTTPServerPtr server
;
402 HTTPRequestPtr request
;
405 assert(!(object
->flags
& OBJECT_INPROGRESS
));
408 server
= getServer(parentHost
->string
, parentPort
, 1);
410 server
= getServer(name
, port
, 0);
412 if(server
== NULL
) return -1;
414 object
->flags
|= OBJECT_INPROGRESS
;
415 object
->requestor
= requestor
;
417 request
= httpMakeRequest();
419 do_log(L_ERROR
, "Couldn't allocate request.\n");
423 /* Because we allocate objects in chunks, we cannot have data that
424 doesn't start at a chunk boundary. */
425 if(from
% CHUNK_SIZE
!= 0) {
426 if(allowUnalignedRangeRequests
) {
427 objectFillFromDisk(object
, from
/ CHUNK_SIZE
* CHUNK_SIZE
, 1);
428 if(objectHoleSize(object
, from
- 1) != 0)
429 from
= from
/ CHUNK_SIZE
* CHUNK_SIZE
;
431 from
= from
/ CHUNK_SIZE
* CHUNK_SIZE
;
435 request
->object
= retainObject(object
);
436 request
->method
= method
;
437 if(method
== METHOD_CONDITIONAL_GET
) {
439 request
->method
= METHOD_HEAD
;
443 (expectContinue
? (requestor
->flags
& REQUEST_WAIT_CONTINUE
) : 0);
444 request
->from
= from
;
446 request
->request
= requestor
;
447 requestor
->request
= request
;
448 request
->cache_control
= requestor
->cache_control
;
449 request
->time0
= null_time
;
450 request
->time1
= null_time
;
452 rc
= httpServerQueueRequest(server
, request
);
454 do_log(L_ERROR
, "Couldn't queue request.\n");
455 request
->request
= NULL
;
456 requestor
->request
= NULL
;
457 object
->flags
&= ~(OBJECT_INPROGRESS
| OBJECT_VALIDATING
);
458 releaseNotifyObject(object
);
459 httpDestroyRequest(request
);
463 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
464 if(server
->version
== HTTP_10
) {
465 httpServerAbortRequest(request
, 1,
466 417, internAtom("Expectation failed"));
469 } else if(expectContinue
>= 2 && server
->version
== HTTP_11
) {
470 if(request
->method
== METHOD_POST
|| request
->method
== METHOD_PUT
)
471 request
->flags
|= REQUEST_WAIT_CONTINUE
;
475 rc
= httpServerTrigger(server
);
477 /* We must be very short on memory. If there are any requests
478 queued, we abort one and try again. If there aren't, we
480 do_log(L_ERROR
, "Couldn't trigger server -- out of memory?\n");
481 if(server
->request
) {
482 httpServerAbortRequest(server
->request
, 1, 503,
483 internAtom("Couldn't trigger server"));
491 httpServerConnection(HTTPServerPtr server
)
493 HTTPConnectionPtr connection
;
496 connection
= httpMakeConnection();
497 if(connection
== NULL
) {
498 do_log(L_ERROR
, "Couldn't allocate server connection.\n");
501 connection
->server
= server
;
503 for(i
= 0; i
< server
->numslots
; i
++) {
504 if(!server
->connection
[i
]) {
505 server
->connection
[i
] = connection
;
509 assert(i
< server
->numslots
);
511 connection
->request
= NULL
;
512 connection
->request_last
= NULL
;
514 do_log(D_SERVER_CONN
, "C... %s:%d.\n",
515 scrub(connection
->server
->name
), connection
->server
->port
);
516 httpSetTimeout(connection
, serverTimeout
);
517 if(socksParentProxy
) {
518 connection
->connecting
= CONNECTING_SOCKS
;
519 do_socks_connect(server
->name
, connection
->server
->port
,
520 httpServerSocksHandler
, connection
);
522 connection
->connecting
= CONNECTING_DNS
;
523 do_gethostbyname(server
->name
, 0,
524 httpServerConnectionDnsHandler
,
531 httpServerConnectionDnsHandler(int status
, GethostbynameRequestPtr request
)
533 HTTPConnectionPtr connection
= request
->data
;
535 httpSetTimeout(connection
, -1);
539 message
= internAtomF("Host %s lookup failed: %s",
541 request
->name
->string
: "(unknown)",
542 request
->error_message
?
543 request
->error_message
->string
:
545 do_log(L_ERROR
, "Host %s lookup failed: %s (%d).\n",
547 scrub(request
->name
->string
) : "(unknown)",
548 request
->error_message
?
549 request
->error_message
->string
:
550 pstrerror(-status
), -status
);
551 connection
->connecting
= 0;
552 if(connection
->server
->request
)
553 httpServerAbortRequest(connection
->server
->request
, 1, 504,
554 retainAtom(message
));
555 httpServerAbort(connection
, 1, 502, message
);
559 if(request
->addr
->string
[0] == DNS_CNAME
) {
560 if(request
->count
> 10) {
561 AtomPtr message
= internAtom("DNS CNAME loop");
562 do_log(L_ERROR
, "DNS CNAME loop.\n");
563 connection
->connecting
= 0;
564 if(connection
->server
->request
)
565 httpServerAbortRequest(connection
->server
->request
, 1, 504,
566 retainAtom(message
));
567 httpServerAbort(connection
, 1, 504, message
);
571 httpSetTimeout(connection
, serverTimeout
);
572 do_gethostbyname(request
->addr
->string
+ 1, request
->count
+ 1,
573 httpServerConnectionDnsHandler
,
578 connection
->connecting
= CONNECTING_CONNECT
;
579 httpSetTimeout(connection
, serverTimeout
);
580 do_connect(retainAtom(request
->addr
), connection
->server
->addrindex
,
581 connection
->server
->port
,
582 httpServerConnectionHandler
, connection
);
587 httpServerConnectionHandler(int status
,
588 FdEventHandlerPtr event
,
589 ConnectRequestPtr request
)
591 HTTPConnectionPtr connection
= request
->data
;
593 assert(connection
->fd
< 0);
594 if(request
->fd
>= 0) {
596 connection
->fd
= request
->fd
;
597 connection
->server
->addrindex
= request
->index
;
598 rc
= setNodelay(connection
->fd
, 1);
600 do_log_error(L_WARN
, errno
, "Couldn't disable Nagle's algorithm");
603 return httpServerConnectionHandlerCommon(status
, connection
);
607 httpServerSocksHandler(int status
, SocksRequestPtr request
)
609 HTTPConnectionPtr connection
= request
->data
;
611 assert(connection
->fd
< 0);
612 if(request
->fd
>= 0) {
613 connection
->fd
= request
->fd
;
614 connection
->server
->addrindex
= 0;
616 return httpServerConnectionHandlerCommon(status
, connection
);
620 httpServerConnectionHandlerCommon(int status
, HTTPConnectionPtr connection
)
622 httpSetTimeout(connection
, -1);
626 internAtomError(-status
, "Connect to %s:%d failed",
627 connection
->server
->name
,
628 connection
->server
->port
);
629 if(status
!= -ECLIENTRESET
)
630 do_log_error(L_ERROR
, -status
, "Connect to %s:%d failed",
631 scrub(connection
->server
->name
),
632 connection
->server
->port
);
633 connection
->connecting
= 0;
634 if(connection
->server
->request
)
635 httpServerAbortRequest(connection
->server
->request
,
636 status
!= -ECLIENTRESET
, 504,
637 retainAtom(message
));
638 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 504, message
);
642 do_log(D_SERVER_CONN
, "C %s:%d.\n",
643 scrub(connection
->server
->name
), connection
->server
->port
);
645 connection
->connecting
= 0;
646 /* serverTrigger will take care of inserting any timeouts */
647 httpServerTrigger(connection
->server
);
652 httpServerIdleHandler(int a
, FdEventHandlerPtr event
)
654 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
655 HTTPServerPtr server
= connection
->server
;
658 assert(!connection
->request
);
660 do_log(D_SERVER_CONN
, "Idle connection to %s:%d died.\n",
661 scrub(connection
->server
->name
), connection
->server
->port
);
663 for(i
= 0; i
< server
->maxslots
; i
++) {
664 if(connection
== server
->connection
[i
]) {
665 server
->idleHandler
[i
] = NULL
;
669 assert(i
< server
->maxslots
);
671 httpServerAbort(connection
, 1, 504, internAtom("Timeout"));
675 /* Discard aborted requests at the head of the queue. */
677 httpServerDiscardRequests(HTTPServerPtr server
)
679 HTTPRequestPtr request
;
680 while(server
->request
&& !server
->request
->request
) {
681 request
= server
->request
;
682 server
->request
= request
->next
;
683 request
->next
= NULL
;
684 if(server
->request
== NULL
)
685 server
->request_last
= NULL
;
686 request
->object
->flags
&= ~(OBJECT_INPROGRESS
| OBJECT_VALIDATING
);
687 releaseNotifyObject(request
->object
);
688 request
->object
= NULL
;
689 httpDestroyRequest(request
);
694 pipelineIsSmall(HTTPConnectionPtr connection
)
696 HTTPRequestPtr request
= connection
->request
;
698 if(pipelineAdditionalRequests
<= 0)
700 else if(pipelineAdditionalRequests
>= 2)
705 if(request
->next
|| !(request
->flags
& REQUEST_PERSISTENT
))
707 if(request
->method
== METHOD_HEAD
||
708 request
->method
== METHOD_CONDITIONAL_GET
)
710 if(request
->to
>= 0 && connection
->server
->rate
> 0 &&
711 request
->to
- request
->from
< connection
->server
->rate
*
718 numRequests(HTTPServerPtr server
)
721 HTTPRequestPtr request
= server
->request
;
724 request
= request
->next
;
730 httpServerGetConnection(HTTPServerPtr server
, int *idle_return
)
733 int connecting
= 0, empty
= 0, idle
= 0;
736 /* Try to find an idle connection */
737 for(i
= 0; i
< server
->numslots
; i
++) {
738 if(server
->connection
[i
]) {
739 if(!server
->connection
[i
]->connecting
) {
740 if(!server
->connection
[i
]->request
) {
741 if(server
->idleHandler
[i
])
742 unregisterFdEvent(server
->idleHandler
[i
]);
743 server
->idleHandler
[i
] = NULL
;
755 return server
->connection
[j
];
758 /* If there's an empty slot, schedule connection creation */
760 /* Don't open a connection if there are already enough in
761 progress, except if the server doesn't do persistent
762 connections and there's only one in progress. */
763 if((connecting
== 0 || (server
->persistent
<= 0 && connecting
<= 1)) ||
764 connecting
< numRequests(server
)) {
765 httpServerConnection(server
);
769 /* Find a connection that can accept additional requests */
770 if(server
->version
== HTTP_11
&& server
->pipeline
>= 4) {
771 for(i
= 0; i
< serverSlots
; i
++) {
772 if(server
->connection
[i
] && !server
->connection
[i
]->connecting
&&
773 pipelineIsSmall(server
->connection
[i
])) {
774 if(server
->idleHandler
[i
])
775 unregisterFdEvent(server
->idleHandler
[i
]);
776 server
->idleHandler
[i
] = NULL
;
778 return server
->connection
[i
];
787 httpServerTrigger(HTTPServerPtr server
)
789 HTTPConnectionPtr connection
;
790 HTTPRequestPtr request
;
791 int idle
, n
, i
, rc
, numidle
;
793 while(server
->request
) {
794 httpServerDiscardRequests(server
);
799 if(REQUEST_SIDE(server
->request
)) {
800 rc
= httpServerSideRequest(server
);
801 /* If rc is 0, httpServerSideRequest didn't dequeue this
802 request. Go through the scheduling loop again, come
807 connection
= httpServerGetConnection(server
, &numidle
);
808 if(!connection
) break;
810 /* If server->pipeline <= 0, we don't do pipelining. If
811 server->pipeline is 1, then we are ready to start probing
812 for pipelining on the server; we then send exactly two
813 requests in what is hopefully a single packet to check
814 whether the server has the nasty habit of discarding its
815 input buffers after each request.
816 If server->pipeline is 2 or 3, the pipelining probe is in
817 progress on this server, and we don't pipeline anything
818 until it succeeds. When server->pipeline >= 4, pipelining
819 is believed to work on this server. */
820 if(server
->version
!= HTTP_11
|| server
->pipeline
<= 0 ||
821 server
->pipeline
== 2 || server
->pipeline
== 3) {
822 if(connection
->pipelined
== 0)
826 } else if(server
->pipeline
== 1) {
827 if(connection
->pipelined
== 0)
828 n
= MIN(2, maxPipelineTrain
);
832 n
= maxPipelineTrain
;
835 /* Don't pipeline if there are more idle connections */
839 idle
= !connection
->pipelined
;
841 while(server
->request
&& connection
->pipelined
< n
) {
842 httpServerDiscardRequests(server
);
843 if(!server
->request
) break;
844 request
= server
->request
;
845 assert(request
->request
->request
== request
);
846 rc
= httpWriteRequest(connection
, request
, -1);
849 httpServerAbortRequest(request
, rc
!= -ECLIENTRESET
, 502,
850 internAtom("Couldn't "
854 do_log(D_SERVER_CONN
, "W: ");
855 do_log_n(D_SERVER_CONN
,
856 request
->object
->key
, request
->object
->key_size
);
857 do_log(D_SERVER_CONN
, " (%d)\n", request
->method
);
858 if(connection
->pipelined
> 0)
859 request
->flags
|= REQUEST_PIPELINED
;
860 request
->time0
= current_time
;
862 server
->request
= request
->next
;
863 request
->next
= NULL
;
864 if(server
->request
== NULL
)
865 server
->request_last
= NULL
;
866 httpQueueRequest(connection
, request
);
867 connection
->pipelined
++;
869 if(server
->persistent
> 0 && server
->pipeline
== 1 && i
>= 2)
870 server
->pipeline
= 2;
872 if(i
> 0) httpServerSendRequest(connection
);
874 if(idle
&& connection
->pipelined
> 0)
875 httpServerReply(connection
, 0);
880 for(i
= 0; i
< server
->maxslots
; i
++) {
881 if(server
->connection
[i
] &&
882 !server
->connection
[i
]->connecting
&&
883 !server
->connection
[i
]->request
) {
884 /* Artificially age any fresh connections that aren't used
885 straight away; this is necessary for the logic for POST and
886 the logic that determines whether a given request should be
888 if(server
->connection
[i
]->serviced
== 0)
889 server
->connection
[i
]->serviced
= 1;
890 if(!server
->idleHandler
[i
])
891 server
->idleHandler
[i
] =
892 registerFdEvent(server
->connection
[i
]->fd
, POLLIN
,
893 httpServerIdleHandler
,
894 sizeof(HTTPConnectionPtr
),
895 &server
->connection
[i
]);
896 if(!server
->idleHandler
[i
]) {
897 do_log(L_ERROR
, "Couldn't register idle handler.\n");
898 httpServerFinish(server
->connection
[i
], 1, 0);
900 httpSetTimeout(server
->connection
[i
], serverIdleTimeout
);
908 httpServerSideRequest(HTTPServerPtr server
)
910 HTTPRequestPtr request
= server
->request
;
911 HTTPConnectionPtr connection
;
912 HTTPRequestPtr requestor
= request
->request
;
913 HTTPConnectionPtr client
= requestor
->connection
;
914 int rc
, i
, freeslots
, idle
, connecting
;
916 assert(REQUEST_SIDE(request
));
923 /* Find a fresh connection */
924 for(i
= 0; i
< server
->numslots
; i
++) {
925 if(!server
->connection
[i
])
927 else if(!server
->connection
[i
]->connecting
) {
928 if(!server
->connection
[i
]->request
) {
929 if(server
->connection
[i
]->serviced
== 0) {
930 if(server
->idleHandler
[i
])
931 unregisterFdEvent(server
->idleHandler
[i
]);
932 server
->idleHandler
[i
] = NULL
;
933 connection
= server
->connection
[i
];
945 /* Make sure that a fresh connection will be established at some
946 point, then wait until httpServerTrigger calls us again. */
948 httpServerConnection(server
);
951 /* Shutdown a random idle connection */
952 pokeFdEvent(server
->connection
[idle
]->fd
,
953 -EDOSHUTDOWN
, POLLIN
| POLLOUT
);
959 rc
= httpWriteRequest(connection
, request
, client
->bodylen
);
961 do_log(L_ERROR
, "Couldn't write POST or PUT request.\n");
962 httpServerAbortRequest(request
, rc
!= -ECLIENTRESET
, 502,
963 internAtom("Couldn't write request"));
966 server
->request
= request
->next
;
967 request
->next
= NULL
;
968 if(server
->request
== NULL
)
969 server
->request_last
= NULL
;
970 httpQueueRequest(connection
, request
);
971 connection
->pipelined
= 1;
972 request
->time0
= current_time
;
973 connection
->reqoffset
= 0;
974 connection
->bodylen
= client
->bodylen
;
975 httpServerDoSide(connection
);
980 httpServerDoSide(HTTPConnectionPtr connection
)
982 HTTPRequestPtr request
= connection
->request
;
983 HTTPRequestPtr requestor
= request
->request
;
984 HTTPConnectionPtr client
= requestor
->connection
;
985 int len
= MIN(client
->reqlen
- client
->reqbegin
,
986 connection
->bodylen
- connection
->reqoffset
);
989 (len
>= maxSideBuffering
||
990 client
->reqbegin
> 0 ||
991 (connection
->reqoffset
+ client
->reqlen
- client
->reqbegin
) >=
992 connection
->bodylen
);
993 int done
= connection
->reqoffset
>= connection
->bodylen
;
995 assert(connection
->bodylen
>= 0);
997 httpSetTimeout(connection
, 60);
999 if(connection
->reqlen
> 0) {
1000 /* Send the headers, but don't send any part of the body if
1001 we're in wait_continue. */
1002 do_stream_2(IO_WRITE
,
1004 connection
->reqbuf
, connection
->reqlen
,
1005 client
->reqbuf
+ client
->reqbegin
,
1006 (request
->flags
& REQUEST_WAIT_CONTINUE
) ? 0 : len
,
1007 httpServerSideHandler2
, connection
);
1008 httpServerReply(connection
, 0);
1009 } else if(request
->object
->flags
& OBJECT_ABORTED
) {
1010 if(connection
->reqbuf
)
1011 dispose_chunk(connection
->reqbuf
);
1012 connection
->reqbuf
= NULL
;
1013 connection
->reqlen
= 0;
1014 pokeFdEvent(connection
->fd
, -ESHUTDOWN
, POLLIN
);
1015 if(client
->flags
& CONN_READER
) {
1016 client
->flags
|= CONN_SIDE_READER
;
1017 do_stream(IO_READ
| IO_IMMEDIATE
| IO_NOTNOW
,
1018 client
->fd
, 0, NULL
, 0,
1019 httpClientSideHandler
, client
);
1021 } else if(!(request
->flags
& REQUEST_WAIT_CONTINUE
) && doflush
) {
1022 /* Make sure there's a reqbuf, as httpServerFinish uses
1023 it to determine if there's a writer. */
1024 if(connection
->reqbuf
== NULL
)
1025 connection
->reqbuf
= get_chunk();
1026 assert(connection
->reqbuf
!= NULL
);
1029 client
->reqbuf
+ client
->reqbegin
, len
,
1030 httpServerSideHandler
, connection
);
1032 if(connection
->reqbuf
) {
1033 httpConnectionDestroyReqbuf(connection
);
1034 connection
->reqlen
= 0;
1036 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
1037 ConditionHandlerPtr chandler
;
1038 do_log(D_SERVER_CONN
, "W... %s:%d.\n",
1039 scrub(connection
->server
->name
), connection
->server
->port
);
1041 conditionWait(&request
->object
->condition
,
1042 httpServerContinueConditionHandler
,
1043 sizeof(connection
), &connection
);
1047 do_log(L_ERROR
, "Couldn't register condition handler.\n");
1048 /* Fall through -- the client side will clean up. */
1050 client
->flags
|= CONN_SIDE_READER
;
1051 do_stream(IO_READ
| (done
? IO_IMMEDIATE
: 0 ) | IO_NOTNOW
,
1052 client
->fd
, client
->reqlen
,
1053 client
->reqbuf
, CHUNK_SIZE
,
1054 httpClientSideHandler
, client
);
1060 httpClientDelayedDoSideHandler(TimeEventHandlerPtr event
)
1062 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1063 httpServerDoSide(connection
);
1068 httpServerDelayedDoSide(HTTPConnectionPtr connection
)
1070 TimeEventHandlerPtr handler
;
1071 handler
= scheduleTimeEvent(0, httpClientDelayedDoSideHandler
,
1072 sizeof(connection
), &connection
);
1074 do_log(L_ERROR
, "Couldn't schedule DoSide -- freeing memory.\n");
1075 free_chunk_arenas();
1076 handler
= scheduleTimeEvent(0, httpClientDelayedDoSideHandler
,
1077 sizeof(connection
), &connection
);
1078 do_log(L_ERROR
, "Couldn't schedule DoSide.\n");
1079 /* Somebody will hopefully end up timing out. */
1086 httpServerSideHandlerCommon(int kind
, int status
,
1087 FdEventHandlerPtr event
,
1088 StreamRequestPtr srequest
)
1090 HTTPConnectionPtr connection
= srequest
->data
;
1091 HTTPRequestPtr request
= connection
->request
;
1092 HTTPRequestPtr requestor
= request
->request
;
1093 HTTPConnectionPtr client
= requestor
->connection
;
1096 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1099 do_log_error(L_ERROR
, -status
, "Couldn't write to server");
1100 httpConnectionDestroyReqbuf(connection
);
1101 if(status
!= -ECLIENTRESET
)
1102 shutdown(connection
->fd
, 2);
1103 abortObject(request
->object
, 502,
1104 internAtom("Couldn't write to server"));
1105 /* Let the read side handle the error */
1106 httpServerDoSide(connection
);
1110 assert(srequest
->offset
> 0);
1113 if(srequest
->offset
< connection
->reqlen
)
1115 bodylen
= srequest
->offset
- connection
->reqlen
;
1116 connection
->reqlen
= 0;
1117 httpConnectionDestroyReqbuf(connection
);
1119 bodylen
= srequest
->offset
;
1123 assert(client
->reqbegin
+ bodylen
<= client
->reqlen
);
1125 if(client
->reqlen
> client
->reqbegin
+ bodylen
)
1126 memmove(client
->reqbuf
, client
->reqbuf
+ client
->reqbegin
+ bodylen
,
1127 client
->reqlen
- client
->reqbegin
- bodylen
);
1128 client
->reqlen
-= bodylen
+ client
->reqbegin
;
1129 client
->reqbegin
= 0;
1130 connection
->reqoffset
+= bodylen
;
1131 httpServerDoSide(connection
);
1136 httpServerSideHandler(int status
,
1137 FdEventHandlerPtr event
,
1138 StreamRequestPtr srequest
)
1140 return httpServerSideHandlerCommon(1, status
, event
, srequest
);
1144 httpServerSideHandler2(int status
,
1145 FdEventHandlerPtr event
,
1146 StreamRequestPtr srequest
)
1148 return httpServerSideHandlerCommon(2, status
, event
, srequest
);
1152 httpServerContinueConditionHandler(int status
, ConditionHandlerPtr chandler
)
1154 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)chandler
->data
;
1156 if(connection
->request
->flags
& REQUEST_WAIT_CONTINUE
)
1158 httpServerDelayedDoSide(connection
);
1162 /* s is 0 to keep the connection alive, 1 to shutdown the connection */
1164 httpServerFinish(HTTPConnectionPtr connection
, int s
, int offset
)
1166 HTTPServerPtr server
= connection
->server
;
1167 HTTPRequestPtr request
= connection
->request
;
1171 assert(connection
->pipelined
>= 1);
1172 assert((connection
->pipelined
> 1) == (request
->next
!= NULL
));
1174 assert(connection
->pipelined
== 0);
1177 if(!s
&& (!connection
->request
||
1178 !(connection
->request
->flags
& REQUEST_PERSISTENT
)))
1181 if(connection
->serviced
>= maxConnectionRequests
||
1182 connection
->time
< current_time
.tv_sec
- maxConnectionAge
)
1185 if(connection
->reqbuf
) {
1186 /* As most normal requests go out in a single packet, this is
1187 extremely unlikely to happen. As for POST/PUT requests,
1188 they are not pipelined, so this can only happen if the
1189 server sent an error reply early. */
1190 assert(connection
->fd
>= 0);
1191 shutdown(connection
->fd
, 1);
1192 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLOUT
);
1193 httpServerDelayedFinish(connection
);
1198 /* Update statistics about the server */
1199 int size
= -1, d
= -1, rtt
= -1, rate
= -1;
1200 if(connection
->offset
> 0 && request
->from
>= 0)
1201 size
= connection
->offset
- request
->from
;
1202 if(request
->time1
.tv_sec
!= null_time
.tv_sec
) {
1203 d
= timeval_minus_usec(¤t_time
, &request
->time1
);
1204 if(!(request
->flags
& REQUEST_PIPELINED
) &&
1205 request
->time0
.tv_sec
!= null_time
.tv_sec
)
1206 rtt
= timeval_minus_usec(&request
->time1
, &request
->time0
);
1207 if(size
>= 8192 && d
> 50000)
1208 rate
= ((double)size
/ (double)d
) * 1000000.0 + 0.5;
1210 request
->time0
= null_time
;
1211 request
->time1
= null_time
;
1214 if(server
->rtt
>= 0)
1215 server
->rtt
= (3 * server
->rtt
+ rtt
+ 2) / 4;
1220 if(server
->rate
>= 0)
1221 server
->rate
= (3 * server
->rate
+ rate
+ 2) / 4;
1223 server
->rate
= rate
;
1226 httpDequeueRequest(connection
);
1227 connection
->pipelined
--;
1228 request
->object
->flags
&= ~(OBJECT_INPROGRESS
| OBJECT_VALIDATING
);
1229 if(request
->request
) {
1230 request
->request
->request
= NULL
;
1231 request
->request
= NULL
;
1233 releaseNotifyObject(request
->object
);
1234 request
->object
= NULL
;
1235 httpDestroyRequest(request
);
1238 do_log(D_SERVER_CONN
, "Done with server %s:%d connection (%d)\n",
1239 scrub(connection
->server
->name
), connection
->server
->port
, s
);
1241 assert(offset
<= connection
->len
);
1244 if(offset
< connection
->len
) {
1245 assert(connection
->buf
!= NULL
);
1246 if(!connection
->pipelined
) {
1248 "Closing connection to %s:%d: "
1249 "%d stray bytes of data.\n",
1250 scrub(server
->name
), server
->port
,
1251 connection
->len
- offset
);
1254 memmove(connection
->buf
, connection
->buf
+ offset
,
1255 connection
->len
- offset
);
1256 connection
->len
= connection
->len
- offset
;
1257 if((connection
->flags
& CONN_BIGBUF
) &&
1258 connection
->len
<= CHUNK_SIZE
)
1259 httpConnectionUnbigify(connection
);
1262 connection
->len
= 0;
1266 connection
->server
->time
= current_time
.tv_sec
;
1267 connection
->serviced
++;
1270 if(connection
->timeout
)
1271 cancelTimeEvent(connection
->timeout
);
1272 connection
->timeout
= NULL
;
1273 httpConnectionDestroyBuf(connection
);
1274 if(connection
->fd
>= 0)
1275 CLOSE(connection
->fd
);
1276 connection
->fd
= -1;
1277 server
->persistent
-= 1;
1278 if(server
->persistent
< -5)
1279 server
->numslots
= MIN(server
->maxslots
, serverMaxSlots
);
1280 if(connection
->request
) {
1282 do_log(D_SERVER_CONN
, "Restarting pipeline to %s:%d.\n",
1283 scrub(server
->name
), server
->port
);
1284 if(server
->pipeline
== 2)
1285 server
->pipeline
-= 20;
1287 server
->pipeline
-= 5;
1288 req
= connection
->request
;
1290 req
->connection
= NULL
;
1294 connection
->request_last
->next
= server
->request
;
1296 server
->request_last
= connection
->request_last
;
1297 server
->request
= connection
->request
;
1298 connection
->request
= NULL
;
1299 connection
->request_last
= NULL
;
1301 /* Make sure we don't get confused into thinking a probe
1303 if(server
->pipeline
== 2 || server
->pipeline
== 3)
1304 server
->pipeline
= 1;
1305 for(i
= 0; i
< server
->maxslots
; i
++)
1306 if(connection
== server
->connection
[i
])
1308 assert(i
< server
->maxslots
);
1309 if(server
->idleHandler
[i
])
1310 unregisterFdEvent(server
->idleHandler
[i
]);
1311 server
->idleHandler
[i
] = NULL
;
1312 server
->connection
[i
] = NULL
;
1315 server
->persistent
+= 1;
1316 if(server
->persistent
> 0)
1317 server
->numslots
= MIN(server
->maxslots
,
1318 server
->version
== HTTP_10
?
1319 serverSlots1
: serverSlots
);
1320 httpSetTimeout(connection
, serverTimeout
);
1321 /* See httpServerTrigger */
1322 if(connection
->pipelined
||
1323 (server
->version
== HTTP_11
&& server
->pipeline
<= 0) ||
1324 (server
->pipeline
== 3)) {
1327 if(connection
->pipelined
) {
1328 httpServerReply(connection
, 1);
1330 httpConnectionDestroyBuf(connection
);
1335 httpServerTrigger(server
);
1339 httpServerDelayedFinishHandler(TimeEventHandlerPtr event
)
1341 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1342 httpServerFinish(connection
, 1, 0);
1347 httpServerDelayedFinish(HTTPConnectionPtr connection
)
1349 TimeEventHandlerPtr handler
;
1351 handler
= scheduleTimeEvent(1, httpServerDelayedFinishHandler
,
1352 sizeof(connection
), &connection
);
1355 "Couldn't schedule delayed finish -- freeing memory.");
1356 free_chunk_arenas();
1357 handler
= scheduleTimeEvent(1, httpServerDelayedFinishHandler
,
1358 sizeof(connection
), &connection
);
1361 "Couldn't schedule delayed finish -- aborting.\n");
1368 httpServerReply(HTTPConnectionPtr connection
, int immediate
)
1370 assert(connection
->pipelined
> 0);
1372 if(connection
->request
->request
== NULL
) {
1373 do_log(L_WARN
, "Aborting pipeline on %s:%d.\n",
1374 scrub(connection
->server
->name
), connection
->server
->port
);
1375 httpServerFinish(connection
, 1, 0);
1379 do_log(D_SERVER_CONN
, "R: %s (%d)\n",
1380 scrub(connection
->request
->object
->key
),
1381 connection
->request
->method
);
1383 if(connection
->len
== 0)
1384 httpConnectionDestroyBuf(connection
);
1386 httpSetTimeout(connection
, serverTimeout
);
1387 do_stream_buf(IO_READ
| (immediate
? IO_IMMEDIATE
: 0) | IO_NOTNOW
,
1388 connection
->fd
, connection
->len
,
1389 &connection
->buf
, CHUNK_SIZE
,
1390 httpServerReplyHandler
, connection
);
1394 httpConnectionPipelined(HTTPConnectionPtr connection
)
1396 HTTPRequestPtr request
= connection
->request
;
1400 request
= request
->next
;
1406 httpServerUnpipeline(HTTPRequestPtr request
)
1408 HTTPConnectionPtr connection
= request
->connection
;
1409 HTTPServerPtr server
= connection
->server
;
1411 request
->flags
&= ~REQUEST_PERSISTENT
;
1415 "Restarting pipeline to %s:%d.\n",
1416 scrub(connection
->server
->name
), connection
->server
->port
);
1417 req
= request
->next
;
1419 req
->connection
= NULL
;
1423 connection
->request_last
->next
= server
->request
;
1425 server
->request_last
= connection
->request_last
;
1426 server
->request
= request
->next
;
1427 request
->next
= NULL
;
1428 connection
->request_last
= request
;
1430 connection
->pipelined
= httpConnectionPipelined(connection
);
1434 httpServerRestart(HTTPConnectionPtr connection
)
1436 HTTPServerPtr server
= connection
->server
;
1437 HTTPRequestPtr request
= connection
->request
;
1443 "Restarting pipeline to %s:%d.\n",
1444 scrub(connection
->server
->name
), connection
->server
->port
);
1447 req
->connection
= NULL
;
1451 connection
->request_last
->next
= server
->request
;
1453 server
->request_last
= connection
->request_last
;
1454 server
->request
= request
;
1455 connection
->request
= NULL
;
1456 connection
->request_last
= NULL
;
1458 connection
->pipelined
= 0;
1459 httpServerFinish(connection
, 1, 0);
1463 httpServerRequest(ObjectPtr object
, int method
, int from
, int to
,
1464 HTTPRequestPtr requestor
, void *closure
)
1471 assert(from
>= 0 && (to
< 0 || to
> from
));
1472 assert(closure
== NULL
);
1473 assert(!(object
->flags
& OBJECT_LOCAL
));
1474 assert(object
->type
== OBJECT_HTTP
);
1476 if(object
->flags
& OBJECT_INPROGRESS
)
1479 if(requestor
->flags
& REQUEST_REQUESTED
)
1482 assert(requestor
->request
== NULL
);
1487 rc
= parseUrl(object
->key
, object
->key_size
, &x
, &y
, &port
, &z
);
1489 if(rc
< 0 || x
< 0 || y
< 0 || y
- x
> 131) {
1490 do_log(L_ERROR
, "Couldn't parse URL %s\n", scrub(object
->key
));
1491 abortObject(object
, 400, internAtom("Couldn't parse URL"));
1492 notifyObject(object
);
1496 if(!intListMember(port
, allowedPorts
)) {
1497 do_log(L_ERROR
, "Attempted connection to port %d.\n", port
);
1498 abortObject(object
, 403, internAtom("Forbidden port"));
1499 notifyObject(object
);
1503 memcpy(name
, ((char*)object
->key
) + x
, y
- x
);
1506 requestor
->flags
|= REQUEST_REQUESTED
;
1507 rc
= httpMakeServerRequest(name
, port
, object
, method
, from
, to
,
1512 503, internAtom("Couldn't schedule server request"));
1513 notifyObject(object
);
1521 httpWriteRequest(HTTPConnectionPtr connection
, HTTPRequestPtr request
,
1524 ObjectPtr object
= request
->object
;
1525 int from
= request
->from
, to
= request
->to
, method
= request
->method
;
1526 char *url
= object
->key
, *m
;
1527 int url_size
= object
->key_size
;
1528 int x
, y
, port
, z
, location_size
;
1530 int l
, n
, rc
, bufsize
;
1532 assert(method
!= METHOD_NONE
);
1534 if(request
->method
== METHOD_GET
||
1535 request
->method
== METHOD_CONDITIONAL_GET
) {
1539 do_log(L_ERROR
, "Requesting empty segment?\n");
1544 if(object
->flags
& OBJECT_DYNAMIC
) {
1548 objectFillFromDisk(object
, from
/ CHUNK_SIZE
* CHUNK_SIZE
, 1);
1549 l
= objectHoleSize(request
->object
, from
);
1551 if(to
<= 0 || to
> from
+ l
)
1555 if(pmmSize
&& connection
->server
->pipeline
>= 4) {
1557 to
= to
< 0 ? pmmFirstSize
: MIN(to
, pmmFirstSize
);
1559 to
= to
< 0 ? from
+ pmmSize
: MIN(to
, from
+ pmmSize
);
1562 if(from
% CHUNK_SIZE
!= 0)
1563 if(objectHoleSize(object
, from
- 1) != 0)
1564 from
= from
/ CHUNK_SIZE
* CHUNK_SIZE
;
1568 rc
= parseUrl(url
, url_size
, &x
, &y
, &port
, &z
);
1570 if(rc
< 0 || x
< 0 || y
< 0) {
1574 if(connection
->reqbuf
== NULL
) {
1575 connection
->reqbuf
= get_chunk();
1576 if(connection
->reqbuf
== NULL
)
1578 connection
->reqlen
= 0;
1581 if(method
== METHOD_CONDITIONAL_GET
&&
1582 object
->last_modified
< 0 && object
->etag
== NULL
)
1583 method
= request
->method
= METHOD_GET
;
1587 (connection
->flags
& CONN_BIGREQBUF
) ? bigBufferSize
: CHUNK_SIZE
;
1588 n
= connection
->reqlen
;
1591 case METHOD_CONDITIONAL_GET
: m
= "GET"; break;
1592 case METHOD_HEAD
: m
= "HEAD"; break;
1593 case METHOD_POST
: m
= "POST"; break;
1594 case METHOD_PUT
: m
= "PUT"; break;
1597 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, "%s ", m
);
1599 if(connection
->server
->isProxy
) {
1600 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
,
1603 if(url_size
- z
== 0) {
1608 location_size
= url_size
- z
;
1611 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
,
1612 location
, location_size
);
1615 do_log(D_SERVER_REQ
, "Server request: ");
1616 do_log_n(D_SERVER_REQ
, url
+ x
, y
- x
);
1617 do_log(D_SERVER_REQ
, ": ");
1618 do_log_n(D_SERVER_REQ
, connection
->reqbuf
, n
);
1619 do_log(D_SERVER_REQ
, " (method %d from %d to %d, 0x%lx for 0x%lx)\n",
1621 (unsigned long)connection
, (unsigned long)object
);
1623 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, " HTTP/1.1");
1625 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, "\r\nHost: ");
1626 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
, url
+ x
, y
- x
);
1628 n
= snnprintf(connection
->reqbuf
, n
, bufsize
, ":%d", port
);
1630 if(connection
->server
->isProxy
&& parentAuthCredentials
) {
1631 n
= buildServerAuthHeaders(connection
->reqbuf
, n
, bufsize
,
1632 parentAuthCredentials
);
1636 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1637 "\r\nContent-Length: %d", bodylen
);
1639 if(request
->flags
& REQUEST_WAIT_CONTINUE
)
1640 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1641 "\r\nExpect: 100-continue");
1643 if(method
!= METHOD_HEAD
&& (from
> 0 || to
>= 0)) {
1645 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1646 "\r\nRange: bytes=%d-%d", from
, to
- 1);
1648 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1649 "\r\nRange: bytes=%d-", from
);
1653 if(method
== METHOD_GET
&& object
->etag
&& (from
> 0 || to
>= 0)) {
1654 if(request
->request
&& request
->request
->request
== request
&&
1655 request
->request
->from
== 0 && request
->request
->to
== -1 &&
1656 pmmSize
== 0 && pmmFirstSize
== 0)
1657 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1658 "\r\nIf-Range: \"%s\"", object
->etag
);
1661 if(method
== METHOD_CONDITIONAL_GET
) {
1662 if(object
->last_modified
>= 0) {
1663 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1664 "\r\nIf-Modified-Since: ");
1665 n
= format_time(connection
->reqbuf
, n
, bufsize
,
1666 object
->last_modified
);
1669 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1670 "\r\nIf-None-Match: \"%s\"", object
->etag
);
1674 n
= httpPrintCacheControl(connection
->reqbuf
, n
, bufsize
,
1675 alwaysAddNoTransform
? CACHE_NO_TRANSFORM
: 0,
1676 &request
->cache_control
);
1680 if(request
->request
&& request
->request
->headers
) {
1681 n
= snnprint_n(connection
->reqbuf
, n
, bufsize
,
1682 request
->request
->headers
->string
,
1683 request
->request
->headers
->length
);
1686 if(request
->request
&& request
->request
->via
) {
1687 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1688 "\r\nVia: %s, 1.1 %s",
1689 request
->request
->via
->string
, proxyName
->string
);
1691 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1697 n
= snnprintf(connection
->reqbuf
, n
, bufsize
,
1698 "\r\nConnection: %s\r\n\r\n",
1699 (request
->flags
& REQUEST_PERSISTENT
) ?
1700 "keep-alive" : "close");
1701 if(n
< 0 || n
>= bufsize
- 1)
1703 connection
->reqlen
= n
;
1708 if(!(connection
->flags
& CONN_BIGREQBUF
))
1709 rc
= httpConnectionBigifyReqbuf(connection
);
1716 httpServerHandler(int status
,
1717 FdEventHandlerPtr event
,
1718 StreamRequestPtr srequest
)
1720 HTTPConnectionPtr connection
= srequest
->data
;
1723 assert(connection
->request
->object
->flags
& OBJECT_INPROGRESS
);
1725 if(connection
->reqlen
== 0) {
1726 do_log(D_SERVER_REQ
, "Writing aborted on 0x%lx\n",
1727 (unsigned long)connection
);
1731 if(status
== 0 && !streamRequestDone(srequest
)) {
1732 httpSetTimeout(connection
, serverTimeout
);
1736 httpConnectionDestroyReqbuf(connection
);
1739 if(connection
->serviced
>= 1) {
1740 httpServerRestart(connection
);
1743 if(status
>= 0 || status
== ECONNRESET
) {
1744 message
= internAtom("Couldn't send request to server: "
1747 if(status
!= -EPIPE
)
1748 do_log_error(L_ERROR
, -status
,
1749 "Couldn't send request to server");
1751 internAtomError(-status
, "Couldn't send request to server");
1759 httpConnectionDestroyReqbuf(connection
);
1760 shutdown(connection
->fd
, 2);
1761 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLIN
);
1762 httpSetTimeout(connection
, 60);
1767 httpServerSendRequest(HTTPConnectionPtr connection
)
1769 assert(connection
->server
);
1771 if(connection
->reqlen
== 0) {
1772 do_log(D_SERVER_REQ
,
1773 "Writing aborted on 0x%lx\n", (unsigned long)connection
);
1774 httpConnectionDestroyReqbuf(connection
);
1775 shutdown(connection
->fd
, 2);
1776 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLIN
| POLLOUT
);
1780 httpSetTimeout(connection
, serverTimeout
);
1781 do_stream(IO_WRITE
, connection
->fd
, 0,
1782 connection
->reqbuf
, connection
->reqlen
,
1783 httpServerHandler
, connection
);
1788 httpServerReplyHandler(int status
,
1789 FdEventHandlerPtr event
,
1790 StreamRequestPtr srequest
)
1792 HTTPConnectionPtr connection
= srequest
->data
;
1793 HTTPRequestPtr request
= connection
->request
;
1796 (connection
->flags
& CONN_BIGBUF
) ? bigBufferSize
: CHUNK_SIZE
;
1798 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1800 if(connection
->serviced
>= 1) {
1801 httpServerRestart(connection
);
1804 if(status
!= -ECLIENTRESET
)
1805 do_log_error(L_ERROR
, -status
, "Read from server failed");
1806 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
1807 internAtomError(-status
, "Read from server failed"));
1811 i
= findEndOfHeaders(connection
->buf
, 0, srequest
->offset
, &body
);
1812 connection
->len
= srequest
->offset
;
1815 request
->time1
= current_time
;
1816 return httpServerHandlerHeaders(status
, event
, srequest
, connection
);
1820 if(connection
->serviced
>= 1) {
1821 httpServerRestart(connection
);
1826 "Error reading server headers: %d\n", -status
);
1827 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
1828 internAtomError(-status
,
1829 "Error reading server headers"));
1831 httpServerAbort(connection
, 1, 502,
1832 internAtom("Server dropped connection"));
1836 if(connection
->len
>= bufsize
) {
1838 if(!(connection
->flags
& CONN_BIGBUF
))
1839 rc
= httpConnectionBigify(connection
);
1841 do_log(L_ERROR
, "Couldn't find end of server's headers.\n");
1842 httpServerAbort(connection
, 1, 502,
1843 internAtom("Couldn't find end "
1844 "of server's headers"));
1847 do_log(L_ERROR
, "Couldn't allocate big buffer.\n");
1848 httpServerAbort(connection
, 1, 500,
1849 internAtom("Couldn't allocate big buffer"));
1852 /* Can't just return 0 -- buf has moved. */
1854 connection
->fd
, connection
->len
,
1855 connection
->buf
, bigBufferSize
,
1856 httpServerReplyHandler
, connection
);
1864 httpServerHandlerHeaders(int eof
,
1865 FdEventHandlerPtr event
,
1866 StreamRequestPtr srequest
,
1867 HTTPConnectionPtr connection
)
1869 HTTPRequestPtr request
= connection
->request
;
1870 ObjectPtr object
= request
->object
;
1877 CacheControlRec cache_control
;
1879 time_t date
, last_modified
, expires
;
1880 struct timeval
*init_time
;
1882 AtomPtr via
, new_via
;
1884 HTTPRangeRec content_range
;
1885 ObjectPtr new_object
= NULL
, old_object
= NULL
;
1887 AtomPtr message
= NULL
;
1891 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1894 httpSetTimeout(connection
, -1);
1896 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
1897 do_log(D_SERVER_CONN
, "W %s:%d.\n",
1898 connection
->server
->name
, connection
->server
->port
);
1899 request
->flags
&= ~REQUEST_WAIT_CONTINUE
;
1902 rc
= httpParseServerFirstLine(connection
->buf
, &code
, &version
, &message
);
1904 do_log(L_ERROR
, "Couldn't parse server status line.\n");
1905 httpServerAbort(connection
, 1, 502,
1906 internAtom("Couldn't parse server status line"));
1910 do_log(D_SERVER_REQ
, "Server status: ");
1911 do_log_n(D_SERVER_REQ
, connection
->buf
,
1912 connection
->buf
[rc
- 1] == '\r' ? rc
- 2 : rc
- 2);
1913 do_log(D_SERVER_REQ
, " (0x%lx for 0x%lx)\n",
1914 (unsigned long)connection
, (unsigned long)object
);
1916 if(version
!= HTTP_10
&& version
!= HTTP_11
) {
1917 do_log(L_ERROR
, "Unknown server HTTP version\n");
1918 httpServerAbort(connection
, 1, 502,
1919 internAtom("Unknown server HTTP version"));
1920 releaseAtom(message
);
1924 connection
->version
= version
;
1925 connection
->server
->version
= version
;
1926 request
->flags
|= REQUEST_PERSISTENT
;
1928 url
= internAtomN(object
->key
, object
->key_size
);
1929 rc
= httpParseHeaders(0, url
, connection
->buf
, rc
, request
,
1930 &headers
, &len
, &cache_control
, NULL
, &te
,
1931 &date
, &last_modified
, &expires
, NULL
, NULL
, NULL
,
1932 &age
, &etag
, NULL
, NULL
, &content_range
,
1935 do_log(L_ERROR
, "Couldn't parse server headers\n");
1937 releaseAtom(message
);
1938 httpServerAbort(connection
, 1, 502,
1939 internAtom("Couldn't parse server headers"));
1944 date
= current_time
.tv_sec
;
1948 releaseAtom(message
);
1949 /* We've already reset wait_continue above, but we must still
1950 ensure that the writer notices. */
1951 notifyObject(request
->object
);
1952 connection
->len
-= rc
;
1953 if(connection
->len
> 0)
1954 memmove(connection
->buf
, connection
->buf
+ rc
, connection
->len
);
1955 httpServerReply(connection
, 1);
1960 httpServerAbort(connection
, 1, 501,
1961 internAtom("Upgrade not implemented"));
1965 if(via
&& !checkVia(proxyName
, via
)) {
1966 httpServerAbort(connection
, 1, 504, internAtom("Proxy loop detected"));
1969 full_len
= content_range
.full_length
;
1972 if(content_range
.from
== -1 || content_range
.to
== -1) {
1973 do_log(L_ERROR
, "Partial content without range.\n");
1974 httpServerAbort(connection
, 1, 502,
1975 internAtom("Partial content without range"));
1978 if(len
>= 0 && len
!= content_range
.to
- content_range
.from
) {
1979 do_log(L_ERROR
, "Inconsistent partial content.\n");
1980 httpServerAbort(connection
, 1, 502,
1981 internAtom("Inconsistent partial content"));
1984 } else if(code
< 400 &&
1985 (content_range
.from
>= 0 || content_range
.to
>= 0 ||
1986 content_range
.full_length
>= 0)) {
1987 do_log(L_WARN
, "Range without partial content.\n");
1989 content_range
.from
= -1;
1990 content_range
.to
= -1;
1991 content_range
.full_length
= -1;
1992 } else if(code
!= 304 && code
!= 412) {
1996 if(te
!= TE_IDENTITY
&& te
!= TE_CHUNKED
) {
1997 do_log(L_ERROR
, "Unsupported transfer-encoding\n");
1998 httpServerAbort(connection
, 1, 502,
1999 internAtom("Unsupported transfer-encoding"));
2004 if(request
->method
!= METHOD_CONDITIONAL_GET
) {
2005 do_log(L_ERROR
, "Unexpected \"not changed\" reply from server\n");
2006 httpServerAbort(connection
, 1, 502,
2007 internAtom("Unexpected \"not changed\" "
2008 "reply from server"));
2011 if(object
->etag
&& !etag
) {
2012 /* RFC 2616 10.3.5. Violated by some front-end proxies. */
2013 do_log(L_WARN
, "\"Not changed\" reply with no ETag.\n");
2018 if(request
->method
!= METHOD_CONDITIONAL_GET
||
2019 (!object
->etag
&& !object
->last_modified
)) {
2021 "Unexpected \"precondition failed\" reply from server.\n");
2022 httpServerAbort(connection
, 1, 502,
2023 internAtom("Unexpected \"precondition failed\" "
2024 "reply from server"));
2031 /* Okay, we're going to accept this reply. */
2033 if((code
== 200 || code
== 206 || code
== 304 || code
== 412) &&
2034 (cache_control
.flags
& (CACHE_NO
| CACHE_NO_STORE
) ||
2035 cache_control
.max_age
== 0 ||
2036 (cacheIsShared
&& cache_control
.s_maxage
== 0) ||
2037 (expires
>= 0 && expires
<= object
->age
))) {
2038 do_log(L_UNCACHEABLE
, "Uncacheable object %s (%d)\n",
2039 scrub(object
->key
), cache_control
.flags
);
2042 if(request
->time0
.tv_sec
!= null_time
.tv_sec
)
2043 init_time
= &request
->time0
;
2045 init_time
= ¤t_time
;
2046 age
= MIN(init_time
->tv_sec
- age
, init_time
->tv_sec
);
2048 if(request
->method
== METHOD_HEAD
||
2049 code
< 200 || code
== 204 || code
== 304)
2051 else if(te
== TE_IDENTITY
)
2052 expect_body
= (len
!= 0);
2056 connection
->chunk_remaining
= -1;
2057 connection
->te
= te
;
2059 old_object
= object
;
2061 connection
->server
->lies
--;
2063 if(object
->cache_control
& CACHE_MISMATCH
)
2066 if(code
== 304 || code
== 412) {
2067 if((object
->etag
&& etag
&& strcmp(object
->etag
, etag
) != 0) ||
2068 (object
->last_modified
>= 0 && last_modified
>= 0 &&
2069 object
->last_modified
!= last_modified
)) {
2070 do_log(L_ERROR
, "Inconsistent \"%s\" reply for %s\n",
2071 code
== 304 ? "not changed":"precondition failed",
2072 scrub(object
->key
));
2073 object
->flags
|= OBJECT_DYNAMIC
;
2076 } else if(!(object
->flags
& OBJECT_INITIAL
)) {
2077 if((object
->last_modified
< 0 || last_modified
< 0) &&
2078 (!object
->etag
|| !etag
))
2080 else if(object
->last_modified
!= last_modified
)
2082 else if(object
->etag
|| etag
) {
2083 /* We need to be permissive here so as to deal with some
2084 front-end proxies that discard ETags on partial
2085 replies but not on full replies. */
2086 if(etag
&& object
->etag
&& strcmp(object
->etag
, etag
) != 0)
2088 else if(!object
->etag
)
2092 if(!supersede
&& (object
->cache_control
& CACHE_VARY
) &&
2093 dontTrustVaryETag
>= 1) {
2094 /* Check content-type to work around mod_gzip bugs */
2095 if(!httpHeaderMatch(atomContentType
, object
->headers
, headers
) ||
2096 !httpHeaderMatch(atomContentEncoding
, object
->headers
, headers
))
2100 if(full_len
< 0 && te
== TE_IDENTITY
) {
2101 /* It's an HTTP/1.0 CGI. Be afraid. */
2102 if(expect_body
&& content_range
.from
< 0 && content_range
.to
< 0)
2106 if(!supersede
&& object
->length
>= 0 && full_len
>= 0 &&
2107 object
->length
!= full_len
) {
2108 do_log(L_WARN
, "Inconsistent length.\n");
2113 ((object
->last_modified
>= 0 && last_modified
>= 0) ||
2114 (object
->etag
&& etag
))) {
2115 if(request
->method
== METHOD_CONDITIONAL_GET
) {
2116 do_log(L_WARN
, "Server ignored conditional request.\n");
2117 connection
->server
->lies
+= 10;
2118 /* Drop the connection? */
2121 } else if(code
== 416) {
2122 do_log(L_ERROR
, "Unexpected \"range not satisfiable\" reply\n");
2123 httpServerAbort(connection
, 1, 502,
2124 internAtom("Unexpected \"range not satisfiable\" "
2126 /* The object may be superseded. Make sure the next request
2127 won't be partial. */
2128 abortObject(object
, 502,
2129 internAtom("Unexpected \"range not satisfiable\" reply"));
2133 if(object
->flags
& OBJECT_INITIAL
)
2137 do_log(L_SUPERSEDED
,
2138 "Superseding object %s (%d %d %d %s -> %d %d %d %s)\n",
2139 scrub(old_object
->key
),
2140 object
->code
, object
->length
, (int)object
->last_modified
,
2141 object
->etag
? object
->etag
: "(none)",
2142 code
, full_len
, (int)last_modified
,
2143 etag
? etag
: "(none)");
2144 privatiseObject(old_object
, 0);
2145 new_object
= makeObject(object
->type
, object
->key
,
2146 object
->key_size
, 1, 0,
2147 object
->request
, NULL
);
2148 if(new_object
== NULL
) {
2149 do_log(L_ERROR
, "Couldn't allocate object\n");
2150 httpServerAbort(connection
, 1, 500,
2151 internAtom("Couldn't allocate object"));
2154 if(urlIsLocal(new_object
->key
, new_object
->key_size
))
2155 new_object
->flags
|= OBJECT_LOCAL
;
2157 new_object
= object
;
2161 (!etag
&& last_modified
< 0) ||
2162 (cache_control
.flags
&
2163 (CACHE_NO_HIDDEN
| CACHE_NO
| CACHE_NO_STORE
|
2164 (cacheIsShared
? CACHE_PRIVATE
: 0))) ||
2165 (cache_control
.max_age
>= 0 && cache_control
.max_age
<= 2) ||
2167 cache_control
.s_maxage
>= 0 && cache_control
.s_maxage
<= 5) ||
2168 (old_object
->last_modified
>= 0 && old_object
->expires
>= 0 &&
2169 (old_object
->expires
- old_object
->last_modified
<= 1)) ||
2170 (supersede
&& (old_object
->date
- date
<= 5));
2173 new_object
->flags
|= OBJECT_DYNAMIC
;
2175 new_object
->flags
&= ~OBJECT_DYNAMIC
;
2176 else if(old_object
->flags
& OBJECT_DYNAMIC
)
2177 new_object
->flags
|= OBJECT_DYNAMIC
;
2179 new_object
->age
= age
;
2180 new_object
->cache_control
|= cache_control
.flags
;
2181 new_object
->max_age
= cache_control
.max_age
;
2182 new_object
->s_maxage
= cache_control
.s_maxage
;
2183 new_object
->flags
&= ~OBJECT_FAILED
;
2186 new_object
->date
= date
;
2187 if(last_modified
>= 0)
2188 new_object
->last_modified
= last_modified
;
2190 new_object
->expires
= expires
;
2191 if(new_object
->etag
== NULL
)
2192 new_object
->etag
= etag
;
2198 case 300: case 301: case 302: case 303: case 307:
2199 case 403: case 404: case 405: case 401:
2200 if(new_object
->message
) releaseAtom(new_object
->message
);
2201 new_object
->code
= code
;
2202 new_object
->message
= message
;
2204 case 206: case 304: case 412:
2205 if(new_object
->code
!= 200 || !new_object
->message
) {
2206 if(new_object
->message
) releaseAtom(new_object
->message
);
2207 new_object
->code
= 200;
2208 new_object
->message
= internAtom("OK");
2210 releaseAtom(message
);
2213 if(new_object
->message
) releaseAtom(new_object
->message
);
2214 new_object
->code
= code
;
2215 new_object
->message
= retainAtom(message
);
2219 httpTweakCachability(new_object
);
2222 new_via
= internAtomF("%s %s",
2223 version
== HTTP_11
? "1.1" : "1.0",
2226 new_via
= internAtomF("%s, %s %s", via
->string
,
2227 version
== HTTP_11
? "1.1" : "1.0",
2229 if(new_via
== NULL
) {
2230 do_log(L_ERROR
, "Couldn't allocate Via.\n");
2232 if(new_object
->via
) releaseAtom(new_object
->via
);
2233 new_object
->via
= new_via
;
2236 if(new_object
->flags
& OBJECT_INITIAL
) {
2237 objectPartial(new_object
, full_len
, headers
);
2239 if(new_object
->length
< 0)
2240 new_object
->length
= full_len
;
2241 /* XXX -- RFC 2616 13.5.3 */
2242 releaseAtom(headers
);
2246 assert(new_object
!= old_object
);
2247 supersedeObject(old_object
);
2250 if(new_object
!= old_object
) {
2251 if(new_object
->flags
& OBJECT_INPROGRESS
) {
2252 /* Make sure we don't fetch this object two times at the
2253 same time. Just drop the connection. */
2254 releaseObject(new_object
);
2255 httpServerFinish(connection
, 1, 0);
2258 old_object
->flags
&= ~OBJECT_VALIDATING
;
2259 new_object
->flags
|= OBJECT_INPROGRESS
;
2260 /* Signal the client side to switch to the new object -- see
2261 httpClientGetHandler. If it doesn't, we'll give up on this
2263 new_object
->flags
|= OBJECT_MUTATING
;
2264 request
->can_mutate
= new_object
;
2265 notifyObject(old_object
);
2266 request
->can_mutate
= NULL
;
2267 new_object
->flags
&= ~OBJECT_MUTATING
;
2268 old_object
->flags
&= ~OBJECT_INPROGRESS
;
2269 if(request
->object
== old_object
) {
2270 if(request
->request
)
2271 request
->request
->request
= NULL
;
2272 request
->request
= NULL
;
2273 request
->object
= new_object
;
2275 assert(request
->object
== new_object
);
2277 releaseNotifyObject(old_object
);
2279 object
= new_object
;
2281 objectMetadataChanged(new_object
, 0);
2284 if(object
->flags
& OBJECT_VALIDATING
) {
2285 object
->flags
&= ~OBJECT_VALIDATING
;
2286 notifyObject(object
);
2290 httpServerFinish(connection
, 0, rc
);
2294 if(request
->request
== NULL
) {
2295 httpServerFinish(connection
, 1, 0);
2300 /* 412 replies contain a useless body. For now, we
2301 drop the connection. */
2302 httpServerFinish(connection
, 1, 0);
2307 if(request
->flags
& REQUEST_PERSISTENT
) {
2308 if(request
->method
!= METHOD_HEAD
&&
2309 connection
->te
== TE_IDENTITY
&& len
< 0) {
2310 do_log(L_ERROR
, "Persistent reply with no Content-Length\n");
2311 /* That's potentially dangerous, as we could start reading
2312 arbitrary data into the object. Unfortunately, some
2314 request
->flags
&= ~REQUEST_PERSISTENT
;
2318 /* we're getting a body */
2319 if(content_range
.from
> 0)
2320 connection
->offset
= content_range
.from
;
2322 connection
->offset
= 0;
2324 if(content_range
.to
>= 0)
2325 request
->to
= content_range
.to
;
2327 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2328 (unsigned long)connection
, (unsigned long)object
,
2329 connection
->offset
);
2331 if(connection
->len
> rc
) {
2332 rc
= connectionAddData(connection
, rc
);
2336 do_log(L_ERROR
, "Couldn't parse chunk size.\n");
2337 httpServerAbort(connection
, 1, 502,
2338 internAtom("Couldn't parse chunk size"));
2340 do_log(L_ERROR
, "Couldn't add data to connection.\n");
2341 httpServerAbort(connection
, 1, 500,
2342 internAtom("Couldn't add data "
2348 if(object
->length
< 0) {
2349 object
->length
= object
->size
;
2350 objectMetadataChanged(object
, 0);
2351 } else if(object
->length
!= object
->size
) {
2352 httpServerAbort(connection
, 1, 500,
2353 internAtom("Inconsistent "
2355 object
->length
= -1;
2359 httpServerFinish(connection
, 0, 0);
2364 connection
->len
= 0;
2368 if(connection
->te
== TE_CHUNKED
||
2369 (object
->length
>= 0 &&
2370 connection
->offset
< object
->length
)) {
2371 do_log(L_ERROR
, "Server closed connection.\n");
2372 httpServerAbort(connection
, 1, 502,
2373 internAtom("Server closed connection"));
2376 if(code
!= 206 && eof
> 0 && object
->length
< 0) {
2377 object
->length
= object
->size
;
2378 objectMetadataChanged(object
, 0);
2380 httpServerFinish(connection
, 1, 0);
2384 return httpServerReadData(connection
, 1);
2390 releaseAtom(message
);
2392 releaseAtom(headers
);
2401 httpServerIndirectHandlerCommon(HTTPConnectionPtr connection
, int eof
)
2403 HTTPRequestPtr request
= connection
->request
;
2406 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
2408 if(connection
->len
> 0) {
2410 rc
= connectionAddData(connection
, 0);
2414 do_log(L_ERROR
, "Couldn't parse chunk size.\n");
2415 httpServerAbort(connection
, 1, 502,
2416 internAtom("Couldn't parse chunk size"));
2418 do_log(L_ERROR
, "Couldn't add data to connection.\n");
2419 httpServerAbort(connection
, 1, 500,
2420 internAtom("Couldn't add data "
2425 if(request
->to
< 0) {
2426 if(request
->object
->length
< 0) {
2427 request
->object
->length
= request
->object
->size
;
2428 objectMetadataChanged(request
->object
, 0);
2429 } else if(request
->object
->length
!=
2430 request
->object
->size
) {
2431 request
->object
->length
= -1;
2432 httpServerAbort(connection
, 1, 502,
2433 internAtom("Inconsistent "
2438 httpServerFinish(connection
, 0, 0);
2444 if(eof
&& connection
->len
== 0) {
2445 if(connection
->te
== TE_CHUNKED
||
2446 (request
->to
>= 0 && connection
->offset
< request
->to
)) {
2447 do_log(L_ERROR
, "Server dropped connection.\n");
2448 httpServerAbort(connection
, 1, 502,
2449 internAtom("Server dropped connection"));
2452 if(request
->object
->length
< 0 && eof
> 0 &&
2453 (request
->to
< 0 || request
->to
> request
->object
->size
)) {
2454 request
->object
->length
= request
->object
->size
;
2455 objectMetadataChanged(request
->object
, 0);
2457 httpServerFinish(connection
, 1, 0);
2461 return httpServerReadData(connection
, 0);
2466 httpServerIndirectHandler(int status
,
2467 FdEventHandlerPtr event
,
2468 StreamRequestPtr srequest
)
2470 HTTPConnectionPtr connection
= srequest
->data
;
2471 assert(connection
->request
->object
->flags
& OBJECT_INPROGRESS
);
2473 httpSetTimeout(connection
, -1);
2475 if(status
!= -ECLIENTRESET
)
2476 do_log_error(L_ERROR
, -status
, "Read from server failed");
2477 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
2478 internAtomError(-status
, "Read from server failed"));
2482 connection
->len
= srequest
->offset
;
2484 return httpServerIndirectHandlerCommon(connection
, status
);
2488 httpServerReadData(HTTPConnectionPtr connection
, int immediate
)
2490 HTTPRequestPtr request
= connection
->request
;
2491 ObjectPtr object
= request
->object
;
2494 assert(object
->flags
& OBJECT_INPROGRESS
);
2496 if(request
->request
== NULL
) {
2497 httpServerFinish(connection
, 1, 0);
2501 if(request
->to
>= 0)
2504 to
= object
->length
;
2506 if(to
>= 0 && to
== connection
->offset
) {
2507 httpServerFinish(connection
, 0, 0);
2511 if(connection
->len
== 0 &&
2512 ((connection
->te
== TE_IDENTITY
&& to
> connection
->offset
) ||
2513 (connection
->te
== TE_CHUNKED
&& connection
->chunk_remaining
> 0))) {
2514 /* Read directly into the object */
2515 int i
= connection
->offset
/ CHUNK_SIZE
;
2516 int j
= connection
->offset
% CHUNK_SIZE
;
2518 /* See httpServerDirectHandlerCommon if you change this */
2519 if(connection
->te
== TE_CHUNKED
) {
2520 len
= connection
->chunk_remaining
;
2521 /* The logic here is that we want more to just fit the
2522 chunk header if we're doing a large read, but do a
2523 large read if we would otherwise do a small one. The
2524 magic constant 2000 comes from the assumption that the
2525 server uses chunks that have a size that are a power of
2526 two (possibly including the chunk header), and that we
2527 want a full ethernet packet to fit into our read. */
2528 more
= (len
>= 2000 ? 20 : MIN(2048 - len
, CHUNK_SIZE
));
2530 len
= to
- connection
->offset
;
2531 /* We read more data only when there is a reasonable
2532 chance of there being another reply coming. */
2533 more
= (connection
->pipelined
> 1) ? CHUNK_SIZE
: 0;
2535 end
= len
+ connection
->offset
;
2537 httpConnectionDestroyBuf(connection
);
2539 /* The order of allocation is important in case we run out of
2541 lockChunk(object
, i
);
2542 if(object
->chunks
[i
].data
== NULL
)
2543 object
->chunks
[i
].data
= get_chunk();
2544 if(object
->chunks
[i
].data
&& object
->chunks
[i
].size
>= j
) {
2545 if(len
+ j
> CHUNK_SIZE
) {
2546 lockChunk(object
, i
+ 1);
2547 if(object
->chunks
[i
+ 1].data
== NULL
)
2548 object
->chunks
[i
+ 1].data
= get_chunk();
2549 /* Unless we're grabbing all len of data, we do not
2550 want to do an indirect read immediately afterwards. */
2551 if(more
&& len
+ j
<= 2 * CHUNK_SIZE
) {
2552 if(!connection
->buf
)
2553 connection
->buf
= get_chunk(); /* checked below */
2555 if(object
->chunks
[i
+ 1].data
) {
2556 do_stream_3(IO_READ
| IO_NOTNOW
, connection
->fd
, j
,
2557 object
->chunks
[i
].data
, CHUNK_SIZE
,
2558 object
->chunks
[i
+ 1].data
,
2560 end
- (i
+ 1) * CHUNK_SIZE
),
2561 connection
->buf
, connection
->buf
? more
: 0,
2562 httpServerDirectHandler2
, connection
);
2565 unlockChunk(object
, i
+ 1);
2567 if(more
&& len
+ j
<= CHUNK_SIZE
) {
2568 if(!connection
->buf
)
2569 connection
->buf
= get_chunk();
2571 do_stream_2(IO_READ
| IO_NOTNOW
, connection
->fd
, j
,
2572 object
->chunks
[i
].data
,
2573 MIN(CHUNK_SIZE
, end
- i
* CHUNK_SIZE
),
2574 connection
->buf
, connection
->buf
? more
: 0,
2575 httpServerDirectHandler
, connection
);
2578 unlockChunk(object
, i
);
2582 if(connection
->len
== 0)
2583 httpConnectionDestroyBuf(connection
);
2585 httpSetTimeout(connection
, serverTimeout
);
2586 do_stream_buf(IO_READ
| IO_NOTNOW
|
2587 ((immediate
&& connection
->len
) ? IO_IMMEDIATE
: 0),
2588 connection
->fd
, connection
->len
,
2590 (connection
->te
== TE_CHUNKED
?
2591 MIN(2048, CHUNK_SIZE
) : CHUNK_SIZE
),
2592 httpServerIndirectHandler
, connection
);
2597 httpServerDirectHandlerCommon(int kind
, int status
,
2598 FdEventHandlerPtr event
,
2599 StreamRequestPtr srequest
)
2601 HTTPConnectionPtr connection
= srequest
->data
;
2602 HTTPRequestPtr request
= connection
->request
;
2603 ObjectPtr object
= request
->object
;
2604 int i
= connection
->offset
/ CHUNK_SIZE
;
2607 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
2609 httpSetTimeout(connection
, -1);
2612 unlockChunk(object
, i
);
2613 if(kind
== 2) unlockChunk(object
, i
+ 1);
2614 if(status
!= -ECLIENTRESET
)
2615 do_log_error(L_ERROR
, -status
, "Read from server failed");
2616 httpServerAbort(connection
, status
!= -ECLIENTRESET
, 502,
2617 internAtomError(-status
, "Read from server failed"));
2621 /* We have incestuous knowledge of the decisions made in
2622 httpServerReadData */
2623 if(request
->to
>= 0)
2626 to
= object
->length
;
2627 if(connection
->te
== TE_CHUNKED
)
2628 end
= connection
->offset
+ connection
->chunk_remaining
;
2631 /* The amount of data actually read into the object */
2632 end1
= MIN(end
, i
* CHUNK_SIZE
+ MIN(kind
* CHUNK_SIZE
, srequest
->offset
));
2635 assert(end1
>= i
* CHUNK_SIZE
);
2636 assert(end1
- 2 * CHUNK_SIZE
<= i
* CHUNK_SIZE
);
2638 object
->chunks
[i
].size
=
2639 MAX(object
->chunks
[i
].size
, MIN(end1
- i
* CHUNK_SIZE
, CHUNK_SIZE
));
2640 if(kind
== 2 && end1
> (i
+ 1) * CHUNK_SIZE
) {
2641 object
->chunks
[i
+ 1].size
=
2642 MAX(object
->chunks
[i
+ 1].size
, end1
- (i
+ 1) * CHUNK_SIZE
);
2644 if(connection
->te
== TE_CHUNKED
) {
2645 connection
->chunk_remaining
-= (end1
- connection
->offset
);
2646 assert(connection
->chunk_remaining
>= 0);
2648 connection
->offset
= end1
;
2649 object
->size
= MAX(object
->size
, end1
);
2650 unlockChunk(object
, i
);
2651 if(kind
== 2) unlockChunk(object
, i
+ 1);
2653 if(i
* CHUNK_SIZE
+ srequest
->offset
> end1
) {
2654 connection
->len
= i
* CHUNK_SIZE
+ srequest
->offset
- end1
;
2655 return httpServerIndirectHandlerCommon(connection
, status
);
2657 notifyObject(object
);
2659 httpServerFinish(connection
, 1, 0);
2662 return httpServerReadData(connection
, 0);
2668 httpServerDirectHandler(int status
,
2669 FdEventHandlerPtr event
,
2670 StreamRequestPtr srequest
)
2672 return httpServerDirectHandlerCommon(1, status
, event
, srequest
);
2676 httpServerDirectHandler2(int status
,
2677 FdEventHandlerPtr event
,
2678 StreamRequestPtr srequest
)
2680 return httpServerDirectHandlerCommon(2, status
, event
, srequest
);
2683 /* Add the data accumulated in connection->buf into the object in
2684 connection->request. Returns 0 in the normal case, 1 if the TE is
2685 self-terminating and we're done, -1 if there was a problem with
2686 objectAddData, -2 if there was a problem with the data. */
2688 connectionAddData(HTTPConnectionPtr connection
, int skip
)
2690 HTTPRequestPtr request
= connection
->request
;
2691 ObjectPtr object
= request
->object
;
2694 if(connection
->te
== TE_IDENTITY
) {
2697 len
= connection
->len
- skip
;
2698 if(object
->length
>= 0) {
2699 len
= MIN(object
->length
- connection
->offset
, len
);
2701 if(request
->to
>= 0)
2702 len
= MIN(request
->to
- connection
->offset
, len
);
2704 rc
= objectAddData(object
, connection
->buf
+ skip
,
2705 connection
->offset
, len
);
2708 connection
->offset
+= len
;
2709 connection
->len
-= (len
+ skip
);
2710 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2711 (unsigned long)connection
, (unsigned long)object
,
2712 connection
->offset
);
2715 if(connection
->len
> 0 && skip
+ len
> 0) {
2716 memmove(connection
->buf
,
2717 connection
->buf
+ skip
+ len
, connection
->len
);
2720 if((object
->length
>= 0 && object
->length
<= connection
->offset
) ||
2721 (request
->to
>= 0 && request
->to
<= connection
->offset
)) {
2722 notifyObject(object
);
2726 notifyObject(object
);
2729 } else if(connection
->te
== TE_CHUNKED
) {
2730 int i
= skip
, j
, size
;
2731 /* connection->chunk_remaining is 0 at the end of a chunk, -1
2732 after the CR/LF pair ending a chunk, and -2 after we've
2733 seen a chunk of length 0. */
2734 if(connection
->chunk_remaining
> -2) {
2736 if(connection
->chunk_remaining
<= 0) {
2737 if(connection
->chunk_remaining
== 0) {
2738 if(connection
->len
< i
+ 2)
2740 if(connection
->buf
[i
] != '\r' ||
2741 connection
->buf
[i
+ 1] != '\n')
2744 connection
->chunk_remaining
= -1;
2746 if(connection
->len
< i
+ 2)
2748 j
= parseChunkSize(connection
->buf
, i
,
2749 connection
->len
, &size
);
2757 connection
->chunk_remaining
= -2;
2760 connection
->chunk_remaining
= size
;
2763 /* connection->chunk_remaining > 0 */
2764 size
= MIN(connection
->chunk_remaining
,
2765 connection
->len
- i
);
2768 rc
= objectAddData(object
, connection
->buf
+ i
,
2769 connection
->offset
, size
);
2770 connection
->offset
+= size
;
2774 connection
->chunk_remaining
-= size
;
2775 do_log(D_SERVER_OFFSET
, "0x%lx(0x%lx): offset = %d\n",
2776 (unsigned long)connection
,
2777 (unsigned long)object
,
2778 connection
->offset
);
2782 connection
->len
-= i
;
2783 if(connection
->len
> 0)
2784 memmove(connection
->buf
, connection
->buf
+ i
, connection
->len
);
2785 if(i
> 0 || connection
->chunk_remaining
== -2)
2786 notifyObject(object
);
2787 if(connection
->chunk_remaining
== -2)
2797 listServers(FILE *out
)
2799 HTTPServerPtr server
;
2802 fprintf(out
, "<!DOCTYPE HTML PUBLIC "
2803 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2804 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2806 "\r\n<title>Known servers</title>\n"
2808 "<h1>Known servers</h1>\n");
2810 alternatingHttpStyle(out
, "servers");
2811 fprintf(out
, "<table id=servers>\n");
2812 fprintf(out
, "<thead><tr><th>Server</th>"
2814 "<th>Persistent</th>"
2816 "<th>Connections</th>"
2821 fprintf(out
, "<tbody>\n");
2825 fprintf(out
, "<tr class=\"%s\">", entry
% 2 == 0 ? "even" : "odd");
2826 if(server
->port
== 80)
2827 fprintf(out
, "<td>%s</td>", server
->name
);
2829 fprintf(out
, "<td>%s:%d</td>", server
->name
, server
->port
);
2831 if(server
->version
== HTTP_11
)
2832 fprintf(out
, "<td>1.1</td>");
2833 else if(server
->version
== HTTP_10
)
2834 fprintf(out
, "<td>1.0</td>");
2836 fprintf(out
, "<td>unknown</td>");
2838 if(server
->persistent
< 0)
2839 fprintf(out
, "<td>no</td>");
2840 else if(server
->persistent
> 0)
2841 fprintf(out
, "<td>yes</td>");
2843 fprintf(out
, "<td>unknown</td>");
2845 if(server
->version
!= HTTP_11
|| server
->persistent
<= 0)
2846 fprintf(out
, "<td></td>");
2847 else if(server
->pipeline
< 0)
2848 fprintf(out
, "<td>no</td>");
2849 else if(server
->pipeline
>= 0 && server
->pipeline
<= 1)
2850 fprintf(out
, "<td>unknown</td>");
2851 else if(server
->pipeline
== 2 || server
->pipeline
== 3)
2852 fprintf(out
, "<td>probing</td>");
2854 fprintf(out
, "<td>yes</td>");
2857 for(i
= 0; i
< server
->maxslots
; i
++)
2858 if(server
->connection
[i
] && !server
->connection
[i
]->connecting
) {
2859 if(i
< server
->numslots
)
2865 fprintf(out
, "<td>%d/%d", n
, server
->numslots
);
2867 fprintf(out
, " + %d</td>", m
);
2869 fprintf(out
, "</td>");
2871 if(server
->lies
> 0)
2872 fprintf(out
, "<td>(%d lies)</td>", (server
->lies
+ 9) / 10);
2874 fprintf(out
, "<td></td>");
2877 fprintf(out
, "<td>%.3f</td>", (double)server
->rtt
/ 1000000.0);
2879 fprintf(out
, "<td></td>");
2880 if(server
->rate
> 0)
2881 fprintf(out
, "<td>%d</td>", server
->rate
);
2883 fprintf(out
, "<td></td>");
2885 fprintf(out
, "</tr>\n");
2886 server
= server
->next
;
2889 fprintf(out
, "</tbody>\n");
2890 fprintf(out
, "</table>\n");
2891 fprintf(out
, "<p><a href=\"/polipo/\">back</a></p>");
2892 fprintf(out
, "</body></html>\n");