rev |
line source |
paulo@0
|
1 /*
|
paulo@0
|
2 * $Id: gt_xfer_obj.c,v 1.48 2004/12/19 12:38:42 mkern Exp $
|
paulo@0
|
3 *
|
paulo@0
|
4 * acts as a gateway between giFT and the HTTP implementation
|
paulo@0
|
5 *
|
paulo@0
|
6 * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net)
|
paulo@0
|
7 *
|
paulo@0
|
8 * This program is free software; you can redistribute it and/or modify it
|
paulo@0
|
9 * under the terms of the GNU General Public License as published by the
|
paulo@0
|
10 * Free Software Foundation; either version 2, or (at your option) any
|
paulo@0
|
11 * later version.
|
paulo@0
|
12 *
|
paulo@0
|
13 * This program is distributed in the hope that it will be useful, but
|
paulo@0
|
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
|
paulo@0
|
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
paulo@0
|
16 * General Public License for more details.
|
paulo@0
|
17 */
|
paulo@0
|
18
|
paulo@0
|
19 #include "gt_gnutella.h"
|
paulo@0
|
20
|
paulo@0
|
21 #include "gt_xfer_obj.h"
|
paulo@0
|
22 #include "gt_xfer.h"
|
paulo@0
|
23 #include "gt_http_server.h"
|
paulo@0
|
24 #include "gt_http_client.h"
|
paulo@0
|
25 #include "gt_share.h"
|
paulo@0
|
26
|
paulo@0
|
27 #include "encoding/url.h"
|
paulo@0
|
28
|
paulo@0
|
29 #include "transfer/source.h"
|
paulo@0
|
30
|
paulo@0
|
31 /*****************************************************************************/
|
paulo@0
|
32
|
paulo@0
|
33 static List *upload_connections = NULL;
|
paulo@0
|
34 static List *download_connections = NULL;
|
paulo@0
|
35
|
paulo@0
|
36 /*****************************************************************************/
|
paulo@0
|
37 /* HTTP COMMUNICATION */
|
paulo@0
|
38
|
paulo@0
|
39 static BOOL header_read_timeout (GtTransfer *xfer)
|
paulo@0
|
40 {
|
paulo@0
|
41 gt_transfer_status (xfer, SOURCE_TIMEOUT, "Timed out");
|
paulo@0
|
42 gt_transfer_close (xfer, TRUE);
|
paulo@0
|
43 return FALSE;
|
paulo@0
|
44 }
|
paulo@0
|
45
|
paulo@0
|
46 GtTransfer *gt_transfer_new (GtTransferType type, Source *source,
|
paulo@0
|
47 in_addr_t ip, in_port_t port,
|
paulo@0
|
48 off_t start, off_t stop)
|
paulo@0
|
49 {
|
paulo@0
|
50 GtTransfer *xfer;
|
paulo@0
|
51 GtTransferCB cb;
|
paulo@0
|
52
|
paulo@0
|
53 if (!(xfer = malloc (sizeof (GtTransfer))))
|
paulo@0
|
54 return NULL;
|
paulo@0
|
55
|
paulo@0
|
56 memset (xfer, 0, sizeof (GtTransfer));
|
paulo@0
|
57
|
paulo@0
|
58 if (type == GT_TRANSFER_UPLOAD)
|
paulo@0
|
59 cb = gt_upload;
|
paulo@0
|
60 else if (type == GT_TRANSFER_DOWNLOAD)
|
paulo@0
|
61 cb = gt_download;
|
paulo@0
|
62 else
|
paulo@0
|
63 abort ();
|
paulo@0
|
64
|
paulo@0
|
65 xfer->type = type;
|
paulo@0
|
66 xfer->callback = cb;
|
paulo@0
|
67 xfer->source = source;
|
paulo@0
|
68
|
paulo@0
|
69 /* parsed information about the source */
|
paulo@0
|
70 xfer->ip = ip;
|
paulo@0
|
71 xfer->port = port;
|
paulo@0
|
72
|
paulo@0
|
73 xfer->start = start;
|
paulo@0
|
74 xfer->stop = stop;
|
paulo@0
|
75
|
paulo@0
|
76 xfer->shared = TRUE;
|
paulo@0
|
77
|
paulo@0
|
78 xfer->detach_timer = TIMER_NONE;
|
paulo@0
|
79 xfer->detach_msgtxt = NULL;
|
paulo@0
|
80
|
paulo@0
|
81 /* set the size of this http request */
|
paulo@0
|
82 xfer->remaining_len = xfer->stop - xfer->start;
|
paulo@0
|
83
|
paulo@0
|
84 /* give this GtTransfer a maximum amount of time before cancelling */
|
paulo@0
|
85 xfer->header_timer = timer_add (1 * MINUTES,
|
paulo@0
|
86 (TimerCallback)header_read_timeout,
|
paulo@0
|
87 xfer);
|
paulo@0
|
88
|
paulo@0
|
89 return xfer;
|
paulo@0
|
90 }
|
paulo@0
|
91
|
paulo@0
|
92 static void gt_transfer_free (GtTransfer *xfer)
|
paulo@0
|
93 {
|
paulo@0
|
94 if (!xfer)
|
paulo@0
|
95 return;
|
paulo@0
|
96
|
paulo@0
|
97 free (xfer->command);
|
paulo@0
|
98 free (xfer->request);
|
paulo@0
|
99 free (xfer->request_path);
|
paulo@0
|
100 free (xfer->content_urns);
|
paulo@0
|
101 free (xfer->detach_msgtxt);
|
paulo@0
|
102
|
paulo@0
|
103 if (xfer->header)
|
paulo@0
|
104 dataset_clear (xfer->header);
|
paulo@0
|
105
|
paulo@0
|
106 #if 0
|
paulo@0
|
107 if (xfer->share_authd && xfer->shared_authd_free)
|
paulo@0
|
108 share_free (xfer->share_authd);
|
paulo@0
|
109 #endif
|
paulo@0
|
110
|
paulo@0
|
111 timer_remove (xfer->detach_timer);
|
paulo@0
|
112 timer_remove (xfer->header_timer);
|
paulo@0
|
113
|
paulo@0
|
114 /* uploads use these */
|
paulo@0
|
115 free (xfer->open_path);
|
paulo@0
|
116 free (xfer->hash);
|
paulo@0
|
117 free (xfer->version);
|
paulo@0
|
118
|
paulo@0
|
119 if (xfer->f)
|
paulo@0
|
120 fclose (xfer->f);
|
paulo@0
|
121
|
paulo@0
|
122 /* whee */
|
paulo@0
|
123 free (xfer);
|
paulo@0
|
124 }
|
paulo@0
|
125
|
paulo@0
|
126 /*****************************************************************************/
|
paulo@0
|
127
|
paulo@0
|
128 void gt_transfer_set_tcpc (GtTransfer *xfer, TCPC *c)
|
paulo@0
|
129 {
|
paulo@0
|
130 assert (c->udata == NULL);
|
paulo@0
|
131 assert (xfer->c == NULL);
|
paulo@0
|
132
|
paulo@0
|
133 c->udata = xfer;
|
paulo@0
|
134 xfer->c = c;
|
paulo@0
|
135 }
|
paulo@0
|
136
|
paulo@0
|
137 void gt_transfer_set_chunk (GtTransfer *xfer, Chunk *chunk)
|
paulo@0
|
138 {
|
paulo@0
|
139 assert (chunk->udata == NULL);
|
paulo@0
|
140 assert (xfer->chunk == NULL);
|
paulo@0
|
141
|
paulo@0
|
142 xfer->chunk = chunk;
|
paulo@0
|
143 chunk->udata = xfer;
|
paulo@0
|
144 }
|
paulo@0
|
145
|
paulo@0
|
146 Chunk *gt_transfer_get_chunk (GtTransfer *xfer)
|
paulo@0
|
147 {
|
paulo@0
|
148 assert (xfer->chunk != NULL);
|
paulo@0
|
149 assert (xfer->chunk->udata == xfer);
|
paulo@0
|
150
|
paulo@0
|
151 return xfer->chunk;
|
paulo@0
|
152 }
|
paulo@0
|
153
|
paulo@0
|
154 TCPC *gt_transfer_get_tcpc (GtTransfer *xfer)
|
paulo@0
|
155 {
|
paulo@0
|
156 assert (xfer->c != NULL);
|
paulo@0
|
157 assert (xfer->c->udata == xfer);
|
paulo@0
|
158
|
paulo@0
|
159 return xfer->c;
|
paulo@0
|
160 }
|
paulo@0
|
161
|
paulo@0
|
162 /*****************************************************************************/
|
paulo@0
|
163
|
paulo@0
|
164 GtSource *gt_transfer_get_source (GtTransfer *xfer)
|
paulo@0
|
165 {
|
paulo@0
|
166 Source *source = xfer->source;
|
paulo@0
|
167
|
paulo@0
|
168 /* could be null for uploads */
|
paulo@0
|
169 if (!source)
|
paulo@0
|
170 return NULL;
|
paulo@0
|
171
|
paulo@0
|
172 assert (source->udata != NULL);
|
paulo@0
|
173 return source->udata;
|
paulo@0
|
174 }
|
paulo@0
|
175
|
paulo@0
|
176 /*****************************************************************************/
|
paulo@0
|
177
|
paulo@0
|
178 /*
|
paulo@0
|
179 * giftd may change the Chunk size. This routine will reset the range of the
|
paulo@0
|
180 * transfer we'll ask the remote end for. It should be called before we've
|
paulo@0
|
181 * transmitted the HTTP headers that include the Range: request.
|
paulo@0
|
182 */
|
paulo@0
|
183 void gt_transfer_set_length (GtTransfer *xfer, Chunk *chunk)
|
paulo@0
|
184 {
|
paulo@0
|
185 TCPC *c;
|
paulo@0
|
186 off_t old_start;
|
paulo@0
|
187 off_t old_stop;
|
paulo@0
|
188 off_t old_len;
|
paulo@0
|
189
|
paulo@0
|
190 c = gt_transfer_get_tcpc (xfer);
|
paulo@0
|
191
|
paulo@0
|
192 /* don't call me if you've already transmitted headers */
|
paulo@0
|
193 assert (!xfer->transmitted_hdrs);
|
paulo@0
|
194
|
paulo@0
|
195 old_start = xfer->start;
|
paulo@0
|
196 old_stop = xfer->stop;
|
paulo@0
|
197 old_len = xfer->remaining_len;
|
paulo@0
|
198
|
paulo@0
|
199 xfer->start = chunk->start + chunk->transmit;
|
paulo@0
|
200 xfer->stop = chunk->stop;
|
paulo@0
|
201
|
paulo@0
|
202 xfer->remaining_len = xfer->stop - xfer->start;
|
paulo@0
|
203
|
paulo@0
|
204 /* i believe this is true even for push downloads... */
|
paulo@0
|
205 assert (xfer->start == old_start);
|
paulo@0
|
206
|
paulo@0
|
207 if (xfer->stop != old_stop)
|
paulo@0
|
208 {
|
paulo@0
|
209 assert (xfer->remaining_len != old_len);
|
paulo@0
|
210
|
paulo@0
|
211 GT->DBGSOCK (GT, c, "(%s) old chunk range: [%lu,%lu) "
|
paulo@0
|
212 "new range: [%lu,%lu) old len: %lu new len: %lu",
|
paulo@0
|
213 xfer->request, (long)old_start,(long)old_stop,
|
paulo@0
|
214 (long)xfer->start, (long)xfer->stop,
|
paulo@0
|
215 (long)old_len, (long)xfer->remaining_len);
|
paulo@0
|
216 }
|
paulo@0
|
217 }
|
paulo@0
|
218
|
paulo@0
|
219 /*****************************************************************************/
|
paulo@0
|
220
|
paulo@0
|
221 /*
|
paulo@0
|
222 * Cancels a possibly active HTTP transfer
|
paulo@0
|
223 *
|
paulo@0
|
224 * NOTE:
|
paulo@0
|
225 * This function may be called recursively if you don't do what giFT expects
|
paulo@0
|
226 * of you. This is a feature, not a bug, trust me.
|
paulo@0
|
227 */
|
paulo@0
|
228 static void gt_transfer_cancel (Chunk *chunk, TransferType type)
|
paulo@0
|
229 {
|
paulo@0
|
230 GtTransfer *xfer;
|
paulo@0
|
231 BOOL force_close = FALSE;
|
paulo@0
|
232
|
paulo@0
|
233 if (!chunk)
|
paulo@0
|
234 return;
|
paulo@0
|
235
|
paulo@0
|
236 xfer = chunk->udata;
|
paulo@0
|
237
|
paulo@0
|
238 /* each time this function is called we _MUST_ uncouple the transfer
|
paulo@0
|
239 * and the chunk or the code to follow will eventually call this func
|
paulo@0
|
240 * again!! */
|
paulo@0
|
241 if (!xfer)
|
paulo@0
|
242 return;
|
paulo@0
|
243
|
paulo@0
|
244 /* do not emit a callback signal */
|
paulo@0
|
245 xfer->callback = NULL;
|
paulo@0
|
246 gt_transfer_close (xfer, force_close);
|
paulo@0
|
247 }
|
paulo@0
|
248
|
paulo@0
|
249 /*****************************************************************************/
|
paulo@0
|
250
|
paulo@0
|
251 static void close_http_connection (TCPC *c, BOOL force_close,
|
paulo@0
|
252 GtTransferType type, GtSource *gt_src)
|
paulo@0
|
253 {
|
paulo@0
|
254 if (!c)
|
paulo@0
|
255 return;
|
paulo@0
|
256
|
paulo@0
|
257 /*
|
paulo@0
|
258 * If this is an incoming indirect download that we sent a push out
|
paulo@0
|
259 * for, then don't store the connection in the HTTP connection cache,
|
paulo@0
|
260 * store it in the separate push connection cache that uses the client
|
paulo@0
|
261 * id as well as the IP address to identify the node.
|
paulo@0
|
262 */
|
paulo@0
|
263 if (!force_close && type == GT_TRANSFER_DOWNLOAD && !c->outgoing)
|
paulo@0
|
264 {
|
paulo@0
|
265 if (gt_src != NULL)
|
paulo@0
|
266 {
|
paulo@0
|
267 if (HTTP_DEBUG)
|
paulo@0
|
268 GT->DBGSOCK (GT, c, "Keeping push connection");
|
paulo@0
|
269
|
paulo@0
|
270 /* nullify the previous data on this connection */
|
paulo@0
|
271 c->udata = NULL;
|
paulo@0
|
272
|
paulo@0
|
273 gt_push_source_add_conn (gt_src->guid, gt_src->user_ip, c);
|
paulo@0
|
274 return;
|
paulo@0
|
275 }
|
paulo@0
|
276
|
paulo@0
|
277 /*
|
paulo@0
|
278 * This could happen if the chunk has no source url to parse
|
paulo@0
|
279 * at the moment. Argh, GtTransfer should always have a GtSource
|
paulo@0
|
280 * if xfer->type == GT_TRANSFER_DOWNLOAD.
|
paulo@0
|
281 */
|
paulo@0
|
282 if (HTTP_DEBUG)
|
paulo@0
|
283 GT->DBGSOCK (GT, c, "Closing pushed connection! ARGH!");
|
paulo@0
|
284
|
paulo@0
|
285 force_close = TRUE;
|
paulo@0
|
286 }
|
paulo@0
|
287
|
paulo@0
|
288 gt_http_connection_close (type, c, force_close);
|
paulo@0
|
289 }
|
paulo@0
|
290
|
paulo@0
|
291 /*
|
paulo@0
|
292 * This function is very critical to OpenFT's transfer system. It is called
|
paulo@0
|
293 * anytime either the client or server HTTP implementations need to "cleanup"
|
paulo@0
|
294 * all data associated. This includes disconnecting the socket, unlinking
|
paulo@0
|
295 * itself from the chunk system and registering this status with giFT, just
|
paulo@0
|
296 * in case this is premature. If anything is leaking or fucking up, blame
|
paulo@0
|
297 * this :)
|
paulo@0
|
298 */
|
paulo@0
|
299 void gt_transfer_close (GtTransfer *xfer, BOOL force_close)
|
paulo@0
|
300 {
|
paulo@0
|
301 TCPC *c;
|
paulo@0
|
302 Chunk *chunk;
|
paulo@0
|
303 GtSource *gt_src = NULL;
|
paulo@0
|
304 char *conn_hdr;
|
paulo@0
|
305
|
paulo@0
|
306 if (!xfer)
|
paulo@0
|
307 return;
|
paulo@0
|
308
|
paulo@0
|
309 c = xfer->c;
|
paulo@0
|
310 chunk = xfer->chunk;
|
paulo@0
|
311
|
paulo@0
|
312 assert (xfer != NULL);
|
paulo@0
|
313
|
paulo@0
|
314 /* remove the xfer from the indirect src list */
|
paulo@0
|
315 gt_push_source_remove_xfer (xfer);
|
paulo@0
|
316
|
paulo@0
|
317 /* get this source if this was a download */
|
paulo@0
|
318 if (xfer->type == GT_TRANSFER_DOWNLOAD && chunk && chunk->source)
|
paulo@0
|
319 gt_src = gt_source_unserialize (chunk->source->url);
|
paulo@0
|
320
|
paulo@0
|
321 /* if we have associated a chunk with this transfer we need to make sure
|
paulo@0
|
322 * we remove cleanly detach */
|
paulo@0
|
323 if (chunk)
|
paulo@0
|
324 {
|
paulo@0
|
325 chunk->udata = NULL;
|
paulo@0
|
326
|
paulo@0
|
327 /*
|
paulo@0
|
328 * notify the transfer callback that we have terminated this
|
paulo@0
|
329 * connection. let giFT handle the rest
|
paulo@0
|
330 *
|
paulo@0
|
331 * NOTE:
|
paulo@0
|
332 * see gt_transfer_cancel for some warnings about this code
|
paulo@0
|
333 */
|
paulo@0
|
334 if (xfer->callback)
|
paulo@0
|
335 (*xfer->callback) (chunk, NULL, 0);
|
paulo@0
|
336
|
paulo@0
|
337 /* WARNING: chunk is free'd in the depths of xfer->callback! */
|
paulo@0
|
338 }
|
paulo@0
|
339
|
paulo@0
|
340 /* HTTP/1.0 does not support persist connections or something...i dunno */
|
paulo@0
|
341 if (!STRCASECMP (xfer->version, "HTTP/1.0"))
|
paulo@0
|
342 force_close = TRUE;
|
paulo@0
|
343
|
paulo@0
|
344 /* older gnutella clients send a plain "HTTP" version, that is
|
paulo@0
|
345 * not persistent */
|
paulo@0
|
346 if (!STRCASECMP (xfer->version, "HTTP"))
|
paulo@0
|
347 force_close = TRUE;
|
paulo@0
|
348
|
paulo@0
|
349 /*
|
paulo@0
|
350 * We must force a socket close if there is still data waiting to
|
paulo@0
|
351 * be read on this transfer.
|
paulo@0
|
352 */
|
paulo@0
|
353 if (!xfer->transmitted_hdrs || xfer->remaining_len != 0)
|
paulo@0
|
354 force_close = TRUE;
|
paulo@0
|
355
|
paulo@0
|
356 /* close the connection if "Connection: close" was supplied */
|
paulo@0
|
357 conn_hdr = dataset_lookupstr (xfer->header, "connection");
|
paulo@0
|
358 if (!STRCASECMP (conn_hdr, "close"))
|
paulo@0
|
359 force_close = TRUE;
|
paulo@0
|
360
|
paulo@0
|
361 close_http_connection (c, force_close, xfer->type, gt_src);
|
paulo@0
|
362
|
paulo@0
|
363 gt_source_free (gt_src);
|
paulo@0
|
364
|
paulo@0
|
365 gt_transfer_free (xfer);
|
paulo@0
|
366 }
|
paulo@0
|
367
|
paulo@0
|
368 void gt_transfer_status (GtTransfer *xfer, SourceStatus status, char *text)
|
paulo@0
|
369 {
|
paulo@0
|
370 Chunk *chunk;
|
paulo@0
|
371 GtSource *gt_src;
|
paulo@0
|
372 char *next_status;
|
paulo@0
|
373
|
paulo@0
|
374 if (!xfer || !text)
|
paulo@0
|
375 return;
|
paulo@0
|
376
|
paulo@0
|
377 /* Added this on 2004-12-19 to track observed assertion failure in
|
paulo@0
|
378 * gt_transfer_get_chunk. -- mkern
|
paulo@0
|
379 */
|
paulo@0
|
380 if (!xfer->chunk || xfer->chunk->udata != xfer)
|
paulo@0
|
381 {
|
paulo@0
|
382 GT->DBGFN (GT, "This is where we say good bye. status = %d, "
|
paulo@0
|
383 "text = %s, xfer->request_path = %s, xfer->ip = %s",
|
paulo@0
|
384 status, text, xfer->request_path, net_ip_str (xfer->ip));
|
paulo@0
|
385 }
|
paulo@0
|
386
|
paulo@0
|
387 chunk = gt_transfer_get_chunk (xfer);
|
paulo@0
|
388
|
paulo@0
|
389 GT->source_status (GT, chunk->source, status, text);
|
paulo@0
|
390
|
paulo@0
|
391 /*
|
paulo@0
|
392 * HACK: Store the status message on the GtSource,
|
paulo@0
|
393 * so we can reuse it sometimes.
|
paulo@0
|
394 */
|
paulo@0
|
395 if (!xfer->source || !(gt_src = xfer->source->udata))
|
paulo@0
|
396 return;
|
paulo@0
|
397
|
paulo@0
|
398 /* allocate first so it's ok to call this function with an old value of
|
paulo@0
|
399 * gt_src->status_txt */
|
paulo@0
|
400 next_status = STRDUP (text);
|
paulo@0
|
401 free (gt_src->status_txt);
|
paulo@0
|
402 gt_src->status_txt = next_status;
|
paulo@0
|
403 }
|
paulo@0
|
404
|
paulo@0
|
405 /*****************************************************************************/
|
paulo@0
|
406 /* PERSISTENT HTTP HANDLING */
|
paulo@0
|
407
|
paulo@0
|
408 struct conn_info
|
paulo@0
|
409 {
|
paulo@0
|
410 in_addr_t ip;
|
paulo@0
|
411 in_port_t port;
|
paulo@0
|
412 size_t count;
|
paulo@0
|
413 };
|
paulo@0
|
414
|
paulo@0
|
415 static int conn_cmp (TCPC *c, struct conn_info *info)
|
paulo@0
|
416 {
|
paulo@0
|
417 if (info->port != c->port)
|
paulo@0
|
418 return -1;
|
paulo@0
|
419
|
paulo@0
|
420 if (net_peer (c->fd) != info->ip)
|
paulo@0
|
421 return 1;
|
paulo@0
|
422
|
paulo@0
|
423 return 0;
|
paulo@0
|
424 }
|
paulo@0
|
425
|
paulo@0
|
426 TCPC *gt_http_connection_lookup (GtTransferType type, in_addr_t ip,
|
paulo@0
|
427 in_port_t port)
|
paulo@0
|
428 {
|
paulo@0
|
429 List *link;
|
paulo@0
|
430 List **connlist_ptr;
|
paulo@0
|
431 TCPC *c = NULL;
|
paulo@0
|
432 struct conn_info info;
|
paulo@0
|
433
|
paulo@0
|
434 info.ip = ip;
|
paulo@0
|
435 info.port = port;
|
paulo@0
|
436
|
paulo@0
|
437 if (type == GT_TRANSFER_DOWNLOAD)
|
paulo@0
|
438 connlist_ptr = &download_connections;
|
paulo@0
|
439 else
|
paulo@0
|
440 connlist_ptr = &upload_connections;
|
paulo@0
|
441
|
paulo@0
|
442 link = list_find_custom (*connlist_ptr, &info, (CompareFunc)conn_cmp);
|
paulo@0
|
443
|
paulo@0
|
444 if (link)
|
paulo@0
|
445 {
|
paulo@0
|
446 c = link->data;
|
paulo@0
|
447
|
paulo@0
|
448 GT->DBGFN (GT, "using previous connection to %s:%hu",
|
paulo@0
|
449 net_ip_str (ip), port);
|
paulo@0
|
450
|
paulo@0
|
451 /* remove from the open list */
|
paulo@0
|
452 *connlist_ptr = list_remove_link (*connlist_ptr, link);
|
paulo@0
|
453 input_remove_all (c->fd);
|
paulo@0
|
454 }
|
paulo@0
|
455
|
paulo@0
|
456 return c;
|
paulo@0
|
457 }
|
paulo@0
|
458
|
paulo@0
|
459 /*
|
paulo@0
|
460 * Handles outgoing HTTP connections. This function is capable of
|
paulo@0
|
461 * retrieving an already connected socket that was left over from a previous
|
paulo@0
|
462 * transfer.
|
paulo@0
|
463 */
|
paulo@0
|
464 TCPC *gt_http_connection_open (GtTransferType type, in_addr_t ip,
|
paulo@0
|
465 in_port_t port)
|
paulo@0
|
466 {
|
paulo@0
|
467 TCPC *c;
|
paulo@0
|
468
|
paulo@0
|
469 if (!(c = gt_http_connection_lookup (type, ip, port)))
|
paulo@0
|
470 c = tcp_open (ip, port, FALSE);
|
paulo@0
|
471
|
paulo@0
|
472 return c;
|
paulo@0
|
473 }
|
paulo@0
|
474
|
paulo@0
|
475 static BOOL count_open (TCPC *c, struct conn_info *info)
|
paulo@0
|
476 {
|
paulo@0
|
477 if (info->ip == net_peer (c->fd))
|
paulo@0
|
478 info->count++;
|
paulo@0
|
479
|
paulo@0
|
480 return FALSE;
|
paulo@0
|
481 }
|
paulo@0
|
482
|
paulo@0
|
483 size_t gt_http_connection_length (GtTransferType type, in_addr_t ip)
|
paulo@0
|
484 {
|
paulo@0
|
485 struct conn_info info;
|
paulo@0
|
486 List *list;
|
paulo@0
|
487
|
paulo@0
|
488 info.ip = ip;
|
paulo@0
|
489 info.port = 0;
|
paulo@0
|
490 info.count = 0;
|
paulo@0
|
491
|
paulo@0
|
492 assert (type == GT_TRANSFER_DOWNLOAD || type == GT_TRANSFER_UPLOAD);
|
paulo@0
|
493 list = (type == GT_TRANSFER_DOWNLOAD ? download_connections :
|
paulo@0
|
494 upload_connections);
|
paulo@0
|
495
|
paulo@0
|
496 list_foreach (list, (ListForeachFunc)count_open, &info);
|
paulo@0
|
497
|
paulo@0
|
498 return info.count;
|
paulo@0
|
499 }
|
paulo@0
|
500
|
paulo@0
|
501 void gt_http_connection_close (GtTransferType type, TCPC *c, BOOL force_close)
|
paulo@0
|
502 {
|
paulo@0
|
503 List **connlist_ptr;
|
paulo@0
|
504
|
paulo@0
|
505 if (!c)
|
paulo@0
|
506 return;
|
paulo@0
|
507
|
paulo@0
|
508 switch (type)
|
paulo@0
|
509 {
|
paulo@0
|
510 case GT_TRANSFER_DOWNLOAD:
|
paulo@0
|
511 {
|
paulo@0
|
512 gt_http_client_reset (c);
|
paulo@0
|
513 connlist_ptr = &download_connections;
|
paulo@0
|
514 }
|
paulo@0
|
515 break;
|
paulo@0
|
516
|
paulo@0
|
517 case GT_TRANSFER_UPLOAD:
|
paulo@0
|
518 {
|
paulo@0
|
519 gt_http_server_reset (c);
|
paulo@0
|
520 connlist_ptr = &upload_connections;
|
paulo@0
|
521 }
|
paulo@0
|
522 break;
|
paulo@0
|
523
|
paulo@0
|
524 default:
|
paulo@0
|
525 abort ();
|
paulo@0
|
526 }
|
paulo@0
|
527
|
paulo@0
|
528 if (force_close)
|
paulo@0
|
529 {
|
paulo@0
|
530 *connlist_ptr = list_remove (*connlist_ptr, c);
|
paulo@0
|
531
|
paulo@0
|
532 if (HTTP_DEBUG)
|
paulo@0
|
533 GT->DBGSOCK (GT, c, "force closing");
|
paulo@0
|
534
|
paulo@0
|
535 tcp_close (c);
|
paulo@0
|
536 return;
|
paulo@0
|
537 }
|
paulo@0
|
538
|
paulo@0
|
539 /* remove the data associated with this connection */
|
paulo@0
|
540 c->udata = NULL;
|
paulo@0
|
541
|
paulo@0
|
542 /*
|
paulo@0
|
543 * This condition will happen because the server doesn't remove the
|
paulo@0
|
544 * connection from the persistent list until the connection fails.
|
paulo@0
|
545 */
|
paulo@0
|
546 if (list_find (*connlist_ptr, c))
|
paulo@0
|
547 {
|
paulo@0
|
548 assert (type == GT_TRANSFER_UPLOAD);
|
paulo@0
|
549 return;
|
paulo@0
|
550 }
|
paulo@0
|
551
|
paulo@0
|
552 /* track it */
|
paulo@0
|
553 *connlist_ptr = list_prepend (*connlist_ptr, c);
|
paulo@0
|
554 }
|
paulo@0
|
555
|
paulo@0
|
556 /*****************************************************************************/
|
paulo@0
|
557
|
paulo@0
|
558 static char *localize_request (GtTransfer *xfer, BOOL *authorized)
|
paulo@0
|
559 {
|
paulo@0
|
560 char *open_path;
|
paulo@0
|
561 char *s_path;
|
paulo@0
|
562 int auth = FALSE;
|
paulo@0
|
563 int need_free = FALSE;
|
paulo@0
|
564
|
paulo@0
|
565 if (!xfer || !xfer->request)
|
paulo@0
|
566 return NULL;
|
paulo@0
|
567
|
paulo@0
|
568 /* dont call secure_path if they dont even care if it's a secure
|
paulo@0
|
569 * lookup */
|
paulo@0
|
570 s_path =
|
paulo@0
|
571 (authorized ? file_secure_path (xfer->request) : xfer->request);
|
paulo@0
|
572
|
paulo@0
|
573 if (authorized)
|
paulo@0
|
574 need_free = TRUE;
|
paulo@0
|
575
|
paulo@0
|
576 open_path = gt_localize_request (xfer, s_path, &auth);
|
paulo@0
|
577
|
paulo@0
|
578 if (need_free || !open_path)
|
paulo@0
|
579 free (s_path);
|
paulo@0
|
580
|
paulo@0
|
581 if (authorized)
|
paulo@0
|
582 *authorized = auth;
|
paulo@0
|
583
|
paulo@0
|
584 /* we need a unix style path for authorization */
|
paulo@0
|
585 return open_path;
|
paulo@0
|
586 }
|
paulo@0
|
587
|
paulo@0
|
588 /*
|
paulo@0
|
589 * request is expected in the form:
|
paulo@0
|
590 * /shared/Fuck%20Me%20Hard.mpg
|
paulo@0
|
591 */
|
paulo@0
|
592 BOOL gt_transfer_set_request (GtTransfer *xfer, char *request)
|
paulo@0
|
593 {
|
paulo@0
|
594 #if 0
|
paulo@0
|
595 FileShare *file;
|
paulo@0
|
596 char *shared_path;
|
paulo@0
|
597 #endif
|
paulo@0
|
598
|
paulo@0
|
599 free (xfer->request);
|
paulo@0
|
600 xfer->request = NULL;
|
paulo@0
|
601
|
paulo@0
|
602 /* lets keep this sane shall we */
|
paulo@0
|
603 if (!request || *request != '/')
|
paulo@0
|
604 return FALSE;
|
paulo@0
|
605
|
paulo@0
|
606 xfer->request = STRDUP (request);
|
paulo@0
|
607 xfer->request_path = gt_url_decode (request); /* storing here for opt */
|
paulo@0
|
608
|
paulo@0
|
609 return TRUE;
|
paulo@0
|
610 }
|
paulo@0
|
611
|
paulo@0
|
612 /* attempts to open the requested file locally.
|
paulo@0
|
613 * NOTE: this handles verification */
|
paulo@0
|
614 FILE *gt_transfer_open_request (GtTransfer *xfer, int *code)
|
paulo@0
|
615 {
|
paulo@0
|
616 FILE *f;
|
paulo@0
|
617 char *shared_path;
|
paulo@0
|
618 char *full_path = NULL;
|
paulo@0
|
619 char *host_path;
|
paulo@0
|
620 int auth = FALSE;
|
paulo@0
|
621 int code_l = 200;
|
paulo@0
|
622
|
paulo@0
|
623 if (code)
|
paulo@0
|
624 *code = 404; /* Not Found */
|
paulo@0
|
625
|
paulo@0
|
626 if (!xfer || !xfer->request)
|
paulo@0
|
627 return NULL;
|
paulo@0
|
628
|
paulo@0
|
629 if (!(shared_path = localize_request (xfer, &auth)))
|
paulo@0
|
630 {
|
paulo@0
|
631 /*
|
paulo@0
|
632 * If we havent finished syncing shares, that may be why the
|
paulo@0
|
633 * request failed. If so, return 503 here.
|
paulo@0
|
634 */
|
paulo@0
|
635 if (!gt_share_local_sync_is_done () && code != NULL)
|
paulo@0
|
636 *code = 503;
|
paulo@0
|
637
|
paulo@0
|
638 return NULL;
|
paulo@0
|
639 }
|
paulo@0
|
640
|
paulo@0
|
641 /* needs more work for virtual requests */
|
paulo@0
|
642 #if 0
|
paulo@0
|
643 /* check to see if we matched a special OpenFT condition. If we did, the
|
paulo@0
|
644 * localized path is in the wrong order, so convert it (just to be
|
paulo@0
|
645 * converted back again...it's easier to maintain this way :) */
|
paulo@0
|
646 if (auth)
|
paulo@0
|
647 {
|
paulo@0
|
648 xfer->shared = FALSE;
|
paulo@0
|
649 full_path = file_unix_path (shared_path);
|
paulo@0
|
650 }
|
paulo@0
|
651 else
|
paulo@0
|
652 #endif
|
paulo@0
|
653 {
|
paulo@0
|
654 Share *share;
|
paulo@0
|
655 int reason = UPLOAD_AUTH_NOTSHARED;
|
paulo@0
|
656 upload_auth_t cond;
|
paulo@0
|
657
|
paulo@0
|
658 /*
|
paulo@0
|
659 * NOTE: the user string is not consistent with the one
|
paulo@0
|
660 * echoed through search results, which prefixes the IP
|
paulo@0
|
661 * with the GUID, if this node is firewalled.
|
paulo@0
|
662 */
|
paulo@0
|
663 if ((share = GT->share_lookup (GT, SHARE_LOOKUP_HPATH, shared_path)))
|
paulo@0
|
664 reason = GT->upload_auth (GT, net_ip_str (xfer->ip), share, &cond);
|
paulo@0
|
665
|
paulo@0
|
666 xfer->share_authd = share;
|
paulo@0
|
667
|
paulo@0
|
668 switch (reason)
|
paulo@0
|
669 {
|
paulo@0
|
670 case UPLOAD_AUTH_STALE:
|
paulo@0
|
671 code_l = 500; /* Internal Server Error */
|
paulo@0
|
672 break;
|
paulo@0
|
673 case UPLOAD_AUTH_NOTSHARED:
|
paulo@0
|
674 code_l = 404; /* Not Found */
|
paulo@0
|
675 break;
|
paulo@0
|
676 case UPLOAD_AUTH_ALLOW:
|
paulo@0
|
677 code_l = 200; /* OK */
|
paulo@0
|
678 xfer->open_path_size = share->size;
|
paulo@0
|
679 xfer->content_type = share->mime;
|
paulo@0
|
680 full_path = STRDUP (share->path);
|
paulo@0
|
681 break;
|
paulo@0
|
682 case UPLOAD_AUTH_MAX:
|
paulo@0
|
683 case UPLOAD_AUTH_MAX_PERUSER:
|
paulo@0
|
684 case UPLOAD_AUTH_HIDDEN:
|
paulo@0
|
685 default:
|
paulo@0
|
686 code_l = 503; /* Service Unavailable */
|
paulo@0
|
687 xfer->queue_pos = cond.queue_pos;
|
paulo@0
|
688 xfer->queue_ttl = cond.queue_ttl;
|
paulo@0
|
689 break;
|
paulo@0
|
690 }
|
paulo@0
|
691 }
|
paulo@0
|
692
|
paulo@0
|
693 if (code)
|
paulo@0
|
694 *code = code_l;
|
paulo@0
|
695
|
paulo@0
|
696 /* error of some kind, get out of here */
|
paulo@0
|
697 if (code_l != 200)
|
paulo@0
|
698 return NULL;
|
paulo@0
|
699
|
paulo@0
|
700 /* figure out the actual filename that we should be opening */
|
paulo@0
|
701 host_path = file_host_path (full_path);
|
paulo@0
|
702 free (full_path);
|
paulo@0
|
703
|
paulo@0
|
704 /* needs more work for virtual requests */
|
paulo@0
|
705 #if 0
|
paulo@0
|
706 /* complete the rest of the data required */
|
paulo@0
|
707 if (auth)
|
paulo@0
|
708 {
|
paulo@0
|
709 struct stat st;
|
paulo@0
|
710
|
paulo@0
|
711 if (!file_stat (host_path, &st))
|
paulo@0
|
712 {
|
paulo@0
|
713 free (host_path);
|
paulo@0
|
714 return NULL;
|
paulo@0
|
715 }
|
paulo@0
|
716
|
paulo@0
|
717 xfer->open_path_size = st.st_size;
|
paulo@0
|
718 xfer->content_type = mime_type (host_path);
|
paulo@0
|
719 }
|
paulo@0
|
720 #endif
|
paulo@0
|
721
|
paulo@0
|
722 if (!(f = fopen (host_path, "rb")))
|
paulo@0
|
723 {
|
paulo@0
|
724 *code = 500;
|
paulo@0
|
725 return NULL;
|
paulo@0
|
726 }
|
paulo@0
|
727
|
paulo@0
|
728 /* NOTE: gt_transfer_close will be responsible for freeing this now */
|
paulo@0
|
729 xfer->open_path = host_path;
|
paulo@0
|
730
|
paulo@0
|
731 if (code)
|
paulo@0
|
732 *code = 200; /* OK */
|
paulo@0
|
733
|
paulo@0
|
734 return f;
|
paulo@0
|
735 }
|
paulo@0
|
736
|
paulo@0
|
737 /*****************************************************************************/
|
paulo@0
|
738
|
paulo@0
|
739 /*
|
paulo@0
|
740 * The callbacks here are from within the HTTP system, centralized for
|
paulo@0
|
741 * maintainability.
|
paulo@0
|
742 */
|
paulo@0
|
743
|
paulo@0
|
744 /* report back the progress of this download chunk */
|
paulo@0
|
745 void gt_download (Chunk *chunk, unsigned char *segment, size_t len)
|
paulo@0
|
746 {
|
paulo@0
|
747 GT->chunk_write (GT, chunk->transfer, chunk, chunk->source,
|
paulo@0
|
748 segment, len);
|
paulo@0
|
749 }
|
paulo@0
|
750
|
paulo@0
|
751 /* report back the progress of this upload chunk */
|
paulo@0
|
752 void gt_upload (Chunk *chunk, unsigned char *segment, size_t len)
|
paulo@0
|
753 {
|
paulo@0
|
754 GT->chunk_write (GT, chunk->transfer, chunk, chunk->source,
|
paulo@0
|
755 segment, len);
|
paulo@0
|
756 }
|
paulo@0
|
757
|
paulo@0
|
758 void gt_transfer_write (GtTransfer *xfer, Chunk *chunk,
|
paulo@0
|
759 unsigned char *segment, size_t len)
|
paulo@0
|
760 {
|
paulo@0
|
761 /*
|
paulo@0
|
762 * Cap the data at the remaining size of the xfer. Note that this is
|
paulo@0
|
763 * the size of the HTTP request issued, _NOT_ the chunk size, which may
|
paulo@0
|
764 * have been altered by giFT in splitting up chunks. I think giFT
|
paulo@0
|
765 * handles that case properly, but we also have to guard against
|
paulo@0
|
766 * remaining_len becoming less than 0. Note that p->chunk_write
|
paulo@0
|
767 * will cancel the transfer if remaining_len goes to 0.
|
paulo@0
|
768 *
|
paulo@0
|
769 * TODO: remaining_len is off_t, make sure this is handled right wrt
|
paulo@0
|
770 * negative file sizes (do big files become negative sizes?)
|
paulo@0
|
771 */
|
paulo@0
|
772 if (len > xfer->remaining_len)
|
paulo@0
|
773 len = xfer->remaining_len;
|
paulo@0
|
774
|
paulo@0
|
775 xfer->remaining_len -= len;
|
paulo@0
|
776 (*xfer->callback) (chunk, segment, len);
|
paulo@0
|
777 }
|
paulo@0
|
778
|
paulo@0
|
779 /*****************************************************************************/
|
paulo@0
|
780
|
paulo@0
|
781 static BOOL throttle_suspend (Chunk *chunk, int s_opt, float factor)
|
paulo@0
|
782 {
|
paulo@0
|
783 GtTransfer *xfer;
|
paulo@0
|
784
|
paulo@0
|
785 if (!chunk)
|
paulo@0
|
786 return FALSE;
|
paulo@0
|
787
|
paulo@0
|
788 xfer = chunk->udata;
|
paulo@0
|
789
|
paulo@0
|
790 if (!xfer || !xfer->c)
|
paulo@0
|
791 {
|
paulo@0
|
792 GT->DBGFN (GT, "no connection found to suspend");
|
paulo@0
|
793 return FALSE;
|
paulo@0
|
794 }
|
paulo@0
|
795
|
paulo@0
|
796 input_suspend_all (xfer->c->fd);
|
paulo@0
|
797
|
paulo@0
|
798 if (factor)
|
paulo@0
|
799 net_sock_adj_buf (xfer->c->fd, s_opt, factor);
|
paulo@0
|
800
|
paulo@0
|
801 return TRUE;
|
paulo@0
|
802 }
|
paulo@0
|
803
|
paulo@0
|
804 static BOOL throttle_resume (Chunk *chunk, int s_opt, float factor)
|
paulo@0
|
805 {
|
paulo@0
|
806 GtTransfer *xfer = NULL;
|
paulo@0
|
807
|
paulo@0
|
808 if (!chunk)
|
paulo@0
|
809 return FALSE;
|
paulo@0
|
810
|
paulo@0
|
811 xfer = chunk->udata;
|
paulo@0
|
812
|
paulo@0
|
813 if (!xfer || !xfer->c)
|
paulo@0
|
814 {
|
paulo@0
|
815 GT->DBGFN (GT, "no connection found to resume");
|
paulo@0
|
816 return FALSE;
|
paulo@0
|
817 }
|
paulo@0
|
818
|
paulo@0
|
819 input_resume_all (xfer->c->fd);
|
paulo@0
|
820
|
paulo@0
|
821 #if 0
|
paulo@0
|
822 if (factor)
|
paulo@0
|
823 net_sock_adj_buf (xfer->c->fd, s_opt, factor);
|
paulo@0
|
824 #endif
|
paulo@0
|
825
|
paulo@0
|
826 return TRUE;
|
paulo@0
|
827 }
|
paulo@0
|
828
|
paulo@0
|
829 /*****************************************************************************/
|
paulo@0
|
830
|
paulo@0
|
831 void gt_download_cancel (Chunk *chunk, void *data)
|
paulo@0
|
832 {
|
paulo@0
|
833 gt_transfer_cancel (chunk, TRANSFER_DOWNLOAD);
|
paulo@0
|
834 }
|
paulo@0
|
835
|
paulo@0
|
836 /* cancel the transfer associate with the chunk giFT gave us */
|
paulo@0
|
837 void gt_upload_cancel (Chunk *chunk, void *data)
|
paulo@0
|
838 {
|
paulo@0
|
839 gt_transfer_cancel (chunk, TRANSFER_UPLOAD);
|
paulo@0
|
840 }
|
paulo@0
|
841
|
paulo@0
|
842 static int throttle_sopt (Transfer *transfer)
|
paulo@0
|
843 {
|
paulo@0
|
844 int sopt = 0;
|
paulo@0
|
845
|
paulo@0
|
846 switch (transfer_direction (transfer))
|
paulo@0
|
847 {
|
paulo@0
|
848 case TRANSFER_DOWNLOAD:
|
paulo@0
|
849 sopt = SO_RCVBUF;
|
paulo@0
|
850 break;
|
paulo@0
|
851 case TRANSFER_UPLOAD:
|
paulo@0
|
852 sopt = SO_SNDBUF;
|
paulo@0
|
853 break;
|
paulo@0
|
854 }
|
paulo@0
|
855
|
paulo@0
|
856 return sopt;
|
paulo@0
|
857 }
|
paulo@0
|
858
|
paulo@0
|
859 BOOL gt_chunk_suspend (Chunk *chunk, Transfer *transfer, void *data)
|
paulo@0
|
860 {
|
paulo@0
|
861 return throttle_suspend (chunk, throttle_sopt (transfer), 0.0);
|
paulo@0
|
862 }
|
paulo@0
|
863
|
paulo@0
|
864 BOOL gt_chunk_resume (Chunk *chunk, Transfer *transfer, void *data)
|
paulo@0
|
865 {
|
paulo@0
|
866 return throttle_resume (chunk, throttle_sopt (transfer), 0.0);
|
paulo@0
|
867 }
|