Changeset 8bd866f


Ignore:
Timestamp:
2012-11-10T22:25:58Z (11 years ago)
Author:
Wilmer van der Gaast <wilmer@…>
Branches:
master
Children:
ddc2de5
Parents:
9e8c945
Message:

Reworked http_client a little bit to support streaming besides just buffering
a complete response before giving it back to the caller.

Location:
lib
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • lib/http_client.c

    r9e8c945 r8bd866f  
    193193}
    194194
     195static gboolean http_handle_headers( struct http_request *req );
     196
    195197static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond )
    196198{
    197199        struct http_request *req = data;
    198         int evil_server = 0;
    199200        char buffer[2048];
    200         char *end1, *end2, *s;
     201        char *s;
    201202        size_t content_length;
    202203        int st;
     
    218219                                   packets that abort connections! \o/ */
    219220                               
    220                                 goto got_reply;
     221                                goto eof;
    221222                        }
    222223                }
    223224                else if( st == 0 )
    224225                {
    225                         goto got_reply;
     226                        goto eof;
    226227                }
    227228        }
     
    239240                else if( st == 0 )
    240241                {
    241                         goto got_reply;
    242                 }
    243         }
    244        
    245         if( st > 0 )
     242                        goto eof;
     243                }
     244        }
     245       
     246        if( st > 0 && !req->sbuf )
    246247        {
    247248                req->reply_headers = g_realloc( req->reply_headers, req->bytes_read + st + 1 );
    248249                memcpy( req->reply_headers + req->bytes_read, buffer, st );
    249250                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 );
    251294       
    252295        /* There will be more! */
     
    255298                                 http_incoming_data, req );
    256299       
    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
     302eof:
    263303        /* Maybe if the webserver is overloaded, or when there's bad SSL
    264304           support... */
     
    269309        }
    270310       
     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
     319cleanup:
     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! */
     348static gboolean http_handle_headers( struct http_request *req )
     349{
     350        char *end1, *end2;
     351        int evil_server = 0;
     352       
    271353        /* Zero termination is very convenient. */
    272         req->reply_headers[req->bytes_read] = 0;
     354        req->reply_headers[req->bytes_read] = '\0';
    273355       
    274356        /* Find the separation between headers and body, and keep stupid
     
    289371        {
    290372                req->status_string = g_strdup( "Malformed HTTP reply" );
    291                 goto cleanup;
     373                return TRUE;
    292374        }
    293375       
     
    306388        if( ( end1 = strchr( req->reply_headers, ' ' ) ) != NULL )
    307389        {
    308                 if( sscanf( end1 + 1, "%d", &req->status_code ) != 1 )
     390                if( sscanf( end1 + 1, "%hd", &req->status_code ) != 1 )
    309391                {
    310392                        req->status_string = g_strdup( "Can't parse status code" );
     
    349431                {
    350432                        req->status_string = g_strdup( "Can't locate Location: header" );
    351                         goto cleanup;
     433                        return TRUE;
    352434                }
    353435               
     
    369451                        req->status_string = g_strdup( "Can't handle recursive redirects" );
    370452                       
    371                         goto cleanup;
     453                        return TRUE;
    372454                }
    373455                else
     
    380462                        s = strstr( loc, "\r\n" );
    381463                        if( s == NULL )
    382                                 goto cleanup;
     464                                return TRUE;
    383465                       
    384466                        url = g_new0( url_t, 1 );
     
    389471                                req->status_string = g_strdup( "Malformed redirect URL" );
    390472                                g_free( url );
    391                                 goto cleanup;
     473                                return TRUE;
    392474                        }
    393475                       
     
    401483                                req->status_string = g_strdup( "Error while rebuilding request string" );
    402484                                g_free( url );
    403                                 goto cleanup;
     485                                return TRUE;
    404486                        }
    405487                       
     
    467549                        req->status_string = g_strdup( "Connection problem during redirect" );
    468550                        g_free( new_request );
    469                         goto cleanup;
     551                        return TRUE;
    470552                }
    471553               
     
    480562        }
    481563       
    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
     567void 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        }
    511574}
    512575
     
    516579        g_free( req->reply_headers );
    517580        g_free( req->status_string );
     581        g_free( req->sbuf );
    518582        g_free( req );
    519583}
    520 
  • lib/http_client.h

    r9e8c945 r8bd866f  
    2626/* http_client allows you to talk (asynchronously, again) to HTTP servers.
    2727   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.
    3230   
    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. */
    3934
    4035#include <glib.h>
     
    4237
    4338struct http_request;
     39
     40typedef enum http_client_flags
     41{
     42        HTTPC_STREAMING = 1,
     43} http_client_flags_t;
    4444
    4545/* Your callback function should look like this: */
     
    5353        char *request;          /* The request to send to the server. */
    5454        int request_length;     /* Its size. */
    55         int status_code;        /* The numeric HTTP status code. (Or -1
     55        short status_code;      /* The numeric HTTP status code. (Or -1
    5656                                   if something really went wrong) */
    5757        char *status_string;    /* The error text. */
     
    5959        char *reply_body;
    6060        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
    6462                                   http_client to follow them. */
     63       
     64        http_client_flags_t flags;
    6565       
    6666        http_input_function func;
     
    6868       
    6969        /* Please don't touch the things down here, you shouldn't need them. */
    70        
    7170        void *ssl;
    7271        int fd;
     
    7574        int bytes_written;
    7675        int bytes_read;
     76       
     77        /* Used in streaming mode. Caller should read from reply_body. */
     78        char *sbuf;
     79        size_t sblen;
    7780};
    7881
     
    8386struct http_request *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data );
    8487struct 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. */
     90void http_flush_bytes( struct http_request *req, size_t len );
Note: See TracChangeset for help on using the changeset viewer.