Changes in lib/http_client.c [c1d9c95:dd7b931]
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
lib/http_client.c
rc1d9c95 rdd7b931 2 2 * BitlBee -- An IRC to other IM-networks gateway * 3 3 * * 4 * Copyright 2002-201 2Wilmer van der Gaast and others *4 * Copyright 2002-2013 Wilmer van der Gaast and others * 5 5 \********************************************************************/ 6 6 … … 69 69 req->request_length = strlen( request ); 70 70 req->redir_ttl = 3; 71 req->content_length = -1; 71 72 72 73 if( getenv( "BITLBEE_DEBUG" ) ) … … 96 97 request = g_strdup_printf( "GET %s HTTP/1.0\r\n" 97 98 "Host: %s\r\n" 98 "Connection: close\r\n"99 99 "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n" 100 100 "\r\n", url->file, url->host ); … … 193 193 } 194 194 195 typedef enum { 196 CR_OK, 197 CR_EOF, 198 CR_ERROR, 199 CR_ABORT, 200 } http_ret_t; 201 195 202 static gboolean http_handle_headers( struct http_request *req ); 203 static http_ret_t http_process_chunked_data( struct http_request *req, const char *buffer, int len ); 204 static http_ret_t http_process_data( struct http_request *req, const char *buffer, int len ); 196 205 197 206 static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond ) … … 199 208 struct http_request *req = data; 200 209 char buffer[4096]; 201 char *s;202 size_t content_length;203 210 int st; 204 211 … … 244 251 } 245 252 246 if( st > 0 && !req->sbuf ) 247 { 248 req->reply_headers = g_realloc( req->reply_headers, req->bytes_read + st + 1 ); 249 memcpy( req->reply_headers + req->bytes_read, buffer, st ); 250 req->bytes_read += st; 251 252 st = 0; 253 } 254 255 if( st >= 0 && ( req->flags & HTTPC_STREAMING ) ) 256 { 257 if( !req->reply_body && 258 ( strstr( req->reply_headers, "\r\n\r\n" ) || 259 strstr( req->reply_headers, "\n\n" ) ) ) 260 { 261 size_t hlen; 262 263 /* We've now received all headers, so process them once 264 before we start feeding back data. */ 265 if( !http_handle_headers( req ) ) 266 return FALSE; 267 268 hlen = req->reply_body - req->reply_headers; 269 270 req->sblen = req->bytes_read - hlen; 271 req->sbuf = g_memdup( req->reply_body, req->sblen + 1 ); 272 req->reply_headers = g_realloc( req->reply_headers, hlen + 1 ); 273 274 req->reply_body = req->sbuf; 275 } 276 277 if( st > 0 ) 278 { 279 int pos = req->reply_body - req->sbuf; 280 req->sbuf = g_realloc( req->sbuf, req->sblen + st + 1 ); 281 memcpy( req->sbuf + req->sblen, buffer, st ); 282 req->bytes_read += st; 283 req->sblen += st; 284 req->sbuf[req->sblen] = '\0'; 285 req->reply_body = req->sbuf + pos; 286 req->body_size = req->sblen - pos; 287 } 288 289 if( req->reply_body ) 290 req->func( req ); 291 } 253 if( st > 0 ) 254 { 255 http_ret_t c; 256 257 if( req->flags & HTTPC_CHUNKED ) 258 c = http_process_chunked_data( req, buffer, st ); 259 else 260 c = http_process_data( req, buffer, st ); 261 262 if( c == CR_EOF ) 263 goto eof; 264 else if( c == CR_ERROR || c == CR_ABORT ) 265 return FALSE; 266 } 267 268 if( req->content_length != -1 && 269 req->body_size >= req->content_length ) 270 goto eof; 292 271 293 272 if( ssl_pending( req->ssl ) ) … … 311 290 goto cleanup; 312 291 } 313 314 if( !( req->flags & HTTPC_STREAMING ) )315 {316 /* Returns FALSE if we were redirected, in which case we should abort317 and not run any callback yet. */318 if( !http_handle_headers( req ) )319 return FALSE;320 }321 292 322 293 cleanup: … … 326 297 closesocket( req->fd ); 327 298 328 if( ( s = get_rfc822_header( req->reply_headers, "Content-Length", 0 ) ) && 329 sscanf( s, "%zd", &content_length ) == 1 ) 330 { 331 if( content_length < req->body_size ) 332 { 333 req->status_code = -1; 334 g_free( req->status_string ); 335 req->status_string = g_strdup( "Response truncated" ); 336 } 337 } 338 g_free( s ); 299 if( req->body_size < req->content_length ) 300 { 301 req->status_code = -1; 302 g_free( req->status_string ); 303 req->status_string = g_strdup( "Response truncated" ); 304 } 339 305 340 306 if( getenv( "BITLBEE_DEBUG" ) && req ) … … 347 313 } 348 314 315 static http_ret_t http_process_chunked_data( struct http_request *req, const char *buffer, int len ) 316 { 317 char *chunk, *eos, *s; 318 319 if( len < 0 ) 320 return TRUE; 321 322 if( len > 0 ) 323 { 324 req->cbuf = g_realloc( req->cbuf, req->cblen + len + 1 ); 325 memcpy( req->cbuf + req->cblen, buffer, len ); 326 req->cblen += len; 327 req->cbuf[req->cblen] = '\0'; 328 } 329 330 /* Turns out writing a proper chunked-encoding state machine is not 331 that simple. :-( */ 332 chunk = req->cbuf; 333 eos = req->cbuf + req->cblen; 334 while( TRUE ) 335 { 336 int clen = 0; 337 338 /* Might be a \r\n from the last chunk. */ 339 s = chunk; 340 while( isspace( *s ) ) 341 s ++; 342 /* Chunk length. Might be incomplete. */ 343 if( s < eos && sscanf( s, "%x", &clen ) != 1 ) 344 return CR_ERROR; 345 while( isxdigit( *s ) ) 346 s ++; 347 348 /* If we read anything here, it *must* be \r\n. */ 349 if( strncmp( s, "\r\n", MIN( 2, eos - s ) ) != 0 ) 350 return CR_ERROR; 351 s += 2; 352 353 if( s >= eos ) 354 break; 355 356 /* 0-length chunk means end of response. */ 357 if( clen == 0 ) 358 return CR_EOF; 359 360 if( s + clen > eos ) 361 break; 362 if( http_process_data( req, s, clen ) != CR_OK ) 363 return CR_ABORT; 364 365 chunk = s + clen; 366 } 367 368 if( chunk != req->cbuf ) 369 { 370 req->cblen = eos - chunk; 371 s = g_memdup( chunk, req->cblen + 1 ); 372 g_free( req->cbuf ); 373 req->cbuf = s; 374 } 375 376 return CR_OK; 377 } 378 379 static http_ret_t http_process_data( struct http_request *req, const char *buffer, int len ) 380 { 381 if( len <= 0 ) 382 return CR_OK; 383 384 if( !req->reply_body ) 385 { 386 req->reply_headers = g_realloc( req->reply_headers, req->bytes_read + len + 1 ); 387 memcpy( req->reply_headers + req->bytes_read, buffer, len ); 388 req->bytes_read += len; 389 req->reply_headers[req->bytes_read] = '\0'; 390 391 if( strstr( req->reply_headers, "\r\n\r\n" ) || 392 strstr( req->reply_headers, "\n\n" ) ) 393 { 394 /* We've now received all headers. Look for something 395 interesting. */ 396 if( !http_handle_headers( req ) ) 397 return CR_ABORT; 398 399 /* Start parsing the body as chunked if required. */ 400 if( req->flags & HTTPC_CHUNKED ) 401 return http_process_chunked_data( req, NULL, 0 ); 402 } 403 } 404 else 405 { 406 int pos = req->reply_body - req->sbuf; 407 req->sbuf = g_realloc( req->sbuf, req->sblen + len + 1 ); 408 memcpy( req->sbuf + req->sblen, buffer, len ); 409 req->bytes_read += len; 410 req->sblen += len; 411 req->sbuf[req->sblen] = '\0'; 412 req->reply_body = req->sbuf + pos; 413 req->body_size = req->sblen - pos; 414 } 415 416 if( ( req->flags & HTTPC_STREAMING ) && req->reply_body ) 417 req->func( req ); 418 419 return CR_OK; 420 } 421 349 422 /* Splits headers and body. Checks result code, in case of 300s it'll handle 350 423 redirects. If this returns FALSE, don't call any callbacks! */ 351 424 static gboolean http_handle_headers( struct http_request *req ) 352 425 { 353 char *end1, *end2 ;426 char *end1, *end2, *s; 354 427 int evil_server = 0; 355 428 … … 377 450 } 378 451 379 *end1 = 0;452 *end1 = '\0'; 380 453 381 454 if( getenv( "BITLBEE_DEBUG" ) ) … … 387 460 req->reply_body = end1 + 2; 388 461 389 req->body_size = req->reply_headers + req->bytes_read - req->reply_body; 462 /* Separately allocated space for headers and body. */ 463 req->sblen = req->body_size = req->reply_headers + req->bytes_read - req->reply_body; 464 req->sbuf = req->reply_body = g_memdup( req->reply_body, req->body_size + 1 ); 465 req->reply_headers = g_realloc( req->reply_headers, end1 - req->reply_headers + 1 ); 390 466 391 467 if( ( end1 = strchr( req->reply_headers, ' ' ) ) != NULL ) … … 452 528 don't need this yet anyway, I won't implement it. */ 453 529 454 req->status_string = g_strdup( "Can't handle re cursive redirects" );530 req->status_string = g_strdup( "Can't handle relative redirects" ); 455 531 456 532 return TRUE; … … 460 536 /* A whole URL */ 461 537 url_t *url; 462 char *s ;538 char *s, *version, *headers; 463 539 const char *new_method; 464 540 … … 488 564 return TRUE; 489 565 } 566 headers = s; 490 567 491 568 /* More or less HTTP/1.0 compliant, from my reading of RFC 2616. … … 507 584 new_method = "POST"; 508 585 586 if( ( version = strstr( req->request, " HTTP/" ) ) && 587 ( s = strstr( version, "\r\n" ) ) ) 588 { 589 version ++; 590 version = g_strndup( version, s - version ); 591 } 592 else 593 version = g_strdup( "HTTP/1.0" ); 594 509 595 /* Okay, this isn't fun! We have to rebuild the request... :-( */ 510 new_request = g_strdup_printf( "%s %s HTTP/1.0\r\nHost: %s%s", 511 new_method, url->file, url->host, s ); 596 new_request = g_strdup_printf( "%s %s %s\r\nHost: %s%s", 597 new_method, url->file, version, 598 url->host, headers ); 512 599 513 600 new_host = g_strdup( url->host ); … … 521 608 522 609 g_free( url ); 610 g_free( version ); 523 611 } 524 612 … … 564 652 return FALSE; 565 653 } 654 655 if( ( s = get_rfc822_header( req->reply_headers, "Content-Length", 0 ) ) && 656 sscanf( s, "%d", &req->content_length ) != 1 ) 657 req->content_length = -1; 658 g_free( s ); 659 660 if( ( s = get_rfc822_header( req->reply_headers, "Transfer-Encoding", 0 ) ) ) 661 { 662 if( strcasestr( s, "chunked" ) ) 663 { 664 req->flags |= HTTPC_CHUNKED; 665 req->cbuf = req->sbuf; 666 req->cblen = req->sblen; 667 668 req->reply_body = req->sbuf = g_strdup( "" ); 669 req->body_size = req->sblen = 0; 670 } 671 g_free( s ); 672 } 566 673 567 674 return TRUE; … … 607 714 g_free( req->status_string ); 608 715 g_free( req->sbuf ); 716 g_free( req->cbuf ); 609 717 g_free( req ); 610 718 }
Note: See TracChangeset
for help on using the changeset viewer.