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