Annotation of XML/nanohttp.c, revision 1.7

1.1       daniel      1: /*
1.5       daniel      2:  * nanohttp.c: minimalist HTTP GET implementation to fetch external subsets.
                      3:  *             focuses on size, streamability, reentrancy and portability
                      4:  *
                      5:  * This is clearly not a general purpose HTTP implementation
                      6:  * If you look for one, check:
                      7:  *         http://www.w3.org/Library/
1.1       daniel      8:  *
                      9:  * See Copyright for the status of this software.
                     10:  *
                     11:  * Daniel.Veillard@w3.org
                     12:  */
                     13:  
1.5       daniel     14: /* TODO add compression support, Send the Accept- , and decompress on the
                     15:         fly with ZLIB if found at compile-time */
                     16: 
1.4       daniel     17: #ifndef WIN32
                     18: #include "config.h"
                     19: #endif
                     20: 
1.1       daniel     21: #include <stdio.h>
                     22: #include <string.h>
1.4       daniel     23: 
                     24: #ifdef HAVE_STDLIB_H
1.1       daniel     25: #include <stdlib.h>
1.4       daniel     26: #endif
                     27: #ifdef HAVE_UNISTD_H
1.1       daniel     28: #include <unistd.h>
1.4       daniel     29: #endif
                     30: #ifdef HAVE_SYS_SOCKET_H
1.1       daniel     31: #include <sys/socket.h>
1.4       daniel     32: #endif
                     33: #ifdef HAVE_NETINET_IN_H
1.1       daniel     34: #include <netinet/in.h>
1.4       daniel     35: #endif
                     36: #ifdef HAVE_ARPA_INET_H
1.1       daniel     37: #include <arpa/inet.h>
1.4       daniel     38: #endif
                     39: #ifdef HAVE_NETDB_H
1.1       daniel     40: #include <netdb.h>
1.4       daniel     41: #endif
                     42: #ifdef HAVE_FCNTL_H
1.1       daniel     43: #include <fcntl.h> 
1.4       daniel     44: #endif
                     45: #ifdef HAVE_ERRNO_H
1.1       daniel     46: #include <errno.h>
1.4       daniel     47: #endif
                     48: #ifdef HAVE_SYS_TIME_H
1.1       daniel     49: #include <sys/time.h>
1.4       daniel     50: #endif
                     51: #ifdef HAVE_SYS_SELECT_H
1.1       daniel     52: #include <sys/select.h>
1.4       daniel     53: #endif
1.1       daniel     54: 
1.7     ! daniel     55: #include "xmlmemory.h"
        !            56: 
1.5       daniel     57: #ifdef STANDALONE
                     58: #define DEBUG_HTTP
                     59: #endif
                     60: 
1.1       daniel     61: #define XML_NANO_HTTP_MAX_REDIR        10
                     62: 
                     63: #define XML_NANO_HTTP_CHUNK    4096
                     64: 
                     65: #define XML_NANO_HTTP_CLOSED   0
                     66: #define XML_NANO_HTTP_WRITE    1
                     67: #define XML_NANO_HTTP_READ     2
                     68: #define XML_NANO_HTTP_NONE     4
                     69: 
                     70: typedef struct xmlNanoHTTPCtxt {
                     71:     char *protocol;    /* the protocol name */
                     72:     char *hostname;    /* the host name */
                     73:     int port;          /* the port */
                     74:     char *path;                /* the path within the URL */
                     75:     int fd;            /* the file descriptor for the socket */
                     76:     int state;         /* WRITE / READ / CLOSED */
                     77:     char *out;         /* buffer sent (zero terminated) */
                     78:     char *outptr;      /* index within the buffer sent */
                     79:     char *in;          /* the receiving buffer */
                     80:     char *content;     /* the start of the content */
                     81:     char *inptr;       /* the next byte to read from network */
                     82:     char *inrptr;      /* the next byte to give back to the client */
                     83:     int inlen;         /* len of the input buffer */
                     84:     int last;          /* return code for last operation */
                     85:     int returnValue;   /* the protocol return value */
                     86:     char *contentType; /* the MIME type for the input */
                     87:     char *location;    /* the new URL in case of redirect */
                     88: } xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;
                     89: 
1.5       daniel     90: /**
                     91:  * xmlNanoHTTPScanURL:
                     92:  * @ctxt:  an HTTP context
                     93:  * @URL:  The URL used to initialize the context
                     94:  *
                     95:  * (Re)Initialize an HTTP context by parsing the URL and finding
                     96:  * the protocol host port and path it indicates.
                     97:  */
                     98: 
                     99: static void
                    100: xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) {
1.1       daniel    101:     const char *cur = URL;
                    102:     char buf[4096];
                    103:     int index = 0;
                    104:     int port = 0;
                    105: 
                    106:     if (ctxt->protocol != NULL) { 
1.7     ! daniel    107:         xmlFree(ctxt->protocol);
1.1       daniel    108:        ctxt->protocol = NULL;
                    109:     }
                    110:     if (ctxt->hostname != NULL) { 
1.7     ! daniel    111:         xmlFree(ctxt->hostname);
1.1       daniel    112:        ctxt->hostname = NULL;
                    113:     }
                    114:     if (ctxt->path != NULL) { 
1.7     ! daniel    115:         xmlFree(ctxt->path);
1.1       daniel    116:        ctxt->path = NULL;
                    117:     }
                    118:     buf[index] = 0;
                    119:     while (*cur != 0) {
                    120:         if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
                    121:            buf[index] = 0;
1.7     ! daniel    122:            ctxt->protocol = xmlMemStrdup(buf);
1.1       daniel    123:            index = 0;
                    124:             cur += 3;
                    125:            break;
                    126:        }
                    127:        buf[index++] = *cur++;
                    128:     }
                    129:     if (*cur == 0) return;
                    130: 
                    131:     buf[index] = 0;
                    132:     while (1) {
                    133:         if (cur[0] == ':') {
                    134:            buf[index] = 0;
1.7     ! daniel    135:            ctxt->hostname = xmlMemStrdup(buf);
1.1       daniel    136:            index = 0;
                    137:            cur += 1;
                    138:            while ((*cur >= '0') && (*cur <= '9')) {
                    139:                port *= 10;
                    140:                port += *cur - '0';
                    141:                cur++;
                    142:            }
                    143:            if (port != 0) ctxt->port = port;
                    144:            while ((cur[0] != '/') && (*cur != 0)) 
                    145:                cur++;
                    146:            break;
                    147:        }
                    148:         if ((*cur == '/') || (*cur == 0)) {
                    149:            buf[index] = 0;
1.7     ! daniel    150:            ctxt->hostname = xmlMemStrdup(buf);
1.1       daniel    151:            index = 0;
                    152:            break;
                    153:        }
                    154:        buf[index++] = *cur++;
                    155:     }
                    156:     if (*cur == 0) 
