Annotation of XML/nanohttp.c, revision 1.5

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.5     ! daniel     55: #ifdef STANDALONE
        !            56: #define DEBUG_HTTP
        !            57: #endif
        !            58: 
1.1       daniel     59: #define XML_NANO_HTTP_MAX_REDIR        10
                     60: 
                     61: #define XML_NANO_HTTP_CHUNK    4096
                     62: 
                     63: #define XML_NANO_HTTP_CLOSED   0
                     64: #define XML_NANO_HTTP_WRITE    1
                     65: #define XML_NANO_HTTP_READ     2
                     66: #define XML_NANO_HTTP_NONE     4
                     67: 
                     68: typedef struct xmlNanoHTTPCtxt {
                     69:     char *protocol;    /* the protocol name */
                     70:     char *hostname;    /* the host name */
                     71:     int port;          /* the port */
                     72:     char *path;                /* the path within the URL */
                     73:     int fd;            /* the file descriptor for the socket */
                     74:     int state;         /* WRITE / READ / CLOSED */
                     75:     char *out;         /* buffer sent (zero terminated) */
                     76:     char *outptr;      /* index within the buffer sent */
                     77:     char *in;          /* the receiving buffer */
                     78:     char *content;     /* the start of the content */
                     79:     char *inptr;       /* the next byte to read from network */
                     80:     char *inrptr;      /* the next byte to give back to the client */
                     81:     int inlen;         /* len of the input buffer */
                     82:     int last;          /* return code for last operation */
                     83:     int returnValue;   /* the protocol return value */
                     84:     char *contentType; /* the MIME type for the input */
                     85:     char *location;    /* the new URL in case of redirect */
                     86: } xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;
                     87: 
1.5     ! daniel     88: /**
        !            89:  * xmlNanoHTTPScanURL:
        !            90:  * @ctxt:  an HTTP context
        !            91:  * @URL:  The URL used to initialize the context
        !            92:  *
        !            93:  * (Re)Initialize an HTTP context by parsing the URL and finding
        !            94:  * the protocol host port and path it indicates.
        !            95:  */
        !            96: 
        !            97: static void
        !            98: xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) {
1.1       daniel     99:     const char *cur = URL;
                    100:     char buf[4096];
                    101:     int index = 0;
                    102:     int port = 0;
                    103: 
                    104:     if (ctxt->protocol != NULL) { 
                    105:         free(ctxt->protocol);
                    106:        ctxt->protocol = NULL;
                    107:     }
                    108:     if (ctxt->hostname != NULL) { 
                    109:         free(ctxt->hostname);
                    110:        ctxt->hostname = NULL;
                    111:     }
                    112:     if (ctxt->path != NULL) { 
                    113:         free(ctxt->path);
                    114:        ctxt->path = NULL;
                    115:     }
                    116:     buf[index] = 0;
                    117:     while (*cur != 0) {
                    118:         if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
                    119:            buf[index] = 0;
                    120:            ctxt->protocol = strdup(buf);
                    121:            index = 0;
                    122:             cur += 3;
                    123:            break;
                    124:        }
                    125:        buf[index++] = *cur++;
                    126:     }
                    127:     if (*cur == 0) return;
                    128: 
                    129:     buf[index] = 0;
                    130:     while (1) {
                    131:         if (cur[0] == ':') {
                    132:            buf[index] = 0;
                    133:            ctxt->hostname = strdup(buf);
                    134:            index = 0;
                    135:            cur += 1;
                    136:            while ((*cur >= '0') && (*cur <= '9')) {
                    137:                port *= 10;
                    138:                port += *cur - '0';
                    139:                cur++;
                    140:            }
                    141:            if (port != 0) ctxt->port = port;
                    142:            while ((cur[0] != '/') && (*cur != 0)) 
                    143:                cur++;
                    144:            break;
                    145:        }
                    146:         if ((*cur == '/') || (*cur == 0)) {
                    147:            buf[index] = 0;
                    148:            ctxt->hostname = strdup(buf);
                    149:            index = 0;
                    150:            break;
                    151:        }
                    152:        buf[index++] = *cur++;
                    153:     }
                    154:     if (*cur == 0) 
                    155:         ctxt->path = strdup("/");
