Update CHANGES for 0.9.99.1.
[polipo.git] / client.c
blob48ba95091656906e9afe7c620461a9cceb9c675e
1 /*
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
20 THE SOFTWARE.
23 #include "polipo.h"
25 static int
26 httpAcceptAgain(TimeEventHandlerPtr event)
28 FdEventHandlerPtr newevent;
29 int fd = *(int*)event->data;
31 newevent = schedule_accept(fd, httpAccept, NULL);
32 if(newevent == NULL) {
33 free_chunk_arenas();
34 newevent = schedule_accept(fd, httpAccept, NULL);
35 if(newevent == NULL) {
36 do_log(L_ERROR, "Couldn't schedule accept.\n");
37 polipoExit();
40 return 1;
43 int
44 httpAccept(int fd, FdEventHandlerPtr event, AcceptRequestPtr request)
46 int rc;
47 HTTPConnectionPtr connection;
48 TimeEventHandlerPtr timeout;
50 if(fd < 0) {
51 if(-fd == EINTR || -fd == EAGAIN || -fd == EWOULDBLOCK)
52 return 0;
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");
57 free_chunk_arenas();
58 again = scheduleTimeEvent(1, httpAcceptAgain,
59 sizeof(request->fd), &request->fd);
60 if(!again) {
61 do_log(L_ERROR, "Couldn't schedule accept -- sleeping.\n");
62 sleep(1);
63 again = scheduleTimeEvent(1, httpAcceptAgain,
64 sizeof(request->fd), &request->fd);
65 if(!again) {
66 do_log(L_ERROR, "Couldn't schedule accept -- aborting.\n");
67 polipoExit();
70 return 1;
71 } else {
72 polipoExit();
73 return 1;
77 if(allowedNets) {
78 if(netAddressMatch(fd, allowedNets) != 1) {
79 do_log(L_WARN, "Refusing connection from unauthorised net\n");
80 close(fd);
81 return 0;
85 rc = setNonblocking(fd, 1);
86 if(rc < 0) {
87 do_log_error(L_WARN, errno, "Couldn't set non blocking mode");
88 close(fd);
89 return 0;
91 rc = setNodelay(fd, 1);
92 if(rc < 0)
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);
99 if(!timeout) {
100 close(fd);
101 free(connection);
102 return 0;
105 connection->fd = fd;
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);
116 return 0;
119 /* Abort a client connection. It is only safe to abort the requests
120 if we know the connection is closed. */
121 void
122 httpClientAbort(HTTPConnectionPtr connection, int closed)
124 HTTPRequestPtr request = connection->request;
126 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLOUT);
127 if(closed) {
128 while(request) {
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. */
142 void
143 httpClientFinish(HTTPConnectionPtr connection, int s)
145 HTTPRequestPtr request = connection->request;
147 assert(!(request && request->request
148 && request->request->request != request));
150 if(s == 0) {
151 if(!request || !(request->flags & REQUEST_PERSISTENT))
152 s = 1;
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));
163 if(s >= 2) {
164 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLIN);
165 } else {
166 pokeFdEvent(connection->fd, -EDOGRACEFUL, POLLIN);
168 return;
171 if(connection->timeout)
172 cancelTimeEvent(connection->timeout);
173 connection->timeout = NULL;
175 if(request) {
176 HTTPRequestPtr requestee;
178 requestee = request->request;
179 if(requestee) {
180 request->request = NULL;
181 requestee->request = NULL;
183 if(requestee)
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);
200 request = NULL;
204 connection->len = -1;
205 connection->offset = 0;
206 connection->te = TE_IDENTITY;
208 if(!s) {
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,
223 &connection->reqbuf,
224 (connection->flags & CONN_BIGREQBUF) ?
225 bigBufferSize : CHUNK_SIZE,
226 httpClientHandler, connection);
228 /* The request has already been validated when it first got
229 into the queue */
230 if(connection->request) {
231 if(connection->request->object != NULL)
232 httpClientNoticeRequest(connection->request, 1);
233 else
234 assert(connection->flags & CONN_READER);
236 return;
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;
245 if(s >= 2) {
246 pokeFdEvent(connection->fd, -EDOSHUTDOWN, POLLIN);
247 } else {
248 pokeFdEvent(connection->fd, -EDOGRACEFUL, POLLIN);
250 return;
252 while(1) {
253 HTTPRequestPtr requestee;
254 request = connection->request;
255 if(!request)
256 break;
257 requestee = request->request;
258 request->request = NULL;
259 if(requestee) {
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) {
276 if(s >= 2)
277 close(connection->fd);
278 else
279 lingeringClose(connection->fd);
281 connection->fd = -1;
282 free(connection);
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);
292 static int
293 httpClientDelayedShutdown(HTTPConnectionPtr connection)
295 TimeEventHandlerPtr handler;
297 assert(connection->flags & CONN_READER);
298 handler = scheduleTimeEvent(1, httpClientDelayedShutdownHandler,
299 sizeof(connection), &connection);
300 if(!handler) {
301 do_log(L_ERROR,
302 "Couldn't schedule delayed shutdown -- freeing memory.");
303 free_chunk_arenas();
304 handler = scheduleTimeEvent(1, httpClientDelayedShutdownHandler,
305 sizeof(connection), &connection);
306 if(!handler) {
307 do_log(L_ERROR,
308 "Couldn't schedule delayed shutdown -- aborting.\n");
309 polipoExit();
312 return 1;
315 static int
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);
329 else
330 httpClientFinish(connection, 1);
331 return 1;
334 httpClientDelayedShutdown(connection);
335 return 1;
338 static int
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);
349 return 1;
351 do_stream(IO_READ | IO_NOTNOW, connection->fd,
352 0, client_shutdown_buffer, 17,
353 httpClientShutdownHandler, connection);
354 return 1;
358 httpClientHandler(int status,
359 FdEventHandlerPtr event, StreamRequestPtr request)
361 HTTPConnectionPtr connection = request->data;
362 int i, body;
363 int bufsize =
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. */
371 if(status != 0) {
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);
378 else
379 httpClientFinish(connection, 1);
380 return 1;
382 httpClientAbort(connection, status > 0 || status == -ECONNRESET);
383 connection->flags &= ~CONN_READER;
384 return 1;
387 i = findEndOfHeaders(connection->reqbuf, 0, request->offset, &body);
388 connection->reqlen = request->offset;
390 if(i >= 0) {
391 connection->reqbegin = i;
392 httpClientHandlerHeaders(event, request, connection);
393 return 1;
396 if(status) {
397 if(connection->reqlen > 0) {
398 if(connection->serviced <= 0)
399 do_log(L_ERROR, "Client dropped connection.\n");
400 else
401 do_log(D_CLIENT_CONN, "Client dropped idle connection.\n");
403 connection->flags &= ~CONN_READER;
404 if(!connection->request)
405 httpClientFinish(connection, 2);
406 else
407 pokeFdEvent(connection->fd, -EDOGRACEFUL, POLLOUT);
408 return 1;
411 if(connection->reqlen >= bufsize) {
412 int rc = 0;
413 if(!(connection->flags & CONN_BIGREQBUF))
414 rc = httpConnectionBigifyReqbuf(connection);
415 if(rc > 0) {
416 do_stream(IO_READ, connection->fd, connection->reqlen,
417 connection->reqbuf, bigBufferSize,
418 httpClientHandler, connection);
419 return 1;
421 connection->reqlen = 0;
422 httpConnectionDestroyReqbuf(connection);
423 if(rc < 0) {
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"));
427 } else {
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"));
432 return 1;
434 httpSetTimeout(connection, clientTimeout);
435 return 0;
439 httpClientRawErrorHeaders(HTTPConnectionPtr connection,
440 int code, AtomPtr message,
441 int close, AtomPtr headers)
443 int fd = connection->fd;
444 int n;
445 char *url; int url_len;
446 char *etag;
448 assert(connection->flags & CONN_WRITER);
449 assert(code != 0);
451 if(close >= 0) {
452 if(connection->request)
453 close =
454 close || !(connection->request->flags & REQUEST_PERSISTENT);
455 else
456 close = 1;
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;
462 } else {
463 url = NULL;
464 url_len = 0;
465 etag = NULL;
468 if(connection->buf == NULL) {
469 connection->buf = get_chunk();
470 if(connection->buf == NULL) {
471 httpClientFinish(connection, 1);
472 return 1;
476 n = httpWriteErrorHeaders(connection->buf, CHUNK_SIZE, 0,
477 connection->request &&
478 connection->request->method != METHOD_HEAD,
479 code, message, close > 0, headers,
480 url, url_len, etag);
481 if(n <= 0) {
482 shutdown(connection->fd, 1);
483 if(close >= 0)
484 httpClientFinish(connection, 1);
485 return 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,
493 connection);
495 return 1;
499 httpClientRawError(HTTPConnectionPtr connection, int code, AtomPtr message,
500 int close)
502 return httpClientRawErrorHeaders(connection, code, message, close, NULL);
506 httpClientNoticeErrorHeaders(HTTPRequestPtr request, int code, AtomPtr message,
507 AtomPtr headers)
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);
517 return 1;
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);
538 return 1;
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;
549 return 1;
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);
562 return 1;
564 request->method = method;
565 if(persist)
566 request->flags |= REQUEST_PERSISTENT;
567 else
568 request->flags &= ~REQUEST_PERSISTENT;
569 request->error_code = code;
570 request->error_message = message;
572 httpQueueRequest(connection, request);
573 httpClientNoticeRequest(request, 0);
574 return 1;
578 httpErrorStreamHandler(int status,
579 FdEventHandlerPtr event,
580 StreamRequestPtr srequest)
582 HTTPConnectionPtr connection = srequest->data;
584 if(status == 0 && !streamRequestDone(srequest))
585 return 0;
587 httpClientFinish(connection, 1);
588 return 1;
592 httpErrorNocloseStreamHandler(int status,
593 FdEventHandlerPtr event,
594 StreamRequestPtr srequest)
596 HTTPConnectionPtr connection = srequest->data;
598 if(status == 0 && !streamRequestDone(srequest))
599 return 0;
601 httpClientFinish(connection, 0);
602 return 1;
606 httpErrorNofinishStreamHandler(int status,
607 FdEventHandlerPtr event,
608 StreamRequestPtr srequest)
610 if(status == 0 && !streamRequestDone(srequest))
611 return 0;
613 return 1;
617 httpClientHandlerHeaders(FdEventHandlerPtr event, StreamRequestPtr srequest,
618 HTTPConnectionPtr connection)
620 HTTPRequestPtr request;
621 int rc;
622 int method, version;
623 AtomPtr url = NULL;
624 int start;
625 int code;
626 AtomPtr message;
628 start = 0;
629 /* Work around clients working around NCSA lossage. */
630 if(connection->reqbuf[0] == '\n')
631 start = 1;
632 else if(connection->reqbuf[0] == '\r' && connection->reqbuf[1] == '\n')
633 start = 2;
635 httpSetTimeout(connection, -1);
636 rc = httpParseClientFirstLine(connection->reqbuf, start,
637 &method, &url, &version);
638 if(rc <= 0) {
639 do_log(L_ERROR, "Couldn't parse client's request line\n");
640 code = 400;
641 message = internAtom("Error in request line");
642 goto fail;
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");
651 code = 400;
652 message = internAtom("Error in first request line");
653 goto fail;
656 if(method == METHOD_UNKNOWN) {
657 code = 501;
658 message = internAtom("Method not implemented");
659 goto fail;
662 request = httpMakeRequest();
663 if(request == NULL) {
664 do_log(L_ERROR, "Couldn't allocate client request.\n");
665 code = 500;
666 message = internAtom("Couldn't allocate client request");
667 goto fail;
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);
682 fail:
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);
690 return 1;
694 static int
695 httpClientRequestDelayed(TimeEventHandlerPtr event)
697 HTTPRequestPtr request = *(HTTPRequestPtr*)event->data;
698 AtomPtr url;
699 url = internAtomN(request->object->key, request->object->key_size);
700 if(url == NULL) {
701 do_log(L_ERROR, "Couldn't allocate url.\n");
702 abortObject(request->object, 503, internAtom("Couldn't allocate url"));
703 return 1;
705 httpClientRequest(request, url);
706 return 1;
710 delayedHttpClientRequest(HTTPRequestPtr request)
712 TimeEventHandlerPtr event;
713 event = scheduleTimeEvent(-1, httpClientRequestDelayed,
714 sizeof(request), &request);
715 if(!event)
716 return -1;
717 return 1;
721 httpClientRequest(HTTPRequestPtr request, AtomPtr url)
723 HTTPConnectionPtr connection = request->connection;
724 int i, rc;
725 int body_len, body_te;
726 AtomPtr headers;
727 CacheControlRec cache_control;
728 AtomPtr via, expect, auth;
729 HTTPConditionPtr condition;
730 HTTPRangeRec range;
732 assert(!request->chandler);
733 assert(connection->reqbuf);
735 i = httpParseHeaders(1, url,
736 connection->reqbuf, connection->reqbegin, request,
737 &headers, &body_len,
738 &cache_control, &condition, &body_te,
739 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
740 &expect, &range, NULL, NULL, &via, &auth);
741 if(i < 0) {
742 releaseAtom(url);
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"));
749 return 1;
752 connection->reqbegin = i;
754 if(body_len < 0) {
755 if(request->method == METHOD_GET || request->method == METHOD_HEAD)
756 body_len = 0;
758 connection->bodylen = body_len;
759 connection->reqte = body_te;
761 if(authRealm) {
762 AtomPtr message = NULL;
763 AtomPtr challenge = NULL;
764 int code = checkClientAuth(auth, url, &message, &challenge);
765 if(auth) {
766 releaseAtom(auth);
767 auth = NULL;
769 if(expect) {
770 releaseAtom(expect);
771 expect = NULL;
773 if(code) {
774 if(expect) releaseAtom(expect);
775 request->flags |= REQUEST_FORCE_ERROR;
776 httpClientDiscardBody(connection);
777 httpClientNoticeErrorHeaders(request, code, message, challenge);
778 return 1;
782 if(auth) {
783 releaseAtom(auth);
784 auth = NULL;
787 if(expect) {
788 if(expect == atom100Continue) {
789 request->flags |= REQUEST_WAIT_CONTINUE;
791 releaseAtom(expect);
794 request->from = range.from < 0 ? 0 : range.from;
795 request->to = range.to;
796 request->cache_control = cache_control;
797 request->via = via;
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) {
807 /* For now */
808 httpClientDiscardBody(connection);
809 httpClientNoticeError(request, 500,
810 internAtom("Pipelined CONNECT "
811 "not supported"));
812 return 1;
814 if(connection->flags & CONN_BIGREQBUF) {
815 /* For now */
816 httpClientDiscardBody(connection);
817 httpClientNoticeError(request, 500,
818 internAtom("CONNECT over big buffer "
819 "not supported"));
821 connection->flags &= ~CONN_READER;
822 do_tunnel(connection->fd, connection->reqbuf,
823 connection->reqbegin, connection->reqlen, url);
824 connection->fd = -1;
825 connection->reqbuf = NULL;
826 connection->reqlen = 0;
827 connection->reqbegin = 0;
828 httpClientFinish(connection, 2);
829 return 1;
832 rc = urlForbidden(url, httpClientRequestContinue, request);
833 if(rc < 0) {
834 do_log(L_ERROR, "Couldn't schedule httpClientRequestContinue.\n");
835 httpClientDiscardBody(connection);
836 httpClientNoticeError(request, 500,
837 internAtom("Couldn't schedule "
838 "httpClientRequestContinue"));
839 return 1;
841 return 1;
845 httpClientRequestContinue(int forbidden_code, AtomPtr url,
846 AtomPtr forbidden_message, AtomPtr forbidden_headers,
847 void *closure)
849 HTTPRequestPtr request = (HTTPRequestPtr)closure;
850 HTTPConnectionPtr connection = request->connection;
851 RequestFunction requestfn;
852 ObjectPtr object = NULL;
854 if(forbidden_code < 0) {
855 releaseAtom(url);
856 httpClientDiscardBody(connection);
857 httpClientNoticeError(request, 500,
858 internAtomError(-forbidden_code,
859 "Couldn't test for forbidden "
860 "URL"));
861 return 1;
864 if(forbidden_code) {
865 releaseAtom(url);
866 httpClientDiscardBody(connection);
867 httpClientNoticeErrorHeaders(request,
868 forbidden_code, forbidden_message,
869 forbidden_headers);
870 return 1;
873 requestfn =
874 urlIsLocal(url->string, url->length) ?
875 httpLocalRequest :
876 httpServerRequest;
878 if(request->method == METHOD_POST || request->method == METHOD_PUT) {
879 do {
880 object = findObject(OBJECT_HTTP, url->string, url->length);
881 if(object) {
882 privatiseObject(object, 0);
883 releaseObject(object);
885 } while(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"));
892 return 1;
894 if(requestfn == httpLocalRequest)
895 request->object->flags |= OBJECT_LOCAL;
896 return httpClientSideRequest(request);
899 if(request->cache_control.flags & CACHE_AUTHORIZATION) {
900 do {
901 object = makeObject(OBJECT_HTTP, url->string, url->length, 0, 0,
902 requestfn, NULL);
903 if(object && object->flags != OBJECT_INITIAL) {
904 if(!(object->cache_control & CACHE_PUBLIC)) {
905 privatiseObject(object, 0);
906 releaseObject(object);
907 object = NULL;
908 } else
909 break;
911 } while(object == NULL);
912 if(object)
913 object->flags |= OBJECT_LINEAR;
914 } else {
915 object = findObject(OBJECT_HTTP, url->string, url->length);
916 if(!object)
917 object = makeObject(OBJECT_HTTP, url->string, url->length, 1, 1,
918 requestfn, NULL);
920 releaseAtom(url);
921 url = NULL;
923 if(!object) {
924 do_log(L_ERROR, "Couldn't allocate object.\n");
925 httpClientDiscardBody(connection);
926 httpClientNoticeError(request, 503,
927 internAtom("Couldn't allocate object"));
928 return 1;
931 if(object->request == httpLocalRequest) {
932 object->flags |= OBJECT_LOCAL;
933 } else {
934 if(disableProxy) {
935 httpClientDiscardBody(connection);
936 httpClientNoticeError(request, 403,
937 internAtom("Proxying disabled"));
938 releaseObject(object);
939 return 1;
942 if(!checkVia(proxyName, request->via)) {
943 httpClientDiscardBody(connection);
944 httpClientNoticeError(request, 504,
945 internAtom("Proxy loop detected"));
946 releaseObject(object);
947 return 1;
951 request->object = object;
953 httpClientDiscardBody(connection);
954 httpClientNoticeRequest(request, 0);
955 return 1;
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)
969 goto fail;
971 if(connection->bodylen < 0)
972 goto fail;
974 if(connection->bodylen + connection->reqbegin < connection->reqlen) {
975 connection->reqbegin += connection->bodylen;
976 connection->bodylen = 0;
977 } else {
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);
991 return 1;
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;
999 } else {
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.");
1012 goto fail;
1014 return 1;
1016 fail:
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;
1028 return 1;
1031 static int
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) {
1039 int bufsize;
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);
1050 } else {
1051 httpConnectionDestroyReqbuf(connection);
1052 do_stream_buf(IO_READ | IO_NOTNOW,
1053 connection->fd, 0,
1054 &connection->reqbuf, CHUNK_SIZE,
1055 httpClientHandler, connection);
1057 return 1;
1061 httpClientDiscardHandler(int status,
1062 FdEventHandlerPtr event, StreamRequestPtr request)
1064 HTTPConnectionPtr connection = request->data;
1066 assert(connection->flags & CONN_READER);
1067 if(status) {
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);
1078 return 1;
1082 httpClientNoticeRequest(HTTPRequestPtr request, int novalidate)
1084 HTTPConnectionPtr connection = request->connection;
1085 ObjectPtr object = request->object;
1086 int serveNow = (request == connection->request);
1087 int validate = 0;
1088 int conditional = 0;
1089 int local, haveData;
1090 int rc;
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)) {
1100 if(serveNow) {
1101 connection->flags |= CONN_WRITER;
1102 return httpClientRawErrorHeaders(connection,
1103 request->error_code,
1104 retainAtom(request->
1105 error_message),
1106 0, request->error_headers);
1107 } else {
1108 return 1;
1113 if(REQUEST_SIDE(request)) {
1114 assert(!(request->flags & REQUEST_REQUESTED));
1115 if(serveNow) {
1116 assert(!request->chandler);
1117 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"),
1128 return 1;
1130 connection->flags |= CONN_WRITER;
1131 rc = object->request(request->object,
1132 request->method,
1133 request->from, request->to,
1134 request,
1135 request->object->request_closure);
1137 return 1;
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) {
1145 if(!object->etag ||
1146 strcmp(object->etag, request->condition->ifrange) != 0) {
1147 request->from = 0;
1148 request->to = -1;
1152 if(object->flags & OBJECT_DYNAMIC) {
1153 request->from = 0;
1154 request->to = -1;
1157 if(request->method == METHOD_HEAD)
1158 haveData = !(request->object->flags & OBJECT_INITIAL);
1159 else
1160 haveData =
1161 (request->object->length >= 0 &&
1162 request->object->length <= request->from) ||
1163 (objectHoleSize(request->object, request->from) == 0);
1165 if(request->flags & REQUEST_REQUESTED)
1166 validate = 0;
1167 else if(novalidate || (!local && proxyOffline))
1168 validate = 0;
1169 else if(local)
1170 validate =
1171 objectMustRevalidate(request->object, &request->cache_control);
1172 else if(request->cache_control.flags & CACHE_ONLY_IF_CACHED)
1173 validate = 0;
1174 else if((request->object->flags & OBJECT_FAILED) &&
1175 !(object->flags & OBJECT_INPROGRESS) &&
1176 !relaxTransparency)
1177 validate = 1;
1178 else if(request->method != METHOD_HEAD &&
1179 !objectHasData(object, request->from, request->to) &&
1180 !(object->flags & OBJECT_INPROGRESS))
1181 validate = 1;
1182 else if(objectMustRevalidate((relaxTransparency <= 1 ?
1183 request->object : NULL),
1184 &request->cache_control))
1185 validate = 1;
1186 else
1187 validate = 0;
1189 if(request->cache_control.flags & CACHE_ONLY_IF_CACHED) {
1190 validate = 0;
1191 if(!haveData) {
1192 if(serveNow)
1193 return httpClientRawError(connection, 504,
1194 internAtom("Object not in cache"),
1196 else
1197 return 1;
1201 if(!(request->object->flags & OBJECT_VALIDATING) &&
1202 ((!validate && haveData) ||
1203 (request->object->flags & OBJECT_FAILED))) {
1204 if(serveNow) {
1205 connection->flags |= CONN_WRITER;
1206 lockChunk(request->object, request->from / CHUNK_SIZE);
1207 return httpServeObject(connection);
1208 } else {
1209 return 1;
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;
1221 if(serveNow) {
1222 connection->flags |= CONN_WRITER;
1223 if(!local && proxyOffline)
1224 return httpClientRawError(connection, 502,
1225 internAtom("Disconnected operation "
1226 "and object not in cache"),
1228 request->chandler =
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)
1240 return 1;
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 :
1249 request->method,
1250 request->from, request->to, request,
1251 request->object->request_closure);
1252 if(rc < 0) {
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");
1263 return 1;
1266 static int
1267 httpClientNoticeRequestDelayed(TimeEventHandlerPtr event)
1269 HTTPRequestPtr request = *(HTTPRequestPtr*)event->data;
1270 httpClientNoticeRequest(request, 0);
1271 return 1;
1275 delayedHttpClientNoticeRequest(HTTPRequestPtr request)
1277 TimeEventHandlerPtr event;
1278 event = scheduleTimeEvent(-1, httpClientNoticeRequestDelayed,
1279 sizeof(request), &request);
1280 if(!event)
1281 return -1;
1282 return 1;
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);
1293 return 1;
1297 delayedHttpClientContinue(HTTPConnectionPtr connection)
1299 TimeEventHandlerPtr event;
1300 event = scheduleTimeEvent(-1, httpClientContinueDelayed,
1301 sizeof(connection), &connection);
1302 if(!event)
1303 return -1;
1304 return 1;
1308 httpClientGetHandler(int status, ConditionHandlerPtr chandler)
1310 HTTPRequestPtr request = *(HTTPRequestPtr*)chandler->data;
1311 HTTPConnectionPtr connection = request->connection;
1312 ObjectPtr object = request->object;
1313 int rc;
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);
1323 if(status < 0) {
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);
1330 if(rc < 0) {
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"));
1335 return 1;
1338 if(object->flags & OBJECT_VALIDATING)
1339 return 0;
1341 if(request->error_code) {
1342 lockChunk(object, request->from / CHUNK_SIZE);
1343 request->chandler = NULL;
1344 rc = delayedHttpServeObject(connection);
1345 if(rc < 0) {
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"));
1350 return 1;
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);
1359 return 0;
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);
1379 if(rc < 0) {
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);
1387 return 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)
1396 return 0;
1397 rc = delayedHttpClientNoticeRequest(request);
1398 if(rc < 0) {
1399 do_log(L_ERROR, "Couldn't schedule noticing of request.");
1400 abortObject(object, 500,
1401 internAtom("Couldn't schedule "
1402 "noticing of request"));
1403 } else {
1404 request->chandler = NULL;
1405 return 1;
1410 if(object->flags & (OBJECT_INITIAL | OBJECT_VALIDATING)) {
1411 if(object->flags & (OBJECT_INPROGRESS | OBJECT_VALIDATING)) {
1412 return 0;
1413 } else if(object->flags & OBJECT_FAILED) {
1414 if(request->error_code)
1415 abortObject(object,
1416 request->error_code,
1417 retainAtom(request->error_message));
1418 else {
1419 abortObject(object, 500,
1420 internAtom("Error message lost in transit"));
1422 } else {
1423 /* The request was pruned by httpServerDiscardRequests */
1424 if(chandler == request->chandler) {
1425 int rc;
1426 request->chandler = NULL;
1427 rc = delayedHttpClientNoticeRequest(request);
1428 if(rc < 0)
1429 abortObject(object, 500,
1430 internAtom("Couldn't allocate "
1431 "delayed notice request"));
1432 else
1433 return 1;
1434 } else {
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) {
1444 request->from = 0;
1445 request->to = -1;
1446 } else {
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);
1454 if(rc < 0) {
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"));
1459 return 1;
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);
1471 return 1;
1473 if(connection->reqte != TE_IDENTITY) {
1474 httpClientNoticeError(request, 501,
1475 internAtom("Chunked requests not implemented"));
1476 httpClientDiscardBody(connection);
1477 return 1;
1479 if(connection->bodylen < 0) {
1480 httpClientNoticeError(request, 502,
1481 internAtom("POST or PUT without "
1482 "Content-Length"));
1483 httpClientDiscardBody(connection);
1484 return 1;
1486 if(connection->reqlen < 0) {
1487 httpClientNoticeError(request, 502,
1488 internAtom("Incomplete POST or PUT"));
1489 httpClientDiscardBody(connection);
1490 return 1;
1493 return httpClientNoticeRequest(request, 0);
1496 int
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;
1505 int push;
1506 int code;
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);
1515 goto fail;
1518 if(status < 0) {
1519 do_log_error(L_ERROR, -status, "Reading from client");
1520 code = 502;
1521 message = internAtomError(-status, "Couldn't read from client");
1522 goto fail;
1525 requestee = request->request;
1526 server = requestee->connection;
1528 push = MIN(srequest->offset - connection->reqlen,
1529 connection->bodylen - connection->reqoffset);
1530 if(push > 0) {
1531 connection->reqlen += push;
1532 httpServerDoSide(server);
1533 return 1;
1536 if(server->reqoffset >= connection->bodylen) {
1537 connection->flags &= ~(CONN_READER | CONN_SIDE_READER);
1538 return 1;
1541 assert(status);
1542 do_log(L_ERROR, "Incomplete client request.\n");
1543 code = 502;
1544 message = internAtom("Incomplete client request");
1546 fail:
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);
1562 return 1;
1565 int
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;
1572 int n, len, rc;
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);
1589 else
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);
1600 } else {
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);
1670 return 1;
1673 if(object->length >= 0 && request->to >= object->length)
1674 request->to = -1;
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;
1685 again:
1687 connection->len = 0;
1689 if((request->from <= 0 && request->to < 0) ||
1690 request->method == METHOD_HEAD) {
1691 n = snnprintf(connection->buf, 0, bufsize,
1692 "HTTP/1.1 %d %s",
1693 object->code, atomString(object->message));
1694 } else {
1695 if(request->from > request->to) {
1696 unlockChunk(object, i);
1697 return httpClientRawError(connection, 416,
1698 internAtom("Requested range "
1699 "not satisfiable"),
1701 } else {
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);
1709 if(n < 0)
1710 goto fail;
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");
1719 } else {
1720 request->flags &= ~REQUEST_PERSISTENT;
1724 if(object->age < current_time.tv_sec) {
1725 n = snnprintf(connection->buf, n, bufsize,
1726 "\r\nAge: %d",
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,
1741 " (%d %s)",
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");
1764 if(n < 0)
1765 goto fail;
1767 connection->offset = request->from;
1769 if(request->method == METHOD_HEAD ||
1770 condition_result == CONDITION_NOT_MODIFIED ||
1771 (object->flags & OBJECT_ABORTED)) {
1772 len = 0;
1773 } else {
1774 if(i < object->numchunks) {
1775 if(object->chunks[i].size <= j)
1776 len = 0;
1777 else
1778 len = object->chunks[i].size - j;
1779 } else {
1780 len = 0;
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),
1793 connection->fd, 0,
1794 connection->buf, n,
1795 object->chunks[i].data + j, len,
1796 httpServeObjectStreamHandler, connection);
1797 return 1;
1799 fail:
1800 rc = 0;
1801 connection->len = 0;
1802 if(!(connection->flags & CONN_BIGBUF))
1803 rc = httpConnectionBigify(connection);
1804 if(rc > 0) {
1805 bufsize = bigBufferSize;
1806 goto again;
1808 unlockChunk(object, i);
1809 return httpClientRawError(connection, 500,
1810 rc == 0 ?
1811 internAtom("No space for headers") :
1812 internAtom("Couldn't allocate big buffer"), 0);
1815 static int
1816 httpServeObjectDelayed(TimeEventHandlerPtr event)
1818 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1819 httpServeObject(connection);
1820 return 1;
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;
1834 return 1;
1837 static int
1838 httpServeObjectFinishHandler(int status,
1839 FdEventHandlerPtr event,
1840 StreamRequestPtr srequest)
1842 HTTPConnectionPtr connection = srequest->data;
1843 HTTPRequestPtr request = connection->request;
1845 (void)request;
1846 assert(!request->chandler);
1848 if(status == 0 && !streamRequestDone(srequest))
1849 return 0;
1851 httpSetTimeout(connection, -1);
1853 if(status < 0) {
1854 do_log(L_ERROR, "Couldn't terminate chunked reply\n");
1855 httpClientFinish(connection, 1);
1856 } else {
1857 httpClientFinish(connection, 0);
1859 return 1;
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;
1870 int rc;
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)
1880 to = request->to;
1881 else
1882 to = -1;
1884 lockChunk(object, i);
1885 len = 0;
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;
1895 if(to >= 0)
1896 len = MIN(len, to - connection->offset);
1898 if(len <= 0) {
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);
1910 } else {
1911 httpClientFinish(connection,
1912 !(object->length >= 0 &&
1913 connection->offset >= object->length));
1915 return 1;
1916 } else {
1917 if(!request->chandler) {
1918 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");
1924 goto fail;
1927 if(!(object->flags & OBJECT_INPROGRESS)) {
1928 if(object->flags & OBJECT_SUPERSEDED) {
1929 goto fail;
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;
1937 return 1;
1939 } else {
1940 /* len > 0 */
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;
1947 len2 = 0;
1948 if(j + len == CHUNK_SIZE && object->numchunks > i + 1) {
1949 len2 = object->chunks[i + 1].size;
1950 if(to >= 0)
1951 len2 = MIN(len2, to - (i + 1) * CHUNK_SIZE);
1953 /* Lock early -- httpServerRequest may get_chunk */
1954 if(len2 > 0)
1955 lockChunk(object, i + 1);
1956 if(object->length >= 0 &&
1957 connection->offset + len + len2 == object->length)
1958 end = 1;
1959 else
1960 end = 0;
1961 /* Prefetch */
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);
1975 if(len2 == 0) {
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) |
1984 (end ? IO_END : 0),
1985 connection->fd, 0,
1986 object->chunks[i].data + j, len,
1987 httpServeObjectStreamHandler, connection);
1988 } else {
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) |
1996 (end ? IO_END : 0),
1997 connection->fd, 0,
1998 object->chunks[i].data + j, len,
1999 object->chunks[i + 1].data, len2,
2000 httpServeObjectStreamHandler2, connection);
2002 return 1;
2005 abort();
2007 fail:
2008 unlockChunk(object, i);
2009 fail_no_unlock:
2010 if(request->chandler)
2011 unregisterConditionHandler(request->chandler);
2012 request->chandler = NULL;
2013 httpClientFinish(connection, 1);
2014 return 1;
2017 static int
2018 httpServeChunkDelayed(TimeEventHandlerPtr event)
2020 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
2021 httpServeChunk(connection);
2022 return 1;
2026 delayedHttpServeChunk(HTTPConnectionPtr connection)
2028 TimeEventHandlerPtr event;
2029 event = scheduleTimeEvent(-1, httpServeChunkDelayed,
2030 sizeof(connection), &connection);
2031 if(!event) return -1;
2032 return 1;
2036 httpServeObjectHandler(int status, ConditionHandlerPtr chandler)
2038 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)chandler->data;
2039 HTTPRequestPtr request = connection->request;
2040 int rc;
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);
2054 if(rc < 0) {
2055 do_log(L_ERROR, "Couldn't schedule serving.\n");
2056 abortObject(request->object, 503,
2057 internAtom("Couldn't schedule serving"));
2059 return 1;
2062 static int
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);
2076 return 0;
2079 httpSetTimeout(connection, -1);
2081 unlockChunk(request->object, i);
2082 if(kind == 2)
2083 unlockChunk(request->object, i + 1);
2085 if(status) {
2086 if(status < 0) {
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);
2091 else
2092 httpClientFinish(connection, 1);
2093 } else {
2094 do_log(D_IO, "Couldn't write to client: short write.\n");
2095 httpClientFinish(connection, 2);
2097 return 1;
2100 if(srequest->operation & IO_CHUNKED) {
2101 assert(srequest->offset > 2);
2102 connection->offset += srequest->offset - 2;
2103 } else {
2104 connection->offset += srequest->offset;
2106 request->flags &= ~REQUEST_REQUESTED;
2108 if(request->object->flags & OBJECT_ABORTED) {
2109 httpClientFinish(connection, 1);
2110 return 1;
2113 if(connection->request->method == METHOD_HEAD ||
2114 condition_result == CONDITION_NOT_MODIFIED) {
2115 httpClientFinish(connection, 0);
2116 return 1;
2119 if(srequest->operation & IO_END)
2120 httpClientFinish(connection, 0);
2121 else {
2122 httpConnectionDestroyBuf(connection);
2123 httpServeChunk(connection);
2125 return 1;
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);