1.7     ! daniel    157:         ctxt->path = xmlMemStrdup("/");
1.5       daniel    158:     else {
                    159:         buf[index] = 0;
                    160:        while (*cur != 0) {
                    161:            if ((cur[0] == '#') || (cur[0] == '?'))
                    162:                break;
                    163:            buf[index++] = *cur++;
                    164:        }
                    165:        buf[index] = 0;
1.7     ! daniel    166:        ctxt->path = xmlMemStrdup(buf);
1.5       daniel    167:     }  
1.1       daniel    168: }
                    169: 
1.5       daniel    170: /**
                    171:  * xmlNanoHTTPNewCtxt:
                    172:  * @URL:  The URL used to initialize the context
                    173:  *
                    174:  * Allocate and initialize a new HTTP context.
                    175:  *
                    176:  * Returns an HTTP context or NULL in case of error.
                    177:  */
                    178: 
                    179: static xmlNanoHTTPCtxtPtr
                    180: xmlNanoHTTPNewCtxt(const char *URL) {
1.1       daniel    181:     xmlNanoHTTPCtxtPtr ret;
                    182: 
1.7     ! daniel    183:     ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt));
1.1       daniel    184:     if (ret == NULL) return(NULL);
                    185: 
                    186:     memset(ret, 0, sizeof(xmlNanoHTTPCtxt));
                    187:     ret->port = 80;
                    188:     ret->returnValue = 0;
                    189: 
                    190:     xmlNanoHTTPScanURL(ret, URL);
                    191: 
                    192:     return(ret);
                    193: }
                    194: 
1.5       daniel    195: /**
                    196:  * xmlNanoHTTPFreeCtxt:
                    197:  * @ctxt:  an HTTP context
                    198:  *
                    199:  * Frees the context after closing the connection.
                    200:  */
                    201: 
                    202: static void
                    203: xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) {
                    204:     if (ctxt == NULL) return;
1.7     ! daniel    205:     if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
        !           206:     if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
        !           207:     if (ctxt->path != NULL) xmlFree(ctxt->path);
        !           208:     if (ctxt->out != NULL) xmlFree(ctxt->out);
        !           209:     if (ctxt->in != NULL) xmlFree(ctxt->in);
        !           210:     if (ctxt->contentType != NULL) xmlFree(ctxt->contentType);
        !           211:     if (ctxt->location != NULL) xmlFree(ctxt->location);
1.1       daniel    212:     ctxt->state = XML_NANO_HTTP_NONE;
                    213:     if (ctxt->fd >= 0) close(ctxt->fd);
                    214:     ctxt->fd = -1;
1.7     ! daniel    215:     xmlFree(ctxt);
1.1       daniel    216: }
                    217: 
1.5       daniel    218: /**
                    219:  * xmlNanoHTTPSend:
                    220:  * @ctxt:  an HTTP context
                    221:  *
                    222:  * Send the input needed to initiate the processing on the server side
                    223:  */
                    224: 
                    225: static void
                    226: xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt) {
1.1       daniel    227:     if (ctxt->state & XML_NANO_HTTP_WRITE)
                    228:        ctxt->last = write(ctxt->fd, ctxt->outptr, strlen(ctxt->outptr));
                    229: }
                    230: 
1.5       daniel    231: /**
                    232:  * xmlNanoHTTPRecv:
                    233:  * @ctxt:  an HTTP context
                    234:  *
                    235:  * Read information coming from the HTTP connection.
                    236:  * This is a blocking call (but it blocks in select(), not read()).
                    237:  *
                    238:  * Returns the number of byte read or -1 in case of error.
                    239:  */
                    240: 
                    241: static int
                    242: xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt) {
1.1       daniel    243:     fd_set rfd;
                    244:     struct timeval tv;
                    245: 
                    246: 
                    247:     while (ctxt->state & XML_NANO_HTTP_READ) {
                    248:        if (ctxt->in == NULL) {
1.7     ! daniel    249:            ctxt->in = (char *) xmlMalloc(65000 * sizeof(char));
1.1       daniel    250:            if (ctxt->in == NULL) {
                    251:                ctxt->last = -1;
                    252:                return(-1);
                    253:            }
                    254:            ctxt->inlen = 65000;
                    255:            ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in;
                    256:        }
                    257:        if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) {
                    258:            int delta = ctxt->inrptr - ctxt->in;
                    259:            int len = ctxt->inptr - ctxt->inrptr;
                    260:            
                    261:            memmove(ctxt->in, ctxt->inrptr, len);
                    262:            ctxt->inrptr -= delta;
                    263:            ctxt->content -= delta;
                    264:            ctxt->inptr -= delta;
                    265:        }
                    266:         if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) {
                    267:            int d_inptr = ctxt->inptr - ctxt->in;
                    268:            int d_content = ctxt->content - ctxt->in;
                    269:            int d_inrptr = ctxt->inrptr - ctxt->in;
                    270: 
                    271:            ctxt->inlen *= 2;
1.7     ! daniel    272:             ctxt->in = (char *) xmlRealloc(ctxt->in, ctxt->inlen);
1.1       daniel    273:            if (ctxt->in == NULL) {
                    274:                ctxt->last = -1;
                    275:                return(-1);
                    276:            }
                    277:             ctxt->inptr = ctxt->in + d_inptr;
                    278:             ctxt->content = ctxt->in + d_content;
                    279:             ctxt->inrptr = ctxt->in + d_inrptr;
                    280:        }
                    281:        ctxt->last = read(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK);
                    282:        if (ctxt->last > 0) {
                    283:            ctxt->inptr += ctxt->last;
                    284:            return(ctxt->last);
                    285:        }
                    286:        if (ctxt->last == 0) {
                    287:            return(0);
                    288:        }
                    289: #ifdef EWOULDBLOCK
                    290:        if ((ctxt->last == -1) && (errno != EWOULDBLOCK)) {
1.5       daniel    291:            return(0);
1.1       daniel    292:        }
                    293: #endif
                    294:        tv.tv_sec=10;
                    295:        tv.tv_usec=0;
                    296:        FD_ZERO(&rfd);
                    297:        FD_SET(ctxt->fd, &rfd);
                    298:        
                    299:        if(select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1)
