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