2 Copyright (c) 2003-2006 by Juliusz Chroboczek
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 httpAcceptAgain(TimeEventHandlerPtr event
)
28 FdEventHandlerPtr newevent
;
29 int fd
= *(int*)event
->data
;
31 newevent
= schedule_accept(fd
, httpAccept
, NULL
);
32 if(newevent
== NULL
) {
34 newevent
= schedule_accept(fd
, httpAccept
, NULL
);
35 if(newevent
== NULL
) {
36 do_log(L_ERROR
, "Couldn't schedule accept.\n");
44 httpAccept(int fd
, FdEventHandlerPtr event
, AcceptRequestPtr request
)
47 HTTPConnectionPtr connection
;
48 TimeEventHandlerPtr timeout
;
51 if(-fd
== EINTR
|| -fd
== EAGAIN
|| -fd
== EWOULDBLOCK
)
53 do_log_error(L_ERROR
, -fd
, "Couldn't establish listening socket");
54 if(-fd
== EMFILE
|| -fd
== ENOMEM
|| -fd
== ENOBUFS
) {
55 TimeEventHandlerPtr again
= NULL
;
56 do_log(L_WARN
, "Refusing client connections for one second.\n");
58 again
= scheduleTimeEvent(1, httpAcceptAgain
,
59 sizeof(request
->fd
), &request
->fd
);
61 do_log(L_ERROR
, "Couldn't schedule accept -- sleeping.\n");
63 again
= scheduleTimeEvent(1, httpAcceptAgain
,
64 sizeof(request
->fd
), &request
->fd
);
66 do_log(L_ERROR
, "Couldn't schedule accept -- aborting.\n");
78 if(netAddressMatch(fd
, allowedNets
) != 1) {
79 do_log(L_WARN
, "Refusing connection from unauthorised net\n");
85 rc
= setNonblocking(fd
, 1);
87 do_log_error(L_WARN
, errno
, "Couldn't set non blocking mode");
91 rc
= setNodelay(fd
, 1);
93 do_log_error(L_WARN
, errno
, "Couldn't disable Nagle's algorithm");
95 connection
= httpMakeConnection();
97 timeout
= scheduleTimeEvent(clientTimeout
, httpTimeoutHandler
,
98 sizeof(connection
), &connection
);
106 connection
->timeout
= timeout
;
108 do_log(D_CLIENT_CONN
, "Accepted client connection 0x%lx\n",
109 (unsigned long)connection
);
111 connection
->flags
= CONN_READER
;
113 do_stream_buf(IO_READ
| IO_NOTNOW
, connection
->fd
, 0,
114 &connection
->reqbuf
, CHUNK_SIZE
,
115 httpClientHandler
, connection
);
119 /* Abort a client connection. It is only safe to abort the requests
120 if we know the connection is closed. */
122 httpClientAbort(HTTPConnectionPtr connection
, int closed
)
124 HTTPRequestPtr request
= connection
->request
;
126 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLOUT
);
129 if(request
->chandler
) {
130 request
->error_code
= 500;
131 request
->error_message
= internAtom("Connection finishing");
132 abortConditionHandler(request
->chandler
);
133 request
->chandler
= NULL
;
135 request
= request
->next
;
140 /* s != 0 specifies that the connection must be shut down. It is 1 in
141 order to linger the connection, 2 to close it straight away. */
143 httpClientFinish(HTTPConnectionPtr connection
, int s
)
145 HTTPRequestPtr request
= connection
->request
;
147 assert(!(request
&& request
->request
148 && request
->request
->request
!= request
));
151 if(!request
|| !(request
->flags
& REQUEST_PERSISTENT
))
155 httpConnectionDestroyBuf(connection
);
157 connection
->flags
&= ~CONN_WRITER
;
159 if(connection
->flags
& CONN_SIDE_READER
) {
160 /* We're in POST or PUT and the reader isn't done yet.
161 Wait for the read side to close the connection. */
162 assert(request
&& (connection
->flags
& CONN_READER
));
164 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLIN
);
166 pokeFdEvent(connection
->fd
, -EDOGRACEFUL
, POLLIN
);
171 if(connection
->timeout
)
172 cancelTimeEvent(connection
->timeout
);
173 connection
->timeout
= NULL
;
176 HTTPRequestPtr requestee
;
178 requestee
= request
->request
;
180 request
->request
= NULL
;
181 requestee
->request
= NULL
;
184 httpServerClientReset(requestee
);
185 if(request
->chandler
) {
186 request
->error_code
= 500;
187 request
->error_message
= internAtom("Connection finishing");
188 abortConditionHandler(request
->chandler
);
189 request
->chandler
= NULL
;
192 if(request
->object
) {
193 if(request
->object
->requestor
== request
)
194 request
->object
->requestor
= NULL
;
195 releaseObject(request
->object
);
196 request
->object
= NULL
;
198 httpDequeueRequest(connection
);
199 httpDestroyRequest(request
);
204 connection
->len
= -1;
205 connection
->offset
= 0;
206 connection
->te
= TE_IDENTITY
;
209 assert(connection
->fd
> 0);
210 connection
->serviced
++;
211 httpSetTimeout(connection
, clientTimeout
);
212 if(!connection
->flags
& CONN_READER
) {
213 if(connection
->reqlen
== 0)
214 httpConnectionDestroyReqbuf(connection
);
215 else if((connection
->flags
& CONN_BIGREQBUF
) &&
216 connection
->reqlen
< CHUNK_SIZE
)
217 httpConnectionUnbigifyReqbuf(connection
);
218 connection
->flags
|= CONN_READER
;
219 httpSetTimeout(connection
, clientTimeout
);
220 do_stream_buf(IO_READ
| IO_NOTNOW
|
221 (connection
->reqlen
? IO_IMMEDIATE
: 0),
222 connection
->fd
, connection
->reqlen
,
224 (connection
->flags
& CONN_BIGREQBUF
) ?
225 bigBufferSize
: CHUNK_SIZE
,
226 httpClientHandler
, connection
);
228 /* The request has already been validated when it first got
230 if(connection
->request
) {
231 if(connection
->request
->object
!= NULL
)
232 httpClientNoticeRequest(connection
->request
, 1);
234 assert(connection
->flags
& CONN_READER
);
239 do_log(D_CLIENT_CONN
, "Closing client connection 0x%lx\n",
240 (unsigned long)connection
);
242 if(connection
->flags
& CONN_READER
) {
243 httpSetTimeout(connection
, 10);
244 if(connection
->fd
< 0) return;
246 pokeFdEvent(connection
->fd
, -EDOSHUTDOWN
, POLLIN
);
248 pokeFdEvent(connection
->fd
, -EDOGRACEFUL
, POLLIN
);
253 HTTPRequestPtr requestee
;
254 request
= connection
->request
;
257 requestee
= request
->request
;
258 request
->request
= NULL
;
260 requestee
->request
= NULL
;
261 httpServerClientReset(requestee
);
263 if(request
->chandler
)
264 abortConditionHandler(request
->chandler
);
265 request
->chandler
= NULL
;
266 if(request
->object
&& request
->object
->requestor
== request
)
267 request
->object
->requestor
= NULL
;
268 httpDequeueRequest(connection
);
269 httpDestroyRequest(request
);
271 httpConnectionDestroyReqbuf(connection
);
272 if(connection
->timeout
)
273 cancelTimeEvent(connection
->timeout
);
274 connection
->timeout
= NULL
;
275 if(connection
->fd
>= 0) {
277 close(connection
->fd
);
279 lingeringClose(connection
->fd
);
285 /* Extremely baroque implementation of close: we need to synchronise
286 between the writer and the reader. */
288 static char client_shutdown_buffer
[17];
290 static int httpClientDelayedShutdownHandler(TimeEventHandlerPtr
);
293 httpClientDelayedShutdown(HTTPConnectionPtr connection
)
295 TimeEventHandlerPtr handler
;
297 assert(connection
->flags
& CONN_READER
);
298 handler
= scheduleTimeEvent(1, httpClientDelayedShutdownHandler
,
299 sizeof(connection
), &connection
);
302 "Couldn't schedule delayed shutdown -- freeing memory.");
304 handler
= scheduleTimeEvent(1, httpClientDelayedShutdownHandler
,
305 sizeof(connection
), &connection
);
308 "Couldn't schedule delayed shutdown -- aborting.\n");
316 httpClientShutdownHandler(int status
,
317 FdEventHandlerPtr event
, StreamRequestPtr request
)
319 HTTPConnectionPtr connection
= request
->data
;
321 assert(connection
->flags
& CONN_READER
);
323 if(!(connection
->flags
& CONN_WRITER
)) {
324 connection
->flags
&= ~CONN_READER
;
325 connection
->reqlen
= 0;
326 httpConnectionDestroyReqbuf(connection
);
327 if(status
&& status
!= -EDOGRACEFUL
)
328 httpClientFinish(connection
, 2);
330 httpClientFinish(connection
, 1);
334 httpClientDelayedShutdown(connection
);
339 httpClientDelayedShutdownHandler(TimeEventHandlerPtr event
)
341 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
342 assert(connection
->flags
& CONN_READER
);
344 if(!(connection
->flags
& CONN_WRITER
)) {
345 connection
->flags
&= ~CONN_READER
;
346 connection
->reqlen
= 0;
347 httpConnectionDestroyReqbuf(connection
);
348 httpClientFinish(connection
, 1);
351 do_stream(IO_READ
| IO_NOTNOW
, connection
->fd
,
352 0, client_shutdown_buffer
, 17,
353 httpClientShutdownHandler
, connection
);
358 httpClientHandler(int status
,
359 FdEventHandlerPtr event
, StreamRequestPtr request
)
361 HTTPConnectionPtr connection
= request
->data
;
364 (connection
->flags
& CONN_BIGREQBUF
) ? connection
->reqlen
: CHUNK_SIZE
;
366 assert(connection
->flags
& CONN_READER
);
368 /* There's no point trying to do something with this request if
369 the client has shut the connection down -- HTTP doesn't do
370 half-open connections. */
372 connection
->reqlen
= 0;
373 httpConnectionDestroyReqbuf(connection
);
374 if(!(connection
->flags
& CONN_WRITER
)) {
375 connection
->flags
&= ~CONN_READER
;
376 if(status
> 0 || status
== -ECONNRESET
|| status
== -EDOSHUTDOWN
)
377 httpClientFinish(connection
, 2);
379 httpClientFinish(connection
, 1);
382 httpClientAbort(connection
, status
> 0 || status
== -ECONNRESET
);
383 connection
->flags
&= ~CONN_READER
;
387 i
= findEndOfHeaders(connection
->reqbuf
, 0, request
->offset
, &body
);
388 connection
->reqlen
= request
->offset
;
391 connection
->reqbegin
= i
;
392 httpClientHandlerHeaders(event
, request
, connection
);
397 if(connection
->reqlen
> 0) {
398 if(connection
->serviced
<= 0)
399 do_log(L_ERROR
, "Client dropped connection.\n");
401 do_log(D_CLIENT_CONN
, "Client dropped idle connection.\n");
403 connection
->flags
&= ~CONN_READER
;
404 if(!connection
->request
)
405 httpClientFinish(connection
, 2);
407 pokeFdEvent(connection
->fd
, -EDOGRACEFUL
, POLLOUT
);
411 if(connection
->reqlen
>= bufsize
) {
413 if(!(connection
->flags
& CONN_BIGREQBUF
))
414 rc
= httpConnectionBigifyReqbuf(connection
);
416 do_stream(IO_READ
, connection
->fd
, connection
->reqlen
,
417 connection
->reqbuf
, bigBufferSize
,
418 httpClientHandler
, connection
);
421 connection
->reqlen
= 0;
422 httpConnectionDestroyReqbuf(connection
);
424 do_log(L_ERROR
, "Couldn't allocate big buffer.\n");
425 httpClientNewError(connection
, METHOD_UNKNOWN
, 0, 400,
426 internAtom("Couldn't allocate big buffer"));
428 do_log(L_ERROR
, "Couldn't find end of client's headers.\n");
429 httpClientNewError(connection
, METHOD_UNKNOWN
, 0, 400,
430 internAtom("Couldn't find end of headers"));
434 httpSetTimeout(connection
, clientTimeout
);
439 httpClientRawErrorHeaders(HTTPConnectionPtr connection
,
440 int code
, AtomPtr message
,
441 int close
, AtomPtr headers
)
443 int fd
= connection
->fd
;
445 char *url
; int url_len
;
448 assert(connection
->flags
& CONN_WRITER
);
452 if(connection
->request
)
454 close
|| !(connection
->request
->flags
& REQUEST_PERSISTENT
);
458 if(connection
->request
&& connection
->request
->object
) {
459 url
= connection
->request
->object
->key
;
460 url_len
= connection
->request
->object
->key_size
;
461 etag
= connection
->request
->object
->etag
;
468 if(connection
->buf
== NULL
) {
469 connection
->buf
= get_chunk();
470 if(connection
->buf
== NULL
) {
471 httpClientFinish(connection
, 1);
476 n
= httpWriteErrorHeaders(connection
->buf
, CHUNK_SIZE
, 0,
477 connection
->request
&&
478 connection
->request
->method
!= METHOD_HEAD
,
479 code
, message
, close
> 0, headers
,
482 shutdown(connection
->fd
, 1);
484 httpClientFinish(connection
, 1);
488 httpSetTimeout(connection
, clientTimeout
);
489 do_stream(IO_WRITE
, fd
, 0, connection
->buf
, n
,
490 close
> 0 ? httpErrorStreamHandler
:
491 close
== 0 ? httpErrorNocloseStreamHandler
:
492 httpErrorNofinishStreamHandler
,
499 httpClientRawError(HTTPConnectionPtr connection
, int code
, AtomPtr message
,
502 return httpClientRawErrorHeaders(connection
, code
, message
, close
, NULL
);
506 httpClientNoticeErrorHeaders(HTTPRequestPtr request
, int code
, AtomPtr message
,
509 if(request
->error_message
)
510 releaseAtom(request
->error_message
);
511 if(request
->error_headers
)
512 releaseAtom(request
->error_headers
);
513 request
->error_code
= code
;
514 request
->error_message
= message
;
515 request
->error_headers
= headers
;
516 httpClientNoticeRequest(request
, 0);
521 httpClientNoticeError(HTTPRequestPtr request
, int code
, AtomPtr message
)
523 return httpClientNoticeErrorHeaders(request
, code
, message
, NULL
);
527 httpClientError(HTTPRequestPtr request
, int code
, AtomPtr message
)
529 if(request
->error_message
)
530 releaseAtom(request
->error_message
);
531 request
->error_code
= code
;
532 request
->error_message
= message
;
533 if(request
->chandler
) {
534 abortConditionHandler(request
->chandler
);
535 request
->chandler
= NULL
;
536 } else if(request
->object
)
537 notifyObject(request
->object
);
541 /* This may be called from object handlers. */
543 httpClientLeanError(HTTPRequestPtr request
, int code
, AtomPtr message
)
545 if(request
->error_message
)
546 releaseAtom(request
->error_message
);
547 request
->error_code
= code
;
548 request
->error_message
= message
;
554 httpClientNewError(HTTPConnectionPtr connection
, int method
, int persist
,
555 int code
, AtomPtr message
)
557 HTTPRequestPtr request
;
558 request
= httpMakeRequest();
559 if(request
== NULL
) {
560 do_log(L_ERROR
, "Couldn't allocate error request.\n");
561 httpClientFinish(connection
, 1);
564 request
->method
= method
;
566 request
->flags
|= REQUEST_PERSISTENT
;
568 request
->flags
&= ~REQUEST_PERSISTENT
;
569 request
->error_code
= code
;
570 request
->error_message
= message
;
572 httpQueueRequest(connection
, request
);
573 httpClientNoticeRequest(request
, 0);
578 httpErrorStreamHandler(int status
,
579 FdEventHandlerPtr event
,
580 StreamRequestPtr srequest
)
582 HTTPConnectionPtr connection
= srequest
->data
;
584 if(status
== 0 && !streamRequestDone(srequest
))
587 httpClientFinish(connection
, 1);
592 httpErrorNocloseStreamHandler(int status
,
593 FdEventHandlerPtr event
,
594 StreamRequestPtr srequest
)
596 HTTPConnectionPtr connection
= srequest
->data
;
598 if(status
== 0 && !streamRequestDone(srequest
))
601 httpClientFinish(connection
, 0);
606 httpErrorNofinishStreamHandler(int status
,
607 FdEventHandlerPtr event
,
608 StreamRequestPtr srequest
)
610 if(status
== 0 && !streamRequestDone(srequest
))
617 httpClientHandlerHeaders(FdEventHandlerPtr event
, StreamRequestPtr srequest
,
618 HTTPConnectionPtr connection
)
620 HTTPRequestPtr request
;
629 /* Work around clients working around NCSA lossage. */
630 if(connection
->reqbuf
[0] == '\n')
632 else if(connection
->reqbuf
[0] == '\r' && connection
->reqbuf
[1] == '\n')
635 httpSetTimeout(connection
, -1);
636 rc
= httpParseClientFirstLine(connection
->reqbuf
, start
,
637 &method
, &url
, &version
);
639 do_log(L_ERROR
, "Couldn't parse client's request line\n");
641 message
= internAtom("Error in request line");
645 do_log(D_CLIENT_REQ
, "Client request: ");
646 do_log_n(D_CLIENT_REQ
, connection
->reqbuf
, rc
- 1);
647 do_log(D_CLIENT_REQ
, "\n");
649 if(version
!= HTTP_10
&& version
!= HTTP_11
) {
650 do_log(L_ERROR
, "Unknown client HTTP version\n");
652 message
= internAtom("Error in first request line");
656 if(method
== METHOD_UNKNOWN
) {
658 message
= internAtom("Method not implemented");
662 request
= httpMakeRequest();
663 if(request
== NULL
) {
664 do_log(L_ERROR
, "Couldn't allocate client request.\n");
666 message
= internAtom("Couldn't allocate client request");
670 if(connection
->version
!= HTTP_UNKNOWN
&& version
!= connection
->version
) {
671 do_log(L_WARN
, "Client version changed!\n");
674 connection
->version
= version
;
675 request
->flags
= REQUEST_PERSISTENT
;
676 request
->method
= method
;
677 request
->cache_control
= no_cache_control
;
678 httpQueueRequest(connection
, request
);
679 connection
->reqbegin
= rc
;
680 return httpClientRequest(request
, url
);
683 if(url
) releaseAtom(url
);
684 shutdown(connection
->fd
, 0);
685 connection
->reqlen
= 0;
686 connection
->reqbegin
= 0;
687 httpConnectionDestroyReqbuf(connection
);
688 connection
->flags
&= ~CONN_READER
;
689 httpClientNewError(connection
, METHOD_UNKNOWN
, 0, code
, message
);
695 httpClientRequestDelayed(TimeEventHandlerPtr event
)
697 HTTPRequestPtr request
= *(HTTPRequestPtr
*)event
->data
;
699 url
= internAtomN(request
->object
->key
, request
->object
->key_size
);
701 do_log(L_ERROR
, "Couldn't allocate url.\n");
702 abortObject(request
->object
, 503, internAtom("Couldn't allocate url"));
705 httpClientRequest(request
, url
);
710 delayedHttpClientRequest(HTTPRequestPtr request
)
712 TimeEventHandlerPtr event
;
713 event
= scheduleTimeEvent(-1, httpClientRequestDelayed
,
714 sizeof(request
), &request
);
721 httpClientRequest(HTTPRequestPtr request
, AtomPtr url
)
723 HTTPConnectionPtr connection
= request
->connection
;
725 int body_len
, body_te
;
727 CacheControlRec cache_control
;
728 AtomPtr via
, expect
, auth
;
729 HTTPConditionPtr condition
;
732 assert(!request
->chandler
);
733 assert(connection
->reqbuf
);
735 i
= httpParseHeaders(1, url
,
736 connection
->reqbuf
, connection
->reqbegin
, request
,
738 &cache_control
, &condition
, &body_te
,
739 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
740 &expect
, &range
, NULL
, NULL
, &via
, &auth
);
743 do_log(L_ERROR
, "Couldn't parse client headers.\n");
744 shutdown(connection
->fd
, 0);
745 request
->flags
&= ~REQUEST_PERSISTENT
;
746 connection
->flags
&= ~CONN_READER
;
747 httpClientNoticeError(request
, 503,
748 internAtom("Couldn't parse client headers"));
752 connection
->reqbegin
= i
;
755 if(request
->method
== METHOD_GET
|| request
->method
== METHOD_HEAD
)
758 connection
->bodylen
= body_len
;
759 connection
->reqte
= body_te
;
762 AtomPtr message
= NULL
;
763 AtomPtr challenge
= NULL
;
764 int code
= checkClientAuth(auth
, url
, &message
, &challenge
);
774 if(expect
) releaseAtom(expect
);
775 request
->flags
|= REQUEST_FORCE_ERROR
;
776 httpClientDiscardBody(connection
);
777 httpClientNoticeErrorHeaders(request
, code
, message
, challenge
);
788 if(expect
== atom100Continue
) {
789 request
->flags
|= REQUEST_WAIT_CONTINUE
;
794 request
->from
= range
.from
< 0 ? 0 : range
.from
;
795 request
->to
= range
.to
;
796 request
->cache_control
= cache_control
;
798 request
->headers
= headers
;
799 request
->condition
= condition
;
800 request
->object
= NULL
;
802 if(connection
->serviced
> 500)
803 request
->flags
&= ~REQUEST_PERSISTENT
;
805 if(request
->method
== METHOD_CONNECT
) {
806 if(connection
->flags
& CONN_WRITER
) {
808 httpClientDiscardBody(connection
);
809 httpClientNoticeError(request
, 500,
810 internAtom("Pipelined CONNECT "
814 if(connection
->flags
& CONN_BIGREQBUF
) {
816 httpClientDiscardBody(connection
);
817 httpClientNoticeError(request
, 500,
818 internAtom("CONNECT over big buffer "
821 connection
->flags
&= ~CONN_READER
;
822 do_tunnel(connection
->fd
, connection
->reqbuf
,
823 connection
->reqbegin
, connection
->reqlen
, url
);
825 connection
->reqbuf
= NULL
;
826 connection
->reqlen
= 0;
827 connection
->reqbegin
= 0;
828 httpClientFinish(connection
, 2);
832 rc
= urlForbidden(url
, httpClientRequestContinue
, request
);
834 do_log(L_ERROR
, "Couldn't schedule httpClientRequestContinue.\n");
835 httpClientDiscardBody(connection
);
836 httpClientNoticeError(request
, 500,
837 internAtom("Couldn't schedule "
838 "httpClientRequestContinue"));
845 httpClientRequestContinue(int forbidden_code
, AtomPtr url
,
846 AtomPtr forbidden_message
, AtomPtr forbidden_headers
,
849 HTTPRequestPtr request
= (HTTPRequestPtr
)closure
;
850 HTTPConnectionPtr connection
= request
->connection
;
851 RequestFunction requestfn
;
852 ObjectPtr object
= NULL
;
854 if(forbidden_code
< 0) {
856 httpClientDiscardBody(connection
);
857 httpClientNoticeError(request
, 500,
858 internAtomError(-forbidden_code
,
859 "Couldn't test for forbidden "
866 httpClientDiscardBody(connection
);
867 httpClientNoticeErrorHeaders(request
,
868 forbidden_code
, forbidden_message
,
874 urlIsLocal(url
->string
, url
->length
) ?
878 if(request
->method
== METHOD_POST
|| request
->method
== METHOD_PUT
) {
880 object
= findObject(OBJECT_HTTP
, url
->string
, url
->length
);
882 privatiseObject(object
, 0);
883 releaseObject(object
);
886 request
->object
= makeObject(OBJECT_HTTP
, url
->string
, url
->length
,
887 0, 0, requestfn
, NULL
);
888 if(request
->object
== NULL
) {
889 httpClientDiscardBody(connection
);
890 httpClientNoticeError(request
, 503,
891 internAtom("Couldn't allocate object"));
894 if(requestfn
== httpLocalRequest
)
895 request
->object
->flags
|= OBJECT_LOCAL
;
896 return httpClientSideRequest(request
);
899 if(request
->cache_control
.flags
& CACHE_AUTHORIZATION
) {
901 object
= makeObject(OBJECT_HTTP
, url
->string
, url
->length
, 0, 0,
903 if(object
&& object
->flags
!= OBJECT_INITIAL
) {
904 if(!(object
->cache_control
& CACHE_PUBLIC
)) {
905 privatiseObject(object
, 0);
906 releaseObject(object
);
911 } while(object
== NULL
);
913 object
->flags
|= OBJECT_LINEAR
;
915 object
= findObject(OBJECT_HTTP
, url
->string
, url
->length
);
917 object
= makeObject(OBJECT_HTTP
, url
->string
, url
->length
, 1, 1,
924 do_log(L_ERROR
, "Couldn't allocate object.\n");
925 httpClientDiscardBody(connection
);
926 httpClientNoticeError(request
, 503,
927 internAtom("Couldn't allocate object"));
931 if(object
->request
== httpLocalRequest
) {
932 object
->flags
|= OBJECT_LOCAL
;
935 httpClientDiscardBody(connection
);
936 httpClientNoticeError(request
, 403,
937 internAtom("Proxying disabled"));
938 releaseObject(object
);
942 if(!checkVia(proxyName
, request
->via
)) {
943 httpClientDiscardBody(connection
);
944 httpClientNoticeError(request
, 504,
945 internAtom("Proxy loop detected"));
946 releaseObject(object
);
951 request
->object
= object
;
953 httpClientDiscardBody(connection
);
954 httpClientNoticeRequest(request
, 0);
958 static int httpClientDelayed(TimeEventHandlerPtr handler
);
961 httpClientDiscardBody(HTTPConnectionPtr connection
)
963 TimeEventHandlerPtr handler
;
965 assert(connection
->reqoffset
== 0);
966 assert(connection
->flags
& CONN_READER
);
968 if(connection
->reqte
!= TE_IDENTITY
)
971 if(connection
->bodylen
< 0)
974 if(connection
->bodylen
+ connection
->reqbegin
< connection
->reqlen
) {
975 connection
->reqbegin
+= connection
->bodylen
;
976 connection
->bodylen
= 0;
978 connection
->bodylen
-= connection
->reqlen
- connection
->reqbegin
;
979 connection
->reqbegin
= 0;
980 connection
->reqlen
= 0;
981 httpConnectionDestroyReqbuf(connection
);
983 connection
->reqte
= TE_UNKNOWN
;
985 if(connection
->bodylen
> 0) {
986 httpSetTimeout(connection
, clientTimeout
);
987 do_stream_buf(IO_READ
| IO_NOTNOW
,
988 connection
->fd
, connection
->reqlen
,
989 &connection
->reqbuf
, CHUNK_SIZE
,
990 httpClientDiscardHandler
, connection
);
994 if(connection
->reqlen
> connection
->reqbegin
) {
995 memmove(connection
->reqbuf
, connection
->reqbuf
+ connection
->reqbegin
,
996 connection
->reqlen
- connection
->reqbegin
);
997 connection
->reqlen
-= connection
->reqbegin
;
998 connection
->reqbegin
= 0;
1000 connection
->reqlen
= 0;
1001 connection
->reqbegin
= 0;
1004 httpSetTimeout(connection
, clientTimeout
);
1005 /* We need to delay in order to make sure the previous request
1006 gets queued on the server side. IO_NOTNOW isn't strong enough
1007 for that due to IO_IMMEDIATE. */
1008 handler
= scheduleTimeEvent(-1, httpClientDelayed
,
1009 sizeof(connection
), &connection
);
1010 if(handler
== NULL
) {
1011 do_log(L_ERROR
, "Couldn't schedule reading from client.");
1017 connection
->reqlen
= 0;
1018 connection
->reqbegin
= 0;
1019 connection
->bodylen
= 0;
1020 connection
->reqte
= TE_UNKNOWN
;
1021 shutdown(connection
->fd
, 2);
1022 handler
= scheduleTimeEvent(-1, httpClientDelayed
,
1023 sizeof(connection
), &connection
);
1024 if(handler
== NULL
) {
1025 do_log(L_ERROR
, "Couldn't schedule reading from client.");
1026 connection
->flags
&= ~CONN_READER
;
1032 httpClientDelayed(TimeEventHandlerPtr event
)
1034 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1036 /* IO_NOTNOW is unfortunate, but needed to avoid starvation if a
1037 client is pipelining a lot of requests. */
1038 if(connection
->reqlen
> 0) {
1040 if((connection
->flags
& CONN_BIGREQBUF
) &&
1041 connection
->reqlen
< CHUNK_SIZE
)
1042 httpConnectionUnbigifyReqbuf(connection
);
1043 /* Don't read new requests if buffer is big. */
1044 bufsize
= (connection
->flags
& CONN_BIGREQBUF
) ?
1045 connection
->reqlen
: CHUNK_SIZE
;
1046 do_stream(IO_READ
| IO_IMMEDIATE
| IO_NOTNOW
,
1047 connection
->fd
, connection
->reqlen
,
1048 connection
->reqbuf
, bufsize
,
1049 httpClientHandler
, connection
);
1051 httpConnectionDestroyReqbuf(connection
);
1052 do_stream_buf(IO_READ
| IO_NOTNOW
,
1054 &connection
->reqbuf
, CHUNK_SIZE
,
1055 httpClientHandler
, connection
);
1061 httpClientDiscardHandler(int status
,
1062 FdEventHandlerPtr event
, StreamRequestPtr request
)
1064 HTTPConnectionPtr connection
= request
->data
;
1066 assert(connection
->flags
& CONN_READER
);
1068 if(status
< 0 && status
!= -EPIPE
)
1069 do_log_error(L_ERROR
, -status
, "Couldn't read from client");
1070 connection
->bodylen
= -1;
1071 return httpClientDiscardBody(connection
);
1074 assert(request
->offset
> connection
->reqlen
);
1075 connection
->reqlen
= request
->offset
;
1077 httpClientDiscardBody(connection
);
1082 httpClientNoticeRequest(HTTPRequestPtr request
, int novalidate
)
1084 HTTPConnectionPtr connection
= request
->connection
;
1085 ObjectPtr object
= request
->object
;
1086 int serveNow
= (request
== connection
->request
);
1088 int conditional
= 0;
1089 int local
, haveData
;
1092 assert(!request
->chandler
);
1094 if(request
->error_code
) {
1095 if((request
->flags
& REQUEST_FORCE_ERROR
) || REQUEST_SIDE(request
) ||
1096 request
->object
== NULL
||
1097 (request
->object
->flags
& OBJECT_LOCAL
) ||
1098 (request
->object
->flags
& OBJECT_ABORTED
) ||
1099 (relaxTransparency
< 1 && !proxyOffline
)) {
1101 connection
->flags
|= CONN_WRITER
;
1102 return httpClientRawErrorHeaders(connection
,
1103 request
->error_code
,
1104 retainAtom(request
->
1106 0, request
->error_headers
);
1113 if(REQUEST_SIDE(request
)) {
1114 assert(!(request
->flags
& REQUEST_REQUESTED
));
1116 assert(!request
->chandler
);
1118 conditionWait(&request
->object
->condition
,
1119 httpClientGetHandler
,
1120 sizeof(request
), &request
);
1121 if(request
->chandler
== NULL
) {
1122 do_log(L_ERROR
, "Couldn't register condition handler.\n");
1123 connection
->flags
|= CONN_WRITER
;
1124 httpClientRawError(connection
, 500,
1125 internAtom("Couldn't register "
1126 "condition handler"),
1130 connection
->flags
|= CONN_WRITER
;
1131 rc
= object
->request(request
->object
,
1133 request
->from
, request
->to
,
1135 request
->object
->request_closure
);
1140 local
= urlIsLocal(object
->key
, object
->key_size
);
1141 objectFillFromDisk(object
, request
->from
,
1142 request
->method
== METHOD_HEAD
? 0 : 1);
1144 if(request
->condition
&& request
->condition
->ifrange
) {
1146 strcmp(object
->etag
, request
->condition
->ifrange
) != 0) {
1152 if(object
->flags
& OBJECT_DYNAMIC
) {
1157 if(request
->method
== METHOD_HEAD
)
1158 haveData
= !(request
->object
->flags
& OBJECT_INITIAL
);
1161 (request
->object
->length
>= 0 &&
1162 request
->object
->length
<= request
->from
) ||
1163 (objectHoleSize(request
->object
, request
->from
) == 0);
1165 if(request
->flags
& REQUEST_REQUESTED
)
1167 else if(novalidate
|| (!local
&& proxyOffline
))
1171 objectMustRevalidate(request
->object
, &request
->cache_control
);
1172 else if(request
->cache_control
.flags
& CACHE_ONLY_IF_CACHED
)
1174 else if((request
->object
->flags
& OBJECT_FAILED
) &&
1175 !(object
->flags
& OBJECT_INPROGRESS
) &&
1178 else if(request
->method
!= METHOD_HEAD
&&
1179 !objectHasData(object
, request
->from
, request
->to
) &&
1180 !(object
->flags
& OBJECT_INPROGRESS
))
1182 else if(objectMustRevalidate((relaxTransparency
<= 1 ?
1183 request
->object
: NULL
),
1184 &request
->cache_control
))
1189 if(request
->cache_control
.flags
& CACHE_ONLY_IF_CACHED
) {
1193 return httpClientRawError(connection
, 504,
1194 internAtom("Object not in cache"),
1201 if(!(request
->object
->flags
& OBJECT_VALIDATING
) &&
1202 ((!validate
&& haveData
) ||
1203 (request
->object
->flags
& OBJECT_FAILED
))) {
1205 connection
->flags
|= CONN_WRITER
;
1206 lockChunk(request
->object
, request
->from
/ CHUNK_SIZE
);
1207 return httpServeObject(connection
);
1213 if((request
->flags
& REQUEST_REQUESTED
) &&
1214 !(request
->object
->flags
& OBJECT_INPROGRESS
)) {
1215 /* This can happen either because the server side ran out of
1216 memory, or because it is using HEAD validation. We mark
1217 the object to be fetched again. */
1218 request
->flags
&= ~REQUEST_REQUESTED
;
1222 connection
->flags
|= CONN_WRITER
;
1223 if(!local
&& proxyOffline
)
1224 return httpClientRawError(connection
, 502,
1225 internAtom("Disconnected operation "
1226 "and object not in cache"),
1229 conditionWait(&request
->object
->condition
, httpClientGetHandler
,
1230 sizeof(request
), &request
);
1231 if(request
->chandler
== NULL
) {
1232 do_log(L_ERROR
, "Couldn't register condition handler.\n");
1233 return httpClientRawError(connection
, 503,
1234 internAtom("Couldn't register "
1235 "condition handler"), 0);
1239 if(request
->object
->flags
& OBJECT_VALIDATING
)
1242 conditional
= (haveData
&& request
->method
== METHOD_GET
);
1243 if(!mindlesslyCacheVary
&& (request
->object
->cache_control
& CACHE_VARY
))
1244 conditional
= conditional
&& (request
->object
->etag
!= NULL
);
1246 request
->object
->flags
|= OBJECT_VALIDATING
;
1247 rc
= request
->object
->request(request
->object
,
1248 conditional
? METHOD_CONDITIONAL_GET
:
1250 request
->from
, request
->to
, request
,
1251 request
->object
->request_closure
);
1253 if(request
->chandler
)
1254 unregisterConditionHandler(request
->chandler
);
1255 request
->chandler
= NULL
;
1256 request
->object
->flags
&= ~OBJECT_VALIDATING
;
1257 request
->object
->flags
|= OBJECT_FAILED
;
1258 if(request
->error_message
)
1259 releaseAtom(request
->error_message
);
1260 request
->error_code
= 503;
1261 request
->error_message
= internAtom("Couldn't schedule get");
1267 httpClientNoticeRequestDelayed(TimeEventHandlerPtr event
)
1269 HTTPRequestPtr request
= *(HTTPRequestPtr
*)event
->data
;
1270 httpClientNoticeRequest(request
, 0);
1275 delayedHttpClientNoticeRequest(HTTPRequestPtr request
)
1277 TimeEventHandlerPtr event
;
1278 event
= scheduleTimeEvent(-1, httpClientNoticeRequestDelayed
,
1279 sizeof(request
), &request
);
1286 httpClientContinueDelayed(TimeEventHandlerPtr event
)
1288 static char httpContinue
[] = "HTTP/1.1 100 Continue\r\n\r\n";
1289 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1291 do_stream(IO_WRITE
, connection
->fd
, 0, httpContinue
, 25,
1292 httpErrorNofinishStreamHandler
, connection
);
1297 delayedHttpClientContinue(HTTPConnectionPtr connection
)
1299 TimeEventHandlerPtr event
;
1300 event
= scheduleTimeEvent(-1, httpClientContinueDelayed
,
1301 sizeof(connection
), &connection
);
1308 httpClientGetHandler(int status
, ConditionHandlerPtr chandler
)
1310 HTTPRequestPtr request
= *(HTTPRequestPtr
*)chandler
->data
;
1311 HTTPConnectionPtr connection
= request
->connection
;
1312 ObjectPtr object
= request
->object
;
1315 assert(request
== connection
->request
);
1317 if(request
->request
) {
1318 assert(request
->object
->flags
& OBJECT_INPROGRESS
);
1319 assert(!request
->request
->object
||
1320 request
->request
->object
== request
->object
);
1324 object
->flags
&= ~OBJECT_VALIDATING
; /* for now */
1325 if(request
->request
&& request
->request
->request
== request
)
1326 httpServerClientReset(request
->request
);
1327 lockChunk(object
, request
->from
/ CHUNK_SIZE
);
1328 request
->chandler
= NULL
;
1329 rc
= delayedHttpServeObject(connection
);
1331 unlockChunk(object
, request
->from
/ CHUNK_SIZE
);
1332 do_log(L_ERROR
, "Couldn't schedule serving.\n");
1333 abortObject(object
, 503, internAtom("Couldn't schedule serving"));
1338 if(object
->flags
& OBJECT_VALIDATING
)
1341 if(request
->error_code
) {
1342 lockChunk(object
, request
->from
/ CHUNK_SIZE
);
1343 request
->chandler
= NULL
;
1344 rc
= delayedHttpServeObject(connection
);
1346 unlockChunk(object
, request
->from
/ CHUNK_SIZE
);
1347 do_log(L_ERROR
, "Couldn't schedule serving.\n");
1348 abortObject(object
, 503, internAtom("Couldn't schedule serving"));
1353 if(request
->flags
& REQUEST_WAIT_CONTINUE
) {
1354 if(request
->request
&&
1355 !(request
->request
->flags
& REQUEST_WAIT_CONTINUE
)) {
1356 request
->flags
&= ~REQUEST_WAIT_CONTINUE
;
1357 delayedHttpClientContinue(connection
);
1362 /* See httpServerHandlerHeaders */
1363 if((object
->flags
& OBJECT_SUPERSEDED
) &&
1364 request
->request
&& request
->request
->can_mutate
) {
1365 ObjectPtr new_object
= retainObject(request
->request
->can_mutate
);
1366 if(object
->requestor
== request
) {
1367 if(new_object
->requestor
== NULL
)
1368 new_object
->requestor
= request
;
1369 object
->requestor
= NULL
;
1371 request
->chandler
= NULL
;
1372 releaseObject(object
);
1373 request
->object
= new_object
;
1374 request
->request
->object
= new_object
;
1375 /* We're handling the wrong object now. It's simpler to
1376 rebuild the whole data structure from scratch rather than
1377 trying to compensate. */
1378 rc
= delayedHttpClientNoticeRequest(request
);
1380 do_log(L_ERROR
, "Couldn't schedule noticing of request.");
1381 abortObject(object
, 500,
1382 internAtom("Couldn't schedule "
1383 "noticing of request"));
1384 /* We're probably out of memory. What can we do? */
1385 shutdown(connection
->fd
, 1);
1390 if(object
->requestor
!= request
&& !(object
->flags
& OBJECT_ABORTED
)) {
1391 /* Make sure we don't serve an object that is stale for us
1392 unless we're the requestor. */
1393 if((object
->flags
& (OBJECT_LINEAR
| OBJECT_MUTATING
)) ||
1394 objectMustRevalidate(object
, &request
->cache_control
)) {
1395 if(object
->flags
& OBJECT_INPROGRESS
)
1397 rc
= delayedHttpClientNoticeRequest(request
);
1399 do_log(L_ERROR
, "Couldn't schedule noticing of request.");
1400 abortObject(object
, 500,
1401 internAtom("Couldn't schedule "
1402 "noticing of request"));
1404 request
->chandler
= NULL
;
1410 if(object
->flags
& (OBJECT_INITIAL
| OBJECT_VALIDATING
)) {
1411 if(object
->flags
& (OBJECT_INPROGRESS
| OBJECT_VALIDATING
)) {
1413 } else if(object
->flags
& OBJECT_FAILED
) {
1414 if(request
->error_code
)
1416 request
->error_code
,
1417 retainAtom(request
->error_message
));
1419 abortObject(object
, 500,
1420 internAtom("Error message lost in transit"));
1423 /* The request was pruned by httpServerDiscardRequests */
1424 if(chandler
== request
->chandler
) {
1426 request
->chandler
= NULL
;
1427 rc
= delayedHttpClientNoticeRequest(request
);
1429 abortObject(object
, 500,
1430 internAtom("Couldn't allocate "
1431 "delayed notice request"));
1435 abortObject(object
, 500,
1436 internAtom("Wrong request pruned -- "
1437 "this shouldn't happen"));
1442 if(request
->object
->flags
& OBJECT_DYNAMIC
) {
1443 if(objectHoleSize(request
->object
, 0) == 0) {
1447 /* We really should request again if that is not the case */
1451 lockChunk(object
, request
->from
/ CHUNK_SIZE
);
1452 request
->chandler
= NULL
;
1453 rc
= delayedHttpServeObject(connection
);
1455 unlockChunk(object
, request
->from
/ CHUNK_SIZE
);
1456 do_log(L_ERROR
, "Couldn't schedule serving.\n");
1457 abortObject(object
, 503, internAtom("Couldn't schedule serving"));
1463 httpClientSideRequest(HTTPRequestPtr request
)
1465 HTTPConnectionPtr connection
= request
->connection
;
1467 if(request
->from
< 0 || request
->to
>= 0) {
1468 httpClientNoticeError(request
, 501,
1469 internAtom("Partial requests not implemented"));
1470 httpClientDiscardBody(connection
);
1473 if(connection
->reqte
!= TE_IDENTITY
) {
1474 httpClientNoticeError(request
, 501,
1475 internAtom("Chunked requests not implemented"));
1476 httpClientDiscardBody(connection
);
1479 if(connection
->bodylen
< 0) {
1480 httpClientNoticeError(request
, 502,
1481 internAtom("POST or PUT without "
1483 httpClientDiscardBody(connection
);
1486 if(connection
->reqlen
< 0) {
1487 httpClientNoticeError(request
, 502,
1488 internAtom("Incomplete POST or PUT"));
1489 httpClientDiscardBody(connection
);
1493 return httpClientNoticeRequest(request
, 0);
1497 httpClientSideHandler(int status
,
1498 FdEventHandlerPtr event
,
1499 StreamRequestPtr srequest
)
1501 HTTPConnectionPtr connection
= srequest
->data
;
1502 HTTPRequestPtr request
= connection
->request
;
1503 HTTPRequestPtr requestee
;
1504 HTTPConnectionPtr server
;
1507 AtomPtr message
= NULL
;
1509 assert(connection
->flags
& CONN_SIDE_READER
);
1511 if((request
->object
->flags
& OBJECT_ABORTED
) ||
1512 !(request
->object
->flags
& OBJECT_INPROGRESS
)) {
1513 code
= request
->object
->code
;
1514 message
= retainAtom(request
->object
->message
);
1519 do_log_error(L_ERROR
, -status
, "Reading from client");
1521 message
= internAtomError(-status
, "Couldn't read from client");
1525 requestee
= request
->request
;
1526 server
= requestee
->connection
;
1528 push
= MIN(srequest
->offset
- connection
->reqlen
,
1529 connection
->bodylen
- connection
->reqoffset
);
1531 connection
->reqlen
+= push
;
1532 httpServerDoSide(server
);
1536 if(server
->reqoffset
>= connection
->bodylen
) {
1537 connection
->flags
&= ~(CONN_READER
| CONN_SIDE_READER
);
1542 do_log(L_ERROR
, "Incomplete client request.\n");
1544 message
= internAtom("Incomplete client request");
1547 request
->error_code
= code
;
1548 if(request
->error_message
)
1549 releaseAtom(request
->error_message
);
1550 request
->error_message
= message
;
1551 if(request
->error_headers
)
1552 releaseAtom(request
->error_headers
);
1553 request
->error_headers
= NULL
;
1555 if(request
->request
) {
1556 shutdown(request
->request
->connection
->fd
, 2);
1557 pokeFdEvent(request
->request
->connection
->fd
, -ESHUTDOWN
, POLLOUT
);
1559 notifyObject(request
->object
);
1560 connection
->flags
&= ~CONN_SIDE_READER
;
1561 httpClientDiscardBody(connection
);
1566 httpServeObject(HTTPConnectionPtr connection
)
1568 HTTPRequestPtr request
= connection
->request
;
1569 ObjectPtr object
= request
->object
;
1570 int i
= request
->from
/ CHUNK_SIZE
;
1571 int j
= request
->from
% CHUNK_SIZE
;
1573 int bufsize
= CHUNK_SIZE
;
1574 int condition_result
;
1576 object
->atime
= current_time
.tv_sec
;
1577 objectMetadataChanged(object
, 0);
1579 httpSetTimeout(connection
, -1);
1581 if((request
->error_code
&& relaxTransparency
<= 0) ||
1582 object
->flags
& OBJECT_INITIAL
) {
1583 object
->flags
&= ~OBJECT_FAILED
;
1584 unlockChunk(object
, i
);
1585 if(request
->error_code
)
1586 return httpClientRawError(connection
,
1587 request
->error_code
,
1588 retainAtom(request
->error_message
), 0);
1590 return httpClientRawError(connection
,
1591 500, internAtom("Object vanished."), 0);
1594 if(!(object
->flags
& OBJECT_INPROGRESS
) && object
->code
== 0) {
1595 if(object
->flags
& OBJECT_INITIAL
) {
1596 unlockChunk(object
, i
);
1597 return httpClientRawError(connection
, 503,
1598 internAtom("Error message lost"), 0);
1601 unlockChunk(object
, i
);
1602 do_log(L_ERROR
, "Internal proxy error: object has no code.\n");
1603 return httpClientRawError(connection
, 500,
1604 internAtom("Internal proxy error: "
1605 "object has no code"), 0);
1609 condition_result
= httpCondition(object
, request
->condition
);
1611 if(condition_result
== CONDITION_FAILED
) {
1612 unlockChunk(object
, i
);
1613 return httpClientRawError(connection
, 412,
1614 internAtom("Precondition failed"), 0);
1615 } else if(condition_result
== CONDITION_NOT_MODIFIED
) {
1616 unlockChunk(object
, i
);
1617 return httpClientRawError(connection
, 304,
1618 internAtom("Not modified"), 0);
1621 objectFillFromDisk(object
, request
->from
,
1622 (request
->method
== METHOD_HEAD
||
1623 condition_result
!= CONDITION_MATCH
) ? 0 : 1);
1625 if(((object
->flags
& OBJECT_LINEAR
) &&
1626 (object
->requestor
!= connection
->request
)) ||
1627 ((object
->flags
& OBJECT_SUPERSEDED
) &&
1628 !(object
->flags
& OBJECT_LINEAR
))) {
1629 if(request
->request
) {
1630 request
->request
->request
= NULL
;
1631 request
->request
= NULL
;
1632 request
->object
->requestor
= NULL
;
1634 object
= makeObject(OBJECT_HTTP
,
1635 object
->key
, object
->key_size
, 1, 0,
1636 object
->request
, NULL
);
1637 if(request
->object
->requestor
== request
)
1638 request
->object
->requestor
= NULL
;
1639 unlockChunk(request
->object
, i
);
1640 releaseObject(request
->object
);
1641 request
->object
= NULL
;
1642 if(object
== NULL
) {
1643 do_log(L_ERROR
, "Couldn't allocate object.");
1644 return httpClientRawError(connection
, 501,
1645 internAtom("Couldn't allocate object"),
1648 if(urlIsLocal(object
->key
, object
->key_size
)) {
1649 object
->flags
|= OBJECT_LOCAL
;
1650 object
->request
= httpLocalRequest
;
1652 request
->object
= object
;
1653 connection
->flags
&= ~CONN_WRITER
;
1654 return httpClientNoticeRequest(request
, 1);
1657 if(object
->flags
& OBJECT_ABORTED
) {
1658 unlockChunk(object
, i
);
1659 return httpClientNoticeError(request
, object
->code
,
1660 retainAtom(object
->message
));
1663 if(connection
->buf
== NULL
)
1664 connection
->buf
= get_chunk();
1665 if(connection
->buf
== NULL
) {
1666 unlockChunk(object
, i
);
1667 do_log(L_ERROR
, "Couldn't allocate client buffer.\n");
1668 connection
->flags
&= ~CONN_WRITER
;
1669 httpClientFinish(connection
, 1);
1673 if(object
->length
>= 0 && request
->to
>= object
->length
)
1676 if(request
->from
> 0 || request
->to
>= 0) {
1677 if(request
->method
== METHOD_HEAD
) {
1678 request
->to
= request
->from
;
1679 } else if(request
->to
< 0) {
1680 if(object
->length
>= 0)
1681 request
->to
= object
->length
;
1687 connection
->len
= 0;
1689 if((request
->from
<= 0 && request
->to
< 0) ||
1690 request
->method
== METHOD_HEAD
) {
1691 n
= snnprintf(connection
->buf
, 0, bufsize
,
1693 object
->code
, atomString(object
->message
));
1695 if(request
->from
> request
->to
) {
1696 unlockChunk(object
, i
);
1697 return httpClientRawError(connection
, 416,
1698 internAtom("Requested range "
1702 n
= snnprintf(connection
->buf
, 0, bufsize
,
1703 "HTTP/1.1 206 Partial content");
1707 n
= httpWriteObjectHeaders(connection
->buf
, n
, bufsize
,
1708 object
, request
->from
, request
->to
);
1712 if(request
->method
!= METHOD_HEAD
&&
1713 condition_result
!= CONDITION_NOT_MODIFIED
&&
1714 request
->to
< 0 && object
->length
< 0) {
1715 if(connection
->version
== HTTP_11
) {
1716 connection
->te
= TE_CHUNKED
;
1717 n
= snnprintf(connection
->buf
, n
, bufsize
,
1718 "\r\nTransfer-Encoding: chunked");
1720 request
->flags
&= ~REQUEST_PERSISTENT
;
1724 if(object
->age
< current_time
.tv_sec
) {
1725 n
= snnprintf(connection
->buf
, n
, bufsize
,
1727 (int)(current_time
.tv_sec
- object
->age
));
1729 n
= snnprintf(connection
->buf
, n
, bufsize
,
1730 "\r\nConnection: %s",
1731 (request
->flags
& REQUEST_PERSISTENT
) ?
1732 "keep-alive" : "close");
1734 if(!(object
->flags
& OBJECT_LOCAL
)) {
1735 if((object
->flags
& OBJECT_FAILED
) && !proxyOffline
) {
1736 n
= snnprintf(connection
->buf
, n
, bufsize
,
1737 "\r\nWarning: 111 %s:%d Revalidation failed",
1738 proxyName
->string
, proxyPort
);
1739 if(request
->error_code
)
1740 n
= snnprintf(connection
->buf
, n
, bufsize
,
1742 request
->error_code
,
1743 atomString(request
->error_message
));
1744 object
->flags
&= ~OBJECT_FAILED
;
1745 } else if(proxyOffline
&&
1746 objectMustRevalidate(object
, &request
->cache_control
)) {
1747 n
= snnprintf(connection
->buf
, n
, bufsize
,
1748 "\r\nWarning: 112 %s:%d Disconnected operation",
1749 proxyName
->string
, proxyPort
);
1750 } else if(objectIsStale(object
, &request
->cache_control
)) {
1751 n
= snnprintf(connection
->buf
, n
, bufsize
,
1752 "\r\nWarning: 110 %s:%d Object is stale",
1753 proxyName
->string
, proxyPort
);
1754 } else if(object
->expires
< 0 && object
->max_age
< 0 &&
1755 object
->age
< current_time
.tv_sec
- 24 * 3600) {
1756 n
= snnprintf(connection
->buf
, n
, bufsize
,
1757 "\r\nWarning: 113 %s:%d Heuristic expiration",
1758 proxyName
->string
, proxyPort
);
1762 n
= snnprintf(connection
->buf
, n
, bufsize
, "\r\n\r\n");
1767 connection
->offset
= request
->from
;
1769 if(request
->method
== METHOD_HEAD
||
1770 condition_result
== CONDITION_NOT_MODIFIED
||
1771 (object
->flags
& OBJECT_ABORTED
)) {
1774 if(i
< object
->numchunks
) {
1775 if(object
->chunks
[i
].size
<= j
)
1778 len
= object
->chunks
[i
].size
- j
;
1782 if(request
->to
>= 0)
1783 len
= MIN(len
, request
->to
- request
->from
);
1786 connection
->offset
= request
->from
;
1787 httpSetTimeout(connection
, clientTimeout
);
1788 do_log(D_CLIENT_DATA
, "Serving on 0x%lx for 0x%lx: offset %d len %d\n",
1789 (unsigned long)connection
, (unsigned long)object
,
1790 connection
->offset
, len
);
1791 do_stream_h(IO_WRITE
|
1792 (connection
->te
== TE_CHUNKED
&& len
> 0 ? IO_CHUNKED
: 0),
1795 object
->chunks
[i
].data
+ j
, len
,
1796 httpServeObjectStreamHandler
, connection
);
1801 connection
->len
= 0;
1802 if(!(connection
->flags
& CONN_BIGBUF
))
1803 rc
= httpConnectionBigify(connection
);
1805 bufsize
= bigBufferSize
;
1808 unlockChunk(object
, i
);
1809 return httpClientRawError(connection
, 500,
1811 internAtom("No space for headers") :
1812 internAtom("Couldn't allocate big buffer"), 0);
1816 httpServeObjectDelayed(TimeEventHandlerPtr event
)
1818 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1819 httpServeObject(connection
);
1824 delayedHttpServeObject(HTTPConnectionPtr connection
)
1826 TimeEventHandlerPtr event
;
1828 assert(connection
->request
->object
->chunks
[connection
->request
->from
/
1829 CHUNK_SIZE
].locked
> 0);
1831 event
= scheduleTimeEvent(-1, httpServeObjectDelayed
,
1832 sizeof(connection
), &connection
);
1833 if(!event
) return -1;
1838 httpServeObjectFinishHandler(int status
,
1839 FdEventHandlerPtr event
,
1840 StreamRequestPtr srequest
)
1842 HTTPConnectionPtr connection
= srequest
->data
;
1843 HTTPRequestPtr request
= connection
->request
;
1846 assert(!request
->chandler
);
1848 if(status
== 0 && !streamRequestDone(srequest
))
1851 httpSetTimeout(connection
, -1);
1854 do_log(L_ERROR
, "Couldn't terminate chunked reply\n");
1855 httpClientFinish(connection
, 1);
1857 httpClientFinish(connection
, 0);
1863 httpServeChunk(HTTPConnectionPtr connection
)
1865 HTTPRequestPtr request
= connection
->request
;
1866 ObjectPtr object
= request
->object
;
1867 int i
= connection
->offset
/ CHUNK_SIZE
;
1868 int j
= connection
->offset
- (i
* CHUNK_SIZE
);
1869 int to
, len
, len2
, end
;
1872 if(object
->flags
& OBJECT_ABORTED
)
1873 goto fail_no_unlock
;
1875 if(object
->length
>= 0 && request
->to
>= 0)
1876 to
= MIN(request
->to
, object
->length
);
1877 else if(object
->length
>= 0)
1878 to
= object
->length
;
1879 else if(request
->to
>= 0)
1884 lockChunk(object
, i
);
1886 if(i
< object
->numchunks
)
1887 len
= object
->chunks
[i
].size
- j
;
1889 if(request
->method
!= METHOD_HEAD
&&
1890 len
< CHUNK_SIZE
&& connection
->offset
+ len
< to
) {
1891 objectFillFromDisk(object
, connection
->offset
+ len
, 2);
1892 len
= object
->chunks
[i
].size
- j
;
1896 len
= MIN(len
, to
- connection
->offset
);
1899 if(to
>= 0 && connection
->offset
>= to
) {
1900 if(request
->chandler
) {
1901 unregisterConditionHandler(request
->chandler
);
1902 request
->chandler
= NULL
;
1904 unlockChunk(object
, i
);
1905 if(connection
->te
== TE_CHUNKED
) {
1906 httpSetTimeout(connection
, clientTimeout
);
1907 do_stream(IO_WRITE
| IO_CHUNKED
| IO_END
,
1908 connection
->fd
, 0, NULL
, 0,
1909 httpServeObjectFinishHandler
, connection
);
1911 httpClientFinish(connection
,
1912 !(object
->length
>= 0 &&
1913 connection
->offset
>= object
->length
));
1917 if(!request
->chandler
) {
1919 conditionWait(&object
->condition
,
1920 httpServeObjectHandler
,
1921 sizeof(connection
), &connection
);
1922 if(!request
->chandler
) {
1923 do_log(L_ERROR
, "Couldn't register condition handler\n");
1927 if(!(object
->flags
& OBJECT_INPROGRESS
)) {
1928 if(object
->flags
& OBJECT_SUPERSEDED
) {
1931 if(REQUEST_SIDE(request
)) goto fail
;
1932 rc
= object
->request(object
, request
->method
,
1933 connection
->offset
, -1, request
,
1934 object
->request_closure
);
1935 if(rc
<= 0) goto fail
;
1941 if(request
->method
!= METHOD_HEAD
)
1942 objectFillFromDisk(object
, (i
+ 1) * CHUNK_SIZE
, 1);
1943 if(request
->chandler
) {
1944 unregisterConditionHandler(request
->chandler
);
1945 request
->chandler
= NULL
;
1948 if(j
+ len
== CHUNK_SIZE
&& object
->numchunks
> i
+ 1) {
1949 len2
= object
->chunks
[i
+ 1].size
;
1951 len2
= MIN(len2
, to
- (i
+ 1) * CHUNK_SIZE
);
1953 /* Lock early -- httpServerRequest may get_chunk */
1955 lockChunk(object
, i
+ 1);
1956 if(object
->length
>= 0 &&
1957 connection
->offset
+ len
+ len2
== object
->length
)
1962 if(!(object
->flags
& OBJECT_INPROGRESS
) && !REQUEST_SIDE(request
)) {
1963 if(object
->chunks
[i
].size
< CHUNK_SIZE
&&
1964 to
>= 0 && connection
->offset
+ len
+ 1 < to
)
1965 object
->request(object
, request
->method
,
1966 connection
->offset
+ len
, -1, request
,
1967 object
->request_closure
);
1968 else if(i
+ 1 < object
->numchunks
&&
1969 object
->chunks
[i
+ 1].size
== 0 &&
1970 to
>= 0 && (i
+ 1) * CHUNK_SIZE
+ 1 < to
)
1971 object
->request(object
, request
->method
,
1972 (i
+ 1) * CHUNK_SIZE
, -1, request
,
1973 object
->request_closure
);
1976 httpSetTimeout(connection
, clientTimeout
);
1977 do_log(D_CLIENT_DATA
,
1978 "Serving on 0x%lx for 0x%lx: offset %d len %d\n",
1979 (unsigned long)connection
, (unsigned long)object
,
1980 connection
->offset
, len
);
1981 /* IO_NOTNOW in order to give other clients a chance to run. */
1982 do_stream(IO_WRITE
| IO_NOTNOW
|
1983 (connection
->te
== TE_CHUNKED
? IO_CHUNKED
: 0) |
1986 object
->chunks
[i
].data
+ j
, len
,
1987 httpServeObjectStreamHandler
, connection
);
1989 httpSetTimeout(connection
, clientTimeout
);
1990 do_log(D_CLIENT_DATA
,
1991 "Serving on 0x%lx for 0x%lx: offset %d len %d + %d\n",
1992 (unsigned long)connection
, (unsigned long)object
,
1993 connection
->offset
, len
, len2
);
1994 do_stream_2(IO_WRITE
| IO_NOTNOW
|
1995 (connection
->te
== TE_CHUNKED
? IO_CHUNKED
: 0) |
1998 object
->chunks
[i
].data
+ j
, len
,
1999 object
->chunks
[i
+ 1].data
, len2
,
2000 httpServeObjectStreamHandler2
, connection
);
2008 unlockChunk(object
, i
);
2010 if(request
->chandler
)
2011 unregisterConditionHandler(request
->chandler
);
2012 request
->chandler
= NULL
;
2013 httpClientFinish(connection
, 1);
2018 httpServeChunkDelayed(TimeEventHandlerPtr event
)
2020 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
2021 httpServeChunk(connection
);
2026 delayedHttpServeChunk(HTTPConnectionPtr connection
)
2028 TimeEventHandlerPtr event
;
2029 event
= scheduleTimeEvent(-1, httpServeChunkDelayed
,
2030 sizeof(connection
), &connection
);
2031 if(!event
) return -1;
2036 httpServeObjectHandler(int status
, ConditionHandlerPtr chandler
)
2038 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)chandler
->data
;
2039 HTTPRequestPtr request
= connection
->request
;
2042 unlockChunk(request
->object
, connection
->offset
/ CHUNK_SIZE
);
2044 if((request
->object
->flags
& OBJECT_ABORTED
) || status
< 0) {
2045 shutdown(connection
->fd
, 1);
2046 httpSetTimeout(connection
, 10);
2047 /* httpServeChunk will take care of the error. */
2050 httpSetTimeout(connection
, -1);
2052 request
->chandler
= NULL
;
2053 rc
= delayedHttpServeChunk(connection
);
2055 do_log(L_ERROR
, "Couldn't schedule serving.\n");
2056 abortObject(request
->object
, 503,
2057 internAtom("Couldn't schedule serving"));
2063 httpServeObjectStreamHandlerCommon(int kind
, int status
,
2064 FdEventHandlerPtr event
,
2065 StreamRequestPtr srequest
)
2067 HTTPConnectionPtr connection
= srequest
->data
;
2068 HTTPRequestPtr request
= connection
->request
;
2069 int condition_result
= httpCondition(request
->object
, request
->condition
);
2070 int i
= connection
->offset
/ CHUNK_SIZE
;
2072 assert(!request
->chandler
);
2074 if(status
== 0 && !streamRequestDone(srequest
)) {
2075 httpSetTimeout(connection
, clientTimeout
);
2079 httpSetTimeout(connection
, -1);
2081 unlockChunk(request
->object
, i
);
2083 unlockChunk(request
->object
, i
+ 1);
2087 do_log_error(status
== -ECONNRESET
? D_IO
: L_ERROR
,
2088 -status
, "Couldn't write to client");
2089 if(status
== -EIO
|| status
== -ESHUTDOWN
)
2090 httpClientFinish(connection
, 2);
2092 httpClientFinish(connection
, 1);
2094 do_log(D_IO
, "Couldn't write to client: short write.\n");
2095 httpClientFinish(connection
, 2);
2100 if(srequest
->operation
& IO_CHUNKED
) {
2101 assert(srequest
->offset
> 2);
2102 connection
->offset
+= srequest
->offset
- 2;
2104 connection
->offset
+= srequest
->offset
;
2106 request
->flags
&= ~REQUEST_REQUESTED
;
2108 if(request
->object
->flags
& OBJECT_ABORTED
) {
2109 httpClientFinish(connection
, 1);
2113 if(connection
->request
->method
== METHOD_HEAD
||
2114 condition_result
== CONDITION_NOT_MODIFIED
) {
2115 httpClientFinish(connection
, 0);
2119 if(srequest
->operation
& IO_END
)
2120 httpClientFinish(connection
, 0);
2122 httpConnectionDestroyBuf(connection
);
2123 httpServeChunk(connection
);
2129 httpServeObjectStreamHandler(int status
,
2130 FdEventHandlerPtr event
,
2131 StreamRequestPtr srequest
)
2133 return httpServeObjectStreamHandlerCommon(1, status
, event
, srequest
);
2137 httpServeObjectStreamHandler2(int status
,
2138 FdEventHandlerPtr event
,
2139 StreamRequestPtr srequest
)
2141 return httpServeObjectStreamHandlerCommon(2, status
, event
, srequest
);