1.5       daniel    300:                return(0);
1.1       daniel    301:     }
                    302:     return(0);
                    303: }
                    304: 
1.5       daniel    305: /**
                    306:  * xmlNanoHTTPReadLine:
                    307:  * @ctxt:  an HTTP context
                    308:  *
                    309:  * Read one line in the HTTP server output, usually for extracting
                    310:  * the HTTP protocol informations from the answer header.
                    311:  *
                    312:  * Returns a newly allocated string with a copy of the line, or NULL
                    313:  *         which indicate the end of the input.
                    314:  */
                    315: 
                    316: static char *
                    317: xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) {
                    318:     char buf[4096];
1.1       daniel    319:     char *bp=buf;
                    320:     
                    321:     while(bp - buf < 4095) {
                    322:        if(ctxt->inrptr == ctxt->inptr) {
                    323:            if (xmlNanoHTTPRecv(ctxt) == 0) {
                    324:                if (bp == buf)
1.5       daniel    325:                    return(NULL);
1.1       daniel    326:                else
                    327:                    *bp = 0;
1.7     ! daniel    328:                return(xmlMemStrdup(buf));
1.1       daniel    329:            }
                    330:        }
                    331:        *bp = *ctxt->inrptr++;
                    332:        if(*bp == '\n') {
                    333:            *bp = 0;
1.7     ! daniel    334:            return(xmlMemStrdup(buf));
1.1       daniel    335:        }
                    336:        if(*bp != '\r')
                    337:            bp++;
                    338:     }
                    339:     buf[4095] = 0;
1.7     ! daniel    340:     return(xmlMemStrdup(buf));
1.1       daniel    341: }
                    342: 
1.5       daniel    343: 
                    344: /**
                    345:  * xmlNanoHTTPScanAnswer:
                    346:  * @ctxt:  an HTTP context
                    347:  * @line:  an HTTP header line
                    348:  *
                    349:  * Try to extract useful informations from the server answer.
                    350:  * We currently parse and process:
                    351:  *  - The HTTP revision/ return code
                    352:  *  - The Content-Type
                    353:  *  - The Location for redirrect processing.
                    354:  *
                    355:  * Returns -1 in case of failure, the file descriptor number otherwise
                    356:  */
                    357: 
                    358: static void
                    359: xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) {
1.1       daniel    360:     const char *cur = line;
                    361: 
                    362:     if (line == NULL) return;
                    363: 
                    364:     if (!strncmp(line, "HTTP/", 5)) {
                    365:         int version = 0;
                    366:        int ret = 0;
                    367: 
                    368:        cur += 5;
                    369:        while ((*cur >= '0') && (*cur <= '9')) {
                    370:            version *= 10;
                    371:            version += *cur - '0';
                    372:            cur++;
                    373:        }
                    374:        if (*cur == '.') {
                    375:            cur++;
                    376:            if ((*cur >= '0') && (*cur <= '9')) {
                    377:                version *= 10;
                    378:                version += *cur - '0';
                    379:                cur++;
                    380:            }
                    381:            while ((*cur >= '0') && (*cur <= '9'))
                    382:                cur++;
                    383:        } else
                    384:            version *= 10;
                    385:        if ((*cur != ' ') && (*cur != '\t')) return;
                    386:        while ((*cur == ' ') || (*cur == '\t')) cur++;
                    387:        if ((*cur < '0') || (*cur > '9')) return;
                    388:        while ((*cur >= '0') && (*cur <= '9')) {
                    389:            ret *= 10;
                    390:            ret += *cur - '0';
                    391:            cur++;
                    392:        }
                    393:        if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return;
                    394:        ctxt->returnValue = ret;
                    395:     } else if (!strncmp(line, "Content-Type:", 13)) {
                    396:         cur += 13;
                    397:        while ((*cur == ' ') || (*cur == '\t')) cur++;
                    398:        if (ctxt->contentType != NULL)
1.7     ! daniel    399:            xmlFree(ctxt->contentType);
        !           400:        ctxt->contentType = xmlMemStrdup(cur);
1.1       daniel    401:     } else if (!strncmp(line, "ContentType:", 12)) {
                    402:         cur += 12;
                    403:        if (ctxt->contentType != NULL) return;
                    404:        while ((*cur == ' ') || (*cur == '\t')) cur++;
1.7     ! daniel    405:        ctxt->contentType = xmlMemStrdup(cur);
1.1       daniel    406:     } else if (!strncmp(line, "content-type:", 13)) {
                    407:         cur += 13;
                    408:        if (ctxt->contentType != NULL) return;
                    409:        while ((*cur == ' ') || (*cur == '\t')) cur++;
1.7     ! daniel    410:        ctxt->contentType = xmlMemStrdup(cur);
1.1       daniel    411:     } else if (!strncmp(line, "contenttype:", 12)) {
                    412:         cur += 12;
                    413:        if (ctxt->contentType != NULL) return;
                    414:        while ((*cur == ' ') || (*cur == '\t')) cur++;
1.7     ! daniel    415:        ctxt->contentType = xmlMemStrdup(cur);
1.1       daniel    416:     } else if (!strncmp(line, "Location:", 9)) {
                    417:         cur += 9;
                    418:        while ((*cur == ' ') || (*cur == '\t')) cur++;
                    419:        if (ctxt->location != NULL)
1.7     ! daniel    420:            xmlFree(ctxt->location);
        !           421:        ctxt->location = xmlMemStrdup(cur);
1.1       daniel    422:     } else if (!strncmp(line, "location:", 9)) {
                    423:         cur += 9;
                    424:        if (ctxt->location != NULL) return;
                    425:        while ((*cur == ' ') || (*cur == '\t')) cur++;
1.7     ! daniel    426:        ctxt->location = xmlMemStrdup(cur);
1.1       daniel    427:     }
                    428: }
                    429: 
