annotate src/http_request.c @ 0:d39e1d0d75b6

initial add
author paulo@hit-nxdomain.opendns.com
date Sat, 20 Feb 2010 21:18:28 -0800
parents
children
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 }