rev |
line source |
paulo@0
|
1 /*
|
paulo@0
|
2 * $Id: gt_xfer.c,v 1.103 2005/01/05 14:08:40 mkern Exp $
|
paulo@0
|
3 *
|
paulo@0
|
4 * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net)
|
paulo@0
|
5 *
|
paulo@0
|
6 * This program is free software; you can redistribute it and/or modify it
|
paulo@0
|
7 * under the terms of the GNU General Public License as published by the
|
paulo@0
|
8 * Free Software Foundation; either version 2, or (at your option) any
|
paulo@0
|
9 * later version.
|
paulo@0
|
10 *
|
paulo@0
|
11 * This program is distributed in the hope that it will be useful, but
|
paulo@0
|
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
|
paulo@0
|
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
paulo@0
|
14 * General Public License for more details.
|
paulo@0
|
15 */
|
paulo@0
|
16
|
paulo@0
|
17 #include "gt_gnutella.h"
|
paulo@0
|
18
|
paulo@0
|
19 #include "gt_xfer_obj.h"
|
paulo@0
|
20 #include "gt_xfer.h"
|
paulo@0
|
21 #include "gt_http_client.h"
|
paulo@0
|
22 #include "gt_http_server.h"
|
paulo@0
|
23 #include "gt_share.h"
|
paulo@0
|
24 #include "gt_share_file.h"
|
paulo@0
|
25 #include "gt_packet.h"
|
paulo@0
|
26 #include "gt_node.h"
|
paulo@0
|
27 #include "gt_node_list.h"
|
paulo@0
|
28 #include "gt_netorg.h"
|
paulo@0
|
29 #include "gt_connect.h"
|
paulo@0
|
30 #include "gt_bind.h"
|
paulo@0
|
31 #include "gt_utils.h"
|
paulo@0
|
32
|
paulo@0
|
33 #include "encoding/url.h"
|
paulo@0
|
34
|
paulo@0
|
35 #include "transfer/source.h"
|
paulo@0
|
36
|
paulo@0
|
37 /******************************************************************************/
|
paulo@0
|
38
|
paulo@0
|
39 /* maximum number of push connections in limbo each remote user */
|
paulo@0
|
40 #define PUSH_MAX_LIMBO gt_config_get_int("transfer/push_max_in_limbo=5")
|
paulo@0
|
41
|
paulo@0
|
42 /******************************************************************************/
|
paulo@0
|
43
|
paulo@0
|
44 /* an alternative to carrying the push TTL around in the source url */
|
paulo@0
|
45 #define PUSH_MAX_TTL 12
|
paulo@0
|
46
|
paulo@0
|
47 /* maximum time to keep push connections in "limbo" while awaiting giftd
|
paulo@0
|
48 * to reissue Chunks */
|
paulo@0
|
49 #define PUSH_LIMBO_TIMEOUT (4 * MINUTES)
|
paulo@0
|
50
|
paulo@0
|
51 /* how long to wait for a PUSH reply before timing out in order to free the
|
paulo@0
|
52 * Chunk */
|
paulo@0
|
53 #define PUSH_WAIT_INTERVAL (30 * SECONDS)
|
paulo@0
|
54
|
paulo@0
|
55 /* minimum interval between pushes */
|
paulo@0
|
56 #define PUSH_MIN_DEFER_TIME (30 * ESECONDS)
|
paulo@0
|
57
|
paulo@0
|
58 /* maximum amount of time a push will be forced to wait before being sent */
|
paulo@0
|
59 #define PUSH_MAX_DEFER_TIME (10 * EMINUTES)
|
paulo@0
|
60
|
paulo@0
|
61 /*****************************************************************************/
|
paulo@0
|
62
|
paulo@0
|
63 /* this stores information about an indirect ("pushed") download */
|
paulo@0
|
64 typedef struct gt_push_source
|
paulo@0
|
65 {
|
paulo@0
|
66 gt_guid_t *guid;
|
paulo@0
|
67 in_addr_t ip;
|
paulo@0
|
68 in_addr_t src_ip; /* whether this push was to a local source */
|
paulo@0
|
69 List *xfers; /* xfers for this source */
|
paulo@0
|
70 List *connections; /* connection for this source */
|
paulo@0
|
71 time_t last_sent; /* time of last push sent to this source */
|
paulo@0
|
72 double defer_time; /* min time to wait before sending another push
|
paulo@0
|
73 * doubles every push to PUSH_MAX_DEFER_TIME */
|
paulo@0
|
74 } GtPushSource;
|
paulo@0
|
75
|
paulo@0
|
76 /*****************************************************************************/
|
paulo@0
|
77
|
paulo@0
|
78 /* Maps guid->{list of unique GtPushSources} */
|
paulo@0
|
79 static Dataset *gt_push_requests;
|
paulo@0
|
80
|
paulo@0
|
81 /******************************************************************************/
|
paulo@0
|
82
|
paulo@0
|
83 static void push_source_reset_last_sent (GtPushSource *push_src);
|
paulo@0
|
84 static void push_source_set_last_sent (gt_guid_t *guid, in_addr_t ip);
|
paulo@0
|
85 static BOOL push_source_should_send (gt_guid_t *guid, in_addr_t ip);
|
paulo@0
|
86
|
paulo@0
|
87 /*****************************************************************************/
|
paulo@0
|
88
|
paulo@0
|
89 /*
|
paulo@0
|
90 * The source URL is stored on disk and could be outdated if the format has
|
paulo@0
|
91 * changed. This updates it with any changes when we first read it from
|
paulo@0
|
92 * the state file on startup.
|
paulo@0
|
93 */
|
paulo@0
|
94 static void replace_url (Source *src, GtSource *gt)
|
paulo@0
|
95 {
|
paulo@0
|
96 char *url;
|
paulo@0
|
97
|
paulo@0
|
98 if (!(url = gt_source_serialize (gt)))
|
paulo@0
|
99 return;
|
paulo@0
|
100
|
paulo@0
|
101 /* swap urls */
|
paulo@0
|
102 FREE (src->url);
|
paulo@0
|
103 src->url = url;
|
paulo@0
|
104 }
|
paulo@0
|
105
|
paulo@0
|
106 /******************************************************************************/
|
paulo@0
|
107
|
paulo@0
|
108 static FileShare *lookup_index (GtTransfer *xfer, char *request)
|
paulo@0
|
109 {
|
paulo@0
|
110 FileShare *file;
|
paulo@0
|
111 char *index_str;
|
paulo@0
|
112 char *filename;
|
paulo@0
|
113 char *decoded;
|
paulo@0
|
114 uint32_t index;
|
paulo@0
|
115
|
paulo@0
|
116 filename = request;
|
paulo@0
|
117 index_str = string_sep (&filename, "/");
|
paulo@0
|
118
|
paulo@0
|
119 if (!filename || !index_str)
|
paulo@0
|
120 return NULL;
|
paulo@0
|
121
|
paulo@0
|
122 index = ATOUL (index_str);
|
paulo@0
|
123
|
paulo@0
|
124 decoded = gt_url_decode (filename);
|
paulo@0
|
125 file = gt_share_local_lookup_by_index (index, decoded);
|
paulo@0
|
126
|
paulo@0
|
127 free (decoded);
|
paulo@0
|
128
|
paulo@0
|
129 /* the filename may or may not be url encoded */
|
paulo@0
|
130 if (!file)
|
paulo@0
|
131 file = gt_share_local_lookup_by_index (index, filename);
|
paulo@0
|
132
|
paulo@0
|
133 return file;
|
paulo@0
|
134 }
|
paulo@0
|
135
|
paulo@0
|
136 static FileShare *lookup_urns (GtTransfer *xfer, char *urns)
|
paulo@0
|
137 {
|
paulo@0
|
138 FileShare *file = NULL;
|
paulo@0
|
139 char *urn;
|
paulo@0
|
140
|
paulo@0
|
141 /*
|
paulo@0
|
142 * Try to lookup all urns provided in the header,
|
paulo@0
|
143 * until one is found.
|
paulo@0
|
144 */
|
paulo@0
|
145 while (!file && !string_isempty (urns))
|
paulo@0
|
146 {
|
paulo@0
|
147 urn = string_sep_set (&urns, ", ");
|
paulo@0
|
148 file = gt_share_local_lookup_by_urn (urn);
|
paulo@0
|
149 }
|
paulo@0
|
150
|
paulo@0
|
151 return file;
|
paulo@0
|
152 }
|
paulo@0
|
153
|
paulo@0
|
154 static FileShare *lookup_uri_res (GtTransfer *xfer, char *request)
|
paulo@0
|
155 {
|
paulo@0
|
156 FileShare *file = NULL;
|
paulo@0
|
157 char *resolver = NULL;
|
paulo@0
|
158 char *urn;
|
paulo@0
|
159
|
paulo@0
|
160 resolver = string_sep (&request, "?");
|
paulo@0
|
161 urn = string_sep (&request, " ");
|
paulo@0
|
162
|
paulo@0
|
163 if (resolver && !strcasecmp (resolver, "N2R"))
|
paulo@0
|
164 {
|
paulo@0
|
165 string_trim (request);
|
paulo@0
|
166 file = lookup_urns (xfer, urn);
|
paulo@0
|
167 }
|
paulo@0
|
168
|
paulo@0
|
169 if (file && HTTP_DEBUG)
|
paulo@0
|
170 GT->dbg (GT, "file=%s", share_get_hpath (file));
|
paulo@0
|
171
|
paulo@0
|
172 return file;
|
paulo@0
|
173 }
|
paulo@0
|
174
|
paulo@0
|
175 static Share *lookup_hpath (char *namespace, GtTransfer *xfer, char *request)
|
paulo@0
|
176 {
|
paulo@0
|
177 char *hpath;
|
paulo@0
|
178 Share *share;
|
paulo@0
|
179
|
paulo@0
|
180 /*
|
paulo@0
|
181 * Reconstruct the hpath
|
paulo@0
|
182 */
|
paulo@0
|
183 if (!(hpath = stringf_dup ("/%s/%s", namespace, request)))
|
paulo@0
|
184 return NULL;
|
paulo@0
|
185
|
paulo@0
|
186 if (HTTP_DEBUG)
|
paulo@0
|
187 GT->dbg (GT, "request by hpath: %s", hpath);
|
paulo@0
|
188
|
paulo@0
|
189 share = GT->share_lookup (GT, SHARE_LOOKUP_HPATH, hpath);
|
paulo@0
|
190 free (hpath);
|
paulo@0
|
191
|
paulo@0
|
192 return share;
|
paulo@0
|
193 }
|
paulo@0
|
194
|
paulo@0
|
195 /* Take a request for a file, i.e. everything after GET in:
|
paulo@0
|
196 *
|
paulo@0
|
197 * "GET /get/1279/filename.mp3"
|
paulo@0
|
198 *
|
paulo@0
|
199 * and convert it to a localized path to a file.
|
paulo@0
|
200 *
|
paulo@0
|
201 * Path has been "secured" already if necessary.
|
paulo@0
|
202 *
|
paulo@0
|
203 * The path returned must be static.
|
paulo@0
|
204 * TODO: this interface is a bit bizarre */
|
paulo@0
|
205 char *gt_localize_request (GtTransfer *xfer, char *s_path, int *authorized)
|
paulo@0
|
206 {
|
paulo@0
|
207 static char open_path[PATH_MAX];
|
paulo@0
|
208 char *namespace;
|
paulo@0
|
209 char *path, *path0;
|
paulo@0
|
210 char *content_urns;
|
paulo@0
|
211 FileShare *file;
|
paulo@0
|
212
|
paulo@0
|
213 /* TODO: use authorized for Browse Host (BH) requests */
|
paulo@0
|
214 if (!STRCMP (s_path, "/"))
|
paulo@0
|
215 {
|
paulo@0
|
216 /* *authorized = TRUE; */
|
paulo@0
|
217 if (HTTP_DEBUG)
|
paulo@0
|
218 GT->DBGFN (GT, "received unimplemented Browse Host request");
|
paulo@0
|
219
|
paulo@0
|
220 return NULL;
|
paulo@0
|
221 }
|
paulo@0
|
222
|
paulo@0
|
223 if (authorized)
|
paulo@0
|
224 *authorized = FALSE;
|
paulo@0
|
225
|
paulo@0
|
226 if (!(path0 = path = STRDUP (s_path)))
|
paulo@0
|
227 return NULL;
|
paulo@0
|
228
|
paulo@0
|
229 if (HTTP_DEBUG)
|
paulo@0
|
230 GT->dbg (GT, "path=%s", path);
|
paulo@0
|
231
|
paulo@0
|
232 /* get rid of leading slash */
|
paulo@0
|
233 string_sep (&path, "/");
|
paulo@0
|
234 namespace = string_sep (&path, "/");
|
paulo@0
|
235
|
paulo@0
|
236 if (!namespace || !path)
|
paulo@0
|
237 {
|
paulo@0
|
238 GT->DBGFN (GT, "null namespace or path: %s %s\n", namespace, path);
|
paulo@0
|
239 free (path0);
|
paulo@0
|
240 return NULL;
|
paulo@0
|
241 }
|
paulo@0
|
242
|
paulo@0
|
243 /*
|
paulo@0
|
244 * If the client supplied "X-Gnutella-Content-URN: ", lookup
|
paulo@0
|
245 * by that instead of the request.
|
paulo@0
|
246 */
|
paulo@0
|
247 content_urns = dataset_lookupstr (xfer->header, "x-gnutella-content-urn");
|
paulo@0
|
248
|
paulo@0
|
249 if (content_urns)
|
paulo@0
|
250 file = lookup_urns (xfer, content_urns);
|
paulo@0
|
251 else if (!strcasecmp (namespace, "get"))
|
paulo@0
|
252 file = lookup_index (xfer, path);
|
paulo@0
|
253 else if (!strcasecmp (namespace, "uri-res"))
|
paulo@0
|
254 file = lookup_uri_res (xfer, path);
|
paulo@0
|
255 else
|
paulo@0
|
256 file = lookup_hpath (namespace, xfer, path);
|
paulo@0
|
257
|
paulo@0
|
258 /*
|
paulo@0
|
259 * Set xfer->content_urn [which replies with 'X-Gnutella-Content-URN']
|
paulo@0
|
260 * to a comma-separated list of all URNs for this file.
|
paulo@0
|
261 */
|
paulo@0
|
262 xfer->content_urns = gt_share_local_get_urns (file);
|
paulo@0
|
263
|
paulo@0
|
264 if (!file)
|
paulo@0
|
265 {
|
paulo@0
|
266 if (HTTP_DEBUG)
|
paulo@0
|
267 GT->DBGFN (GT, "bad request: /%s/%s", namespace, path);
|
paulo@0
|
268
|
paulo@0
|
269 free (path0);
|
paulo@0
|
270 return NULL;
|
paulo@0
|
271 }
|
paulo@0
|
272
|
paulo@0
|
273 free (path0);
|
paulo@0
|
274
|
paulo@0
|
275 if (!share_complete (file))
|
paulo@0
|
276 return NULL;
|
paulo@0
|
277
|
paulo@0
|
278 /* argh, need to return static data */
|
paulo@0
|
279 snprintf (open_path, sizeof (open_path) - 1, "%s", share_get_hpath (file));
|
paulo@0
|
280
|
paulo@0
|
281 /* try to fill in the hash */
|
paulo@0
|
282 xfer->hash = share_dsp_hash (file, "SHA1");
|
paulo@0
|
283
|
paulo@0
|
284 return open_path;
|
paulo@0
|
285 }
|
paulo@0
|
286
|
paulo@0
|
287 /******************************************************************************/
|
paulo@0
|
288
|
paulo@0
|
289 static char *index_request (char *request, size_t size,
|
paulo@0
|
290 uint32_t index, const char *filename)
|
paulo@0
|
291 {
|
paulo@0
|
292 /*
|
paulo@0
|
293 * The filename may not have ever been set. Fail the request in
|
paulo@0
|
294 * this case.
|
paulo@0
|
295 */
|
paulo@0
|
296 if (!filename || string_isempty (filename))
|
paulo@0
|
297 return NULL;
|
paulo@0
|
298
|
paulo@0
|
299 /*
|
paulo@0
|
300 * Filename is encoded, we don't support sending unecoded requests
|
paulo@0
|
301 * anymore. NOTE: filename doesnt have leading '/' here, that may change
|
paulo@0
|
302 */
|
paulo@0
|
303 snprintf (request, size - 1, "/get/%u/%s", index, filename);
|
paulo@0
|
304 return request;
|
paulo@0
|
305 }
|
paulo@0
|
306
|
paulo@0
|
307 /*
|
paulo@0
|
308 * Setup a request string. Try request-by-hash (/uri-res/N2R?urn:sha1:..),
|
paulo@0
|
309 * but if there are any problems, fallback to a "/get/<index>/<filename>"
|
paulo@0
|
310 * request.
|
paulo@0
|
311 */
|
paulo@0
|
312 static char *request_str (Source *source, uint32_t index, char *filename)
|
paulo@0
|
313 {
|
paulo@0
|
314 static char request[RW_BUFFER];
|
paulo@0
|
315 char *hash = source->hash;
|
paulo@0
|
316 GtSource *gt;
|
paulo@0
|
317
|
paulo@0
|
318 gt = source->udata;
|
paulo@0
|
319 assert (gt != NULL);
|
paulo@0
|
320
|
paulo@0
|
321 /*
|
paulo@0
|
322 * Do a uri-res request unless one has failed already or
|
paulo@0
|
323 * if we have no filename and thus no choice but to use the hash.
|
paulo@0
|
324 * (doesn't happen currently but will for download-mesh sources
|
paulo@0
|
325 * that only have the hash and no filename)
|
paulo@0
|
326 */
|
paulo@0
|
327 if (hash && (!gt->uri_res_failed || string_isempty (filename)))
|
paulo@0
|
328 {
|
paulo@0
|
329 char *str0, *str;
|
paulo@0
|
330
|
paulo@0
|
331 if (!(str0 = STRDUP (hash)))
|
paulo@0
|
332 return index_request (request, sizeof (request), index, filename);
|
paulo@0
|
333
|
paulo@0
|
334 str = str0;
|
paulo@0
|
335 string_sep (&str, ":");
|
paulo@0
|
336
|
paulo@0
|
337 /* hashes are canonically uppercase on the gnet */
|
paulo@0
|
338 string_upper (str);
|
paulo@0
|
339
|
paulo@0
|
340 if (str)
|
paulo@0
|
341 {
|
paulo@0
|
342 snprintf (request, sizeof (request) - 1,
|
paulo@0
|
343 "/uri-res/N2R?urn:sha1:%s", str);
|
paulo@0
|
344 free (str0);
|
paulo@0
|
345 return request;
|
paulo@0
|
346 }
|
paulo@0
|
347
|
paulo@0
|
348 free (str0);
|
paulo@0
|
349 }
|
paulo@0
|
350
|
paulo@0
|
351 return index_request (request, sizeof (request), index, filename);
|
paulo@0
|
352 }
|
paulo@0
|
353
|
paulo@0
|
354 /*****************************************************************************/
|
paulo@0
|
355 /* PUSH HANDLING */
|
paulo@0
|
356
|
paulo@0
|
357 /*
|
paulo@0
|
358 * This code has to deal with some tricky race conditions involving
|
paulo@0
|
359 * chunk timeouts and pushes.
|
paulo@0
|
360 */
|
paulo@0
|
361
|
paulo@0
|
362 static GtPushSource *gt_push_source_new (gt_guid_t *guid, in_addr_t ip,
|
paulo@0
|
363 in_addr_t src_ip)
|
paulo@0
|
364 {
|
paulo@0
|
365 GtPushSource *src;
|
paulo@0
|
366
|
paulo@0
|
367 if (!(src = MALLOC (sizeof (GtPushSource))))
|
paulo@0
|
368 return NULL;
|
paulo@0
|
369
|
paulo@0
|
370 src->guid = gt_guid_dup (guid);
|
paulo@0
|
371 src->ip = ip;
|
paulo@0
|
372 src->src_ip = src_ip;
|
paulo@0
|
373 src->xfers = NULL;
|
paulo@0
|
374 src->connections = NULL;
|
paulo@0
|
375
|
paulo@0
|
376 push_source_reset_last_sent (src);
|
paulo@0
|
377
|
paulo@0
|
378 return src;
|
paulo@0
|
379 }
|
paulo@0
|
380
|
paulo@0
|
381 static void gt_push_source_free (GtPushSource *src)
|
paulo@0
|
382 {
|
paulo@0
|
383 if (!src)
|
paulo@0
|
384 return;
|
paulo@0
|
385
|
paulo@0
|
386 assert (src->xfers == NULL);
|
paulo@0
|
387 assert (src->connections == NULL);
|
paulo@0
|
388
|
paulo@0
|
389 free (src->guid);
|
paulo@0
|
390 free (src);
|
paulo@0
|
391 }
|
paulo@0
|
392
|
paulo@0
|
393 /*****************************************************************************/
|
paulo@0
|
394
|
paulo@0
|
395 /* TODO: break this into two parts, first part looks in the
|
paulo@0
|
396 * list for matching ip. If none is found, look for
|
paulo@0
|
397 * a firewalled (local ip) push source. */
|
paulo@0
|
398 static int find_ip (GtPushSource *src, in_addr_t *ip)
|
paulo@0
|
399 {
|
paulo@0
|
400 /* If the source is a local IP address behind a non-local one,
|
paulo@0
|
401 * authorize by just the client guid. Otherwise, use the IP. */
|
paulo@0
|
402 if (gt_is_local_ip (src->ip, src->src_ip) || src->ip == *ip)
|
paulo@0
|
403 return 0;
|
paulo@0
|
404
|
paulo@0
|
405 return -1;
|
paulo@0
|
406 }
|
paulo@0
|
407
|
paulo@0
|
408 static List *lookup_source_list (gt_guid_t *guid)
|
paulo@0
|
409 {
|
paulo@0
|
410 List *src_list;
|
paulo@0
|
411
|
paulo@0
|
412 if (!(src_list = dataset_lookup (gt_push_requests, guid, 16)))
|
paulo@0
|
413 return NULL;
|
paulo@0
|
414
|
paulo@0
|
415 return src_list;
|
paulo@0
|
416 }
|
paulo@0
|
417
|
paulo@0
|
418 static GtPushSource *push_source_lookup (gt_guid_t *guid, in_addr_t ip)
|
paulo@0
|
419 {
|
paulo@0
|
420 List *requests;
|
paulo@0
|
421 List *list;
|
paulo@0
|
422
|
paulo@0
|
423 if (!(requests = lookup_source_list (guid)))
|
paulo@0
|
424 return NULL;
|
paulo@0
|
425
|
paulo@0
|
426 list = list_find_custom (requests, &ip, (ListForeachFunc)find_ip);
|
paulo@0
|
427 return list_nth_data (list, 0);
|
paulo@0
|
428 }
|
paulo@0
|
429
|
paulo@0
|
430 /*****************************************************************************/
|
paulo@0
|
431
|
paulo@0
|
432 static void insert_source_list (gt_guid_t *guid, List *src_list)
|
paulo@0
|
433 {
|
paulo@0
|
434 if (!gt_push_requests)
|
paulo@0
|
435 gt_push_requests = dataset_new (DATASET_HASH);
|
paulo@0
|
436
|
paulo@0
|
437 dataset_insert (>_push_requests, guid, 16, src_list, 0);
|
paulo@0
|
438 }
|
paulo@0
|
439
|
paulo@0
|
440 static void add_push_source (List *pushes, gt_guid_t *guid, in_addr_t ip,
|
paulo@0
|
441 in_addr_t src_ip)
|
paulo@0
|
442 {
|
paulo@0
|
443 GtPushSource *src;
|
paulo@0
|
444 List *old_list;
|
paulo@0
|
445
|
paulo@0
|
446 if (!(src = gt_push_source_new (guid, ip, src_ip)))
|
paulo@0
|
447 return;
|
paulo@0
|
448
|
paulo@0
|
449 if ((old_list = list_find_custom (pushes, &ip, (ListForeachFunc)find_ip)))
|
paulo@0
|
450 {
|
paulo@0
|
451 /* push source is already there */
|
paulo@0
|
452 gt_push_source_free (src);
|
paulo@0
|
453 return;
|
paulo@0
|
454 }
|
paulo@0
|
455
|
paulo@0
|
456 pushes = list_prepend (pushes, src);
|
paulo@0
|
457 insert_source_list (guid, pushes);
|
paulo@0
|
458 }
|
paulo@0
|
459
|
paulo@0
|
460 void gt_push_source_add (gt_guid_t *guid, in_addr_t ip, in_addr_t src_ip)
|
paulo@0
|
461 {
|
paulo@0
|
462 List *pushes;
|
paulo@0
|
463
|
paulo@0
|
464 pushes = lookup_source_list (guid);
|
paulo@0
|
465 add_push_source (pushes, guid, ip, src_ip);
|
paulo@0
|
466 }
|
paulo@0
|
467
|
paulo@0
|
468 /*****************************************************************************/
|
paulo@0
|
469 /* Timing controls for push requests */
|
paulo@0
|
470
|
paulo@0
|
471 static void push_source_reset_last_sent (GtPushSource *push_src)
|
paulo@0
|
472 {
|
paulo@0
|
473 push_src->last_sent = gt_uptime (); /* wrong */
|
paulo@0
|
474 push_src->defer_time = 0.0;
|
paulo@0
|
475 }
|
paulo@0
|
476
|
paulo@0
|
477 static void push_source_set_last_sent (gt_guid_t *guid, in_addr_t ip)
|
paulo@0
|
478 {
|
paulo@0
|
479 GtPushSource *src;
|
paulo@0
|
480
|
paulo@0
|
481 if (!(src = push_source_lookup (guid, ip)))
|
paulo@0
|
482 return;
|
paulo@0
|
483
|
paulo@0
|
484 time (&src->last_sent);
|
paulo@0
|
485 }
|
paulo@0
|
486
|
paulo@0
|
487 static BOOL push_source_should_send (gt_guid_t *guid, in_addr_t ip)
|
paulo@0
|
488 {
|
paulo@0
|
489 GtPushSource *src;
|
paulo@0
|
490 double deferred;
|
paulo@0
|
491 time_t now;
|
paulo@0
|
492
|
paulo@0
|
493 time (&now);
|
paulo@0
|
494
|
paulo@0
|
495 if (!(src = push_source_lookup (guid, ip)))
|
paulo@0
|
496 return FALSE;
|
paulo@0
|
497
|
paulo@0
|
498 deferred = difftime (now, src->last_sent);
|
paulo@0
|
499
|
paulo@0
|
500 /* randomize the defer time a bit in order to not send pushes for all
|
paulo@0
|
501 * downloads at once */
|
paulo@0
|
502 if (deferred < src->defer_time + -10.0 + 20.0 * rand() / (RAND_MAX + 1.0))
|
paulo@0
|
503 return FALSE;
|
paulo@0
|
504
|
paulo@0
|
505 /*
|
paulo@0
|
506 * Double the defer time interval (the minimum time between sent
|
paulo@0
|
507 * pushes for this source).
|
paulo@0
|
508 */
|
paulo@0
|
509 src->defer_time *= 2;
|
paulo@0
|
510 if (src->defer_time >= PUSH_MAX_DEFER_TIME)
|
paulo@0
|
511 src->defer_time = PUSH_MAX_DEFER_TIME;
|
paulo@0
|
512
|
paulo@0
|
513 if (src->defer_time == 0)
|
paulo@0
|
514 src->defer_time = PUSH_MIN_DEFER_TIME;
|
paulo@0
|
515
|
paulo@0
|
516 return TRUE;
|
paulo@0
|
517 }
|
paulo@0
|
518
|
paulo@0
|
519 /*****************************************************************************/
|
paulo@0
|
520
|
paulo@0
|
521 static void flush_inputs (TCPC *c)
|
paulo@0
|
522 {
|
paulo@0
|
523 int ret;
|
paulo@0
|
524
|
paulo@0
|
525 assert (c->fd >= 0);
|
paulo@0
|
526
|
paulo@0
|
527 /* queued writes arent used by the HTTP system in this plugin,
|
paulo@0
|
528 * so this should always be true */
|
paulo@0
|
529 ret = tcp_flush (c, TRUE);
|
paulo@0
|
530 assert (ret == 0);
|
paulo@0
|
531
|
paulo@0
|
532 input_remove_all (c->fd);
|
paulo@0
|
533 }
|
paulo@0
|
534
|
paulo@0
|
535 static void continue_download (GtPushSource *push_src, GtTransfer *xfer,
|
paulo@0
|
536 TCPC *c)
|
paulo@0
|
537 {
|
paulo@0
|
538 Chunk *chunk;
|
paulo@0
|
539
|
paulo@0
|
540 chunk = gt_transfer_get_chunk (xfer);
|
paulo@0
|
541
|
paulo@0
|
542 /* remove all previous inputs */
|
paulo@0
|
543 flush_inputs (c);
|
paulo@0
|
544
|
paulo@0
|
545 /* HACK: remove the detach timeout */
|
paulo@0
|
546 timer_remove_zero (&xfer->detach_timer);
|
paulo@0
|
547
|
paulo@0
|
548 /* connect the TCPC and GtTransfer */
|
paulo@0
|
549 gt_transfer_set_tcpc (xfer, c);
|
paulo@0
|
550
|
paulo@0
|
551 /* update the IP and port for placing in Host: */
|
paulo@0
|
552 peer_addr (c->fd, &xfer->ip, &xfer->port);
|
paulo@0
|
553
|
paulo@0
|
554 /* the connection and the chunk have met up */
|
paulo@0
|
555 gt_transfer_status (xfer, SOURCE_WAITING, "Received GIV response");
|
paulo@0
|
556
|
paulo@0
|
557 if (HTTP_DEBUG)
|
paulo@0
|
558 GT->DBGSOCK (GT, c, "Continuing download for %s", xfer->request);
|
paulo@0
|
559
|
paulo@0
|
560 input_add (c->fd, xfer, INPUT_WRITE,
|
paulo@0
|
561 (InputCallback)gt_http_client_start, TIMEOUT_DEF);
|
paulo@0
|
562 }
|
paulo@0
|
563
|
paulo@0
|
564 static void reset_conn (int fd, input_id id, TCPC *c)
|
paulo@0
|
565 {
|
paulo@0
|
566 /*
|
paulo@0
|
567 * We should only get here if some data was sent, or if it timed out. In
|
paulo@0
|
568 * which case we should close this connection, because it shouldn't be
|
paulo@0
|
569 * sending anything.
|
paulo@0
|
570 */
|
paulo@0
|
571 if (HTTP_DEBUG)
|
paulo@0
|
572 {
|
paulo@0
|
573 if (fd == -1)
|
paulo@0
|
574 GT->DBGSOCK (GT, c, "connection timed out");
|
paulo@0
|
575 else
|
paulo@0
|
576 GT->DBGSOCK (GT, c, "connection closed or sent invalid data");
|
paulo@0
|
577 }
|
paulo@0
|
578
|
paulo@0
|
579 gt_push_source_remove_conn (c);
|
paulo@0
|
580 tcp_close (c);
|
paulo@0
|
581 }
|
paulo@0
|
582
|
paulo@0
|
583 static void store_conn (GtPushSource *src, TCPC *c)
|
paulo@0
|
584 {
|
paulo@0
|
585 flush_inputs (c);
|
paulo@0
|
586
|
paulo@0
|
587 input_add (c->fd, c, INPUT_READ,
|
paulo@0
|
588 (InputCallback)reset_conn, PUSH_LIMBO_TIMEOUT);
|
paulo@0
|
589
|
paulo@0
|
590 assert (!list_find (src->connections, c));
|
paulo@0
|
591 src->connections = list_prepend (src->connections, c);
|
paulo@0
|
592
|
paulo@0
|
593 if (HTTP_DEBUG)
|
paulo@0
|
594 GT->DBGSOCK (GT, c, "storing connection");
|
paulo@0
|
595 }
|
paulo@0
|
596
|
paulo@0
|
597 BOOL gt_push_source_add_conn (gt_guid_t *guid, in_addr_t ip, TCPC *c)
|
paulo@0
|
598 {
|
paulo@0
|
599 GtTransfer *xfer;
|
paulo@0
|
600 GtPushSource *push_src;
|
paulo@0
|
601
|
paulo@0
|
602 if (!(push_src = push_source_lookup (guid, ip)))
|
paulo@0
|
603 {
|
paulo@0
|
604 if (HTTP_DEBUG)
|
paulo@0
|
605 {
|
paulo@0
|
606 GT->err (GT, "couldn't find push source %s:[%s]",
|
paulo@0
|
607 gt_guid_str (guid), net_ip_str (ip));
|
paulo@0
|
608 }
|
paulo@0
|
609
|
paulo@0
|
610 tcp_close (c);
|
paulo@0
|
611 return FALSE;
|
paulo@0
|
612 }
|
paulo@0
|
613
|
paulo@0
|
614 /*
|
paulo@0
|
615 * Don't allow too many connections in flight from the same remote user.
|
paulo@0
|
616 */
|
paulo@0
|
617 if (list_length (push_src->connections) >= PUSH_MAX_LIMBO)
|
paulo@0
|
618 {
|
paulo@0
|
619 if (HTTP_DEBUG)
|
paulo@0
|
620 {
|
paulo@0
|
621 GT->DBGSOCK (GT, c, "too many push connections from %s, closing",
|
paulo@0
|
622 gt_guid_str (guid));
|
paulo@0
|
623 }
|
paulo@0
|
624
|
paulo@0
|
625 tcp_close (c);
|
paulo@0
|
626 return FALSE;
|
paulo@0
|
627 }
|
paulo@0
|
628
|
paulo@0
|
629 /*
|
paulo@0
|
630 * Since we now know this push source is alive, reset the push send
|
paulo@0
|
631 * tracking time: in case the connection is lost, we'll resend the push
|
paulo@0
|
632 * right away instead of waiting.
|
paulo@0
|
633 */
|
paulo@0
|
634 push_source_reset_last_sent (push_src);
|
paulo@0
|
635
|
paulo@0
|
636 /*
|
paulo@0
|
637 * Store the connection if there are no GtTransfer requests from
|
paulo@0
|
638 * giFT at the moment.
|
paulo@0
|
639 */
|
paulo@0
|
640 if (!push_src->xfers)
|
paulo@0
|
641 {
|
paulo@0
|
642 store_conn (push_src, c);
|
paulo@0
|
643 return FALSE;
|
paulo@0
|
644 }
|
paulo@0
|
645
|
paulo@0
|
646 xfer = list_nth_data (push_src->xfers, 0);
|
paulo@0
|
647 push_src->xfers = list_remove (push_src->xfers, xfer);
|
paulo@0
|
648
|
paulo@0
|
649 continue_download (push_src, xfer, c);
|
paulo@0
|
650 return TRUE;
|
paulo@0
|
651 }
|
paulo@0
|
652
|
paulo@0
|
653 /* return TRUE if there's a connection residing on this push source */
|
paulo@0
|
654 static BOOL push_source_lookup_conn (gt_guid_t *guid, in_addr_t ip)
|
paulo@0
|
655 {
|
paulo@0
|
656 GtPushSource *push_src;
|
paulo@0
|
657
|
paulo@0
|
658 if (!(push_src = push_source_lookup (guid, ip)))
|
paulo@0
|
659 return FALSE;
|
paulo@0
|
660
|
paulo@0
|
661 if (push_src->connections)
|
paulo@0
|
662 {
|
paulo@0
|
663 if (HTTP_DEBUG)
|
paulo@0
|
664 GT->DBGFN (GT, "found push connection for %s", net_ip_str (ip));
|
paulo@0
|
665
|
paulo@0
|
666 return TRUE;
|
paulo@0
|
667 }
|
paulo@0
|
668
|
paulo@0
|
669 return FALSE;
|
paulo@0
|
670 }
|
paulo@0
|
671
|
paulo@0
|
672 static void store_xfer (GtPushSource *src, GtTransfer *xfer)
|
paulo@0
|
673 {
|
paulo@0
|
674 assert (!list_find (src->xfers, xfer));
|
paulo@0
|
675 src->xfers = list_prepend (src->xfers, xfer);
|
paulo@0
|
676 }
|
paulo@0
|
677
|
paulo@0
|
678 BOOL gt_push_source_add_xfer (gt_guid_t *guid, in_addr_t ip,
|
paulo@0
|
679 in_addr_t src_ip, GtTransfer *xfer)
|
paulo@0
|
680 {
|
paulo@0
|
681 TCPC *c;
|
paulo@0
|
682 GtPushSource *push_src;
|
paulo@0
|
683
|
paulo@0
|
684 assert (xfer != NULL);
|
paulo@0
|
685
|
paulo@0
|
686 /* create the source if it doesn't exist already */
|
paulo@0
|
687 gt_push_source_add (guid, ip, src_ip);
|
paulo@0
|
688
|
paulo@0
|
689 if (!(push_src = push_source_lookup (guid, ip)))
|
paulo@0
|
690 {
|
paulo@0
|
691 if (HTTP_DEBUG)
|
paulo@0
|
692 {
|
paulo@0
|
693 GT->err (GT, "couldn't find push source (%s:[%s]) for chunk %s",
|
paulo@0
|
694 gt_guid_str (guid), net_ip_str (ip), xfer->request);
|
paulo@0
|
695 }
|
paulo@0
|
696
|
paulo@0
|
697 return FALSE;
|
paulo@0
|
698 }
|
paulo@0
|
699
|
paulo@0
|
700 /*
|
paulo@0
|
701 * Store the GtTransfer if there are no connections to service it
|
paulo@0
|
702 * at the moment.
|
paulo@0
|
703 */
|
paulo@0
|
704 if (!push_src->connections)
|
paulo@0
|
705 {
|
paulo@0
|
706 store_xfer (push_src, xfer);
|
paulo@0
|
707 return FALSE;
|
paulo@0
|
708 }
|
paulo@0
|
709
|
paulo@0
|
710 c = list_nth_data (push_src->connections, 0);
|
paulo@0
|
711 push_src->connections = list_remove (push_src->connections, c);
|
paulo@0
|
712
|
paulo@0
|
713 continue_download (push_src, xfer, c);
|
paulo@0
|
714 return TRUE;
|
paulo@0
|
715 }
|
paulo@0
|
716
|
paulo@0
|
717 /*****************************************************************************/
|
paulo@0
|
718
|
paulo@0
|
719 static BOOL remove_xfer (GtPushSource *src, GtTransfer *xfer)
|
paulo@0
|
720 {
|
paulo@0
|
721 src->xfers = list_remove (src->xfers, xfer);
|
paulo@0
|
722 return FALSE;
|
paulo@0
|
723 }
|
paulo@0
|
724
|
paulo@0
|
725 static void remove_xfer_list (ds_data_t *key, ds_data_t *value,
|
paulo@0
|
726 GtTransfer *xfer)
|
paulo@0
|
727 {
|
paulo@0
|
728 List *src_list = value->data;
|
paulo@0
|
729
|
paulo@0
|
730 list_foreach (src_list, (ListForeachFunc)remove_xfer, xfer);
|
paulo@0
|
731 }
|
paulo@0
|
732
|
paulo@0
|
733 /*
|
paulo@0
|
734 * The chunk is being cancelled, so remove it from being tracked.
|
paulo@0
|
735 *
|
paulo@0
|
736 * After this, if the push recipient connects, we will have to wait
|
paulo@0
|
737 * for another chunk timeout before transmitting.
|
paulo@0
|
738 */
|
paulo@0
|
739 void gt_push_source_remove_xfer (GtTransfer *xfer)
|
paulo@0
|
740 {
|
paulo@0
|
741 if (!xfer)
|
paulo@0
|
742 return;
|
paulo@0
|
743
|
paulo@0
|
744 dataset_foreach (gt_push_requests, DS_FOREACH(remove_xfer_list), xfer);
|
paulo@0
|
745 }
|
paulo@0
|
746
|
paulo@0
|
747 static BOOL remove_conn (GtPushSource *src, TCPC *c)
|
paulo@0
|
748 {
|
paulo@0
|
749 src->connections = list_remove (src->connections, c);
|
paulo@0
|
750 return FALSE;
|
paulo@0
|
751 }
|
paulo@0
|
752
|
paulo@0
|
753 static void remove_conn_list (ds_data_t *key, ds_data_t *value, TCPC *c)
|
paulo@0
|
754 {
|
paulo@0
|
755 List *src_list = value->data;
|
paulo@0
|
756
|
paulo@0
|
757 list_foreach (src_list, (ListForeachFunc)remove_conn, c);
|
paulo@0
|
758 }
|
paulo@0
|
759
|
paulo@0
|
760 /*
|
paulo@0
|
761 * The connection from this push download closed
|
paulo@0
|
762 */
|
paulo@0
|
763 void gt_push_source_remove_conn (TCPC *c)
|
paulo@0
|
764 {
|
paulo@0
|
765 if (!c)
|
paulo@0
|
766 return;
|
paulo@0
|
767
|
paulo@0
|
768 dataset_foreach (gt_push_requests, DS_FOREACH(remove_conn_list), c);
|
paulo@0
|
769 }
|
paulo@0
|
770
|
paulo@0
|
771 static BOOL cleanup_xfer (GtTransfer *xfer, void *udata)
|
paulo@0
|
772 {
|
paulo@0
|
773 gt_push_source_remove_xfer (xfer);
|
paulo@0
|
774 return TRUE;
|
paulo@0
|
775 }
|
paulo@0
|
776
|
paulo@0
|
777 static BOOL cleanup_conn (TCPC *c, void *udata)
|
paulo@0
|
778 {
|
paulo@0
|
779 gt_push_source_remove_conn (c);
|
paulo@0
|
780 tcp_close (c);
|
paulo@0
|
781 return TRUE;
|
paulo@0
|
782 }
|
paulo@0
|
783
|
paulo@0
|
784 static void remove_push_source (GtPushSource *src)
|
paulo@0
|
785 {
|
paulo@0
|
786 List *src_list;
|
paulo@0
|
787
|
paulo@0
|
788 src_list = lookup_source_list (src->guid);
|
paulo@0
|
789 src_list = list_remove (src_list, src);
|
paulo@0
|
790
|
paulo@0
|
791 insert_source_list (src->guid, src_list);
|
paulo@0
|
792 }
|
paulo@0
|
793
|
paulo@0
|
794 void gt_push_source_remove (gt_guid_t *guid, in_addr_t ip, in_addr_t src_ip)
|
paulo@0
|
795 {
|
paulo@0
|
796 GtPushSource *src;
|
paulo@0
|
797
|
paulo@0
|
798 if (!(src = push_source_lookup (guid, ip)))
|
paulo@0
|
799 return;
|
paulo@0
|
800
|
paulo@0
|
801 /* cleanup all the chunks and connections */
|
paulo@0
|
802 src->xfers =
|
paulo@0
|
803 list_foreach_remove (src->xfers, (ListForeachFunc)cleanup_xfer,
|
paulo@0
|
804 NULL);
|
paulo@0
|
805 src->connections =
|
paulo@0
|
806 list_foreach_remove (src->connections, (ListForeachFunc)cleanup_conn,
|
paulo@0
|
807 NULL);
|
paulo@0
|
808
|
paulo@0
|
809 /* remove this source from the global list */
|
paulo@0
|
810 remove_push_source (src);
|
paulo@0
|
811
|
paulo@0
|
812 gt_push_source_free (src);
|
paulo@0
|
813 }
|
paulo@0
|
814
|
paulo@0
|
815 /*****************************************************************************/
|
paulo@0
|
816
|
paulo@0
|
817 static BOOL detach_timeout (void *udata)
|
paulo@0
|
818 {
|
paulo@0
|
819 GtTransfer *xfer = udata;
|
paulo@0
|
820
|
paulo@0
|
821 /* Added this on 2004-12-22 to track observed assertion failure in
|
paulo@0
|
822 * gt_transfer_get_chunk. -- mkern
|
paulo@0
|
823 */
|
paulo@0
|
824 if (!xfer->chunk || xfer->chunk->udata != xfer)
|
paulo@0
|
825 {
|
paulo@0
|
826 GT->DBGFN (GT, "Detach timeout troubles. status = %d, "
|
paulo@0
|
827 "text = %s, xfer->ip = %s, "
|
paulo@0
|
828 "xfer = %p, xfer->chunk->udata = %p, "
|
paulo@0
|
829 "xfer->detach_timer = 0x%X",
|
paulo@0
|
830 xfer->detach_status, xfer->detach_msgtxt,
|
paulo@0
|
831 net_ip_str (xfer->ip), xfer,
|
paulo@0
|
832 xfer->chunk->udata, xfer->detach_timer);
|
paulo@0
|
833 }
|
paulo@0
|
834
|
paulo@0
|
835 /* Sometimes gt_transfer_status will trigger an
|
paulo@0
|
836 * assert (xfer->chunk->udata == xfer) failure in gt_transfer_get_chunk.
|
paulo@0
|
837 * But why? Is xfer already freed? Does it have another chunk and the
|
paulo@0
|
838 * timer was not removed?
|
paulo@0
|
839 */
|
paulo@0
|
840 gt_transfer_status (xfer, xfer->detach_status, xfer->detach_msgtxt);
|
paulo@0
|
841 gt_transfer_close (xfer, TRUE);
|
paulo@0
|
842
|
paulo@0
|
843 return FALSE;
|
paulo@0
|
844 }
|
paulo@0
|
845
|
paulo@0
|
846 /*
|
paulo@0
|
847 * Attach a timer that will "detach" the GtTransfer from the Chunk by
|
paulo@0
|
848 * cancelling it, but pretend that the Source is in some other state besides
|
paulo@0
|
849 * "cancelled" or "timed out" by changing the status text.
|
paulo@0
|
850 *
|
paulo@0
|
851 * This is useful to keep a semi-consistent UI in certain situations, such as
|
paulo@0
|
852 * sending out push requests, and cancelling requests when the remote side has
|
paulo@0
|
853 * queued our request.
|
paulo@0
|
854 */
|
paulo@0
|
855 static void detach_transfer_in (GtTransfer *xfer, SourceStatus status,
|
paulo@0
|
856 char *status_txt, time_t interval)
|
paulo@0
|
857 {
|
paulo@0
|
858 char *msg;
|
paulo@0
|
859
|
paulo@0
|
860 msg = STRDUP (status_txt);
|
paulo@0
|
861
|
paulo@0
|
862 gt_transfer_status (xfer, status, msg);
|
paulo@0
|
863 xfer->detach_status = status;
|
paulo@0
|
864
|
paulo@0
|
865 free (xfer->detach_msgtxt);
|
paulo@0
|
866 xfer->detach_msgtxt = msg;
|
paulo@0
|
867
|
paulo@0
|
868 xfer->detach_timer = timer_add (interval,
|
paulo@0
|
869 (TimerCallback)detach_timeout, xfer);
|
paulo@0
|
870 }
|
paulo@0
|
871
|
paulo@0
|
872 /*
|
paulo@0
|
873 * Detach an GtTransfer from its Chunk.
|
paulo@0
|
874 */
|
paulo@0
|
875 static void detach_transfer (GtTransfer *xfer, SourceStatus status,
|
paulo@0
|
876 char *msgtxt)
|
paulo@0
|
877 {
|
paulo@0
|
878 /*
|
paulo@0
|
879 * Cancelling from p->download_start will cause download_pause() to crash.
|
paulo@0
|
880 * So, the detach must happen from timer context.
|
paulo@0
|
881 */
|
paulo@0
|
882 detach_transfer_in (xfer, status, msgtxt, 2 * SECONDS);
|
paulo@0
|
883 }
|
paulo@0
|
884
|
paulo@0
|
885 /*****************************************************************************/
|
paulo@0
|
886
|
paulo@0
|
887 static void send_push (GtTransfer *xfer, GtSource *gt, TCPC *server)
|
paulo@0
|
888 {
|
paulo@0
|
889 GtPacket *packet;
|
paulo@0
|
890
|
paulo@0
|
891 if (!(packet = gt_packet_new (GT_MSG_PUSH, PUSH_MAX_TTL, NULL)))
|
paulo@0
|
892 return;
|
paulo@0
|
893
|
paulo@0
|
894 gt_packet_put_ustr (packet, gt->guid, 16);
|
paulo@0
|
895 gt_packet_put_uint32 (packet, gt->index);
|
paulo@0
|
896 gt_packet_put_ip (packet, GT_NODE(server)->my_ip);
|
paulo@0
|
897 gt_packet_put_port (packet, GT_SELF->gt_port);
|
paulo@0
|
898
|
paulo@0
|
899 if (gt_packet_error (packet))
|
paulo@0
|
900 {
|
paulo@0
|
901 gt_packet_free (packet);
|
paulo@0
|
902 return;
|
paulo@0
|
903 }
|
paulo@0
|
904
|
paulo@0
|
905 gt_packet_send (server, packet);
|
paulo@0
|
906 gt_packet_free (packet);
|
paulo@0
|
907
|
paulo@0
|
908 /*
|
paulo@0
|
909 * Don't wait for the whole Chunk timeout -- that keeps the Chunk
|
paulo@0
|
910 * occupied for too long if there are other active sources (the Chunk
|
paulo@0
|
911 * also times out longer and longer each time, so this gets worse
|
paulo@0
|
912 * the longer the transfer is inactive).
|
paulo@0
|
913 *
|
paulo@0
|
914 * This is really an infelicity of the Chunk system.
|
paulo@0
|
915 */
|
paulo@0
|
916 detach_transfer_in (xfer, SOURCE_QUEUED_REMOTE, "Sent PUSH request",
|
paulo@0
|
917 PUSH_WAIT_INTERVAL);
|
paulo@0
|
918
|
paulo@0
|
919 /* set the last time we sent a push to now */
|
paulo@0
|
920 push_source_set_last_sent (gt->guid, gt->user_ip);
|
paulo@0
|
921 }
|
paulo@0
|
922
|
paulo@0
|
923 static BOOL send_push_to_server (in_addr_t server_ip, in_port_t server_port,
|
paulo@0
|
924 GtTransfer *xfer, GtSource *gt)
|
paulo@0
|
925 {
|
paulo@0
|
926 GtNode *server;
|
paulo@0
|
927
|
paulo@0
|
928 if (!(server = gt_node_lookup (server_ip, server_port)))
|
paulo@0
|
929 {
|
paulo@0
|
930 server = gt_node_register (server_ip, server_port,
|
paulo@0
|
931 GT_NODE_ULTRA);
|
paulo@0
|
932 }
|
paulo@0
|
933
|
paulo@0
|
934 if (!server)
|
paulo@0
|
935 {
|
paulo@0
|
936 GT->err (GT, "couldn't register server");
|
paulo@0
|
937 return FALSE;
|
paulo@0
|
938 }
|
paulo@0
|
939
|
paulo@0
|
940 if (server->state & (GT_NODE_CONNECTED | GT_NODE_CONNECTING_2))
|
paulo@0
|
941 {
|
paulo@0
|
942 assert (GT_CONN(server) != NULL);
|
paulo@0
|
943
|
paulo@0
|
944 /* Server is in a state for receiving packets -- send the push */
|
paulo@0
|
945 send_push (xfer, gt, GT_CONN(server));
|
paulo@0
|
946 return TRUE;
|
paulo@0
|
947 }
|
paulo@0
|
948 else if (server->state & GT_NODE_CONNECTING_1)
|
paulo@0
|
949 {
|
paulo@0
|
950 /* dont try to connect again; wait till we're connected */
|
paulo@0
|
951 return FALSE;
|
paulo@0
|
952 }
|
paulo@0
|
953 else if (gt_conn_need_connections (GT_NODE_ULTRA) > 0 &&
|
paulo@0
|
954 !server->tried_connect &&
|
paulo@0
|
955 gt_connect (server) >= 0)
|
paulo@0
|
956 {
|
paulo@0
|
957 /*
|
paulo@0
|
958 * We've tried to connect to the server so we could deliver the push
|
paulo@0
|
959 * request eventually NOTE: this doesnt send a push until the next
|
paulo@0
|
960 * chunk timeout.
|
paulo@0
|
961 */
|
paulo@0
|
962 return FALSE;
|
paulo@0
|
963 }
|
paulo@0
|
964
|
paulo@0
|
965 return FALSE;
|
paulo@0
|
966 }
|
paulo@0
|
967
|
paulo@0
|
968 static void handle_push_download (Chunk *chunk, GtTransfer *xfer, GtSource *gt)
|
paulo@0
|
969 {
|
paulo@0
|
970 GtNode *server;
|
paulo@0
|
971
|
paulo@0
|
972 /*
|
paulo@0
|
973 * If this succeeds, we already have a connection to this
|
paulo@0
|
974 * user and the transfer will continue by using that connection.
|
paulo@0
|
975 *
|
paulo@0
|
976 * TODO: the gt_push_source_add() should be used by some
|
paulo@0
|
977 * per-source data structure
|
paulo@0
|
978 */
|
paulo@0
|
979 if (gt_push_source_add_xfer (gt->guid, gt->user_ip, gt->server_ip, xfer))
|
paulo@0
|
980 return;
|
paulo@0
|
981
|
paulo@0
|
982 /*
|
paulo@0
|
983 * Dont send pushes too often. Maybe should use a global queue instead.
|
paulo@0
|
984 *
|
paulo@0
|
985 * NOTE: we can't free the xfer here because we have stored it.
|
paulo@0
|
986 */
|
paulo@0
|
987 if (push_source_should_send (gt->guid, gt->user_ip) == FALSE)
|
paulo@0
|
988 {
|
paulo@0
|
989 /* don't occupy the Chunk forever */
|
paulo@0
|
990 detach_transfer_in (xfer, SOURCE_QUEUED_REMOTE, "Awaiting connection",
|
paulo@0
|
991 PUSH_WAIT_INTERVAL);
|
paulo@0
|
992 return;
|
paulo@0
|
993 }
|
paulo@0
|
994
|
paulo@0
|
995 /*
|
paulo@0
|
996 * Next, try to find the server that supplied this result,
|
paulo@0
|
997 * and send them a push.
|
paulo@0
|
998 */
|
paulo@0
|
999 if (send_push_to_server (gt->server_ip, gt->server_port, xfer, gt))
|
paulo@0
|
1000 return;
|
paulo@0
|
1001
|
paulo@0
|
1002 /*
|
paulo@0
|
1003 * Finally, try sending to a random connected server.
|
paulo@0
|
1004 *
|
paulo@0
|
1005 * TODO: these should be rate-limited, either globally or
|
paulo@0
|
1006 * per-source.
|
paulo@0
|
1007 */
|
paulo@0
|
1008 if ((server = gt_conn_random (GT_NODE_ULTRA, GT_NODE_CONNECTED)))
|
paulo@0
|
1009 {
|
paulo@0
|
1010 send_push_to_server (server->ip, server->gt_port, xfer, gt);
|
paulo@0
|
1011 return;
|
paulo@0
|
1012 }
|
paulo@0
|
1013
|
paulo@0
|
1014 detach_transfer (xfer, SOURCE_QUEUED_REMOTE, "No PUSH route");
|
paulo@0
|
1015 }
|
paulo@0
|
1016
|
paulo@0
|
1017 static BOOL set_request (GtTransfer *xfer, Chunk *chunk, Source *source,
|
paulo@0
|
1018 GtSource *gt_src)
|
paulo@0
|
1019 {
|
paulo@0
|
1020 char *request;
|
paulo@0
|
1021
|
paulo@0
|
1022 if (!chunk || !xfer)
|
paulo@0
|
1023 return FALSE;
|
paulo@0
|
1024
|
paulo@0
|
1025 request = request_str (source, gt_src->index, gt_src->filename);
|
paulo@0
|
1026
|
paulo@0
|
1027 if (!gt_transfer_set_request (xfer, request))
|
paulo@0
|
1028 {
|
paulo@0
|
1029 GT->DBGFN (GT, "UI made an invalid request for '%s'", request);
|
paulo@0
|
1030 return FALSE;
|
paulo@0
|
1031 }
|
paulo@0
|
1032
|
paulo@0
|
1033 /* connect the xfer and the chunk */
|
paulo@0
|
1034 gt_transfer_set_chunk (xfer, chunk);
|
paulo@0
|
1035
|
paulo@0
|
1036 return TRUE;
|
paulo@0
|
1037 }
|
paulo@0
|
1038
|
paulo@0
|
1039 static BOOL should_push (GtSource *gt)
|
paulo@0
|
1040 {
|
paulo@0
|
1041 TCPC *persistent;
|
paulo@0
|
1042
|
paulo@0
|
1043 /* we cannot push if there is no guid to send the push to */
|
paulo@0
|
1044 if (gt_guid_is_empty (gt->guid))
|
paulo@0
|
1045 return FALSE;
|
paulo@0
|
1046
|
paulo@0
|
1047 persistent = gt_http_connection_lookup (GT_TRANSFER_DOWNLOAD,
|
paulo@0
|
1048 gt->user_ip,
|
paulo@0
|
1049 gt->user_port);
|
paulo@0
|
1050
|
paulo@0
|
1051 /* need to close the connection to re-add it to the list, because
|
paulo@0
|
1052 * _lookup removes it from the persistent connection list */
|
paulo@0
|
1053 gt_http_connection_close (GT_TRANSFER_DOWNLOAD, persistent, FALSE);
|
paulo@0
|
1054
|
paulo@0
|
1055 /* if we already have a connection don't send a push */
|
paulo@0
|
1056 if (persistent)
|
paulo@0
|
1057 return FALSE;
|
paulo@0
|
1058
|
paulo@0
|
1059 /* now check for a persistent "pushed" connection, which would be stored
|
paulo@0
|
1060 * separately from a directly connected one */
|
paulo@0
|
1061 if (push_source_lookup_conn (gt->guid, gt->user_ip))
|
paulo@0
|
1062 return TRUE;
|
paulo@0
|
1063
|
paulo@0
|
1064 /* send a push if the IP is local */
|
paulo@0
|
1065 if (gt_is_local_ip (gt->user_ip, gt->server_ip))
|
paulo@0
|
1066 return TRUE;
|
paulo@0
|
1067
|
paulo@0
|
1068 /* don't send a push if we cannot receive incoming connections */
|
paulo@0
|
1069 if (gt_bind_is_firewalled())
|
paulo@0
|
1070 return FALSE;
|
paulo@0
|
1071
|
paulo@0
|
1072 /* send a push if they set the firewalled bit */
|
paulo@0
|
1073 if (gt->firewalled)
|
paulo@0
|
1074 return TRUE;
|
paulo@0
|
1075
|
paulo@0
|
1076 /* the last connection attempt failed, so try a push */
|
paulo@0
|
1077 if (gt->connect_failed)
|
paulo@0
|
1078 return TRUE;
|
paulo@0
|
1079
|
paulo@0
|
1080 return FALSE;
|
paulo@0
|
1081 }
|
paulo@0
|
1082
|
paulo@0
|
1083 static void handle_download (Chunk *chunk, GtTransfer *xfer, GtSource *gt)
|
paulo@0
|
1084 {
|
paulo@0
|
1085 /*
|
paulo@0
|
1086 * Send a push, or connect directly.
|
paulo@0
|
1087 */
|
paulo@0
|
1088 if (should_push (gt))
|
paulo@0
|
1089 {
|
paulo@0
|
1090 /* (possibly) retry a connection attempt next time */
|
paulo@0
|
1091 gt->connect_failed = FALSE;
|
paulo@0
|
1092
|
paulo@0
|
1093 handle_push_download (chunk, xfer, gt);
|
paulo@0
|
1094 }
|
paulo@0
|
1095 else
|
paulo@0
|
1096 {
|
paulo@0
|
1097 gt_http_client_get (chunk, xfer);
|
paulo@0
|
1098 }
|
paulo@0
|
1099 }
|
paulo@0
|
1100
|
paulo@0
|
1101 static BOOL download_is_queued (GtSource *gt)
|
paulo@0
|
1102 {
|
paulo@0
|
1103 /* back out if the request is still too early */
|
paulo@0
|
1104 if (time (NULL) < gt->retry_time)
|
paulo@0
|
1105 return TRUE;
|
paulo@0
|
1106
|
paulo@0
|
1107 return FALSE;
|
paulo@0
|
1108 }
|
paulo@0
|
1109
|
paulo@0
|
1110 int gnutella_download_start (Protocol *p, Transfer *transfer, Chunk *chunk,
|
paulo@0
|
1111 Source *source)
|
paulo@0
|
1112 {
|
paulo@0
|
1113 GtTransfer *xfer;
|
paulo@0
|
1114 GtSource *gt;
|
paulo@0
|
1115 off_t start;
|
paulo@0
|
1116 off_t stop;
|
paulo@0
|
1117
|
paulo@0
|
1118 gt = source->udata;
|
paulo@0
|
1119 assert (gt != NULL);
|
paulo@0
|
1120
|
paulo@0
|
1121 /* giftd should send us only deactivated Chunks */
|
paulo@0
|
1122 assert (chunk->udata == NULL);
|
paulo@0
|
1123
|
paulo@0
|
1124 /* free the Source URL and update it with any format changes */
|
paulo@0
|
1125 replace_url (source, gt);
|
paulo@0
|
1126
|
paulo@0
|
1127 /* thank you, pretender :) */
|
paulo@0
|
1128 start = chunk->start + chunk->transmit;
|
paulo@0
|
1129 stop = chunk->stop;
|
paulo@0
|
1130
|
paulo@0
|
1131 if (!(xfer = gt_transfer_new (GT_TRANSFER_DOWNLOAD, source,
|
paulo@0
|
1132 gt->user_ip, gt->user_port, start, stop)))
|
paulo@0
|
1133 {
|
paulo@0
|
1134 GT->DBGFN (GT, "gt_transfer_new failed");
|
paulo@0
|
1135 return FALSE;
|
paulo@0
|
1136 }
|
paulo@0
|
1137
|
paulo@0
|
1138 if (!set_request (xfer, chunk, source, gt))
|
paulo@0
|
1139 {
|
paulo@0
|
1140 gt_transfer_close (xfer, TRUE);
|
paulo@0
|
1141 return FALSE;
|
paulo@0
|
1142 }
|
paulo@0
|
1143
|
paulo@0
|
1144 if (download_is_queued (gt))
|
paulo@0
|
1145 {
|
paulo@0
|
1146 detach_transfer (xfer, SOURCE_QUEUED_REMOTE, gt->status_txt);
|
paulo@0
|
1147 return TRUE;
|
paulo@0
|
1148 }
|
paulo@0
|
1149
|
paulo@0
|
1150 handle_download (chunk, xfer, gt);
|
paulo@0
|
1151
|
paulo@0
|
1152 return TRUE;
|
paulo@0
|
1153 }
|
paulo@0
|
1154
|
paulo@0
|
1155 void gnutella_download_stop (Protocol *p, Transfer *transfer, Chunk *chunk,
|
paulo@0
|
1156 Source *source, BOOL complete)
|
paulo@0
|
1157 {
|
paulo@0
|
1158 gt_download_cancel (chunk, NULL);
|
paulo@0
|
1159 }
|
paulo@0
|
1160
|
paulo@0
|
1161 int gnutella_upload_start (Protocol *p, Transfer *transfer, Chunk *chunk,
|
paulo@0
|
1162 Source *source, unsigned long avail)
|
paulo@0
|
1163 {
|
paulo@0
|
1164 return TRUE;
|
paulo@0
|
1165 }
|
paulo@0
|
1166
|
paulo@0
|
1167 void gnutella_upload_stop (Protocol *p, Transfer *transfer, Chunk *chunk,
|
paulo@0
|
1168 Source *source)
|
paulo@0
|
1169 {
|
paulo@0
|
1170 gt_upload_cancel (chunk, NULL);
|
paulo@0
|
1171 }
|
paulo@0
|
1172
|
paulo@0
|
1173 int gnutella_chunk_suspend (Protocol *p, Transfer *transfer, Chunk *chunk,
|
paulo@0
|
1174 Source *source)
|
paulo@0
|
1175 {
|
paulo@0
|
1176 return gt_chunk_suspend (chunk, transfer, NULL);
|
paulo@0
|
1177 }
|
paulo@0
|
1178
|
paulo@0
|
1179 int gnutella_chunk_resume (Protocol *p, Transfer *transfer, Chunk *chunk,
|
paulo@0
|
1180 Source *source)
|
paulo@0
|
1181 {
|
paulo@0
|
1182 return gt_chunk_resume (chunk, transfer, NULL);
|
paulo@0
|
1183 }
|