1.5       daniel    430: /**
                    431:  * xmlNanoHTTPConnectAttempt:
                    432:  * @ia:  an internet adress structure
                    433:  * @port:  the port number
                    434:  *
                    435:  * Attempt a connection to the given IP:port endpoint. It forces
                    436:  * non-blocking semantic on the socket, and allow 60 seconds for
                    437:  * the host to answer.
                    438:  *
                    439:  * Returns -1 in case of failure, the file descriptor number otherwise
                    440:  */
                    441: 
                    442: static int
                    443: xmlNanoHTTPConnectAttempt(struct in_addr ia, int port)
1.1       daniel    444: {
                    445:     int s=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
                    446:     struct sockaddr_in sin;
                    447:     fd_set wfd;
                    448:     struct timeval tv;
1.2       daniel    449:     int status;
1.1       daniel    450:     
                    451:     if(s==-1) {
1.5       daniel    452: #ifdef DEBUG_HTTP
1.1       daniel    453:        perror("socket");
1.5       daniel    454: #endif
1.1       daniel    455:        return(-1);
                    456:     }
                    457:     
1.2       daniel    458: #ifdef _WINSOCKAPI_
                    459:     {
                    460:        long levents = FD_READ | FD_WRITE | FD_ACCEPT |
                    461:                       FD_CONNECT | FD_CLOSE ;
                    462:        int rv = 0 ;
                    463:        u_long one = 1;
                    464: 
1.3       daniel    465:        status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0;
1.2       daniel    466:     }
                    467: #else /* _WINSOCKAPI_ */
                    468: #if defined(VMS)
                    469:     {
                    470:        int enable = 1;
1.3       daniel    471:        status = IOCTL(s, FIONBIO, &enable);
1.2       daniel    472:     }
                    473: #else /* VMS */
1.3       daniel    474:     if((status = fcntl(s, F_GETFL, 0)) != -1) {
1.2       daniel    475: #ifdef O_NONBLOCK
                    476:        status |= O_NONBLOCK;
                    477: #else /* O_NONBLOCK */
                    478: #ifdef F_NDELAY
                    479:        status |= F_NDELAY;
                    480: #endif /* F_NDELAY */
                    481: #endif /* !O_NONBLOCK */
1.3       daniel    482:        status = fcntl(s, F_SETFL, status);
1.2       daniel    483:     }
                    484:     if(status < 0) {
1.5       daniel    485: #ifdef DEBUG_HTTP
1.1       daniel    486:        perror("nonblocking");
1.5       daniel    487: #endif
1.1       daniel    488:        close(s);
                    489:        return(-1);
                    490:     }
1.2       daniel    491: #endif /* !VMS */
                    492: #endif /* !_WINSOCKAPI_ */
                    493: 
1.1       daniel    494: 
                    495:     sin.sin_family = AF_INET;  
                    496:     sin.sin_addr   = ia;
                    497:     sin.sin_port   = htons(port);
                    498:     
                    499:     if((connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1) &&
                    500:        (errno != EINPROGRESS)) {
                    501:        perror("connect");
                    502:        close(s);
                    503:        return(-1);
                    504:     }  
                    505:     
                    506:     tv.tv_sec = 60;            /* We use 60 second timeouts for now */
                    507:     tv.tv_usec = 0;
                    508:     
                    509:     FD_ZERO(&wfd);
                    510:     FD_SET(s, &wfd);
                    511:     
                    512:     switch(select(s+1, NULL, &wfd, NULL, &tv))
                    513:     {
                    514:        case 0:
                    515:            /* Time out */
                    516:            close(s);
                    517:            return(-1);
                    518:        case -1:
                    519:            /* Ermm.. ?? */
1.5       daniel    520: #ifdef DEBUG_HTTP
1.1       daniel    521:            perror("select");
1.5       daniel    522: #endif
1.1       daniel    523:            close(s);
                    524:            return(-1);
                    525:     }
                    526:     
1.5       daniel    527:     return(s);
1.1       daniel    528: }
                    529:  
1.5       daniel    530: /**
                    531:  * xmlNanoHTTPConnectHost:
                    532:  * @host:  the host name
                    533:  * @port:  the port number
                    534:  *
                    535:  * Attempt a connection to the given host:port endpoint. It tries
                    536:  * the multiple IP provided by the DNS if available.
                    537:  *
                    538:  * Returns -1 in case of failure, the file descriptor number otherwise
                    539:  */
                    540: 
                    541: static int
                    542: xmlNanoHTTPConnectHost(const char *host, int port)