1.5     ! daniel    156:     else {
        !           157:         buf[index] = 0;
1.1       daniel    158:        ctxt->path = strdup(cur);
1.5     ! daniel    159:        while (*cur != 0) {
        !           160:            if ((cur[0] == '#') || (cur[0] == '?'))
        !           161:                break;
        !           162:            buf[index++] = *cur++;
        !           163:        }
        !           164:        buf[index] = 0;
        !           165:        ctxt->path = strdup(buf);
        !           166:     }  
1.1       daniel    167: }
                    168: 
1.5     ! daniel    169: /**
        !           170:  * xmlNanoHTTPNewCtxt:
        !           171:  * @URL:  The URL used to initialize the context
        !           172:  *
        !           173:  * Allocate and initialize a new HTTP context.
        !           174:  *
        !           175:  * Returns an HTTP context or NULL in case of error.
        !           176:  */
        !           177: 
        !           178: static xmlNanoHTTPCtxtPtr
        !           179: xmlNanoHTTPNewCtxt(const char *URL) {
1.1       daniel    180:     xmlNanoHTTPCtxtPtr ret;
                    181: 
                    182:     ret = (xmlNanoHTTPCtxtPtr) malloc(sizeof(xmlNanoHTTPCtxt));
                    183:     if (ret == NULL) return(NULL);
                    184: 
                    185:     memset(ret, 0, sizeof(xmlNanoHTTPCtxt));
                    186:     ret->port = 80;
                    187:     ret->returnValue = 0;
                    188: 
                    189:     xmlNanoHTTPScanURL(ret, URL);
                    190: 
                    191:     return(ret);
                    192: }
                    193: 
1.5     ! daniel    194: /**
        !           195:  * xmlNanoHTTPFreeCtxt:
        !           196:  * @ctxt:  an HTTP context
        !           197:  *
        !           198:  * Frees the context after closing the connection.
        !           199:  */
        !           200: 
        !           201: static void
        !           202: xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) {
        !           203:     if (ctxt == NULL) return;
1.1       daniel    204:     if (ctxt->hostname != NULL) free(ctxt->hostname);
                    205:     if (ctxt->protocol != NULL) free(ctxt->protocol);
                    206:     if (ctxt->path != NULL) free(ctxt->path);
                    207:     if (ctxt->out != NULL) free(ctxt->out);
                    208:     if (ctxt->in != NULL) free(ctxt->in);
                    209:     if (ctxt->contentType != NULL) free(ctxt->contentType);
                    210:     if (ctxt->location != NULL) free(ctxt->location);
                    211:     ctxt->state = XML_NANO_HTTP_NONE;
                    212:     if (ctxt->fd >= 0) close(ctxt->fd);
                    213:     ctxt->fd = -1;
                    214:     free(ctxt);
                    215: }
                    216: 
1.5     ! daniel    217: /**
        !           218:  * xmlNanoHTTPSend:
        !           219:  * @ctxt:  an HTTP context
        !           220:  *
        !           221:  * Send the input needed to initiate the processing on the server side
        !           222:  */
        !           223: 
        !           224: static void
        !           225: xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt) {
1.1       daniel    226:     if (ctxt->state & XML_NANO_HTTP_WRITE)
                    227:        ctxt->last = write(ctxt->fd, ctxt->outptr, strlen(ctxt->outptr));
                    228: }
                    229: 
