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