Changeset 8bd866f
- Timestamp:
- 2012-11-10T22:25:58Z (12 years ago)
- Branches:
- master
- Children:
- ddc2de5
- Parents:
- 9e8c945
- Location:
- lib
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
lib/http_client.c
r9e8c945 r8bd866f 193 193 } 194 194 195 static gboolean http_handle_headers( struct http_request *req ); 196 195 197 static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond ) 196 198 { 197 199 struct http_request *req = data; 198 int evil_server = 0;199 200 char buffer[2048]; 200 char * end1, *end2, *s;201 char *s; 201 202 size_t content_length; 202 203 int st; … … 218 219 packets that abort connections! \o/ */ 219 220 220 goto got_reply;221 goto eof; 221 222 } 222 223 } 223 224 else if( st == 0 ) 224 225 { 225 goto got_reply;226 goto eof; 226 227 } 227 228 } … … 239 240 else if( st == 0 ) 240 241 { 241 goto got_reply;242 } 243 } 244 245 if( st > 0 )242 goto eof; 243 } 244 } 245 246 if( st > 0 && !req->sbuf ) 246 247 { 247 248 req->reply_headers = g_realloc( req->reply_headers, req->bytes_read + st + 1 ); 248 249 memcpy( req->reply_headers + req->bytes_read, buffer, st ); 249 250 req->bytes_read += st; 250 } 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->reply_body = req->sbuf + pos; 285 req->body_size = req->sblen - pos; 286 } 287 288 if( req->reply_body ) 289 req->func( req ); 290 } 291 292 if( ssl_pending( req->ssl ) ) 293 return http_incoming_data( data, source, cond ); 251 294 252 295 /* There will be more! */ … … 255 298 http_incoming_data, req ); 256 299 257 if( ssl_pending( req->ssl ) ) 258 return http_incoming_data( data, source, cond ); 259 else 260 return FALSE; 261 262 got_reply: 300 return FALSE; 301 302 eof: 263 303 /* Maybe if the webserver is overloaded, or when there's bad SSL 264 304 support... */ … … 269 309 } 270 310 311 if( !( req->flags & HTTPC_STREAMING ) ) 312 { 313 /* Returns FALSE if we were redirected, in which case we should abort 314 and not run any callback yet. */ 315 if( !http_handle_headers( req ) ) 316 return FALSE; 317 } 318 319 cleanup: 320 if( req->ssl ) 321 ssl_disconnect( req->ssl ); 322 else 323 closesocket( req->fd ); 324 325 if( ( s = get_rfc822_header( req->reply_headers, "Content-Length", 0 ) ) && 326 sscanf( s, "%zd", &content_length ) == 1 ) 327 { 328 if( content_length < req->body_size ) 329 { 330 req->status_code = -1; 331 g_free( req->status_string ); 332 req->status_string = g_strdup( "Response truncated" ); 333 } 334 } 335 g_free( s ); 336 337 if( getenv( "BITLBEE_DEBUG" ) && req ) 338 printf( "Finishing HTTP request with status: %s\n", 339 req->status_string ? req->status_string : "NULL" ); 340 341 req->func( req ); 342 http_free( req ); 343 return FALSE; 344 } 345 346 /* Splits headers and body. Checks result code, in case of 300s it'll handle 347 redirects. If this returns FALSE, don't call any callbacks! */ 348 static gboolean http_handle_headers( struct http_request *req ) 349 { 350 char *end1, *end2; 351 int evil_server = 0; 352 271 353 /* Zero termination is very convenient. */ 272 req->reply_headers[req->bytes_read] = 0;354 req->reply_headers[req->bytes_read] = '\0'; 273 355 274 356 /* Find the separation between headers and body, and keep stupid … … 289 371 { 290 372 req->status_string = g_strdup( "Malformed HTTP reply" ); 291 goto cleanup;373 return TRUE; 292 374 } 293 375 … … 306 388 if( ( end1 = strchr( req->reply_headers, ' ' ) ) != NULL ) 307 389 { 308 if( sscanf( end1 + 1, "% d", &req->status_code ) != 1 )390 if( sscanf( end1 + 1, "%hd", &req->status_code ) != 1 ) 309 391 { 310 392 req->status_string = g_strdup( "Can't parse status code" ); … … 349 431 { 350 432 req->status_string = g_strdup( "Can't locate Location: header" ); 351 goto cleanup;433 return TRUE; 352 434 } 353 435 … … 369 451 req->status_string = g_strdup( "Can't handle recursive redirects" ); 370 452 371 goto cleanup;453 return TRUE; 372 454 } 373 455 else … … 380 462 s = strstr( loc, "\r\n" ); 381 463 if( s == NULL ) 382 goto cleanup;464 return TRUE; 383 465 384 466 url = g_new0( url_t, 1 ); … … 389 471 req->status_string = g_strdup( "Malformed redirect URL" ); 390 472 g_free( url ); 391 goto cleanup;473 return TRUE; 392 474 } 393 475 … … 401 483 req->status_string = g_strdup( "Error while rebuilding request string" ); 402 484 g_free( url ); 403 goto cleanup;485 return TRUE; 404 486 } 405 487 … … 467 549 req->status_string = g_strdup( "Connection problem during redirect" ); 468 550 g_free( new_request ); 469 goto cleanup;551 return TRUE; 470 552 } 471 553 … … 480 562 } 481 563 482 /* Assume that a closed connection means we're finished, this indeed 483 breaks with keep-alive connections and faulty connections. */ 484 /* req->finished = 1; */ 485 486 cleanup: 487 if( req->ssl ) 488 ssl_disconnect( req->ssl ); 489 else 490 closesocket( req->fd ); 491 492 if( ( s = get_rfc822_header( req->reply_headers, "Content-Length", 0 ) ) && 493 sscanf( s, "%zd", &content_length ) == 1 ) 494 { 495 if( content_length < req->body_size ) 496 { 497 req->status_code = -1; 498 g_free( req->status_string ); 499 req->status_string = g_strdup( "Response truncated" ); 500 } 501 } 502 g_free( s ); 503 504 if( getenv( "BITLBEE_DEBUG" ) && req ) 505 printf( "Finishing HTTP request with status: %s\n", 506 req->status_string ? req->status_string : "NULL" ); 507 508 req->func( req ); 509 http_free( req ); 510 return FALSE; 564 return TRUE; 565 } 566 567 void http_flush_bytes( struct http_request *req, size_t len ) 568 { 569 if( len > 0 && len <= req->body_size ) 570 { 571 req->reply_body += len; 572 req->body_size -= len; 573 } 511 574 } 512 575 … … 516 579 g_free( req->reply_headers ); 517 580 g_free( req->status_string ); 581 g_free( req->sbuf ); 518 582 g_free( req ); 519 583 } 520 -
lib/http_client.h
r9e8c945 r8bd866f 26 26 /* http_client allows you to talk (asynchronously, again) to HTTP servers. 27 27 In the "background" it will send the whole query and wait for a complete 28 response to come back. Right now it's only used by the MSN Passport 29 authentication code, but it might be useful for other things too (for 30 example the AIM usericon patch uses this so icons can be stored on 31 webservers instead of the local filesystem). 28 response to come back. Initially written for MS Passport authentication, 29 but used for many other things now like OAuth and Twitter. 32 30 33 Didn't test this too much, but it seems to work well. Just don't look 34 at the code that handles HTTP 30x redirects. ;-) The function is 35 probably not very useful for downloading lots of data since it keeps 36 everything in a memory buffer until the download is completed (and 37 can't pass any data or whatever before then). It's very useful for 38 doing quick requests without blocking the whole program, though. */ 31 It's very useful for doing quick requests without blocking the whole 32 program. Unfortunately it doesn't support fancy stuff like HTTP keep- 33 alives. */ 39 34 40 35 #include <glib.h> … … 42 37 43 38 struct http_request; 39 40 typedef enum http_client_flags 41 { 42 HTTPC_STREAMING = 1, 43 } http_client_flags_t; 44 44 45 45 /* Your callback function should look like this: */ … … 53 53 char *request; /* The request to send to the server. */ 54 54 int request_length; /* Its size. */ 55 int status_code;/* The numeric HTTP status code. (Or -155 short status_code; /* The numeric HTTP status code. (Or -1 56 56 if something really went wrong) */ 57 57 char *status_string; /* The error text. */ … … 59 59 char *reply_body; 60 60 int body_size; /* The number of bytes in reply_body. */ 61 /* int finished; Set to non-0 if the request was completed 62 successfully. */ 63 int redir_ttl; /* You can set it to 0 if you don't want 61 short redir_ttl; /* You can set it to 0 if you don't want 64 62 http_client to follow them. */ 63 64 http_client_flags_t flags; 65 65 66 66 http_input_function func; … … 68 68 69 69 /* Please don't touch the things down here, you shouldn't need them. */ 70 71 70 void *ssl; 72 71 int fd; … … 75 74 int bytes_written; 76 75 int bytes_read; 76 77 /* Used in streaming mode. Caller should read from reply_body. */ 78 char *sbuf; 79 size_t sblen; 77 80 }; 78 81 … … 83 86 struct http_request *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data ); 84 87 struct http_request *http_dorequest_url( char *url_string, http_input_function func, gpointer data ); 88 89 /* For streaming connections only; flushes len bytes at the start of the buffer. */ 90 void http_flush_bytes( struct http_request *req, size_t len );
Note: See TracChangeset
for help on using the changeset viewer.