1.5     ! daniel    230: /**
        !           231:  * xmlNanoHTTPRecv:
        !           232:  * @ctxt:  an HTTP context
        !           233:  *
        !           234:  * Read information coming from the HTTP connection.
        !           235:  * This is a blocking call (but it blocks in select(), not read()).
        !           236:  *
        !           237:  * Returns the number of byte read or -1 in case of error.
        !           238:  */
        !           239: 
        !           240: static int
        !           241: xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt) {
1.1       daniel    242:     fd_set rfd;
                    243:     struct timeval tv;
                    244: 
                    245: 
                    246:     while (ctxt->state & XML_NANO_HTTP_READ) {
                    247:        if (ctxt->in == NULL) {
                    248:            ctxt->in = (char *) malloc(65000 * sizeof(char));
                    249:            if (ctxt->in == NULL) {
                    250:                ctxt->last = -1;
                    251:                return(-1);
                    252:            }
                    253:            ctxt->inlen = 65000;
                    254:            ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in;
                    255:        }
                    256:        if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) {
                    257:            int delta = ctxt->inrptr - ctxt->in;
                    258:            int len = ctxt->inptr - ctxt->inrptr;
                    259:            
                    260:            memmove(ctxt->in, ctxt->inrptr, len);
                    261:            ctxt->inrptr -= delta;
                    262:            ctxt->content -= delta;
                    263:            ctxt->inptr -= delta;
                    264:        }
                    265:         if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) {
                    266:            int d_inptr = ctxt->inptr - ctxt->in;
                    267:            int d_content = ctxt->content - ctxt->in;
                    268:            int d_inrptr = ctxt->inrptr - ctxt->in;
                    269: 
                    270:            ctxt->inlen *= 2;
                    271:             ctxt->in = (char *) realloc(ctxt->in, ctxt->inlen);
                    272:            if (ctxt->in == NULL) {
                    273:                ctxt->last = -1;
                    274:                return(-1);
                    275:            }
                    276:             ctxt->inptr = ctxt->in + d_inptr;
                    277:             ctxt->content = ctxt->in + d_content;
                    278:             ctxt->inrptr = ctxt->in + d_inrptr;
                    279:        }
                    280:        ctxt->last = read(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK);
                    281:        if (ctxt->last > 0) {
                    282:            ctxt->inptr += ctxt->last;
                    283:            return(ctxt->last);
                    284:        }
                    285:        if (ctxt->last == 0) {
                    286:            return(0);
                    287:        }
                    288: #ifdef EWOULDBLOCK
                    289:        if ((ctxt->last == -1) && (errno != EWOULDBLOCK)) {
1.5     ! daniel    290:            return(0);
1.1       daniel    291:        }
                    292: #endif
                    293:        tv.tv_sec=10;
                    294:        tv.tv_usec=0;
                    295:        FD_ZERO(&rfd);
                    296:        FD_SET(ctxt->fd, &rfd);
                    297:        
                    298:        if(select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1)
1.5     ! daniel    299:                return(0);
1.1       daniel    300:     }
                    301:     return(0);
                    302: }
                    303: 
1.5     ! daniel    304: /**
        !           305:  * xmlNanoHTTPReadLine:
        !           306:  * @ctxt:  an HTTP context
        !           307:  *
        !           308:  * Read one line in the HTTP server output, usually for extracting
        !           309:  * the HTTP protocol informations from the answer header.
        !           310:  *
        !           311:  * Returns a newly allocated string with a copy of the line, or NULL
        !           312:  *         which indicate the end of the input.
        !           313:  */
        !           314: 
        !           315: static char *
        !           316: xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) {
        !           317:     char buf[4096];
1.1       daniel    318:     char *bp=buf;
                    319:     
                    320:     while(bp - buf < 4095) {
                    321:        if(ctxt->inrptr == ctxt->inptr) {
                    322:            if (xmlNanoHTTPRecv(ctxt) == 0) {
                    323:                if (bp == buf)
1.5     ! daniel    324:                    return(NULL);
1.1       daniel    325:                else
                    326:                    *bp = 0;
1.5     ! daniel    327:                return(strdup(buf));
1.1       daniel    328:            }
                    329:        }
                    330:        *bp = *ctxt->inrptr++;
                    331:        if(*bp == '\n') {
                    332:            *bp = 0;
1.5     ! daniel    333:            return(strdup(buf));
1.1       daniel    334:        }
                    335:        if(*bp != '\r')
                    336:            bp++;
                    337:     }
                    338:     buf[4095] = 0;
1.5     ! daniel    339:     return(strdup(buf));
1.1       daniel    340: }
                    341: 
