rev |
line source |
paulo@0
|
1 /*
|
paulo@0
|
2 * $Id: http_request.c,v 1.25 2005/01/04 15:03:41 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 #include "gt_version.h"
|
paulo@0
|
19
|
paulo@0
|
20 #include "gt_accept.h"
|
paulo@0
|
21 #include "gt_web_cache.h"
|
paulo@0
|
22
|
paulo@0
|
23 #include "http_request.h"
|
paulo@0
|
24
|
paulo@0
|
25 /*****************************************************************************/
|
paulo@0
|
26
|
paulo@0
|
27 #define MAX_REDIRECTS (5)
|
paulo@0
|
28
|
paulo@0
|
29 /*****************************************************************************/
|
paulo@0
|
30
|
paulo@0
|
31 static void decode_chunked_data (int fd, input_id id, TCPC *c);
|
paulo@0
|
32 static void read_chunked_header (int fd, input_id id, TCPC *c);
|
paulo@0
|
33
|
paulo@0
|
34 /*****************************************************************************/
|
paulo@0
|
35
|
paulo@0
|
36 /*
|
paulo@0
|
37 * Dummy callbacks
|
paulo@0
|
38 */
|
paulo@0
|
39
|
paulo@0
|
40 static void dummy_close (HttpRequest *r, int code)
|
paulo@0
|
41 {
|
paulo@0
|
42 return;
|
paulo@0
|
43 }
|
paulo@0
|
44
|
paulo@0
|
45 static BOOL dummy_recv (HttpRequest *r, char *d, size_t l)
|
paulo@0
|
46 {
|
paulo@0
|
47 return TRUE;
|
paulo@0
|
48 }
|
paulo@0
|
49
|
paulo@0
|
50 static BOOL dummy_add_header (HttpRequest *r, Dataset **d)
|
paulo@0
|
51 {
|
paulo@0
|
52 return TRUE;
|
paulo@0
|
53 }
|
paulo@0
|
54
|
paulo@0
|
55 static BOOL dummy_redirect (HttpRequest *r, const char *h, const char *p)
|
paulo@0
|
56 {
|
paulo@0
|
57 return TRUE;
|
paulo@0
|
58 }
|
paulo@0
|
59
|
paulo@0
|
60 /*****************************************************************************/
|
paulo@0
|
61
|
paulo@0
|
62 BOOL gt_http_url_parse (char *value, char **r_host, char **r_path)
|
paulo@0
|
63 {
|
paulo@0
|
64 char *host_name;
|
paulo@0
|
65
|
paulo@0
|
66 if (r_host)
|
paulo@0
|
67 *r_host = NULL;
|
paulo@0
|
68 if (r_path)
|
paulo@0
|
69 *r_path = NULL;
|
paulo@0
|
70
|
paulo@0
|
71 string_sep (&value, "http://");
|
paulo@0
|
72
|
paulo@0
|
73 /* divide the url in two parts */
|
paulo@0
|
74 host_name = string_sep (&value, "/");
|
paulo@0
|
75
|
paulo@0
|
76 if (r_host)
|
paulo@0
|
77 *r_host = host_name;
|
paulo@0
|
78
|
paulo@0
|
79 if (r_path)
|
paulo@0
|
80 *r_path = STRING_NOTNULL (value);
|
paulo@0
|
81
|
paulo@0
|
82 if (!host_name || host_name[0] == 0)
|
paulo@0
|
83 return FALSE;
|
paulo@0
|
84
|
paulo@0
|
85 return TRUE;
|
paulo@0
|
86 }
|
paulo@0
|
87
|
paulo@0
|
88 static void setup_dummy_functbl (HttpRequest *r)
|
paulo@0
|
89 {
|
paulo@0
|
90 r->close_req_func = dummy_close;
|
paulo@0
|
91 r->recv_func = dummy_recv;
|
paulo@0
|
92 r->add_header_func = dummy_add_header;
|
paulo@0
|
93 r->redirect_func = dummy_redirect;
|
paulo@0
|
94 }
|
paulo@0
|
95
|
paulo@0
|
96 HttpRequest *gt_http_request_new (const char *url, const char *request)
|
paulo@0
|
97 {
|
paulo@0
|
98 HttpRequest *req;
|
paulo@0
|
99 char *dup;
|
paulo@0
|
100 char *host;
|
paulo@0
|
101 char *path;
|
paulo@0
|
102
|
paulo@0
|
103 if (!(dup = STRDUP (url)))
|
paulo@0
|
104 return NULL;
|
paulo@0
|
105
|
paulo@0
|
106 if (!gt_http_url_parse (dup, &host, &path) ||
|
paulo@0
|
107 !(req = MALLOC (sizeof (HttpRequest))))
|
paulo@0
|
108 {
|
paulo@0
|
109 free (dup);
|
paulo@0
|
110 return NULL;
|
paulo@0
|
111 }
|
paulo@0
|
112
|
paulo@0
|
113 req->host = STRDUP (host);
|
paulo@0
|
114 req->path = STRDUP (path);
|
paulo@0
|
115 req->request = STRDUP (request);
|
paulo@0
|
116 req->timeout = 0;
|
paulo@0
|
117 req->redirects = 0;
|
paulo@0
|
118 req->headers = NULL;
|
paulo@0
|
119
|
paulo@0
|
120 /* setup functbl */
|
paulo@0
|
121 setup_dummy_functbl (req);
|
paulo@0
|
122
|
paulo@0
|
123 free (dup);
|
paulo@0
|
124
|
paulo@0
|
125 return req;
|
paulo@0
|
126 }
|
paulo@0
|
127
|
paulo@0
|
128 static void gt_http_request_free (HttpRequest *req)
|
paulo@0
|
129 {
|
paulo@0
|
130 if (!req)
|
paulo@0
|
131 return;
|
paulo@0
|
132
|
paulo@0
|
133 dataset_clear (req->headers);
|
paulo@0
|
134
|
paulo@0
|
135 free (req->host);
|
paulo@0
|
136 free (req->path);
|
paulo@0
|
137 free (req->request);
|
paulo@0
|
138
|
paulo@0
|
139 free (req);
|
paulo@0
|
140 }
|
paulo@0
|
141
|
paulo@0
|
142 void gt_http_request_close (HttpRequest *req, int error_code)
|
paulo@0
|
143 {
|
paulo@0
|
144 /* notify the callback */
|
paulo@0
|
145 req->close_req_func (req, error_code);
|
paulo@0
|
146
|
paulo@0
|
147 if (req->c)
|
paulo@0
|
148 tcp_close (req->c);
|
paulo@0
|
149
|
paulo@0
|
150 timer_remove_zero (&req->timeout);
|
paulo@0
|
151
|
paulo@0
|
152 gt_http_request_free (req);
|
paulo@0
|
153 }
|
paulo@0
|
154
|
paulo@0
|
155 /*****************************************************************************/
|
paulo@0
|
156
|
paulo@0
|
157 static BOOL request_timeout (HttpRequest *req)
|
paulo@0
|
158 {
|
paulo@0
|
159 GT->DBGFN (GT, "request to %s timed out", req->host);
|
paulo@0
|
160 gt_http_request_close (req, -1);
|
paulo@0
|
161 return FALSE;
|
paulo@0
|
162 }
|
paulo@0
|
163
|
paulo@0
|
164 void gt_http_request_set_timeout (HttpRequest *req, time_t time)
|
paulo@0
|
165 {
|
paulo@0
|
166 if (!req)
|
paulo@0
|
167 return;
|
paulo@0
|
168
|
paulo@0
|
169 if (req->timeout)
|
paulo@0
|
170 timer_remove (req->timeout);
|
paulo@0
|
171
|
paulo@0
|
172 req->timeout = timer_add (time, (TimerCallback)request_timeout, req);
|
paulo@0
|
173 }
|
paulo@0
|
174
|
paulo@0
|
175 void gt_http_request_set_proxy (HttpRequest *req, const char *proxy)
|
paulo@0
|
176 {
|
paulo@0
|
177 free (req->proxy);
|
paulo@0
|
178 req->proxy = NULL;
|
paulo@0
|
179
|
paulo@0
|
180 if (!proxy)
|
paulo@0
|
181 return;
|
paulo@0
|
182
|
paulo@0
|
183 req->proxy = STRDUP (proxy);
|
paulo@0
|
184 }
|
paulo@0
|
185
|
paulo@0
|
186 void gt_http_request_set_conn (HttpRequest *req, TCPC *c)
|
paulo@0
|
187 {
|
paulo@0
|
188 assert (c->udata == NULL);
|
paulo@0
|
189 assert (req->c == NULL);
|
paulo@0
|
190
|
paulo@0
|
191 req->c = c;
|
paulo@0
|
192 c->udata = req;
|
paulo@0
|
193 }
|
paulo@0
|
194
|
paulo@0
|
195 void gt_http_request_set_max_len (HttpRequest *req, size_t max_len)
|
paulo@0
|
196 {
|
paulo@0
|
197 req->max_len = max_len;
|
paulo@0
|
198 }
|
paulo@0
|
199
|
paulo@0
|
200 /*****************************************************************************/
|
paulo@0
|
201
|
paulo@0
|
202 static BOOL write_data (HttpRequest *req, char *data, size_t len)
|
paulo@0
|
203 {
|
paulo@0
|
204 if (!req)
|
paulo@0
|
205 return FALSE;
|
paulo@0
|
206
|
paulo@0
|
207 req->recvd_len += len;
|
paulo@0
|
208
|
paulo@0
|
209 /* check if we overflowed the max length the user wants to receive */
|
paulo@0
|
210 if (req->max_len > 0 && req->recvd_len > req->max_len)
|
paulo@0
|
211 {
|
paulo@0
|
212 GT->DBGFN (GT, "%s sent %lu bytes overflowing max length of %lu",
|
paulo@0
|
213 req->host, req->recvd_len, req->max_len);
|
paulo@0
|
214 gt_http_request_close (req, -1);
|
paulo@0
|
215 return FALSE;
|
paulo@0
|
216 }
|
paulo@0
|
217
|
paulo@0
|
218 /* send the data to the listener */
|
paulo@0
|
219 if (req->recv_func (req, data, len) == FALSE)
|
paulo@0
|
220 {
|
paulo@0
|
221 gt_http_request_close (req, -1);
|
paulo@0
|
222 return FALSE;
|
paulo@0
|
223 }
|
paulo@0
|
224
|
paulo@0
|
225 return TRUE;
|
paulo@0
|
226 }
|
paulo@0
|
227
|
paulo@0
|
228 /*****************************************************************************/
|
paulo@0
|
229
|
paulo@0
|
230 static void write_header (ds_data_t *key, ds_data_t *value, String *s)
|
paulo@0
|
231 {
|
paulo@0
|
232 char *header = key->data;
|
paulo@0
|
233 char *field = value->data;
|
paulo@0
|
234
|
paulo@0
|
235 string_appendf (s, "%s: %s\r\n", header, field);
|
paulo@0
|
236 }
|
paulo@0
|
237
|
paulo@0
|
238 static int http_send (TCPC *c, char *command, char *request,
|
paulo@0
|
239 Dataset *headers)
|
paulo@0
|
240 {
|
paulo@0
|
241 String *s;
|
paulo@0
|
242 int ret;
|
paulo@0
|
243
|
paulo@0
|
244 if (!command || !request)
|
paulo@0
|
245 return -1;
|
paulo@0
|
246
|
paulo@0
|
247 if (!(s = string_new (NULL, 0, 0, TRUE)))
|
paulo@0
|
248 return -1;
|
paulo@0
|
249
|
paulo@0
|
250 string_appendf (s, "%s %s HTTP/1.1\r\n", command, request);
|
paulo@0
|
251
|
paulo@0
|
252 dataset_foreach (headers, DS_FOREACH(write_header), s);
|
paulo@0
|
253 string_append (s, "\r\n");
|
paulo@0
|
254
|
paulo@0
|
255 GT->DBGSOCK (GT, c, "<http_request.c> sending:\n%s", s->str);
|
paulo@0
|
256
|
paulo@0
|
257 ret = tcp_send (c, s->str, s->len);
|
paulo@0
|
258 string_free (s);
|
paulo@0
|
259
|
paulo@0
|
260 return ret;
|
paulo@0
|
261 }
|
paulo@0
|
262
|
paulo@0
|
263 static HttpRequest *get_request (TCPC *c)
|
paulo@0
|
264 {
|
paulo@0
|
265 return c->udata;
|
paulo@0
|
266 }
|
paulo@0
|
267
|
paulo@0
|
268 /*****************************************************************************/
|
paulo@0
|
269
|
paulo@0
|
270 static void decode_chunked_data (int fd, input_id id, TCPC *c)
|
paulo@0
|
271 {
|
paulo@0
|
272 HttpRequest *req;
|
paulo@0
|
273 FDBuf *buf;
|
paulo@0
|
274 char *data;
|
paulo@0
|
275 int data_len = 0;
|
paulo@0
|
276 int n;
|
paulo@0
|
277
|
paulo@0
|
278 req = get_request (c);
|
paulo@0
|
279
|
paulo@0
|
280 if (!req->size)
|
paulo@0
|
281 {
|
paulo@0
|
282 gt_http_request_close (req, 200);
|
paulo@0
|
283 return;
|
paulo@0
|
284 }
|
paulo@0
|
285
|
paulo@0
|
286 buf = tcp_readbuf (c);
|
paulo@0
|
287
|
paulo@0
|
288 if ((n = fdbuf_fill (buf, req->size)) < 0)
|
paulo@0
|
289 {
|
paulo@0
|
290 GT->DBGFN (GT, "error on host %s: %s", req->host, GIFT_NETERROR ());
|
paulo@0
|
291 gt_http_request_close (req, -1);
|
paulo@0
|
292 return;
|
paulo@0
|
293 }
|
paulo@0
|
294
|
paulo@0
|
295 if (gt_fdbuf_full (buf))
|
paulo@0
|
296 {
|
paulo@0
|
297 gt_http_request_close (req, -1);
|
paulo@0
|
298 return;
|
paulo@0
|
299 }
|
paulo@0
|
300
|
paulo@0
|
301 if (n > 0)
|
paulo@0
|
302 return;
|
paulo@0
|
303
|
paulo@0
|
304 data = fdbuf_data (buf, &data_len);
|
paulo@0
|
305 fdbuf_release (buf);
|
paulo@0
|
306
|
paulo@0
|
307 if (!write_data (req, data, data_len))
|
paulo@0
|
308 return;
|
paulo@0
|
309
|
paulo@0
|
310 input_remove (id);
|
paulo@0
|
311 input_add (fd, c, INPUT_READ,
|
paulo@0
|
312 (InputCallback)read_chunked_header, TIMEOUT_DEF);
|
paulo@0
|
313 }
|
paulo@0
|
314
|
paulo@0
|
315 static void read_chunked_header (int fd, input_id id, TCPC *c)
|
paulo@0
|
316 {
|
paulo@0
|
317 HttpRequest *req;
|
paulo@0
|
318 FDBuf *buf;
|
paulo@0
|
319 char *response;
|
paulo@0
|
320 int n;
|
paulo@0
|
321
|
paulo@0
|
322 req = get_request (c);
|
paulo@0
|
323 buf = tcp_readbuf (c);
|
paulo@0
|
324
|
paulo@0
|
325 if ((n = fdbuf_delim (buf, "\n")) < 0)
|
paulo@0
|
326 {
|
paulo@0
|
327 GT->DBGFN (GT, "error on %s: %s", req->host, GIFT_NETERROR ());
|
paulo@0
|
328 gt_http_request_close (req, -1);
|
paulo@0
|
329 return;
|
paulo@0
|
330 }
|
paulo@0
|
331
|
paulo@0
|
332 if (gt_fdbuf_full (buf))
|
paulo@0
|
333 {
|
paulo@0
|
334 gt_http_request_close (req, -1);
|
paulo@0
|
335 return;
|
paulo@0
|
336 }
|
paulo@0
|
337
|
paulo@0
|
338 if (n > 0)
|
paulo@0
|
339 return;
|
paulo@0
|
340
|
paulo@0
|
341 response = fdbuf_data (buf, NULL);
|
paulo@0
|
342 fdbuf_release (buf);
|
paulo@0
|
343
|
paulo@0
|
344 /* read the chunk size, its a hexadecimal integer */
|
paulo@0
|
345 req->size = strtoul (response, NULL, 16);
|
paulo@0
|
346 GT->DBGFN (GT, "server sent chunk size of %lu", req->size);
|
paulo@0
|
347
|
paulo@0
|
348 if (req->size == ULONG_MAX)
|
paulo@0
|
349 {
|
paulo@0
|
350 GT->DBGFN (GT, "overflow reading chunk size: %s", GIFT_STRERROR ());
|
paulo@0
|
351 gt_http_request_close (req, -1);
|
paulo@0
|
352 return;
|
paulo@0
|
353 }
|
paulo@0
|
354
|
paulo@0
|
355 if (req->size == 0)
|
paulo@0
|
356 {
|
paulo@0
|
357 /* ok, done */
|
paulo@0
|
358 if (!write_data (req, NULL, 0))
|
paulo@0
|
359 return;
|
paulo@0
|
360
|
paulo@0
|
361 /* there could be a CRLF at the end. should we read it?
|
paulo@0
|
362 * To avoid screwing up persistent http, yes.. */
|
paulo@0
|
363 gt_http_request_close (req, 200);
|
paulo@0
|
364 return;
|
paulo@0
|
365 }
|
paulo@0
|
366
|
paulo@0
|
367 input_remove (id);
|
paulo@0
|
368 input_add (fd, c, INPUT_READ,
|
paulo@0
|
369 (InputCallback)decode_chunked_data, TIMEOUT_DEF);
|
paulo@0
|
370 }
|
paulo@0
|
371
|
paulo@0
|
372 /* read the amount of data specified by Content-Length: */
|
paulo@0
|
373 static void read_file (int fd, input_id id, TCPC *c)
|
paulo@0
|
374 {
|
paulo@0
|
375 HttpRequest *req;
|
paulo@0
|
376 FDBuf *buf;
|
paulo@0
|
377 int n;
|
paulo@0
|
378 size_t len;
|
paulo@0
|
379 unsigned char *data;
|
paulo@0
|
380
|
paulo@0
|
381 req = get_request (c);
|
paulo@0
|
382
|
paulo@0
|
383 if (!req->size)
|
paulo@0
|
384 {
|
paulo@0
|
385 gt_http_request_close (req, 200);
|
paulo@0
|
386 return;
|
paulo@0
|
387 }
|
paulo@0
|
388
|
paulo@0
|
389 buf = tcp_readbuf (c);
|
paulo@0
|
390
|
paulo@0
|
391 if ((n = fdbuf_fill (buf, req->size)) < 0)
|
paulo@0
|
392 {
|
paulo@0
|
393 GT->DBGFN (GT, "error from %s: %s", req->host, GIFT_NETERROR ());
|
paulo@0
|
394 gt_http_request_close (req, -1);
|
paulo@0
|
395 return;
|
paulo@0
|
396 }
|
paulo@0
|
397
|
paulo@0
|
398 if (n > 0)
|
paulo@0
|
399 return;
|
paulo@0
|
400
|
paulo@0
|
401 data = fdbuf_data (buf, &len);
|
paulo@0
|
402 fdbuf_release (buf);
|
paulo@0
|
403
|
paulo@0
|
404 if (!write_data (req, data, len))
|
paulo@0
|
405 return;
|
paulo@0
|
406
|
paulo@0
|
407 /*
|
paulo@0
|
408 * We've read all the data, the total length of the request being provided
|
paulo@0
|
409 * by fdbuf_fill(). Now send the closing notification to our callback.
|
paulo@0
|
410 */
|
paulo@0
|
411 if (!write_data (req, NULL, 0))
|
paulo@0
|
412 return;
|
paulo@0
|
413
|
paulo@0
|
414 /* success */
|
paulo@0
|
415 gt_http_request_close (req, 200);
|
paulo@0
|
416 }
|
paulo@0
|
417
|
paulo@0
|
418 /* callback to read when no Content-Length: header is provided */
|
paulo@0
|
419 static void read_until_eof (int fd, input_id id, TCPC *c)
|
paulo@0
|
420 {
|
paulo@0
|
421 char data[RW_BUFFER];
|
paulo@0
|
422 int n;
|
paulo@0
|
423 HttpRequest *req;
|
paulo@0
|
424
|
paulo@0
|
425 req = get_request (c);
|
paulo@0
|
426
|
paulo@0
|
427 if ((n = tcp_recv (c, data, sizeof (data) - 1)) < 0)
|
paulo@0
|
428 {
|
paulo@0
|
429 GT->DBGFN (GT, "error from %s: %s", req->host, GIFT_NETERROR());
|
paulo@0
|
430 gt_http_request_close (req, -1);
|
paulo@0
|
431 return;
|
paulo@0
|
432 }
|
paulo@0
|
433
|
paulo@0
|
434 /* terminate the buffer */
|
paulo@0
|
435 data[n] = 0;
|
paulo@0
|
436
|
paulo@0
|
437 if (n == 0)
|
paulo@0
|
438 {
|
paulo@0
|
439 /* signal to the listener that EOF was reached */
|
paulo@0
|
440 if (!write_data (req, NULL, 0))
|
paulo@0
|
441 return;
|
paulo@0
|
442
|
paulo@0
|
443 gt_http_request_close (req, 200);
|
paulo@0
|
444 return;
|
paulo@0
|
445 }
|
paulo@0
|
446
|
paulo@0
|
447 if (!write_data (req, data, n))
|
paulo@0
|
448 return;
|
paulo@0
|
449 }
|
paulo@0
|
450
|
paulo@0
|
451 static void reset_request (HttpRequest *req, const char *host,
|
paulo@0
|
452 const char *path)
|
paulo@0
|
453 {
|
paulo@0
|
454 free (req->host);
|
paulo@0
|
455 free (req->path);
|
paulo@0
|
456 req->host = STRDUP (host);
|
paulo@0
|
457 req->path = STRDUP (path);
|
paulo@0
|
458
|
paulo@0
|
459 dataset_clear (req->headers);
|
paulo@0
|
460 req->headers = NULL;
|
paulo@0
|
461 }
|
paulo@0
|
462
|
paulo@0
|
463 /*
|
paulo@0
|
464 * This will do a limited redirect on the same connection.
|
paulo@0
|
465 * One bug is it doesn't care if the Location header posts a different port,
|
paulo@0
|
466 */
|
paulo@0
|
467 static void handle_redirect (HttpRequest *req, int code)
|
paulo@0
|
468 {
|
paulo@0
|
469 char *new_host;
|
paulo@0
|
470 char *new_path;
|
paulo@0
|
471 char *location;
|
paulo@0
|
472
|
paulo@0
|
473 /* make sure the Location: header points to the same host */
|
paulo@0
|
474 location = dataset_lookupstr (req->headers, "location");
|
paulo@0
|
475
|
paulo@0
|
476 /* butchers Location header, but it will be freed soon anyway */
|
paulo@0
|
477 if (!location ||
|
paulo@0
|
478 !gt_http_url_parse (location, &new_host, &new_path))
|
paulo@0
|
479 {
|
paulo@0
|
480 gt_http_request_close (req, code);
|
paulo@0
|
481 return;
|
paulo@0
|
482 }
|
paulo@0
|
483
|
paulo@0
|
484 assert (new_host != NULL);
|
paulo@0
|
485
|
paulo@0
|
486 if (++req->redirects >= MAX_REDIRECTS)
|
paulo@0
|
487 {
|
paulo@0
|
488 GT->DBGSOCK (GT, req->c, "Too many redirects");
|
paulo@0
|
489 gt_http_request_close (req, code);
|
paulo@0
|
490 return;
|
paulo@0
|
491 }
|
paulo@0
|
492
|
paulo@0
|
493 /*
|
paulo@0
|
494 * Let the caller know we're redirecting so it can reset it's ancilliary
|
paulo@0
|
495 * data.
|
paulo@0
|
496 */
|
paulo@0
|
497 if (req->redirect_func (req, new_host, new_path) == FALSE)
|
paulo@0
|
498 {
|
paulo@0
|
499 gt_http_request_close (req, code);
|
paulo@0
|
500 return;
|
paulo@0
|
501 }
|
paulo@0
|
502
|
paulo@0
|
503 /* setup the new request */
|
paulo@0
|
504 reset_request (req, new_host, new_path);
|
paulo@0
|
505
|
paulo@0
|
506 /* restart the request */
|
paulo@0
|
507 input_remove_all (req->c->fd);
|
paulo@0
|
508 input_add (req->c->fd, req->c, INPUT_WRITE,
|
paulo@0
|
509 (InputCallback)gt_http_request_handle, TIMEOUT_DEF);
|
paulo@0
|
510 }
|
paulo@0
|
511
|
paulo@0
|
512 static BOOL parse_server_response (char *reply, HttpRequest *req)
|
paulo@0
|
513 {
|
paulo@0
|
514 char *response;
|
paulo@0
|
515 int code; /* 200, 404, ... */
|
paulo@0
|
516
|
paulo@0
|
517 response = string_sep (&reply, "\r\n");
|
paulo@0
|
518
|
paulo@0
|
519 if (!response)
|
paulo@0
|
520 return FALSE;
|
paulo@0
|
521
|
paulo@0
|
522 /* */ string_sep (&response, " "); /* shift past HTTP/1.1 */
|
paulo@0
|
523 code = ATOI (string_sep (&response, " ")); /* shift past 200 */
|
paulo@0
|
524
|
paulo@0
|
525 /* parse the headers */
|
paulo@0
|
526 gt_http_header_parse (reply, &req->headers);
|
paulo@0
|
527
|
paulo@0
|
528 if (code >= 200 && code <= 299)
|
paulo@0
|
529 return TRUE;
|
paulo@0
|
530
|
paulo@0
|
531 /* redirection */
|
paulo@0
|
532 if (code >= 300 && code <= 399)
|
paulo@0
|
533 {
|
paulo@0
|
534 handle_redirect (req, code);
|
paulo@0
|
535 return FALSE; /* stop this request */
|
paulo@0
|
536 }
|
paulo@0
|
537
|
paulo@0
|
538 /* request error: could blacklist the server in recv_callback */
|
paulo@0
|
539 GT->DBGFN (GT, "error parsing response from %s, closing", req->host);
|
paulo@0
|
540 gt_http_request_close (req, code);
|
paulo@0
|
541
|
paulo@0
|
542 return FALSE;
|
paulo@0
|
543 }
|
paulo@0
|
544
|
paulo@0
|
545 static void read_headers (int fd, input_id id, TCPC *c)
|
paulo@0
|
546 {
|
paulo@0
|
547 HttpRequest *req;
|
paulo@0
|
548 FDBuf *buf;
|
paulo@0
|
549 char *response;
|
paulo@0
|
550 size_t response_len = 0;
|
paulo@0
|
551 char *encoding;
|
paulo@0
|
552 char *len_str;
|
paulo@0
|
553 int n;
|
paulo@0
|
554
|
paulo@0
|
555 req = get_request (c);
|
paulo@0
|
556 buf = tcp_readbuf (c);
|
paulo@0
|
557
|
paulo@0
|
558 if ((n = fdbuf_delim (buf, "\n")) < 0)
|
paulo@0
|
559 {
|
paulo@0
|
560 GT->DBGFN (GT, "error reading from %s: %s", net_peer_ip (c->fd),
|
paulo@0
|
561 GIFT_NETERROR ());
|
paulo@0
|
562 gt_http_request_close (req, -1);
|
paulo@0
|
563 return;
|
paulo@0
|
564 }
|
paulo@0
|
565
|
paulo@0
|
566 if (gt_fdbuf_full (buf))
|
paulo@0
|
567 {
|
paulo@0
|
568 gt_http_request_close (req, -1);
|
paulo@0
|
569 return;
|
paulo@0
|
570 }
|
paulo@0
|
571
|
paulo@0
|
572 if (n > 0)
|
paulo@0
|
573 return;
|
paulo@0
|
574
|
paulo@0
|
575 response = fdbuf_data (buf, &response_len);
|
paulo@0
|
576
|
paulo@0
|
577 if (response_len >= req->max_len)
|
paulo@0
|
578 {
|
paulo@0
|
579 GT->DBGFN (GT, "headers too large(%lu)", (long)response_len);
|
paulo@0
|
580 gt_http_request_close (req, -1);
|
paulo@0
|
581 }
|
paulo@0
|
582
|
paulo@0
|
583 if (!gt_http_header_terminated (response, response_len))
|
paulo@0
|
584 return;
|
paulo@0
|
585
|
paulo@0
|
586 fdbuf_release (buf);
|
paulo@0
|
587 GT->DBGFN (GT, "response=\n%s", response);
|
paulo@0
|
588
|
paulo@0
|
589 if (!parse_server_response (response, req))
|
paulo@0
|
590 return;
|
paulo@0
|
591
|
paulo@0
|
592 input_remove (id);
|
paulo@0
|
593
|
paulo@0
|
594 encoding = dataset_lookupstr (req->headers, "transfer-encoding");
|
paulo@0
|
595
|
paulo@0
|
596 if (encoding && !strcasecmp (encoding, "chunked"))
|
paulo@0
|
597 {
|
paulo@0
|
598 input_add (fd, c, INPUT_READ,
|
paulo@0
|
599 (InputCallback)read_chunked_header, TIMEOUT_DEF);
|
paulo@0
|
600 return;
|
paulo@0
|
601 }
|
paulo@0
|
602
|
paulo@0
|
603 if (!(len_str = dataset_lookupstr (req->headers, "content-length")))
|
paulo@0
|
604 {
|
paulo@0
|
605 GT->warn (GT, "no Content-Length header from %s", req->host);
|
paulo@0
|
606 input_add (fd, c, INPUT_READ,
|
paulo@0
|
607 (InputCallback)read_until_eof, TIMEOUT_DEF);
|
paulo@0
|
608 return;
|
paulo@0
|
609 }
|
paulo@0
|
610
|
paulo@0
|
611 req->size = ATOUL (len_str);
|
paulo@0
|
612
|
paulo@0
|
613 if (req->max_len > 0 && req->size >= req->max_len)
|
paulo@0
|
614 {
|
paulo@0
|
615 GT->DBGFN (GT, "bad size (%s) in content length field for %s",
|
paulo@0
|
616 len_str, req->host);
|
paulo@0
|
617 gt_http_request_close (req, -1);
|
paulo@0
|
618 return;
|
paulo@0
|
619 }
|
paulo@0
|
620
|
paulo@0
|
621 input_add (fd, c, INPUT_READ,
|
paulo@0
|
622 (InputCallback)read_file, TIMEOUT_DEF);
|
paulo@0
|
623 }
|
paulo@0
|
624
|
paulo@0
|
625 /*
|
paulo@0
|
626 * Determine the part after the GET. If proxied, this need to be a complete
|
paulo@0
|
627 * URL, and otherwise should be a simple path.
|
paulo@0
|
628 */
|
paulo@0
|
629 static void append_request_line (String *s, HttpRequest *req)
|
paulo@0
|
630 {
|
paulo@0
|
631 if (req->proxy)
|
paulo@0
|
632 string_appendf (s, "http://%s", req->host);
|
paulo@0
|
633
|
paulo@0
|
634 string_appendf (s, "/%s", STRING_NOTNULL(req->path));
|
paulo@0
|
635 }
|
paulo@0
|
636
|
paulo@0
|
637 static int send_request (HttpRequest *req)
|
paulo@0
|
638 {
|
paulo@0
|
639 Dataset *headers = NULL;
|
paulo@0
|
640 String *s;
|
paulo@0
|
641 int ret;
|
paulo@0
|
642
|
paulo@0
|
643 if (!(s = string_new (NULL, 0, 0, TRUE)))
|
paulo@0
|
644 return -1;
|
paulo@0
|
645
|
paulo@0
|
646 append_request_line (s, req);
|
paulo@0
|
647
|
paulo@0
|
648 if (!string_isempty (req->request))
|
paulo@0
|
649 string_appendf (s, "?%s", req->request);
|
paulo@0
|
650
|
paulo@0
|
651 dataset_insertstr (&headers, "Host", req->host); /* required by HTTP/1.1 */
|
paulo@0
|
652 dataset_insertstr (&headers, "User-Agent", gt_version ());
|
paulo@0
|
653
|
paulo@0
|
654 if (req->add_header_func (req, &headers) == FALSE)
|
paulo@0
|
655 {
|
paulo@0
|
656 /* Hmm, this is our error, what should the error code be */
|
paulo@0
|
657 gt_http_request_close (req, -1);
|
paulo@0
|
658 dataset_clear (headers);
|
paulo@0
|
659 string_free (s);
|
paulo@0
|
660 return -1;
|
paulo@0
|
661 }
|
paulo@0
|
662
|
paulo@0
|
663 ret = http_send (req->c, "GET", s->str, headers);
|
paulo@0
|
664
|
paulo@0
|
665 dataset_clear (headers);
|
paulo@0
|
666 string_free (s);
|
paulo@0
|
667
|
paulo@0
|
668 return ret;
|
paulo@0
|
669 }
|
paulo@0
|
670
|
paulo@0
|
671 void gt_http_request_handle (int fd, input_id id, TCPC *c)
|
paulo@0
|
672 {
|
paulo@0
|
673 HttpRequest *req;
|
paulo@0
|
674
|
paulo@0
|
675 req = get_request (c);
|
paulo@0
|
676
|
paulo@0
|
677 if (send_request (req) <= 0)
|
paulo@0
|
678 {
|
paulo@0
|
679 GT->DBGFN (GT, "send failed: %s", GIFT_NETERROR());
|
paulo@0
|
680 gt_http_request_close (req, -1);
|
paulo@0
|
681 return;
|
paulo@0
|
682 }
|
paulo@0
|
683
|
paulo@0
|
684 input_remove (id);
|
paulo@0
|
685 input_add (fd, c, INPUT_READ,
|
paulo@0
|
686 (InputCallback)read_headers, TIMEOUT_DEF);
|
paulo@0
|
687 }
|