source: trunk/libtransmission/http.c @ 2391

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

Include trcompat.h for asprintf()
Don't mix function and non function pointers without casting.
Replace a couple bzero()s with memset()s.
Remove a stray ;
Include a missing header.

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