1.1       daniel    543: {
                    544:     struct hostent *h;
                    545:     int i;
                    546:     int s;
                    547:     
                    548:     h=gethostbyname(host);
                    549:     if(h==NULL)
                    550:     {
1.5       daniel    551: #ifdef DEBUG_HTTP
1.1       daniel    552:        fprintf(stderr,"unable to resolve '%s'.\n", host);
1.5       daniel    553: #endif
1.1       daniel    554:        return(-1);
                    555:     }
                    556:     
                    557:     for(i=0; h->h_addr_list[i]; i++)
                    558:     {
                    559:        struct in_addr ia;
                    560:        memcpy(&ia, h->h_addr_list[i],4);
                    561:        s = xmlNanoHTTPConnectAttempt(ia, port);
                    562:        if(s != -1)
1.5       daniel    563:            return(s);
1.1       daniel    564:     }
1.5       daniel    565: 
                    566: #ifdef DEBUG_HTTP
1.1       daniel    567:     fprintf(stderr, "unable to connect to '%s'.\n", host);
1.5       daniel    568: #endif
1.1       daniel    569:     return(-1);
                    570: }
                    571: 
                    572: 
1.5       daniel    573: /**
                    574:  * xmlNanoHTTPOpen:
                    575:  * @URL:  The URL to load
                    576:  * @contentType:  if available the Content-Type information will be
                    577:  *                returned at that location
                    578:  *
                    579:  * This function try to open a connection to the indicated resource
                    580:  * via HTTP GET.
                    581:  *
1.6       daniel    582:  * Returns NULL in case of failure, otherwise a request handler.
                    583:  *     The contentType, if provided must be freed by the caller
1.5       daniel    584:  */
1.1       daniel    585: 
                    586: void *
                    587: xmlNanoHTTPOpen(const char *URL, char **contentType) {
                    588:     xmlNanoHTTPCtxtPtr ctxt;
                    589:     char buf[4096];
                    590:     int ret;
                    591:     char *p;
                    592:     int head;
                    593:     int nbRedirects = 0;
                    594:     char *redirURL = NULL;
                    595:     
1.5       daniel    596:     if (contentType != NULL) *contentType = NULL;
                    597: 
1.1       daniel    598: retry:
                    599:     if (redirURL == NULL)
                    600:        ctxt = xmlNanoHTTPNewCtxt(URL);
                    601:     else {
                    602:        ctxt = xmlNanoHTTPNewCtxt(redirURL);
1.7     ! daniel    603:        xmlFree(redirURL);
1.1       daniel    604:        redirURL = NULL;
                    605:     }
                    606: 
                    607:     if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
                    608:         xmlNanoHTTPFreeCtxt(ctxt);
1.7     ! daniel    609:        if (redirURL != NULL) xmlFree(redirURL);
1.1       daniel    610:         return(NULL);
                    611:     }
                    612:     if (ctxt->hostname == NULL) {
                    613:         xmlNanoHTTPFreeCtxt(ctxt);
                    614:         return(NULL);
                    615:     }
                    616:     ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
                    617:     if (ret < 0) {
                    618:         xmlNanoHTTPFreeCtxt(ctxt);
                    619:         return(NULL);
                    620:     }
                    621:     ctxt->fd = ret;
1.7     ! daniel    622: #ifdef HAVE_SNPRINTF
1.5       daniel    623:     snprintf(buf, sizeof(buf),"GET %s HTTP/1.0\r\nHost: %s\r\n\r\n",
1.1       daniel    624:             ctxt->path, ctxt->hostname);
1.7     ! daniel    625: #else
        !           626:     sprintf(buf, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n",
        !           627:             ctxt->path, ctxt->hostname);
        !           628: #endif
1.5       daniel    629: #ifdef DEBUG_HTTP
                    630:     printf("-> GET %s HTTP/1.0\n-> Host: %s\n\n",
                    631:            ctxt->path, ctxt->hostname);
                    632: #endif
1.7     ! daniel    633:     ctxt->outptr = ctxt->out = xmlMemStrdup(buf);
1.1       daniel    634:     ctxt->state = XML_NANO_HTTP_WRITE;
                    635:     xmlNanoHTTPSend(ctxt);
                    636:     ctxt->state = XML_NANO_HTTP_READ;
                    637:     head = 1;
                    638: 
                    639:     while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
                    640:         if (head && (*p == 0)) {
                    641:            head = 0;
                    642:            ctxt->content = ctxt->inrptr;
                    643:            break;
                    644:        }
                    645:        xmlNanoHTTPScanAnswer(ctxt, p);
                    646: 
1.5       daniel    647: #ifdef DEBUG_HTTP
                    648:        if (p != NULL) printf("<- %s\n", p);
                    649: #endif
1.7     ! daniel    650:         if (p != NULL) xmlFree(p);
1.1       daniel    651:     }
                    652: 
                    653:     if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
                    654:         (ctxt->returnValue < 400)) {
1.5       daniel    655: #ifdef DEBUG_HTTP
                    656:        printf("\nRedirect to: %s\n", ctxt->location);
                    657: #endif
1.1       daniel    658:        while (xmlNanoHTTPRecv(ctxt)) ;
                    659:         if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
                    660:            nbRedirects++;
1.7     ! daniel    661:            redirURL = xmlMemStrdup(ctxt->location);
1.1       daniel    662:            xmlNanoHTTPFreeCtxt(ctxt);
                    663:            goto retry;
                    664:        }
                    665:        xmlNanoHTTPFreeCtxt(ctxt);
1.5       daniel    666: #ifdef DEBUG_HTTP
                    667:        printf("Too many redirrects, aborting ...\n");
                    668: #endif
