Annotation of XML/nanohttp.c, revision 1.6

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

Webmaster