Merge branch 'fleischer-dc17'
[polipo.git] / client.c
blob2d28191e76c712716cdc5ec13c55340e96870d10
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);
991 if(connection->bodylen > 0) {
992 httpSetTimeout(connection, clientTimeout);
993 do_stream_buf(IO_READ | IO_NOTNOW,
994 connection->fd, connection->reqlen,
995 &connection->reqbuf, CHUNK_SIZE,
996 httpClientDiscardHandler, connection);
997 return 1;
1000 if(connection->reqlen > connection->reqbegin &&
1001 (connection->reqlen - connection->reqbegin) > 0) {
1002 memmove(connection->reqbuf, connection->reqbuf + connection->reqbegin,
1003 connection->reqlen - connection->reqbegin);
1004 connection->reqlen -= connection->reqbegin;
1005 connection->reqbegin = 0;
1006 } else {
1007 connection->reqlen = 0;
1008 connection->reqbegin = 0;
1011 httpSetTimeout(connection, clientTimeout);
1012 /* We need to delay in order to make sure the previous request
1013 gets queued on the server side. IO_NOTNOW isn't strong enough
1014 for that due to IO_IMMEDIATE. */
1015 handler = scheduleTimeEvent(-1, httpClientDelayed,
1016 sizeof(connection), &connection);
1017 if(handler == NULL) {
1018 do_log(L_ERROR, "Couldn't schedule reading from client.");
1019 goto fail;
1021 return 1;
1023 fail:
1024 connection->reqlen = 0;
1025 connection->reqbegin = 0;
1026 connection->bodylen = 0;
1027 connection->reqte = TE_UNKNOWN;
1028 shutdown(connection->fd, 2);
1029 handler = scheduleTimeEvent(-1, httpClientDelayed,
1030 sizeof(connection), &connection);
1031 if(handler == NULL) {
1032 do_log(L_ERROR, "Couldn't schedule reading from client.");
1033 connection->flags &= ~CONN_READER;
1035 return 1;
1038 static int
1039 httpClientDelayed(TimeEventHandlerPtr event)
1041 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1043 /* IO_NOTNOW is unfortunate, but needed to avoid starvation if a
1044 client is pipelining a lot of requests. */
1045 if(connection->reqlen > 0) {
1046 int bufsize;
1047 if((connection->flags & CONN_BIGREQBUF) &&
1048 connection->reqlen < CHUNK_SIZE)
1049 httpConnectionUnbigifyReqbuf(connection);
1050 /* Don't read new requests if buffer is big. */
1051 bufsize = (connection->flags & CONN_BIGREQBUF) ?
1052 connection->reqlen : CHUNK_SIZE;
1053 do_stream(IO_READ | IO_IMMEDIATE | IO_NOTNOW,
1054 connection->fd, connection->reqlen,
1055 connection->reqbuf, bufsize,
1056 httpClientHandler, connection);
1057 } else {
1058 httpConnectionDestroyReqbuf(connection);
1059 do_stream_buf(IO_READ | IO_NOTNOW,
1060 connection->fd, 0,
1061 &connection->reqbuf, CHUNK_SIZE,
1062 httpClientHandler, connection);
1064 return 1;
1068 httpClientDiscardHandler(int status,
1069 FdEventHandlerPtr event, StreamRequestPtr request)
1071 HTTPConnectionPtr connection = request->data;
1073 assert(connection->flags & CONN_READER);
1074 if(status) {
1075 if(status < 0 && status != -EPIPE)
1076 do_log_error(L_ERROR, -status, "Couldn't read from client");
1077 connection->bodylen = -1;
1078 return httpClientDiscardBody(connection);
1081 assert(request->offset > connection->reqlen);
1082 connection->reqlen = request->offset;
1084 httpClientDiscardBody(connection);
1085 return 1;
1089 httpClientNoticeRequest(HTTPRequestPtr request, int novalidate)
1091 HTTPConnectionPtr connection = request->connection;
1092 ObjectPtr object = request->object;
1093 int serveNow = (request == connection->request);
1094 int validate = 0;
1095 int conditional = 0;
1096 int local, haveData;
1097 int rc;
1099 assert(!request->chandler);
1101 if(request->error_code) {
1102 if((request->flags & REQUEST_FORCE_ERROR) || REQUEST_SIDE(request) ||
1103 request->object == NULL ||
1104 (request->object->flags & OBJECT_LOCAL) ||
1105 (request->object->flags & OBJECT_ABORTED) ||
1106 (relaxTransparency < 1 && !proxyOffline)) {
1107 if(serveNow) {
1108 connection->flags |= CONN_WRITER;
1109 return httpClientRawErrorHeaders(connection,
1110 request->error_code,
1111 retainAtom(request->
1112 error_message),
1113 0, request->error_headers);
1114 } else {
1115 return 1;
1120 if(REQUEST_SIDE(request)) {
1121 assert(!(request->flags & REQUEST_REQUESTED));
1122 if(serveNow) {
1123 assert(!request->chandler);
1124 request->chandler =
1125 conditionWait(&request->object->condition,
1126 httpClientGetHandler,
1127 sizeof(request), &request);
1128 if(request->chandler == NULL) {
1129 do_log(L_ERROR, "Couldn't register condition handler.\n");
1130 connection->flags |= CONN_WRITER;
1131 httpClientRawError(connection, 500,
1132 internAtom("Couldn't register "
1133 "condition handler"),
1135 return 1;
1137 connection->flags |= CONN_WRITER;
1138 rc = object->request(request->object,
1139 request->method,
1140 request->from, request->to,
1141 request,
1142 request->object->request_closure);
1144 return 1;
1147 local = urlIsLocal(object->key, object->key_size);
1148 objectFillFromDisk(object, request->from,
1149 request->method == METHOD_HEAD ? 0 : 1);
1151 /* The spec doesn't strictly forbid 206 for non-200 instances, but doing
1152 that breaks some client software. */
1153 if(object->code && object->code != 200) {
1154 request->from = 0;
1155 request->to = -1;
1158 if(request->condition && request->condition->ifrange) {
1159 if(!object->etag ||
1160 strcmp(object->etag, request->condition->ifrange) != 0) {
1161 request->from = 0;
1162 request->to = -1;
1166 if(object->flags & OBJECT_DYNAMIC) {
1167 request->from = 0;
1168 request->to = -1;
1171 if(request->method == METHOD_HEAD ||
1172 request->object->code == 204 ||
1173 request->object->code < 200)
1174 haveData = !(request->object->flags & OBJECT_INITIAL);
1175 else
1176 haveData =
1177 (request->object->length >= 0 &&
1178 request->object->length <= request->from) ||
1179 (objectHoleSize(request->object, request->from) == 0);
1181 if(request->flags & REQUEST_REQUESTED)
1182 validate = 0;
1183 else if(novalidate || (!local && proxyOffline))
1184 validate = 0;
1185 else if(local)
1186 validate =
1187 objectMustRevalidate(request->object, &request->cache_control);
1188 else if(request->cache_control.flags & CACHE_ONLY_IF_CACHED)
1189 validate = 0;
1190 else if((request->object->flags & OBJECT_FAILED) &&
1191 !(object->flags & OBJECT_INPROGRESS) &&
1192 !relaxTransparency)
1193 validate = 1;
1194 else if(request->method != METHOD_HEAD &&
1195 !objectHasData(object, request->from, request->to) &&
1196 !(object->flags & OBJECT_INPROGRESS))
1197 validate = 1;
1198 else if(objectMustRevalidate((relaxTransparency <= 1 ?
1199 request->object : NULL),
1200 &request->cache_control))
1201 validate = 1;
1202 else
1203 validate = 0;
1205 if(request->cache_control.flags & CACHE_ONLY_IF_CACHED) {
1206 validate = 0;
1207 if(!haveData) {
1208 if(serveNow) {
1209 connection->flags |= CONN_WRITER;
1210 return httpClientRawError(connection, 504,
1211 internAtom("Object not in cache"),
1213 } else
1214 return 1;
1218 if(!(request->object->flags & OBJECT_VALIDATING) &&
1219 ((!validate && haveData) ||
1220 (request->object->flags & OBJECT_FAILED))) {
1221 if(serveNow) {
1222 connection->flags |= CONN_WRITER;
1223 lockChunk(request->object, request->from / CHUNK_SIZE);
1224 return httpServeObject(connection);
1225 } else {
1226 return 1;
1230 if((request->flags & REQUEST_REQUESTED) &&
1231 !(request->object->flags & OBJECT_INPROGRESS)) {
1232 /* This can happen either because the server side ran out of
1233 memory, or because it is using HEAD validation. We mark
1234 the object to be fetched again. */
1235 request->flags &= ~REQUEST_REQUESTED;
1238 if(serveNow) {
1239 connection->flags |= CONN_WRITER;
1240 if(!local && proxyOffline)
1241 return httpClientRawError(connection, 502,
1242 internAtom("Disconnected operation "
1243 "and object not in cache"),
1245 request->chandler =
1246 conditionWait(&request->object->condition, httpClientGetHandler,
1247 sizeof(request), &request);
1248 if(request->chandler == NULL) {
1249 do_log(L_ERROR, "Couldn't register condition handler.\n");
1250 return httpClientRawError(connection, 503,
1251 internAtom("Couldn't register "
1252 "condition handler"), 0);
1256 if(request->object->flags & OBJECT_VALIDATING)
1257 return 1;
1259 conditional = (haveData && request->method == METHOD_GET);
1260 if(!mindlesslyCacheVary && (request->object->cache_control & CACHE_VARY))
1261 conditional = conditional && (request->object->etag != NULL);
1263 conditional =
1264 conditional && !(request->object->cache_control & CACHE_MISMATCH);
1266 if(!(request->object->flags & OBJECT_INPROGRESS))
1267 request->object->flags |= OBJECT_VALIDATING;
1268 rc = request->object->request(request->object,
1269 conditional ? METHOD_CONDITIONAL_GET :
1270 request->method,
1271 request->from, request->to, request,
1272 request->object->request_closure);
1273 if(rc < 0) {
1274 if(request->chandler)
1275 unregisterConditionHandler(request->chandler);
1276 request->chandler = NULL;
1277 request->object->flags &= ~OBJECT_VALIDATING;
1278 request->object->flags |= OBJECT_FAILED;
1279 if(request->error_message)
1280 releaseAtom(request->error_message);
1281 request->error_code = 503;
1282 request->error_message = internAtom("Couldn't schedule get");
1284 return 1;
1287 static int
1288 httpClientNoticeRequestDelayed(TimeEventHandlerPtr event)
1290 HTTPRequestPtr request = *(HTTPRequestPtr*)event->data;
1291 httpClientNoticeRequest(request, 0);
1292 return 1;
1296 delayedHttpClientNoticeRequest(HTTPRequestPtr request)
1298 TimeEventHandlerPtr event;
1299 event = scheduleTimeEvent(-1, httpClientNoticeRequestDelayed,
1300 sizeof(request), &request);
1301 if(!event)
1302 return -1;
1303 return 1;
1307 httpClientContinueDelayed(TimeEventHandlerPtr event)
1309 static char httpContinue[] = "HTTP/1.1 100 Continue\r\n\r\n";
1310 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1312 do_stream(IO_WRITE, connection->fd, 0, httpContinue, 25,
1313 httpErrorNofinishStreamHandler, connection);
1314 return 1;
1318 delayedHttpClientContinue(HTTPConnectionPtr connection)
1320 TimeEventHandlerPtr event;
1321 event = scheduleTimeEvent(-1, httpClientContinueDelayed,
1322 sizeof(connection), &connection);
1323 if(!event)
1324 return -1;
1325 return 1;
1329 httpClientGetHandler(int status, ConditionHandlerPtr chandler)
1331 HTTPRequestPtr request = *(HTTPRequestPtr*)chandler->data;
1332 HTTPConnectionPtr connection = request->connection;
1333 ObjectPtr object = request->object;
1334 int rc;
1336 assert(request == connection->request);
1338 if(request->request) {
1339 assert(request->object->flags & OBJECT_INPROGRESS);
1340 assert(!request->request->object ||
1341 request->request->object == request->object);
1344 if(status < 0) {
1345 object->flags &= ~OBJECT_VALIDATING; /* for now */
1346 if(request->request && request->request->request == request)
1347 httpServerClientReset(request->request);
1348 lockChunk(object, request->from / CHUNK_SIZE);
1349 request->chandler = NULL;
1350 rc = delayedHttpServeObject(connection);
1351 if(rc < 0) {
1352 unlockChunk(object, request->from / CHUNK_SIZE);
1353 do_log(L_ERROR, "Couldn't schedule serving.\n");
1354 abortObject(object, 503, internAtom("Couldn't schedule serving"));
1356 return 1;
1359 if(object->flags & OBJECT_VALIDATING)
1360 return 0;
1362 if(request->error_code) {
1363 lockChunk(object, request->from / CHUNK_SIZE);
1364 request->chandler = NULL;
1365 rc = delayedHttpServeObject(connection);
1366 if(rc < 0) {
1367 unlockChunk(object, request->from / CHUNK_SIZE);
1368 do_log(L_ERROR, "Couldn't schedule serving.\n");
1369 abortObject(object, 503, internAtom("Couldn't schedule serving"));
1371 return 1;
1374 if(request->flags & REQUEST_WAIT_CONTINUE) {
1375 request->flags &= ~REQUEST_WAIT_CONTINUE;
1376 if(object->code == 100 && request->request &&
1377 !(request->request->flags & REQUEST_WAIT_CONTINUE))
1378 delayedHttpClientContinue(connection);
1379 return 0;
1382 /* See httpServerHandlerHeaders */
1383 if((object->flags & OBJECT_SUPERSEDED) &&
1384 request->request && request->request->can_mutate) {
1385 ObjectPtr new_object = retainObject(request->request->can_mutate);
1386 if(object->requestor == request) {
1387 if(new_object->requestor == NULL)
1388 new_object->requestor = request;
1389 object->requestor = NULL;
1391 request->chandler = NULL;
1392 releaseObject(object);
1393 request->object = new_object;
1394 request->request->object = new_object;
1395 /* We're handling the wrong object now. It's simpler to
1396 rebuild the whole data structure from scratch rather than
1397 trying to compensate. */
1398 rc = delayedHttpClientNoticeRequest(request);
1399 if(rc < 0) {
1400 do_log(L_ERROR, "Couldn't schedule noticing of request.");
1401 abortObject(object, 500,
1402 internAtom("Couldn't schedule "
1403 "noticing of request"));
1404 /* We're probably out of memory. What can we do? */
1405 shutdown(connection->fd, 1);
1407 return 1;
1410 if(object->requestor != request && !(object->flags & OBJECT_ABORTED)) {
1411 /* Make sure we don't serve an object that is stale for us
1412 unless we're the requestor. */
1413 if((object->flags & (OBJECT_LINEAR | OBJECT_MUTATING)) ||
1414 objectMustRevalidate(object, &request->cache_control)) {
1415 if(object->flags & OBJECT_INPROGRESS)
1416 return 0;
1417 rc = delayedHttpClientNoticeRequest(request);
1418 if(rc < 0) {
1419 do_log(L_ERROR, "Couldn't schedule noticing of request.");
1420 abortObject(object, 500,
1421 internAtom("Couldn't schedule "
1422 "noticing of request"));
1423 } else {
1424 request->chandler = NULL;
1425 return 1;
1430 if(object->flags & (OBJECT_INITIAL | OBJECT_VALIDATING)) {
1431 if(object->flags & (OBJECT_INPROGRESS | OBJECT_VALIDATING)) {
1432 return 0;
1433 } else if(object->flags & OBJECT_FAILED) {
1434 if(request->error_code)
1435 abortObject(object,
1436 request->error_code,
1437 retainAtom(request->error_message));
1438 else {
1439 abortObject(object, 500,
1440 internAtom("Error message lost in transit"));
1442 } else {
1443 /* The request was pruned by httpServerDiscardRequests */
1444 if(chandler == request->chandler) {
1445 int rc;
1446 request->chandler = NULL;
1447 rc = delayedHttpClientNoticeRequest(request);
1448 if(rc < 0)
1449 abortObject(object, 500,
1450 internAtom("Couldn't allocate "
1451 "delayed notice request"));
1452 else
1453 return 1;
1454 } else {
1455 abortObject(object, 500,
1456 internAtom("Wrong request pruned -- "
1457 "this shouldn't happen"));
1462 if(request->object->flags & OBJECT_DYNAMIC) {
1463 if(objectHoleSize(request->object, 0) == 0) {
1464 request->from = 0;
1465 request->to = -1;
1466 } else {
1467 /* We really should request again if that is not the case */
1471 lockChunk(object, request->from / CHUNK_SIZE);
1472 request->chandler = NULL;
1473 rc = delayedHttpServeObject(connection);
1474 if(rc < 0) {
1475 unlockChunk(object, request->from / CHUNK_SIZE);
1476 do_log(L_ERROR, "Couldn't schedule serving.\n");
1477 abortObject(object, 503, internAtom("Couldn't schedule serving"));
1479 return 1;
1483 httpClientSideRequest(HTTPRequestPtr request)
1485 HTTPConnectionPtr connection = request->connection;
1487 if(request->from < 0 || request->to >= 0) {
1488 httpClientNoticeError(request, 501,
1489 internAtom("Partial requests not implemented"));
1490 httpClientDiscardBody(connection);
1491 return 1;
1493 if(connection->reqte != TE_IDENTITY) {
1494 httpClientNoticeError(request, 501,
1495 internAtom("Chunked requests not implemented"));
1496 httpClientDiscardBody(connection);
1497 return 1;
1499 if(connection->bodylen < 0) {
1500 httpClientNoticeError(request, 502,
1501 internAtom("POST or PUT without "
1502 "Content-Length"));
1503 httpClientDiscardBody(connection);
1504 return 1;
1506 if(connection->reqlen < 0) {
1507 httpClientNoticeError(request, 502,
1508 internAtom("Incomplete POST or PUT"));
1509 httpClientDiscardBody(connection);
1510 return 1;
1513 return httpClientNoticeRequest(request, 0);
1516 int
1517 httpClientSideHandler(int status,
1518 FdEventHandlerPtr event,
1519 StreamRequestPtr srequest)
1521 HTTPConnectionPtr connection = srequest->data;
1522 HTTPRequestPtr request = connection->request;
1523 HTTPRequestPtr requestee;
1524 HTTPConnectionPtr server;
1525 int push;
1526 int code;
1527 AtomPtr message = NULL;
1529 assert(connection->flags & CONN_SIDE_READER);
1531 if((request->object->flags & OBJECT_ABORTED) ||
1532 !(request->object->flags & OBJECT_INPROGRESS)) {
1533 code = request->object->code;
1534 message = retainAtom(request->object->message);
1535 goto fail;
1538 if(status < 0) {
1539 do_log_error(L_ERROR, -status, "Reading from client");
1540 code = 502;
1541 message = internAtomError(-status, "Couldn't read from client");
1542 goto fail;
1545 requestee = request->request;
1546 server = requestee->connection;
1548 push = MIN(srequest->offset - connection->reqlen,
1549 connection->bodylen - connection->reqoffset);
1550 if(push > 0) {
1551 connection->reqlen += push;
1552 httpServerDoSide(server);
1553 return 1;
1556 if(server->reqoffset >= connection->bodylen) {
1557 connection->flags &= ~(CONN_READER | CONN_SIDE_READER);
1558 return 1;
1561 assert(status);
1562 do_log(L_ERROR, "Incomplete client request.\n");
1563 code = 502;
1564 message = internAtom("Incomplete client request");
1566 fail:
1567 request->error_code = code;
1568 if(request->error_message)
1569 releaseAtom(request->error_message);
1570 request->error_message = message;
1571 if(request->error_headers)
1572 releaseAtom(request->error_headers);
1573 request->error_headers = NULL;
1575 if(request->request) {
1576 shutdown(request->request->connection->fd, 2);
1577 pokeFdEvent(request->request->connection->fd, -ESHUTDOWN, POLLOUT);
1579 notifyObject(request->object);
1580 connection->flags &= ~CONN_SIDE_READER;
1581 httpClientDiscardBody(connection);
1582 return 1;
1585 int
1586 httpServeObject(HTTPConnectionPtr connection)
1588 HTTPRequestPtr request = connection->request;
1589 ObjectPtr object = request->object;
1590 int i = request->from / CHUNK_SIZE;
1591 int j = request->from % CHUNK_SIZE;
1592 int n, len, rc;
1593 int bufsize = CHUNK_SIZE;
1594 int condition_result;
1596 object->atime = current_time.tv_sec;
1597 objectMetadataChanged(object, 0);
1599 httpSetTimeout(connection, -1);
1601 if((request->error_code && relaxTransparency <= 0) ||
1602 object->flags & OBJECT_INITIAL) {
1603 object->flags &= ~OBJECT_FAILED;
1604 unlockChunk(object, i);
1605 if(request->error_code)
1606 return httpClientRawError(connection,
1607 request->error_code,
1608 retainAtom(request->error_message), 0);
1609 else
1610 return httpClientRawError(connection,
1611 500, internAtom("Object vanished."), 0);
1614 if(!(object->flags & OBJECT_INPROGRESS) && object->code == 0) {
1615 if(object->flags & OBJECT_INITIAL) {
1616 unlockChunk(object, i);
1617 return httpClientRawError(connection, 503,
1618 internAtom("Error message lost"), 0);
1620 } else {
1621 unlockChunk(object, i);
1622 do_log(L_ERROR, "Internal proxy error: object has no code.\n");
1623 return httpClientRawError(connection, 500,
1624 internAtom("Internal proxy error: "
1625 "object has no code"), 0);
1629 condition_result = httpCondition(object, request->condition);
1631 if(condition_result == CONDITION_FAILED) {
1632 unlockChunk(object, i);
1633 return httpClientRawError(connection, 412,
1634 internAtom("Precondition failed"), 0);
1635 } else if(condition_result == CONDITION_NOT_MODIFIED) {
1636 unlockChunk(object, i);
1637 return httpClientRawError(connection, 304,
1638 internAtom("Not modified"), 0);
1641 objectFillFromDisk(object, request->from,
1642 (request->method == METHOD_HEAD ||
1643 condition_result != CONDITION_MATCH) ? 0 : 1);
1645 if(((object->flags & OBJECT_LINEAR) &&
1646 (object->requestor != connection->request)) ||
1647 ((object->flags & OBJECT_SUPERSEDED) &&
1648 !(object->flags & OBJECT_LINEAR))) {
1649 if(request->request) {
1650 request->request->request = NULL;
1651 request->request = NULL;
1652 request->object->requestor = NULL;
1654 object = makeObject(OBJECT_HTTP,
1655 object->key, object->key_size, 1, 0,
1656 object->request, NULL);
1657 if(request->object->requestor == request)
1658 request->object->requestor = NULL;
1659 unlockChunk(request->object, i);
1660 releaseObject(request->object);
1661 request->object = NULL;
1662 if(object == NULL) {
1663 do_log(L_ERROR, "Couldn't allocate object.");
1664 return httpClientRawError(connection, 501,
1665 internAtom("Couldn't allocate object"),
1668 if(urlIsLocal(object->key, object->key_size)) {
1669 object->flags |= OBJECT_LOCAL;
1670 object->request = httpLocalRequest;
1672 request->object = object;
1673 connection->flags &= ~CONN_WRITER;
1674 return httpClientNoticeRequest(request, 1);
1677 if(object->flags & OBJECT_ABORTED) {
1678 unlockChunk(object, i);
1679 return httpClientNoticeError(request, object->code,
1680 retainAtom(object->message));
1683 if(connection->buf == NULL)
1684 connection->buf = get_chunk();
1685 if(connection->buf == NULL) {
1686 unlockChunk(object, i);
1687 do_log(L_ERROR, "Couldn't allocate client buffer.\n");
1688 connection->flags &= ~CONN_WRITER;
1689 httpClientFinish(connection, 1);
1690 return 1;
1693 if(object->length >= 0 && request->to >= object->length)
1694 request->to = object->length;
1696 if(request->from > 0 || request->to >= 0) {
1697 if(request->method == METHOD_HEAD) {
1698 request->to = request->from;
1699 } else if(request->to < 0) {
1700 if(object->length >= 0)
1701 request->to = object->length;
1705 again:
1707 connection->len = 0;
1709 if((request->from <= 0 && request->to < 0) ||
1710 request->method == METHOD_HEAD) {
1711 n = snnprintf(connection->buf, 0, bufsize,
1712 "HTTP/1.1 %d %s",
1713 object->code, atomString(object->message));
1714 } else {
1715 if((object->length >= 0 && request->from >= object->length) ||
1716 (request->to >= 0 && request->from >= request->to)) {
1717 unlockChunk(object, i);
1718 return httpClientRawError(connection, 416,
1719 internAtom("Requested range "
1720 "not satisfiable"),
1722 } else {
1723 n = snnprintf(connection->buf, 0, bufsize,
1724 "HTTP/1.1 206 Partial content");
1728 n = httpWriteObjectHeaders(connection->buf, n, bufsize,
1729 object, request->from, request->to);
1730 if(n < 0)
1731 goto fail;
1733 if(request->method != METHOD_HEAD &&
1734 condition_result != CONDITION_NOT_MODIFIED &&
1735 request->to < 0 && object->length < 0) {
1736 if(connection->version == HTTP_11) {
1737 connection->te = TE_CHUNKED;
1738 n = snnprintf(connection->buf, n, bufsize,
1739 "\r\nTransfer-Encoding: chunked");
1740 } else {
1741 request->flags &= ~REQUEST_PERSISTENT;
1745 if(object->age < current_time.tv_sec) {
1746 n = snnprintf(connection->buf, n, bufsize,
1747 "\r\nAge: %d",
1748 (int)(current_time.tv_sec - object->age));
1750 n = snnprintf(connection->buf, n, bufsize,
1751 "\r\nConnection: %s",
1752 (request->flags & REQUEST_PERSISTENT) ?
1753 "keep-alive" : "close");
1755 if(!(object->flags & OBJECT_LOCAL)) {
1756 if((object->flags & OBJECT_FAILED) && !proxyOffline) {
1757 n = snnprintf(connection->buf, n, bufsize,
1758 "\r\nWarning: 111 %s Revalidation failed",
1759 proxyName->string);
1760 if(request->error_code)
1761 n = snnprintf(connection->buf, n, bufsize,
1762 " (%d %s)",
1763 request->error_code,
1764 atomString(request->error_message));
1765 object->flags &= ~OBJECT_FAILED;
1766 } else if(proxyOffline &&
1767 objectMustRevalidate(object, &request->cache_control)) {
1768 n = snnprintf(connection->buf, n, bufsize,
1769 "\r\nWarning: 112 %s Disconnected operation",
1770 proxyName->string);
1771 } else if(objectIsStale(object, &request->cache_control)) {
1772 n = snnprintf(connection->buf, n, bufsize,
1773 "\r\nWarning: 110 %s Object is stale",
1774 proxyName->string);
1775 } else if(object->expires < 0 && object->max_age < 0 &&
1776 object->age < current_time.tv_sec - 24 * 3600) {
1777 n = snnprintf(connection->buf, n, bufsize,
1778 "\r\nWarning: 113 %s Heuristic expiration",
1779 proxyName->string);
1783 n = snnprintf(connection->buf, n, bufsize, "\r\n\r\n");
1785 if(n < 0)
1786 goto fail;
1788 connection->offset = request->from;
1790 if(request->method == METHOD_HEAD ||
1791 condition_result == CONDITION_NOT_MODIFIED ||
1792 (object->flags & OBJECT_ABORTED)) {
1793 len = 0;
1794 } else {
1795 if(i < object->numchunks) {
1796 if(object->chunks[i].size <= j)
1797 len = 0;
1798 else
1799 len = object->chunks[i].size - j;
1800 } else {
1801 len = 0;
1803 if(request->to >= 0)
1804 len = MIN(len, request->to - request->from);
1807 connection->offset = request->from;
1808 httpSetTimeout(connection, clientTimeout);
1809 do_log(D_CLIENT_DATA, "Serving on 0x%lx for 0x%lx: offset %d len %d\n",
1810 (unsigned long)connection, (unsigned long)object,
1811 connection->offset, len);
1812 do_stream_h(IO_WRITE |
1813 (connection->te == TE_CHUNKED && len > 0 ? IO_CHUNKED : 0),
1814 connection->fd, 0,
1815 connection->buf, n,
1816 object->chunks[i].data + j, len,
1817 httpServeObjectStreamHandler, connection);
1818 return 1;
1820 fail:
1821 rc = 0;
1822 connection->len = 0;
1823 if(!(connection->flags & CONN_BIGBUF))
1824 rc = httpConnectionBigify(connection);
1825 if(rc > 0) {
1826 bufsize = bigBufferSize;
1827 goto again;
1829 unlockChunk(object, i);
1830 return httpClientRawError(connection, 500,
1831 rc == 0 ?
1832 internAtom("No space for headers") :
1833 internAtom("Couldn't allocate big buffer"), 0);
1836 static int
1837 httpServeObjectDelayed(TimeEventHandlerPtr event)
1839 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
1840 httpServeObject(connection);
1841 return 1;
1845 delayedHttpServeObject(HTTPConnectionPtr connection)
1847 TimeEventHandlerPtr event;
1849 assert(connection->request->object->chunks[connection->request->from /
1850 CHUNK_SIZE].locked > 0);
1852 event = scheduleTimeEvent(-1, httpServeObjectDelayed,
1853 sizeof(connection), &connection);
1854 if(!event) return -1;
1855 return 1;
1858 static int
1859 httpServeObjectFinishHandler(int status,
1860 FdEventHandlerPtr event,
1861 StreamRequestPtr srequest)
1863 HTTPConnectionPtr connection = srequest->data;
1864 HTTPRequestPtr request = connection->request;
1866 (void)request;
1867 assert(!request->chandler);
1869 if(status == 0 && !streamRequestDone(srequest))
1870 return 0;
1872 httpSetTimeout(connection, -1);
1874 if(status < 0) {
1875 do_log(L_ERROR, "Couldn't terminate chunked reply\n");
1876 httpClientFinish(connection, 1);
1877 } else {
1878 httpClientFinish(connection, 0);
1880 return 1;
1884 httpServeChunk(HTTPConnectionPtr connection)
1886 HTTPRequestPtr request = connection->request;
1887 ObjectPtr object = request->object;
1888 int i = connection->offset / CHUNK_SIZE;
1889 int j = connection->offset - (i * CHUNK_SIZE);
1890 int to, len, len2, end;
1891 int rc;
1893 if(object->flags & OBJECT_ABORTED)
1894 goto fail_no_unlock;
1896 if(object->length >= 0 && request->to >= 0)
1897 to = MIN(request->to, object->length);
1898 else if(object->length >= 0)
1899 to = object->length;
1900 else if(request->to >= 0)
1901 to = request->to;
1902 else
1903 to = -1;
1905 lockChunk(object, i);
1906 len = 0;
1907 if(i < object->numchunks)
1908 len = object->chunks[i].size - j;
1910 if(request->method != METHOD_HEAD &&
1911 len < CHUNK_SIZE && connection->offset + len < to) {
1912 objectFillFromDisk(object, connection->offset + len, 2);
1913 len = object->chunks[i].size - j;
1916 if(to >= 0)
1917 len = MIN(len, to - connection->offset);
1919 if(len <= 0) {
1920 if(to >= 0 && connection->offset >= to) {
1921 if(request->chandler) {
1922 unregisterConditionHandler(request->chandler);
1923 request->chandler = NULL;
1925 unlockChunk(object, i);
1926 if(connection->te == TE_CHUNKED) {
1927 httpSetTimeout(connection, clientTimeout);
1928 do_stream(IO_WRITE | IO_CHUNKED | IO_END,
1929 connection->fd, 0, NULL, 0,
1930 httpServeObjectFinishHandler, connection);
1931 } else {
1932 httpClientFinish(connection,
1933 !(object->length >= 0 &&
1934 connection->offset >= object->length));
1936 return 1;
1937 } else {
1938 if(!request->chandler) {
1939 request->chandler =
1940 conditionWait(&object->condition,
1941 httpServeObjectHandler,
1942 sizeof(connection), &connection);
1943 if(!request->chandler) {
1944 do_log(L_ERROR, "Couldn't register condition handler\n");
1945 goto fail;
1948 if(!(object->flags & OBJECT_INPROGRESS)) {
1949 if(object->flags & OBJECT_SUPERSEDED) {
1950 goto fail;
1952 if(REQUEST_SIDE(request)) goto fail;
1953 rc = object->request(object, request->method,
1954 connection->offset, -1, request,
1955 object->request_closure);
1956 if(rc <= 0) goto fail;
1958 return 1;
1960 } else {
1961 /* len > 0 */
1962 if(request->method != METHOD_HEAD)
1963 objectFillFromDisk(object, (i + 1) * CHUNK_SIZE, 1);
1964 if(request->chandler) {
1965 unregisterConditionHandler(request->chandler);
1966 request->chandler = NULL;
1968 len2 = 0;
1969 if(j + len == CHUNK_SIZE && object->numchunks > i + 1) {
1970 len2 = object->chunks[i + 1].size;
1971 if(to >= 0)
1972 len2 = MIN(len2, to - (i + 1) * CHUNK_SIZE);
1974 /* Lock early -- httpServerRequest may get_chunk */
1975 if(len2 > 0)
1976 lockChunk(object, i + 1);
1977 if(object->length >= 0 &&
1978 connection->offset + len + len2 == object->length)
1979 end = 1;
1980 else
1981 end = 0;
1982 /* Prefetch */
1983 if(!(object->flags & OBJECT_INPROGRESS) && !REQUEST_SIDE(request)) {
1984 if(object->chunks[i].size < CHUNK_SIZE &&
1985 to >= 0 && connection->offset + len + 1 < to)
1986 object->request(object, request->method,
1987 connection->offset + len, -1, request,
1988 object->request_closure);
1989 else if(i + 1 < object->numchunks &&
1990 object->chunks[i + 1].size == 0 &&
1991 to >= 0 && (i + 1) * CHUNK_SIZE + 1 < to)
1992 object->request(object, request->method,
1993 (i + 1) * CHUNK_SIZE, -1, request,
1994 object->request_closure);
1996 if(len2 == 0) {
1997 httpSetTimeout(connection, clientTimeout);
1998 do_log(D_CLIENT_DATA,
1999 "Serving on 0x%lx for 0x%lx: offset %d len %d\n",
2000 (unsigned long)connection, (unsigned long)object,
2001 connection->offset, len);
2002 /* IO_NOTNOW in order to give other clients a chance to run. */
2003 do_stream(IO_WRITE | IO_NOTNOW |
2004 (connection->te == TE_CHUNKED ? IO_CHUNKED : 0) |
2005 (end ? IO_END : 0),
2006 connection->fd, 0,
2007 object->chunks[i].data + j, len,
2008 httpServeObjectStreamHandler, connection);
2009 } else {
2010 httpSetTimeout(connection, clientTimeout);
2011 do_log(D_CLIENT_DATA,
2012 "Serving on 0x%lx for 0x%lx: offset %d len %d + %d\n",
2013 (unsigned long)connection, (unsigned long)object,
2014 connection->offset, len, len2);
2015 do_stream_2(IO_WRITE | IO_NOTNOW |
2016 (connection->te == TE_CHUNKED ? IO_CHUNKED : 0) |
2017 (end ? IO_END : 0),
2018 connection->fd, 0,
2019 object->chunks[i].data + j, len,
2020 object->chunks[i + 1].data, len2,
2021 httpServeObjectStreamHandler2, connection);
2023 return 1;
2026 abort();
2028 fail:
2029 unlockChunk(object, i);
2030 fail_no_unlock:
2031 if(request->chandler)
2032 unregisterConditionHandler(request->chandler);
2033 request->chandler = NULL;
2034 httpClientFinish(connection, 1);
2035 return 1;
2038 static int
2039 httpServeChunkDelayed(TimeEventHandlerPtr event)
2041 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
2042 unlockChunk(connection->request->object,
2043 connection->offset / CHUNK_SIZE);
2044 httpServeChunk(connection);
2045 return 1;
2049 delayedHttpServeChunk(HTTPConnectionPtr connection)
2051 TimeEventHandlerPtr event;
2052 event = scheduleTimeEvent(-1, httpServeChunkDelayed,
2053 sizeof(connection), &connection);
2054 if(!event) return -1;
2055 return 1;
2059 httpServeObjectHandler(int status, ConditionHandlerPtr chandler)
2061 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)chandler->data;
2062 HTTPRequestPtr request = connection->request;
2063 int rc;
2065 if((request->object->flags & OBJECT_ABORTED) || status < 0) {
2066 shutdown(connection->fd, 1);
2067 httpSetTimeout(connection, 10);
2068 /* httpServeChunk will take care of the error. */
2071 httpSetTimeout(connection, -1);
2073 request->chandler = NULL;
2074 rc = delayedHttpServeChunk(connection);
2075 if(rc < 0) {
2076 do_log(L_ERROR, "Couldn't schedule serving.\n");
2077 abortObject(request->object, 503,
2078 internAtom("Couldn't schedule serving"));
2080 return 1;
2083 static int
2084 httpServeObjectStreamHandlerCommon(int kind, int status,
2085 FdEventHandlerPtr event,
2086 StreamRequestPtr srequest)
2088 HTTPConnectionPtr connection = srequest->data;
2089 HTTPRequestPtr request = connection->request;
2090 int condition_result = httpCondition(request->object, request->condition);
2091 int i = connection->offset / CHUNK_SIZE;
2093 assert(!request->chandler);
2095 if(status == 0 && !streamRequestDone(srequest)) {
2096 httpSetTimeout(connection, clientTimeout);
2097 return 0;
2100 httpSetTimeout(connection, -1);
2102 unlockChunk(request->object, i);
2103 if(kind == 2)
2104 unlockChunk(request->object, i + 1);
2106 if(status) {
2107 if(status < 0) {
2108 do_log_error(status == -ECONNRESET ? D_IO : L_ERROR,
2109 -status, "Couldn't write to client");
2110 if(status == -EIO || status == -ESHUTDOWN)
2111 httpClientFinish(connection, 2);
2112 else
2113 httpClientFinish(connection, 1);
2114 } else {
2115 do_log(D_IO, "Couldn't write to client: short write.\n");
2116 httpClientFinish(connection, 2);
2118 return 1;
2121 if(srequest->operation & IO_CHUNKED) {
2122 assert(srequest->offset > 2);
2123 connection->offset += srequest->offset - 2;
2124 } else {
2125 connection->offset += srequest->offset;
2127 request->flags &= ~REQUEST_REQUESTED;
2129 if(request->object->flags & OBJECT_ABORTED) {
2130 httpClientFinish(connection, 1);
2131 return 1;
2134 if(request->method == METHOD_HEAD ||
2135 request->object->code == 204 ||
2136 request->object->code < 200 ||
2137 condition_result == CONDITION_NOT_MODIFIED) {
2138 httpClientFinish(connection, 0);
2139 return 1;
2142 if(srequest->operation & IO_END)
2143 httpClientFinish(connection, 0);
2144 else {
2145 httpConnectionDestroyBuf(connection);
2146 httpServeChunk(connection);
2148 return 1;
2152 httpServeObjectStreamHandler(int status,
2153 FdEventHandlerPtr event,
2154 StreamRequestPtr srequest)
2156 return httpServeObjectStreamHandlerCommon(1, status, event, srequest);
2160 httpServeObjectStreamHandler2(int status,
2161 FdEventHandlerPtr event,
2162 StreamRequestPtr srequest)
2164 return httpServeObjectStreamHandlerCommon(2, status, event, srequest);