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
->reqbegin
< connection
->reqlen
) {
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
);
990 connection
->reqte
= TE_UNKNOWN
;
992 if(connection
->bodylen
> 0) {
993 httpSetTimeout(connection
, clientTimeout
);
994 do_stream_buf(IO_READ
| IO_NOTNOW
,
995 connection
->fd
, connection
->reqlen
,
996 &connection
->reqbuf
, CHUNK_SIZE
,
997 httpClientDiscardHandler
, connection
);
1001 if(connection
->reqlen
> connection
->reqbegin
) {
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 if(request
->condition
&& request
->condition
->ifrange
) {
1153 strcmp(object
->etag
, request
->condition
->ifrange
) != 0) {
1159 if(object
->flags
& OBJECT_DYNAMIC
) {
1164 if(request
->method
== METHOD_HEAD
)
1165 haveData
= !(request
->object
->flags
& OBJECT_INITIAL
);
1168 (request
->object
->length
>= 0 &&
1169 request
->object
->length
<= request
->from
) ||
1170 (objectHoleSize(request
->object
, request
->from
) == 0);
1172 if(request
->flags
& REQUEST_REQUESTED
)
1174 else if(novalidate
|| (!local
&& proxyOffline
))
1178 objectMustRevalidate(request
->object
, &request
->cache_control
);
1179 else if(request
->cache_control
.flags
& CACHE_ONLY_IF_CACHED
)
1181 else if((request
->object
->flags
& OBJECT_FAILED
) &&
1182 !(object
->flags
& OBJECT_INPROGRESS
) &&
1185 else if(request
->method
!= METHOD_HEAD
&&
1186 !objectHasData(object
, request
->from
, request
->to
) &&
1187 !(object
->flags
& OBJECT_INPROGRESS
))
1189 else if(objectMustRevalidate((relaxTransparency
<= 1 ?
1190 request
->object
: NULL
),
1191 &request
->cache_control
))
1196 if(request
->cache_control
.flags
& CACHE_ONLY_IF_CACHED
) {
1200 connection
->flags
|= CONN_WRITER
;
1201 return httpClientRawError(connection
, 504,
1202 internAtom("Object not in cache"),
1209 if(!(request
->object
->flags
& OBJECT_VALIDATING
) &&
1210 ((!validate
&& haveData
) ||
1211 (request
->object
->flags
& OBJECT_FAILED
))) {
1213 connection
->flags
|= CONN_WRITER
;
1214 lockChunk(request
->object
, request
->from
/ CHUNK_SIZE
);
1215 return httpServeObject(connection
);
1221 if((request
->flags
& REQUEST_REQUESTED
) &&
1222 !(request
->object
->flags
& OBJECT_INPROGRESS
)) {
1223 /* This can happen either because the server side ran out of
1224 memory, or because it is using HEAD validation. We mark
1225 the object to be fetched again. */
1226 request
->flags
&= ~REQUEST_REQUESTED
;
1230 connection
->flags
|= CONN_WRITER
;
1231 if(!local
&& proxyOffline
)
1232 return httpClientRawError(connection
, 502,
1233 internAtom("Disconnected operation "
1234 "and object not in cache"),
1237 conditionWait(&request
->object
->condition
, httpClientGetHandler
,
1238 sizeof(request
), &request
);
1239 if(request
->chandler
== NULL
) {
1240 do_log(L_ERROR
, "Couldn't register condition handler.\n");
1241 return httpClientRawError(connection
, 503,
1242 internAtom("Couldn't register "
1243 "condition handler"), 0);
1247 if(request
->object
->flags
& OBJECT_VALIDATING
)
1250 conditional
= (haveData
&& request
->method
== METHOD_GET
);
1251 if(!mindlesslyCacheVary
&& (request
->object
->cache_control
& CACHE_VARY
))
1252 conditional
= conditional
&& (request
->object
->etag
!= NULL
);
1255 conditional
&& !(request
->object
->cache_control
& CACHE_MISMATCH
);
1257 request
->object
->flags
|= OBJECT_VALIDATING
;
1258 rc
= request
->object
->request(request
->object
,
1259 conditional
? METHOD_CONDITIONAL_GET
:
1261 request
->from
, request
->to
, request
,
1262 request
->object
->request_closure
);
1264 if(request
->chandler
)
1265 unregisterConditionHandler(request
->chandler
);
1266 request
->chandler
= NULL
;
1267 request
->object
->flags
&= ~OBJECT_VALIDATING
;
1268 request
->object
->flags
|= OBJECT_FAILED
;
1269 if(request
->error_message
)
1270 releaseAtom(request
->error_message
);
1271 request
->error_code
= 503;
1272 request
->error_message
= internAtom("Couldn't schedule get");
1278 httpClientNoticeRequestDelayed(TimeEventHandlerPtr event
)
1280 HTTPRequestPtr request
= *(HTTPRequestPtr
*)event
->data
;
1281 httpClientNoticeRequest(request
, 0);
1286 delayedHttpClientNoticeRequest(HTTPRequestPtr request
)
1288 TimeEventHandlerPtr event
;
1289 event
= scheduleTimeEvent(-1, httpClientNoticeRequestDelayed
,
1290 sizeof(request
), &request
);
1297 httpClientContinueDelayed(TimeEventHandlerPtr event
)
1299 static char httpContinue
[] = "HTTP/1.1 100 Continue\r\n\r\n";
1300 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1302 do_stream(IO_WRITE
, connection
->fd
, 0, httpContinue
, 25,
1303 httpErrorNofinishStreamHandler
, connection
);
1308 delayedHttpClientContinue(HTTPConnectionPtr connection
)
1310 TimeEventHandlerPtr event
;
1311 event
= scheduleTimeEvent(-1, httpClientContinueDelayed
,
1312 sizeof(connection
), &connection
);
1319 httpClientGetHandler(int status
, ConditionHandlerPtr chandler
)
1321 HTTPRequestPtr request
= *(HTTPRequestPtr
*)chandler
->data
;
1322 HTTPConnectionPtr connection
= request
->connection
;
1323 ObjectPtr object
= request
->object
;
1326 assert(request
== connection
->request
);
1328 if(request
->request
) {
1329 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1330 assert(!request
->request
->object
||
1331 request
->request
->object
== request
->object
);
1335 object
->flags
&= ~OBJECT_VALIDATING
; /* for now */
1336 if(request
->request
&& request
->request
->request
== request
)
1337 httpServerClientReset(request
->request
);
1338 lockChunk(object
, request
->from
/ CHUNK_SIZE
);
1339 request
->chandler
= NULL
;
1340 rc
= delayedHttpServeObject(connection
);
1342 unlockChunk(object
, request
->from
/ CHUNK_SIZE
);
1343 do_log(L_ERROR
, "Couldn't schedule serving.\n");
1344 abortObject(object
, 503, internAtom("Couldn't schedule serving"));
1349 if(object
->flags
& OBJECT_VALIDATING
)
1352 if(request
->error_code
) {
1353 lockChunk(object
, request
->from
/ CHUNK_SIZE
);
1354 request
->chandler
= NULL
;
1355 rc
= delayedHttpServeObject(connection
);
1357 unlockChunk(object
, request
->from
/ CHUNK_SIZE
);
1358 do_log(L_ERROR
, "Couldn't schedule serving.\n");
1359 abortObject(object
, 503, internAtom("Couldn't schedule serving"));
1364 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
1365 if(request
->request
&&
1366 !(request
->request
->flags
& REQUEST_WAIT_CONTINUE
)) {
1367 request
->flags
&= ~REQUEST_WAIT_CONTINUE
;
1368 delayedHttpClientContinue(connection
);
1373 /* See httpServerHandlerHeaders */
1374 if((object
->flags
& OBJECT_SUPERSEDED
) &&
1375 request
->request
&& request
->request
->can_mutate
) {
1376 ObjectPtr new_object
= retainObject(request
->request
->can_mutate
);
1377 if(object
->requestor
== request
) {
1378 if(new_object
->requestor
== NULL
)
1379 new_object
->requestor
= request
;
1380 object
->requestor
= NULL
;
1382 request
->chandler
= NULL
;
1383 releaseObject(object
);
1384 request
->object
= new_object
;
1385 request
->request
->object
= new_object
;
1386 /* We're handling the wrong object now. It's simpler to
1387 rebuild the whole data structure from scratch rather than
1388 trying to compensate. */
1389 rc
= delayedHttpClientNoticeRequest(request
);
1391 do_log(L_ERROR
, "Couldn't schedule noticing of request.");
1392 abortObject(object
, 500,
1393 internAtom("Couldn't schedule "
1394 "noticing of request"));
1395 /* We're probably out of memory. What can we do? */
1396 shutdown(connection
->fd
, 1);
1401 if(object
->requestor
!= request
&& !(object
->flags
& OBJECT_ABORTED
)) {
1402 /* Make sure we don't serve an object that is stale for us
1403 unless we're the requestor. */
1404 if((object
->flags
& (OBJECT_LINEAR
| OBJECT_MUTATING
)) ||
1405 objectMustRevalidate(object
, &request
->cache_control
)) {
1406 if(object
->flags
& OBJECT_INPROGRESS
)
1408 rc
= delayedHttpClientNoticeRequest(request
);
1410 do_log(L_ERROR
, "Couldn't schedule noticing of request.");
1411 abortObject(object
, 500,
1412 internAtom("Couldn't schedule "
1413 "noticing of request"));
1415 request
->chandler
= NULL
;
1421 if(object
->flags
& (OBJECT_INITIAL
| OBJECT_VALIDATING
)) {
1422 if(object
->flags
& (OBJECT_INPROGRESS
| OBJECT_VALIDATING
)) {
1424 } else if(object
->flags
& OBJECT_FAILED
) {
1425 if(request
->error_code
)
1427 request
->error_code
,
1428 retainAtom(request
->error_message
));
1430 abortObject(object
, 500,
1431 internAtom("Error message lost in transit"));
1434 /* The request was pruned by httpServerDiscardRequests */
1435 if(chandler
== request
->chandler
) {
1437 request
->chandler
= NULL
;
1438 rc
= delayedHttpClientNoticeRequest(request
);
1440 abortObject(object
, 500,
1441 internAtom("Couldn't allocate "
1442 "delayed notice request"));
1446 abortObject(object
, 500,
1447 internAtom("Wrong request pruned -- "
1448 "this shouldn't happen"));
1453 if(request
->object
->flags
& OBJECT_DYNAMIC
) {
1454 if(objectHoleSize(request
->object
, 0) == 0) {
1458 /* We really should request again if that is not the case */
1462 lockChunk(object
, request
->from
/ CHUNK_SIZE
);
1463 request
->chandler
= NULL
;
1464 rc
= delayedHttpServeObject(connection
);
1466 unlockChunk(object
, request
->from
/ CHUNK_SIZE
);
1467 do_log(L_ERROR
, "Couldn't schedule serving.\n");
1468 abortObject(object
, 503, internAtom("Couldn't schedule serving"));
1474 httpClientSideRequest(HTTPRequestPtr request
)
1476 HTTPConnectionPtr connection
= request
->connection
;
1478 if(request
->from
< 0 || request
->to
>= 0) {
1479 httpClientNoticeError(request
, 501,
1480 internAtom("Partial requests not implemented"));
1481 httpClientDiscardBody(connection
);
1484 if(connection
->reqte
!= TE_IDENTITY
) {
1485 httpClientNoticeError(request
, 501,
1486 internAtom("Chunked requests not implemented"));
1487 httpClientDiscardBody(connection
);
1490 if(connection
->bodylen
< 0) {
1491 httpClientNoticeError(request
, 502,
1492 internAtom("POST or PUT without "
1494 httpClientDiscardBody(connection
);
1497 if(connection
->reqlen
< 0) {
1498 httpClientNoticeError(request
, 502,
1499 internAtom("Incomplete POST or PUT"));
1500 httpClientDiscardBody(connection
);
1504 return httpClientNoticeRequest(request
, 0);
1508 httpClientSideHandler(int status
,
1509 FdEventHandlerPtr event
,
1510 StreamRequestPtr srequest
)
1512 HTTPConnectionPtr connection
= srequest
->data
;
1513 HTTPRequestPtr request
= connection
->request
;
1514 HTTPRequestPtr requestee
;
1515 HTTPConnectionPtr server
;
1518 AtomPtr message
= NULL
;
1520 assert(connection
->flags
& CONN_SIDE_READER
);
1522 if((request
->object
->flags
& OBJECT_ABORTED
) ||
1523 !(request
->object
->flags
& OBJECT_INPROGRESS
)) {
1524 code
= request
->object
->code
;
1525 message
= retainAtom(request
->object
->message
);
1530 do_log_error(L_ERROR
, -status
, "Reading from client");
1532 message
= internAtomError(-status
, "Couldn't read from client");
1536 requestee
= request
->request
;
1537 server
= requestee
->connection
;
1539 push
= MIN(srequest
->offset
- connection
->reqlen
,
1540 connection
->bodylen
- connection
->reqoffset
);
1542 connection
->reqlen
+= push
;
1543 httpServerDoSide(server
);
1547 if(server
->reqoffset
>= connection
->bodylen
) {
1548 connection
->flags
&= ~(CONN_READER
| CONN_SIDE_READER
);
1553 do_log(L_ERROR
, "Incomplete client request.\n");
1555 message
= internAtom("Incomplete client request");
1558 request
->error_code
= code
;
1559 if(request
->error_message
)
1560 releaseAtom(request
->error_message
);
1561 request
->error_message
= message
;
1562 if(request
->error_headers
)
1563 releaseAtom(request
->error_headers
);
1564 request
->error_headers
= NULL
;
1566 if(request
->request
) {
1567 shutdown(request
->request
->connection
->fd
, 2);
1568 pokeFdEvent(request
->request
->connection
->fd
, -ESHUTDOWN
, POLLOUT
);
1570 notifyObject(request
->object
);
1571 connection
->flags
&= ~CONN_SIDE_READER
;
1572 httpClientDiscardBody(connection
);
1577 httpServeObject(HTTPConnectionPtr connection
)
1579 HTTPRequestPtr request
= connection
->request
;
1580 ObjectPtr object
= request
->object
;
1581 int i
= request
->from
/ CHUNK_SIZE
;
1582 int j
= request
->from
% CHUNK_SIZE
;
1584 int bufsize
= CHUNK_SIZE
;
1585 int condition_result
;
1587 object
->atime
= current_time
.tv_sec
;
1588 objectMetadataChanged(object
, 0);
1590 httpSetTimeout(connection
, -1);
1592 if((request
->error_code
&& relaxTransparency
<= 0) ||
1593 object
->flags
& OBJECT_INITIAL
) {
1594 object
->flags
&= ~OBJECT_FAILED
;
1595 unlockChunk(object
, i
);
1596 if(request
->error_code
)
1597 return httpClientRawError(connection
,
1598 request
->error_code
,
1599 retainAtom(request
->error_message
), 0);
1601 return httpClientRawError(connection
,
1602 500, internAtom("Object vanished."), 0);
1605 if(!(object
->flags
& OBJECT_INPROGRESS
) && object
->code
== 0) {
1606 if(object
->flags
& OBJECT_INITIAL
) {
1607 unlockChunk(object
, i
);
1608 return httpClientRawError(connection
, 503,
1609 internAtom("Error message lost"), 0);
1612 unlockChunk(object
, i
);
1613 do_log(L_ERROR
, "Internal proxy error: object has no code.\n");
1614 return httpClientRawError(connection
, 500,
1615 internAtom("Internal proxy error: "
1616 "object has no code"), 0);
1620 condition_result
= httpCondition(object
, request
->condition
);
1622 if(condition_result
== CONDITION_FAILED
) {
1623 unlockChunk(object
, i
);
1624 return httpClientRawError(connection
, 412,
1625 internAtom("Precondition failed"), 0);
1626 } else if(condition_result
== CONDITION_NOT_MODIFIED
) {
1627 unlockChunk(object
, i
);
1628 return httpClientRawError(connection
, 304,
1629 internAtom("Not modified"), 0);
1632 objectFillFromDisk(object
, request
->from
,
1633 (request
->method
== METHOD_HEAD
||
1634 condition_result
!= CONDITION_MATCH
) ? 0 : 1);
1636 if(((object
->flags
& OBJECT_LINEAR
) &&
1637 (object
->requestor
!= connection
->request
)) ||
1638 ((object
->flags
& OBJECT_SUPERSEDED
) &&
1639 !(object
->flags
& OBJECT_LINEAR
))) {
1640 if(request
->request
) {
1641 request
->request
->request
= NULL
;
1642 request
->request
= NULL
;
1643 request
->object
->requestor
= NULL
;
1645 object
= makeObject(OBJECT_HTTP
,
1646 object
->key
, object
->key_size
, 1, 0,
1647 object
->request
, NULL
);
1648 if(request
->object
->requestor
== request
)
1649 request
->object
->requestor
= NULL
;
1650 unlockChunk(request
->object
, i
);
1651 releaseObject(request
->object
);
1652 request
->object
= NULL
;
1653 if(object
== NULL
) {
1654 do_log(L_ERROR
, "Couldn't allocate object.");
1655 return httpClientRawError(connection
, 501,
1656 internAtom("Couldn't allocate object"),
1659 if(urlIsLocal(object
->key
, object
->key_size
)) {
1660 object
->flags
|= OBJECT_LOCAL
;
1661 object
->request
= httpLocalRequest
;
1663 request
->object
= object
;
1664 connection
->flags
&= ~CONN_WRITER
;
1665 return httpClientNoticeRequest(request
, 1);
1668 if(object
->flags
& OBJECT_ABORTED
) {
1669 unlockChunk(object
, i
);
1670 return httpClientNoticeError(request
, object
->code
,
1671 retainAtom(object
->message
));
1674 if(connection
->buf
== NULL
)
1675 connection
->buf
= get_chunk();
1676 if(connection
->buf
== NULL
) {
1677 unlockChunk(object
, i
);
1678 do_log(L_ERROR
, "Couldn't allocate client buffer.\n");
1679 connection
->flags
&= ~CONN_WRITER
;
1680 httpClientFinish(connection
, 1);
1684 if(object
->length
>= 0 && request
->to
>= object
->length
)
1687 if(request
->from
> 0 || request
->to
>= 0) {
1688 if(request
->method
== METHOD_HEAD
) {
1689 request
->to
= request
->from
;
1690 } else if(request
->to
< 0) {
1691 if(object
->length
>= 0)
1692 request
->to
= object
->length
;
1698 connection
->len
= 0;
1700 if((request
->from
<= 0 && request
->to
< 0) ||
1701 request
->method
== METHOD_HEAD
) {
1702 n
= snnprintf(connection
->buf
, 0, bufsize
,
1704 object
->code
, atomString(object
->message
));
1706 if(request
->from
> request
->to
) {
1707 unlockChunk(object
, i
);
1708 return httpClientRawError(connection
, 416,
1709 internAtom("Requested range "
1713 n
= snnprintf(connection
->buf
, 0, bufsize
,
1714 "HTTP/1.1 206 Partial content");
1718 n
= httpWriteObjectHeaders(connection
->buf
, n
, bufsize
,
1719 object
, request
->from
, request
->to
);
1723 if(request
->method
!= METHOD_HEAD
&&
1724 condition_result
!= CONDITION_NOT_MODIFIED
&&
1725 request
->to
< 0 && object
->length
< 0) {
1726 if(connection
->version
== HTTP_11
) {
1727 connection
->te
= TE_CHUNKED
;
1728 n
= snnprintf(connection
->buf
, n
, bufsize
,
1729 "\r\nTransfer-Encoding: chunked");
1731 request
->flags
&= ~REQUEST_PERSISTENT
;
1735 if(object
->age
< current_time
.tv_sec
) {
1736 n
= snnprintf(connection
->buf
, n
, bufsize
,
1738 (int)(current_time
.tv_sec
- object
->age
));
1740 n
= snnprintf(connection
->buf
, n
, bufsize
,
1741 "\r\nConnection: %s",
1742 (request
->flags
& REQUEST_PERSISTENT
) ?
1743 "keep-alive" : "close");
1745 if(!(object
->flags
& OBJECT_LOCAL
)) {
1746 if((object
->flags
& OBJECT_FAILED
) && !proxyOffline
) {
1747 n
= snnprintf(connection
->buf
, n
, bufsize
,
1748 "\r\nWarning: 111 %s:%d Revalidation failed",
1749 proxyName
->string
, proxyPort
);
1750 if(request
->error_code
)
1751 n
= snnprintf(connection
->buf
, n
, bufsize
,
1753 request
->error_code
,
1754 atomString(request
->error_message
));
1755 object
->flags
&= ~OBJECT_FAILED
;
1756 } else if(proxyOffline
&&
1757 objectMustRevalidate(object
, &request
->cache_control
)) {
1758 n
= snnprintf(connection
->buf
, n
, bufsize
,
1759 "\r\nWarning: 112 %s:%d Disconnected operation",
1760 proxyName
->string
, proxyPort
);
1761 } else if(objectIsStale(object
, &request
->cache_control
)) {
1762 n
= snnprintf(connection
->buf
, n
, bufsize
,
1763 "\r\nWarning: 110 %s:%d Object is stale",
1764 proxyName
->string
, proxyPort
);
1765 } else if(object
->expires
< 0 && object
->max_age
< 0 &&
1766 object
->age
< current_time
.tv_sec
- 24 * 3600) {
1767 n
= snnprintf(connection
->buf
, n
, bufsize
,
1768 "\r\nWarning: 113 %s:%d Heuristic expiration",
1769 proxyName
->string
, proxyPort
);
1773 n
= snnprintf(connection
->buf
, n
, bufsize
, "\r\n\r\n");
1778 connection
->offset
= request
->from
;
1780 if(request
->method
== METHOD_HEAD
||
1781 condition_result
== CONDITION_NOT_MODIFIED
||
1782 (object
->flags
& OBJECT_ABORTED
)) {
1785 if(i
< object
->numchunks
) {
1786 if(object
->chunks
[i
].size
<= j
)
1789 len
= object
->chunks
[i
].size
- j
;
1793 if(request
->to
>= 0)
1794 len
= MIN(len
, request
->to
- request
->from
);
1797 connection
->offset
= request
->from
;
1798 httpSetTimeout(connection
, clientTimeout
);
1799 do_log(D_CLIENT_DATA
, "Serving on 0x%lx for 0x%lx: offset %d len %d\n",
1800 (unsigned long)connection
, (unsigned long)object
,
1801 connection
->offset
, len
);
1802 do_stream_h(IO_WRITE
|
1803 (connection
->te
== TE_CHUNKED
&& len
> 0 ? IO_CHUNKED
: 0),
1806 object
->chunks
[i
].data
+ j
, len
,
1807 httpServeObjectStreamHandler
, connection
);
1812 connection
->len
= 0;
1813 if(!(connection
->flags
& CONN_BIGBUF
))
1814 rc
= httpConnectionBigify(connection
);
1816 bufsize
= bigBufferSize
;
1819 unlockChunk(object
, i
);
1820 return httpClientRawError(connection
, 500,
1822 internAtom("No space for headers") :
1823 internAtom("Couldn't allocate big buffer"), 0);
1827 httpServeObjectDelayed(TimeEventHandlerPtr event
)
1829 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1830 httpServeObject(connection
);
1835 delayedHttpServeObject(HTTPConnectionPtr connection
)
1837 TimeEventHandlerPtr event
;
1839 assert(connection
->request
->object
->chunks
[connection
->request
->from
/
1840 CHUNK_SIZE
].locked
> 0);
1842 event
= scheduleTimeEvent(-1, httpServeObjectDelayed
,
1843 sizeof(connection
), &connection
);
1844 if(!event
) return -1;
1849 httpServeObjectFinishHandler(int status
,
1850 FdEventHandlerPtr event
,
1851 StreamRequestPtr srequest
)
1853 HTTPConnectionPtr connection
= srequest
->data
;
1854 HTTPRequestPtr request
= connection
->request
;
1857 assert(!request
->chandler
);
1859 if(status
== 0 && !streamRequestDone(srequest
))
1862 httpSetTimeout(connection
, -1);
1865 do_log(L_ERROR
, "Couldn't terminate chunked reply\n");
1866 httpClientFinish(connection
, 1);
1868 httpClientFinish(connection
, 0);
1874 httpServeChunk(HTTPConnectionPtr connection
)
1876 HTTPRequestPtr request
= connection
->request
;
1877 ObjectPtr object
= request
->object
;
1878 int i
= connection
->offset
/ CHUNK_SIZE
;
1879 int j
= connection
->offset
- (i
* CHUNK_SIZE
);
1880 int to
, len
, len2
, end
;
1883 if(object
->flags
& OBJECT_ABORTED
)
1884 goto fail_no_unlock
;
1886 if(object
->length
>= 0 && request
->to
>= 0)
1887 to
= MIN(request
->to
, object
->length
);
1888 else if(object
->length
>= 0)
1889 to
= object
->length
;
1890 else if(request
->to
>= 0)
1895 lockChunk(object
, i
);
1897 if(i
< object
->numchunks
)
1898 len
= object
->chunks
[i
].size
- j
;
1900 if(request
->method
!= METHOD_HEAD
&&
1901 len
< CHUNK_SIZE
&& connection
->offset
+ len
< to
) {
1902 objectFillFromDisk(object
, connection
->offset
+ len
, 2);
1903 len
= object
->chunks
[i
].size
- j
;
1907 len
= MIN(len
, to
- connection
->offset
);
1910 if(to
>= 0 && connection
->offset
>= to
) {
1911 if(request
->chandler
) {
1912 unregisterConditionHandler(request
->chandler
);
1913 request
->chandler
= NULL
;
1915 unlockChunk(object
, i
);
1916 if(connection
->te
== TE_CHUNKED
) {
1917 httpSetTimeout(connection
, clientTimeout
);
1918 do_stream(IO_WRITE
| IO_CHUNKED
| IO_END
,
1919 connection
->fd
, 0, NULL
, 0,
1920 httpServeObjectFinishHandler
, connection
);
1922 httpClientFinish(connection
,
1923 !(object
->length
>= 0 &&
1924 connection
->offset
>= object
->length
));
1928 if(!request
->chandler
) {
1930 conditionWait(&object
->condition
,
1931 httpServeObjectHandler
,
1932 sizeof(connection
), &connection
);
1933 if(!request
->chandler
) {
1934 do_log(L_ERROR
, "Couldn't register condition handler\n");
1938 if(!(object
->flags
& OBJECT_INPROGRESS
)) {
1939 if(object
->flags
& OBJECT_SUPERSEDED
) {
1942 if(REQUEST_SIDE(request
)) goto fail
;
1943 rc
= object
->request(object
, request
->method
,
1944 connection
->offset
, -1, request
,
1945 object
->request_closure
);
1946 if(rc
<= 0) goto fail
;
1952 if(request
->method
!= METHOD_HEAD
)
1953 objectFillFromDisk(object
, (i
+ 1) * CHUNK_SIZE
, 1);
1954 if(request
->chandler
) {
1955 unregisterConditionHandler(request
->chandler
);
1956 request
->chandler
= NULL
;
1959 if(j
+ len
== CHUNK_SIZE
&& object
->numchunks
> i
+ 1) {
1960 len2
= object
->chunks
[i
+ 1].size
;
1962 len2
= MIN(len2
, to
- (i
+ 1) * CHUNK_SIZE
);
1964 /* Lock early -- httpServerRequest may get_chunk */
1966 lockChunk(object
, i
+ 1);
1967 if(object
->length
>= 0 &&
1968 connection
->offset
+ len
+ len2
== object
->length
)
1973 if(!(object
->flags
& OBJECT_INPROGRESS
) && !REQUEST_SIDE(request
)) {
1974 if(object
->chunks
[i
].size
< CHUNK_SIZE
&&
1975 to
>= 0 && connection
->offset
+ len
+ 1 < to
)
1976 object
->request(object
, request
->method
,
1977 connection
->offset
+ len
, -1, request
,
1978 object
->request_closure
);
1979 else if(i
+ 1 < object
->numchunks
&&
1980 object
->chunks
[i
+ 1].size
== 0 &&
1981 to
>= 0 && (i
+ 1) * CHUNK_SIZE
+ 1 < to
)
1982 object
->request(object
, request
->method
,
1983 (i
+ 1) * CHUNK_SIZE
, -1, request
,
1984 object
->request_closure
);
1987 httpSetTimeout(connection
, clientTimeout
);
1988 do_log(D_CLIENT_DATA
,
1989 "Serving on 0x%lx for 0x%lx: offset %d len %d\n",
1990 (unsigned long)connection
, (unsigned long)object
,
1991 connection
->offset
, len
);
1992 /* IO_NOTNOW in order to give other clients a chance to run. */
1993 do_stream(IO_WRITE
| IO_NOTNOW
|
1994 (connection
->te
== TE_CHUNKED
? IO_CHUNKED
: 0) |
1997 object
->chunks
[i
].data
+ j
, len
,
1998 httpServeObjectStreamHandler
, connection
);
2000 httpSetTimeout(connection
, clientTimeout
);
2001 do_log(D_CLIENT_DATA
,
2002 "Serving on 0x%lx for 0x%lx: offset %d len %d + %d\n",
2003 (unsigned long)connection
, (unsigned long)object
,
2004 connection
->offset
, len
, len2
);
2005 do_stream_2(IO_WRITE
| IO_NOTNOW
|
2006 (connection
->te
== TE_CHUNKED
? IO_CHUNKED
: 0) |
2009 object
->chunks
[i
].data
+ j
, len
,
2010 object
->chunks
[i
+ 1].data
, len2
,
2011 httpServeObjectStreamHandler2
, connection
);
2019 unlockChunk(object
, i
);
2021 if(request
->chandler
)
2022 unregisterConditionHandler(request
->chandler
);
2023 request
->chandler
= NULL
;
2024 httpClientFinish(connection
, 1);
2029 httpServeChunkDelayed(TimeEventHandlerPtr event
)
2031 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
2032 httpServeChunk(connection
);
2037 delayedHttpServeChunk(HTTPConnectionPtr connection
)
2039 TimeEventHandlerPtr event
;
2040 event
= scheduleTimeEvent(-1, httpServeChunkDelayed
,
2041 sizeof(connection
), &connection
);
2042 if(!event
) return -1;
2047 httpServeObjectHandler(int status
, ConditionHandlerPtr chandler
)
2049 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)chandler
->data
;
2050 HTTPRequestPtr request
= connection
->request
;
2053 unlockChunk(request
->object
, connection
->offset
/ CHUNK_SIZE
);
2055 if((request
->object
->flags
& OBJECT_ABORTED
) || status
< 0) {
2056 shutdown(connection
->fd
, 1);
2057 httpSetTimeout(connection
, 10);
2058 /* httpServeChunk will take care of the error. */
2061 httpSetTimeout(connection
, -1);
2063 request
->chandler
= NULL
;
2064 rc
= delayedHttpServeChunk(connection
);
2066 do_log(L_ERROR
, "Couldn't schedule serving.\n");
2067 abortObject(request
->object
, 503,
2068 internAtom("Couldn't schedule serving"));
2074 httpServeObjectStreamHandlerCommon(int kind
, int status
,
2075 FdEventHandlerPtr event
,
2076 StreamRequestPtr srequest
)
2078 HTTPConnectionPtr connection
= srequest
->data
;
2079 HTTPRequestPtr request
= connection
->request
;
2080 int condition_result
= httpCondition(request
->object
, request
->condition
);
2081 int i
= connection
->offset
/ CHUNK_SIZE
;
2083 assert(!request
->chandler
);
2085 if(status
== 0 && !streamRequestDone(srequest
)) {
2086 httpSetTimeout(connection
, clientTimeout
);
2090 httpSetTimeout(connection
, -1);
2092 unlockChunk(request
->object
, i
);
2094 unlockChunk(request
->object
, i
+ 1);
2098 do_log_error(status
== -ECONNRESET
? D_IO
: L_ERROR
,
2099 -status
, "Couldn't write to client");
2100 if(status
== -EIO
|| status
== -ESHUTDOWN
)
2101 httpClientFinish(connection
, 2);
2103 httpClientFinish(connection
, 1);
2105 do_log(D_IO
, "Couldn't write to client: short write.\n");
2106 httpClientFinish(connection
, 2);
2111 if(srequest
->operation
& IO_CHUNKED
) {
2112 assert(srequest
->offset
> 2);
2113 connection
->offset
+= srequest
->offset
- 2;
2115 connection
->offset
+= srequest
->offset
;
2117 request
->flags
&= ~REQUEST_REQUESTED
;
2119 if(request
->object
->flags
& OBJECT_ABORTED
) {
2120 httpClientFinish(connection
, 1);
2124 if(connection
->request
->method
== METHOD_HEAD
||
2125 condition_result
== CONDITION_NOT_MODIFIED
) {
2126 httpClientFinish(connection
, 0);
2130 if(srequest
->operation
& IO_END
)
2131 httpClientFinish(connection
, 0);
2133 httpConnectionDestroyBuf(connection
);
2134 httpServeChunk(connection
);
2140 httpServeObjectStreamHandler(int status
,
2141 FdEventHandlerPtr event
,
2142 StreamRequestPtr srequest
)
2144 return httpServeObjectStreamHandlerCommon(1, status
, event
, srequest
);
2148 httpServeObjectStreamHandler2(int status
,
2149 FdEventHandlerPtr event
,
2150 StreamRequestPtr srequest
)
2152 return httpServeObjectStreamHandlerCommon(2, status
, event
, srequest
);