source: trunk/libtransmission/http.c @ 2343

Last change on this file since 2343 was 2343, checked in by joshe, 15 years ago

Change a couple functions to take an in_addr pointer instead of an in_addr.
Forward declare struct in_addr and include the relevant headers in the .c files where it's used.

  • Property svn:keywords set to Date Rev Author Id
File size: 22.1 KB
Line 
1/******************************************************************************
2 * $Id: http.c 2343 2007-07-14 16:29:21Z joshe $
3 *
4 * Copyright (c) 2006-2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <netinet/in.h>
28#include <arpa/inet.h>
29#include <stdarg.h>
30
31#include "transmission.h"
32#include "http.h"
33#include "net.h"
34
35#define HTTP_PORT               80      /* default http port 80 */
36#define HTTP_TIMEOUT            60000   /* one minute http timeout */
37#define HTTP_BUFSIZE            1500    /* 1.5K buffer size increment */
38#define LF                      "\012"
39#define CR                      "\015"
40#define SP( cc )                ( ' ' == (cc)  || '\t' == (cc) )
41#define NL( cc )                ( '\015' == (cc) || '\012' == (cc) )
42#define NUM( cc )               ( '0' <= (cc) && '9' >= (cc) )
43#define ALEN( aa )              ( (int)(sizeof( (aa) ) / sizeof( (aa)[0] ) ) )
44#define SKIP( off, len, done ) \
45    while( (off) < (len) && (done) ) { (off)++; };
46
47static const char *
48slice( const char * data, int * len, const char * delim );
49static tr_tristate_t
50sendrequest( tr_http_t * http );
51static tr_tristate_t
52receiveresponse( tr_http_t * http );
53static int
54checklength( tr_http_t * http );
55static int
56learnlength( tr_http_t * http );
57
58#define EXPANDBUF( bs )         &(bs).buf, &(bs).used, &(bs).size
59struct buf {
60    char         * buf;
61    int            size;
62    int            used;
63};
64
65struct tr_http_s {
66#define HTTP_STATE_CONSTRUCT    1
67#define HTTP_STATE_RESOLVE      2
68#define HTTP_STATE_CONNECT      3
69#define HTTP_STATE_RECEIVE      4
70#define HTTP_STATE_DONE         5
71#define HTTP_STATE_ERROR        6
72    char           state;
73#define HTTP_LENGTH_UNKNOWN     1
74#define HTTP_LENGTH_EOF         2
75#define HTTP_LENGTH_FIXED       3
76#define HTTP_LENGTH_CHUNKED     4
77    char           lengthtype;
78    tr_resolve_t * resolve;
79    char         * host;
80    int            port;
81    int            sock;
82    struct buf     header;
83    struct buf     body;
84    uint64_t       date;
85    /*
86      eof: unused
87      fixed: lenptr is the start of the body, lenint is the body length
88      chunked: lenptr is start of chunk (after length), lenint is chunk size
89    */
90    int            chunkoff;
91    int            chunklen;
92};
93
94int
95tr_httpRequestType( const char * data, int len, char ** method, char ** uri )
96{
97    const char * words[6];
98    int          ii, ret;
99    const char * end;
100
101    /* find the end of the line */
102    for( end = data; data + len > end; end++ )
103    {
104        if( NL( *data) )
105        {
106            break;
107        }
108    }
109
110    /* find the first three "words" in the line */
111    for( ii = 0; ALEN( words ) > ii && data < end; ii++ )
112    {
113        /* find the next space or non-space */
114        while( data < end && ( ii % 2 ? !SP( *data ) : SP( *data ) ) )
115        {
116            data++;
117        }
118
119        /* save the beginning of the word */
120        words[ii] = data;
121    }
122
123    /* check for missing words */
124    if( ALEN( words) > ii )
125    {
126        return -1;
127    }
128
129    /* parse HTTP version */
130    ret = -1;
131    if( 8 <= words[5] - words[4] )
132    {
133        if( 0 == tr_strncasecmp( words[4], "HTTP/1.1", 8 ) )
134        {
135            ret = 11;
136        }
137        else if( 0 == tr_strncasecmp( words[4], "HTTP/1.0", 8 ) )
138        {
139            ret = 10;
140        }
141    }
142
143    /* copy the method */
144    if( 0 <= ret && NULL != method )
145    {
146        *method = tr_strndup( words[0], words[1] - words[0] );
147        if( NULL == *method )
148        {
149            ret = -1;
150        }
151    }
152    /* copy uri */
153    if( 0 <= ret && NULL != uri )
154    {
155        *uri = tr_strndup( words[2], words[3] - words[2] );
156        if( NULL == *uri )
157        {
158            free( *method );
159            ret = -1;
160        }
161    }
162
163    return ret;
164}
165
166int
167tr_httpResponseCode( const char * data, int len )
168{
169    char code[4];
170    int ret;
171
172    /* check for the minimum legal length */
173    if( 12 > len ||
174    /* check for valid http version */
175        0 != tr_strncasecmp( data, "HTTP/1.", 7 ) ||
176        ( '1' != data[7] && '0' != data[7] ) ||
177    /* there should be at least one space after the version */
178        !SP( data[8] ) )
179    {
180        return -1;
181    }
182
183    /* skip any extra spaces */
184    data += 9;
185    len -= 9;
186    while( 0 < len && SP( *data ) )
187    {
188        data++;
189        len--;
190    }
191
192    /* check for a valid three-digit code */
193    if( 3 > len || !NUM( data[0] ) || !NUM( data[1] ) || !NUM( data[2] ) ||
194        ( 3 < len && NUM( data[3] ) ) )
195    {
196        return -1;
197    }
198
199    /* parse and return the code */
200    memcpy( code, data, 3 );
201    code[3] = '\0';
202    ret = strtol( code, NULL, 10 );
203    if( 100 > ret )
204    {
205        ret = -1;
206    }
207
208    return ret;
209}
210
211char *
212tr_httpParse( const char * data, int len, tr_http_header_t *headers )
213{
214    const char * body, * begin;
215    int          ii, jj, full;
216
217    /* find the end of the http headers */
218    body = slice( data, &len, CR LF CR LF );
219    if( NULL == body )
220    {
221        body = slice( data, &len, LF LF );
222        if( NULL == body )
223        {
224            body = slice( data, &len, CR CR );
225            if( NULL == body )
226            {
227                return NULL;
228            }
229        }
230    }
231
232    /* return if no headers were requested */
233    if( NULL == headers || NULL == headers[0].name )
234    {
235        return (char*) body;
236    }
237
238    /* NULL out all the header's data pointers */
239    for( ii = 0; NULL != headers[ii].name; ii++ )
240    {
241        headers[ii].data = NULL;
242        headers[ii].len = 0;
243    }
244
245    /* skip the http request or response line */
246    ii = 0;
247    SKIP( ii, len, !NL( data[ii] ) );
248    SKIP( ii, len, NL( data[ii] ) );
249
250    /* find the requested headers */
251    while(ii < len )
252    {
253        /* skip leading spaces and find the header name */
254        SKIP( ii, len, SP( data[ii] ) );
255        begin = data + ii;
256        SKIP( ii, len, ':' != data[ii] && !NL( data[ii] ) );
257
258        if( ':' == data[ii] )
259        {
260            full = 1;
261
262            /* try to match the found header with one of the requested */
263            for( jj = 0; NULL != headers[jj].name; jj++ )
264            {
265                if( NULL == headers[jj].data )
266                {
267                    full = 0;
268                    if( 0 == tr_strncasecmp( headers[jj].name, begin,
269                                             ( data + ii ) - begin ) )
270                    {
271                        ii++;
272                        /* skip leading whitespace and save the header value */
273                        SKIP( ii, len, SP( data[ii] ) );
274                        headers[jj].data = data + ii;
275                        SKIP( ii, len, !NL( data[ii] ) );
276                        headers[jj].len = ( data + ii ) - headers[jj].data;
277                        break;
278                    }
279                }
280            }
281            if( full )
282            {
283                break;
284            }
285
286            /* skip to the end of the header */
287            SKIP( ii, len, !NL( data[ii] ) );
288        }
289
290        /* skip past the newline */
291        SKIP( ii, len, NL( data[ii] ) );
292    }
293
294    return (char*)body;
295}
296
297static const char *
298slice( const char * data, int * len, const char * delim )
299{
300    const char *body;
301    int dlen;
302
303    dlen = strlen( delim );
304    body = tr_memmem( data, *len, delim, dlen );
305
306    if( NULL != body )
307    {
308        *len = body - data;
309        body += dlen;
310    }
311
312    return body;
313}
314
315int
316tr_httpIsUrl( const char * url, int len )
317{
318    if( 0 > len )
319    {
320        len = strlen( url );
321    }
322
323    /* check for protocol */
324    if( 7 > len || 0 != tr_strncasecmp( url, "http://", 7 ) )
325    {
326        return 0;
327    }
328
329    return 7;
330}
331
332int
333tr_httpParseUrl( const char * url, int len,
334                 char ** host, int * port, char ** path )
335{
336    const char * pathstart, * hostend;
337    int          ii, colon, portnum;
338    char         str[6];
339
340    if( 0 > len )
341    {
342        len = strlen( url );
343    }
344
345    ii = tr_httpIsUrl( url, len );
346    if( 0 >= ii )
347    {
348        return 1;
349    }
350    url += ii;
351    len -= ii;
352
353    /* find the hostname and port */
354    colon = -1;
355    for( ii = 0; len > ii && '/' != url[ii]; ii++ )
356    {
357        if( ':' == url[ii] )
358        {
359            colon = ii;
360        }
361    }
362    hostend = url + ( 0 > colon ? ii : colon );
363    pathstart = url + ii;
364
365    /* parse the port number */
366    portnum = HTTP_PORT;
367    if( 0 <= colon )
368    {
369        colon++;
370        memset( str, 0, sizeof( str ) );
371        memcpy( str, url + colon, MIN( (int) sizeof( str) - 1, ii - colon ) );
372        portnum = strtol( str, NULL, 10 );
373        if( 0 >= portnum || 0xffff <= portnum )
374        {
375            tr_err( "Invalid port (%i)", portnum );
376            return 1;
377        }
378    }
379
380    if( NULL != host )
381    {
382        *host = tr_strndup( url, hostend - url );
383    }
384    if( NULL != port )
385    {
386        *port = portnum;
387    }
388    if( NULL != path )
389    {
390        if( 0 < len - ( pathstart - url ) )
391        {
392            *path = tr_strndup( pathstart, len - ( pathstart - url ) );
393        }
394        else
395        {
396            *path = strdup( "/" );
397        }
398    }
399
400    return 0;
401}
402
403tr_http_t *
404tr_httpClient( int method, const char * host, int port, const char * fmt, ... )
405{
406    tr_http_t    * http;
407    va_list        ap1, ap2;
408    char         * methodstr;
409
410    http = malloc( sizeof( *http ) );
411    if( NULL == http )
412    {
413        return NULL;
414    }
415
416    memset( http, 0, sizeof( *http ) );
417    http->state      = HTTP_STATE_CONSTRUCT;
418    http->lengthtype = HTTP_LENGTH_UNKNOWN;
419    http->host       = strdup( host );
420    http->port       = port;
421    http->sock       = -1;
422
423    if( NULL == http->host || NULL == fmt )
424    {
425        goto err;
426    }
427
428    switch( method )
429    {
430        case TR_HTTP_GET:
431            methodstr = "GET";
432            break;
433        case TR_HTTP_POST:
434            methodstr = "POST";
435            break;
436        case TR_HTTP_M_POST:
437            methodstr = "M-POST";
438            break;
439        default:
440            goto err;
441    }
442    if( tr_sprintf( EXPANDBUF( http->header ), "%s ", methodstr ) )
443    {
444        goto err;
445    }
446
447    va_start( ap1, fmt );
448    va_start( ap2, fmt );
449    if( tr_vsprintf( EXPANDBUF( http->header ), fmt, ap1, ap2 ) )
450    {
451        va_end( ap2 );
452        va_end( ap1 );
453        goto err;
454    }
455    va_end( ap2 );
456    va_end( ap1 );
457
458    if( tr_sprintf( EXPANDBUF( http->header ), " HTTP/1.1" CR LF
459                    "Host: %s:%d" CR LF
460                    "User-Agent: " TR_NAME "/" LONG_VERSION_STRING CR LF
461                    "Connection: close" CR LF,
462                    http->host, http->port ) )
463    {
464        goto err;
465    }
466
467    return http;
468
469  err:
470    tr_httpClose( http );
471    return NULL;
472}
473
474tr_http_t *
475tr_httpClientUrl( int method, const char * fmt, ... )
476{
477    char      * url, * host, * path;
478    int         port;
479    va_list     ap;
480    tr_http_t * ret;
481
482    va_start( ap, fmt );
483    url = NULL;
484    vasprintf( &url, fmt, ap );
485    va_end( ap );
486
487    if( tr_httpParseUrl( url, -1, &host, &port, &path ) )
488    {
489        tr_err( "Invalid HTTP URL: %s", url );
490        free( url );
491        return NULL;
492    }
493    free( url );
494
495    ret = tr_httpClient( method, host, port, "%s", path );
496    free( host );
497    free( path );
498
499    return ret;
500}
501
502void
503tr_httpAddHeader( tr_http_t * http, const char * name, const char * value )
504{
505    if( HTTP_STATE_CONSTRUCT == http->state )
506    {
507        if( tr_sprintf( EXPANDBUF( http->header ),
508                        "%s: %s" CR LF, name, value ) )
509        {
510            http->state = HTTP_STATE_ERROR;
511        }
512    }
513    else
514    {
515        assert( HTTP_STATE_ERROR == http->state );
516    }
517}
518
519void
520tr_httpAddBody( tr_http_t * http , const char * fmt , ... )
521{
522    va_list ap1, ap2;
523
524    if( HTTP_STATE_CONSTRUCT == http->state )
525    {
526        va_start( ap1, fmt );
527        va_start( ap2, fmt );
528        if( tr_vsprintf( EXPANDBUF( http->body ), fmt, ap1, ap2 ) )
529        {
530            http->state = HTTP_STATE_ERROR;
531        }
532        va_end( ap2 );
533        va_end( ap1 );
534    }
535    else
536    {
537        assert( HTTP_STATE_ERROR == http->state );
538    }
539}
540
541void
542tr_httpGetHeaders( tr_http_t * http, const char ** buf, int * len )
543{
544    *buf = http->header.buf;
545    *len = http->header.used;
546}
547
548void
549tr_httpGetBody( tr_http_t * http, const char ** buf, int * len )
550{
551    *buf = http->body.buf;
552    *len = http->body.used;
553}
554
555tr_tristate_t
556tr_httpPulse( tr_http_t * http, const char ** data, int * len )
557{
558    struct in_addr addr;
559
560    switch( http->state )
561    {
562        case HTTP_STATE_CONSTRUCT:
563            if( tr_sprintf( EXPANDBUF( http->header ), "Content-length: %i"
564                            CR LF CR LF, http->body.used ) )
565            {
566                goto err;
567            }
568            if( !tr_netResolve( http->host, &addr ) )
569            {
570                http->sock = tr_netOpenTCP( &addr, htons( http->port ), 1 );
571                http->state = HTTP_STATE_CONNECT;
572                break;
573            }
574            http->resolve = tr_netResolveInit( http->host );
575            if( NULL == http->resolve )
576            {
577                goto err;
578            }
579            http->state = HTTP_STATE_RESOLVE;
580            /* fallthrough */
581
582        case HTTP_STATE_RESOLVE:
583            switch( tr_netResolvePulse( http->resolve, &addr ) )
584            {
585                case TR_NET_WAIT:
586                    return TR_NET_WAIT;
587                case TR_NET_ERROR:
588                    goto err;
589                case TR_NET_OK:
590                    tr_netResolveClose( http->resolve );
591                    http->resolve = NULL;
592                    http->sock = tr_netOpenTCP( &addr, htons( http->port ), 1 );
593                    http->state = HTTP_STATE_CONNECT;
594            }
595            /* fallthrough */
596
597        case HTTP_STATE_CONNECT:
598            switch( sendrequest( http ) )
599            {
600                case TR_NET_WAIT:
601                    return TR_NET_WAIT;
602                case TR_NET_ERROR:
603                    goto err;
604                case TR_NET_OK:
605                    http->state = HTTP_STATE_RECEIVE;
606            }
607            /* fallthrough */
608
609        case HTTP_STATE_RECEIVE:
610            switch( receiveresponse( http ) )
611            {
612                case TR_NET_WAIT:
613                    return TR_NET_WAIT;
614                case TR_NET_ERROR:
615                    goto err;
616                case TR_NET_OK:
617                    goto ok;
618            }
619            break;
620
621        case HTTP_STATE_DONE:
622            goto ok;
623
624        case HTTP_STATE_ERROR:
625            goto err;
626    }
627
628    return TR_NET_WAIT;
629
630  err:
631    http->state = HTTP_STATE_ERROR;
632    return TR_NET_ERROR;
633
634  ok:
635    http->state = HTTP_STATE_DONE;
636    if( NULL != data )
637    {
638        *data = http->header.buf;
639    }
640    if( NULL != len )
641    {
642        *len = http->header.used;
643    }
644    return TR_NET_OK;
645}
646
647static tr_tristate_t
648sendrequest( tr_http_t * http )
649{
650    struct buf * buf;
651
652    if( !http->date )
653         http->date = tr_date();
654
655    if( 0 > http->sock || tr_date() > http->date + HTTP_TIMEOUT )
656    {
657        return TR_NET_ERROR;
658    }
659
660    buf = ( 0 < http->header.used ? &http->header : &http->body );
661    while( 0 < buf->used )
662    {
663        const int ret = tr_netSend( http->sock, buf->buf, buf->used );
664        if( ret & TR_NET_CLOSE ) return TR_NET_ERROR;
665        if( ret & TR_NET_BLOCK ) return TR_NET_WAIT;
666        buf->used = 0;
667        buf = &http->body;
668    }
669
670    free( http->body.buf );
671    http->body.buf = NULL;
672    http->body.size = 0;
673    http->date = 0;
674
675    return TR_NET_OK;
676}
677
678static tr_tristate_t
679receiveresponse( tr_http_t * http )
680{
681    int    ret, before;
682    void * newbuf;
683
684    if( 0 == http->date )
685    {
686        http->date = tr_date();
687    }
688
689    before = http->header.used;
690    for(;;)
691    {
692        if( http->header.size - http->header.used < HTTP_BUFSIZE )
693        {
694            newbuf = realloc( http->header.buf,
695                              http->header.size + HTTP_BUFSIZE );
696            if( NULL == newbuf )
697            {
698                return TR_NET_ERROR;
699            }
700            http->header.buf = newbuf;
701            http->header.size += HTTP_BUFSIZE;
702        }
703
704        ret = tr_netRecv( http->sock,
705                          (uint8_t *) ( http->header.buf + http->header.used ),
706                          http->header.size - http->header.used );
707        if( ret & TR_NET_CLOSE )
708        {
709            checklength( http );
710            return TR_NET_OK;
711        }
712        else if( ret & TR_NET_BLOCK )
713        {
714            break;
715        }
716        else
717        {
718            http->header.used += ret;
719        }
720    }
721
722    if( before < http->header.used && checklength( http ) )
723    {
724        return TR_NET_OK;
725    }
726
727    if( tr_date() > HTTP_TIMEOUT + http->date )
728    {
729        return TR_NET_ERROR;
730    }
731
732    return TR_NET_WAIT;
733}
734
735static int
736checklength( tr_http_t * http )
737{
738    char * buf;
739    int    num, ii, len, lastnum;
740
741    switch( http->lengthtype )
742    {
743        case HTTP_LENGTH_UNKNOWN:
744            if( learnlength( http ) )
745            {
746                return checklength( http );
747            }
748            break;
749
750        case HTTP_LENGTH_EOF:
751            break;
752
753        case HTTP_LENGTH_FIXED:
754            if( http->header.used >= http->chunkoff + http->chunklen )
755            {
756                http->header.used = http->chunkoff + http->chunklen;
757                return 1;
758            }
759            break;
760
761        case HTTP_LENGTH_CHUNKED:
762            buf     = http->header.buf;
763            lastnum = -1;
764            while( http->header.used > http->chunkoff + http->chunklen )
765            {
766                num = http->chunkoff + http->chunklen;
767                if( lastnum == num )
768                {
769                    /* ugh, some trackers send Transfer-encoding: chunked
770                       and then don't encode the body */
771                    http->lengthtype = HTTP_LENGTH_EOF;
772                    return checklength( http );
773                }
774                lastnum = num;
775                while(  http->header.used > num && NL( buf[num] ) )
776                {
777                    num++;
778                }
779                ii = num;
780                while( http->header.used > ii && !NL( buf[ii] ) )
781                {
782                    ii++;
783                }
784                if( http->header.used > ii )
785                {
786                    /* strtol should stop at the newline */
787                    len = strtol( buf + num, NULL, 16 );
788                    if( 0 == len )
789                    {
790                        /* XXX should handle invalid length
791                               differently than 0 length chunk */
792                        http->header.used = http->chunkoff + http->chunklen;
793                        return 1;
794                    }
795                    if( http->header.used > ii + 1 )
796                    {
797                        ii += ( 0 == memcmp( buf + ii, CR LF, 2 ) ? 2 : 1 );
798                        if( http->header.used > ii )
799                        {
800                            memmove( buf + http->chunkoff + http->chunklen,
801                                     buf + ii, http->header.used - ii );
802                        }
803                        http->header.used -=
804                            ii - ( http->chunkoff + http->chunklen );
805                        http->chunkoff += http->chunklen;
806                        http->chunklen = len;
807                    }
808                }
809            }
810            break;
811    }
812
813    return 0;
814}
815
816static int
817learnlength( tr_http_t * http )
818{
819    tr_http_header_t hdr[] = {
820        { "Content-Length",    NULL, 0 },
821        /*
822          XXX this probably doesn't handle multiple encodings correctly
823          http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.41
824        */
825        { "Transfer-Encoding", NULL, 0 },
826        { NULL,                NULL, 0 }
827    };
828    const char * body;
829    char       * duped;
830
831    body = tr_httpParse( http->header.buf, http->header.used, hdr );
832    if( NULL != body )
833    {
834        if( 0 < hdr[1].len &&
835            0 == tr_strncasecmp( "chunked", hdr[1].data, hdr[1].len ) )
836        {
837            http->lengthtype = HTTP_LENGTH_CHUNKED;
838            http->chunkoff = body - http->header.buf;
839            http->chunklen = 0;
840        }
841        else if( 0 < hdr[0].len )
842        {
843            http->lengthtype = HTTP_LENGTH_FIXED;
844            http->chunkoff = body - http->header.buf;
845            duped = tr_strndup( hdr[0].data, hdr[0].len );
846            http->chunklen = strtol( duped, NULL, 10 );
847            free( duped );
848        }
849        else
850        {
851            http->lengthtype = HTTP_LENGTH_EOF;
852        }
853        return 1;
854    }
855
856    return 0;
857}
858
859char *
860tr_httpWhatsMyAddress( tr_http_t * http )
861{
862    struct sockaddr_in sin;
863    socklen_t          size;
864    char               buf[INET_ADDRSTRLEN];
865
866    if( 0 > http->sock )
867    {
868        return NULL;
869    }
870
871    size = sizeof( sin );
872    if( 0 > getsockname( http->sock, (struct sockaddr *) &sin, &size ) )
873    {
874        return NULL;
875    }
876
877    tr_netNtop( &sin.sin_addr, buf, sizeof( buf ) );
878
879    return strdup( buf );
880}
881
882void
883tr_httpClose( tr_http_t * http )
884{
885    if( NULL != http->resolve )
886    {
887        tr_netResolveClose( http->resolve );
888    }
889    free( http->host );
890    if( 0 <= http->sock )
891    {
892        tr_netClose( http->sock );
893    }
894    free( http->header.buf );
895    free( http->body.buf );
896    free( http );
897}
Note: See TracBrowser for help on using the repository browser.