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 /* 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 haveData
= !(request
->object
->flags
& OBJECT_INITIAL
);
1175 (request
->object
->length
>= 0 &&
1176 request
->object
->length
<= request
->from
) ||
1177 (objectHoleSize(request
->object
, request
->from
) == 0);
1179 if(request
->flags
& REQUEST_REQUESTED
)
1181 else if(novalidate
|| (!local
&& proxyOffline
))
1185 objectMustRevalidate(request
->object
, &request
->cache_control
);
1186 else if(request
->cache_control
.flags
& CACHE_ONLY_IF_CACHED
)
1188 else if((request
->object
->flags
& OBJECT_FAILED
) &&
1189 !(object
->flags
& OBJECT_INPROGRESS
) &&
1192 else if(request
->method
!= METHOD_HEAD
&&
1193 !objectHasData(object
, request
->from
, request
->to
) &&
1194 !(object
->flags
& OBJECT_INPROGRESS
))
1196 else if(objectMustRevalidate((relaxTransparency
<= 1 ?
1197 request
->object
: NULL
),
1198 &request
->cache_control
))
1203 if(request
->cache_control
.flags
& CACHE_ONLY_IF_CACHED
) {
1207 connection
->flags
|= CONN_WRITER
;
1208 return httpClientRawError(connection
, 504,
1209 internAtom("Object not in cache"),
1216 if(!(request
->object
->flags
& OBJECT_VALIDATING
) &&
1217 ((!validate
&& haveData
) ||
1218 (request
->object
->flags
& OBJECT_FAILED
))) {
1220 connection
->flags
|= CONN_WRITER
;
1221 lockChunk(request
->object
, request
->from
/ CHUNK_SIZE
);
1222 return httpServeObject(connection
);
1228 if((request
->flags
& REQUEST_REQUESTED
) &&
1229 !(request
->object
->flags
& OBJECT_INPROGRESS
)) {
1230 /* This can happen either because the server side ran out of
1231 memory, or because it is using HEAD validation. We mark
1232 the object to be fetched again. */
1233 request
->flags
&= ~REQUEST_REQUESTED
;
1237 connection
->flags
|= CONN_WRITER
;
1238 if(!local
&& proxyOffline
)
1239 return httpClientRawError(connection
, 502,
1240 internAtom("Disconnected operation "
1241 "and object not in cache"),
1244 conditionWait(&request
->object
->condition
, httpClientGetHandler
,
1245 sizeof(request
), &request
);
1246 if(request
->chandler
== NULL
) {
1247 do_log(L_ERROR
, "Couldn't register condition handler.\n");
1248 return httpClientRawError(connection
, 503,
1249 internAtom("Couldn't register "
1250 "condition handler"), 0);
1254 if(request
->object
->flags
& OBJECT_VALIDATING
)
1257 conditional
= (haveData
&& request
->method
== METHOD_GET
);
1258 if(!mindlesslyCacheVary
&& (request
->object
->cache_control
& CACHE_VARY
))
1259 conditional
= conditional
&& (request
->object
->etag
!= NULL
);
1262 conditional
&& !(request
->object
->cache_control
& CACHE_MISMATCH
);
1264 request
->object
->flags
|= OBJECT_VALIDATING
;
1265 rc
= request
->object
->request(request
->object
,
1266 conditional
? METHOD_CONDITIONAL_GET
:
1268 request
->from
, request
->to
, request
,
1269 request
->object
->request_closure
);
1271 if(request
->chandler
)
1272 unregisterConditionHandler(request
->chandler
);
1273 request
->chandler
= NULL
;
1274 request
->object
->flags
&= ~OBJECT_VALIDATING
;
1275 request
->object
->flags
|= OBJECT_FAILED
;
1276 if(request
->error_message
)
1277 releaseAtom(request
->error_message
);
1278 request
->error_code
= 503;
1279 request
->error_message
= internAtom("Couldn't schedule get");
1285 httpClientNoticeRequestDelayed(TimeEventHandlerPtr event
)
1287 HTTPRequestPtr request
= *(HTTPRequestPtr
*)event
->data
;
1288 httpClientNoticeRequest(request
, 0);
1293 delayedHttpClientNoticeRequest(HTTPRequestPtr request
)
1295 TimeEventHandlerPtr event
;
1296 event
= scheduleTimeEvent(-1, httpClientNoticeRequestDelayed
,
1297 sizeof(request
), &request
);
1304 httpClientContinueDelayed(TimeEventHandlerPtr event
)
1306 static char httpContinue
[] = "HTTP/1.1 100 Continue\r\n\r\n";
1307 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1309 do_stream(IO_WRITE
, connection
->fd
, 0, httpContinue
, 25,
1310 httpErrorNofinishStreamHandler
, connection
);
1315 delayedHttpClientContinue(HTTPConnectionPtr connection
)
1317 TimeEventHandlerPtr event
;
1318 event
= scheduleTimeEvent(-1, httpClientContinueDelayed
,
1319 sizeof(connection
), &connection
);
1326 httpClientGetHandler(int status
, ConditionHandlerPtr chandler
)
1328 HTTPRequestPtr request
= *(HTTPRequestPtr
*)chandler
->data
;
1329 HTTPConnectionPtr connection
= request
->connection
;
1330 ObjectPtr object
= request
->object
;
1333 assert(request
== connection
->request
);
1335 if(request
->request
) {
1336 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1337 assert(!request
->request
->object
||
1338 request
->request
->object
== request
->object
);
1342 object
->flags
&= ~OBJECT_VALIDATING
; /* for now */
1343 if(request
->request
&& request
->request
->request
== request
)
1344 httpServerClientReset(request
->request
);
1345 lockChunk(object
, request
->from
/ CHUNK_SIZE
);
1346 request
->chandler
= NULL
;
1347 rc
= delayedHttpServeObject(connection
);
1349 unlockChunk(object
, request
->from
/ CHUNK_SIZE
);
1350 do_log(L_ERROR
, "Couldn't schedule serving.\n");
1351 abortObject(object
, 503, internAtom("Couldn't schedule serving"));
1356 if(object
->flags
& OBJECT_VALIDATING
)
1359 if(request
->error_code
) {
1360 lockChunk(object
, request
->from
/ CHUNK_SIZE
);
1361 request
->chandler
= NULL
;
1362 rc
= delayedHttpServeObject(connection
);
1364 unlockChunk(object
, request
->from
/ CHUNK_SIZE
);
1365 do_log(L_ERROR
, "Couldn't schedule serving.\n");
1366 abortObject(object
, 503, internAtom("Couldn't schedule serving"));
1371 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
1372 if(request
->request
&&
1373 !(request
->request
->flags
& REQUEST_WAIT_CONTINUE
)) {
1374 request
->flags
&= ~REQUEST_WAIT_CONTINUE
;
1375 delayedHttpClientContinue(connection
);
1380 /* See httpServerHandlerHeaders */
1381 if((object
->flags
& OBJECT_SUPERSEDED
) &&
1382 request
->request
&& request
->request
->can_mutate
) {
1383 ObjectPtr new_object
= retainObject(request
->request
->can_mutate
);
1384 if(object
->requestor
== request
) {
1385 if(new_object
->requestor
== NULL
)
1386 new_object
->requestor
= request
;
1387 object
->requestor
= NULL
;
1389 request
->chandler
= NULL
;
1390 releaseObject(object
);
1391 request
->object
= new_object
;
1392 request
->request
->object
= new_object
;
1393 /* We're handling the wrong object now. It's simpler to
1394 rebuild the whole data structure from scratch rather than
1395 trying to compensate. */
1396 rc
= delayedHttpClientNoticeRequest(request
);
1398 do_log(L_ERROR
, "Couldn't schedule noticing of request.");
1399 abortObject(object
, 500,
1400 internAtom("Couldn't schedule "
1401 "noticing of request"));
1402 /* We're probably out of memory. What can we do? */
1403 shutdown(connection
->fd
, 1);
1408 if(object
->requestor
!= request
&& !(object
->flags
& OBJECT_ABORTED
)) {
1409 /* Make sure we don't serve an object that is stale for us
1410 unless we're the requestor. */
1411 if((object
->flags
& (OBJECT_LINEAR
| OBJECT_MUTATING
)) ||
1412 objectMustRevalidate(object
, &request
->cache_control
)) {
1413 if(object
->flags
& OBJECT_INPROGRESS
)
1415 rc
= delayedHttpClientNoticeRequest(request
);
1417 do_log(L_ERROR
, "Couldn't schedule noticing of request.");
1418 abortObject(object
, 500,
1419 internAtom("Couldn't schedule "
1420 "noticing of request"));
1422 request
->chandler
= NULL
;
1428 if(object
->flags
& (OBJECT_INITIAL
| OBJECT_VALIDATING
)) {
1429 if(object
->flags
& (OBJECT_INPROGRESS
| OBJECT_VALIDATING
)) {
1431 } else if(object
->flags
& OBJECT_FAILED
) {
1432 if(request
->error_code
)
1434 request
->error_code
,
1435 retainAtom(request
->error_message
));
1437 abortObject(object
, 500,
1438 internAtom("Error message lost in transit"));
1441 /* The request was pruned by httpServerDiscardRequests */
1442 if(chandler
== request
->chandler
) {
1444 request
->chandler
= NULL
;
1445 rc
= delayedHttpClientNoticeRequest(request
);
1447 abortObject(object
, 500,
1448 internAtom("Couldn't allocate "
1449 "delayed notice request"));
1453 abortObject(object
, 500,
1454 internAtom("Wrong request pruned -- "
1455 "this shouldn't happen"));
1460 if(request
->object
->flags
& OBJECT_DYNAMIC
) {
1461 if(objectHoleSize(request
->object
, 0) == 0) {
1465 /* We really should request again if that is not the case */
1469 lockChunk(object
, request
->from
/ CHUNK_SIZE
);
1470 request
->chandler
= NULL
;
1471 rc
= delayedHttpServeObject(connection
);
1473 unlockChunk(object
, request
->from
/ CHUNK_SIZE
);
1474 do_log(L_ERROR
, "Couldn't schedule serving.\n");
1475 abortObject(object
, 503, internAtom("Couldn't schedule serving"));
1481 httpClientSideRequest(HTTPRequestPtr request
)
1483 HTTPConnectionPtr connection
= request
->connection
;
1485 if(request
->from
< 0 || request
->to
>= 0) {
1486 httpClientNoticeError(request
, 501,
1487 internAtom("Partial requests not implemented"));
1488 httpClientDiscardBody(connection
);
1491 if(connection
->reqte
!= TE_IDENTITY
) {
1492 httpClientNoticeError(request
, 501,
1493 internAtom("Chunked requests not implemented"));
1494 httpClientDiscardBody(connection
);
1497 if(connection
->bodylen
< 0) {
1498 httpClientNoticeError(request
, 502,
1499 internAtom("POST or PUT without "
1501 httpClientDiscardBody(connection
);
1504 if(connection
->reqlen
< 0) {
1505 httpClientNoticeError(request
, 502,
1506 internAtom("Incomplete POST or PUT"));
1507 httpClientDiscardBody(connection
);
1511 return httpClientNoticeRequest(request
, 0);
1515 httpClientSideHandler(int status
,
1516 FdEventHandlerPtr event
,
1517 StreamRequestPtr srequest
)
1519 HTTPConnectionPtr connection
= srequest
->data
;
1520 HTTPRequestPtr request
= connection
->request
;
1521 HTTPRequestPtr requestee
;
1522 HTTPConnectionPtr server
;
1525 AtomPtr message
= NULL
;
1527 assert(connection
->flags
& CONN_SIDE_READER
);
1529 if((request
->object
->flags
& OBJECT_ABORTED
) ||
1530 !(request
->object
->flags
& OBJECT_INPROGRESS
)) {
1531 code
= request
->object
->code
;
1532 message
= retainAtom(request
->object
->message
);
1537 do_log_error(L_ERROR
, -status
, "Reading from client");
1539 message
= internAtomError(-status
, "Couldn't read from client");
1543 requestee
= request
->request
;
1544 server
= requestee
->connection
;
1546 push
= MIN(srequest
->offset
- connection
->reqlen
,
1547 connection
->bodylen
- connection
->reqoffset
);
1549 connection
->reqlen
+= push
;
1550 httpServerDoSide(server
);
1554 if(server
->reqoffset
>= connection
->bodylen
) {
1555 connection
->flags
&= ~(CONN_READER
| CONN_SIDE_READER
);
1560 do_log(L_ERROR
, "Incomplete client request.\n");
1562 message
= internAtom("Incomplete client request");
1565 request
->error_code
= code
;
1566 if(request
->error_message
)
1567 releaseAtom(request
->error_message
);
1568 request
->error_message
= message
;
1569 if(request
->error_headers
)
1570 releaseAtom(request
->error_headers
);
1571 request
->error_headers
= NULL
;
1573 if(request
->request
) {
1574 shutdown(request
->request
->connection
->fd
, 2);
1575 pokeFdEvent(request
->request
->connection
->fd
, -ESHUTDOWN
, POLLOUT
);
1577 notifyObject(request
->object
);
1578 connection
->flags
&= ~CONN_SIDE_READER
;
1579 httpClientDiscardBody(connection
);
1584 httpServeObject(HTTPConnectionPtr connection
)
1586 HTTPRequestPtr request
= connection
->request
;
1587 ObjectPtr object
= request
->object
;
1588 int i
= request
->from
/ CHUNK_SIZE
;
1589 int j
= request
->from
% CHUNK_SIZE
;
1591 int bufsize
= CHUNK_SIZE
;
1592 int condition_result
;
1594 object
->atime
= current_time
.tv_sec
;
1595 objectMetadataChanged(object
, 0);
1597 httpSetTimeout(connection
, -1);
1599 if((request
->error_code
&& relaxTransparency
<= 0) ||
1600 object
->flags
& OBJECT_INITIAL
) {
1601 object
->flags
&= ~OBJECT_FAILED
;
1602 unlockChunk(object
, i
);
1603 if(request
->error_code
)
1604 return httpClientRawError(connection
,
1605 request
->error_code
,
1606 retainAtom(request
->error_message
), 0);
1608 return httpClientRawError(connection
,
1609 500, internAtom("Object vanished."), 0);
1612 if(!(object
->flags
& OBJECT_INPROGRESS
) && object
->code
== 0) {
1613 if(object
->flags
& OBJECT_INITIAL
) {
1614 unlockChunk(object
, i
);
1615 return httpClientRawError(connection
, 503,
1616 internAtom("Error message lost"), 0);
1619 unlockChunk(object
, i
);
1620 do_log(L_ERROR
, "Internal proxy error: object has no code.\n");
1621 return httpClientRawError(connection
, 500,
1622 internAtom("Internal proxy error: "
1623 "object has no code"), 0);
1627 condition_result
= httpCondition(object
, request
->condition
);
1629 if(condition_result
== CONDITION_FAILED
) {
1630 unlockChunk(object
, i
);
1631 return httpClientRawError(connection
, 412,
1632 internAtom("Precondition failed"), 0);
1633 } else if(condition_result
== CONDITION_NOT_MODIFIED
) {
1634 unlockChunk(object
, i
);
1635 return httpClientRawError(connection
, 304,
1636 internAtom("Not modified"), 0);
1639 objectFillFromDisk(object
, request
->from
,
1640 (request
->method
== METHOD_HEAD
||
1641 condition_result
!= CONDITION_MATCH
) ? 0 : 1);
1643 if(((object
->flags
& OBJECT_LINEAR
) &&
1644 (object
->requestor
!= connection
->request
)) ||
1645 ((object
->flags
& OBJECT_SUPERSEDED
) &&
1646 !(object
->flags
& OBJECT_LINEAR
))) {
1647 if(request
->request
) {
1648 request
->request
->request
= NULL
;
1649 request
->request
= NULL
;
1650 request
->object
->requestor
= NULL
;
1652 object
= makeObject(OBJECT_HTTP
,
1653 object
->key
, object
->key_size
, 1, 0,
1654 object
->request
, NULL
);
1655 if(request
->object
->requestor
== request
)
1656 request
->object
->requestor
= NULL
;
1657 unlockChunk(request
->object
, i
);
1658 releaseObject(request
->object
);
1659 request
->object
= NULL
;
1660 if(object
== NULL
) {
1661 do_log(L_ERROR
, "Couldn't allocate object.");
1662 return httpClientRawError(connection
, 501,
1663 internAtom("Couldn't allocate object"),
1666 if(urlIsLocal(object
->key
, object
->key_size
)) {
1667 object
->flags
|= OBJECT_LOCAL
;
1668 object
->request
= httpLocalRequest
;
1670 request
->object
= object
;
1671 connection
->flags
&= ~CONN_WRITER
;
1672 return httpClientNoticeRequest(request
, 1);
1675 if(object
->flags
& OBJECT_ABORTED
) {
1676 unlockChunk(object
, i
);
1677 return httpClientNoticeError(request
, object
->code
,
1678 retainAtom(object
->message
));
1681 if(connection
->buf
== NULL
)
1682 connection
->buf
= get_chunk();
1683 if(connection
->buf
== NULL
) {
1684 unlockChunk(object
, i
);
1685 do_log(L_ERROR
, "Couldn't allocate client buffer.\n");
1686 connection
->flags
&= ~CONN_WRITER
;
1687 httpClientFinish(connection
, 1);
1691 if(object
->length
>= 0 && request
->to
>= object
->length
)
1692 request
->to
= object
->length
;
1694 if(request
->from
> 0 || request
->to
>= 0) {
1695 if(request
->method
== METHOD_HEAD
) {
1696 request
->to
= request
->from
;
1697 } else if(request
->to
< 0) {
1698 if(object
->length
>= 0)
1699 request
->to
= object
->length
;
1705 connection
->len
= 0;
1707 if((request
->from
<= 0 && request
->to
< 0) ||
1708 request
->method
== METHOD_HEAD
) {
1709 n
= snnprintf(connection
->buf
, 0, bufsize
,
1711 object
->code
, atomString(object
->message
));
1713 if((object
->length
>= 0 && request
->from
>= object
->length
) ||
1714 (request
->to
>= 0 && request
->from
>= request
->to
)) {
1715 unlockChunk(object
, i
);
1716 return httpClientRawError(connection
, 416,
1717 internAtom("Requested range "
1721 n
= snnprintf(connection
->buf
, 0, bufsize
,
1722 "HTTP/1.1 206 Partial content");
1726 n
= httpWriteObjectHeaders(connection
->buf
, n
, bufsize
,
1727 object
, request
->from
, request
->to
);
1731 if(request
->method
!= METHOD_HEAD
&&
1732 condition_result
!= CONDITION_NOT_MODIFIED
&&
1733 request
->to
< 0 && object
->length
< 0) {
1734 if(connection
->version
== HTTP_11
) {
1735 connection
->te
= TE_CHUNKED
;
1736 n
= snnprintf(connection
->buf
, n
, bufsize
,
1737 "\r\nTransfer-Encoding: chunked");
1739 request
->flags
&= ~REQUEST_PERSISTENT
;
1743 if(object
->age
< current_time
.tv_sec
) {
1744 n
= snnprintf(connection
->buf
, n
, bufsize
,
1746 (int)(current_time
.tv_sec
- object
->age
));
1748 n
= snnprintf(connection
->buf
, n
, bufsize
,
1749 "\r\nConnection: %s",
1750 (request
->flags
& REQUEST_PERSISTENT
) ?
1751 "keep-alive" : "close");
1753 if(!(object
->flags
& OBJECT_LOCAL
)) {
1754 if((object
->flags
& OBJECT_FAILED
) && !proxyOffline
) {
1755 n
= snnprintf(connection
->buf
, n
, bufsize
,
1756 "\r\nWarning: 111 %s:%d Revalidation failed",
1757 proxyName
->string
, proxyPort
);
1758 if(request
->error_code
)
1759 n
= snnprintf(connection
->buf
, n
, bufsize
,
1761 request
->error_code
,
1762 atomString(request
->error_message
));
1763 object
->flags
&= ~OBJECT_FAILED
;
1764 } else if(proxyOffline
&&
1765 objectMustRevalidate(object
, &request
->cache_control
)) {
1766 n
= snnprintf(connection
->buf
, n
, bufsize
,
1767 "\r\nWarning: 112 %s:%d Disconnected operation",
1768 proxyName
->string
, proxyPort
);
1769 } else if(objectIsStale(object
, &request
->cache_control
)) {
1770 n
= snnprintf(connection
->buf
, n
, bufsize
,
1771 "\r\nWarning: 110 %s:%d Object is stale",
1772 proxyName
->string
, proxyPort
);
1773 } else if(object
->expires
< 0 && object
->max_age
< 0 &&
1774 object
->age
< current_time
.tv_sec
- 24 * 3600) {
1775 n
= snnprintf(connection
->buf
, n
, bufsize
,
1776 "\r\nWarning: 113 %s:%d Heuristic expiration",
1777 proxyName
->string
, proxyPort
);
1781 n
= snnprintf(connection
->buf
, n
, bufsize
, "\r\n\r\n");
1786 connection
->offset
= request
->from
;
1788 if(request
->method
== METHOD_HEAD
||
1789 condition_result
== CONDITION_NOT_MODIFIED
||
1790 (object
->flags
& OBJECT_ABORTED
)) {
1793 if(i
< object
->numchunks
) {
1794 if(object
->chunks
[i
].size
<= j
)
1797 len
= object
->chunks
[i
].size
- j
;
1801 if(request
->to
>= 0)
1802 len
= MIN(len
, request
->to
- request
->from
);
1805 connection
->offset
= request
->from
;
1806 httpSetTimeout(connection
, clientTimeout
);
1807 do_log(D_CLIENT_DATA
, "Serving on 0x%lx for 0x%lx: offset %d len %d\n",
1808 (unsigned long)connection
, (unsigned long)object
,
1809 connection
->offset
, len
);
1810 do_stream_h(IO_WRITE
|
1811 (connection
->te
== TE_CHUNKED
&& len
> 0 ? IO_CHUNKED
: 0),
1814 object
->chunks
[i
].data
+ j
, len
,
1815 httpServeObjectStreamHandler
, connection
);
1820 connection
->len
= 0;
1821 if(!(connection
->flags
& CONN_BIGBUF
))
1822 rc
= httpConnectionBigify(connection
);
1824 bufsize
= bigBufferSize
;
1827 unlockChunk(object
, i
);
1828 return httpClientRawError(connection
, 500,
1830 internAtom("No space for headers") :
1831 internAtom("Couldn't allocate big buffer"), 0);
1835 httpServeObjectDelayed(TimeEventHandlerPtr event
)
1837 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1838 httpServeObject(connection
);
1843 delayedHttpServeObject(HTTPConnectionPtr connection
)
1845 TimeEventHandlerPtr event
;
1847 assert(connection
->request
->object
->chunks
[connection
->request
->from
/
1848 CHUNK_SIZE
].locked
> 0);
1850 event
= scheduleTimeEvent(-1, httpServeObjectDelayed
,
1851 sizeof(connection
), &connection
);
1852 if(!event
) return -1;
1857 httpServeObjectFinishHandler(int status
,
1858 FdEventHandlerPtr event
,
1859 StreamRequestPtr srequest
)
1861 HTTPConnectionPtr connection
= srequest
->data
;
1862 HTTPRequestPtr request
= connection
->request
;
1865 assert(!request
->chandler
);
1867 if(status
== 0 && !streamRequestDone(srequest
))
1870 httpSetTimeout(connection
, -1);
1873 do_log(L_ERROR
, "Couldn't terminate chunked reply\n");
1874 httpClientFinish(connection
, 1);
1876 httpClientFinish(connection
, 0);
1882 httpServeChunk(HTTPConnectionPtr connection
)
1884 HTTPRequestPtr request
= connection
->request
;
1885 ObjectPtr object
= request
->object
;
1886 int i
= connection
->offset
/ CHUNK_SIZE
;
1887 int j
= connection
->offset
- (i
* CHUNK_SIZE
);
1888 int to
, len
, len2
, end
;
1891 if(object
->flags
& OBJECT_ABORTED
)
1892 goto fail_no_unlock
;
1894 if(object
->length
>= 0 && request
->to
>= 0)
1895 to
= MIN(request
->to
, object
->length
);
1896 else if(object
->length
>= 0)
1897 to
= object
->length
;
1898 else if(request
->to
>= 0)
1903 lockChunk(object
, i
);
1905 if(i
< object
->numchunks
)
1906 len
= object
->chunks
[i
].size
- j
;
1908 if(request
->method
!= METHOD_HEAD
&&
1909 len
< CHUNK_SIZE
&& connection
->offset
+ len
< to
) {
1910 objectFillFromDisk(object
, connection
->offset
+ len
, 2);
1911 len
= object
->chunks
[i
].size
- j
;
1915 len
= MIN(len
, to
- connection
->offset
);
1918 if(to
>= 0 && connection
->offset
>= to
) {
1919 if(request
->chandler
) {
1920 unregisterConditionHandler(request
->chandler
);
1921 request
->chandler
= NULL
;
1923 unlockChunk(object
, i
);
1924 if(connection
->te
== TE_CHUNKED
) {
1925 httpSetTimeout(connection
, clientTimeout
);
1926 do_stream(IO_WRITE
| IO_CHUNKED
| IO_END
,
1927 connection
->fd
, 0, NULL
, 0,
1928 httpServeObjectFinishHandler
, connection
);
1930 httpClientFinish(connection
,
1931 !(object
->length
>= 0 &&
1932 connection
->offset
>= object
->length
));
1936 if(!request
->chandler
) {
1938 conditionWait(&object
->condition
,
1939 httpServeObjectHandler
,
1940 sizeof(connection
), &connection
);
1941 if(!request
->chandler
) {
1942 do_log(L_ERROR
, "Couldn't register condition handler\n");
1946 if(!(object
->flags
& OBJECT_INPROGRESS
)) {
1947 if(object
->flags
& OBJECT_SUPERSEDED
) {
1950 if(REQUEST_SIDE(request
)) goto fail
;
1951 rc
= object
->request(object
, request
->method
,
1952 connection
->offset
, -1, request
,
1953 object
->request_closure
);
1954 if(rc
<= 0) goto fail
;
1960 if(request
->method
!= METHOD_HEAD
)
1961 objectFillFromDisk(object
, (i
+ 1) * CHUNK_SIZE
, 1);
1962 if(request
->chandler
) {
1963 unregisterConditionHandler(request
->chandler
);
1964 request
->chandler
= NULL
;
1967 if(j
+ len
== CHUNK_SIZE
&& object
->numchunks
> i
+ 1) {
1968 len2
= object
->chunks
[i
+ 1].size
;
1970 len2
= MIN(len2
, to
- (i
+ 1) * CHUNK_SIZE
);
1972 /* Lock early -- httpServerRequest may get_chunk */
1974 lockChunk(object
, i
+ 1);
1975 if(object
->length
>= 0 &&
1976 connection
->offset
+ len
+ len2
== object
->length
)
1981 if(!(object
->flags
& OBJECT_INPROGRESS
) && !REQUEST_SIDE(request
)) {
1982 if(object
->chunks
[i
].size
< CHUNK_SIZE
&&
1983 to
>= 0 && connection
->offset
+ len
+ 1 < to
)
1984 object
->request(object
, request
->method
,
1985 connection
->offset
+ len
, -1, request
,
1986 object
->request_closure
);
1987 else if(i
+ 1 < object
->numchunks
&&
1988 object
->chunks
[i
+ 1].size
== 0 &&
1989 to
>= 0 && (i
+ 1) * CHUNK_SIZE
+ 1 < to
)
1990 object
->request(object
, request
->method
,
1991 (i
+ 1) * CHUNK_SIZE
, -1, request
,
1992 object
->request_closure
);
1995 httpSetTimeout(connection
, clientTimeout
);
1996 do_log(D_CLIENT_DATA
,
1997 "Serving on 0x%lx for 0x%lx: offset %d len %d\n",
1998 (unsigned long)connection
, (unsigned long)object
,
1999 connection
->offset
, len
);
2000 /* IO_NOTNOW in order to give other clients a chance to run. */
2001 do_stream(IO_WRITE
| IO_NOTNOW
|
2002 (connection
->te
== TE_CHUNKED
? IO_CHUNKED
: 0) |
2005 object
->chunks
[i
].data
+ j
, len
,
2006 httpServeObjectStreamHandler
, connection
);
2008 httpSetTimeout(connection
, clientTimeout
);
2009 do_log(D_CLIENT_DATA
,
2010 "Serving on 0x%lx for 0x%lx: offset %d len %d + %d\n",
2011 (unsigned long)connection
, (unsigned long)object
,
2012 connection
->offset
, len
, len2
);
2013 do_stream_2(IO_WRITE
| IO_NOTNOW
|
2014 (connection
->te
== TE_CHUNKED
? IO_CHUNKED
: 0) |
2017 object
->chunks
[i
].data
+ j
, len
,
2018 object
->chunks
[i
+ 1].data
, len2
,
2019 httpServeObjectStreamHandler2
, connection
);
2027 unlockChunk(object
, i
);
2029 if(request
->chandler
)
2030 unregisterConditionHandler(request
->chandler
);
2031 request
->chandler
= NULL
;
2032 httpClientFinish(connection
, 1);
2037 httpServeChunkDelayed(TimeEventHandlerPtr event
)
2039 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
2040 httpServeChunk(connection
);
2045 delayedHttpServeChunk(HTTPConnectionPtr connection
)
2047 TimeEventHandlerPtr event
;
2048 event
= scheduleTimeEvent(-1, httpServeChunkDelayed
,
2049 sizeof(connection
), &connection
);
2050 if(!event
) return -1;
2055 httpServeObjectHandler(int status
, ConditionHandlerPtr chandler
)
2057 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)chandler
->data
;
2058 HTTPRequestPtr request
= connection
->request
;
2061 unlockChunk(request
->object
, connection
->offset
/ CHUNK_SIZE
);
2063 if((request
->object
->flags
& OBJECT_ABORTED
) || status
< 0) {
2064 shutdown(connection
->fd
, 1);
2065 httpSetTimeout(connection
, 10);
2066 /* httpServeChunk will take care of the error. */
2069 httpSetTimeout(connection
, -1);
2071 request
->chandler
= NULL
;
2072 rc
= delayedHttpServeChunk(connection
);
2074 do_log(L_ERROR
, "Couldn't schedule serving.\n");
2075 abortObject(request
->object
, 503,
2076 internAtom("Couldn't schedule serving"));
2082 httpServeObjectStreamHandlerCommon(int kind
, int status
,
2083 FdEventHandlerPtr event
,
2084 StreamRequestPtr srequest
)
2086 HTTPConnectionPtr connection
= srequest
->data
;
2087 HTTPRequestPtr request
= connection
->request
;
2088 int condition_result
= httpCondition(request
->object
, request
->condition
);
2089 int i
= connection
->offset
/ CHUNK_SIZE
;
2091 assert(!request
->chandler
);
2093 if(status
== 0 && !streamRequestDone(srequest
)) {
2094 httpSetTimeout(connection
, clientTimeout
);
2098 httpSetTimeout(connection
, -1);
2100 unlockChunk(request
->object
, i
);
2102 unlockChunk(request
->object
, i
+ 1);
2106 do_log_error(status
== -ECONNRESET
? D_IO
: L_ERROR
,
2107 -status
, "Couldn't write to client");
2108 if(status
== -EIO
|| status
== -ESHUTDOWN
)
2109 httpClientFinish(connection
, 2);
2111 httpClientFinish(connection
, 1);
2113 do_log(D_IO
, "Couldn't write to client: short write.\n");
2114 httpClientFinish(connection
, 2);
2119 if(srequest
->operation
& IO_CHUNKED
) {
2120 assert(srequest
->offset
> 2);
2121 connection
->offset
+= srequest
->offset
- 2;
2123 connection
->offset
+= srequest
->offset
;
2125 request
->flags
&= ~REQUEST_REQUESTED
;
2127 if(request
->object
->flags
& OBJECT_ABORTED
) {
2128 httpClientFinish(connection
, 1);
2132 if(connection
->request
->method
== METHOD_HEAD
||
2133 condition_result
== CONDITION_NOT_MODIFIED
) {
2134 httpClientFinish(connection
, 0);
2138 if(srequest
->operation
& IO_END
)
2139 httpClientFinish(connection
, 0);
2141 httpConnectionDestroyBuf(connection
);
2142 httpServeChunk(connection
);
2148 httpServeObjectStreamHandler(int status
,
2149 FdEventHandlerPtr event
,
2150 StreamRequestPtr srequest
)
2152 return httpServeObjectStreamHandlerCommon(1, status
, event
, srequest
);
2156 httpServeObjectStreamHandler2(int status
,
2157 FdEventHandlerPtr event
,
2158 StreamRequestPtr srequest
)
2160 return httpServeObjectStreamHandlerCommon(2, status
, event
, srequest
);