1.5     ! daniel    342: 
        !           343: /**
        !           344:  * xmlNanoHTTPScanAnswer:
        !           345:  * @ctxt:  an HTTP context
        !           346:  * @line:  an HTTP header line
        !           347:  *
        !           348:  * Try to extract useful informations from the server answer.
        !           349:  * We currently parse and process:
        !           350:  *  - The HTTP revision/ return code
        !           351:  *  - The Content-Type
        !           352:  *  - The Location for redirrect processing.
        !           353:  *
        !           354:  * Returns -1 in case of failure, the file descriptor number otherwise
        !           355:  */
        !           356: 
        !           357: static void
        !           358: xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) {
1.1       daniel    359:     const char *cur = line;
                    360: 
                    361:     if (line == NULL) return;
                    362: 
                    363:     if (!strncmp(line, "HTTP/", 5)) {
                    364:         int version = 0;
                    365:        int ret = 0;
                    366: 
                    367:        cur += 5;
                    368:        while ((*cur >= '0') && (*cur <= '9')) {
                    369:            version *= 10;
                    370:            version += *cur - '0';
                    371:            cur++;
                    372:        }
                    373:        if (*cur == '.') {
                    374:            cur++;
                    375:            if ((*cur >= '0') && (*cur <= '9')) {
                    376:                version *= 10;
                    377:                version += *cur - '0';
                    378:                cur++;
                    379:            }
                    380:            while ((*cur >= '0') && (*cur <= '9'))
                    381:                cur++;
                    382:        } else
                    383:            version *= 10;
                    384:        if ((*cur != ' ') && (*cur != '\t')) return;
                    385:        while ((*cur == ' ') || (*cur == '\t')) cur++;
                    386:        if ((*cur < '0') || (*cur > '9')) return;
                    387:        while ((*cur >= '0') && (*cur <= '9')) {
                    388:            ret *= 10;
                    389:            ret += *cur - '0';
                    390:            cur++;
                    391:        }
                    392:        if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return;
                    393:        ctxt->returnValue = ret;
                    394:     } else if (!strncmp(line, "Content-Type:", 13)) {
                    395:         cur += 13;
                    396:        while ((*cur == ' ') || (*cur == '\t')) cur++;
                    397:        if (ctxt->contentType != NULL)
                    398:            free(ctxt->contentType);
                    399:        ctxt->contentType = strdup(cur);
                    400:     } else if (!strncmp(line, "ContentType:", 12)) {
                    401:         cur += 12;
                    402:        if (ctxt->contentType != NULL) return;
                    403:        while ((*cur == ' ') || (*cur == '\t')) cur++;
                    404:        ctxt->contentType = strdup(cur);
                    405:     } else if (!strncmp(line, "content-type:", 13)) {
                    406:         cur += 13;
                    407:        if (ctxt->contentType != NULL) return;
                    408:        while ((*cur == ' ') || (*cur == '\t')) cur++;
                    409:        ctxt->contentType = strdup(cur);
                    410:     } else if (!strncmp(line, "contenttype:", 12)) {
                    411:         cur += 12;
                    412:        if (ctxt->contentType != NULL) return;
                    413:        while ((*cur == ' ') || (*cur == '\t')) cur++;
                    414:        ctxt->contentType = strdup(cur);
                    415:     } else if (!strncmp(line, "Location:", 9)) {
                    416:         cur += 9;
                    417:        while ((*cur == ' ') || (*cur == '\t')) cur++;
                    418:        if (ctxt->location != NULL)
                    419:            free(ctxt->location);
                    420:        ctxt->location = strdup(cur);
                    421:     } else if (!strncmp(line, "location:", 9)) {
                    422:         cur += 9;
                    423:        if (ctxt->location != NULL) return;
                    424:        while ((*cur == ' ') || (*cur == '\t')) cur++;
                    425:        ctxt->location = strdup(cur);
                    426:     }
                    427: }
                    428: 