1.1       daniel    669:        return(NULL);
                    670: 
                    671:     }
                    672: 
1.5       daniel    673:     if ((contentType != NULL) && (ctxt->contentType != NULL))
1.7     ! daniel    674:         *contentType = xmlMemStrdup(ctxt->contentType);
1.5       daniel    675: 
                    676: #ifdef DEBUG_HTTP
                    677:     if (ctxt->contentType != NULL)
                    678:        printf("\nCode %d, content-type '%s'\n\n",
                    679:               ctxt->returnValue, ctxt->contentType);
                    680:     else
                    681:        printf("\nCode %d, no content-type\n\n",
                    682:               ctxt->returnValue);
                    683: #endif
1.1       daniel    684: 
                    685:     return((void *) ctxt);
                    686: }
                    687: 
1.5       daniel    688: /**
                    689:  * xmlNanoHTTPRead:
                    690:  * @ctx:  the HTTP context
                    691:  * @dest:  a buffer
                    692:  * @len:  the buffer length
                    693:  *
                    694:  * This function tries to read @len bytes from the existing HTTP connection
                    695:  * and saves them in @dest. This is a blocking call.
                    696:  *
                    697:  * Returns the number of byte read. 0 is an indication of an end of connection.
                    698:  *         -1 indicates a parameter error.
                    699:  */
1.1       daniel    700: int
                    701: xmlNanoHTTPRead(void *ctx, void *dest, int len) {
                    702:     xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
                    703: 
                    704:     if (ctx == NULL) return(-1);
                    705:     if (dest == NULL) return(-1);
                    706:     if (len <= 0) return(0);
                    707: 
                    708:     while (ctxt->inptr - ctxt->inrptr < len) {
                    709:         if (xmlNanoHTTPRecv(ctxt) == 0) break;
                    710:     }
                    711:     if (ctxt->inptr - ctxt->inrptr < len)
                    712:         len = ctxt->inptr - ctxt->inrptr;
                    713:     memcpy(dest, ctxt->inrptr, len);
                    714:     ctxt->inrptr += len;
                    715:     return(len);
                    716: }
                    717: 
1.5       daniel    718: /**
                    719:  * xmlNanoHTTPClose:
                    720:  * @ctx:  the HTTP context
                    721:  *
                    722:  * This function closes an HTTP context, it ends up the connection and
                    723:  * free all data related to it.
                    724:  */
1.1       daniel    725: void
                    726: xmlNanoHTTPClose(void *ctx) {
                    727:     xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
                    728: 
                    729:     if (ctx == NULL) return;
                    730: 
                    731:     xmlNanoHTTPFreeCtxt(ctxt);
                    732: }
                    733: 
