Use shorts for chunk sizes when possible.
[polipo.git] / client.c
blob358c1b7731bc8928ca773b877bfccb0d9df59c99
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 unlockChunk(object, i);
1584 if(request->error_code)
1585 return httpClientRawError(connection,
1586 request->error_code,
1587 retainAtom(request->error_message), 0);
1588 else
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);
1599 } else {
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);
1669 return 1;
1672 if(object->length >= 0 && request->to >= object->length)
1673 request->to = -1;
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;
1684 again:
1686 connection->len = 0;
1688 if((request->from <= 0 && request->to < 0) ||
1689 request->method == METHOD_HEAD) {
1690 n = snnprintf(connection->buf, 0, bufsize,
1691 "HTTP/1.1 %d %s",
1692 object->code, atomString(object->message));
1693 } else {
1694 if(request->from > request->to) {
1695 unlockChunk(object, i);
1696 return httpClientRawError(connection, 416,
1697 internAtom("Requested range "
1698 "not satisfiable"),
1700 } else {
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);
1708 if(n < 0)
1709 goto fail;
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");
1718 } else {
1719 request->flags &= ~REQUEST_PERSISTENT;
1723 if(object->age < current_time.tv_sec) {
1724 n = snnprintf(connection->buf, n, bufsize,
1725 "\r\nAge: %d",
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,
1740 " (%d %s)",
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 && object->max_age < 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");
1762 if(n < 0)
1763 goto fail;
1765 connection->offset = request->from;
1767 if(request->method == METHOD_HEAD ||
1768 condition_result == CONDITION_NOT_MODIFIED ||
1769 (object->flags & OBJECT_ABORTED)) {
1770 len = 0;
1771 } else {
1772 if(i < object->numchunks) {
1773 if(object->chunks[i].size <= j)
1774 len = 0;
1775 else
1776 len = object->chunks[i].size - j;
1777 } else {
1778 len = 0;
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),
1791 connection->fd, 0,
1792 connection->buf, n,
1793 object->chunks[i].data + j, len,
1794 httpServeObjectStreamHandler, connection);
1795 return 1;
1797 fail:
1798 rc = 0;
1799 connection->len = 0;
1800 if(!(connection->flags & CONN_BIGBUF))
1801 rc = httpConnectionBigify(connection);
1802 if(rc > 0) {
1803 bufsize = bigBufferSize;
1804 goto again;
1806 unlockChunk(object, i);
1807 return httpClientRawError(connection, 500,
1808 rc == 0 ?
1809 internAtom("No space for headers") :
1810 internAtom("Couldn't allocate big buffer"), 0);
1813 static int
1814 httpServeObjectDelayed(TimeEventHandlerPtr event)
1816 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1817 httpServeObject(connection);
1818 return 1;
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;
1832 return 1;
1835 static int
1836 httpServeObjectFinishHandler(int status,
1837 FdEventHandlerPtr event,
1838 StreamRequestPtr srequest)
1840 HTTPConnectionPtr connection = srequest->data;
1841 HTTPRequestPtr request = connection->request;
1843 (void)request;
1844 assert(!request->chandler);
1846 if(status == 0 && !streamRequestDone(srequest))
1847 return 0;
1849 httpSetTimeout(connection, -1);
1851 if(status < 0) {
1852 do_log(L_ERROR, "Couldn't terminate chunked reply\n");
1853 httpClientFinish(connection, 1);
1854 } else {
1855 httpClientFinish(connection, 0);
1857 return 1;
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;
1868 int rc;
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)
1878 to = request->to;
1879 else
1880 to = -1;
1882 lockChunk(object, i);
1883 len = 0;
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;
1893 if(to >= 0)
1894 len = MIN(len, to - connection->offset);
1896 if(len <= 0) {
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);
1908 } else {
1909 httpClientFinish(connection,
1910 !(object->length >= 0 &&
1911 connection->offset >= object->length));
1913 return 1;
1914 } else {
1915 if(!request->chandler) {
1916 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");
1922 goto fail;
1925 if(!(object->flags & OBJECT_INPROGRESS)) {
1926 if(object->flags & OBJECT_SUPERSEDED) {
1927 goto fail;
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;
1935 return 1;
1937 } else {
1938 /* len > 0 */
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;
1945 len2 = 0;
1946 if(j + len == CHUNK_SIZE && object->numchunks > i + 1) {
1947 len2 = object->chunks[i + 1].size;
1948 if(to >= 0)
1949 len2 = MIN(len2, to - (i + 1) * CHUNK_SIZE);
1951 /* Lock early -- httpServerRequest may get_chunk */
1952 if(len2 > 0)
1953 lockChunk(object, i + 1);
1954 if(object->length >= 0 &&
1955 connection->offset + len + len2 == object->length)
1956 end = 1;
1957 else
1958 end = 0;
1959 /* Prefetch */
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);
1973 if(len2 == 0) {
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) |
1982 (end ? IO_END : 0),
1983 connection->fd, 0,
1984 object->chunks[i].data + j, len,
1985 httpServeObjectStreamHandler, connection);
1986 } else {
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) |
1994 (end ? IO_END : 0),
1995 connection->fd, 0,
1996 object->chunks[i].data + j, len,
1997 object->chunks[i + 1].data, len2,
1998 httpServeObjectStreamHandler2, connection);
2000 return 1;
2003 abort();
2005 fail:
2006 unlockChunk(object, i);
2007 fail_no_unlock:
2008 if(request->chandler)
2009 unregisterConditionHandler(request->chandler);
2010 request->chandler = NULL;
2011 httpClientFinish(connection, 1);
2012 return 1;
2015 static int
2016 httpServeChunkDelayed(TimeEventHandlerPtr event)
2018 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
2019 httpServeChunk(connection);
2020 return 1;
2024 delayedHttpServeChunk(HTTPConnectionPtr connection)
2026 TimeEventHandlerPtr event;
2027 event = scheduleTimeEvent(-1, httpServeChunkDelayed,
2028 sizeof(connection), &connection);
2029 if(!event) return -1;
2030 return 1;
2034 httpServeObjectHandler(int status, ConditionHandlerPtr chandler)
2036 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)chandler->data;
2037 HTTPRequestPtr request = connection->request;
2038 int rc;
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);
2052 if(rc < 0) {
2053 do_log(L_ERROR, "Couldn't schedule serving.\n");
2054 abortObject(request->object, 503,
2055 internAtom("Couldn't schedule serving"));
2057 return 1;
2060 static int
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);
2074 return 0;
2077 httpSetTimeout(connection, -1);
2079 unlockChunk(request->object, i);
2080 if(kind == 2)
2081 unlockChunk(request->object, i + 1);
2083 if(status) {
2084 if(status < 0) {
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);
2089 else
2090 httpClientFinish(connection, 1);
2091 } else {
2092 do_log(D_IO, "Couldn't write to client: short write.\n");
2093 httpClientFinish(connection, 2);
2095 return 1;
2098 if(srequest->operation & IO_CHUNKED) {
2099 assert(srequest->offset > 2);
2100 connection->offset += srequest->offset - 2;
2101 } else {
2102 connection->offset += srequest->offset;
2104 request->flags &= ~REQUEST_REQUESTED;
2106 if(request->object->flags & OBJECT_ABORTED) {
2107 httpClientFinish(connection, 1);
2108 return 1;
2111 if(connection->request->method == METHOD_HEAD ||
2112 condition_result == CONDITION_NOT_MODIFIED) {
2113 httpClientFinish(connection, 0);
2114 return 1;
2117 if(srequest->operation & IO_END)
2118 httpClientFinish(connection, 0);
2119 else {
2120 httpConnectionDestroyBuf(connection);
2121 httpServeChunk(connection);
2123 return 1;
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);