1.5     ! daniel    429: /**
        !           430:  * xmlNanoHTTPConnectAttempt:
        !           431:  * @ia:  an internet adress structure
        !           432:  * @port:  the port number
        !           433:  *
        !           434:  * Attempt a connection to the given IP:port endpoint. It forces
        !           435:  * non-blocking semantic on the socket, and allow 60 seconds for
        !           436:  * the host to answer.
        !           437:  *
        !           438:  * Returns -1 in case of failure, the file descriptor number otherwise
        !           439:  */
        !           440: 
        !           441: static int
        !           442: xmlNanoHTTPConnectAttempt(struct in_addr ia, int port)
1.1       daniel    443: {
                    444:     int s=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
                    445:     struct sockaddr_in sin;
                    446:     fd_set wfd;
                    447:     struct timeval tv;
1.2       daniel    448:     int status;
1.1       daniel    449:     
                    450:     if(s==-1) {
1.5     ! daniel    451: #ifdef DEBUG_HTTP
1.1       daniel    452:        perror("socket");
1.5     ! daniel    453: #endif
1.1       daniel    454:        return(-1);
                    455:     }
                    456:     
1.2       daniel    457: #ifdef _WINSOCKAPI_
                    458:     {
                    459:        long levents = FD_READ | FD_WRITE | FD_ACCEPT |
                    460:                       FD_CONNECT | FD_CLOSE ;
                    461:        int rv = 0 ;
                    462:        u_long one = 1;
                    463: 
1.3       daniel    464:        status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0;
1.2       daniel    465:     }
                    466: #else /* _WINSOCKAPI_ */
                    467: #if defined(VMS)
                    468:     {
                    469:        int enable = 1;
1.3       daniel    470:        status = IOCTL(s, FIONBIO, &enable);
1.2       daniel    471:     }
                    472: #else /* VMS */
1.3       daniel    473:     if((status = fcntl(s, F_GETFL, 0)) != -1) {
1.2       daniel    474: #ifdef O_NONBLOCK
                    475:        status |= O_NONBLOCK;
                    476: #else /* O_NONBLOCK */
                    477: #ifdef F_NDELAY
                    478:        status |= F_NDELAY;
                    479: #endif /* F_NDELAY */
                    480: #endif /* !O_NONBLOCK */
1.3       daniel    481:        status = fcntl(s, F_SETFL, status);
1.2       daniel    482:     }
                    483:     if(status < 0) {
1.5     ! daniel    484: #ifdef DEBUG_HTTP
1.1       daniel    485:        perror("nonblocking");
1.5     ! daniel    486: #endif
1.1       daniel    487:        close(s);
                    488:        return(-1);
                    489:     }
1.2       daniel    490: #endif /* !VMS */
                    491: #endif /* !_WINSOCKAPI_ */
                    492: 
1.1       daniel    493: 
                    494:     sin.sin_family = AF_INET;  
                    495:     sin.sin_addr   = ia;
                    496:     sin.sin_port   = htons(port);
                    497:     
                    498:     if((connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1) &&
                    499:        (errno != EINPROGRESS)) {
                    500:        perror("connect");
                    501:        close(s);
                    502:        return(-1);
                    503:     }  
                    504:     
                    505:     tv.tv_sec = 60;            /* We use 60 second timeouts for now */
                    506:     tv.tv_usec = 0;
                    507:     
                    508:     FD_ZERO(&wfd);
                    509:     FD_SET(s, &wfd);
                    510:     
                    511:     switch(select(s+1, NULL, &wfd, NULL, &tv))
                    512:     {
                    513:        case 0:
                    514:            /* Time out */
                    515:            close(s);
                    516:            return(-1);
                    517:        case -1:
                    518:            /* Ermm.. ?? */
1.5     ! daniel    519: #ifdef DEBUG_HTTP
1.1       daniel    520:            perror("select");
1.5     ! daniel    521: #endif
1.1       daniel    522:            close(s);
                    523:            return(-1);
                    524:     }
                    525:     
1.5     ! daniel    526:     return(s);
1.1       daniel    527: }
                    528:  
