comparison src/gt_http_server.c @ 0:d39e1d0d75b6

initial add
author paulo@hit-nxdomain.opendns.com
date Sat, 20 Feb 2010 21:18:28 -0800
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:f02ef53fb05d
1 /*
2 * $Id: gt_http_server.c,v 1.73 2005/01/04 15:03:40 mkern Exp $
3 *
4 * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net)
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 */
16
17 #include "gt_gnutella.h"
18
19 #include "gt_xfer_obj.h"
20 #include "gt_xfer.h"
21
22 #include "gt_http_server.h"
23 #include "gt_http_client.h"
24
25 #include "gt_accept.h"
26 #include "gt_ban.h"
27 #include "gt_version.h"
28
29 /*****************************************************************************/
30
31 /* convenient shorthand */
32 #define CONTENT_URN_FIELD "X-Gnutella-Content-URN"
33
34 #define INCOMING_TIMEOUT (1 * MINUTES)
35
36 /*****************************************************************************/
37
38 struct http_incoming
39 {
40 TCPC *c;
41 timer_id timer;
42 };
43
44 /*****************************************************************************/
45
46 static void server_handle_get (GtTransfer *xfer);
47 static void get_client_request (int fd, input_id id,
48 struct http_incoming *http);
49 static void send_http_response (int fd, input_id id, GtTransfer *xfer);
50
51 /*****************************************************************************/
52 /* SERVER HELPERS */
53
54 static char *lookup_http_code (int code, char **desc)
55 {
56 char *err;
57 char *txt;
58
59 switch (code)
60 {
61 case 200: err = "OK";
62 txt = "Success";
63 break;
64 case 206: err = "Partial Content";
65 txt = "Resume successful";
66 break;
67 case 403: err = "Forbidden";
68 txt = "You do not have access to this file";
69 break;
70 case 404: err = "Not Found";
71 txt = "File is not available";
72 break;
73 case 500: err = "Internal Server Error";
74 txt = "Stale file entry, retry later";
75 break;
76 case 501: err = "Not Implemented";
77 txt = "???";
78 break;
79 case 503: err = "Service Unavailable";
80 txt = "Upload queue is currently full, please try again later";
81 break;
82 default: err = NULL;
83 txt = NULL;
84 break;
85 }
86
87 if (desc)
88 *desc = txt;
89
90 return err;
91 }
92
93 static String *alloc_header (int code)
94 {
95 char *code_text;
96 String *s;
97
98 /* so that we can communicate both the numerical code and the human
99 * readable string */
100 if (!(code_text = lookup_http_code (code, NULL)))
101 return FALSE;
102
103 if (!(s = string_new (NULL, 0, 0, TRUE)))
104 return FALSE;
105
106 string_appendf (s, "HTTP/1.1 %i %s\r\n", code, code_text);
107
108 return s;
109 }
110
111 static void construct_header_va (String *s, int code, va_list args)
112 {
113 char *key;
114 char *value;
115
116 /* Add "Server: " header */
117 string_appendf (s, "Server: %s\r\n", gt_version ());
118
119 for (;;)
120 {
121 if (!(key = va_arg (args, char *)))
122 break;
123
124 if (!(value = va_arg (args, char *)))
125 continue;
126
127 string_appendf (s, "%s: %s\r\n", key, value);
128 }
129
130 /* append final message terminator */
131 string_append (s, "\r\n");
132 }
133
134 static String *construct_header (int code, ...)
135 {
136 String *s;
137 va_list args;
138
139 if (!(s = alloc_header (code)))
140 return NULL;
141
142 va_start (args, code);
143 construct_header_va (s, code, args);
144 va_end (args);
145
146 return s;
147 }
148
149 /*
150 * Construct and send a server reply.
151 */
152 static BOOL gt_http_server_send (TCPC *c, int code, ...)
153 {
154 String *s;
155 int ret;
156 size_t len;
157 va_list args;
158
159 if (!(s = alloc_header (code)))
160 return FALSE;
161
162 va_start (args, code);
163
164 construct_header_va (s, code, args);
165
166 va_end (args);
167
168 if (HTTP_DEBUG)
169 GT->DBGSOCK (GT, c, "sending reply to client =\n%s", s->str);
170
171 len = s->len;
172 ret = tcp_send (c, s->str, s->len);
173
174 string_free (s);
175
176 return (ret == len);
177 }
178
179 static char *get_error_page (GtTransfer *xfer, int code)
180 {
181 char *page;
182 char *err;
183 char *errtxt = NULL;
184
185 if (!(err = lookup_http_code (code, &errtxt)))
186 return 0;
187
188 page = stringf ("<h1>%i %s</h1><br>%s.", code, err, errtxt);
189
190 return page;
191 }
192
193 static BOOL supports_queue (GtTransfer *xfer)
194 {
195 char *features;
196
197 if (dataset_lookupstr (xfer->header, "x-queue"))
198 return TRUE;
199
200 if ((features = dataset_lookupstr (xfer->header, "x-features")))
201 {
202 /* XXX: case-sensitive */
203 if (strstr (features, "queue"))
204 return TRUE;
205 }
206
207 return FALSE;
208 }
209
210 static char *get_queue_line (GtTransfer *xfer)
211 {
212 String *s;
213
214 /* do nothing if not queued */
215 if (xfer->queue_pos == 0)
216 return NULL;
217
218 if (!(s = string_new (NULL, 0, 0, TRUE)))
219 return NULL;
220
221 string_appendf (s, "position=%d,length=%d,pollMin=%d,pollMax=%d",
222 xfer->queue_pos, xfer->queue_ttl, 45, 120);
223
224 return string_free_keep (s);
225 }
226
227 static String *get_error_header (GtTransfer *xfer, int code,
228 const char *error_page)
229 {
230 size_t len;
231 char content_len[256];
232 char *queue_line = NULL;
233 char *content_type = "text/html";
234 String *s;
235
236 len = strlen (error_page);
237 snprintf (content_len, sizeof (content_len), "%u", len);
238
239 if (code == 503 && supports_queue (xfer))
240 queue_line = get_queue_line (xfer);
241
242 /* don't send a content-type header if there is no entity body */
243 if (len == 0)
244 content_type = NULL;
245
246 s = construct_header (code,
247 "Content-Type", content_type,
248 "Content-Length", content_len,
249 CONTENT_URN_FIELD, xfer->content_urns,
250 "X-Queue", queue_line,
251 NULL);
252
253 free (queue_line);
254 return s;
255 }
256
257 static void send_error_reply (int fd, input_id id, GtTransfer *xfer)
258 {
259 String *s;
260 const char *error_page;
261 int ret;
262 TCPC *c;
263
264 c = gt_transfer_get_tcpc (xfer);
265
266 if (!(error_page = get_error_page (xfer, xfer->code)))
267 {
268 gt_transfer_close (xfer, TRUE);
269 return;
270 }
271
272 /*
273 * If the remote end supports queueing or supplied
274 * "X-Gnutella-Content-URN",, it's a Gnutella client and we don't want to
275 * keep sending the error page, because over many requests from this
276 * client the bandwidth could add up.
277 */
278 if (supports_queue (xfer) ||
279 dataset_lookupstr (xfer->header, "x-gnutella-content-urn"))
280 {
281 error_page = ""; /* empty */
282 }
283
284 if (!(s = get_error_header (xfer, xfer->code, error_page)))
285 {
286 gt_transfer_close (xfer, TRUE);
287 return;
288 }
289
290 string_append (s, error_page);
291
292 if (HTTP_DEBUG)
293 GT->DBGSOCK (GT, c, "sending reply to client =\n%s", s->str);
294
295 /* send the whole page at once */
296 ret = tcp_send (c, s->str, s->len);
297
298 /* if the whole thing was sent keep the connection open */
299 if (ret == s->len)
300 {
301 xfer->transmitted_hdrs = TRUE;
302 xfer->remaining_len = 0;
303 }
304
305 string_free (s);
306
307 gt_transfer_close (xfer, FALSE);
308 }
309
310 static void gt_http_server_send_error_and_close (GtTransfer *xfer, int code)
311 {
312 TCPC *c;
313
314 c = gt_transfer_get_tcpc (xfer);
315
316 xfer->code = code;
317
318 input_remove_all (c->fd);
319 input_add (c->fd, xfer, INPUT_WRITE,
320 (InputCallback)send_error_reply, TIMEOUT_DEF);
321 }
322
323 /*****************************************************************************/
324
325 /* parse the Range: bytes=0-10000 format */
326 static void parse_client_request_range (Dataset *dataset,
327 off_t *r_start, off_t *r_stop)
328 {
329 char *range;
330 off_t start;
331 off_t stop;
332
333 if (!r_start && !r_stop)
334 return;
335
336 if (r_start)
337 *r_start = 0;
338 if (r_stop)
339 *r_stop = 0;
340
341 /* leave stop as 0 if we can't figure anything out yet. This is expected
342 * to be handled separately by GET and PUSH */
343 if (!(range = dataset_lookupstr (dataset, "range")))
344 return;
345
346 /* WARNING: this butchers the data in the dataset! */
347 string_sep (&range, "bytes");
348 string_sep_set (&range, " =");
349
350 if (!range)
351 {
352 if (HTTP_DEBUG)
353 GT->DBGFN (GT, "error parsing Range: header");
354
355 return;
356 }
357
358 start = (off_t) ATOI (string_sep (&range, "-"));
359 stop = (off_t) ATOI (string_sep (&range, " "));
360
361 /*
362 * The end of the range is optional (e.g. "Range: 0-"), and in that case
363 * stop == 0. In the case of a single byte file, stop == 1.
364 *
365 * TODO: this is broken for one-byte requests at the start of the file.
366 */
367 if (stop > 0)
368 stop = stop + 1;
369
370 if (r_start)
371 *r_start = start;
372 if (r_stop)
373 *r_stop = stop;
374 }
375
376 /*
377 * Parse requests of the forms:
378 *
379 * /get/47/File maybe with spaces HTTP
380 * /get/47/files_%20_url_encoded HTTP/1.1
381 * /uri-res/N2R?urn:sha1:ABCD HTTP/1.0
382 *
383 * and combinations thereof. The "HTTP" trailer is mandatory.
384 */
385 static void get_request_and_version (char *data, char **request,
386 char **version)
387 {
388 size_t len;
389 char *next;
390 char *dup;
391 char *http_version = NULL;
392
393 *request = NULL;
394 *version = NULL;
395
396 /* trim whitespace inbetween command and request */
397 string_trim (data);
398
399 if (!(dup = STRDUP (data)))
400 return;
401
402 string_upper (dup);
403
404 next = dup;
405
406 /* find the last instance of "HTTP" in the string */
407 while ((next = strstr (next, "HTTP")))
408 {
409 http_version = next;
410 next += sizeof ("HTTP") - 1;
411 }
412
413 /* the rest of the string must be the request */
414 if (http_version != NULL && http_version != dup)
415 {
416 len = http_version - dup;
417 data[len - 1] = 0;
418
419 *request = data;
420 *version = data + len;
421 }
422
423 free (dup);
424 }
425
426 /*
427 * Break down the clients HTTP request
428 */
429 static int parse_client_request (Dataset **r_dataset, char **r_command,
430 char **r_request, char **r_version,
431 off_t *r_start, off_t *r_stop, char *hdr)
432 {
433 Dataset *dataset = NULL;
434 char *command; /* GET */
435 char *request; /* /file.tar.gz */
436 char *version; /* HTTP/1.1 */
437 char *req_line;
438
439 if (!hdr)
440 return FALSE;
441
442 /*
443 * Get the first line of the request
444 */
445 req_line = string_sep_set (&hdr, "\r\n");
446
447 /* get the command (GET, HEAD, etc.) */
448 command = string_sep (&req_line, " ");
449
450 /*
451 * Handle non-url-encoded requests as well as encoded
452 * ones and get the request and version from this HTTP line.
453 */
454 get_request_and_version (req_line, &request, &version);
455
456 if (HTTP_DEBUG)
457 {
458 GT->DBGFN (GT, "command=%s version=%s request=%s",
459 command, version, request);
460 }
461
462 if (!request || string_isempty (request))
463 return FALSE;
464
465 if (r_command)
466 *r_command = command;
467 if (r_request)
468 *r_request = request;
469 if (r_version)
470 *r_version = version;
471
472 gt_http_header_parse (hdr, &dataset);
473
474 if (r_dataset)
475 *r_dataset = dataset;
476
477 /* handle Range: header */
478 parse_client_request_range (dataset, r_start, r_stop);
479
480 if (r_start && r_stop)
481 {
482 if (HTTP_DEBUG)
483 GT->dbg (GT, "range: [%i, %i)", *r_start, *r_stop);
484 }
485
486 return TRUE;
487 }
488
489 /*****************************************************************************/
490
491 /*
492 * Send the request reply back to the client
493 *
494 * NOTE:
495 * This is used by both GET / and PUSH /
496 */
497 static void reply_to_client_request (GtTransfer *xfer)
498 {
499 TCPC *c;
500 Chunk *chunk;
501 off_t entity_size;
502 char range[128];
503 char length[32];
504 BOOL ret;
505
506 if (!xfer)
507 return;
508
509 c = gt_transfer_get_tcpc (xfer);
510 chunk = gt_transfer_get_chunk (xfer);
511
512 /*
513 * Determine the "total" entity body that we have locally, not necessarily
514 * the data that we are uploading. HTTP demands this, but OpenFT really
515 * doesn't give a shit.
516 *
517 * NOTE:
518 * This only works to standard when operating on a GET / request, PUSH's
519 * merely use the range!
520 */
521 if (xfer->open_path_size)
522 entity_size = xfer->open_path_size;
523 else
524 entity_size = xfer->stop - xfer->start;
525
526 /* NOTE: we are "working" around the fact that HTTP's Content-Range
527 * reply is inclusive for the last byte, whereas giFT's is not. */
528 snprintf (range, sizeof (range) - 1, "bytes %i-%i/%i",
529 (int) xfer->start, (int) (xfer->stop - 1), (int) entity_size);
530
531 snprintf (length, sizeof (length) - 1, "%i",
532 (int) (xfer->stop - xfer->start));
533
534 ret = gt_http_server_send (c, xfer->code,
535 "Content-Range", range,
536 "Content-Length", length,
537 "Content-Type", xfer->content_type,
538 CONTENT_URN_FIELD, xfer->content_urns,
539 NULL);
540
541 /* if we transmitted all headers successfully, set transmitted_hdrs
542 * to keep the connection alive, possibly */
543 if (ret)
544 xfer->transmitted_hdrs = TRUE;
545 }
546
547 /*****************************************************************************/
548
549 static void http_incoming_free (struct http_incoming *incoming)
550 {
551 timer_remove (incoming->timer);
552 free (incoming);
553 }
554
555 static void http_incoming_close (struct http_incoming *incoming)
556 {
557 gt_http_connection_close (GT_TRANSFER_UPLOAD, incoming->c, TRUE);
558 http_incoming_free (incoming);
559 }
560
561 static BOOL http_incoming_timeout (struct http_incoming *incoming)
562 {
563 http_incoming_close (incoming);
564 return FALSE;
565 }
566
567 static struct http_incoming *http_incoming_alloc (TCPC *c)
568 {
569 struct http_incoming *incoming;
570
571 incoming = malloc (sizeof (struct http_incoming));
572 if (!incoming)
573 return NULL;
574
575 incoming->c = c;
576 incoming->timer = timer_add (INCOMING_TIMEOUT,
577 (TimerCallback)http_incoming_timeout,
578 incoming);
579
580 return incoming;
581 }
582
583 void gt_http_server_dispatch (int fd, input_id id, TCPC *c)
584 {
585 struct http_incoming *incoming;
586
587 if (net_sock_error (c->fd))
588 {
589 gt_http_connection_close (GT_TRANSFER_UPLOAD, c, TRUE);
590 return;
591 }
592
593 if (!(incoming = http_incoming_alloc (c)))
594 {
595 gt_http_connection_close (GT_TRANSFER_UPLOAD, c, TRUE);
596 return;
597 }
598
599 /* keep track of this incoming connection */
600 /* gt_http_connection_insert (GT_TRANSFER_UPLOAD, c); */
601
602 input_remove (id);
603 input_add (c->fd, incoming, INPUT_READ,
604 (InputCallback)get_client_request, 0);
605 }
606
607 /*
608 * Handle the client's GET commands.
609 */
610 static void get_client_request (int fd, input_id id, struct http_incoming *http)
611 {
612 GtTransfer *xfer;
613 TCPC *c;
614 Dataset *dataset = NULL;
615 char *command = NULL;
616 char *request = NULL;
617 char *version = NULL;
618 off_t start = 0;
619 off_t stop = 0;
620 FDBuf *buf;
621 unsigned char *data;
622 size_t data_len = 0;
623 int n;
624
625 c = http->c;
626 buf = tcp_readbuf (c);
627
628 if ((n = fdbuf_delim (buf, "\n")) < 0)
629 {
630 http_incoming_close (http);
631 return;
632 }
633
634 if (gt_fdbuf_full (buf))
635 {
636 http_incoming_close (http);
637 return;
638 }
639
640 if (n > 0)
641 return;
642
643 data = fdbuf_data (buf, &data_len);
644
645 if (!gt_http_header_terminated (data, data_len))
646 return;
647
648 fdbuf_release (buf);
649
650 if (HTTP_DEBUG)
651 GT->DBGSOCK (GT, c, "client request:\n%s", data);
652
653 /* parse the client's request and determine how we should proceed */
654 if (!parse_client_request (&dataset, &command, &request, &version,
655 &start, &stop, data))
656 {
657 GT->DBGSOCK (GT, c, "invalid http header");
658 http_incoming_close (http);
659 return;
660 }
661
662 /* discard incoming connection timeout maintainance structure */
663 http_incoming_free (http);
664
665 /*
666 * We have enough information now to actually allocate the transfer
667 * structure and pass it along to all logic that follows this
668 *
669 * NOTE:
670 * Each individual handler can determine when it wants to let giFT
671 * in on this
672 */
673 xfer = gt_transfer_new (GT_TRANSFER_UPLOAD, NULL,
674 net_peer (c->fd), 0, start, stop);
675
676 /* connect the connection and the xfer in unholy matrimony */
677 gt_transfer_set_tcpc (xfer, c);
678
679 /* assign all our own memory */
680 xfer->command = STRDUP (command);
681 xfer->header = dataset;
682 xfer->version = STRDUP (version);
683
684 if (!gt_transfer_set_request (xfer, request))
685 {
686 if (HTTP_DEBUG)
687 GT->DBGSOCK (GT, c, "invalid request \"s\"", request);
688
689 gt_transfer_close (xfer, TRUE);
690 return;
691 }
692
693 /* no need for this function again */
694 input_remove (id);
695
696 /* figure out how to handle this request */
697 if (!strcasecmp (xfer->command, "GET") ||
698 !strcasecmp (xfer->command, "HEAD"))
699 {
700 server_handle_get (xfer);
701 return;
702 }
703
704 gt_http_server_send_error_and_close (xfer, 501);
705 }
706
707 /*****************************************************************************/
708
709 static Transfer *start_upload (GtTransfer *xfer, Chunk **chunk)
710 {
711 Transfer *transfer;
712 char *user;
713
714 user = net_ip_str (xfer->ip);
715
716 transfer = GT->upload_start (GT, chunk, user, xfer->share_authd,
717 xfer->start, xfer->stop);
718
719 assert (transfer != NULL);
720
721 return transfer;
722 }
723
724 /* setup the structure for uploading. this will be called from within
725 * client space for PUSH requests as well */
726 int gt_server_setup_upload (GtTransfer *xfer)
727 {
728 Transfer *transfer; /* giFT structure */
729 Chunk *chunk;
730 TCPC *c;
731
732 if (!xfer)
733 return FALSE;
734
735 c = gt_transfer_get_tcpc (xfer);
736 assert (xfer->chunk == NULL);
737
738 /*
739 * Ban the host if they don't have access -- this gives no information
740 * about whether we have the file or not, and i think this is in violation
741 * of the HTTP spec (supposed to return "404 not found" before 403, but
742 * i'm not sure.
743 */
744 if (gt_ban_ipv4_is_banned (c->host))
745 {
746 xfer->code = 403;
747 return FALSE;
748 }
749
750 /* open the file that was requested before we go bringing giFT into
751 * this */
752 if (!(xfer->f = gt_transfer_open_request (xfer, &xfer->code)))
753 return FALSE;
754
755 /* assign stop a value before we proceed */
756 if (xfer->stop == 0)
757 {
758 struct stat st;
759
760 if (!file_stat (xfer->open_path, &st) || st.st_size == 0)
761 {
762 /* stupid bastards have a 0 length file */
763 GT->DBGSOCK (GT, c, "cannot satisfy %s: invalid share",
764 xfer->open_path);
765 return FALSE;
766 }
767
768 xfer->stop = st.st_size;
769 xfer->remaining_len = xfer->stop - xfer->start;
770 }
771
772 /* we can now be certain that we are handling a download request from
773 * the client. allocate the appropriate structures to hook into giFT */
774 if (!(transfer = start_upload (xfer, &chunk)))
775 {
776 GT->DBGFN (GT, "unable to register upload with the daemon");
777 return FALSE;
778 }
779
780 /* override 200 w/ 206 if the request is not the whole file size */
781 if (xfer->remaining_len != xfer->share_authd->size)
782 xfer->code = 206;
783
784 /* assign the circular references for passing the chunk along */
785 gt_transfer_set_chunk (xfer, chunk);
786
787 /* finally, seek the file descriptor where it needs to be */
788 fseek (xfer->f, xfer->start, SEEK_SET);
789
790 return TRUE;
791 }
792
793 static void server_handle_get (GtTransfer *xfer)
794 {
795 TCPC *c;
796
797 c = gt_transfer_get_tcpc (xfer);
798 assert (xfer->chunk == NULL);
799
800 /* WARNING: this block is duplicated in http_client:client_push_request */
801 if (!gt_server_setup_upload (xfer))
802 {
803 if (xfer->code == 200)
804 xfer->code = 404;
805
806 gt_http_server_send_error_and_close (xfer, xfer->code);
807 return;
808 }
809
810 input_add (c->fd, xfer, INPUT_WRITE,
811 (InputCallback)send_http_response, TIMEOUT_DEF);
812 }
813
814 static void send_http_response (int fd, input_id id, GtTransfer *xfer)
815 {
816 TCPC *c;
817
818 c = gt_transfer_get_tcpc (xfer);
819
820 if (net_sock_error (c->fd))
821 {
822 gt_transfer_close (xfer, TRUE);
823 return;
824 }
825
826 /* ok, send client the header */
827 reply_to_client_request (xfer);
828
829 if (!strcasecmp (xfer->command, "HEAD"))
830 {
831 gt_transfer_close (xfer, TRUE);
832 return;
833 }
834
835 /* disable header read timer */
836 timer_remove_zero (&xfer->header_timer);
837
838 input_remove (id);
839 input_add (c->fd, xfer, INPUT_WRITE,
840 (InputCallback)gt_server_upload_file, 0);
841 }
842
843 /*
844 * Uploads the file requests
845 */
846 void gt_server_upload_file (int fd, input_id id, GtTransfer *xfer)
847 {
848 TCPC *c;
849 Chunk *chunk;
850 char buf[RW_BUFFER];
851 size_t read_len;
852 size_t size;
853 int sent_len = 0;
854 off_t remainder;
855
856 c = gt_transfer_get_tcpc (xfer);
857 chunk = gt_transfer_get_chunk (xfer);
858
859 assert (xfer->f != NULL);
860
861 /* number of bytes left to be uploaded by this chunk */
862 if ((remainder = xfer->remaining_len) <= 0)
863 {
864 /* for whatever reason this function may have been called when we have
865 * already overrun the transfer...in that case we will simply fall
866 * through to the end-of-transfer condition */
867 gt_transfer_write (xfer, chunk, NULL, 0);
868 return;
869 }
870
871 size = sizeof (buf);
872
873 if (size > remainder)
874 size = remainder;
875
876 /*
877 * Ask giFT for the size we should send. If this returns 0, the upload
878 * was suspended.
879 */
880 if ((size = upload_throttle (chunk, size)) == 0)
881 return;
882
883 /* read as much as we can from the local file */
884 if (!(read_len = fread (buf, sizeof (char), size, xfer->f)))
885 {
886 GT->DBGFN (GT, "unable to read from %s: %s", xfer->open_path,
887 GIFT_STRERROR ());
888 gt_transfer_status (xfer, SOURCE_CANCELLED, "Local read error");
889 gt_transfer_close (xfer, TRUE);
890 return;
891 }
892
893 if ((sent_len = tcp_send (c, buf, MIN (read_len, remainder))) <= 0)
894 {
895 gt_transfer_status (xfer, SOURCE_CANCELLED,
896 "Unable to send data block");
897 gt_transfer_close (xfer, TRUE);
898 return;
899 }
900
901 /* check if the file was too small for the transfer TODO: this isn't
902 * checked earlier, but should be */
903 if (read_len != size)
904 {
905 gt_transfer_status (xfer, SOURCE_CANCELLED, "Unexpected end of file");
906 gt_transfer_close (xfer, TRUE);
907 return;
908 }
909
910 /*
911 * Check for short send(). This could use fseek(), but I have this
912 * growing feeling that using stdio everywhere is a bad idea.
913 */
914 if (read_len != sent_len)
915 {
916 gt_transfer_status (xfer, SOURCE_CANCELLED, "Short send()");
917 gt_transfer_close (xfer, TRUE);
918 return;
919 }
920
921 /*
922 * Call gt_upload to report back to giFT. This will also cancel
923 * the transfer if the upload has completed.
924 */
925 gt_transfer_write (xfer, chunk, buf, sent_len);
926 }
927
928 /*****************************************************************************/
929
930 void gt_http_server_reset (TCPC *c)
931 {
932 /*
933 * This can happen because the GtTransfer and TCPC can be decoupled, as in
934 * the case of a push request sent.
935 */
936 if (!c)
937 return;
938
939 /* finish all queued writes before we reset */
940 tcp_flush (c, TRUE);
941
942 /* reset the input state */
943 input_remove_all (c->fd);
944 input_add (c->fd, c, INPUT_READ,
945 (InputCallback)gt_http_server_dispatch, 2 * MINUTES);
946 }