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