1.5     ! daniel    529: /**
        !           530:  * xmlNanoHTTPConnectHost:
        !           531:  * @host:  the host name
        !           532:  * @port:  the port number
        !           533:  *
        !           534:  * Attempt a connection to the given host:port endpoint. It tries
        !           535:  * the multiple IP provided by the DNS if available.
        !           536:  *
        !           537:  * Returns -1 in case of failure, the file descriptor number otherwise
        !           538:  */
        !           539: 
        !           540: static int
        !           541: xmlNanoHTTPConnectHost(const char *host, int port)
1.1       daniel    542: {
                    543:     struct hostent *h;
                    544:     int i;
                    545:     int s;
                    546:     
                    547:     h=gethostbyname(host);
                    548:     if(h==NULL)
                    549:     {
1.5     ! daniel    550: #ifdef DEBUG_HTTP
1.1       daniel    551:        fprintf(stderr,"unable to resolve '%s'.\n", host);
1.5     ! daniel    552: #endif
1.1       daniel    553:        return(-1);
                    554:     }
                    555:     
                    556:     for(i=0; h->h_addr_list[i]; i++)
                    557:     {
                    558:        struct in_addr ia;
                    559:        memcpy(&ia, h->h_addr_list[i],4);
                    560:        s = xmlNanoHTTPConnectAttempt(ia, port);
                    561:        if(s != -1)
1.5     ! daniel    562:            return(s);
1.1       daniel    563:     }
1.5     ! daniel    564: 
        !           565: #ifdef DEBUG_HTTP
1.1       daniel    566:     fprintf(stderr, "unable to connect to '%s'.\n", host);
1.5     ! daniel    567: #endif
1.1       daniel    568:     return(-1);
                    569: }
                    570: 
                    571: 
1.5     ! daniel    572: /**
        !           573:  * xmlNanoHTTPOpen:
        !           574:  * @URL:  The URL to load
        !           575:  * @contentType:  if available the Content-Type information will be
        !           576:  *                returned at that location
        !           577:  *
        !           578:  * This function try to open a connection to the indicated resource
        !           579:  * via HTTP GET.
        !           580:  *
        !           581:  * Returns -1 in case of failure, 0 incase of success. The contentType,
        !           582:  *     if provided must be freed by the caller
        !           583:  */
1.1       daniel    584: 
                    585: void *
                    586: xmlNanoHTTPOpen(const char *URL, char **contentType) {
                    587:     xmlNanoHTTPCtxtPtr ctxt;
                    588:     char buf[4096];
                    589:     int ret;
                    590:     char *p;
                    591:     int head;
                    592:     int nbRedirects = 0;
                    593:     char *redirURL = NULL;
                    594:     
1.5     ! daniel    595:     if (contentType != NULL) *contentType = NULL;
        !           596: 
1.1       daniel    597: retry:
                    598:     if (redirURL == NULL)
                    599:        ctxt = xmlNanoHTTPNewCtxt(URL);
                    600:     else {
                    601:        ctxt = xmlNanoHTTPNewCtxt(redirURL);
                    602:        free(redirURL);
                    603:        redirURL = NULL;
                    604:     }
                    605: 
                    606:     if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
                    607:         xmlNanoHTTPFreeCtxt(ctxt);
                    608:        if (redirURL != NULL) free(redirURL);
                    609:         return(NULL);
                    610:     }
                    611:     if (ctxt->hostname == NULL) {
                    612:         xmlNanoHTTPFreeCtxt(ctxt);
                    613:         return(NULL);
                    614:     }
                    615:     ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
                    616:     if (ret < 0) {
                    617:         xmlNanoHTTPFreeCtxt(ctxt);
                    618:         return(NULL);
                    619:     }
                    620:     ctxt->fd = ret;