1.5       daniel    734: /**
1.6       daniel    735:  * xmlNanoHTTPMethod:
                    736:  * @URL:  The URL to load
                    737:  * @method:  the HTTP method to use
                    738:  * @input:  the input string if any
                    739:  * @contentType:  the Content-Type information IN and OUT
                    740:  * @headers:  the extra headers
                    741:  *
                    742:  * This function try to open a connection to the indicated resource
                    743:  * via HTTP using the given @method, adding the given extra headers
                    744:  * and the input buffer for the request content.
                    745:  *
                    746:  * Returns NULL in case of failure, otherwise a request handler.
                    747:  *     The contentType, if provided must be freed by the caller
                    748:  */
                    749: 
                    750: #ifndef DEBUG_HTTP
                    751: #define DEBUG_HTTP
                    752: #endif
                    753: void *
                    754: xmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
                    755:                   char **contentType, const char *headers) {
                    756:     xmlNanoHTTPCtxtPtr ctxt;
                    757:     char buf[20000];
                    758:     int ret;
                    759:     char *p;
                    760:     int head;
                    761:     int nbRedirects = 0;
                    762:     char *redirURL = NULL;
                    763:     
                    764:     if (URL == NULL) return(NULL);
                    765:     if (method == NULL) method = "GET";
                    766:     if (contentType != NULL) *contentType = NULL;
                    767: 
                    768: retry:
                    769:     if (redirURL == NULL)
                    770:        ctxt = xmlNanoHTTPNewCtxt(URL);
                    771:     else {
                    772:        ctxt = xmlNanoHTTPNewCtxt(redirURL);
1.7     ! daniel    773:        xmlFree(redirURL);
1.6       daniel    774:        redirURL = NULL;
                    775:     }
                    776: 
                    777:     if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
                    778:         xmlNanoHTTPFreeCtxt(ctxt);
1.7     ! daniel    779:        if (redirURL != NULL) xmlFree(redirURL);
1.6       daniel    780:         return(NULL);
                    781:     }
                    782:     if (ctxt->hostname == NULL) {
                    783:         xmlNanoHTTPFreeCtxt(ctxt);
                    784:         return(NULL);
                    785:     }
                    786:     ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
                    787:     if (ret < 0) {
                    788:         xmlNanoHTTPFreeCtxt(ctxt);
                    789:         return(NULL);
                    790:     }
                    791:     ctxt->fd = ret;
                    792: 
                    793:     if (input == NULL) {
                    794:         if (headers == NULL) {
                    795:            if ((contentType == NULL) || (*contentType == NULL)) {
1.7     ! daniel    796: #ifdef HAVE_SNPRINTF
1.6       daniel    797:                snprintf(buf, sizeof(buf),
                    798:                         "%s %s HTTP/1.0\r\nHost: %s\r\n\r\n",
                    799:                         method, ctxt->path, ctxt->hostname);
1.7     ! daniel    800: #else
        !           801:                sprintf(buf,
        !           802:                         "%s %s HTTP/1.0\r\nHost: %s\r\n\r\n",
        !           803:                         method, ctxt->path, ctxt->hostname);
        !           804: #endif
1.6       daniel    805:            } else {
1.7     ! daniel    806: #ifdef HAVE_SNPRINTF
1.6       daniel    807:                snprintf(buf, sizeof(buf),
                    808:                     "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n\r\n",
                    809:                         method, ctxt->path, ctxt->hostname, *contentType);
1.7     ! daniel    810: #else
        !           811:                sprintf(buf,
        !           812:                     "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n\r\n",
        !           813:                         method, ctxt->path, ctxt->hostname, *contentType);
        !           814: #endif
1.6       daniel    815:            }
                    816:        } else {
                    817:            if ((contentType == NULL) || (*contentType == NULL)) {
1.7     ! daniel    818: #ifdef HAVE_SNPRINTF
1.6       daniel    819:                snprintf(buf, sizeof(buf),
                    820:                         "%s %s HTTP/1.0\r\nHost: %s\r\n%s\r\n",
                    821:                         method, ctxt->path, ctxt->hostname, headers);
1.7     ! daniel    822: #else
        !           823:                sprintf(buf,
        !           824:                         "%s %s HTTP/1.0\r\nHost: %s\r\n%s\r\n",
        !           825:                         method, ctxt->path, ctxt->hostname, headers);
        !           826: #endif
1.6       daniel    827:            } else {
1.7     ! daniel    828: #ifdef HAVE_SNPRINTF
1.6       daniel    829:                snprintf(buf, sizeof(buf),
                    830:                 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n%s\r\n",
                    831:                         method, ctxt->path, ctxt->hostname, *contentType,
                    832:                         headers);
1.7     ! daniel    833: #else
        !           834:                sprintf(buf,
        !           835:                 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n%s\r\n",
        !           836:                         method, ctxt->path, ctxt->hostname, *contentType,
        !           837:                         headers);
        !           838: #endif
1.6       daniel    839:            }
                    840:        }
                    841:     } else {
                    842:         int len = strlen(input);
                    843:         if (headers == NULL) {
                    844:            if ((contentType == NULL) || (*contentType == NULL)) {
1.7     ! daniel    845: #ifdef HAVE_SNPRINTF
1.6       daniel    846:                snprintf(buf, sizeof(buf),
                    847:                 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s",
                    848:                         method, ctxt->path, ctxt->hostname, len, input);
1.7     ! daniel    849: #else
        !           850:                sprintf(buf,
        !           851:                 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s",
        !           852:                         method, ctxt->path, ctxt->hostname, len, input);
        !           853: #endif
1.6       daniel    854:            } else {
1.7     ! daniel    855: #ifdef HAVE_SNPRINTF
1.6       daniel    856:                snprintf(buf, sizeof(buf),
                    857: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n%s",
                    858:                         method, ctxt->path, ctxt->hostname, *contentType, len,
                    859:                         input);
1.7     ! daniel    860: #else
        !           861:                sprintf(buf,
        !           862: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n%s",
        !           863:                         method, ctxt->path, ctxt->hostname, *contentType, len,
        !           864:                         input);
        !           865: #endif
1.6       daniel    866:            }
                    867:        } else {
                    868:            if ((contentType == NULL) || (*contentType == NULL)) {
1.7     ! daniel    869: #ifdef HAVE_SNPRINTF
1.6       daniel    870:                snprintf(buf, sizeof(buf),
                    871:             "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n%s\r\n%s",
                    872:                         method, ctxt->path, ctxt->hostname, len,
                    873:                         headers, input);
1.7     ! daniel    874: #else
        !           875:                sprintf(buf,
        !           876:             "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n%s\r\n%s",
        !           877:                         method, ctxt->path, ctxt->hostname, len,
        !           878:                         headers, input);
        !           879: #endif
1.6       daniel    880:            } else {
1.7     ! daniel    881: #ifdef HAVE_SNPRINTF
1.6       daniel    882:                snprintf(buf, sizeof(buf),
                    883: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n%s\r\n%s",
                    884:                         method, ctxt->path, ctxt->hostname, *contentType,
                    885:                         len, headers, input);
1.7     ! daniel    886: #else
        !           887:                sprintf(buf,
        !           888: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n%s\r\n%s",
        !           889:                         method, ctxt->path, ctxt->hostname, *contentType,
        !           890:                         len, headers, input);
        !           891: #endif
1.6       daniel    892:            }
                    893:        }
                    894:     }
                    895: #ifdef DEBUG_HTTP
                    896:     printf("-> %s", buf);
                    897: #endif
1.7     ! daniel    898:     ctxt->outptr = ctxt->out = xmlMemStrdup(buf);
1.6       daniel    899:     ctxt->state = XML_NANO_HTTP_WRITE;
                    900:     xmlNanoHTTPSend(ctxt);
                    901:     ctxt->state = XML_NANO_HTTP_READ;
                    902:     head = 1;
                    903: 
                    904:     while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
                    905:         if (head && (*p == 0)) {
                    906:            head = 0;
                    907:            ctxt->content = ctxt->inrptr;
1.7     ! daniel    908:            if (p != NULL) xmlFree(p);
1.6       daniel    909:            break;
                    910:        }
                    911:        xmlNanoHTTPScanAnswer(ctxt, p);
                    912: 
                    913: #ifdef DEBUG_HTTP
                    914:        if (p != NULL) printf("<- %s\n", p);
                    915: #endif
