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
) ? bigBufferSize
: 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
);
415 if((connection
->flags
& CONN_BIGREQBUF
) &&
416 connection
->reqlen
< bigBufferSize
) {
417 do_stream(IO_READ
, connection
->fd
, connection
->reqlen
,
418 connection
->reqbuf
, bigBufferSize
,
419 httpClientHandler
, connection
);
422 connection
->reqlen
= 0;
423 httpConnectionDestroyReqbuf(connection
);
425 do_log(L_ERROR
, "Couldn't allocate big buffer.\n");
426 httpClientNewError(connection
, METHOD_UNKNOWN
, 0, 400,
427 internAtom("Couldn't allocate big buffer"));
429 do_log(L_ERROR
, "Couldn't find end of client's headers.\n");
430 httpClientNewError(connection
, METHOD_UNKNOWN
, 0, 400,
431 internAtom("Couldn't find end of headers"));
435 httpSetTimeout(connection
, clientTimeout
);
440 httpClientRawErrorHeaders(HTTPConnectionPtr connection
,
441 int code
, AtomPtr message
,
442 int close
, AtomPtr headers
)
444 int fd
= connection
->fd
;
446 char *url
; int url_len
;
449 assert(connection
->flags
& CONN_WRITER
);
453 if(connection
->request
)
455 close
|| !(connection
->request
->flags
& REQUEST_PERSISTENT
);
459 if(connection
->request
&& connection
->request
->object
) {
460 url
= connection
->request
->object
->key
;
461 url_len
= connection
->request
->object
->key_size
;
462 etag
= connection
->request
->object
->etag
;
469 if(connection
->buf
== NULL
) {
470 connection
->buf
= get_chunk();
471 if(connection
->buf
== NULL
) {
472 httpClientFinish(connection
, 1);
477 n
= httpWriteErrorHeaders(connection
->buf
, CHUNK_SIZE
, 0,
478 connection
->request
&&
479 connection
->request
->method
!= METHOD_HEAD
,
480 code
, message
, close
> 0, headers
,
483 shutdown(connection
->fd
, 1);
485 httpClientFinish(connection
, 1);
489 httpSetTimeout(connection
, clientTimeout
);
490 do_stream(IO_WRITE
, fd
, 0, connection
->buf
, n
,
491 close
> 0 ? httpErrorStreamHandler
:
492 close
== 0 ? httpErrorNocloseStreamHandler
:
493 httpErrorNofinishStreamHandler
,
500 httpClientRawError(HTTPConnectionPtr connection
, int code
, AtomPtr message
,
503 return httpClientRawErrorHeaders(connection
, code
, message
, close
, NULL
);
507 httpClientNoticeErrorHeaders(HTTPRequestPtr request
, int code
, AtomPtr message
,
510 if(request
->error_message
)
511 releaseAtom(request
->error_message
);
512 if(request
->error_headers
)
513 releaseAtom(request
->error_headers
);
514 request
->error_code
= code
;
515 request
->error_message
= message
;
516 request
->error_headers
= headers
;
517 httpClientNoticeRequest(request
, 0);
522 httpClientNoticeError(HTTPRequestPtr request
, int code
, AtomPtr message
)
524 return httpClientNoticeErrorHeaders(request
, code
, message
, NULL
);
528 httpClientError(HTTPRequestPtr request
, int code
, AtomPtr message
)
530 if(request
->error_message
)
531 releaseAtom(request
->error_message
);
532 request
->error_code
= code
;
533 request
->error_message
= message
;
534 if(request
->chandler
) {
535 abortConditionHandler(request
->chandler
);
536 request
->chandler
= NULL
;
537 } else if(request
->object
)
538 notifyObject(request
->object
);
542 /* This may be called from object handlers. */
544 httpClientLeanError(HTTPRequestPtr request
, int code
, AtomPtr message
)
546 if(request
->error_message
)
547 releaseAtom(request
->error_message
);
548 request
->error_code
= code
;
549 request
->error_message
= message
;
555 httpClientNewError(HTTPConnectionPtr connection
, int method
, int persist
,
556 int code
, AtomPtr message
)
558 HTTPRequestPtr request
;
559 request
= httpMakeRequest();
560 if(request
== NULL
) {
561 do_log(L_ERROR
, "Couldn't allocate error request.\n");
562 httpClientFinish(connection
, 1);
565 request
->method
= method
;
567 request
->flags
|= REQUEST_PERSISTENT
;
569 request
->flags
&= ~REQUEST_PERSISTENT
;
570 request
->error_code
= code
;
571 request
->error_message
= message
;
573 httpQueueRequest(connection
, request
);
574 httpClientNoticeRequest(request
, 0);
579 httpErrorStreamHandler(int status
,
580 FdEventHandlerPtr event
,
581 StreamRequestPtr srequest
)
583 HTTPConnectionPtr connection
= srequest
->data
;
585 if(status
== 0 && !streamRequestDone(srequest
))
588 httpClientFinish(connection
, 1);
593 httpErrorNocloseStreamHandler(int status
,
594 FdEventHandlerPtr event
,
595 StreamRequestPtr srequest
)
597 HTTPConnectionPtr connection
= srequest
->data
;
599 if(status
== 0 && !streamRequestDone(srequest
))
602 httpClientFinish(connection
, 0);
607 httpErrorNofinishStreamHandler(int status
,
608 FdEventHandlerPtr event
,
609 StreamRequestPtr srequest
)
611 if(status
== 0 && !streamRequestDone(srequest
))
618 httpClientHandlerHeaders(FdEventHandlerPtr event
, StreamRequestPtr srequest
,
619 HTTPConnectionPtr connection
)
621 HTTPRequestPtr request
;
630 /* Work around clients working around NCSA lossage. */
631 if(connection
->reqbuf
[0] == '\n')
633 else if(connection
->reqbuf
[0] == '\r' && connection
->reqbuf
[1] == '\n')
636 httpSetTimeout(connection
, -1);
637 rc
= httpParseClientFirstLine(connection
->reqbuf
, start
,
638 &method
, &url
, &version
);
640 do_log(L_ERROR
, "Couldn't parse client's request line\n");
642 message
= internAtom("Error in request line");
646 do_log(D_CLIENT_REQ
, "Client request: ");
647 do_log_n(D_CLIENT_REQ
, connection
->reqbuf
, rc
- 1);
648 do_log(D_CLIENT_REQ
, "\n");
650 if(version
!= HTTP_10
&& version
!= HTTP_11
) {
651 do_log(L_ERROR
, "Unknown client HTTP version\n");
653 message
= internAtom("Error in first request line");
657 if(method
== METHOD_UNKNOWN
) {
659 message
= internAtom("Method not implemented");
663 request
= httpMakeRequest();
664 if(request
== NULL
) {
665 do_log(L_ERROR
, "Couldn't allocate client request.\n");
667 message
= internAtom("Couldn't allocate client request");
671 if(connection
->version
!= HTTP_UNKNOWN
&& version
!= connection
->version
) {
672 do_log(L_WARN
, "Client version changed!\n");
675 connection
->version
= version
;
676 request
->flags
= REQUEST_PERSISTENT
;
677 request
->method
= method
;
678 request
->cache_control
= no_cache_control
;
679 httpQueueRequest(connection
, request
);
680 connection
->reqbegin
= rc
;
681 return httpClientRequest(request
, url
);
684 if(url
) releaseAtom(url
);
685 shutdown(connection
->fd
, 0);
686 connection
->reqlen
= 0;
687 connection
->reqbegin
= 0;
688 httpConnectionDestroyReqbuf(connection
);
689 connection
->flags
&= ~CONN_READER
;
690 httpClientNewError(connection
, METHOD_UNKNOWN
, 0, code
, message
);
696 httpClientRequestDelayed(TimeEventHandlerPtr event
)
698 HTTPRequestPtr request
= *(HTTPRequestPtr
*)event
->data
;
700 url
= internAtomN(request
->object
->key
, request
->object
->key_size
);
702 do_log(L_ERROR
, "Couldn't allocate url.\n");
703 abortObject(request
->object
, 503, internAtom("Couldn't allocate url"));
706 httpClientRequest(request
, url
);
711 delayedHttpClientRequest(HTTPRequestPtr request
)
713 TimeEventHandlerPtr event
;
714 event
= scheduleTimeEvent(-1, httpClientRequestDelayed
,
715 sizeof(request
), &request
);
722 httpClientRequest(HTTPRequestPtr request
, AtomPtr url
)
724 HTTPConnectionPtr connection
= request
->connection
;
726 int body_len
, body_te
;
728 CacheControlRec cache_control
;
729 AtomPtr via
, expect
, auth
;
730 HTTPConditionPtr condition
;
733 assert(!request
->chandler
);
734 assert(connection
->reqbuf
);
736 i
= httpParseHeaders(1, url
,
737 connection
->reqbuf
, connection
->reqbegin
, request
,
739 &cache_control
, &condition
, &body_te
,
740 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
741 &expect
, &range
, NULL
, NULL
, &via
, &auth
);
744 do_log(L_ERROR
, "Couldn't parse client headers.\n");
745 shutdown(connection
->fd
, 0);
746 request
->flags
&= ~REQUEST_PERSISTENT
;
747 connection
->flags
&= ~CONN_READER
;
748 httpClientNoticeError(request
, 503,
749 internAtom("Couldn't parse client headers"));
753 connection
->reqbegin
= i
;
756 if(request
->method
== METHOD_GET
|| request
->method
== METHOD_HEAD
)
759 connection
->bodylen
= body_len
;
760 connection
->reqte
= body_te
;
763 AtomPtr message
= NULL
;
764 AtomPtr challenge
= NULL
;
765 int code
= checkClientAuth(auth
, url
, &message
, &challenge
);
775 request
->flags
|= REQUEST_FORCE_ERROR
;
776 httpClientDiscardBody(connection
);
777 httpClientNoticeErrorHeaders(request
, code
, message
, challenge
);
788 if(expect
== atom100Continue
&& REQUEST_SIDE(request
)) {
789 request
->flags
|= REQUEST_WAIT_CONTINUE
;
791 httpClientDiscardBody(connection
);
792 httpClientNoticeError(request
, 417,
793 internAtom("Expectation failed"));
800 request
->from
= range
.from
< 0 ? 0 : range
.from
;
801 request
->to
= range
.to
;
802 request
->cache_control
= cache_control
;
804 request
->headers
= headers
;
805 request
->condition
= condition
;
806 request
->object
= NULL
;
808 if(connection
->serviced
> 500)
809 request
->flags
&= ~REQUEST_PERSISTENT
;
811 if(request
->method
== METHOD_CONNECT
) {
812 if(connection
->flags
& CONN_WRITER
) {
814 httpClientDiscardBody(connection
);
815 httpClientNoticeError(request
, 500,
816 internAtom("Pipelined CONNECT "
820 if(connection
->flags
& CONN_BIGREQBUF
) {
822 httpClientDiscardBody(connection
);
823 httpClientNoticeError(request
, 500,
824 internAtom("CONNECT over big buffer "
828 connection
->flags
&= ~CONN_READER
;
829 do_tunnel(connection
->fd
, connection
->reqbuf
,
830 connection
->reqbegin
, connection
->reqlen
, url
);
832 connection
->reqbuf
= NULL
;
833 connection
->reqlen
= 0;
834 connection
->reqbegin
= 0;
835 httpClientFinish(connection
, 2);
839 rc
= urlForbidden(url
, httpClientRequestContinue
, request
);
841 do_log(L_ERROR
, "Couldn't schedule httpClientRequestContinue.\n");
842 httpClientDiscardBody(connection
);
843 httpClientNoticeError(request
, 500,
844 internAtom("Couldn't schedule "
845 "httpClientRequestContinue"));
852 httpClientRequestContinue(int forbidden_code
, AtomPtr url
,
853 AtomPtr forbidden_message
, AtomPtr forbidden_headers
,
856 HTTPRequestPtr request
= (HTTPRequestPtr
)closure
;
857 HTTPConnectionPtr connection
= request
->connection
;
858 RequestFunction requestfn
;
859 ObjectPtr object
= NULL
;
861 if(forbidden_code
< 0) {
863 httpClientDiscardBody(connection
);
864 httpClientNoticeError(request
, 500,
865 internAtomError(-forbidden_code
,
866 "Couldn't test for forbidden "
873 httpClientDiscardBody(connection
);
874 httpClientNoticeErrorHeaders(request
,
875 forbidden_code
, forbidden_message
,
881 urlIsLocal(url
->string
, url
->length
) ?
885 if(request
->method
== METHOD_POST
|| request
->method
== METHOD_PUT
) {
887 object
= findObject(OBJECT_HTTP
, url
->string
, url
->length
);
889 privatiseObject(object
, 0);
890 releaseObject(object
);
893 request
->object
= makeObject(OBJECT_HTTP
, url
->string
, url
->length
,
894 0, 0, requestfn
, NULL
);
895 if(request
->object
== NULL
) {
896 httpClientDiscardBody(connection
);
897 httpClientNoticeError(request
, 503,
898 internAtom("Couldn't allocate object"));
901 if(requestfn
== httpLocalRequest
)
902 request
->object
->flags
|= OBJECT_LOCAL
;
903 return httpClientSideRequest(request
);
906 if(request
->cache_control
.flags
& CACHE_AUTHORIZATION
) {
908 object
= makeObject(OBJECT_HTTP
, url
->string
, url
->length
, 0, 0,
910 if(object
&& object
->flags
!= OBJECT_INITIAL
) {
911 if(!(object
->cache_control
& CACHE_PUBLIC
)) {
912 privatiseObject(object
, 0);
913 releaseObject(object
);
918 } while(object
== NULL
);
920 object
->flags
|= OBJECT_LINEAR
;
922 object
= findObject(OBJECT_HTTP
, url
->string
, url
->length
);
924 object
= makeObject(OBJECT_HTTP
, url
->string
, url
->length
, 1, 1,
931 do_log(L_ERROR
, "Couldn't allocate object.\n");
932 httpClientDiscardBody(connection
);
933 httpClientNoticeError(request
, 503,
934 internAtom("Couldn't allocate object"));
938 if(object
->request
== httpLocalRequest
) {
939 object
->flags
|= OBJECT_LOCAL
;
942 httpClientDiscardBody(connection
);
943 httpClientNoticeError(request
, 403,
944 internAtom("Proxying disabled"));
945 releaseObject(object
);
949 if(!checkVia(proxyName
, request
->via
)) {
950 httpClientDiscardBody(connection
);
951 httpClientNoticeError(request
, 504,
952 internAtom("Proxy loop detected"));
953 releaseObject(object
);
958 request
->object
= object
;
960 httpClientDiscardBody(connection
);
961 httpClientNoticeRequest(request
, 0);
965 static int httpClientDelayed(TimeEventHandlerPtr handler
);
968 httpClientDiscardBody(HTTPConnectionPtr connection
)
970 TimeEventHandlerPtr handler
;
972 assert(connection
->reqoffset
== 0);
973 assert(connection
->flags
& CONN_READER
);
975 if(connection
->reqte
!= TE_IDENTITY
)
978 if(connection
->bodylen
< 0)
981 if(connection
->bodylen
< connection
->reqlen
- connection
->reqbegin
) {
982 connection
->reqbegin
+= connection
->bodylen
;
983 connection
->bodylen
= 0;
985 connection
->bodylen
-= connection
->reqlen
- connection
->reqbegin
;
986 connection
->reqbegin
= 0;
987 connection
->reqlen
= 0;
988 httpConnectionDestroyReqbuf(connection
);
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 (connection
->reqlen
- connection
->reqbegin
) > 0) {
1002 memmove(connection
->reqbuf
, connection
->reqbuf
+ connection
->reqbegin
,
1003 connection
->reqlen
- connection
->reqbegin
);
1004 connection
->reqlen
-= connection
->reqbegin
;
1005 connection
->reqbegin
= 0;
1007 connection
->reqlen
= 0;
1008 connection
->reqbegin
= 0;
1011 httpSetTimeout(connection
, clientTimeout
);
1012 /* We need to delay in order to make sure the previous request
1013 gets queued on the server side. IO_NOTNOW isn't strong enough
1014 for that due to IO_IMMEDIATE. */
1015 handler
= scheduleTimeEvent(-1, httpClientDelayed
,
1016 sizeof(connection
), &connection
);
1017 if(handler
== NULL
) {
1018 do_log(L_ERROR
, "Couldn't schedule reading from client.");
1024 connection
->reqlen
= 0;
1025 connection
->reqbegin
= 0;
1026 connection
->bodylen
= 0;
1027 connection
->reqte
= TE_UNKNOWN
;
1028 shutdown(connection
->fd
, 2);
1029 handler
= scheduleTimeEvent(-1, httpClientDelayed
,
1030 sizeof(connection
), &connection
);
1031 if(handler
== NULL
) {
1032 do_log(L_ERROR
, "Couldn't schedule reading from client.");
1033 connection
->flags
&= ~CONN_READER
;
1039 httpClientDelayed(TimeEventHandlerPtr event
)
1041 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1043 /* IO_NOTNOW is unfortunate, but needed to avoid starvation if a
1044 client is pipelining a lot of requests. */
1045 if(connection
->reqlen
> 0) {
1047 if((connection
->flags
& CONN_BIGREQBUF
) &&
1048 connection
->reqlen
< CHUNK_SIZE
)
1049 httpConnectionUnbigifyReqbuf(connection
);
1050 /* Don't read new requests if buffer is big. */
1051 bufsize
= (connection
->flags
& CONN_BIGREQBUF
) ?
1052 connection
->reqlen
: CHUNK_SIZE
;
1053 do_stream(IO_READ
| IO_IMMEDIATE
| IO_NOTNOW
,
1054 connection
->fd
, connection
->reqlen
,
1055 connection
->reqbuf
, bufsize
,
1056 httpClientHandler
, connection
);
1058 httpConnectionDestroyReqbuf(connection
);
1059 do_stream_buf(IO_READ
| IO_NOTNOW
,
1061 &connection
->reqbuf
, CHUNK_SIZE
,
1062 httpClientHandler
, connection
);
1068 httpClientDiscardHandler(int status
,
1069 FdEventHandlerPtr event
, StreamRequestPtr request
)
1071 HTTPConnectionPtr connection
= request
->data
;
1073 assert(connection
->flags
& CONN_READER
);
1075 if(status
< 0 && status
!= -EPIPE
)
1076 do_log_error(L_ERROR
, -status
, "Couldn't read from client");
1077 connection
->bodylen
= -1;
1078 return httpClientDiscardBody(connection
);
1081 assert(request
->offset
> connection
->reqlen
);
1082 connection
->reqlen
= request
->offset
;
1084 httpClientDiscardBody(connection
);
1089 httpClientNoticeRequest(HTTPRequestPtr request
, int novalidate
)
1091 HTTPConnectionPtr connection
= request
->connection
;
1092 ObjectPtr object
= request
->object
;
1093 int serveNow
= (request
== connection
->request
);
1095 int conditional
= 0;
1096 int local
, haveData
;
1099 assert(!request
->chandler
);
1101 if(request
->error_code
) {
1102 if((request
->flags
& REQUEST_FORCE_ERROR
) || REQUEST_SIDE(request
) ||
1103 request
->object
== NULL
||
1104 (request
->object
->flags
& OBJECT_LOCAL
) ||
1105 (request
->object
->flags
& OBJECT_ABORTED
) ||
1106 (relaxTransparency
< 1 && !proxyOffline
)) {
1108 connection
->flags
|= CONN_WRITER
;
1109 return httpClientRawErrorHeaders(connection
,
1110 request
->error_code
,
1111 retainAtom(request
->
1113 0, request
->error_headers
);
1120 if(REQUEST_SIDE(request
)) {
1121 assert(!(request
->flags
& REQUEST_REQUESTED
));
1123 assert(!request
->chandler
);
1125 conditionWait(&request
->object
->condition
,
1126 httpClientGetHandler
,
1127 sizeof(request
), &request
);
1128 if(request
->chandler
== NULL
) {
1129 do_log(L_ERROR
, "Couldn't register condition handler.\n");
1130 connection
->flags
|= CONN_WRITER
;
1131 httpClientRawError(connection
, 500,
1132 internAtom("Couldn't register "
1133 "condition handler"),
1137 connection
->flags
|= CONN_WRITER
;
1138 rc
= object
->request(request
->object
,
1140 request
->from
, request
->to
,
1142 request
->object
->request_closure
);
1147 local
= urlIsLocal(object
->key
, object
->key_size
);
1148 objectFillFromDisk(object
, request
->from
,
1149 request
->method
== METHOD_HEAD
? 0 : 1);
1151 /* The spec doesn't strictly forbid 206 for non-200 instances, but doing
1152 that breaks some client software. */
1153 if(object
->code
&& object
->code
!= 200) {
1158 if(request
->condition
&& request
->condition
->ifrange
) {
1160 strcmp(object
->etag
, request
->condition
->ifrange
) != 0) {
1166 if(object
->flags
& OBJECT_DYNAMIC
) {
1171 if(request
->method
== METHOD_HEAD
||
1172 request
->object
->code
== 204 ||
1173 request
->object
->code
< 200)
1174 haveData
= !(request
->object
->flags
& OBJECT_INITIAL
);
1177 (request
->object
->length
>= 0 &&
1178 request
->object
->length
<= request
->from
) ||
1179 (objectHoleSize(request
->object
, request
->from
) == 0);
1181 if(request
->flags
& REQUEST_REQUESTED
)
1183 else if(novalidate
|| (!local
&& proxyOffline
))
1187 objectMustRevalidate(request
->object
, &request
->cache_control
);
1188 else if(request
->cache_control
.flags
& CACHE_ONLY_IF_CACHED
)
1190 else if((request
->object
->flags
& OBJECT_FAILED
) &&
1191 !(object
->flags
& OBJECT_INPROGRESS
) &&
1194 else if(request
->method
!= METHOD_HEAD
&&
1195 !objectHasData(object
, request
->from
, request
->to
) &&
1196 !(object
->flags
& OBJECT_INPROGRESS
))
1198 else if(objectMustRevalidate((relaxTransparency
<= 1 ?
1199 request
->object
: NULL
),
1200 &request
->cache_control
))
1205 if(request
->cache_control
.flags
& CACHE_ONLY_IF_CACHED
) {
1209 connection
->flags
|= CONN_WRITER
;
1210 return httpClientRawError(connection
, 504,
1211 internAtom("Object not in cache"),
1218 if(!(request
->object
->flags
& OBJECT_VALIDATING
) &&
1219 ((!validate
&& haveData
) ||
1220 (request
->object
->flags
& OBJECT_FAILED
))) {
1222 connection
->flags
|= CONN_WRITER
;
1223 lockChunk(request
->object
, request
->from
/ CHUNK_SIZE
);
1224 return httpServeObject(connection
);
1230 if((request
->flags
& REQUEST_REQUESTED
) &&
1231 !(request
->object
->flags
& OBJECT_INPROGRESS
)) {
1232 /* This can happen either because the server side ran out of
1233 memory, or because it is using HEAD validation. We mark
1234 the object to be fetched again. */
1235 request
->flags
&= ~REQUEST_REQUESTED
;
1239 connection
->flags
|= CONN_WRITER
;
1240 if(!local
&& proxyOffline
)
1241 return httpClientRawError(connection
, 502,
1242 internAtom("Disconnected operation "
1243 "and object not in cache"),
1246 conditionWait(&request
->object
->condition
, httpClientGetHandler
,
1247 sizeof(request
), &request
);
1248 if(request
->chandler
== NULL
) {
1249 do_log(L_ERROR
, "Couldn't register condition handler.\n");
1250 return httpClientRawError(connection
, 503,
1251 internAtom("Couldn't register "
1252 "condition handler"), 0);
1256 if(request
->object
->flags
& OBJECT_VALIDATING
)
1259 conditional
= (haveData
&& request
->method
== METHOD_GET
);
1260 if(!mindlesslyCacheVary
&& (request
->object
->cache_control
& CACHE_VARY
))
1261 conditional
= conditional
&& (request
->object
->etag
!= NULL
);
1264 conditional
&& !(request
->object
->cache_control
& CACHE_MISMATCH
);
1266 if(!(request
->object
->flags
& OBJECT_INPROGRESS
))
1267 request
->object
->flags
|= OBJECT_VALIDATING
;
1268 rc
= request
->object
->request(request
->object
,
1269 conditional
? METHOD_CONDITIONAL_GET
:
1271 request
->from
, request
->to
, request
,
1272 request
->object
->request_closure
);
1274 if(request
->chandler
)
1275 unregisterConditionHandler(request
->chandler
);
1276 request
->chandler
= NULL
;
1277 request
->object
->flags
&= ~OBJECT_VALIDATING
;
1278 request
->object
->flags
|= OBJECT_FAILED
;
1279 if(request
->error_message
)
1280 releaseAtom(request
->error_message
);
1281 request
->error_code
= 503;
1282 request
->error_message
= internAtom("Couldn't schedule get");
1288 httpClientNoticeRequestDelayed(TimeEventHandlerPtr event
)
1290 HTTPRequestPtr request
= *(HTTPRequestPtr
*)event
->data
;
1291 httpClientNoticeRequest(request
, 0);
1296 delayedHttpClientNoticeRequest(HTTPRequestPtr request
)
1298 TimeEventHandlerPtr event
;
1299 event
= scheduleTimeEvent(-1, httpClientNoticeRequestDelayed
,
1300 sizeof(request
), &request
);
1307 httpClientContinueDelayed(TimeEventHandlerPtr event
)
1309 static char httpContinue
[] = "HTTP/1.1 100 Continue\r\n\r\n";
1310 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1312 do_stream(IO_WRITE
, connection
->fd
, 0, httpContinue
, 25,
1313 httpErrorNofinishStreamHandler
, connection
);
1318 delayedHttpClientContinue(HTTPConnectionPtr connection
)
1320 TimeEventHandlerPtr event
;
1321 event
= scheduleTimeEvent(-1, httpClientContinueDelayed
,
1322 sizeof(connection
), &connection
);
1329 httpClientGetHandler(int status
, ConditionHandlerPtr chandler
)
1331 HTTPRequestPtr request
= *(HTTPRequestPtr
*)chandler
->data
;
1332 HTTPConnectionPtr connection
= request
->connection
;
1333 ObjectPtr object
= request
->object
;
1336 assert(request
== connection
->request
);
1338 if(request
->request
) {
1339 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1340 assert(!request
->request
->object
||
1341 request
->request
->object
== request
->object
);
1345 object
->flags
&= ~OBJECT_VALIDATING
; /* for now */
1346 if(request
->request
&& request
->request
->request
== request
)
1347 httpServerClientReset(request
->request
);
1348 lockChunk(object
, request
->from
/ CHUNK_SIZE
);
1349 request
->chandler
= NULL
;
1350 rc
= delayedHttpServeObject(connection
);
1352 unlockChunk(object
, request
->from
/ CHUNK_SIZE
);
1353 do_log(L_ERROR
, "Couldn't schedule serving.\n");
1354 abortObject(object
, 503, internAtom("Couldn't schedule serving"));
1359 if(object
->flags
& OBJECT_VALIDATING
)
1362 if(request
->error_code
) {
1363 lockChunk(object
, request
->from
/ CHUNK_SIZE
);
1364 request
->chandler
= NULL
;
1365 rc
= delayedHttpServeObject(connection
);
1367 unlockChunk(object
, request
->from
/ CHUNK_SIZE
);
1368 do_log(L_ERROR
, "Couldn't schedule serving.\n");
1369 abortObject(object
, 503, internAtom("Couldn't schedule serving"));
1374 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
1375 request
->flags
&= ~REQUEST_WAIT_CONTINUE
;
1376 if(object
->code
== 100 && request
->request
&&
1377 !(request
->request
->flags
& REQUEST_WAIT_CONTINUE
))
1378 delayedHttpClientContinue(connection
);
1382 /* See httpServerHandlerHeaders */
1383 if((object
->flags
& OBJECT_SUPERSEDED
) &&
1384 request
->request
&& request
->request
->can_mutate
) {
1385 ObjectPtr new_object
= retainObject(request
->request
->can_mutate
);
1386 if(object
->requestor
== request
) {
1387 if(new_object
->requestor
== NULL
)
1388 new_object
->requestor
= request
;
1389 object
->requestor
= NULL
;
1391 request
->chandler
= NULL
;
1392 releaseObject(object
);
1393 request
->object
= new_object
;
1394 request
->request
->object
= new_object
;
1395 /* We're handling the wrong object now. It's simpler to
1396 rebuild the whole data structure from scratch rather than
1397 trying to compensate. */
1398 rc
= delayedHttpClientNoticeRequest(request
);
1400 do_log(L_ERROR
, "Couldn't schedule noticing of request.");
1401 abortObject(object
, 500,
1402 internAtom("Couldn't schedule "
1403 "noticing of request"));
1404 /* We're probably out of memory. What can we do? */
1405 shutdown(connection
->fd
, 1);
1410 if(object
->requestor
!= request
&& !(object
->flags
& OBJECT_ABORTED
)) {
1411 /* Make sure we don't serve an object that is stale for us
1412 unless we're the requestor. */
1413 if((object
->flags
& (OBJECT_LINEAR
| OBJECT_MUTATING
)) ||
1414 objectMustRevalidate(object
, &request
->cache_control
)) {
1415 if(object
->flags
& OBJECT_INPROGRESS
)
1417 rc
= delayedHttpClientNoticeRequest(request
);
1419 do_log(L_ERROR
, "Couldn't schedule noticing of request.");
1420 abortObject(object
, 500,
1421 internAtom("Couldn't schedule "
1422 "noticing of request"));
1424 request
->chandler
= NULL
;
1430 if(object
->flags
& (OBJECT_INITIAL
| OBJECT_VALIDATING
)) {
1431 if(object
->flags
& (OBJECT_INPROGRESS
| OBJECT_VALIDATING
)) {
1433 } else if(object
->flags
& OBJECT_FAILED
) {
1434 if(request
->error_code
)
1436 request
->error_code
,
1437 retainAtom(request
->error_message
));
1439 abortObject(object
, 500,
1440 internAtom("Error message lost in transit"));
1443 /* The request was pruned by httpServerDiscardRequests */
1444 if(chandler
== request
->chandler
) {
1446 request
->chandler
= NULL
;
1447 rc
= delayedHttpClientNoticeRequest(request
);
1449 abortObject(object
, 500,
1450 internAtom("Couldn't allocate "
1451 "delayed notice request"));
1455 abortObject(object
, 500,
1456 internAtom("Wrong request pruned -- "
1457 "this shouldn't happen"));
1462 if(request
->object
->flags
& OBJECT_DYNAMIC
) {
1463 if(objectHoleSize(request
->object
, 0) == 0) {
1467 /* We really should request again if that is not the case */
1471 lockChunk(object
, request
->from
/ CHUNK_SIZE
);
1472 request
->chandler
= NULL
;
1473 rc
= delayedHttpServeObject(connection
);
1475 unlockChunk(object
, request
->from
/ CHUNK_SIZE
);
1476 do_log(L_ERROR
, "Couldn't schedule serving.\n");
1477 abortObject(object
, 503, internAtom("Couldn't schedule serving"));
1483 httpClientSideRequest(HTTPRequestPtr request
)
1485 HTTPConnectionPtr connection
= request
->connection
;
1487 if(request
->from
< 0 || request
->to
>= 0) {
1488 httpClientNoticeError(request
, 501,
1489 internAtom("Partial requests not implemented"));
1490 httpClientDiscardBody(connection
);
1493 if(connection
->reqte
!= TE_IDENTITY
) {
1494 httpClientNoticeError(request
, 501,
1495 internAtom("Chunked requests not implemented"));
1496 httpClientDiscardBody(connection
);
1499 if(connection
->bodylen
< 0) {
1500 httpClientNoticeError(request
, 502,
1501 internAtom("POST or PUT without "
1503 httpClientDiscardBody(connection
);
1506 if(connection
->reqlen
< 0) {
1507 httpClientNoticeError(request
, 502,
1508 internAtom("Incomplete POST or PUT"));
1509 httpClientDiscardBody(connection
);
1513 return httpClientNoticeRequest(request
, 0);
1517 httpClientSideHandler(int status
,
1518 FdEventHandlerPtr event
,
1519 StreamRequestPtr srequest
)
1521 HTTPConnectionPtr connection
= srequest
->data
;
1522 HTTPRequestPtr request
= connection
->request
;
1523 HTTPRequestPtr requestee
;
1524 HTTPConnectionPtr server
;
1527 AtomPtr message
= NULL
;
1529 assert(connection
->flags
& CONN_SIDE_READER
);
1531 if((request
->object
->flags
& OBJECT_ABORTED
) ||
1532 !(request
->object
->flags
& OBJECT_INPROGRESS
)) {
1533 code
= request
->object
->code
;
1534 message
= retainAtom(request
->object
->message
);
1539 do_log_error(L_ERROR
, -status
, "Reading from client");
1541 message
= internAtomError(-status
, "Couldn't read from client");
1545 requestee
= request
->request
;
1546 server
= requestee
->connection
;
1548 push
= MIN(srequest
->offset
- connection
->reqlen
,
1549 connection
->bodylen
- connection
->reqoffset
);
1551 connection
->reqlen
+= push
;
1552 httpServerDoSide(server
);
1556 if(server
->reqoffset
>= connection
->bodylen
) {
1557 connection
->flags
&= ~(CONN_READER
| CONN_SIDE_READER
);
1562 do_log(L_ERROR
, "Incomplete client request.\n");
1564 message
= internAtom("Incomplete client request");
1567 request
->error_code
= code
;
1568 if(request
->error_message
)
1569 releaseAtom(request
->error_message
);
1570 request
->error_message
= message
;
1571 if(request
->error_headers
)
1572 releaseAtom(request
->error_headers
);
1573 request
->error_headers
= NULL
;
1575 if(request
->request
) {
1576 shutdown(request
->request
->connection
->fd
, 2);
1577 pokeFdEvent(request
->request
->connection
->fd
, -ESHUTDOWN
, POLLOUT
);
1579 notifyObject(request
->object
);
1580 connection
->flags
&= ~CONN_SIDE_READER
;
1581 httpClientDiscardBody(connection
);
1586 httpServeObject(HTTPConnectionPtr connection
)
1588 HTTPRequestPtr request
= connection
->request
;
1589 ObjectPtr object
= request
->object
;
1590 int i
= request
->from
/ CHUNK_SIZE
;
1591 int j
= request
->from
% CHUNK_SIZE
;
1593 int bufsize
= CHUNK_SIZE
;
1594 int condition_result
;
1596 object
->atime
= current_time
.tv_sec
;
1597 objectMetadataChanged(object
, 0);
1599 httpSetTimeout(connection
, -1);
1601 if((request
->error_code
&& relaxTransparency
<= 0) ||
1602 object
->flags
& OBJECT_INITIAL
) {
1603 object
->flags
&= ~OBJECT_FAILED
;
1604 unlockChunk(object
, i
);
1605 if(request
->error_code
)
1606 return httpClientRawError(connection
,
1607 request
->error_code
,
1608 retainAtom(request
->error_message
), 0);
1610 return httpClientRawError(connection
,
1611 500, internAtom("Object vanished."), 0);
1614 if(!(object
->flags
& OBJECT_INPROGRESS
) && object
->code
== 0) {
1615 if(object
->flags
& OBJECT_INITIAL
) {
1616 unlockChunk(object
, i
);
1617 return httpClientRawError(connection
, 503,
1618 internAtom("Error message lost"), 0);
1621 unlockChunk(object
, i
);
1622 do_log(L_ERROR
, "Internal proxy error: object has no code.\n");
1623 return httpClientRawError(connection
, 500,
1624 internAtom("Internal proxy error: "
1625 "object has no code"), 0);
1629 condition_result
= httpCondition(object
, request
->condition
);
1631 if(condition_result
== CONDITION_FAILED
) {
1632 unlockChunk(object
, i
);
1633 return httpClientRawError(connection
, 412,
1634 internAtom("Precondition failed"), 0);
1635 } else if(condition_result
== CONDITION_NOT_MODIFIED
) {
1636 unlockChunk(object
, i
);
1637 return httpClientRawError(connection
, 304,
1638 internAtom("Not modified"), 0);
1641 objectFillFromDisk(object
, request
->from
,
1642 (request
->method
== METHOD_HEAD
||
1643 condition_result
!= CONDITION_MATCH
) ? 0 : 1);
1645 if(((object
->flags
& OBJECT_LINEAR
) &&
1646 (object
->requestor
!= connection
->request
)) ||
1647 ((object
->flags
& OBJECT_SUPERSEDED
) &&
1648 !(object
->flags
& OBJECT_LINEAR
))) {
1649 if(request
->request
) {
1650 request
->request
->request
= NULL
;
1651 request
->request
= NULL
;
1652 request
->object
->requestor
= NULL
;
1654 object
= makeObject(OBJECT_HTTP
,
1655 object
->key
, object
->key_size
, 1, 0,
1656 object
->request
, NULL
);
1657 if(request
->object
->requestor
== request
)
1658 request
->object
->requestor
= NULL
;
1659 unlockChunk(request
->object
, i
);
1660 releaseObject(request
->object
);
1661 request
->object
= NULL
;
1662 if(object
== NULL
) {
1663 do_log(L_ERROR
, "Couldn't allocate object.");
1664 return httpClientRawError(connection
, 501,
1665 internAtom("Couldn't allocate object"),
1668 if(urlIsLocal(object
->key
, object
->key_size
)) {
1669 object
->flags
|= OBJECT_LOCAL
;
1670 object
->request
= httpLocalRequest
;
1672 request
->object
= object
;
1673 connection
->flags
&= ~CONN_WRITER
;
1674 return httpClientNoticeRequest(request
, 1);
1677 if(object
->flags
& OBJECT_ABORTED
) {
1678 unlockChunk(object
, i
);
1679 return httpClientNoticeError(request
, object
->code
,
1680 retainAtom(object
->message
));
1683 if(connection
->buf
== NULL
)
1684 connection
->buf
= get_chunk();
1685 if(connection
->buf
== NULL
) {
1686 unlockChunk(object
, i
);
1687 do_log(L_ERROR
, "Couldn't allocate client buffer.\n");
1688 connection
->flags
&= ~CONN_WRITER
;
1689 httpClientFinish(connection
, 1);
1693 if(object
->length
>= 0 && request
->to
>= object
->length
)
1694 request
->to
= object
->length
;
1696 if(request
->from
> 0 || request
->to
>= 0) {
1697 if(request
->method
== METHOD_HEAD
) {
1698 request
->to
= request
->from
;
1699 } else if(request
->to
< 0) {
1700 if(object
->length
>= 0)
1701 request
->to
= object
->length
;
1707 connection
->len
= 0;
1709 if((request
->from
<= 0 && request
->to
< 0) ||
1710 request
->method
== METHOD_HEAD
) {
1711 n
= snnprintf(connection
->buf
, 0, bufsize
,
1713 object
->code
, atomString(object
->message
));
1715 if((object
->length
>= 0 && request
->from
>= object
->length
) ||
1716 (request
->to
>= 0 && request
->from
>= request
->to
)) {
1717 unlockChunk(object
, i
);
1718 return httpClientRawError(connection
, 416,
1719 internAtom("Requested range "
1723 n
= snnprintf(connection
->buf
, 0, bufsize
,
1724 "HTTP/1.1 206 Partial content");
1728 n
= httpWriteObjectHeaders(connection
->buf
, n
, bufsize
,
1729 object
, request
->from
, request
->to
);
1733 if(request
->method
!= METHOD_HEAD
&&
1734 condition_result
!= CONDITION_NOT_MODIFIED
&&
1735 request
->to
< 0 && object
->length
< 0) {
1736 if(connection
->version
== HTTP_11
) {
1737 connection
->te
= TE_CHUNKED
;
1738 n
= snnprintf(connection
->buf
, n
, bufsize
,
1739 "\r\nTransfer-Encoding: chunked");
1741 request
->flags
&= ~REQUEST_PERSISTENT
;
1745 if(object
->age
< current_time
.tv_sec
) {
1746 n
= snnprintf(connection
->buf
, n
, bufsize
,
1748 (int)(current_time
.tv_sec
- object
->age
));
1750 n
= snnprintf(connection
->buf
, n
, bufsize
,
1751 "\r\nConnection: %s",
1752 (request
->flags
& REQUEST_PERSISTENT
) ?
1753 "keep-alive" : "close");
1755 if(!(object
->flags
& OBJECT_LOCAL
)) {
1756 if((object
->flags
& OBJECT_FAILED
) && !proxyOffline
) {
1757 n
= snnprintf(connection
->buf
, n
, bufsize
,
1758 "\r\nWarning: 111 %s Revalidation failed",
1760 if(request
->error_code
)
1761 n
= snnprintf(connection
->buf
, n
, bufsize
,
1763 request
->error_code
,
1764 atomString(request
->error_message
));
1765 object
->flags
&= ~OBJECT_FAILED
;
1766 } else if(proxyOffline
&&
1767 objectMustRevalidate(object
, &request
->cache_control
)) {
1768 n
= snnprintf(connection
->buf
, n
, bufsize
,
1769 "\r\nWarning: 112 %s Disconnected operation",
1771 } else if(objectIsStale(object
, &request
->cache_control
)) {
1772 n
= snnprintf(connection
->buf
, n
, bufsize
,
1773 "\r\nWarning: 110 %s Object is stale",
1775 } else if(object
->expires
< 0 && object
->max_age
< 0 &&
1776 object
->age
< current_time
.tv_sec
- 24 * 3600) {
1777 n
= snnprintf(connection
->buf
, n
, bufsize
,
1778 "\r\nWarning: 113 %s Heuristic expiration",
1783 n
= snnprintf(connection
->buf
, n
, bufsize
, "\r\n\r\n");
1788 connection
->offset
= request
->from
;
1790 if(request
->method
== METHOD_HEAD
||
1791 condition_result
== CONDITION_NOT_MODIFIED
||
1792 (object
->flags
& OBJECT_ABORTED
)) {
1795 if(i
< object
->numchunks
) {
1796 if(object
->chunks
[i
].size
<= j
)
1799 len
= object
->chunks
[i
].size
- j
;
1803 if(request
->to
>= 0)
1804 len
= MIN(len
, request
->to
- request
->from
);
1807 connection
->offset
= request
->from
;
1808 httpSetTimeout(connection
, clientTimeout
);
1809 do_log(D_CLIENT_DATA
, "Serving on 0x%lx for 0x%lx: offset %d len %d\n",
1810 (unsigned long)connection
, (unsigned long)object
,
1811 connection
->offset
, len
);
1812 do_stream_h(IO_WRITE
|
1813 (connection
->te
== TE_CHUNKED
&& len
> 0 ? IO_CHUNKED
: 0),
1816 object
->chunks
[i
].data
+ j
, len
,
1817 httpServeObjectStreamHandler
, connection
);
1822 connection
->len
= 0;
1823 if(!(connection
->flags
& CONN_BIGBUF
))
1824 rc
= httpConnectionBigify(connection
);
1826 bufsize
= bigBufferSize
;
1829 unlockChunk(object
, i
);
1830 return httpClientRawError(connection
, 500,
1832 internAtom("No space for headers") :
1833 internAtom("Couldn't allocate big buffer"), 0);
1837 httpServeObjectDelayed(TimeEventHandlerPtr event
)
1839 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1840 httpServeObject(connection
);
1845 delayedHttpServeObject(HTTPConnectionPtr connection
)
1847 TimeEventHandlerPtr event
;
1849 assert(connection
->request
->object
->chunks
[connection
->request
->from
/
1850 CHUNK_SIZE
].locked
> 0);
1852 event
= scheduleTimeEvent(-1, httpServeObjectDelayed
,
1853 sizeof(connection
), &connection
);
1854 if(!event
) return -1;
1859 httpServeObjectFinishHandler(int status
,
1860 FdEventHandlerPtr event
,
1861 StreamRequestPtr srequest
)
1863 HTTPConnectionPtr connection
= srequest
->data
;
1864 HTTPRequestPtr request
= connection
->request
;
1867 assert(!request
->chandler
);
1869 if(status
== 0 && !streamRequestDone(srequest
))
1872 httpSetTimeout(connection
, -1);
1875 do_log(L_ERROR
, "Couldn't terminate chunked reply\n");
1876 httpClientFinish(connection
, 1);
1878 httpClientFinish(connection
, 0);
1884 httpServeChunk(HTTPConnectionPtr connection
)
1886 HTTPRequestPtr request
= connection
->request
;
1887 ObjectPtr object
= request
->object
;
1888 int i
= connection
->offset
/ CHUNK_SIZE
;
1889 int j
= connection
->offset
- (i
* CHUNK_SIZE
);
1890 int to
, len
, len2
, end
;
1893 if(object
->flags
& OBJECT_ABORTED
)
1894 goto fail_no_unlock
;
1896 if(object
->length
>= 0 && request
->to
>= 0)
1897 to
= MIN(request
->to
, object
->length
);
1898 else if(object
->length
>= 0)
1899 to
= object
->length
;
1900 else if(request
->to
>= 0)
1905 lockChunk(object
, i
);
1907 if(i
< object
->numchunks
)
1908 len
= object
->chunks
[i
].size
- j
;
1910 if(request
->method
!= METHOD_HEAD
&&
1911 len
< CHUNK_SIZE
&& connection
->offset
+ len
< to
) {
1912 objectFillFromDisk(object
, connection
->offset
+ len
, 2);
1913 len
= object
->chunks
[i
].size
- j
;
1917 len
= MIN(len
, to
- connection
->offset
);
1920 if(to
>= 0 && connection
->offset
>= to
) {
1921 if(request
->chandler
) {
1922 unregisterConditionHandler(request
->chandler
);
1923 request
->chandler
= NULL
;
1925 unlockChunk(object
, i
);
1926 if(connection
->te
== TE_CHUNKED
) {
1927 httpSetTimeout(connection
, clientTimeout
);
1928 do_stream(IO_WRITE
| IO_CHUNKED
| IO_END
,
1929 connection
->fd
, 0, NULL
, 0,
1930 httpServeObjectFinishHandler
, connection
);
1932 httpClientFinish(connection
,
1933 !(object
->length
>= 0 &&
1934 connection
->offset
>= object
->length
));
1938 if(!request
->chandler
) {
1940 conditionWait(&object
->condition
,
1941 httpServeObjectHandler
,
1942 sizeof(connection
), &connection
);
1943 if(!request
->chandler
) {
1944 do_log(L_ERROR
, "Couldn't register condition handler\n");
1948 if(!(object
->flags
& OBJECT_INPROGRESS
)) {
1949 if(object
->flags
& OBJECT_SUPERSEDED
) {
1952 if(REQUEST_SIDE(request
)) goto fail
;
1953 rc
= object
->request(object
, request
->method
,
1954 connection
->offset
, -1, request
,
1955 object
->request_closure
);
1956 if(rc
<= 0) goto fail
;
1962 if(request
->method
!= METHOD_HEAD
)
1963 objectFillFromDisk(object
, (i
+ 1) * CHUNK_SIZE
, 1);
1964 if(request
->chandler
) {
1965 unregisterConditionHandler(request
->chandler
);
1966 request
->chandler
= NULL
;
1969 if(j
+ len
== CHUNK_SIZE
&& object
->numchunks
> i
+ 1) {
1970 len2
= object
->chunks
[i
+ 1].size
;
1972 len2
= MIN(len2
, to
- (i
+ 1) * CHUNK_SIZE
);
1974 /* Lock early -- httpServerRequest may get_chunk */
1976 lockChunk(object
, i
+ 1);
1977 if(object
->length
>= 0 &&
1978 connection
->offset
+ len
+ len2
== object
->length
)
1983 if(!(object
->flags
& OBJECT_INPROGRESS
) && !REQUEST_SIDE(request
)) {
1984 if(object
->chunks
[i
].size
< CHUNK_SIZE
&&
1985 to
>= 0 && connection
->offset
+ len
+ 1 < to
)
1986 object
->request(object
, request
->method
,
1987 connection
->offset
+ len
, -1, request
,
1988 object
->request_closure
);
1989 else if(i
+ 1 < object
->numchunks
&&
1990 object
->chunks
[i
+ 1].size
== 0 &&
1991 to
>= 0 && (i
+ 1) * CHUNK_SIZE
+ 1 < to
)
1992 object
->request(object
, request
->method
,
1993 (i
+ 1) * CHUNK_SIZE
, -1, request
,
1994 object
->request_closure
);
1997 httpSetTimeout(connection
, clientTimeout
);
1998 do_log(D_CLIENT_DATA
,
1999 "Serving on 0x%lx for 0x%lx: offset %d len %d\n",
2000 (unsigned long)connection
, (unsigned long)object
,
2001 connection
->offset
, len
);
2002 /* IO_NOTNOW in order to give other clients a chance to run. */
2003 do_stream(IO_WRITE
| IO_NOTNOW
|
2004 (connection
->te
== TE_CHUNKED
? IO_CHUNKED
: 0) |
2007 object
->chunks
[i
].data
+ j
, len
,
2008 httpServeObjectStreamHandler
, connection
);
2010 httpSetTimeout(connection
, clientTimeout
);
2011 do_log(D_CLIENT_DATA
,
2012 "Serving on 0x%lx for 0x%lx: offset %d len %d + %d\n",
2013 (unsigned long)connection
, (unsigned long)object
,
2014 connection
->offset
, len
, len2
);
2015 do_stream_2(IO_WRITE
| IO_NOTNOW
|
2016 (connection
->te
== TE_CHUNKED
? IO_CHUNKED
: 0) |
2019 object
->chunks
[i
].data
+ j
, len
,
2020 object
->chunks
[i
+ 1].data
, len2
,
2021 httpServeObjectStreamHandler2
, connection
);
2029 unlockChunk(object
, i
);
2031 if(request
->chandler
)
2032 unregisterConditionHandler(request
->chandler
);
2033 request
->chandler
= NULL
;
2034 httpClientFinish(connection
, 1);
2039 httpServeChunkDelayed(TimeEventHandlerPtr event
)
2041 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
2042 unlockChunk(connection
->request
->object
,
2043 connection
->offset
/ CHUNK_SIZE
);
2044 httpServeChunk(connection
);
2049 delayedHttpServeChunk(HTTPConnectionPtr connection
)
2051 TimeEventHandlerPtr event
;
2052 event
= scheduleTimeEvent(-1, httpServeChunkDelayed
,
2053 sizeof(connection
), &connection
);
2054 if(!event
) return -1;
2059 httpServeObjectHandler(int status
, ConditionHandlerPtr chandler
)
2061 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)chandler
->data
;
2062 HTTPRequestPtr request
= connection
->request
;
2065 if((request
->object
->flags
& OBJECT_ABORTED
) || status
< 0) {
2066 shutdown(connection
->fd
, 1);
2067 httpSetTimeout(connection
, 10);
2068 /* httpServeChunk will take care of the error. */
2071 httpSetTimeout(connection
, -1);
2073 request
->chandler
= NULL
;
2074 rc
= delayedHttpServeChunk(connection
);
2076 do_log(L_ERROR
, "Couldn't schedule serving.\n");
2077 abortObject(request
->object
, 503,
2078 internAtom("Couldn't schedule serving"));
2084 httpServeObjectStreamHandlerCommon(int kind
, int status
,
2085 FdEventHandlerPtr event
,
2086 StreamRequestPtr srequest
)
2088 HTTPConnectionPtr connection
= srequest
->data
;
2089 HTTPRequestPtr request
= connection
->request
;
2090 int condition_result
= httpCondition(request
->object
, request
->condition
);
2091 int i
= connection
->offset
/ CHUNK_SIZE
;
2093 assert(!request
->chandler
);
2095 if(status
== 0 && !streamRequestDone(srequest
)) {
2096 httpSetTimeout(connection
, clientTimeout
);
2100 httpSetTimeout(connection
, -1);
2102 unlockChunk(request
->object
, i
);
2104 unlockChunk(request
->object
, i
+ 1);
2108 do_log_error(status
== -ECONNRESET
? D_IO
: L_ERROR
,
2109 -status
, "Couldn't write to client");
2110 if(status
== -EIO
|| status
== -ESHUTDOWN
)
2111 httpClientFinish(connection
, 2);
2113 httpClientFinish(connection
, 1);
2115 do_log(D_IO
, "Couldn't write to client: short write.\n");
2116 httpClientFinish(connection
, 2);
2121 if(srequest
->operation
& IO_CHUNKED
) {
2122 assert(srequest
->offset
> 2);
2123 connection
->offset
+= srequest
->offset
- 2;
2125 connection
->offset
+= srequest
->offset
;
2127 request
->flags
&= ~REQUEST_REQUESTED
;
2129 if(request
->object
->flags
& OBJECT_ABORTED
) {
2130 httpClientFinish(connection
, 1);
2134 if(request
->method
== METHOD_HEAD
||
2135 request
->object
->code
== 204 ||
2136 request
->object
->code
< 200 ||
2137 condition_result
== CONDITION_NOT_MODIFIED
) {
2138 httpClientFinish(connection
, 0);
2142 if(srequest
->operation
& IO_END
)
2143 httpClientFinish(connection
, 0);
2145 httpConnectionDestroyBuf(connection
);
2146 httpServeChunk(connection
);
2152 httpServeObjectStreamHandler(int status
,
2153 FdEventHandlerPtr event
,
2154 StreamRequestPtr srequest
)
2156 return httpServeObjectStreamHandlerCommon(1, status
, event
, srequest
);
2160 httpServeObjectStreamHandler2(int status
,
2161 FdEventHandlerPtr event
,
2162 StreamRequestPtr srequest
)
2164 return httpServeObjectStreamHandlerCommon(2, status
, event
, srequest
);