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
26 httpAcceptAgain(TimeEventHandlerPtr event
)
28 FdEventHandlerPtr newevent
;
29 int fd
= *(int*)event
->data
;
31 newevent
= schedule_accept(fd
, httpAccept
, NULL
);
32 if(newevent
== NULL
) {
34 newevent
= schedule_accept(fd
, httpAccept
, NULL
);
35 if(newevent
== NULL
) {
36 do_log(L_ERROR
, "Couldn't schedule accept.\n");
44 httpAccept(int fd
, FdEventHandlerPtr event
, AcceptRequestPtr request
)
47 HTTPConnectionPtr connection
;
48 TimeEventHandlerPtr timeout
;
51 if(-fd
== EINTR
|| -fd
== EAGAIN
|| -fd
== EWOULDBLOCK
)
53 do_log_error(L_ERROR
, -fd
, "Couldn't establish listening socket");
54 if(-fd
== EMFILE
|| -fd
== ENOMEM
|| -fd
== ENOBUFS
) {
55 TimeEventHandlerPtr again
= NULL
;
56 do_log(L_WARN
, "Refusing client connections for one second.\n");
58 again
= scheduleTimeEvent(1, httpAcceptAgain
,
59 sizeof(request
->fd
), &request
->fd
);
61 do_log(L_ERROR
, "Couldn't schedule accept -- sleeping.\n");
63 again
= scheduleTimeEvent(1, httpAcceptAgain
,
64 sizeof(request
->fd
), &request
->fd
);
66 do_log(L_ERROR
, "Couldn't schedule accept -- aborting.\n");
78 if(netAddressMatch(fd
, allowedNets
) != 1) {
79 do_log(L_WARN
, "Refusing connection from unauthorised net\n");
85 rc
= setNonblocking(fd
, 1);
87 do_log_error(L_WARN
, errno
, "Couldn't set non blocking mode");
91 rc
= setNodelay(fd
, 1);
93 do_log_error(L_WARN
, errno
, "Couldn't disable Nagle's algorithm");
95 connection
= httpMakeConnection();
97 timeout
= scheduleTimeEvent(clientTimeout
, httpTimeoutHandler
,
98 sizeof(connection
), &connection
);
106 connection
->timeout
= timeout
;
108 do_log(D_CLIENT_CONN
, "Accepted client connection 0x%lx\n",
109 (unsigned long)connection
);
111 connection
->flags
= CONN_READER
;
113 do_stream_buf(IO_READ
| IO_NOTNOW
, connection
->fd
, 0,
114 &connection
->reqbuf
, CHUNK_SIZE
,
115 httpClientHandler
, connection
);
119 /* Abort a client connection. It is only safe to abort the requests
120 if we know the connection is closed. */
122 httpClientAbort(HTTPConnectionPtr connection
, int closed
)
124 HTTPRequestPtr request
= connection
->request
;
126 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLOUT
);
129 if(request
->chandler
) {
130 request
->error_code
= 500;
131 request
->error_message
= internAtom("Connection finishing");
132 abortConditionHandler(request
->chandler
);
133 request
->chandler
= NULL
;
135 request
= request
->next
;
140 /* s != 0 specifies that the connection must be shut down. It is 1 in
141 order to linger the connection, 2 to close it straight away. */
143 httpClientFinish(HTTPConnectionPtr connection
, int s
)
145 HTTPRequestPtr request
= connection
->request
;
147 assert(!(request
&& request
->request
148 && request
->request
->request
!= request
));
151 if(!request
|| !(request
->flags
& REQUEST_PERSISTENT
))
155 httpConnectionDestroyBuf(connection
);
157 connection
->flags
&= ~CONN_WRITER
;
159 if(connection
->flags
& CONN_SIDE_READER
) {
160 /* We're in POST or PUT and the reader isn't done yet.
161 Wait for the read side to close the connection. */
162 assert(request
&& (connection
->flags
& CONN_READER
));
164 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLIN
);
166 pokeFdEvent(connection
->fd
, -EDOGRACEFUL
, POLLIN
);
171 if(connection
->timeout
)
172 cancelTimeEvent(connection
->timeout
);
173 connection
->timeout
= NULL
;
176 HTTPRequestPtr requestee
;
178 requestee
= request
->request
;
180 request
->request
= NULL
;
181 requestee
->request
= NULL
;
184 httpServerClientReset(requestee
);
185 if(request
->chandler
) {
186 request
->error_code
= 500;
187 request
->error_message
= internAtom("Connection finishing");
188 abortConditionHandler(request
->chandler
);
189 request
->chandler
= NULL
;
192 if(request
->object
) {
193 if(request
->object
->requestor
== request
)
194 request
->object
->requestor
= NULL
;
195 releaseObject(request
->object
);
196 request
->object
= NULL
;
198 httpDequeueRequest(connection
);
199 httpDestroyRequest(request
);
204 connection
->len
= -1;
205 connection
->offset
= 0;
206 connection
->te
= TE_IDENTITY
;
209 assert(connection
->fd
> 0);
210 connection
->serviced
++;
211 httpSetTimeout(connection
, clientTimeout
);
212 if(!connection
->flags
& CONN_READER
) {
213 if(connection
->reqlen
== 0)
214 httpConnectionDestroyReqbuf(connection
);
215 else if((connection
->flags
& CONN_BIGREQBUF
) &&
216 connection
->reqlen
< CHUNK_SIZE
)
217 httpConnectionUnbigifyReqbuf(connection
);
218 connection
->flags
|= CONN_READER
;
219 httpSetTimeout(connection
, clientTimeout
);
220 do_stream_buf(IO_READ
| IO_NOTNOW
|
221 (connection
->reqlen
? IO_IMMEDIATE
: 0),
222 connection
->fd
, connection
->reqlen
,
224 (connection
->flags
& CONN_BIGREQBUF
) ?
225 bigBufferSize
: CHUNK_SIZE
,
226 httpClientHandler
, connection
);
228 /* The request has already been validated when it first got
230 if(connection
->request
) {
231 if(connection
->request
->object
!= NULL
)
232 httpClientNoticeRequest(connection
->request
, 1);
234 assert(connection
->flags
& CONN_READER
);
239 do_log(D_CLIENT_CONN
, "Closing client connection 0x%lx\n",
240 (unsigned long)connection
);
242 if(connection
->flags
& CONN_READER
) {
243 httpSetTimeout(connection
, 10);
244 if(connection
->fd
< 0) return;
246 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLIN
);
248 pokeFdEvent(connection
->fd
, -EDOGRACEFUL
, POLLIN
);
253 HTTPRequestPtr requestee
;
254 request
= connection
->request
;
257 requestee
= request
->request
;
258 request
->request
= NULL
;
260 requestee
->request
= NULL
;
261 httpServerClientReset(requestee
);
263 if(request
->chandler
)
264 abortConditionHandler(request
->chandler
);
265 request
->chandler
= NULL
;
266 if(request
->object
&& request
->object
->requestor
== request
)
267 request
->object
->requestor
= NULL
;
268 httpDequeueRequest(connection
);
269 httpDestroyRequest(request
);
271 httpConnectionDestroyReqbuf(connection
);
272 if(connection
->timeout
)
273 cancelTimeEvent(connection
->timeout
);
274 connection
->timeout
= NULL
;
275 if(connection
->fd
>= 0) {
277 CLOSE(connection
->fd
);
279 lingeringClose(connection
->fd
);
285 /* Extremely baroque implementation of close: we need to synchronise
286 between the writer and the reader. */
288 static char client_shutdown_buffer
[17];
290 static int httpClientDelayedShutdownHandler(TimeEventHandlerPtr
);
293 httpClientDelayedShutdown(HTTPConnectionPtr connection
)
295 TimeEventHandlerPtr handler
;
297 assert(connection
->flags
& CONN_READER
);
298 handler
= scheduleTimeEvent(1, httpClientDelayedShutdownHandler
,
299 sizeof(connection
), &connection
);
302 "Couldn't schedule delayed shutdown -- freeing memory.");
304 handler
= scheduleTimeEvent(1, httpClientDelayedShutdownHandler
,
305 sizeof(connection
), &connection
);
308 "Couldn't schedule delayed shutdown -- aborting.\n");
316 httpClientShutdownHandler(int status
,
317 FdEventHandlerPtr event
, StreamRequestPtr request
)
319 HTTPConnectionPtr connection
= request
->data
;
321 assert(connection
->flags
& CONN_READER
);
323 if(!(connection
->flags
& CONN_WRITER
)) {
324 connection
->flags
&= ~CONN_READER
;
325 connection
->reqlen
= 0;
326 httpConnectionDestroyReqbuf(connection
);
327 if(status
&& status
!= -EDOGRACEFUL
)
328 httpClientFinish(connection
, 2);
330 httpClientFinish(connection
, 1);
334 httpClientDelayedShutdown(connection
);
339 httpClientDelayedShutdownHandler(TimeEventHandlerPtr event
)
341 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
342 assert(connection
->flags
& CONN_READER
);
344 if(!(connection
->flags
& CONN_WRITER
)) {
345 connection
->flags
&= ~CONN_READER
;
346 connection
->reqlen
= 0;
347 httpConnectionDestroyReqbuf(connection
);
348 httpClientFinish(connection
, 1);
351 do_stream(IO_READ
| IO_NOTNOW
, connection
->fd
,
352 0, client_shutdown_buffer
, 17,
353 httpClientShutdownHandler
, connection
);
358 httpClientHandler(int status
,
359 FdEventHandlerPtr event
, StreamRequestPtr request
)
361 HTTPConnectionPtr connection
= request
->data
;
364 (connection
->flags
& CONN_BIGREQBUF
) ? connection
->reqlen
: CHUNK_SIZE
;
366 assert(connection
->flags
& CONN_READER
);
368 /* There's no point trying to do something with this request if
369 the client has shut the connection down -- HTTP doesn't do
370 half-open connections. */
372 connection
->reqlen
= 0;
373 httpConnectionDestroyReqbuf(connection
);
374 if(!(connection
->flags
& CONN_WRITER
)) {
375 connection
->flags
&= ~CONN_READER
;
376 if(status
> 0 || status
== -ECONNRESET
|| status
== -EDOSHUTDOWN
)
377 httpClientFinish(connection
, 2);
379 httpClientFinish(connection
, 1);
382 httpClientAbort(connection
, status
> 0 || status
== -ECONNRESET
);
383 connection
->flags
&= ~CONN_READER
;
387 i
= findEndOfHeaders(connection
->reqbuf
, 0, request
->offset
, &body
);
388 connection
->reqlen
= request
->offset
;
391 connection
->reqbegin
= i
;
392 httpClientHandlerHeaders(event
, request
, connection
);
397 if(connection
->reqlen
> 0) {
398 if(connection
->serviced
<= 0)
399 do_log(L_ERROR
, "Client dropped connection.\n");
401 do_log(D_CLIENT_CONN
, "Client dropped idle connection.\n");
403 connection
->flags
&= ~CONN_READER
;
404 if(!connection
->request
)
405 httpClientFinish(connection
, 2);
407 pokeFdEvent(connection
->fd
, -EDOGRACEFUL
, POLLOUT
);
411 if(connection
->reqlen
>= bufsize
) {
413 if(!(connection
->flags
& CONN_BIGREQBUF
))
414 rc
= httpConnectionBigifyReqbuf(connection
);
416 do_stream(IO_READ
, connection
->fd
, connection
->reqlen
,
417 connection
->reqbuf
, bigBufferSize
,
418 httpClientHandler
, connection
);
421 connection
->reqlen
= 0;
422 httpConnectionDestroyReqbuf(connection
);
424 do_log(L_ERROR
, "Couldn't allocate big buffer.\n");
425 httpClientNewError(connection
, METHOD_UNKNOWN
, 0, 400,
426 internAtom("Couldn't allocate big buffer"));
428 do_log(L_ERROR
, "Couldn't find end of client's headers.\n");
429 httpClientNewError(connection
, METHOD_UNKNOWN
, 0, 400,
430 internAtom("Couldn't find end of headers"));
434 httpSetTimeout(connection
, clientTimeout
);
439 httpClientRawErrorHeaders(HTTPConnectionPtr connection
,
440 int code
, AtomPtr message
,
441 int close
, AtomPtr headers
)
443 int fd
= connection
->fd
;
445 char *url
; int url_len
;
448 assert(connection
->flags
& CONN_WRITER
);
452 if(connection
->request
)
454 close
|| !(connection
->request
->flags
& REQUEST_PERSISTENT
);
458 if(connection
->request
&& connection
->request
->object
) {
459 url
= connection
->request
->object
->key
;
460 url_len
= connection
->request
->object
->key_size
;
461 etag
= connection
->request
->object
->etag
;
468 if(connection
->buf
== NULL
) {
469 connection
->buf
= get_chunk();
470 if(connection
->buf
== NULL
) {
471 httpClientFinish(connection
, 1);
476 n
= httpWriteErrorHeaders(connection
->buf
, CHUNK_SIZE
, 0,
477 connection
->request
&&
478 connection
->request
->method
!= METHOD_HEAD
,
479 code
, message
, close
> 0, headers
,
482 shutdown(connection
->fd
, 1);
484 httpClientFinish(connection
, 1);
488 httpSetTimeout(connection
, clientTimeout
);
489 do_stream(IO_WRITE
, fd
, 0, connection
->buf
, n
,
490 close
> 0 ? httpErrorStreamHandler
:
491 close
== 0 ? httpErrorNocloseStreamHandler
:
492 httpErrorNofinishStreamHandler
,
499 httpClientRawError(HTTPConnectionPtr connection
, int code
, AtomPtr message
,
502 return httpClientRawErrorHeaders(connection
, code
, message
, close
, NULL
);
506 httpClientNoticeErrorHeaders(HTTPRequestPtr request
, int code
, AtomPtr message
,
509 if(request
->error_message
)
510 releaseAtom(request
->error_message
);
511 if(request
->error_headers
)
512 releaseAtom(request
->error_headers
);
513 request
->error_code
= code
;
514 request
->error_message
= message
;
515 request
->error_headers
= headers
;
516 httpClientNoticeRequest(request
, 0);
521 httpClientNoticeError(HTTPRequestPtr request
, int code
, AtomPtr message
)
523 return httpClientNoticeErrorHeaders(request
, code
, message
, NULL
);
527 httpClientError(HTTPRequestPtr request
, int code
, AtomPtr message
)
529 if(request
->error_message
)
530 releaseAtom(request
->error_message
);
531 request
->error_code
= code
;
532 request
->error_message
= message
;
533 if(request
->chandler
) {
534 abortConditionHandler(request
->chandler
);
535 request
->chandler
= NULL
;
536 } else if(request
->object
)
537 notifyObject(request
->object
);
541 /* This may be called from object handlers. */
543 httpClientLeanError(HTTPRequestPtr request
, int code
, AtomPtr message
)
545 if(request
->error_message
)
546 releaseAtom(request
->error_message
);
547 request
->error_code
= code
;
548 request
->error_message
= message
;
554 httpClientNewError(HTTPConnectionPtr connection
, int method
, int persist
,
555 int code
, AtomPtr message
)
557 HTTPRequestPtr request
;
558 request
= httpMakeRequest();
559 if(request
== NULL
) {
560 do_log(L_ERROR
, "Couldn't allocate error request.\n");
561 httpClientFinish(connection
, 1);
564 request
->method
= method
;
566 request
->flags
|= REQUEST_PERSISTENT
;
568 request
->flags
&= ~REQUEST_PERSISTENT
;
569 request
->error_code
= code
;
570 request
->error_message
= message
;
572 httpQueueRequest(connection
, request
);
573 httpClientNoticeRequest(request
, 0);
578 httpErrorStreamHandler(int status
,
579 FdEventHandlerPtr event
,
580 StreamRequestPtr srequest
)
582 HTTPConnectionPtr connection
= srequest
->data
;
584 if(status
== 0 && !streamRequestDone(srequest
))
587 httpClientFinish(connection
, 1);
592 httpErrorNocloseStreamHandler(int status
,
593 FdEventHandlerPtr event
,
594 StreamRequestPtr srequest
)
596 HTTPConnectionPtr connection
= srequest
->data
;
598 if(status
== 0 && !streamRequestDone(srequest
))
601 httpClientFinish(connection
, 0);
606 httpErrorNofinishStreamHandler(int status
,
607 FdEventHandlerPtr event
,
608 StreamRequestPtr srequest
)
610 if(status
== 0 && !streamRequestDone(srequest
))
617 httpClientHandlerHeaders(FdEventHandlerPtr event
, StreamRequestPtr srequest
,
618 HTTPConnectionPtr connection
)
620 HTTPRequestPtr request
;
629 /* Work around clients working around NCSA lossage. */
630 if(connection
->reqbuf
[0] == '\n')
632 else if(connection
->reqbuf
[0] == '\r' && connection
->reqbuf
[1] == '\n')
635 httpSetTimeout(connection
, -1);
636 rc
= httpParseClientFirstLine(connection
->reqbuf
, start
,
637 &method
, &url
, &version
);
639 do_log(L_ERROR
, "Couldn't parse client's request line\n");
641 message
= internAtom("Error in request line");
645 do_log(D_CLIENT_REQ
, "Client request: ");
646 do_log_n(D_CLIENT_REQ
, connection
->reqbuf
, rc
- 1);
647 do_log(D_CLIENT_REQ
, "\n");
649 if(version
!= HTTP_10
&& version
!= HTTP_11
) {
650 do_log(L_ERROR
, "Unknown client HTTP version\n");
652 message
= internAtom("Error in first request line");
656 if(method
== METHOD_UNKNOWN
) {
658 message
= internAtom("Method not implemented");
662 request
= httpMakeRequest();
663 if(request
== NULL
) {
664 do_log(L_ERROR
, "Couldn't allocate client request.\n");
666 message
= internAtom("Couldn't allocate client request");
670 if(connection
->version
!= HTTP_UNKNOWN
&& version
!= connection
->version
) {
671 do_log(L_WARN
, "Client version changed!\n");
674 connection
->version
= version
;
675 request
->flags
= REQUEST_PERSISTENT
;
676 request
->method
= method
;
677 request
->cache_control
= no_cache_control
;
678 httpQueueRequest(connection
, request
);
679 connection
->reqbegin
= rc
;
680 return httpClientRequest(request
, url
);
683 if(url
) releaseAtom(url
);
684 shutdown(connection
->fd
, 0);
685 connection
->reqlen
= 0;
686 connection
->reqbegin
= 0;
687 httpConnectionDestroyReqbuf(connection
);
688 connection
->flags
&= ~CONN_READER
;
689 httpClientNewError(connection
, METHOD_UNKNOWN
, 0, code
, message
);
695 httpClientRequestDelayed(TimeEventHandlerPtr event
)
697 HTTPRequestPtr request
= *(HTTPRequestPtr
*)event
->data
;
699 url
= internAtomN(request
->object
->key
, request
->object
->key_size
);
701 do_log(L_ERROR
, "Couldn't allocate url.\n");
702 abortObject(request
->object
, 503, internAtom("Couldn't allocate url"));
705 httpClientRequest(request
, url
);
710 delayedHttpClientRequest(HTTPRequestPtr request
)
712 TimeEventHandlerPtr event
;
713 event
= scheduleTimeEvent(-1, httpClientRequestDelayed
,
714 sizeof(request
), &request
);
721 httpClientRequest(HTTPRequestPtr request
, AtomPtr url
)
723 HTTPConnectionPtr connection
= request
->connection
;
725 int body_len
, body_te
;
727 CacheControlRec cache_control
;
728 AtomPtr via
, expect
, auth
;
729 HTTPConditionPtr condition
;
732 assert(!request
->chandler
);
733 assert(connection
->reqbuf
);
735 i
= httpParseHeaders(1, url
,
736 connection
->reqbuf
, connection
->reqbegin
, request
,
738 &cache_control
, &condition
, &body_te
,
739 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
740 &expect
, &range
, NULL
, NULL
, &via
, &auth
);
743 do_log(L_ERROR
, "Couldn't parse client headers.\n");
744 shutdown(connection
->fd
, 0);
745 request
->flags
&= ~REQUEST_PERSISTENT
;
746 connection
->flags
&= ~CONN_READER
;
747 httpClientNoticeError(request
, 503,
748 internAtom("Couldn't parse client headers"));
752 connection
->reqbegin
= i
;
755 if(request
->method
== METHOD_GET
|| request
->method
== METHOD_HEAD
)
758 connection
->bodylen
= body_len
;
759 connection
->reqte
= body_te
;
762 AtomPtr message
= NULL
;
763 AtomPtr challenge
= NULL
;
764 int code
= checkClientAuth(auth
, url
, &message
, &challenge
);
774 request
->flags
|= REQUEST_FORCE_ERROR
;
775 httpClientDiscardBody(connection
);
776 httpClientNoticeErrorHeaders(request
, code
, message
, challenge
);
787 if(expect
== atom100Continue
&& REQUEST_SIDE(request
)) {
788 request
->flags
|= REQUEST_WAIT_CONTINUE
;
790 httpClientDiscardBody(connection
);
791 httpClientNoticeError(request
, 417,
792 internAtom("Expectation failed"));
799 request
->from
= range
.from
< 0 ? 0 : range
.from
;
800 request
->to
= range
.to
;
801 request
->cache_control
= cache_control
;
803 request
->headers
= headers
;
804 request
->condition
= condition
;
805 request
->object
= NULL
;
807 if(connection
->serviced
> 500)
808 request
->flags
&= ~REQUEST_PERSISTENT
;
810 if(request
->method
== METHOD_CONNECT
) {
811 if(connection
->flags
& CONN_WRITER
) {
813 httpClientDiscardBody(connection
);
814 httpClientNoticeError(request
, 500,
815 internAtom("Pipelined CONNECT "
819 if(connection
->flags
& CONN_BIGREQBUF
) {
821 httpClientDiscardBody(connection
);
822 httpClientNoticeError(request
, 500,
823 internAtom("CONNECT over big buffer "
827 connection
->flags
&= ~CONN_READER
;
828 do_tunnel(connection
->fd
, connection
->reqbuf
,
829 connection
->reqbegin
, connection
->reqlen
, url
);
831 connection
->reqbuf
= NULL
;
832 connection
->reqlen
= 0;
833 connection
->reqbegin
= 0;
834 httpClientFinish(connection
, 2);
838 rc
= urlForbidden(url
, httpClientRequestContinue
, request
);
840 do_log(L_ERROR
, "Couldn't schedule httpClientRequestContinue.\n");
841 httpClientDiscardBody(connection
);
842 httpClientNoticeError(request
, 500,
843 internAtom("Couldn't schedule "
844 "httpClientRequestContinue"));
851 httpClientRequestContinue(int forbidden_code
, AtomPtr url
,
852 AtomPtr forbidden_message
, AtomPtr forbidden_headers
,
855 HTTPRequestPtr request
= (HTTPRequestPtr
)closure
;
856 HTTPConnectionPtr connection
= request
->connection
;
857 RequestFunction requestfn
;
858 ObjectPtr object
= NULL
;
860 if(forbidden_code
< 0) {
862 httpClientDiscardBody(connection
);
863 httpClientNoticeError(request
, 500,
864 internAtomError(-forbidden_code
,
865 "Couldn't test for forbidden "
872 httpClientDiscardBody(connection
);
873 httpClientNoticeErrorHeaders(request
,
874 forbidden_code
, forbidden_message
,
880 urlIsLocal(url
->string
, url
->length
) ?
884 if(request
->method
== METHOD_POST
|| request
->method
== METHOD_PUT
) {
886 object
= findObject(OBJECT_HTTP
, url
->string
, url
->length
);
888 privatiseObject(object
, 0);
889 releaseObject(object
);
892 request
->object
= makeObject(OBJECT_HTTP
, url
->string
, url
->length
,
893 0, 0, requestfn
, NULL
);
894 if(request
->object
== NULL
) {
895 httpClientDiscardBody(connection
);
896 httpClientNoticeError(request
, 503,
897 internAtom("Couldn't allocate object"));
900 if(requestfn
== httpLocalRequest
)
901 request
->object
->flags
|= OBJECT_LOCAL
;
902 return httpClientSideRequest(request
);
905 if(request
->cache_control
.flags
& CACHE_AUTHORIZATION
) {
907 object
= makeObject(OBJECT_HTTP
, url
->string
, url
->length
, 0, 0,
909 if(object
&& object
->flags
!= OBJECT_INITIAL
) {
910 if(!(object
->cache_control
& CACHE_PUBLIC
)) {
911 privatiseObject(object
, 0);
912 releaseObject(object
);
917 } while(object
== NULL
);
919 object
->flags
|= OBJECT_LINEAR
;
921 object
= findObject(OBJECT_HTTP
, url
->string
, url
->length
);
923 object
= makeObject(OBJECT_HTTP
, url
->string
, url
->length
, 1, 1,
930 do_log(L_ERROR
, "Couldn't allocate object.\n");
931 httpClientDiscardBody(connection
);
932 httpClientNoticeError(request
, 503,
933 internAtom("Couldn't allocate object"));
937 if(object
->request
== httpLocalRequest
) {
938 object
->flags
|= OBJECT_LOCAL
;
941 httpClientDiscardBody(connection
);
942 httpClientNoticeError(request
, 403,
943 internAtom("Proxying disabled"));
944 releaseObject(object
);
948 if(!checkVia(proxyName
, request
->via
)) {
949 httpClientDiscardBody(connection
);
950 httpClientNoticeError(request
, 504,
951 internAtom("Proxy loop detected"));
952 releaseObject(object
);
957 request
->object
= object
;
959 httpClientDiscardBody(connection
);
960 httpClientNoticeRequest(request
, 0);
964 static int httpClientDelayed(TimeEventHandlerPtr handler
);
967 httpClientDiscardBody(HTTPConnectionPtr connection
)
969 TimeEventHandlerPtr handler
;
971 assert(connection
->reqoffset
== 0);
972 assert(connection
->flags
& CONN_READER
);
974 if(connection
->reqte
!= TE_IDENTITY
)
977 if(connection
->bodylen
< 0)
980 if(connection
->bodylen
+ connection
->reqbegin
< connection
->reqlen
) {
981 connection
->reqbegin
+= connection
->bodylen
;
982 connection
->bodylen
= 0;
984 connection
->bodylen
-= connection
->reqlen
- connection
->reqbegin
;
985 connection
->reqbegin
= 0;
986 connection
->reqlen
= 0;
987 httpConnectionDestroyReqbuf(connection
);
989 connection
->reqte
= TE_UNKNOWN
;
991 if(connection
->bodylen
> 0) {
992 httpSetTimeout(connection
, clientTimeout
);
993 do_stream_buf(IO_READ
| IO_NOTNOW
,
994 connection
->fd
, connection
->reqlen
,
995 &connection
->reqbuf
, CHUNK_SIZE
,
996 httpClientDiscardHandler
, connection
);
1000 if(connection
->reqlen
> connection
->reqbegin
) {
1001 memmove(connection
->reqbuf
, connection
->reqbuf
+ connection
->reqbegin
,
1002 connection
->reqlen
- connection
->reqbegin
);
1003 connection
->reqlen
-= connection
->reqbegin
;
1004 connection
->reqbegin
= 0;
1006 connection
->reqlen
= 0;
1007 connection
->reqbegin
= 0;
1010 httpSetTimeout(connection
, clientTimeout
);
1011 /* We need to delay in order to make sure the previous request
1012 gets queued on the server side. IO_NOTNOW isn't strong enough
1013 for that due to IO_IMMEDIATE. */
1014 handler
= scheduleTimeEvent(-1, httpClientDelayed
,
1015 sizeof(connection
), &connection
);
1016 if(handler
== NULL
) {
1017 do_log(L_ERROR
, "Couldn't schedule reading from client.");
1023 connection
->reqlen
= 0;
1024 connection
->reqbegin
= 0;
1025 connection
->bodylen
= 0;
1026 connection
->reqte
= TE_UNKNOWN
;
1027 shutdown(connection
->fd
, 2);
1028 handler
= scheduleTimeEvent(-1, httpClientDelayed
,
1029 sizeof(connection
), &connection
);
1030 if(handler
== NULL
) {
1031 do_log(L_ERROR
, "Couldn't schedule reading from client.");
1032 connection
->flags
&= ~CONN_READER
;
1038 httpClientDelayed(TimeEventHandlerPtr event
)
1040 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1042 /* IO_NOTNOW is unfortunate, but needed to avoid starvation if a
1043 client is pipelining a lot of requests. */
1044 if(connection
->reqlen
> 0) {
1046 if((connection
->flags
& CONN_BIGREQBUF
) &&
1047 connection
->reqlen
< CHUNK_SIZE
)
1048 httpConnectionUnbigifyReqbuf(connection
);
1049 /* Don't read new requests if buffer is big. */
1050 bufsize
= (connection
->flags
& CONN_BIGREQBUF
) ?
1051 connection
->reqlen
: CHUNK_SIZE
;
1052 do_stream(IO_READ
| IO_IMMEDIATE
| IO_NOTNOW
,
1053 connection
->fd
, connection
->reqlen
,
1054 connection
->reqbuf
, bufsize
,
1055 httpClientHandler
, connection
);
1057 httpConnectionDestroyReqbuf(connection
);
1058 do_stream_buf(IO_READ
| IO_NOTNOW
,
1060 &connection
->reqbuf
, CHUNK_SIZE
,
1061 httpClientHandler
, connection
);
1067 httpClientDiscardHandler(int status
,
1068 FdEventHandlerPtr event
, StreamRequestPtr request
)
1070 HTTPConnectionPtr connection
= request
->data
;
1072 assert(connection
->flags
& CONN_READER
);
1074 if(status
< 0 && status
!= -EPIPE
)
1075 do_log_error(L_ERROR
, -status
, "Couldn't read from client");
1076 connection
->bodylen
= -1;
1077 return httpClientDiscardBody(connection
);
1080 assert(request
->offset
> connection
->reqlen
);
1081 connection
->reqlen
= request
->offset
;
1083 httpClientDiscardBody(connection
);
1088 httpClientNoticeRequest(HTTPRequestPtr request
, int novalidate
)
1090 HTTPConnectionPtr connection
= request
->connection
;
1091 ObjectPtr object
= request
->object
;
1092 int serveNow
= (request
== connection
->request
);
1094 int conditional
= 0;
1095 int local
, haveData
;
1098 assert(!request
->chandler
);
1100 if(request
->error_code
) {
1101 if((request
->flags
& REQUEST_FORCE_ERROR
) || REQUEST_SIDE(request
) ||
1102 request
->object
== NULL
||
1103 (request
->object
->flags
& OBJECT_LOCAL
) ||
1104 (request
->object
->flags
& OBJECT_ABORTED
) ||
1105 (relaxTransparency
< 1 && !proxyOffline
)) {
1107 connection
->flags
|= CONN_WRITER
;
1108 return httpClientRawErrorHeaders(connection
,
1109 request
->error_code
,
1110 retainAtom(request
->
1112 0, request
->error_headers
);
1119 if(REQUEST_SIDE(request
)) {
1120 assert(!(request
->flags
& REQUEST_REQUESTED
));
1122 assert(!request
->chandler
);
1124 conditionWait(&request
->object
->condition
,
1125 httpClientGetHandler
,
1126 sizeof(request
), &request
);
1127 if(request
->chandler
== NULL
) {
1128 do_log(L_ERROR
, "Couldn't register condition handler.\n");
1129 connection
->flags
|= CONN_WRITER
;
1130 httpClientRawError(connection
, 500,
1131 internAtom("Couldn't register "
1132 "condition handler"),
1136 connection
->flags
|= CONN_WRITER
;
1137 rc
= object
->request(request
->object
,
1139 request
->from
, request
->to
,
1141 request
->object
->request_closure
);
1146 local
= urlIsLocal(object
->key
, object
->key_size
);
1147 objectFillFromDisk(object
, request
->from
,
1148 request
->method
== METHOD_HEAD
? 0 : 1);
1150 if(request
->condition
&& request
->condition
->ifrange
) {
1152 strcmp(object
->etag
, request
->condition
->ifrange
) != 0) {
1158 if(object
->flags
& OBJECT_DYNAMIC
) {
1163 if(request
->method
== METHOD_HEAD
)
1164 haveData
= !(request
->object
->flags
& OBJECT_INITIAL
);
1167 (request
->object
->length
>= 0 &&
1168 request
->object
->length
<= request
->from
) ||
1169 (objectHoleSize(request
->object
, request
->from
) == 0);
1171 if(request
->flags
& REQUEST_REQUESTED
)
1173 else if(novalidate
|| (!local
&& proxyOffline
))
1177 objectMustRevalidate(request
->object
, &request
->cache_control
);
1178 else if(request
->cache_control
.flags
& CACHE_ONLY_IF_CACHED
)
1180 else if((request
->object
->flags
& OBJECT_FAILED
) &&
1181 !(object
->flags
& OBJECT_INPROGRESS
) &&
1184 else if(request
->method
!= METHOD_HEAD
&&
1185 !objectHasData(object
, request
->from
, request
->to
) &&
1186 !(object
->flags
& OBJECT_INPROGRESS
))
1188 else if(objectMustRevalidate((relaxTransparency
<= 1 ?
1189 request
->object
: NULL
),
1190 &request
->cache_control
))
1195 if(request
->cache_control
.flags
& CACHE_ONLY_IF_CACHED
) {
1199 connection
->flags
|= CONN_WRITER
;
1200 return httpClientRawError(connection
, 504,
1201 internAtom("Object not in cache"),
1208 if(!(request
->object
->flags
& OBJECT_VALIDATING
) &&
1209 ((!validate
&& haveData
) ||
1210 (request
->object
->flags
& OBJECT_FAILED
))) {
1212 connection
->flags
|= CONN_WRITER
;
1213 lockChunk(request
->object
, request
->from
/ CHUNK_SIZE
);
1214 return httpServeObject(connection
);
1220 if((request
->flags
& REQUEST_REQUESTED
) &&
1221 !(request
->object
->flags
& OBJECT_INPROGRESS
)) {
1222 /* This can happen either because the server side ran out of
1223 memory, or because it is using HEAD validation. We mark
1224 the object to be fetched again. */
1225 request
->flags
&= ~REQUEST_REQUESTED
;
1229 connection
->flags
|= CONN_WRITER
;
1230 if(!local
&& proxyOffline
)
1231 return httpClientRawError(connection
, 502,
1232 internAtom("Disconnected operation "
1233 "and object not in cache"),
1236 conditionWait(&request
->object
->condition
, httpClientGetHandler
,
1237 sizeof(request
), &request
);
1238 if(request
->chandler
== NULL
) {
1239 do_log(L_ERROR
, "Couldn't register condition handler.\n");
1240 return httpClientRawError(connection
, 503,
1241 internAtom("Couldn't register "
1242 "condition handler"), 0);
1246 if(request
->object
->flags
& OBJECT_VALIDATING
)
1249 conditional
= (haveData
&& request
->method
== METHOD_GET
);
1250 if(!mindlesslyCacheVary
&& (request
->object
->cache_control
& CACHE_VARY
))
1251 conditional
= conditional
&& (request
->object
->etag
!= NULL
);
1254 conditional
&& !(request
->object
->cache_control
& CACHE_MISMATCH
);
1256 request
->object
->flags
|= OBJECT_VALIDATING
;
1257 rc
= request
->object
->request(request
->object
,
1258 conditional
? METHOD_CONDITIONAL_GET
:
1260 request
->from
, request
->to
, request
,
1261 request
->object
->request_closure
);
1263 if(request
->chandler
)
1264 unregisterConditionHandler(request
->chandler
);
1265 request
->chandler
= NULL
;
1266 request
->object
->flags
&= ~OBJECT_VALIDATING
;
1267 request
->object
->flags
|= OBJECT_FAILED
;
1268 if(request
->error_message
)
1269 releaseAtom(request
->error_message
);
1270 request
->error_code
= 503;
1271 request
->error_message
= internAtom("Couldn't schedule get");
1277 httpClientNoticeRequestDelayed(TimeEventHandlerPtr event
)
1279 HTTPRequestPtr request
= *(HTTPRequestPtr
*)event
->data
;
1280 httpClientNoticeRequest(request
, 0);
1285 delayedHttpClientNoticeRequest(HTTPRequestPtr request
)
1287 TimeEventHandlerPtr event
;
1288 event
= scheduleTimeEvent(-1, httpClientNoticeRequestDelayed
,
1289 sizeof(request
), &request
);
1296 httpClientContinueDelayed(TimeEventHandlerPtr event
)
1298 static char httpContinue
[] = "HTTP/1.1 100 Continue\r\n\r\n";
1299 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1301 do_stream(IO_WRITE
, connection
->fd
, 0, httpContinue
, 25,
1302 httpErrorNofinishStreamHandler
, connection
);
1307 delayedHttpClientContinue(HTTPConnectionPtr connection
)
1309 TimeEventHandlerPtr event
;
1310 event
= scheduleTimeEvent(-1, httpClientContinueDelayed
,
1311 sizeof(connection
), &connection
);
1318 httpClientGetHandler(int status
, ConditionHandlerPtr chandler
)
1320 HTTPRequestPtr request
= *(HTTPRequestPtr
*)chandler
->data
;
1321 HTTPConnectionPtr connection
= request
->connection
;
1322 ObjectPtr object
= request
->object
;
1325 assert(request
== connection
->request
);
1327 if(request
->request
) {
1328 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1329 assert(!request
->request
->object
||
1330 request
->request
->object
== request
->object
);
1334 object
->flags
&= ~OBJECT_VALIDATING
; /* for now */
1335 if(request
->request
&& request
->request
->request
== request
)
1336 httpServerClientReset(request
->request
);
1337 lockChunk(object
, request
->from
/ CHUNK_SIZE
);
1338 request
->chandler
= NULL
;
1339 rc
= delayedHttpServeObject(connection
);
1341 unlockChunk(object
, request
->from
/ CHUNK_SIZE
);
1342 do_log(L_ERROR
, "Couldn't schedule serving.\n");
1343 abortObject(object
, 503, internAtom("Couldn't schedule serving"));
1348 if(object
->flags
& OBJECT_VALIDATING
)
1351 if(request
->error_code
) {
1352 lockChunk(object
, request
->from
/ CHUNK_SIZE
);
1353 request
->chandler
= NULL
;
1354 rc
= delayedHttpServeObject(connection
);
1356 unlockChunk(object
, request
->from
/ CHUNK_SIZE
);
1357 do_log(L_ERROR
, "Couldn't schedule serving.\n");
1358 abortObject(object
, 503, internAtom("Couldn't schedule serving"));
1363 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
1364 if(request
->request
&&
1365 !(request
->request
->flags
& REQUEST_WAIT_CONTINUE
)) {
1366 request
->flags
&= ~REQUEST_WAIT_CONTINUE
;
1367 delayedHttpClientContinue(connection
);
1372 /* See httpServerHandlerHeaders */
1373 if((object
->flags
& OBJECT_SUPERSEDED
) &&
1374 request
->request
&& request
->request
->can_mutate
) {
1375 ObjectPtr new_object
= retainObject(request
->request
->can_mutate
);
1376 if(object
->requestor
== request
) {
1377 if(new_object
->requestor
== NULL
)
1378 new_object
->requestor
= request
;
1379 object
->requestor
= NULL
;
1381 request
->chandler
= NULL
;
1382 releaseObject(object
);
1383 request
->object
= new_object
;
1384 request
->request
->object
= new_object
;
1385 /* We're handling the wrong object now. It's simpler to
1386 rebuild the whole data structure from scratch rather than
1387 trying to compensate. */
1388 rc
= delayedHttpClientNoticeRequest(request
);
1390 do_log(L_ERROR
, "Couldn't schedule noticing of request.");
1391 abortObject(object
, 500,
1392 internAtom("Couldn't schedule "
1393 "noticing of request"));
1394 /* We're probably out of memory. What can we do? */
1395 shutdown(connection
->fd
, 1);
1400 if(object
->requestor
!= request
&& !(object
->flags
& OBJECT_ABORTED
)) {
1401 /* Make sure we don't serve an object that is stale for us
1402 unless we're the requestor. */
1403 if((object
->flags
& (OBJECT_LINEAR
| OBJECT_MUTATING
)) ||
1404 objectMustRevalidate(object
, &request
->cache_control
)) {
1405 if(object
->flags
& OBJECT_INPROGRESS
)
1407 rc
= delayedHttpClientNoticeRequest(request
);
1409 do_log(L_ERROR
, "Couldn't schedule noticing of request.");
1410 abortObject(object
, 500,
1411 internAtom("Couldn't schedule "
1412 "noticing of request"));
1414 request
->chandler
= NULL
;
1420 if(object
->flags
& (OBJECT_INITIAL
| OBJECT_VALIDATING
)) {
1421 if(object
->flags
& (OBJECT_INPROGRESS
| OBJECT_VALIDATING
)) {
1423 } else if(object
->flags
& OBJECT_FAILED
) {
1424 if(request
->error_code
)
1426 request
->error_code
,
1427 retainAtom(request
->error_message
));
1429 abortObject(object
, 500,
1430 internAtom("Error message lost in transit"));
1433 /* The request was pruned by httpServerDiscardRequests */
1434 if(chandler
== request
->chandler
) {
1436 request
->chandler
= NULL
;
1437 rc
= delayedHttpClientNoticeRequest(request
);
1439 abortObject(object
, 500,
1440 internAtom("Couldn't allocate "
1441 "delayed notice request"));
1445 abortObject(object
, 500,
1446 internAtom("Wrong request pruned -- "
1447 "this shouldn't happen"));
1452 if(request
->object
->flags
& OBJECT_DYNAMIC
) {
1453 if(objectHoleSize(request
->object
, 0) == 0) {
1457 /* We really should request again if that is not the case */
1461 lockChunk(object
, request
->from
/ CHUNK_SIZE
);
1462 request
->chandler
= NULL
;
1463 rc
= delayedHttpServeObject(connection
);
1465 unlockChunk(object
, request
->from
/ CHUNK_SIZE
);
1466 do_log(L_ERROR
, "Couldn't schedule serving.\n");
1467 abortObject(object
, 503, internAtom("Couldn't schedule serving"));
1473 httpClientSideRequest(HTTPRequestPtr request
)
1475 HTTPConnectionPtr connection
= request
->connection
;
1477 if(request
->from
< 0 || request
->to
>= 0) {
1478 httpClientNoticeError(request
, 501,
1479 internAtom("Partial requests not implemented"));
1480 httpClientDiscardBody(connection
);
1483 if(connection
->reqte
!= TE_IDENTITY
) {
1484 httpClientNoticeError(request
, 501,
1485 internAtom("Chunked requests not implemented"));
1486 httpClientDiscardBody(connection
);
1489 if(connection
->bodylen
< 0) {
1490 httpClientNoticeError(request
, 502,
1491 internAtom("POST or PUT without "
1493 httpClientDiscardBody(connection
);
1496 if(connection
->reqlen
< 0) {
1497 httpClientNoticeError(request
, 502,
1498 internAtom("Incomplete POST or PUT"));
1499 httpClientDiscardBody(connection
);
1503 return httpClientNoticeRequest(request
, 0);
1507 httpClientSideHandler(int status
,
1508 FdEventHandlerPtr event
,
1509 StreamRequestPtr srequest
)
1511 HTTPConnectionPtr connection
= srequest
->data
;
1512 HTTPRequestPtr request
= connection
->request
;
1513 HTTPRequestPtr requestee
;
1514 HTTPConnectionPtr server
;
1517 AtomPtr message
= NULL
;
1519 assert(connection
->flags
& CONN_SIDE_READER
);
1521 if((request
->object
->flags
& OBJECT_ABORTED
) ||
1522 !(request
->object
->flags
& OBJECT_INPROGRESS
)) {
1523 code
= request
->object
->code
;
1524 message
= retainAtom(request
->object
->message
);
1529 do_log_error(L_ERROR
, -status
, "Reading from client");
1531 message
= internAtomError(-status
, "Couldn't read from client");
1535 requestee
= request
->request
;
1536 server
= requestee
->connection
;
1538 push
= MIN(srequest
->offset
- connection
->reqlen
,
1539 connection
->bodylen
- connection
->reqoffset
);
1541 connection
->reqlen
+= push
;
1542 httpServerDoSide(server
);
1546 if(server
->reqoffset
>= connection
->bodylen
) {
1547 connection
->flags
&= ~(CONN_READER
| CONN_SIDE_READER
);
1552 do_log(L_ERROR
, "Incomplete client request.\n");
1554 message
= internAtom("Incomplete client request");
1557 request
->error_code
= code
;
1558 if(request
->error_message
)
1559 releaseAtom(request
->error_message
);
1560 request
->error_message
= message
;
1561 if(request
->error_headers
)
1562 releaseAtom(request
->error_headers
);
1563 request
->error_headers
= NULL
;
1565 if(request
->request
) {
1566 shutdown(request
->request
->connection
->fd
, 2);
1567 pokeFdEvent(request
->request
->connection
->fd
, -ESHUTDOWN
, POLLOUT
);
1569 notifyObject(request
->object
);
1570 connection
->flags
&= ~CONN_SIDE_READER
;
1571 httpClientDiscardBody(connection
);
1576 httpServeObject(HTTPConnectionPtr connection
)
1578 HTTPRequestPtr request
= connection
->request
;
1579 ObjectPtr object
= request
->object
;
1580 int i
= request
->from
/ CHUNK_SIZE
;
1581 int j
= request
->from
% CHUNK_SIZE
;
1583 int bufsize
= CHUNK_SIZE
;
1584 int condition_result
;
1586 object
->atime
= current_time
.tv_sec
;
1587 objectMetadataChanged(object
, 0);
1589 httpSetTimeout(connection
, -1);
1591 if((request
->error_code
&& relaxTransparency
<= 0) ||
1592 object
->flags
& OBJECT_INITIAL
) {
1593 object
->flags
&= ~OBJECT_FAILED
;
1594 unlockChunk(object
, i
);
1595 if(request
->error_code
)
1596 return httpClientRawError(connection
,
1597 request
->error_code
,
1598 retainAtom(request
->error_message
), 0);
1600 return httpClientRawError(connection
,
1601 500, internAtom("Object vanished."), 0);
1604 if(!(object
->flags
& OBJECT_INPROGRESS
) && object
->code
== 0) {
1605 if(object
->flags
& OBJECT_INITIAL
) {
1606 unlockChunk(object
, i
);
1607 return httpClientRawError(connection
, 503,
1608 internAtom("Error message lost"), 0);
1611 unlockChunk(object
, i
);
1612 do_log(L_ERROR
, "Internal proxy error: object has no code.\n");
1613 return httpClientRawError(connection
, 500,
1614 internAtom("Internal proxy error: "
1615 "object has no code"), 0);
1619 condition_result
= httpCondition(object
, request
->condition
);
1621 if(condition_result
== CONDITION_FAILED
) {
1622 unlockChunk(object
, i
);
1623 return httpClientRawError(connection
, 412,
1624 internAtom("Precondition failed"), 0);
1625 } else if(condition_result
== CONDITION_NOT_MODIFIED
) {
1626 unlockChunk(object
, i
);
1627 return httpClientRawError(connection
, 304,
1628 internAtom("Not modified"), 0);
1631 objectFillFromDisk(object
, request
->from
,
1632 (request
->method
== METHOD_HEAD
||
1633 condition_result
!= CONDITION_MATCH
) ? 0 : 1);
1635 if(((object
->flags
& OBJECT_LINEAR
) &&
1636 (object
->requestor
!= connection
->request
)) ||
1637 ((object
->flags
& OBJECT_SUPERSEDED
) &&
1638 !(object
->flags
& OBJECT_LINEAR
))) {
1639 if(request
->request
) {
1640 request
->request
->request
= NULL
;
1641 request
->request
= NULL
;
1642 request
->object
->requestor
= NULL
;
1644 object
= makeObject(OBJECT_HTTP
,
1645 object
->key
, object
->key_size
, 1, 0,
1646 object
->request
, NULL
);
1647 if(request
->object
->requestor
== request
)
1648 request
->object
->requestor
= NULL
;
1649 unlockChunk(request
->object
, i
);
1650 releaseObject(request
->object
);
1651 request
->object
= NULL
;
1652 if(object
== NULL
) {
1653 do_log(L_ERROR
, "Couldn't allocate object.");
1654 return httpClientRawError(connection
, 501,
1655 internAtom("Couldn't allocate object"),
1658 if(urlIsLocal(object
->key
, object
->key_size
)) {
1659 object
->flags
|= OBJECT_LOCAL
;
1660 object
->request
= httpLocalRequest
;
1662 request
->object
= object
;
1663 connection
->flags
&= ~CONN_WRITER
;
1664 return httpClientNoticeRequest(request
, 1);
1667 if(object
->flags
& OBJECT_ABORTED
) {
1668 unlockChunk(object
, i
);
1669 return httpClientNoticeError(request
, object
->code
,
1670 retainAtom(object
->message
));
1673 if(connection
->buf
== NULL
)
1674 connection
->buf
= get_chunk();
1675 if(connection
->buf
== NULL
) {
1676 unlockChunk(object
, i
);
1677 do_log(L_ERROR
, "Couldn't allocate client buffer.\n");
1678 connection
->flags
&= ~CONN_WRITER
;
1679 httpClientFinish(connection
, 1);
1683 if(object
->length
>= 0 && request
->to
>= object
->length
)
1686 if(request
->from
> 0 || request
->to
>= 0) {
1687 if(request
->method
== METHOD_HEAD
) {
1688 request
->to
= request
->from
;
1689 } else if(request
->to
< 0) {
1690 if(object
->length
>= 0)
1691 request
->to
= object
->length
;
1697 connection
->len
= 0;
1699 if((request
->from
<= 0 && request
->to
< 0) ||
1700 request
->method
== METHOD_HEAD
) {
1701 n
= snnprintf(connection
->buf
, 0, bufsize
,
1703 object
->code
, atomString(object
->message
));
1705 if(request
->from
> request
->to
) {
1706 unlockChunk(object
, i
);
1707 return httpClientRawError(connection
, 416,
1708 internAtom("Requested range "
1712 n
= snnprintf(connection
->buf
, 0, bufsize
,
1713 "HTTP/1.1 206 Partial content");
1717 n
= httpWriteObjectHeaders(connection
->buf
, n
, bufsize
,
1718 object
, request
->from
, request
->to
);
1722 if(request
->method
!= METHOD_HEAD
&&
1723 condition_result
!= CONDITION_NOT_MODIFIED
&&
1724 request
->to
< 0 && object
->length
< 0) {
1725 if(connection
->version
== HTTP_11
) {
1726 connection
->te
= TE_CHUNKED
;
1727 n
= snnprintf(connection
->buf
, n
, bufsize
,
1728 "\r\nTransfer-Encoding: chunked");
1730 request
->flags
&= ~REQUEST_PERSISTENT
;
1734 if(object
->age
< current_time
.tv_sec
) {
1735 n
= snnprintf(connection
->buf
, n
, bufsize
,
1737 (int)(current_time
.tv_sec
- object
->age
));
1739 n
= snnprintf(connection
->buf
, n
, bufsize
,
1740 "\r\nConnection: %s",
1741 (request
->flags
& REQUEST_PERSISTENT
) ?
1742 "keep-alive" : "close");
1744 if(!(object
->flags
& OBJECT_LOCAL
)) {
1745 if((object
->flags
& OBJECT_FAILED
) && !proxyOffline
) {
1746 n
= snnprintf(connection
->buf
, n
, bufsize
,
1747 "\r\nWarning: 111 %s:%d Revalidation failed",
1748 proxyName
->string
, proxyPort
);
1749 if(request
->error_code
)
1750 n
= snnprintf(connection
->buf
, n
, bufsize
,
1752 request
->error_code
,
1753 atomString(request
->error_message
));
1754 object
->flags
&= ~OBJECT_FAILED
;
1755 } else if(proxyOffline
&&
1756 objectMustRevalidate(object
, &request
->cache_control
)) {
1757 n
= snnprintf(connection
->buf
, n
, bufsize
,
1758 "\r\nWarning: 112 %s:%d Disconnected operation",
1759 proxyName
->string
, proxyPort
);
1760 } else if(objectIsStale(object
, &request
->cache_control
)) {
1761 n
= snnprintf(connection
->buf
, n
, bufsize
,
1762 "\r\nWarning: 110 %s:%d Object is stale",
1763 proxyName
->string
, proxyPort
);
1764 } else if(object
->expires
< 0 && object
->max_age
< 0 &&
1765 object
->age
< current_time
.tv_sec
- 24 * 3600) {
1766 n
= snnprintf(connection
->buf
, n
, bufsize
,
1767 "\r\nWarning: 113 %s:%d Heuristic expiration",
1768 proxyName
->string
, proxyPort
);
1772 n
= snnprintf(connection
->buf
, n
, bufsize
, "\r\n\r\n");
1777 connection
->offset
= request
->from
;
1779 if(request
->method
== METHOD_HEAD
||
1780 condition_result
== CONDITION_NOT_MODIFIED
||
1781 (object
->flags
& OBJECT_ABORTED
)) {
1784 if(i
< object
->numchunks
) {
1785 if(object
->chunks
[i
].size
<= j
)
1788 len
= object
->chunks
[i
].size
- j
;
1792 if(request
->to
>= 0)
1793 len
= MIN(len
, request
->to
- request
->from
);
1796 connection
->offset
= request
->from
;
1797 httpSetTimeout(connection
, clientTimeout
);
1798 do_log(D_CLIENT_DATA
, "Serving on 0x%lx for 0x%lx: offset %d len %d\n",
1799 (unsigned long)connection
, (unsigned long)object
,
1800 connection
->offset
, len
);
1801 do_stream_h(IO_WRITE
|
1802 (connection
->te
== TE_CHUNKED
&& len
> 0 ? IO_CHUNKED
: 0),
1805 object
->chunks
[i
].data
+ j
, len
,
1806 httpServeObjectStreamHandler
, connection
);
1811 connection
->len
= 0;
1812 if(!(connection
->flags
& CONN_BIGBUF
))
1813 rc
= httpConnectionBigify(connection
);
1815 bufsize
= bigBufferSize
;
1818 unlockChunk(object
, i
);
1819 return httpClientRawError(connection
, 500,
1821 internAtom("No space for headers") :
1822 internAtom("Couldn't allocate big buffer"), 0);
1826 httpServeObjectDelayed(TimeEventHandlerPtr event
)
1828 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1829 httpServeObject(connection
);
1834 delayedHttpServeObject(HTTPConnectionPtr connection
)
1836 TimeEventHandlerPtr event
;
1838 assert(connection
->request
->object
->chunks
[connection
->request
->from
/
1839 CHUNK_SIZE
].locked
> 0);
1841 event
= scheduleTimeEvent(-1, httpServeObjectDelayed
,
1842 sizeof(connection
), &connection
);
1843 if(!event
) return -1;
1848 httpServeObjectFinishHandler(int status
,
1849 FdEventHandlerPtr event
,
1850 StreamRequestPtr srequest
)
1852 HTTPConnectionPtr connection
= srequest
->data
;
1853 HTTPRequestPtr request
= connection
->request
;
1856 assert(!request
->chandler
);
1858 if(status
== 0 && !streamRequestDone(srequest
))
1861 httpSetTimeout(connection
, -1);
1864 do_log(L_ERROR
, "Couldn't terminate chunked reply\n");
1865 httpClientFinish(connection
, 1);
1867 httpClientFinish(connection
, 0);
1873 httpServeChunk(HTTPConnectionPtr connection
)
1875 HTTPRequestPtr request
= connection
->request
;
1876 ObjectPtr object
= request
->object
;
1877 int i
= connection
->offset
/ CHUNK_SIZE
;
1878 int j
= connection
->offset
- (i
* CHUNK_SIZE
);
1879 int to
, len
, len2
, end
;
1882 if(object
->flags
& OBJECT_ABORTED
)
1883 goto fail_no_unlock
;
1885 if(object
->length
>= 0 && request
->to
>= 0)
1886 to
= MIN(request
->to
, object
->length
);
1887 else if(object
->length
>= 0)
1888 to
= object
->length
;
1889 else if(request
->to
>= 0)
1894 lockChunk(object
, i
);
1896 if(i
< object
->numchunks
)
1897 len
= object
->chunks
[i
].size
- j
;
1899 if(request
->method
!= METHOD_HEAD
&&
1900 len
< CHUNK_SIZE
&& connection
->offset
+ len
< to
) {
1901 objectFillFromDisk(object
, connection
->offset
+ len
, 2);
1902 len
= object
->chunks
[i
].size
- j
;
1906 len
= MIN(len
, to
- connection
->offset
);
1909 if(to
>= 0 && connection
->offset
>= to
) {
1910 if(request
->chandler
) {
1911 unregisterConditionHandler(request
->chandler
);
1912 request
->chandler
= NULL
;
1914 unlockChunk(object
, i
);
1915 if(connection
->te
== TE_CHUNKED
) {
1916 httpSetTimeout(connection
, clientTimeout
);
1917 do_stream(IO_WRITE
| IO_CHUNKED
| IO_END
,
1918 connection
->fd
, 0, NULL
, 0,
1919 httpServeObjectFinishHandler
, connection
);
1921 httpClientFinish(connection
,
1922 !(object
->length
>= 0 &&
1923 connection
->offset
>= object
->length
));
1927 if(!request
->chandler
) {
1929 conditionWait(&object
->condition
,
1930 httpServeObjectHandler
,
1931 sizeof(connection
), &connection
);
1932 if(!request
->chandler
) {
1933 do_log(L_ERROR
, "Couldn't register condition handler\n");
1937 if(!(object
->flags
& OBJECT_INPROGRESS
)) {
1938 if(object
->flags
& OBJECT_SUPERSEDED
) {
1941 if(REQUEST_SIDE(request
)) goto fail
;
1942 rc
= object
->request(object
, request
->method
,
1943 connection
->offset
, -1, request
,
1944 object
->request_closure
);
1945 if(rc
<= 0) goto fail
;
1951 if(request
->method
!= METHOD_HEAD
)
1952 objectFillFromDisk(object
, (i
+ 1) * CHUNK_SIZE
, 1);
1953 if(request
->chandler
) {
1954 unregisterConditionHandler(request
->chandler
);
1955 request
->chandler
= NULL
;
1958 if(j
+ len
== CHUNK_SIZE
&& object
->numchunks
> i
+ 1) {
1959 len2
= object
->chunks
[i
+ 1].size
;
1961 len2
= MIN(len2
, to
- (i
+ 1) * CHUNK_SIZE
);
1963 /* Lock early -- httpServerRequest may get_chunk */
1965 lockChunk(object
, i
+ 1);
1966 if(object
->length
>= 0 &&
1967 connection
->offset
+ len
+ len2
== object
->length
)
1972 if(!(object
->flags
& OBJECT_INPROGRESS
) && !REQUEST_SIDE(request
)) {
1973 if(object
->chunks
[i
].size
< CHUNK_SIZE
&&
1974 to
>= 0 && connection
->offset
+ len
+ 1 < to
)
1975 object
->request(object
, request
->method
,
1976 connection
->offset
+ len
, -1, request
,
1977 object
->request_closure
);
1978 else if(i
+ 1 < object
->numchunks
&&
1979 object
->chunks
[i
+ 1].size
== 0 &&
1980 to
>= 0 && (i
+ 1) * CHUNK_SIZE
+ 1 < to
)
1981 object
->request(object
, request
->method
,
1982 (i
+ 1) * CHUNK_SIZE
, -1, request
,
1983 object
->request_closure
);
1986 httpSetTimeout(connection
, clientTimeout
);
1987 do_log(D_CLIENT_DATA
,
1988 "Serving on 0x%lx for 0x%lx: offset %d len %d\n",
1989 (unsigned long)connection
, (unsigned long)object
,
1990 connection
->offset
, len
);
1991 /* IO_NOTNOW in order to give other clients a chance to run. */
1992 do_stream(IO_WRITE
| IO_NOTNOW
|
1993 (connection
->te
== TE_CHUNKED
? IO_CHUNKED
: 0) |
1996 object
->chunks
[i
].data
+ j
, len
,
1997 httpServeObjectStreamHandler
, connection
);
1999 httpSetTimeout(connection
, clientTimeout
);
2000 do_log(D_CLIENT_DATA
,
2001 "Serving on 0x%lx for 0x%lx: offset %d len %d + %d\n",
2002 (unsigned long)connection
, (unsigned long)object
,
2003 connection
->offset
, len
, len2
);
2004 do_stream_2(IO_WRITE
| IO_NOTNOW
|
2005 (connection
->te
== TE_CHUNKED
? IO_CHUNKED
: 0) |
2008 object
->chunks
[i
].data
+ j
, len
,
2009 object
->chunks
[i
+ 1].data
, len2
,
2010 httpServeObjectStreamHandler2
, connection
);
2018 unlockChunk(object
, i
);
2020 if(request
->chandler
)
2021 unregisterConditionHandler(request
->chandler
);
2022 request
->chandler
= NULL
;
2023 httpClientFinish(connection
, 1);
2028 httpServeChunkDelayed(TimeEventHandlerPtr event
)
2030 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
2031 httpServeChunk(connection
);
2036 delayedHttpServeChunk(HTTPConnectionPtr connection
)
2038 TimeEventHandlerPtr event
;
2039 event
= scheduleTimeEvent(-1, httpServeChunkDelayed
,
2040 sizeof(connection
), &connection
);
2041 if(!event
) return -1;
2046 httpServeObjectHandler(int status
, ConditionHandlerPtr chandler
)
2048 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)chandler
->data
;
2049 HTTPRequestPtr request
= connection
->request
;
2052 unlockChunk(request
->object
, connection
->offset
/ CHUNK_SIZE
);
2054 if((request
->object
->flags
& OBJECT_ABORTED
) || status
< 0) {
2055 shutdown(connection
->fd
, 1);
2056 httpSetTimeout(connection
, 10);
2057 /* httpServeChunk will take care of the error. */
2060 httpSetTimeout(connection
, -1);
2062 request
->chandler
= NULL
;
2063 rc
= delayedHttpServeChunk(connection
);
2065 do_log(L_ERROR
, "Couldn't schedule serving.\n");
2066 abortObject(request
->object
, 503,
2067 internAtom("Couldn't schedule serving"));
2073 httpServeObjectStreamHandlerCommon(int kind
, int status
,
2074 FdEventHandlerPtr event
,
2075 StreamRequestPtr srequest
)
2077 HTTPConnectionPtr connection
= srequest
->data
;
2078 HTTPRequestPtr request
= connection
->request
;
2079 int condition_result
= httpCondition(request
->object
, request
->condition
);
2080 int i
= connection
->offset
/ CHUNK_SIZE
;
2082 assert(!request
->chandler
);
2084 if(status
== 0 && !streamRequestDone(srequest
)) {
2085 httpSetTimeout(connection
, clientTimeout
);
2089 httpSetTimeout(connection
, -1);
2091 unlockChunk(request
->object
, i
);
2093 unlockChunk(request
->object
, i
+ 1);
2097 do_log_error(status
== -ECONNRESET
? D_IO
: L_ERROR
,
2098 -status
, "Couldn't write to client");
2099 if(status
== -EIO
|| status
== -ESHUTDOWN
)
2100 httpClientFinish(connection
, 2);
2102 httpClientFinish(connection
, 1);
2104 do_log(D_IO
, "Couldn't write to client: short write.\n");
2105 httpClientFinish(connection
, 2);
2110 if(srequest
->operation
& IO_CHUNKED
) {
2111 assert(srequest
->offset
> 2);
2112 connection
->offset
+= srequest
->offset
- 2;
2114 connection
->offset
+= srequest
->offset
;
2116 request
->flags
&= ~REQUEST_REQUESTED
;
2118 if(request
->object
->flags
& OBJECT_ABORTED
) {
2119 httpClientFinish(connection
, 1);
2123 if(connection
->request
->method
== METHOD_HEAD
||
2124 condition_result
== CONDITION_NOT_MODIFIED
) {
2125 httpClientFinish(connection
, 0);
2129 if(srequest
->operation
& IO_END
)
2130 httpClientFinish(connection
, 0);
2132 httpConnectionDestroyBuf(connection
);
2133 httpServeChunk(connection
);
2139 httpServeObjectStreamHandler(int status
,
2140 FdEventHandlerPtr event
,
2141 StreamRequestPtr srequest
)
2143 return httpServeObjectStreamHandlerCommon(1, status
, event
, srequest
);
2147 httpServeObjectStreamHandler2(int status
,
2148 FdEventHandlerPtr event
,
2149 StreamRequestPtr srequest
)
2151 return httpServeObjectStreamHandlerCommon(2, status
, event
, srequest
);