Changeset 8bd866f for lib/http_client.c


Ignore:
Timestamp:
2012-11-10T22:25:58Z (8 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.

File:
1 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 
Note: See TracChangeset for help on using the changeset viewer.