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 unlockChunk(object
, i
);
1584 if(request
->error_code
)
1585 return httpClientRawError(connection
,
1586 request
->error_code
,
1587 retainAtom(request
->error_message
), 0);
1589 return httpClientRawError(connection
,
1590 500, internAtom("Object vanished."), 0);
1593 if(!(object
->flags
& OBJECT_INPROGRESS
) && object
->code
== 0) {
1594 if(object
->flags
& OBJECT_INITIAL
) {
1595 unlockChunk(object
, i
);
1596 return httpClientRawError(connection
, 503,
1597 internAtom("Error message lost"), 0);
1600 unlockChunk(object
, i
);
1601 do_log(L_ERROR
, "Internal proxy error: object has no code.\n");
1602 return httpClientRawError(connection
, 500,
1603 internAtom("Internal proxy error: "
1604 "object has no code"), 0);
1608 condition_result
= httpCondition(object
, request
->condition
);
1610 if(condition_result
== CONDITION_FAILED
) {
1611 unlockChunk(object
, i
);
1612 return httpClientRawError(connection
, 412,
1613 internAtom("Precondition failed"), 0);
1614 } else if(condition_result
== CONDITION_NOT_MODIFIED
) {
1615 unlockChunk(object
, i
);
1616 return httpClientRawError(connection
, 304,
1617 internAtom("Not modified"), 0);
1620 objectFillFromDisk(object
, request
->from
,
1621 (request
->method
== METHOD_HEAD
||
1622 condition_result
!= CONDITION_MATCH
) ? 0 : 1);
1624 if(((object
->flags
& OBJECT_LINEAR
) &&
1625 (object
->requestor
!= connection
->request
)) ||
1626 ((object
->flags
& OBJECT_SUPERSEDED
) &&
1627 !(object
->flags
& OBJECT_LINEAR
))) {
1628 if(request
->request
) {
1629 request
->request
->request
= NULL
;
1630 request
->request
= NULL
;
1631 request
->object
->requestor
= NULL
;
1633 object
= makeObject(OBJECT_HTTP
,
1634 object
->key
, object
->key_size
, 1, 0,
1635 object
->request
, NULL
);
1636 if(request
->object
->requestor
== request
)
1637 request
->object
->requestor
= NULL
;
1638 unlockChunk(request
->object
, i
);
1639 releaseObject(request
->object
);
1640 request
->object
= NULL
;
1641 if(object
== NULL
) {
1642 do_log(L_ERROR
, "Couldn't allocate object.");
1643 return httpClientRawError(connection
, 501,
1644 internAtom("Couldn't allocate object"),
1647 if(urlIsLocal(object
->key
, object
->key_size
)) {
1648 object
->flags
|= OBJECT_LOCAL
;
1649 object
->request
= httpLocalRequest
;
1651 request
->object
= object
;
1652 connection
->flags
&= ~CONN_WRITER
;
1653 return httpClientNoticeRequest(request
, 1);
1656 if(object
->flags
& OBJECT_ABORTED
) {
1657 unlockChunk(object
, i
);
1658 return httpClientNoticeError(request
, object
->code
,
1659 retainAtom(object
->message
));
1662 if(connection
->buf
== NULL
)
1663 connection
->buf
= get_chunk();
1664 if(connection
->buf
== NULL
) {
1665 unlockChunk(object
, i
);
1666 do_log(L_ERROR
, "Couldn't allocate client buffer.\n");
1667 connection
->flags
&= ~CONN_WRITER
;
1668 httpClientFinish(connection
, 1);
1672 if(object
->length
>= 0 && request
->to
>= object
->length
)
1675 if(request
->from
> 0 || request
->to
>= 0) {
1676 if(request
->method
== METHOD_HEAD
) {
1677 request
->to
= request
->from
;
1678 } else if(request
->to
< 0) {
1679 if(object
->length
>= 0)
1680 request
->to
= object
->length
;
1686 connection
->len
= 0;
1688 if((request
->from
<= 0 && request
->to
< 0) ||
1689 request
->method
== METHOD_HEAD
) {
1690 n
= snnprintf(connection
->buf
, 0, bufsize
,
1692 object
->code
, atomString(object
->message
));
1694 if(request
->from
> request
->to
) {
1695 unlockChunk(object
, i
);
1696 return httpClientRawError(connection
, 416,
1697 internAtom("Requested range "
1701 n
= snnprintf(connection
->buf
, 0, bufsize
,
1702 "HTTP/1.1 206 Partial content");
1706 n
= httpWriteObjectHeaders(connection
->buf
, n
, bufsize
,
1707 object
, request
->from
, request
->to
);
1711 if(request
->method
!= METHOD_HEAD
&&
1712 condition_result
!= CONDITION_NOT_MODIFIED
&&
1713 request
->to
< 0 && object
->length
< 0) {
1714 if(connection
->version
== HTTP_11
) {
1715 connection
->te
= TE_CHUNKED
;
1716 n
= snnprintf(connection
->buf
, n
, bufsize
,
1717 "\r\nTransfer-Encoding: chunked");
1719 request
->flags
&= ~REQUEST_PERSISTENT
;
1723 if(object
->age
< current_time
.tv_sec
) {
1724 n
= snnprintf(connection
->buf
, n
, bufsize
,
1726 (int)(current_time
.tv_sec
- object
->age
));
1728 n
= snnprintf(connection
->buf
, n
, bufsize
,
1729 "\r\nConnection: %s",
1730 (request
->flags
& REQUEST_PERSISTENT
) ?
1731 "keep-alive" : "close");
1733 if(!(object
->flags
& OBJECT_LOCAL
)) {
1734 if((object
->flags
& OBJECT_FAILED
) && !proxyOffline
) {
1735 n
= snnprintf(connection
->buf
, n
, bufsize
,
1736 "\r\nWarning: 111 %s:%d Revalidation failed",
1737 proxyName
->string
, proxyPort
);
1738 if(request
->error_code
)
1739 n
= snnprintf(connection
->buf
, n
, bufsize
,
1741 request
->error_code
,
1742 atomString(request
->error_message
));
1743 } else if(proxyOffline
&&
1744 objectMustRevalidate(object
, &request
->cache_control
)) {
1745 n
= snnprintf(connection
->buf
, n
, bufsize
,
1746 "\r\nWarning: 112 %s:%d Disconnected operation",
1747 proxyName
->string
, proxyPort
);
1748 } else if(objectIsStale(object
, &request
->cache_control
)) {
1749 n
= snnprintf(connection
->buf
, n
, bufsize
,
1750 "\r\nWarning: 110 %s:%d Object is stale",
1751 proxyName
->string
, proxyPort
);
1752 } else if(object
->expires
< 0 &&
1753 object
->age
< current_time
.tv_sec
- 24 * 3600) {
1754 n
= snnprintf(connection
->buf
, n
, bufsize
,
1755 "\r\nWarning: 113 %s:%d Heuristic expiration",
1756 proxyName
->string
, proxyPort
);
1760 n
= snnprintf(connection
->buf
, n
, bufsize
, "\r\n\r\n");
1765 connection
->offset
= request
->from
;
1767 if(request
->method
== METHOD_HEAD
||
1768 condition_result
== CONDITION_NOT_MODIFIED
||
1769 (object
->flags
& OBJECT_ABORTED
)) {
1772 if(i
< object
->numchunks
) {
1773 if(object
->chunks
[i
].size
<= j
)
1776 len
= object
->chunks
[i
].size
- j
;
1780 if(request
->to
>= 0)
1781 len
= MIN(len
, request
->to
- request
->from
);
1784 connection
->offset
= request
->from
;
1785 httpSetTimeout(connection
, clientTimeout
);
1786 do_log(D_CLIENT_DATA
, "Serving on 0x%lx for 0x%lx: offset %d len %d\n",
1787 (unsigned long)connection
, (unsigned long)object
,
1788 connection
->offset
, len
);
1789 do_stream_h(IO_WRITE
|
1790 (connection
->te
== TE_CHUNKED
&& len
> 0 ? IO_CHUNKED
: 0),
1793 object
->chunks
[i
].data
+ j
, len
,
1794 httpServeObjectStreamHandler
, connection
);
1799 connection
->len
= 0;
1800 if(!(connection
->flags
& CONN_BIGBUF
))
1801 rc
= httpConnectionBigify(connection
);
1803 bufsize
= bigBufferSize
;
1806 unlockChunk(object
, i
);
1807 return httpClientRawError(connection
, 500,
1809 internAtom("No space for headers") :
1810 internAtom("Couldn't allocate big buffer"), 0);
1814 httpServeObjectDelayed(TimeEventHandlerPtr event
)
1816 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
1817 httpServeObject(connection
);
1822 delayedHttpServeObject(HTTPConnectionPtr connection
)
1824 TimeEventHandlerPtr event
;
1826 assert(connection
->request
->object
->chunks
[connection
->request
->from
/
1827 CHUNK_SIZE
].locked
> 0);
1829 event
= scheduleTimeEvent(-1, httpServeObjectDelayed
,
1830 sizeof(connection
), &connection
);
1831 if(!event
) return -1;
1836 httpServeObjectFinishHandler(int status
,
1837 FdEventHandlerPtr event
,
1838 StreamRequestPtr srequest
)
1840 HTTPConnectionPtr connection
= srequest
->data
;
1841 HTTPRequestPtr request
= connection
->request
;
1844 assert(!request
->chandler
);
1846 if(status
== 0 && !streamRequestDone(srequest
))
1849 httpSetTimeout(connection
, -1);
1852 do_log(L_ERROR
, "Couldn't terminate chunked reply\n");
1853 httpClientFinish(connection
, 1);
1855 httpClientFinish(connection
, 0);
1861 httpServeChunk(HTTPConnectionPtr connection
)
1863 HTTPRequestPtr request
= connection
->request
;
1864 ObjectPtr object
= request
->object
;
1865 int i
= connection
->offset
/ CHUNK_SIZE
;
1866 int j
= connection
->offset
- (i
* CHUNK_SIZE
);
1867 int to
, len
, len2
, end
;
1870 if(object
->flags
& OBJECT_ABORTED
)
1871 goto fail_no_unlock
;
1873 if(object
->length
>= 0 && request
->to
>= 0)
1874 to
= MIN(request
->to
, object
->length
);
1875 else if(object
->length
>= 0)
1876 to
= object
->length
;
1877 else if(request
->to
>= 0)
1882 lockChunk(object
, i
);
1884 if(i
< object
->numchunks
)
1885 len
= object
->chunks
[i
].size
- j
;
1887 if(request
->method
!= METHOD_HEAD
&&
1888 len
< CHUNK_SIZE
&& connection
->offset
+ len
< to
) {
1889 objectFillFromDisk(object
, connection
->offset
+ len
, 2);
1890 len
= object
->chunks
[i
].size
- j
;
1894 len
= MIN(len
, to
- connection
->offset
);
1897 if(to
>= 0 && connection
->offset
>= to
) {
1898 if(request
->chandler
) {
1899 unregisterConditionHandler(request
->chandler
);
1900 request
->chandler
= NULL
;
1902 unlockChunk(object
, i
);
1903 if(connection
->te
== TE_CHUNKED
) {
1904 httpSetTimeout(connection
, clientTimeout
);
1905 do_stream(IO_WRITE
| IO_CHUNKED
| IO_END
,
1906 connection
->fd
, 0, NULL
, 0,
1907 httpServeObjectFinishHandler
, connection
);
1909 httpClientFinish(connection
,
1910 !(object
->length
>= 0 &&
1911 connection
->offset
>= object
->length
));
1915 if(!request
->chandler
) {
1917 conditionWait(&object
->condition
,
1918 httpServeObjectHandler
,
1919 sizeof(connection
), &connection
);
1920 if(!request
->chandler
) {
1921 do_log(L_ERROR
, "Couldn't register condition handler\n");
1925 if(!(object
->flags
& OBJECT_INPROGRESS
)) {
1926 if(object
->flags
& OBJECT_SUPERSEDED
) {
1929 if(REQUEST_SIDE(request
)) goto fail
;
1930 rc
= object
->request(object
, request
->method
,
1931 connection
->offset
, -1, request
,
1932 object
->request_closure
);
1933 if(rc
<= 0) goto fail
;
1939 if(request
->method
!= METHOD_HEAD
)
1940 objectFillFromDisk(object
, (i
+ 1) * CHUNK_SIZE
, 1);
1941 if(request
->chandler
) {
1942 unregisterConditionHandler(request
->chandler
);
1943 request
->chandler
= NULL
;
1946 if(j
+ len
== CHUNK_SIZE
&& object
->numchunks
> i
+ 1) {
1947 len2
= object
->chunks
[i
+ 1].size
;
1949 len2
= MIN(len2
, to
- (i
+ 1) * CHUNK_SIZE
);
1951 /* Lock early -- httpServerRequest may get_chunk */
1953 lockChunk(object
, i
+ 1);
1954 if(object
->length
>= 0 &&
1955 connection
->offset
+ len
+ len2
== object
->length
)
1960 if(!(object
->flags
& OBJECT_INPROGRESS
) && !REQUEST_SIDE(request
)) {
1961 if(object
->chunks
[i
].size
< CHUNK_SIZE
&&
1962 to
>= 0 && connection
->offset
+ len
+ 1 < to
)
1963 object
->request(object
, request
->method
,
1964 connection
->offset
+ len
, -1, request
,
1965 object
->request_closure
);
1966 else if(i
+ 1 < object
->numchunks
&&
1967 object
->chunks
[i
+ 1].size
== 0 &&
1968 to
>= 0 && (i
+ 1) * CHUNK_SIZE
+ 1 < to
)
1969 object
->request(object
, request
->method
,
1970 (i
+ 1) * CHUNK_SIZE
, -1, request
,
1971 object
->request_closure
);
1974 httpSetTimeout(connection
, clientTimeout
);
1975 do_log(D_CLIENT_DATA
,
1976 "Serving on 0x%lx for 0x%lx: offset %d len %d\n",
1977 (unsigned long)connection
, (unsigned long)object
,
1978 connection
->offset
, len
);
1979 /* IO_NOTNOW in order to give other clients a chance to run. */
1980 do_stream(IO_WRITE
| IO_NOTNOW
|
1981 (connection
->te
== TE_CHUNKED
? IO_CHUNKED
: 0) |
1984 object
->chunks
[i
].data
+ j
, len
,
1985 httpServeObjectStreamHandler
, connection
);
1987 httpSetTimeout(connection
, clientTimeout
);
1988 do_log(D_CLIENT_DATA
,
1989 "Serving on 0x%lx for 0x%lx: offset %d len %d + %d\n",
1990 (unsigned long)connection
, (unsigned long)object
,
1991 connection
->offset
, len
, len2
);
1992 do_stream_2(IO_WRITE
| IO_NOTNOW
|
1993 (connection
->te
== TE_CHUNKED
? IO_CHUNKED
: 0) |
1996 object
->chunks
[i
].data
+ j
, len
,
1997 object
->chunks
[i
+ 1].data
, len2
,
1998 httpServeObjectStreamHandler2
, connection
);
2006 unlockChunk(object
, i
);
2008 if(request
->chandler
)
2009 unregisterConditionHandler(request
->chandler
);
2010 request
->chandler
= NULL
;
2011 httpClientFinish(connection
, 1);
2016 httpServeChunkDelayed(TimeEventHandlerPtr event
)
2018 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)event
->data
;
2019 httpServeChunk(connection
);
2024 delayedHttpServeChunk(HTTPConnectionPtr connection
)
2026 TimeEventHandlerPtr event
;
2027 event
= scheduleTimeEvent(-1, httpServeChunkDelayed
,
2028 sizeof(connection
), &connection
);
2029 if(!event
) return -1;
2034 httpServeObjectHandler(int status
, ConditionHandlerPtr chandler
)
2036 HTTPConnectionPtr connection
= *(HTTPConnectionPtr
*)chandler
->data
;
2037 HTTPRequestPtr request
= connection
->request
;
2040 unlockChunk(request
->object
, connection
->offset
/ CHUNK_SIZE
);
2042 if((request
->object
->flags
& OBJECT_ABORTED
) || status
< 0) {
2043 shutdown(connection
->fd
, 1);
2044 httpSetTimeout(connection
, 10);
2045 /* httpServeChunk will take care of the error. */
2048 httpSetTimeout(connection
, -1);
2050 request
->chandler
= NULL
;
2051 rc
= delayedHttpServeChunk(connection
);
2053 do_log(L_ERROR
, "Couldn't schedule serving.\n");
2054 abortObject(request
->object
, 503,
2055 internAtom("Couldn't schedule serving"));
2061 httpServeObjectStreamHandlerCommon(int kind
, int status
,
2062 FdEventHandlerPtr event
,
2063 StreamRequestPtr srequest
)
2065 HTTPConnectionPtr connection
= srequest
->data
;
2066 HTTPRequestPtr request
= connection
->request
;
2067 int condition_result
= httpCondition(request
->object
, request
->condition
);
2068 int i
= connection
->offset
/ CHUNK_SIZE
;
2070 assert(!request
->chandler
);
2072 if(status
== 0 && !streamRequestDone(srequest
)) {
2073 httpSetTimeout(connection
, clientTimeout
);
2077 httpSetTimeout(connection
, -1);
2079 unlockChunk(request
->object
, i
);
2081 unlockChunk(request
->object
, i
+ 1);
2085 do_log_error(status
== -ECONNRESET
? D_IO
: L_ERROR
,
2086 -status
, "Couldn't write to client");
2087 if(status
== -EIO
|| status
== -ESHUTDOWN
)
2088 httpClientFinish(connection
, 2);
2090 httpClientFinish(connection
, 1);
2092 do_log(D_IO
, "Couldn't write to client: short write.\n");
2093 httpClientFinish(connection
, 2);
2098 if(srequest
->operation
& IO_CHUNKED
) {
2099 assert(srequest
->offset
> 2);
2100 connection
->offset
+= srequest
->offset
- 2;
2102 connection
->offset
+= srequest
->offset
;
2104 request
->flags
&= ~REQUEST_REQUESTED
;
2106 if(request
->object
->flags
& OBJECT_ABORTED
) {
2107 httpClientFinish(connection
, 1);
2111 if(connection
->request
->method
== METHOD_HEAD
||
2112 condition_result
== CONDITION_NOT_MODIFIED
) {
2113 httpClientFinish(connection
, 0);
2117 if(srequest
->operation
& IO_END
)
2118 httpClientFinish(connection
, 0);
2120 httpConnectionDestroyBuf(connection
);
2121 httpServeChunk(connection
);
2127 httpServeObjectStreamHandler(int status
,
2128 FdEventHandlerPtr event
,
2129 StreamRequestPtr srequest
)
2131 return httpServeObjectStreamHandlerCommon(1, status
, event
, srequest
);
2135 httpServeObjectStreamHandler2(int status
,
2136 FdEventHandlerPtr event
,
2137 StreamRequestPtr srequest
)
2139 return httpServeObjectStreamHandlerCommon(2, status
, event
, srequest
);