1.7     ! daniel    916:         if (p != NULL) xmlFree(p);
1.6       daniel    917:     }
                    918: 
                    919:     if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
                    920:         (ctxt->returnValue < 400)) {
                    921: #ifdef DEBUG_HTTP
                    922:        printf("\nRedirect to: %s\n", ctxt->location);
                    923: #endif
                    924:        while (xmlNanoHTTPRecv(ctxt)) ;
                    925:         if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
                    926:            nbRedirects++;
1.7     ! daniel    927:            redirURL = xmlMemStrdup(ctxt->location);
1.6       daniel    928:            xmlNanoHTTPFreeCtxt(ctxt);
                    929:            goto retry;
                    930:        }
                    931:        xmlNanoHTTPFreeCtxt(ctxt);
                    932: #ifdef DEBUG_HTTP
                    933:        printf("Too many redirrects, aborting ...\n");
                    934: #endif
                    935:        return(NULL);
                    936: 
                    937:     }
                    938: 
                    939:     if ((contentType != NULL) && (ctxt->contentType != NULL))
1.7     ! daniel    940:         *contentType = xmlMemStrdup(ctxt->contentType);
        !           941:     else if (contentType != NULL)
        !           942:         *contentType = NULL;
1.6       daniel    943: 
                    944: #ifdef DEBUG_HTTP
                    945:     if (ctxt->contentType != NULL)
                    946:        printf("\nCode %d, content-type '%s'\n\n",
                    947:               ctxt->returnValue, ctxt->contentType);
                    948:     else
                    949:        printf("\nCode %d, no content-type\n\n",
                    950:               ctxt->returnValue);
                    951: #endif
                    952: 
                    953:     return((void *) ctxt);
                    954: }
                    955: 
                    956: /**
1.5       daniel    957:  * xmlNanoHTTPFetch:
                    958:  * @URL:  The URL to load
                    959:  * @filename:  the filename where the content should be saved
                    960:  * @contentType:  if available the Content-Type information will be
                    961:  *                returned at that location
                    962:  *
                    963:  * This function try to fetch the indicated resource via HTTP GET
                    964:  * and save it's content in the file.
                    965:  *
                    966:  * Returns -1 in case of failure, 0 incase of success. The contentType,
                    967:  *     if provided must be freed by the caller
                    968:  */
                    969: int
                    970: xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
1.1       daniel    971:     void *ctxt;
                    972:     char buf[4096];
                    973:     int fd;
                    974:     int len;
                    975:     
                    976:     ctxt = xmlNanoHTTPOpen(URL, contentType);
                    977:     if (ctxt == NULL) return(-1);
                    978: 
                    979:     if (!strcmp(filename, "-")) 
                    980:         fd = 0;
                    981:     else {
                    982:         fd = open(filename, O_CREAT | O_WRONLY);
                    983:        if (fd < 0) {
                    984:            xmlNanoHTTPClose(ctxt);
1.5       daniel    985:            if ((contentType != NULL) && (*contentType != NULL)) {
1.7     ! daniel    986:                xmlFree(*contentType);
1.5       daniel    987:                *contentType = NULL;
                    988:            }
1.1       daniel    989:            return(-1);
                    990:        }
                    991:     }
                    992: 
                    993:     while ((len = xmlNanoHTTPRead(ctxt, buf, sizeof(buf))) > 0) {
                    994:        write(fd, buf, len);
                    995:     }
                    996: 
                    997:     xmlNanoHTTPClose(ctxt);
                    998:     return(0);
1.6       daniel    999: }
                   1000: 
                   1001: /**
                   1002:  * xmlNanoHTTPSave:
                   1003:  * @ctx:  the HTTP context
                   1004:  * @filename:  the filename where the content should be saved
                   1005:  *
                   1006:  * This function saves the output of the HTTP transaction to a file
                   1007:  * It closes and free the context at the end
                   1008:  *
                   1009:  * Returns -1 in case of failure, 0 incase of success.
                   1010:  */
                   1011: int
                   1012: xmlNanoHTTPSave(void *ctxt, const char *filename) {
                   1013:     char buf[4096];
                   1014:     int fd;
                   1015:     int len;
                   1016:     
                   1017:     if (ctxt == NULL) return(-1);
                   1018: 
                   1019:     if (!strcmp(filename, "-")) 
                   1020:         fd = 0;
                   1021:     else {
                   1022:         fd = open(filename, O_CREAT | O_WRONLY);
                   1023:        if (fd < 0) {
                   1024:            xmlNanoHTTPClose(ctxt);
                   1025:            return(-1);
                   1026:        }
                   1027:     }
                   1028: 
                   1029:     while ((len = xmlNanoHTTPRead(ctxt, buf, sizeof(buf))) > 0) {
                   1030:        write(fd, buf, len);
                   1031:     }
                   1032: 
                   1033:     xmlNanoHTTPClose(ctxt);
                   1034:     return(0);
                   1035: }
                   1036: 
                   1037: /**
                   1038:  * xmlNanoHTTPReturnCode:
                   1039:  * @ctx:  the HTTP context
                   1040:  *
                   1041:  * Returns the HTTP return code for the request.
                   1042:  */
                   1043: int
                   1044: xmlNanoHTTPReturnCode(void *ctx) {
                   1045:     xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
                   1046: 
                   1047:     if (ctxt == NULL) return(-1);
                   1048: 
                   1049:     return(ctxt->returnValue);
1.1       daniel   1050: }
                   1051: 
                   1052: #ifdef STANDALONE
                   1053: int main(int argc, char **argv) {
                   1054:     char *contentType = NULL;
                   1055: 
                   1056:     if (argv[1] != NULL) {
                   1057:        if (argv[2] != NULL) 
                   1058:            xmlNanoHTTPFetch(argv[1], argv[2], &contentType);
                   1059:         else
                   1060:            xmlNanoHTTPFetch(argv[1], "-", &contentType);
1.7     ! daniel   1061:        if (contentType != NULL) xmlFree(contentType);
1.1       daniel   1062:     } else {
                   1063:         printf("%s: minimal HTTP GET implementation\n", argv[0]);
                   1064:         printf("\tusage %s [ URL [ filename ] ]\n", argv[0]);
                   1065:     }
                   1066:     return(0);
                   1067: }
                   1068: #endif /* STANDALONE */

Webmaster