1.5     ! daniel    621:     snprintf(buf, sizeof(buf),"GET %s HTTP/1.0\r\nHost: %s\r\n\r\n",
1.1       daniel    622:             ctxt->path, ctxt->hostname);
1.5     ! daniel    623: #ifdef DEBUG_HTTP
        !           624:     printf("-> GET %s HTTP/1.0\n-> Host: %s\n\n",
        !           625:            ctxt->path, ctxt->hostname);
        !           626: #endif
1.1       daniel    627:     ctxt->outptr = ctxt->out = strdup(buf);
                    628:     ctxt->state = XML_NANO_HTTP_WRITE;
                    629:     xmlNanoHTTPSend(ctxt);
                    630:     ctxt->state = XML_NANO_HTTP_READ;
                    631:     head = 1;
                    632: 
                    633:     while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
                    634:         if (head && (*p == 0)) {
                    635:            head = 0;
                    636:            ctxt->content = ctxt->inrptr;
                    637:            break;
                    638:        }
                    639:        xmlNanoHTTPScanAnswer(ctxt, p);
                    640: 
1.5     ! daniel    641: #ifdef DEBUG_HTTP
        !           642:        if (p != NULL) printf("<- %s\n", p);
        !           643: #endif
        !           644:         if (p != NULL) free(p);
1.1       daniel    645:     }
                    646: 
                    647:     if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
                    648:         (ctxt->returnValue < 400)) {
1.5     ! daniel    649: #ifdef DEBUG_HTTP
        !           650:        printf("\nRedirect to: %s\n", ctxt->location);
        !           651: #endif
1.1       daniel    652:        while (xmlNanoHTTPRecv(ctxt)) ;
                    653:         if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
                    654:            nbRedirects++;
                    655:            redirURL = strdup(ctxt->location);
                    656:            xmlNanoHTTPFreeCtxt(ctxt);
                    657:            goto retry;
                    658:        }
                    659:        xmlNanoHTTPFreeCtxt(ctxt);
1.5     ! daniel    660: #ifdef DEBUG_HTTP
        !           661:        printf("Too many redirrects, aborting ...\n");
        !           662: #endif
1.1       daniel    663:        return(NULL);
                    664: 
                    665:     }
                    666: 
1.5     ! daniel    667:     if ((contentType != NULL) && (ctxt->contentType != NULL))
        !           668:         *contentType = strdup(ctxt->contentType);
        !           669: 
        !           670: #ifdef DEBUG_HTTP
        !           671:     if (ctxt->contentType != NULL)
        !           672:        printf("\nCode %d, content-type '%s'\n\n",
        !           673:               ctxt->returnValue, ctxt->contentType);
        !           674:     else
        !           675:        printf("\nCode %d, no content-type\n\n",
        !           676:               ctxt->returnValue);
        !           677: #endif
1.1       daniel    678: 
                    679:     return((void *) ctxt);
                    680: }
                    681: 
1.5     ! daniel    682: /**
        !           683:  * xmlNanoHTTPRead:
        !           684:  * @ctx:  the HTTP context
        !           685:  * @dest:  a buffer
        !           686:  * @len:  the buffer length
        !           687:  *
        !           688:  * This function tries to read @len bytes from the existing HTTP connection
        !           689:  * and saves them in @dest. This is a blocking call.
        !           690:  *
        !           691:  * Returns the number of byte read. 0 is an indication of an end of connection.
        !           692:  *         -1 indicates a parameter error.
        !           693:  */
