Remove dead store in cachePrintSeparator().
[polipo.git] / client.c
blob86c6f20d44c2f5013f2d6f3a456d0ab1a4d3dc9b
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) ? bigBufferSize : CHUNK_SIZE;
366 assert(connection->flags & CONN_READER);
368 /* There's no point trying to do something with this request if
369 the client has shut the connection down -- HTTP doesn't do
370 half-open connections. */
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((connection->flags & CONN_BIGREQBUF) &&
416 connection->reqlen < bigBufferSize) {
417 do_stream(IO_READ, connection->fd, connection->reqlen,
418 connection->reqbuf, bigBufferSize,
419 httpClientHandler, connection);
420 return 1;
422 connection->reqlen = 0;
423 httpConnectionDestroyReqbuf(connection);
424 if(rc < 0) {
425 do_log(L_ERROR, "Couldn't allocate big buffer.\n");
426 httpClientNewError(connection, METHOD_UNKNOWN, 0, 400,
427 internAtom("Couldn't allocate big buffer"));
428 } else {
429 do_log(L_ERROR, "Couldn't find end of client's headers.\n");
430 httpClientNewError(connection, METHOD_UNKNOWN, 0, 400,
431 internAtom("Couldn't find end of headers"));
433 return 1;
435 httpSetTimeout(connection, clientTimeout);
436 return 0;
440 httpClientRawErrorHeaders(HTTPConnectionPtr connection,
441 int code, AtomPtr message,
442 int close, AtomPtr headers)
444 int fd = connection->fd;
445 int n;
446 char *url; int url_len;
447 char *etag;
449 assert(connection->flags & CONN_WRITER);
450 assert(code != 0);
452 if(close >= 0) {
453 if(connection->request)
454 close =
455 close || !(connection->request->flags & REQUEST_PERSISTENT);
456 else
457 close = 1;
459 if(connection->request && connection->request->object) {
460 url = connection->request->object->key;
461 url_len = connection->request->object->key_size;
462 etag = connection->request->object->etag;
463 } else {
464 url = NULL;
465 url_len = 0;
466 etag = NULL;
469 if(connection->buf == NULL) {
470 connection->buf = get_chunk();
471 if(connection->buf == NULL) {
472 httpClientFinish(connection, 1);
473 return 1;
477 n = httpWriteErrorHeaders(connection->buf, CHUNK_SIZE, 0,
478 connection->request &&
479 connection->request->method != METHOD_HEAD,
480 code, message, close > 0, headers,
481 url, url_len, etag);
482 if(n <= 0) {
483 shutdown(connection->fd, 1);
484 if(close >= 0)
485 httpClientFinish(connection, 1);
486 return 1;
489 httpSetTimeout(connection, clientTimeout);
490 do_stream(IO_WRITE, fd, 0, connection->buf, n,
491 close > 0 ? httpErrorStreamHandler :
492 close == 0 ? httpErrorNocloseStreamHandler :
493 httpErrorNofinishStreamHandler,
494 connection);
496 return 1;
500 httpClientRawError(HTTPConnectionPtr connection, int code, AtomPtr message,
501 int close)
503 return httpClientRawErrorHeaders(connection, code, message, close, NULL);
507 httpClientNoticeErrorHeaders(HTTPRequestPtr request, int code, AtomPtr message,
508 AtomPtr headers)
510 if(request->error_message)
511 releaseAtom(request->error_message);
512 if(request->error_headers)
513 releaseAtom(request->error_headers);
514 request->error_code = code;
515 request->error_message = message;
516 request->error_headers = headers;
517 httpClientNoticeRequest(request, 0);
518 return 1;
522 httpClientNoticeError(HTTPRequestPtr request, int code, AtomPtr message)
524 return httpClientNoticeErrorHeaders(request, code, message, NULL);
528 httpClientError(HTTPRequestPtr request, int code, AtomPtr message)
530 if(request->error_message)
531 releaseAtom(request->error_message);
532 request->error_code = code;
533 request->error_message = message;
534 if(request->chandler) {
535 abortConditionHandler(request->chandler);
536 request->chandler = NULL;
537 } else if(request->object)
538 notifyObject(request->object);
539 return 1;
542 /* This may be called from object handlers. */
544 httpClientLeanError(HTTPRequestPtr request, int code, AtomPtr message)
546 if(request->error_message)
547 releaseAtom(request->error_message);
548 request->error_code = code;
549 request->error_message = message;
550 return 1;
555 httpClientNewError(HTTPConnectionPtr connection, int method, int persist,
556 int code, AtomPtr message)
558 HTTPRequestPtr request;
559 request = httpMakeRequest();
560 if(request == NULL) {
561 do_log(L_ERROR, "Couldn't allocate error request.\n");
562 httpClientFinish(connection, 1);
563 return 1;
565 request->method = method;
566 if(persist)
567 request->flags |= REQUEST_PERSISTENT;
568 else
569 request->flags &= ~REQUEST_PERSISTENT;
570 request->error_code = code;
571 request->error_message = message;
573 httpQueueRequest(connection, request);
574 httpClientNoticeRequest(request, 0);
575 return 1;
579 httpErrorStreamHandler(int status,
580 FdEventHandlerPtr event,
581 StreamRequestPtr srequest)
583 HTTPConnectionPtr connection = srequest->data;
585 if(status == 0 && !streamRequestDone(srequest))
586 return 0;
588 httpClientFinish(connection, 1);
589 return 1;
593 httpErrorNocloseStreamHandler(int status,
594 FdEventHandlerPtr event,
595 StreamRequestPtr srequest)
597 HTTPConnectionPtr connection = srequest->data;
599 if(status == 0 && !streamRequestDone(srequest))
600 return 0;
602 httpClientFinish(connection, 0);
603 return 1;
607 httpErrorNofinishStreamHandler(int status,
608 FdEventHandlerPtr event,
609 StreamRequestPtr srequest)
611 if(status == 0 && !streamRequestDone(srequest))
612 return 0;
614 return 1;
618 httpClientHandlerHeaders(FdEventHandlerPtr event, StreamRequestPtr srequest,
619 HTTPConnectionPtr connection)
621 HTTPRequestPtr request;
622 int rc;
623 int method, version;
624 AtomPtr url = NULL;
625 int start;
626 int code;
627 AtomPtr message;
629 start = 0;
630 /* Work around clients working around NCSA lossage. */
631 if(connection->reqbuf[0] == '\n')
632 start = 1;
633 else if(connection->reqbuf[0] == '\r' && connection->reqbuf[1] == '\n')
634 start = 2;
636 httpSetTimeout(connection, -1);
637 rc = httpParseClientFirstLine(connection->reqbuf, start,
638 &method, &url, &version);
639 if(rc <= 0) {
640 do_log(L_ERROR, "Couldn't parse client's request line\n");
641 code = 400;
642 message = internAtom("Error in request line");
643 goto fail;
646 do_log(D_CLIENT_REQ, "Client request: ");
647 do_log_n(D_CLIENT_REQ, connection->reqbuf, rc - 1);
648 do_log(D_CLIENT_REQ, "\n");
650 if(version != HTTP_10 && version != HTTP_11) {
651 do_log(L_ERROR, "Unknown client HTTP version\n");
652 code = 400;
653 message = internAtom("Error in first request line");
654 goto fail;
657 if(method == METHOD_UNKNOWN) {
658 code = 501;
659 message = internAtom("Method not implemented");
660 goto fail;
663 request = httpMakeRequest();
664 if(request == NULL) {
665 do_log(L_ERROR, "Couldn't allocate client request.\n");
666 code = 500;
667 message = internAtom("Couldn't allocate client request");
668 goto fail;
671 if(connection->version != HTTP_UNKNOWN && version != connection->version) {
672 do_log(L_WARN, "Client version changed!\n");
675 connection->version = version;
676 request->flags = REQUEST_PERSISTENT;
677 request->method = method;
678 request->cache_control = no_cache_control;
679 httpQueueRequest(connection, request);
680 connection->reqbegin = rc;
681 return httpClientRequest(request, url);
683 fail:
684 if(url) releaseAtom(url);
685 shutdown(connection->fd, 0);
686 connection->reqlen = 0;
687 connection->reqbegin = 0;
688 httpConnectionDestroyReqbuf(connection);
689 connection->flags &= ~CONN_READER;
690 httpClientNewError(connection, METHOD_UNKNOWN, 0, code, message);
691 return 1;
695 static int
696 httpClientRequestDelayed(TimeEventHandlerPtr event)
698 HTTPRequestPtr request = *(HTTPRequestPtr*)event->data;
699 AtomPtr url;
700 url = internAtomN(request->object->key, request->object->key_size);
701 if(url == NULL) {
702 do_log(L_ERROR, "Couldn't allocate url.\n");
703 abortObject(request->object, 503, internAtom("Couldn't allocate url"));
704 return 1;
706 httpClientRequest(request, url);
707 return 1;
711 delayedHttpClientRequest(HTTPRequestPtr request)
713 TimeEventHandlerPtr event;
714 event = scheduleTimeEvent(-1, httpClientRequestDelayed,
715 sizeof(request), &request);
716 if(!event)
717 return -1;
718 return 1;
722 httpClientRequest(HTTPRequestPtr request, AtomPtr url)
724 HTTPConnectionPtr connection = request->connection;
725 int i, rc;
726 int body_len, body_te;
727 AtomPtr headers;
728 CacheControlRec cache_control;
729 AtomPtr via, expect, auth;
730 HTTPConditionPtr condition;
731 HTTPRangeRec range;
733 assert(!request->chandler);
734 assert(connection->reqbuf);
736 i = httpParseHeaders(1, url,
737 connection->reqbuf, connection->reqbegin, request,
738 &headers, &body_len,
739 &cache_control, &condition, &body_te,
740 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
741 &expect, &range, NULL, NULL, &via, &auth);
742 if(i < 0) {
743 releaseAtom(url);
744 do_log(L_ERROR, "Couldn't parse client headers.\n");
745 shutdown(connection->fd, 0);
746 request->flags &= ~REQUEST_PERSISTENT;
747 connection->flags &= ~CONN_READER;
748 httpClientNoticeError(request, 503,
749 internAtom("Couldn't parse client headers"));
750 return 1;
753 connection->reqbegin = i;
755 if(body_len < 0) {
756 if(request->method == METHOD_GET || request->method == METHOD_HEAD)
757 body_len = 0;
759 connection->bodylen = body_len;
760 connection->reqte = body_te;
762 if(authRealm) {
763 AtomPtr message = NULL;
764 AtomPtr challenge = NULL;
765 int code = checkClientAuth(auth, url, &message, &challenge);
766 if(auth) {
767 releaseAtom(auth);
768 auth = NULL;
770 if(expect) {
771 releaseAtom(expect);
772 expect = NULL;
774 if(code) {
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 && REQUEST_SIDE(request)) {
789 request->flags |= REQUEST_WAIT_CONTINUE;
790 } else {
791 httpClientDiscardBody(connection);
792 httpClientNoticeError(request, 417,
793 internAtom("Expectation failed"));
794 releaseAtom(expect);
795 return 1;
797 releaseAtom(expect);
800 request->from = range.from < 0 ? 0 : range.from;
801 request->to = range.to;
802 request->cache_control = cache_control;
803 request->via = via;
804 request->headers = headers;
805 request->condition = condition;
806 request->object = NULL;
808 if(connection->serviced > 500)
809 request->flags &= ~REQUEST_PERSISTENT;
811 if(request->method == METHOD_CONNECT) {
812 if(connection->flags & CONN_WRITER) {
813 /* For now */
814 httpClientDiscardBody(connection);
815 httpClientNoticeError(request, 500,
816 internAtom("Pipelined CONNECT "
817 "not supported"));
818 return 1;
820 if(connection->flags & CONN_BIGREQBUF) {
821 /* For now */
822 httpClientDiscardBody(connection);
823 httpClientNoticeError(request, 500,
824 internAtom("CONNECT over big buffer "
825 "not supported"));
826 return 1;
828 connection->flags &= ~CONN_READER;
829 do_tunnel(connection->fd, connection->reqbuf,
830 connection->reqbegin, connection->reqlen, url);
831 connection->fd = -1;
832 connection->reqbuf = NULL;
833 connection->reqlen = 0;
834 connection->reqbegin = 0;
835 httpClientFinish(connection, 2);
836 return 1;
839 rc = urlForbidden(url, httpClientRequestContinue, request);
840 if(rc < 0) {
841 do_log(L_ERROR, "Couldn't schedule httpClientRequestContinue.\n");
842 httpClientDiscardBody(connection);
843 httpClientNoticeError(request, 500,
844 internAtom("Couldn't schedule "
845 "httpClientRequestContinue"));
846 return 1;
848 return 1;
852 httpClientRequestContinue(int forbidden_code, AtomPtr url,
853 AtomPtr forbidden_message, AtomPtr forbidden_headers,
854 void *closure)
856 HTTPRequestPtr request = (HTTPRequestPtr)closure;
857 HTTPConnectionPtr connection = request->connection;
858 RequestFunction requestfn;
859 ObjectPtr object = NULL;
861 if(forbidden_code < 0) {
862 releaseAtom(url);
863 httpClientDiscardBody(connection);
864 httpClientNoticeError(request, 500,
865 internAtomError(-forbidden_code,
866 "Couldn't test for forbidden "
867 "URL"));
868 return 1;
871 if(forbidden_code) {
872 releaseAtom(url);
873 httpClientDiscardBody(connection);
874 httpClientNoticeErrorHeaders(request,
875 forbidden_code, forbidden_message,
876 forbidden_headers);
877 return 1;
880 requestfn =
881 urlIsLocal(url->string, url->length) ?
882 httpLocalRequest :
883 httpServerRequest;
885 if(request->method == METHOD_POST || request->method == METHOD_PUT) {
886 do {
887 object = findObject(OBJECT_HTTP, url->string, url->length);
888 if(object) {
889 privatiseObject(object, 0);
890 releaseObject(object);
892 } while(object);
893 request->object = makeObject(OBJECT_HTTP, url->string, url->length,
894 0, 0, requestfn, NULL);
895 if(request->object == NULL) {
896 httpClientDiscardBody(connection);
897 httpClientNoticeError(request, 503,
898 internAtom("Couldn't allocate object"));
899 return 1;
901 if(requestfn == httpLocalRequest)
902 request->object->flags |= OBJECT_LOCAL;
903 return httpClientSideRequest(request);
906 if(request->cache_control.flags & CACHE_AUTHORIZATION) {
907 do {
908 object = makeObject(OBJECT_HTTP, url->string, url->length, 0, 0,
909 requestfn, NULL);
910 if(object && object->flags != OBJECT_INITIAL) {
911 if(!(object->cache_control & CACHE_PUBLIC)) {
912 privatiseObject(object, 0);
913 releaseObject(object);
914 object = NULL;
915 } else
916 break;
918 } while(object == NULL);
919 if(object)
920 object->flags |= OBJECT_LINEAR;
921 } else {
922 object = findObject(OBJECT_HTTP, url->string, url->length);
923 if(!object)
924 object = makeObject(OBJECT_HTTP, url->string, url->length, 1, 1,
925 requestfn, NULL);
927 releaseAtom(url);
928 url = NULL;
930 if(!object) {
931 do_log(L_ERROR, "Couldn't allocate object.\n");
932 httpClientDiscardBody(connection);
933 httpClientNoticeError(request, 503,
934 internAtom("Couldn't allocate object"));
935 return 1;
938 if(object->request == httpLocalRequest) {
939 object->flags |= OBJECT_LOCAL;
940 } else {
941 if(disableProxy) {
942 httpClientDiscardBody(connection);
943 httpClientNoticeError(request, 403,
944 internAtom("Proxying disabled"));
945 releaseObject(object);
946 return 1;
949 if(!checkVia(proxyName, request->via)) {
950 httpClientDiscardBody(connection);
951 httpClientNoticeError(request, 504,
952 internAtom("Proxy loop detected"));
953 releaseObject(object);
954 return 1;
958 request->object = object;
960 httpClientDiscardBody(connection);
961 httpClientNoticeRequest(request, 0);
962 return 1;
965 static int httpClientDelayed(TimeEventHandlerPtr handler);
968 httpClientDiscardBody(HTTPConnectionPtr connection)
970 TimeEventHandlerPtr handler;
972 assert(connection->reqoffset == 0);
973 assert(connection->flags & CONN_READER);
975 if(connection->reqte != TE_IDENTITY)
976 goto fail;
978 if(connection->bodylen < 0)
979 goto fail;
981 if(connection->bodylen < connection->reqlen - connection->reqbegin) {
982 connection->reqbegin += connection->bodylen;
983 connection->bodylen = 0;
984 } else {
985 connection->bodylen -= connection->reqlen - connection->reqbegin;
986 connection->reqbegin = 0;
987 connection->reqlen = 0;
988 httpConnectionDestroyReqbuf(connection);
990 connection->reqte = TE_UNKNOWN;
992 if(connection->bodylen > 0) {
993 httpSetTimeout(connection, clientTimeout);
994 do_stream_buf(IO_READ | IO_NOTNOW,
995 connection->fd, connection->reqlen,
996 &connection->reqbuf, CHUNK_SIZE,
997 httpClientDiscardHandler, connection);
998 return 1;
1001 if(connection->reqlen > connection->reqbegin &&
1002 (connection->reqlen - connection->reqbegin) > 0) {
1003 memmove(connection->reqbuf, connection->reqbuf + connection->reqbegin,
1004 connection->reqlen - connection->reqbegin);
1005 connection->reqlen -= connection->reqbegin;
1006 connection->reqbegin = 0;
1007 } else {
1008 connection->reqlen = 0;
1009 connection->reqbegin = 0;
1012 httpSetTimeout(connection, clientTimeout);
1013 /* We need to delay in order to make sure the previous request
1014 gets queued on the server side. IO_NOTNOW isn't strong enough
1015 for that due to IO_IMMEDIATE. */
1016 handler = scheduleTimeEvent(-1, httpClientDelayed,
1017 sizeof(connection), &connection);
1018 if(handler == NULL) {
1019 do_log(L_ERROR, "Couldn't schedule reading from client.");
1020 goto fail;
1022 return 1;
1024 fail:
1025 connection->reqlen = 0;
1026 connection->reqbegin = 0;
1027 connection->bodylen = 0;
1028 connection->reqte = TE_UNKNOWN;
1029 shutdown(connection->fd, 2);
1030 handler = scheduleTimeEvent(-1, httpClientDelayed,
1031 sizeof(connection), &connection);
1032 if(handler == NULL) {
1033 do_log(L_ERROR, "Couldn't schedule reading from client.");
1034 connection->flags &= ~CONN_READER;
1036 return 1;
1039 static int
1040 httpClientDelayed(TimeEventHandlerPtr event)
1042 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1044 /* IO_NOTNOW is unfortunate, but needed to avoid starvation if a
1045 client is pipelining a lot of requests. */
1046 if(connection->reqlen > 0) {
1047 int bufsize;
1048 if((connection->flags & CONN_BIGREQBUF) &&
1049 connection->reqlen < CHUNK_SIZE)
1050 httpConnectionUnbigifyReqbuf(connection);
1051 /* Don't read new requests if buffer is big. */
1052 bufsize = (connection->flags & CONN_BIGREQBUF) ?
1053 connection->reqlen : CHUNK_SIZE;
1054 do_stream(IO_READ | IO_IMMEDIATE | IO_NOTNOW,
1055 connection->fd, connection->reqlen,
1056 connection->reqbuf, bufsize,
1057 httpClientHandler, connection);
1058 } else {
1059 httpConnectionDestroyReqbuf(connection);
1060 do_stream_buf(IO_READ | IO_NOTNOW,
1061 connection->fd, 0,
1062 &connection->reqbuf, CHUNK_SIZE,
1063 httpClientHandler, connection);
1065 return 1;
1069 httpClientDiscardHandler(int status,
1070 FdEventHandlerPtr event, StreamRequestPtr request)
1072 HTTPConnectionPtr connection = request->data;
1074 assert(connection->flags & CONN_READER);
1075 if(status) {
1076 if(status < 0 && status != -EPIPE)
1077 do_log_error(L_ERROR, -status, "Couldn't read from client");
1078 connection->bodylen = -1;
1079 return httpClientDiscardBody(connection);
1082 assert(request->offset > connection->reqlen);
1083 connection->reqlen = request->offset;
1085 httpClientDiscardBody(connection);
1086 return 1;
1090 httpClientNoticeRequest(HTTPRequestPtr request, int novalidate)
1092 HTTPConnectionPtr connection = request->connection;
1093 ObjectPtr object = request->object;
1094 int serveNow = (request == connection->request);
1095 int validate = 0;
1096 int conditional = 0;
1097 int local, haveData;
1098 int rc;
1100 assert(!request->chandler);
1102 if(request->error_code) {
1103 if((request->flags & REQUEST_FORCE_ERROR) || REQUEST_SIDE(request) ||
1104 request->object == NULL ||
1105 (request->object->flags & OBJECT_LOCAL) ||
1106 (request->object->flags & OBJECT_ABORTED) ||
1107 (relaxTransparency < 1 && !proxyOffline)) {
1108 if(serveNow) {
1109 connection->flags |= CONN_WRITER;
1110 return httpClientRawErrorHeaders(connection,
1111 request->error_code,
1112 retainAtom(request->
1113 error_message),
1114 0, request->error_headers);
1115 } else {
1116 return 1;
1121 if(REQUEST_SIDE(request)) {
1122 assert(!(request->flags & REQUEST_REQUESTED));
1123 if(serveNow) {
1124 assert(!request->chandler);
1125 request->chandler =
1126 conditionWait(&request->object->condition,
1127 httpClientGetHandler,
1128 sizeof(request), &request);
1129 if(request->chandler == NULL) {
1130 do_log(L_ERROR, "Couldn't register condition handler.\n");
1131 connection->flags |= CONN_WRITER;
1132 httpClientRawError(connection, 500,
1133 internAtom("Couldn't register "
1134 "condition handler"),
1136 return 1;
1138 connection->flags |= CONN_WRITER;
1139 rc = object->request(request->object,
1140 request->method,
1141 request->from, request->to,
1142 request,
1143 request->object->request_closure);
1145 return 1;
1148 local = urlIsLocal(object->key, object->key_size);
1149 objectFillFromDisk(object, request->from,
1150 request->method == METHOD_HEAD ? 0 : 1);
1152 /* The spec doesn't strictly forbid 206 for non-200 instances, but doing
1153 that breaks some client software. */
1154 if(object->code && object->code != 200) {
1155 request->from = 0;
1156 request->to = -1;
1159 if(request->condition && request->condition->ifrange) {
1160 if(!object->etag ||
1161 strcmp(object->etag, request->condition->ifrange) != 0) {
1162 request->from = 0;
1163 request->to = -1;
1167 if(object->flags & OBJECT_DYNAMIC) {
1168 request->from = 0;
1169 request->to = -1;
1172 if(request->method == METHOD_HEAD)
1173 haveData = !(request->object->flags & OBJECT_INITIAL);
1174 else
1175 haveData =
1176 (request->object->length >= 0 &&
1177 request->object->length <= request->from) ||
1178 (objectHoleSize(request->object, request->from) == 0);
1180 if(request->flags & REQUEST_REQUESTED)
1181 validate = 0;
1182 else if(novalidate || (!local && proxyOffline))
1183 validate = 0;
1184 else if(local)
1185 validate =
1186 objectMustRevalidate(request->object, &request->cache_control);
1187 else if(request->cache_control.flags & CACHE_ONLY_IF_CACHED)
1188 validate = 0;
1189 else if((request->object->flags & OBJECT_FAILED) &&
1190 !(object->flags & OBJECT_INPROGRESS) &&
1191 !relaxTransparency)
1192 validate = 1;
1193 else if(request->method != METHOD_HEAD &&
1194 !objectHasData(object, request->from, request->to) &&
1195 !(object->flags & OBJECT_INPROGRESS))
1196 validate = 1;
1197 else if(objectMustRevalidate((relaxTransparency <= 1 ?
1198 request->object : NULL),
1199 &request->cache_control))
1200 validate = 1;
1201 else
1202 validate = 0;
1204 if(request->cache_control.flags & CACHE_ONLY_IF_CACHED) {
1205 validate = 0;
1206 if(!haveData) {
1207 if(serveNow) {
1208 connection->flags |= CONN_WRITER;
1209 return httpClientRawError(connection, 504,
1210 internAtom("Object not in cache"),
1212 } else
1213 return 1;
1217 if(!(request->object->flags & OBJECT_VALIDATING) &&
1218 ((!validate && haveData) ||
1219 (request->object->flags & OBJECT_FAILED))) {
1220 if(serveNow) {
1221 connection->flags |= CONN_WRITER;
1222 lockChunk(request->object, request->from / CHUNK_SIZE);
1223 return httpServeObject(connection);
1224 } else {
1225 return 1;
1229 if((request->flags & REQUEST_REQUESTED) &&
1230 !(request->object->flags & OBJECT_INPROGRESS)) {
1231 /* This can happen either because the server side ran out of
1232 memory, or because it is using HEAD validation. We mark
1233 the object to be fetched again. */
1234 request->flags &= ~REQUEST_REQUESTED;
1237 if(serveNow) {
1238 connection->flags |= CONN_WRITER;
1239 if(!local && proxyOffline)
1240 return httpClientRawError(connection, 502,
1241 internAtom("Disconnected operation "
1242 "and object not in cache"),
1244 request->chandler =
1245 conditionWait(&request->object->condition, httpClientGetHandler,
1246 sizeof(request), &request);
1247 if(request->chandler == NULL) {
1248 do_log(L_ERROR, "Couldn't register condition handler.\n");
1249 return httpClientRawError(connection, 503,
1250 internAtom("Couldn't register "
1251 "condition handler"), 0);
1255 if(request->object->flags & OBJECT_VALIDATING)
1256 return 1;
1258 conditional = (haveData && request->method == METHOD_GET);
1259 if(!mindlesslyCacheVary && (request->object->cache_control & CACHE_VARY))
1260 conditional = conditional && (request->object->etag != NULL);
1262 conditional =
1263 conditional && !(request->object->cache_control & CACHE_MISMATCH);
1265 request->object->flags |= OBJECT_VALIDATING;
1266 rc = request->object->request(request->object,
1267 conditional ? METHOD_CONDITIONAL_GET :
1268 request->method,
1269 request->from, request->to, request,
1270 request->object->request_closure);
1271 if(rc < 0) {
1272 if(request->chandler)
1273 unregisterConditionHandler(request->chandler);
1274 request->chandler = NULL;
1275 request->object->flags &= ~OBJECT_VALIDATING;
1276 request->object->flags |= OBJECT_FAILED;
1277 if(request->error_message)
1278 releaseAtom(request->error_message);
1279 request->error_code = 503;
1280 request->error_message = internAtom("Couldn't schedule get");
1282 return 1;
1285 static int
1286 httpClientNoticeRequestDelayed(TimeEventHandlerPtr event)
1288 HTTPRequestPtr request = *(HTTPRequestPtr*)event->data;
1289 httpClientNoticeRequest(request, 0);
1290 return 1;
1294 delayedHttpClientNoticeRequest(HTTPRequestPtr request)
1296 TimeEventHandlerPtr event;
1297 event = scheduleTimeEvent(-1, httpClientNoticeRequestDelayed,
1298 sizeof(request), &request);
1299 if(!event)
1300 return -1;
1301 return 1;
1305 httpClientContinueDelayed(TimeEventHandlerPtr event)
1307 static char httpContinue[] = "HTTP/1.1 100 Continue\r\n\r\n";
1308 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1310 do_stream(IO_WRITE, connection->fd, 0, httpContinue, 25,
1311 httpErrorNofinishStreamHandler, connection);
1312 return 1;
1316 delayedHttpClientContinue(HTTPConnectionPtr connection)
1318 TimeEventHandlerPtr event;
1319 event = scheduleTimeEvent(-1, httpClientContinueDelayed,
1320 sizeof(connection), &connection);
1321 if(!event)
1322 return -1;
1323 return 1;
1327 httpClientGetHandler(int status, ConditionHandlerPtr chandler)
1329 HTTPRequestPtr request = *(HTTPRequestPtr*)chandler->data;
1330 HTTPConnectionPtr connection = request->connection;
1331 ObjectPtr object = request->object;
1332 int rc;
1334 assert(request == connection->request);
1336 if(request->request) {
1337 assert(request->object->flags & OBJECT_INPROGRESS);
1338 assert(!request->request->object ||
1339 request->request->object == request->object);
1342 if(status < 0) {
1343 object->flags &= ~OBJECT_VALIDATING; /* for now */
1344 if(request->request && request->request->request == request)
1345 httpServerClientReset(request->request);
1346 lockChunk(object, request->from / CHUNK_SIZE);
1347 request->chandler = NULL;
1348 rc = delayedHttpServeObject(connection);
1349 if(rc < 0) {
1350 unlockChunk(object, request->from / CHUNK_SIZE);
1351 do_log(L_ERROR, "Couldn't schedule serving.\n");
1352 abortObject(object, 503, internAtom("Couldn't schedule serving"));
1354 return 1;
1357 if(object->flags & OBJECT_VALIDATING)
1358 return 0;
1360 if(request->error_code) {
1361 lockChunk(object, request->from / CHUNK_SIZE);
1362 request->chandler = NULL;
1363 rc = delayedHttpServeObject(connection);
1364 if(rc < 0) {
1365 unlockChunk(object, request->from / CHUNK_SIZE);
1366 do_log(L_ERROR, "Couldn't schedule serving.\n");
1367 abortObject(object, 503, internAtom("Couldn't schedule serving"));
1369 return 1;
1372 if(request->flags & REQUEST_WAIT_CONTINUE) {
1373 if(request->request &&
1374 !(request->request->flags & REQUEST_WAIT_CONTINUE)) {
1375 request->flags &= ~REQUEST_WAIT_CONTINUE;
1376 delayedHttpClientContinue(connection);
1378 return 0;
1381 /* See httpServerHandlerHeaders */
1382 if((object->flags & OBJECT_SUPERSEDED) &&
1383 request->request && request->request->can_mutate) {
1384 ObjectPtr new_object = retainObject(request->request->can_mutate);
1385 if(object->requestor == request) {
1386 if(new_object->requestor == NULL)
1387 new_object->requestor = request;
1388 object->requestor = NULL;
1390 request->chandler = NULL;
1391 releaseObject(object);
1392 request->object = new_object;
1393 request->request->object = new_object;
1394 /* We're handling the wrong object now. It's simpler to
1395 rebuild the whole data structure from scratch rather than
1396 trying to compensate. */
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 /* We're probably out of memory. What can we do? */
1404 shutdown(connection->fd, 1);
1406 return 1;
1409 if(object->requestor != request && !(object->flags & OBJECT_ABORTED)) {
1410 /* Make sure we don't serve an object that is stale for us
1411 unless we're the requestor. */
1412 if((object->flags & (OBJECT_LINEAR | OBJECT_MUTATING)) ||
1413 objectMustRevalidate(object, &request->cache_control)) {
1414 if(object->flags & OBJECT_INPROGRESS)
1415 return 0;
1416 rc = delayedHttpClientNoticeRequest(request);
1417 if(rc < 0) {
1418 do_log(L_ERROR, "Couldn't schedule noticing of request.");
1419 abortObject(object, 500,
1420 internAtom("Couldn't schedule "
1421 "noticing of request"));
1422 } else {
1423 request->chandler = NULL;
1424 return 1;
1429 if(object->flags & (OBJECT_INITIAL | OBJECT_VALIDATING)) {
1430 if(object->flags & (OBJECT_INPROGRESS | OBJECT_VALIDATING)) {
1431 return 0;
1432 } else if(object->flags & OBJECT_FAILED) {
1433 if(request->error_code)
1434 abortObject(object,
1435 request->error_code,
1436 retainAtom(request->error_message));
1437 else {
1438 abortObject(object, 500,
1439 internAtom("Error message lost in transit"));
1441 } else {
1442 /* The request was pruned by httpServerDiscardRequests */
1443 if(chandler == request->chandler) {
1444 int rc;
1445 request->chandler = NULL;
1446 rc = delayedHttpClientNoticeRequest(request);
1447 if(rc < 0)
1448 abortObject(object, 500,
1449 internAtom("Couldn't allocate "
1450 "delayed notice request"));
1451 else
1452 return 1;
1453 } else {
1454 abortObject(object, 500,
1455 internAtom("Wrong request pruned -- "
1456 "this shouldn't happen"));
1461 if(request->object->flags & OBJECT_DYNAMIC) {
1462 if(objectHoleSize(request->object, 0) == 0) {
1463 request->from = 0;
1464 request->to = -1;
1465 } else {
1466 /* We really should request again if that is not the case */
1470 lockChunk(object, request->from / CHUNK_SIZE);
1471 request->chandler = NULL;
1472 rc = delayedHttpServeObject(connection);
1473 if(rc < 0) {
1474 unlockChunk(object, request->from / CHUNK_SIZE);
1475 do_log(L_ERROR, "Couldn't schedule serving.\n");
1476 abortObject(object, 503, internAtom("Couldn't schedule serving"));
1478 return 1;
1482 httpClientSideRequest(HTTPRequestPtr request)
1484 HTTPConnectionPtr connection = request->connection;
1486 if(request->from < 0 || request->to >= 0) {
1487 httpClientNoticeError(request, 501,
1488 internAtom("Partial requests not implemented"));
1489 httpClientDiscardBody(connection);
1490 return 1;
1492 if(connection->reqte != TE_IDENTITY) {
1493 httpClientNoticeError(request, 501,
1494 internAtom("Chunked requests not implemented"));
1495 httpClientDiscardBody(connection);
1496 return 1;
1498 if(connection->bodylen < 0) {
1499 httpClientNoticeError(request, 502,
1500 internAtom("POST or PUT without "
1501 "Content-Length"));
1502 httpClientDiscardBody(connection);
1503 return 1;
1505 if(connection->reqlen < 0) {
1506 httpClientNoticeError(request, 502,
1507 internAtom("Incomplete POST or PUT"));
1508 httpClientDiscardBody(connection);
1509 return 1;
1512 return httpClientNoticeRequest(request, 0);
1515 int
1516 httpClientSideHandler(int status,
1517 FdEventHandlerPtr event,
1518 StreamRequestPtr srequest)
1520 HTTPConnectionPtr connection = srequest->data;
1521 HTTPRequestPtr request = connection->request;
1522 HTTPRequestPtr requestee;
1523 HTTPConnectionPtr server;
1524 int push;
1525 int code;
1526 AtomPtr message = NULL;
1528 assert(connection->flags & CONN_SIDE_READER);
1530 if((request->object->flags & OBJECT_ABORTED) ||
1531 !(request->object->flags & OBJECT_INPROGRESS)) {
1532 code = request->object->code;
1533 message = retainAtom(request->object->message);
1534 goto fail;
1537 if(status < 0) {
1538 do_log_error(L_ERROR, -status, "Reading from client");
1539 code = 502;
1540 message = internAtomError(-status, "Couldn't read from client");
1541 goto fail;
1544 requestee = request->request;
1545 server = requestee->connection;
1547 push = MIN(srequest->offset - connection->reqlen,
1548 connection->bodylen - connection->reqoffset);
1549 if(push > 0) {
1550 connection->reqlen += push;
1551 httpServerDoSide(server);
1552 return 1;
1555 if(server->reqoffset >= connection->bodylen) {
1556 connection->flags &= ~(CONN_READER | CONN_SIDE_READER);
1557 return 1;
1560 assert(status);
1561 do_log(L_ERROR, "Incomplete client request.\n");
1562 code = 502;
1563 message = internAtom("Incomplete client request");
1565 fail:
1566 request->error_code = code;
1567 if(request->error_message)
1568 releaseAtom(request->error_message);
1569 request->error_message = message;
1570 if(request->error_headers)
1571 releaseAtom(request->error_headers);
1572 request->error_headers = NULL;
1574 if(request->request) {
1575 shutdown(request->request->connection->fd, 2);
1576 pokeFdEvent(request->request->connection->fd, -ESHUTDOWN, POLLOUT);
1578 notifyObject(request->object);
1579 connection->flags &= ~CONN_SIDE_READER;
1580 httpClientDiscardBody(connection);
1581 return 1;
1584 int
1585 httpServeObject(HTTPConnectionPtr connection)
1587 HTTPRequestPtr request = connection->request;
1588 ObjectPtr object = request->object;
1589 int i = request->from / CHUNK_SIZE;
1590 int j = request->from % CHUNK_SIZE;
1591 int n, len, rc;
1592 int bufsize = CHUNK_SIZE;
1593 int condition_result;
1595 object->atime = current_time.tv_sec;
1596 objectMetadataChanged(object, 0);
1598 httpSetTimeout(connection, -1);
1600 if((request->error_code && relaxTransparency <= 0) ||
1601 object->flags & OBJECT_INITIAL) {
1602 object->flags &= ~OBJECT_FAILED;
1603 unlockChunk(object, i);
1604 if(request->error_code)
1605 return httpClientRawError(connection,
1606 request->error_code,
1607 retainAtom(request->error_message), 0);
1608 else
1609 return httpClientRawError(connection,
1610 500, internAtom("Object vanished."), 0);
1613 if(!(object->flags & OBJECT_INPROGRESS) && object->code == 0) {
1614 if(object->flags & OBJECT_INITIAL) {
1615 unlockChunk(object, i);
1616 return httpClientRawError(connection, 503,
1617 internAtom("Error message lost"), 0);
1619 } else {
1620 unlockChunk(object, i);
1621 do_log(L_ERROR, "Internal proxy error: object has no code.\n");
1622 return httpClientRawError(connection, 500,
1623 internAtom("Internal proxy error: "
1624 "object has no code"), 0);
1628 condition_result = httpCondition(object, request->condition);
1630 if(condition_result == CONDITION_FAILED) {
1631 unlockChunk(object, i);
1632 return httpClientRawError(connection, 412,
1633 internAtom("Precondition failed"), 0);
1634 } else if(condition_result == CONDITION_NOT_MODIFIED) {
1635 unlockChunk(object, i);
1636 return httpClientRawError(connection, 304,
1637 internAtom("Not modified"), 0);
1640 objectFillFromDisk(object, request->from,
1641 (request->method == METHOD_HEAD ||
1642 condition_result != CONDITION_MATCH) ? 0 : 1);
1644 if(((object->flags & OBJECT_LINEAR) &&
1645 (object->requestor != connection->request)) ||
1646 ((object->flags & OBJECT_SUPERSEDED) &&
1647 !(object->flags & OBJECT_LINEAR))) {
1648 if(request->request) {
1649 request->request->request = NULL;
1650 request->request = NULL;
1651 request->object->requestor = NULL;
1653 object = makeObject(OBJECT_HTTP,
1654 object->key, object->key_size, 1, 0,
1655 object->request, NULL);
1656 if(request->object->requestor == request)
1657 request->object->requestor = NULL;
1658 unlockChunk(request->object, i);
1659 releaseObject(request->object);
1660 request->object = NULL;
1661 if(object == NULL) {
1662 do_log(L_ERROR, "Couldn't allocate object.");
1663 return httpClientRawError(connection, 501,
1664 internAtom("Couldn't allocate object"),
1667 if(urlIsLocal(object->key, object->key_size)) {
1668 object->flags |= OBJECT_LOCAL;
1669 object->request = httpLocalRequest;
1671 request->object = object;
1672 connection->flags &= ~CONN_WRITER;
1673 return httpClientNoticeRequest(request, 1);
1676 if(object->flags & OBJECT_ABORTED) {
1677 unlockChunk(object, i);
1678 return httpClientNoticeError(request, object->code,
1679 retainAtom(object->message));
1682 if(connection->buf == NULL)
1683 connection->buf = get_chunk();
1684 if(connection->buf == NULL) {
1685 unlockChunk(object, i);
1686 do_log(L_ERROR, "Couldn't allocate client buffer.\n");
1687 connection->flags &= ~CONN_WRITER;
1688 httpClientFinish(connection, 1);
1689 return 1;
1692 if(object->length >= 0 && request->to >= object->length)
1693 request->to = object->length;
1695 if(request->from > 0 || request->to >= 0) {
1696 if(request->method == METHOD_HEAD) {
1697 request->to = request->from;
1698 } else if(request->to < 0) {
1699 if(object->length >= 0)
1700 request->to = object->length;
1704 again:
1706 connection->len = 0;
1708 if((request->from <= 0 && request->to < 0) ||
1709 request->method == METHOD_HEAD) {
1710 n = snnprintf(connection->buf, 0, bufsize,
1711 "HTTP/1.1 %d %s",
1712 object->code, atomString(object->message));
1713 } else {
1714 if((object->length >= 0 && request->from >= object->length) ||
1715 (request->to >= 0 && request->from >= request->to)) {
1716 unlockChunk(object, i);
1717 return httpClientRawError(connection, 416,
1718 internAtom("Requested range "
1719 "not satisfiable"),
1721 } else {
1722 n = snnprintf(connection->buf, 0, bufsize,
1723 "HTTP/1.1 206 Partial content");
1727 n = httpWriteObjectHeaders(connection->buf, n, bufsize,
1728 object, request->from, request->to);
1729 if(n < 0)
1730 goto fail;
1732 if(request->method != METHOD_HEAD &&
1733 condition_result != CONDITION_NOT_MODIFIED &&
1734 request->to < 0 && object->length < 0) {
1735 if(connection->version == HTTP_11) {
1736 connection->te = TE_CHUNKED;
1737 n = snnprintf(connection->buf, n, bufsize,
1738 "\r\nTransfer-Encoding: chunked");
1739 } else {
1740 request->flags &= ~REQUEST_PERSISTENT;
1744 if(object->age < current_time.tv_sec) {
1745 n = snnprintf(connection->buf, n, bufsize,
1746 "\r\nAge: %d",
1747 (int)(current_time.tv_sec - object->age));
1749 n = snnprintf(connection->buf, n, bufsize,
1750 "\r\nConnection: %s",
1751 (request->flags & REQUEST_PERSISTENT) ?
1752 "keep-alive" : "close");
1754 if(!(object->flags & OBJECT_LOCAL)) {
1755 if((object->flags & OBJECT_FAILED) && !proxyOffline) {
1756 n = snnprintf(connection->buf, n, bufsize,
1757 "\r\nWarning: 111 %s:%d Revalidation failed",
1758 proxyName->string, proxyPort);
1759 if(request->error_code)
1760 n = snnprintf(connection->buf, n, bufsize,
1761 " (%d %s)",
1762 request->error_code,
1763 atomString(request->error_message));
1764 object->flags &= ~OBJECT_FAILED;
1765 } else if(proxyOffline &&
1766 objectMustRevalidate(object, &request->cache_control)) {
1767 n = snnprintf(connection->buf, n, bufsize,
1768 "\r\nWarning: 112 %s:%d Disconnected operation",
1769 proxyName->string, proxyPort);
1770 } else if(objectIsStale(object, &request->cache_control)) {
1771 n = snnprintf(connection->buf, n, bufsize,
1772 "\r\nWarning: 110 %s:%d Object is stale",
1773 proxyName->string, proxyPort);
1774 } else if(object->expires < 0 && object->max_age < 0 &&
1775 object->age < current_time.tv_sec - 24 * 3600) {
1776 n = snnprintf(connection->buf, n, bufsize,
1777 "\r\nWarning: 113 %s:%d Heuristic expiration",
1778 proxyName->string, proxyPort);
1782 n = snnprintf(connection->buf, n, bufsize, "\r\n\r\n");
1784 if(n < 0)
1785 goto fail;
1787 connection->offset = request->from;
1789 if(request->method == METHOD_HEAD ||
1790 condition_result == CONDITION_NOT_MODIFIED ||
1791 (object->flags & OBJECT_ABORTED)) {
1792 len = 0;
1793 } else {
1794 if(i < object->numchunks) {
1795 if(object->chunks[i].size <= j)
1796 len = 0;
1797 else
1798 len = object->chunks[i].size - j;
1799 } else {
1800 len = 0;
1802 if(request->to >= 0)
1803 len = MIN(len, request->to - request->from);
1806 connection->offset = request->from;
1807 httpSetTimeout(connection, clientTimeout);
1808 do_log(D_CLIENT_DATA, "Serving on 0x%lx for 0x%lx: offset %d len %d\n",
1809 (unsigned long)connection, (unsigned long)object,
1810 connection->offset, len);
1811 do_stream_h(IO_WRITE |
1812 (connection->te == TE_CHUNKED && len > 0 ? IO_CHUNKED : 0),
1813 connection->fd, 0,
1814 connection->buf, n,
1815 object->chunks[i].data + j, len,
1816 httpServeObjectStreamHandler, connection);
1817 return 1;
1819 fail:
1820 rc = 0;
1821 connection->len = 0;
1822 if(!(connection->flags & CONN_BIGBUF))
1823 rc = httpConnectionBigify(connection);
1824 if(rc > 0) {
1825 bufsize = bigBufferSize;
1826 goto again;
1828 unlockChunk(object, i);
1829 return httpClientRawError(connection, 500,
1830 rc == 0 ?
1831 internAtom("No space for headers") :
1832 internAtom("Couldn't allocate big buffer"), 0);
1835 static int
1836 httpServeObjectDelayed(TimeEventHandlerPtr event)
1838 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1839 httpServeObject(connection);
1840 return 1;
1844 delayedHttpServeObject(HTTPConnectionPtr connection)
1846 TimeEventHandlerPtr event;
1848 assert(connection->request->object->chunks[connection->request->from /
1849 CHUNK_SIZE].locked > 0);
1851 event = scheduleTimeEvent(-1, httpServeObjectDelayed,
1852 sizeof(connection), &connection);
1853 if(!event) return -1;
1854 return 1;
1857 static int
1858 httpServeObjectFinishHandler(int status,
1859 FdEventHandlerPtr event,
1860 StreamRequestPtr srequest)
1862 HTTPConnectionPtr connection = srequest->data;
1863 HTTPRequestPtr request = connection->request;
1865 (void)request;
1866 assert(!request->chandler);
1868 if(status == 0 && !streamRequestDone(srequest))
1869 return 0;
1871 httpSetTimeout(connection, -1);
1873 if(status < 0) {
1874 do_log(L_ERROR, "Couldn't terminate chunked reply\n");
1875 httpClientFinish(connection, 1);
1876 } else {
1877 httpClientFinish(connection, 0);
1879 return 1;
1883 httpServeChunk(HTTPConnectionPtr connection)
1885 HTTPRequestPtr request = connection->request;
1886 ObjectPtr object = request->object;
1887 int i = connection->offset / CHUNK_SIZE;
1888 int j = connection->offset - (i * CHUNK_SIZE);
1889 int to, len, len2, end;
1890 int rc;
1892 if(object->flags & OBJECT_ABORTED)
1893 goto fail_no_unlock;
1895 if(object->length >= 0 && request->to >= 0)
1896 to = MIN(request->to, object->length);
1897 else if(object->length >= 0)
1898 to = object->length;
1899 else if(request->to >= 0)
1900 to = request->to;
1901 else
1902 to = -1;
1904 lockChunk(object, i);
1905 len = 0;
1906 if(i < object->numchunks)
1907 len = object->chunks[i].size - j;
1909 if(request->method != METHOD_HEAD &&
1910 len < CHUNK_SIZE && connection->offset + len < to) {
1911 objectFillFromDisk(object, connection->offset + len, 2);
1912 len = object->chunks[i].size - j;
1915 if(to >= 0)
1916 len = MIN(len, to - connection->offset);
1918 if(len <= 0) {
1919 if(to >= 0 && connection->offset >= to) {
1920 if(request->chandler) {
1921 unregisterConditionHandler(request->chandler);
1922 request->chandler = NULL;
1924 unlockChunk(object, i);
1925 if(connection->te == TE_CHUNKED) {
1926 httpSetTimeout(connection, clientTimeout);
1927 do_stream(IO_WRITE | IO_CHUNKED | IO_END,
1928 connection->fd, 0, NULL, 0,
1929 httpServeObjectFinishHandler, connection);
1930 } else {
1931 httpClientFinish(connection,
1932 !(object->length >= 0 &&
1933 connection->offset >= object->length));
1935 return 1;
1936 } else {
1937 if(!request->chandler) {
1938 request->chandler =
1939 conditionWait(&object->condition,
1940 httpServeObjectHandler,
1941 sizeof(connection), &connection);
1942 if(!request->chandler) {
1943 do_log(L_ERROR, "Couldn't register condition handler\n");
1944 goto fail;
1947 if(!(object->flags & OBJECT_INPROGRESS)) {
1948 if(object->flags & OBJECT_SUPERSEDED) {
1949 goto fail;
1951 if(REQUEST_SIDE(request)) goto fail;
1952 rc = object->request(object, request->method,
1953 connection->offset, -1, request,
1954 object->request_closure);
1955 if(rc <= 0) goto fail;
1957 return 1;
1959 } else {
1960 /* len > 0 */
1961 if(request->method != METHOD_HEAD)
1962 objectFillFromDisk(object, (i + 1) * CHUNK_SIZE, 1);
1963 if(request->chandler) {
1964 unregisterConditionHandler(request->chandler);
1965 request->chandler = NULL;
1967 len2 = 0;
1968 if(j + len == CHUNK_SIZE && object->numchunks > i + 1) {
1969 len2 = object->chunks[i + 1].size;
1970 if(to >= 0)
1971 len2 = MIN(len2, to - (i + 1) * CHUNK_SIZE);
1973 /* Lock early -- httpServerRequest may get_chunk */
1974 if(len2 > 0)
1975 lockChunk(object, i + 1);
1976 if(object->length >= 0 &&
1977 connection->offset + len + len2 == object->length)
1978 end = 1;
1979 else
1980 end = 0;
1981 /* Prefetch */
1982 if(!(object->flags & OBJECT_INPROGRESS) && !REQUEST_SIDE(request)) {
1983 if(object->chunks[i].size < CHUNK_SIZE &&
1984 to >= 0 && connection->offset + len + 1 < to)
1985 object->request(object, request->method,
1986 connection->offset + len, -1, request,
1987 object->request_closure);
1988 else if(i + 1 < object->numchunks &&
1989 object->chunks[i + 1].size == 0 &&
1990 to >= 0 && (i + 1) * CHUNK_SIZE + 1 < to)
1991 object->request(object, request->method,
1992 (i + 1) * CHUNK_SIZE, -1, request,
1993 object->request_closure);
1995 if(len2 == 0) {
1996 httpSetTimeout(connection, clientTimeout);
1997 do_log(D_CLIENT_DATA,
1998 "Serving on 0x%lx for 0x%lx: offset %d len %d\n",
1999 (unsigned long)connection, (unsigned long)object,
2000 connection->offset, len);
2001 /* IO_NOTNOW in order to give other clients a chance to run. */
2002 do_stream(IO_WRITE | IO_NOTNOW |
2003 (connection->te == TE_CHUNKED ? IO_CHUNKED : 0) |
2004 (end ? IO_END : 0),
2005 connection->fd, 0,
2006 object->chunks[i].data + j, len,
2007 httpServeObjectStreamHandler, connection);
2008 } else {
2009 httpSetTimeout(connection, clientTimeout);
2010 do_log(D_CLIENT_DATA,
2011 "Serving on 0x%lx for 0x%lx: offset %d len %d + %d\n",
2012 (unsigned long)connection, (unsigned long)object,
2013 connection->offset, len, len2);
2014 do_stream_2(IO_WRITE | IO_NOTNOW |
2015 (connection->te == TE_CHUNKED ? IO_CHUNKED : 0) |
2016 (end ? IO_END : 0),
2017 connection->fd, 0,
2018 object->chunks[i].data + j, len,
2019 object->chunks[i + 1].data, len2,
2020 httpServeObjectStreamHandler2, connection);
2022 return 1;
2025 abort();
2027 fail:
2028 unlockChunk(object, i);
2029 fail_no_unlock:
2030 if(request->chandler)
2031 unregisterConditionHandler(request->chandler);
2032 request->chandler = NULL;
2033 httpClientFinish(connection, 1);
2034 return 1;
2037 static int
2038 httpServeChunkDelayed(TimeEventHandlerPtr event)
2040 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
2041 httpServeChunk(connection);
2042 return 1;
2046 delayedHttpServeChunk(HTTPConnectionPtr connection)
2048 TimeEventHandlerPtr event;
2049 event = scheduleTimeEvent(-1, httpServeChunkDelayed,
2050 sizeof(connection), &connection);
2051 if(!event) return -1;
2052 return 1;
2056 httpServeObjectHandler(int status, ConditionHandlerPtr chandler)
2058 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)chandler->data;
2059 HTTPRequestPtr request = connection->request;
2060 int rc;
2062 unlockChunk(request->object, connection->offset / CHUNK_SIZE);
2064 if((request->object->flags & OBJECT_ABORTED) || status < 0) {
2065 shutdown(connection->fd, 1);
2066 httpSetTimeout(connection, 10);
2067 /* httpServeChunk will take care of the error. */
2070 httpSetTimeout(connection, -1);
2072 request->chandler = NULL;
2073 rc = delayedHttpServeChunk(connection);
2074 if(rc < 0) {
2075 do_log(L_ERROR, "Couldn't schedule serving.\n");
2076 abortObject(request->object, 503,
2077 internAtom("Couldn't schedule serving"));
2079 return 1;
2082 static int
2083 httpServeObjectStreamHandlerCommon(int kind, int status,
2084 FdEventHandlerPtr event,
2085 StreamRequestPtr srequest)
2087 HTTPConnectionPtr connection = srequest->data;
2088 HTTPRequestPtr request = connection->request;
2089 int condition_result = httpCondition(request->object, request->condition);
2090 int i = connection->offset / CHUNK_SIZE;
2092 assert(!request->chandler);
2094 if(status == 0 && !streamRequestDone(srequest)) {
2095 httpSetTimeout(connection, clientTimeout);
2096 return 0;
2099 httpSetTimeout(connection, -1);
2101 unlockChunk(request->object, i);
2102 if(kind == 2)
2103 unlockChunk(request->object, i + 1);
2105 if(status) {
2106 if(status < 0) {
2107 do_log_error(status == -ECONNRESET ? D_IO : L_ERROR,
2108 -status, "Couldn't write to client");
2109 if(status == -EIO || status == -ESHUTDOWN)
2110 httpClientFinish(connection, 2);
2111 else
2112 httpClientFinish(connection, 1);
2113 } else {
2114 do_log(D_IO, "Couldn't write to client: short write.\n");
2115 httpClientFinish(connection, 2);
2117 return 1;
2120 if(srequest->operation & IO_CHUNKED) {
2121 assert(srequest->offset > 2);
2122 connection->offset += srequest->offset - 2;
2123 } else {
2124 connection->offset += srequest->offset;
2126 request->flags &= ~REQUEST_REQUESTED;
2128 if(request->object->flags & OBJECT_ABORTED) {
2129 httpClientFinish(connection, 1);
2130 return 1;
2133 if(connection->request->method == METHOD_HEAD ||
2134 condition_result == CONDITION_NOT_MODIFIED) {
2135 httpClientFinish(connection, 0);
2136 return 1;
2139 if(srequest->operation & IO_END)
2140 httpClientFinish(connection, 0);
2141 else {
2142 httpConnectionDestroyBuf(connection);
2143 httpServeChunk(connection);
2145 return 1;
2149 httpServeObjectStreamHandler(int status,
2150 FdEventHandlerPtr event,
2151 StreamRequestPtr srequest)
2153 return httpServeObjectStreamHandlerCommon(1, status, event, srequest);
2157 httpServeObjectStreamHandler2(int status,
2158 FdEventHandlerPtr event,
2159 StreamRequestPtr srequest)
2161 return httpServeObjectStreamHandlerCommon(2, status, event, srequest);