1.1       daniel    694: int
                    695: xmlNanoHTTPRead(void *ctx, void *dest, int len) {
                    696:     xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
                    697: 
                    698:     if (ctx == NULL) return(-1);
                    699:     if (dest == NULL) return(-1);
                    700:     if (len <= 0) return(0);
                    701: 
                    702:     while (ctxt->inptr - ctxt->inrptr < len) {
                    703:         if (xmlNanoHTTPRecv(ctxt) == 0) break;
                    704:     }
                    705:     if (ctxt->inptr - ctxt->inrptr < len)
                    706:         len = ctxt->inptr - ctxt->inrptr;
                    707:     memcpy(dest, ctxt->inrptr, len);
                    708:     ctxt->inrptr += len;
                    709:     return(len);
                    710: }
                    711: 
1.5     ! daniel    712: /**
        !           713:  * xmlNanoHTTPClose:
        !           714:  * @ctx:  the HTTP context
        !           715:  *
        !           716:  * This function closes an HTTP context, it ends up the connection and
        !           717:  * free all data related to it.
        !           718:  */
1.1       daniel    719: void
                    720: xmlNanoHTTPClose(void *ctx) {
                    721:     xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
                    722: 
                    723:     if (ctx == NULL) return;
                    724: 
                    725:     xmlNanoHTTPFreeCtxt(ctxt);
                    726: }
                    727: 
1.5     ! daniel    728: /**
        !           729:  * xmlNanoHTTPFetch:
        !           730:  * @URL:  The URL to load
        !           731:  * @filename:  the filename where the content should be saved
        !           732:  * @contentType:  if available the Content-Type information will be
        !           733:  *                returned at that location
        !           734:  *
        !           735:  * This function try to fetch the indicated resource via HTTP GET
        !           736:  * and save it's content in the file.
        !           737:  *
        !           738:  * Returns -1 in case of failure, 0 incase of success. The contentType,
        !           739:  *     if provided must be freed by the caller
        !           740:  */
        !           741: int
        !           742: xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
1.1       daniel    743:     void *ctxt;
                    744:     char buf[4096];
                    745:     int fd;
                    746:     int len;
                    747:     
                    748:     ctxt = xmlNanoHTTPOpen(URL, contentType);
                    749:     if (ctxt == NULL) return(-1);
                    750: 
                    751:     if (!strcmp(filename, "-")) 
                    752:         fd = 0;
                    753:     else {
                    754:         fd = open(filename, O_CREAT | O_WRONLY);
                    755:        if (fd < 0) {
                    756:            xmlNanoHTTPClose(ctxt);
1.5     ! daniel    757:            if ((contentType != NULL) && (*contentType != NULL)) {
        !           758:                free(*contentType);
        !           759:                *contentType = NULL;
        !           760:            }
1.1       daniel    761:            return(-1);
                    762:        }
                    763:     }
                    764: 
                    765:     while ((len = xmlNanoHTTPRead(ctxt, buf, sizeof(buf))) > 0) {
                    766:        write(fd, buf, len);
                    767:     }
                    768: 
                    769:     xmlNanoHTTPClose(ctxt);
                    770:     return(0);
                    771: }
                    772: 
                    773: #ifdef STANDALONE
                    774: int main(int argc, char **argv) {
                    775:     char *contentType = NULL;
                    776: 
                    777:     if (argv[1] != NULL) {
                    778:        if (argv[2] != NULL) 
                    779:            xmlNanoHTTPFetch(argv[1], argv[2], &contentType);
                    780:         else
                    781:            xmlNanoHTTPFetch(argv[1], "-", &contentType);
1.5     ! daniel    782:        if (contentType != NULL) free(contentType);
1.1       daniel    783:     } else {
                    784:         printf("%s: minimal HTTP GET implementation\n", argv[0]);
                    785:         printf("\tusage %s [ URL [ filename ] ]\n", argv[0]);
                    786:     }
                    787:     return(0);
                    788: }
                    789: #endif /* STANDALONE */

Webmaster