dsrc isn't necessary for this repo
[client-tools.git] / src / external / 3rd / library / libxml / nanohttp.c
blobc99f6e13145fc5edb14e25aee6d4e1cb8ee24111
1 /*
2 * nanohttp.c: minimalist HTTP GET implementation to fetch external subsets.
3 * focuses on size, streamability, reentrancy and portability
5 * This is clearly not a general purpose HTTP implementation
6 * If you look for one, check:
7 * http://www.w3.org/Library/
9 * See Copyright for the status of this software.
11 * daniel@veillard.com
14 /* TODO add compression support, Send the Accept- , and decompress on the
15 fly with ZLIB if found at compile-time */
17 #define NEED_SOCKETS
18 #define IN_LIBXML
19 #include "libxml.h"
21 #ifdef LIBXML_HTTP_ENABLED
22 #include <string.h>
24 #ifdef HAVE_STDLIB_H
25 #include <stdlib.h>
26 #endif
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30 #ifdef HAVE_SYS_SOCKET_H
31 #include <sys/socket.h>
32 #endif
33 #ifdef HAVE_NETINET_IN_H
34 #include <netinet/in.h>
35 #endif
36 #ifdef HAVE_ARPA_INET_H
37 #include <arpa/inet.h>
38 #endif
39 #ifdef HAVE_NETDB_H
40 #include <netdb.h>
41 #endif
42 #ifdef HAVE_RESOLV_H
43 #ifdef HAVE_ARPA_NAMESER_H
44 #include <arpa/nameser.h>
45 #endif
46 #include <resolv.h>
47 #endif
48 #ifdef HAVE_FCNTL_H
49 #include <fcntl.h>
50 #endif
51 #ifdef HAVE_ERRNO_H
52 #include <errno.h>
53 #endif
54 #ifdef HAVE_SYS_TIME_H
55 #include <sys/time.h>
56 #endif
57 #ifdef HAVE_SYS_SELECT_H
58 #include <sys/select.h>
59 #endif
60 #ifdef HAVE_STRINGS_H
61 #include <strings.h>
62 #endif
63 #ifdef SUPPORT_IP6
64 #include <resolv.h>
65 #endif
67 #ifdef VMS
68 #include <stropts>
69 #define SOCKLEN_T unsigned int
70 #define SOCKET int
71 #endif
73 #include <libxml/globals.h>
74 #include <libxml/xmlerror.h>
75 #include <libxml/xmlmemory.h>
76 #include <libxml/parser.h> /* for xmlStr(n)casecmp() */
77 #include <libxml/nanohttp.h>
78 #include <libxml/globals.h>
80 /**
81 * A couple portability macros
83 #ifndef _WINSOCKAPI_
84 #define closesocket(s) close(s)
85 #define SOCKET int
86 #endif
88 #ifndef SOCKLEN_T
89 #define SOCKLEN_T unsigned int
90 #endif
91 #ifndef SOCKET
92 #define SOCKET int
93 #endif
95 #ifdef STANDALONE
96 #define DEBUG_HTTP
97 #define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)
98 #define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b)
99 #endif
101 #define XML_NANO_HTTP_MAX_REDIR 10
103 #define XML_NANO_HTTP_CHUNK 4096
105 #define XML_NANO_HTTP_CLOSED 0
106 #define XML_NANO_HTTP_WRITE 1
107 #define XML_NANO_HTTP_READ 2
108 #define XML_NANO_HTTP_NONE 4
110 typedef struct xmlNanoHTTPCtxt {
111 char *protocol; /* the protocol name */
112 char *hostname; /* the host name */
113 int port; /* the port */
114 char *path; /* the path within the URL */
115 SOCKET fd; /* the file descriptor for the socket */
116 int state; /* WRITE / READ / CLOSED */
117 char *out; /* buffer sent (zero terminated) */
118 char *outptr; /* index within the buffer sent */
119 char *in; /* the receiving buffer */
120 char *content; /* the start of the content */
121 char *inptr; /* the next byte to read from network */
122 char *inrptr; /* the next byte to give back to the client */
123 int inlen; /* len of the input buffer */
124 int last; /* return code for last operation */
125 int returnValue; /* the protocol return value */
126 int ContentLength; /* specified content length from HTTP header */
127 char *contentType; /* the MIME type for the input */
128 char *location; /* the new URL in case of redirect */
129 char *authHeader; /* contents of {WWW,Proxy}-Authenticate header */
130 } xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;
132 static int initialized = 0;
133 static char *proxy = NULL; /* the proxy name if any */
134 static int proxyPort; /* the proxy port if any */
135 static unsigned int timeout = 60;/* the select() timeout in seconds */
137 int xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len );
138 int xmlNanoHTTPContentLength( void * ctx );
141 * A portability function
143 static int socket_errno(void) {
144 #ifdef _WINSOCKAPI_
145 return(WSAGetLastError());
146 #else
147 return(errno);
148 #endif
152 * xmlNanoHTTPInit:
154 * Initialize the HTTP protocol layer.
155 * Currently it just checks for proxy informations
158 void
159 xmlNanoHTTPInit(void) {
160 const char *env;
161 #ifdef _WINSOCKAPI_
162 WSADATA wsaData;
163 #endif
165 if (initialized)
166 return;
168 #ifdef _WINSOCKAPI_
169 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
170 return;
171 #endif
173 if (proxy == NULL) {
174 proxyPort = 80;
175 env = getenv("no_proxy");
176 if (env != NULL)
177 goto done;
178 env = getenv("http_proxy");
179 if (env != NULL) {
180 xmlNanoHTTPScanProxy(env);
181 goto done;
183 env = getenv("HTTP_PROXY");
184 if (env != NULL) {
185 xmlNanoHTTPScanProxy(env);
186 goto done;
189 done:
190 initialized = 1;
194 * xmlNanoHTTPCleanup:
196 * Cleanup the HTTP protocol layer.
199 void
200 xmlNanoHTTPCleanup(void) {
201 if (proxy != NULL)
202 xmlFree(proxy);
203 #ifdef _WINSOCKAPI_
204 if (initialized)
205 WSACleanup();
206 #endif
207 initialized = 0;
208 return;
212 * xmlNanoHTTPScanURL:
213 * @ctxt: an HTTP context
214 * @URL: The URL used to initialize the context
216 * (Re)Initialize an HTTP context by parsing the URL and finding
217 * the protocol host port and path it indicates.
220 static void
221 xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) {
222 const char *cur = URL;
223 char buf[4096];
224 int indx = 0;
225 int port = 0;
227 if (ctxt->protocol != NULL) {
228 xmlFree(ctxt->protocol);
229 ctxt->protocol = NULL;
231 if (ctxt->hostname != NULL) {
232 xmlFree(ctxt->hostname);
233 ctxt->hostname = NULL;
235 if (ctxt->path != NULL) {
236 xmlFree(ctxt->path);
237 ctxt->path = NULL;
239 if (URL == NULL) return;
240 buf[indx] = 0;
241 while (*cur != 0) {
242 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
243 buf[indx] = 0;
244 ctxt->protocol = xmlMemStrdup(buf);
245 indx = 0;
246 cur += 3;
247 break;
249 buf[indx++] = *cur++;
251 if (*cur == 0) return;
253 buf[indx] = 0;
254 while (1) {
255 if (cur[0] == ':') {
256 buf[indx] = 0;
257 ctxt->hostname = xmlMemStrdup(buf);
258 indx = 0;
259 cur += 1;
260 while ((*cur >= '0') && (*cur <= '9')) {
261 port *= 10;
262 port += *cur - '0';
263 cur++;
265 if (port != 0) ctxt->port = port;
266 while ((cur[0] != '/') && (*cur != 0))
267 cur++;
268 break;
270 if ((*cur == '/') || (*cur == 0)) {
271 buf[indx] = 0;
272 ctxt->hostname = xmlMemStrdup(buf);
273 indx = 0;
274 break;
276 buf[indx++] = *cur++;
278 if (*cur == 0)
279 ctxt->path = xmlMemStrdup("/");
280 else {
281 indx = 0;
282 buf[indx] = 0;
283 while (*cur != 0)
284 buf[indx++] = *cur++;
285 buf[indx] = 0;
286 ctxt->path = xmlMemStrdup(buf);
291 * xmlNanoHTTPScanProxy:
292 * @URL: The proxy URL used to initialize the proxy context
294 * (Re)Initialize the HTTP Proxy context by parsing the URL and finding
295 * the protocol host port it indicates.
296 * Should be like http://myproxy/ or http://myproxy:3128/
297 * A NULL URL cleans up proxy informations.
300 void
301 xmlNanoHTTPScanProxy(const char *URL) {
302 const char *cur = URL;
303 char buf[4096];
304 int indx = 0;
305 int port = 0;
307 if (proxy != NULL) {
308 xmlFree(proxy);
309 proxy = NULL;
311 if (proxyPort != 0) {
312 proxyPort = 0;
314 #ifdef DEBUG_HTTP
315 if (URL == NULL)
316 xmlGenericError(xmlGenericErrorContext,
317 "Removing HTTP proxy info\n");
318 else
319 xmlGenericError(xmlGenericErrorContext,
320 "Using HTTP proxy %s\n", URL);
321 #endif
322 if (URL == NULL) return;
323 buf[indx] = 0;
324 while (*cur != 0) {
325 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
326 buf[indx] = 0;
327 indx = 0;
328 cur += 3;
329 break;
331 buf[indx++] = *cur++;
333 if (*cur == 0) return;
335 buf[indx] = 0;
336 while (1) {
337 if (cur[0] == ':') {
338 buf[indx] = 0;
339 proxy = xmlMemStrdup(buf);
340 indx = 0;
341 cur += 1;
342 while ((*cur >= '0') && (*cur <= '9')) {
343 port *= 10;
344 port += *cur - '0';
345 cur++;
347 if (port != 0) proxyPort = port;
348 while ((cur[0] != '/') && (*cur != 0))
349 cur++;
350 break;
352 if ((*cur == '/') || (*cur == 0)) {
353 buf[indx] = 0;
354 proxy = xmlMemStrdup(buf);
355 indx = 0;
356 break;
358 buf[indx++] = *cur++;
363 * xmlNanoHTTPNewCtxt:
364 * @URL: The URL used to initialize the context
366 * Allocate and initialize a new HTTP context.
368 * Returns an HTTP context or NULL in case of error.
371 static xmlNanoHTTPCtxtPtr
372 xmlNanoHTTPNewCtxt(const char *URL) {
373 xmlNanoHTTPCtxtPtr ret;
375 ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt));
376 if (ret == NULL) return(NULL);
378 memset(ret, 0, sizeof(xmlNanoHTTPCtxt));
379 ret->port = 80;
380 ret->returnValue = 0;
381 ret->fd = -1;
382 ret->ContentLength = -1;
384 xmlNanoHTTPScanURL(ret, URL);
386 return(ret);
390 * xmlNanoHTTPFreeCtxt:
391 * @ctxt: an HTTP context
393 * Frees the context after closing the connection.
396 static void
397 xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) {
398 if (ctxt == NULL) return;
399 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
400 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
401 if (ctxt->path != NULL) xmlFree(ctxt->path);
402 if (ctxt->out != NULL) xmlFree(ctxt->out);
403 if (ctxt->in != NULL) xmlFree(ctxt->in);
404 if (ctxt->contentType != NULL) xmlFree(ctxt->contentType);
405 if (ctxt->location != NULL) xmlFree(ctxt->location);
406 if (ctxt->authHeader != NULL) xmlFree(ctxt->authHeader);
407 ctxt->state = XML_NANO_HTTP_NONE;
408 if (ctxt->fd >= 0) closesocket(ctxt->fd);
409 ctxt->fd = -1;
410 xmlFree(ctxt);
414 * xmlNanoHTTPSend:
415 * @ctxt: an HTTP context
417 * Send the input needed to initiate the processing on the server side
418 * Returns number of bytes sent or -1 on error.
421 static int
422 xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt, const char * xmt_ptr, int outlen) {
424 int total_sent = 0;
426 if ( (ctxt->state & XML_NANO_HTTP_WRITE) && (xmt_ptr != NULL ) ) {
427 while (total_sent < outlen) {
428 int nsent = send(ctxt->fd, xmt_ptr + total_sent,
429 outlen - total_sent, 0);
430 if (nsent>0)
431 total_sent += nsent;
432 else if ( ( nsent == -1 ) &&
433 #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
434 ( socket_errno( ) != EAGAIN ) &&
435 #endif
436 ( socket_errno( ) != EWOULDBLOCK ) ) {
437 xmlGenericError( xmlGenericErrorContext,
438 "xmlNanoHTTPSend error: %s",
439 strerror( socket_errno( ) ) );
441 if ( total_sent == 0 )
442 total_sent = -1;
443 break;
445 else {
447 ** No data sent
448 ** Since non-blocking sockets are used, wait for
449 ** socket to be writable or default timeout prior
450 ** to retrying.
453 struct timeval tv;
454 fd_set wfd;
456 tv.tv_sec = timeout;
457 tv.tv_usec = 0;
458 FD_ZERO( &wfd );
459 FD_SET( ctxt->fd, &wfd );
460 (void)select( ctxt->fd + 1, NULL, &wfd, NULL, &tv );
465 return total_sent;
469 * xmlNanoHTTPRecv:
470 * @ctxt: an HTTP context
472 * Read information coming from the HTTP connection.
473 * This is a blocking call (but it blocks in select(), not read()).
475 * Returns the number of byte read or -1 in case of error.
478 static int
479 xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt) {
480 fd_set rfd;
481 struct timeval tv;
484 while (ctxt->state & XML_NANO_HTTP_READ) {
485 if (ctxt->in == NULL) {
486 ctxt->in = (char *) xmlMalloc(65000 * sizeof(char));
487 if (ctxt->in == NULL) {
488 ctxt->last = -1;
489 xmlGenericError( xmlGenericErrorContext,
490 "xmlNanoHTTPRecv: Error allocating input memory." );
491 return(-1);
493 ctxt->inlen = 65000;
494 ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in;
496 if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) {
497 int delta = ctxt->inrptr - ctxt->in;
498 int len = ctxt->inptr - ctxt->inrptr;
500 memmove(ctxt->in, ctxt->inrptr, len);
501 ctxt->inrptr -= delta;
502 ctxt->content -= delta;
503 ctxt->inptr -= delta;
505 if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) {
506 int d_inptr = ctxt->inptr - ctxt->in;
507 int d_content = ctxt->content - ctxt->in;
508 int d_inrptr = ctxt->inrptr - ctxt->in;
509 char * tmp_ptr = ctxt->in;
511 ctxt->inlen *= 2;
512 ctxt->in = (char *) xmlRealloc(tmp_ptr, ctxt->inlen);
513 if (ctxt->in == NULL) {
514 xmlGenericError( xmlGenericErrorContext,
515 "xmlNanoHTTPRecv: %s %d bytes.",
516 "Failed to realloc input buffer to",
517 ctxt->inlen );
518 xmlFree( tmp_ptr );
519 ctxt->last = -1;
520 return(-1);
522 ctxt->inptr = ctxt->in + d_inptr;
523 ctxt->content = ctxt->in + d_content;
524 ctxt->inrptr = ctxt->in + d_inrptr;
526 ctxt->last = recv(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK, 0);
527 if (ctxt->last > 0) {
528 ctxt->inptr += ctxt->last;
529 return(ctxt->last);
531 if (ctxt->last == 0) {
532 return(0);
534 if (ctxt->last == -1) {
535 switch (socket_errno()) {
536 case EINPROGRESS:
537 case EWOULDBLOCK:
538 #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
539 case EAGAIN:
540 #endif
541 break;
543 case ECONNRESET:
544 case ESHUTDOWN:
545 return ( 0 );
547 default:
548 xmlGenericError( xmlGenericErrorContext,
549 "xmlNanoHTTPRecv: recv( ) failure - %s",
550 strerror( socket_errno( ) ) );
551 return(-1);
555 tv.tv_sec = timeout;
556 tv.tv_usec = 0;
557 FD_ZERO(&rfd);
558 FD_SET(ctxt->fd, &rfd);
560 if ( (select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1)
561 #if defined(EINTR)
562 && (errno != EINTR)
563 #endif
565 return(0);
567 return(0);
571 * xmlNanoHTTPReadLine:
572 * @ctxt: an HTTP context
574 * Read one line in the HTTP server output, usually for extracting
575 * the HTTP protocol informations from the answer header.
577 * Returns a newly allocated string with a copy of the line, or NULL
578 * which indicate the end of the input.
581 static char *
582 xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) {
583 char buf[4096];
584 char *bp = buf;
585 int rc;
587 while (bp - buf < 4095) {
588 if (ctxt->inrptr == ctxt->inptr) {
589 if ( (rc = xmlNanoHTTPRecv(ctxt)) == 0) {
590 if (bp == buf)
591 return(NULL);
592 else
593 *bp = 0;
594 return(xmlMemStrdup(buf));
596 else if ( rc == -1 ) {
597 return ( NULL );
600 *bp = *ctxt->inrptr++;
601 if (*bp == '\n') {
602 *bp = 0;
603 return(xmlMemStrdup(buf));
605 if (*bp != '\r')
606 bp++;
608 buf[4095] = 0;
609 return(xmlMemStrdup(buf));
614 * xmlNanoHTTPScanAnswer:
615 * @ctxt: an HTTP context
616 * @line: an HTTP header line
618 * Try to extract useful informations from the server answer.
619 * We currently parse and process:
620 * - The HTTP revision/ return code
621 * - The Content-Type
622 * - The Location for redirect processing.
624 * Returns -1 in case of failure, the file descriptor number otherwise
627 static void
628 xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) {
629 const char *cur = line;
631 if (line == NULL) return;
633 if (!strncmp(line, "HTTP/", 5)) {
634 int version = 0;
635 int ret = 0;
637 cur += 5;
638 while ((*cur >= '0') && (*cur <= '9')) {
639 version *= 10;
640 version += *cur - '0';
641 cur++;
643 if (*cur == '.') {
644 cur++;
645 if ((*cur >= '0') && (*cur <= '9')) {
646 version *= 10;
647 version += *cur - '0';
648 cur++;
650 while ((*cur >= '0') && (*cur <= '9'))
651 cur++;
652 } else
653 version *= 10;
654 if ((*cur != ' ') && (*cur != '\t')) return;
655 while ((*cur == ' ') || (*cur == '\t')) cur++;
656 if ((*cur < '0') || (*cur > '9')) return;
657 while ((*cur >= '0') && (*cur <= '9')) {
658 ret *= 10;
659 ret += *cur - '0';
660 cur++;
662 if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return;
663 ctxt->returnValue = ret;
664 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Type:", 13)) {
665 cur += 13;
666 while ((*cur == ' ') || (*cur == '\t')) cur++;
667 if (ctxt->contentType != NULL)
668 xmlFree(ctxt->contentType);
669 ctxt->contentType = xmlMemStrdup(cur);
670 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"ContentType:", 12)) {
671 cur += 12;
672 if (ctxt->contentType != NULL) return;
673 while ((*cur == ' ') || (*cur == '\t')) cur++;
674 ctxt->contentType = xmlMemStrdup(cur);
675 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Location:", 9)) {
676 cur += 9;
677 while ((*cur == ' ') || (*cur == '\t')) cur++;
678 if (ctxt->location != NULL)
679 xmlFree(ctxt->location);
680 ctxt->location = xmlMemStrdup(cur);
681 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"WWW-Authenticate:", 17)) {
682 cur += 17;
683 while ((*cur == ' ') || (*cur == '\t')) cur++;
684 if (ctxt->authHeader != NULL)
685 xmlFree(ctxt->authHeader);
686 ctxt->authHeader = xmlMemStrdup(cur);
687 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Proxy-Authenticate:", 19)) {
688 cur += 19;
689 while ((*cur == ' ') || (*cur == '\t')) cur++;
690 if (ctxt->authHeader != NULL)
691 xmlFree(ctxt->authHeader);
692 ctxt->authHeader = xmlMemStrdup(cur);
693 } else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Length:", 15) ) {
694 cur += 15;
695 ctxt->ContentLength = strtol( cur, NULL, 10 );
700 * xmlNanoHTTPConnectAttempt:
701 * @addr: a socket address structure
703 * Attempt a connection to the given IP:port endpoint. It forces
704 * non-blocking semantic on the socket, and allow 60 seconds for
705 * the host to answer.
707 * Returns -1 in case of failure, the file descriptor number otherwise
710 static int
711 xmlNanoHTTPConnectAttempt(struct sockaddr *addr)
713 SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
714 fd_set wfd;
715 struct timeval tv;
716 int status;
718 if (s==-1) {
719 #ifdef DEBUG_HTTP
720 perror("socket");
721 #endif
722 xmlGenericError( xmlGenericErrorContext,
723 "xmlNanoHTTPConnectAttempt: %s - %s",
724 "socket creation failure",
725 strerror( socket_errno( ) ) );
726 return(-1);
729 #ifdef _WINSOCKAPI_
731 u_long one = 1;
733 status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0;
735 #else /* _WINSOCKAPI_ */
736 #if defined(VMS)
738 int enable = 1;
739 status = ioctl(s, FIONBIO, &enable);
741 #else /* VMS */
742 if ((status = fcntl(s, F_GETFL, 0)) != -1) {
743 #ifdef O_NONBLOCK
744 status |= O_NONBLOCK;
745 #else /* O_NONBLOCK */
746 #ifdef F_NDELAY
747 status |= F_NDELAY;
748 #endif /* F_NDELAY */
749 #endif /* !O_NONBLOCK */
750 status = fcntl(s, F_SETFL, status);
752 if (status < 0) {
753 #ifdef DEBUG_HTTP
754 perror("nonblocking");
755 #endif
756 xmlGenericError( xmlGenericErrorContext,
757 "xmlNanoHTTPConnectAttempt: %s - %s",
758 "error setting non-blocking IO",
759 strerror( socket_errno( ) ) );
760 closesocket(s);
761 return(-1);
763 #endif /* !VMS */
764 #endif /* !_WINSOCKAPI_ */
766 if ((connect(s, addr, sizeof(*addr))==-1)) {
767 switch (socket_errno()) {
768 case EINPROGRESS:
769 case EWOULDBLOCK:
770 break;
771 default:
772 xmlGenericError( xmlGenericErrorContext,
773 "xmlNanoHTTPConnectAttempt: %s - %s",
774 "error connecting to HTTP server",
775 strerror( socket_errno( ) ) );
776 closesocket(s);
777 return(-1);
781 tv.tv_sec = timeout;
782 tv.tv_usec = 0;
784 FD_ZERO(&wfd);
785 FD_SET(s, &wfd);
787 switch(select(s+1, NULL, &wfd, NULL, &tv))
789 case 0:
790 /* Time out */
791 xmlGenericError( xmlGenericErrorContext,
792 "xmlNanoHTTPConnectAttempt: %s",
793 "Connect attempt timed out." );
794 closesocket(s);
795 return(-1);
796 case -1:
797 /* Ermm.. ?? */
798 xmlGenericError( xmlGenericErrorContext,
799 "xmlNanoHTTPConnectAttempt: %s - %s",
800 "Error connecting to host",
801 strerror( socket_errno( ) ) );
802 closesocket(s);
803 return(-1);
806 if ( FD_ISSET(s, &wfd) ) {
807 SOCKLEN_T len;
808 len = sizeof(status);
809 if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&status, &len) < 0 ) {
810 /* Solaris error code */
811 xmlGenericError( xmlGenericErrorContext,
812 "xmlNanoHTTPConnectAttempt: %s - %s",
813 "Error retrieving pending socket errors",
814 strerror( socket_errno( ) ) );
815 return (-1);
817 if ( status ) {
818 closesocket(s);
819 errno = status;
820 xmlGenericError( xmlGenericErrorContext,
821 "xmlNanoHTTPConnectAttempt: %s - %s",
822 "Error connecting to remote host",
823 strerror( status ) );
824 return (-1);
826 } else {
827 /* pbm */
828 xmlGenericError( xmlGenericErrorContext,
829 "xmlNanoHTTPConnectAttempt: %s\n",
830 "Select returned, but descriptor not set for connection.\n" );
831 closesocket(s);
832 return (-1);
835 return(s);
839 * xmlNanoHTTPConnectHost:
840 * @host: the host name
841 * @port: the port number
843 * Attempt a connection to the given host:port endpoint. It tries
844 * the multiple IP provided by the DNS if available.
846 * Returns -1 in case of failure, the file descriptor number otherwise
849 static int
850 xmlNanoHTTPConnectHost(const char *host, int port)
852 struct hostent *h;
853 struct sockaddr *addr;
854 struct in_addr ia;
855 struct sockaddr_in sockin;
857 #ifdef SUPPORT_IP6
858 struct in6_addr ia6;
859 struct sockaddr_in6 sockin6;
860 #endif
861 int i;
862 int s;
864 #if defined(SUPPORT_IP6) && defined(RES_USE_INET6)
865 if (!(_res.options & RES_INIT))
866 res_init();
867 _res.options |= RES_USE_INET6;
868 #endif
869 h = gethostbyname(host);
870 if (h == NULL) {
873 * Okay, I got fed up by the non-portability of this error message
874 * extraction code. it work on Linux, if it work on your platform
875 * and one want to enable it, send me the defined(foobar) needed
877 #if defined(HAVE_NETDB_H) && defined(HOST_NOT_FOUND) && defined(linux)
878 const char *h_err_txt = "";
880 switch (h_errno) {
881 case HOST_NOT_FOUND:
882 h_err_txt = "Authoritive host not found";
883 break;
885 case TRY_AGAIN:
886 h_err_txt =
887 "Non-authoritive host not found or server failure.";
888 break;
890 case NO_RECOVERY:
891 h_err_txt =
892 "Non-recoverable errors: FORMERR, REFUSED, or NOTIMP.";
893 break;
895 case NO_ADDRESS:
896 h_err_txt =
897 "Valid name, no data record of requested type.";
898 break;
900 default:
901 h_err_txt = "No error text defined.";
902 break;
904 xmlGenericError(xmlGenericErrorContext,
905 "xmlNanoHTTPConnectHost: %s '%s' - %s",
906 "Failed to resolve host", host, h_err_txt);
907 #else
908 xmlGenericError(xmlGenericErrorContext,
909 "xmlNanoHTTPConnectHost: %s '%s'",
910 "Failed to resolve host", host);
911 #endif
912 return (-1);
915 for (i = 0; h->h_addr_list[i]; i++) {
916 if (h->h_addrtype == AF_INET) {
917 /* A records (IPv4) */
918 memcpy(&ia, h->h_addr_list[i], h->h_length);
919 sockin.sin_family = h->h_addrtype;
920 sockin.sin_addr = ia;
921 sockin.sin_port = htons(port);
922 addr = (struct sockaddr *) &sockin;
923 #ifdef SUPPORT_IP6
924 } else if (h->h_addrtype == AF_INET6) {
925 /* AAAA records (IPv6) */
926 memcpy(&ia6, h->h_addr_list[i], h->h_length);
927 sockin6.sin_family = h->h_addrtype;
928 sockin6.sin_addr = ia6;
929 sockin6.sin_port = htons(port);
930 addr = (struct sockaddr *) &sockin6;
931 #endif
932 } else
933 break; /* for */
935 s = xmlNanoHTTPConnectAttempt(addr);
936 if (s != -1)
937 return (s);
940 #ifdef DEBUG_HTTP
941 xmlGenericError(xmlGenericErrorContext,
942 "xmlNanoHTTPConnectHost: unable to connect to '%s'.\n",
943 host);
944 #endif
945 return (-1);
950 * xmlNanoHTTPOpen:
951 * @URL: The URL to load
952 * @contentType: if available the Content-Type information will be
953 * returned at that location
955 * This function try to open a connection to the indicated resource
956 * via HTTP GET.
958 * Returns NULL in case of failure, otherwise a request handler.
959 * The contentType, if provided must be freed by the caller
962 void*
963 xmlNanoHTTPOpen(const char *URL, char **contentType) {
964 if (contentType != NULL) *contentType = NULL;
965 return(xmlNanoHTTPMethod(URL, NULL, NULL, contentType, NULL, 0));
969 * xmlNanoHTTPOpenRedir:
970 * @URL: The URL to load
971 * @contentType: if available the Content-Type information will be
972 * returned at that location
973 * @redir: if available the redirected URL will be returned
975 * This function try to open a connection to the indicated resource
976 * via HTTP GET.
978 * Returns NULL in case of failure, otherwise a request handler.
979 * The contentType, if provided must be freed by the caller
982 void*
983 xmlNanoHTTPOpenRedir(const char *URL, char **contentType, char **redir) {
984 if (contentType != NULL) *contentType = NULL;
985 if (redir != NULL) *redir = NULL;
986 return(xmlNanoHTTPMethodRedir(URL, NULL, NULL, contentType, redir, NULL,0));
990 * xmlNanoHTTPRead:
991 * @ctx: the HTTP context
992 * @dest: a buffer
993 * @len: the buffer length
995 * This function tries to read @len bytes from the existing HTTP connection
996 * and saves them in @dest. This is a blocking call.
998 * Returns the number of byte read. 0 is an indication of an end of connection.
999 * -1 indicates a parameter error.
1002 xmlNanoHTTPRead(void *ctx, void *dest, int len) {
1003 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1005 if (ctx == NULL) return(-1);
1006 if (dest == NULL) return(-1);
1007 if (len <= 0) return(0);
1009 while (ctxt->inptr - ctxt->inrptr < len) {
1010 if (xmlNanoHTTPRecv(ctxt) <= 0) break;
1012 if (ctxt->inptr - ctxt->inrptr < len)
1013 len = ctxt->inptr - ctxt->inrptr;
1014 memcpy(dest, ctxt->inrptr, len);
1015 ctxt->inrptr += len;
1016 return(len);
1020 * xmlNanoHTTPClose:
1021 * @ctx: the HTTP context
1023 * This function closes an HTTP context, it ends up the connection and
1024 * free all data related to it.
1026 void
1027 xmlNanoHTTPClose(void *ctx) {
1028 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1030 if (ctx == NULL) return;
1032 xmlNanoHTTPFreeCtxt(ctxt);
1036 * xmlNanoHTTPMethodRedir:
1037 * @URL: The URL to load
1038 * @method: the HTTP method to use
1039 * @input: the input string if any
1040 * @contentType: the Content-Type information IN and OUT
1041 * @redir: the redirected URL OUT
1042 * @headers: the extra headers
1043 * @ilen: input length
1045 * This function try to open a connection to the indicated resource
1046 * via HTTP using the given @method, adding the given extra headers
1047 * and the input buffer for the request content.
1049 * Returns NULL in case of failure, otherwise a request handler.
1050 * The contentType, or redir, if provided must be freed by the caller
1053 void*
1054 xmlNanoHTTPMethodRedir(const char *URL, const char *method, const char *input,
1055 char **contentType, char **redir,
1056 const char *headers, int ilen ) {
1057 xmlNanoHTTPCtxtPtr ctxt;
1058 char *bp, *p;
1059 int blen, ret;
1060 int head;
1061 int xmt_bytes;
1062 int nbRedirects = 0;
1063 char *redirURL = NULL;
1065 if (URL == NULL) return(NULL);
1066 if (method == NULL) method = "GET";
1067 xmlNanoHTTPInit();
1069 retry:
1070 if (redirURL == NULL)
1071 ctxt = xmlNanoHTTPNewCtxt(URL);
1072 else {
1073 ctxt = xmlNanoHTTPNewCtxt(redirURL);
1076 if ( ctxt == NULL ) {
1077 xmlGenericError( xmlGenericErrorContext,
1078 "xmlNanoHTTPMethodRedir: %s %s.",
1079 "Unable to allocate HTTP context to URI",
1080 ( ( redirURL == NULL ) ? URL : redirURL ) );
1081 return ( NULL );
1084 if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
1085 xmlGenericError( xmlGenericErrorContext,
1086 "xmlNanoHTTPMethodRedir: %s - %s.",
1087 "Not a valid HTTP URI",
1088 ( ( redirURL == NULL ) ? URL : redirURL ) );
1089 xmlNanoHTTPFreeCtxt(ctxt);
1090 if (redirURL != NULL) xmlFree(redirURL);
1091 return(NULL);
1093 if (ctxt->hostname == NULL) {
1094 xmlGenericError( xmlGenericErrorContext,
1095 "xmlNanoHTTPMethodRedir: %s - %s",
1096 "Failed to identify host in URI",
1097 ( ( redirURL == NULL ) ? URL : redirURL ) );
1098 xmlNanoHTTPFreeCtxt(ctxt);
1099 if (redirURL != NULL) xmlFree(redirURL);
1100 return(NULL);
1102 if (proxy) {
1103 blen = strlen(ctxt->hostname) * 2 + 16;
1104 ret = xmlNanoHTTPConnectHost(proxy, proxyPort);
1106 else {
1107 blen = strlen(ctxt->hostname);
1108 ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
1110 if (ret < 0) {
1111 xmlNanoHTTPFreeCtxt(ctxt);
1112 if (redirURL != NULL) xmlFree(redirURL);
1113 return(NULL);
1115 ctxt->fd = ret;
1117 if (input == NULL)
1118 ilen = 0;
1119 else
1120 blen += 36;
1122 if (headers != NULL)
1123 blen += strlen(headers) + 2;
1124 if (contentType && *contentType)
1125 blen += strlen(*contentType) + 16;
1126 blen += strlen(method) + strlen(ctxt->path) + 24;
1127 bp = xmlMalloc(blen);
1128 if ( bp == NULL ) {
1129 xmlNanoHTTPFreeCtxt( ctxt );
1130 xmlGenericError( xmlGenericErrorContext,
1131 "xmlNanoHTTPMethodRedir: %s",
1132 "Error allocating HTTP header buffer." );
1133 return ( NULL );
1136 p = bp;
1138 if (proxy) {
1139 if (ctxt->port != 80) {
1140 p += snprintf( p, blen - (p - bp), "%s http://%s:%d%s",
1141 method, ctxt->hostname,
1142 ctxt->port, ctxt->path );
1144 else
1145 p += snprintf( p, blen - (p - bp), "%s http://%s%s", method,
1146 ctxt->hostname, ctxt->path);
1148 else
1149 p += snprintf( p, blen - (p - bp), "%s %s", method, ctxt->path);
1151 p += snprintf( p, blen - (p - bp), " HTTP/1.0\r\nHost: %s\r\n",
1152 ctxt->hostname);
1154 if (contentType != NULL && *contentType)
1155 p += snprintf(p, blen - (p - bp), "Content-Type: %s\r\n", *contentType);
1157 if (headers != NULL)
1158 p += snprintf( p, blen - (p - bp), "%s", headers );
1160 if (input != NULL)
1161 snprintf(p, blen - (p - bp), "Content-Length: %d\r\n\r\n", ilen );
1162 else
1163 snprintf(p, blen - (p - bp), "\r\n");
1165 #ifdef DEBUG_HTTP
1166 xmlGenericError(xmlGenericErrorContext,
1167 "-> %s%s", proxy? "(Proxy) " : "", bp);
1168 if ((blen -= strlen(bp)+1) < 0)
1169 xmlGenericError(xmlGenericErrorContext,
1170 "ERROR: overflowed buffer by %d bytes\n", -blen);
1171 #endif
1172 ctxt->outptr = ctxt->out = bp;
1173 ctxt->state = XML_NANO_HTTP_WRITE;
1174 blen = strlen( ctxt->out );
1175 xmt_bytes = xmlNanoHTTPSend(ctxt, ctxt->out, blen );
1176 #ifdef DEBUG_HTTP
1177 if ( xmt_bytes != blen )
1178 xmlGenericError( xmlGenericErrorContext,
1179 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1180 xmt_bytes, blen,
1181 "bytes of HTTP headers sent to host",
1182 ctxt->hostname );
1183 #endif
1185 if ( input != NULL ) {
1186 xmt_bytes = xmlNanoHTTPSend( ctxt, input, ilen );
1188 #ifdef DEBUG_HTTP
1189 if ( xmt_bytes != ilen )
1190 xmlGenericError( xmlGenericErrorContext,
1191 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1192 xmt_bytes, ilen,
1193 "bytes of HTTP content sent to host",
1194 ctxt->hostname );
1195 #endif
1198 ctxt->state = XML_NANO_HTTP_READ;
1199 head = 1;
1201 while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
1202 if (head && (*p == 0)) {
1203 head = 0;
1204 ctxt->content = ctxt->inrptr;
1205 xmlFree(p);
1206 break;
1208 xmlNanoHTTPScanAnswer(ctxt, p);
1210 #ifdef DEBUG_HTTP
1211 xmlGenericError(xmlGenericErrorContext, "<- %s\n", p);
1212 #endif
1213 xmlFree(p);
1216 if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
1217 (ctxt->returnValue < 400)) {
1218 #ifdef DEBUG_HTTP
1219 xmlGenericError(xmlGenericErrorContext,
1220 "\nRedirect to: %s\n", ctxt->location);
1221 #endif
1222 while ( xmlNanoHTTPRecv(ctxt) > 0 ) ;
1223 if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
1224 nbRedirects++;
1225 if (redirURL != NULL)
1226 xmlFree(redirURL);
1227 redirURL = xmlMemStrdup(ctxt->location);
1228 xmlNanoHTTPFreeCtxt(ctxt);
1229 goto retry;
1231 xmlNanoHTTPFreeCtxt(ctxt);
1232 if (redirURL != NULL) xmlFree(redirURL);
1233 #ifdef DEBUG_HTTP
1234 xmlGenericError(xmlGenericErrorContext,
1235 "xmlNanoHTTPMethodRedir: Too many redirects, aborting ...\n");
1236 #endif
1237 return(NULL);
1240 if (contentType != NULL) {
1241 if (ctxt->contentType != NULL)
1242 *contentType = xmlMemStrdup(ctxt->contentType);
1243 else
1244 *contentType = NULL;
1247 if ((redir != NULL) && (redirURL != NULL)) {
1248 *redir = redirURL;
1249 } else {
1250 if (redirURL != NULL)
1251 xmlFree(redirURL);
1252 if (redir != NULL)
1253 *redir = NULL;
1256 #ifdef DEBUG_HTTP
1257 if (ctxt->contentType != NULL)
1258 xmlGenericError(xmlGenericErrorContext,
1259 "\nCode %d, content-type '%s'\n\n",
1260 ctxt->returnValue, ctxt->contentType);
1261 else
1262 xmlGenericError(xmlGenericErrorContext,
1263 "\nCode %d, no content-type\n\n",
1264 ctxt->returnValue);
1265 #endif
1267 return((void *) ctxt);
1271 * xmlNanoHTTPMethod:
1272 * @URL: The URL to load
1273 * @method: the HTTP method to use
1274 * @input: the input string if any
1275 * @contentType: the Content-Type information IN and OUT
1276 * @headers: the extra headers
1277 * @ilen: input length
1279 * This function try to open a connection to the indicated resource
1280 * via HTTP using the given @method, adding the given extra headers
1281 * and the input buffer for the request content.
1283 * Returns NULL in case of failure, otherwise a request handler.
1284 * The contentType, if provided must be freed by the caller
1287 void*
1288 xmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
1289 char **contentType, const char *headers, int ilen) {
1290 return(xmlNanoHTTPMethodRedir(URL, method, input, contentType,
1291 NULL, headers, ilen));
1295 * xmlNanoHTTPFetch:
1296 * @URL: The URL to load
1297 * @filename: the filename where the content should be saved
1298 * @contentType: if available the Content-Type information will be
1299 * returned at that location
1301 * This function try to fetch the indicated resource via HTTP GET
1302 * and save it's content in the file.
1304 * Returns -1 in case of failure, 0 incase of success. The contentType,
1305 * if provided must be freed by the caller
1308 xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
1309 void *ctxt = NULL;
1310 char *buf = NULL;
1311 int fd;
1312 int len;
1314 ctxt = xmlNanoHTTPOpen(URL, contentType);
1315 if (ctxt == NULL) return(-1);
1317 if (!strcmp(filename, "-"))
1318 fd = 0;
1319 else {
1320 fd = open(filename, O_CREAT | O_WRONLY, 00644);
1321 if (fd < 0) {
1322 xmlNanoHTTPClose(ctxt);
1323 if ((contentType != NULL) && (*contentType != NULL)) {
1324 xmlFree(*contentType);
1325 *contentType = NULL;
1327 return(-1);
1331 xmlNanoHTTPFetchContent( ctxt, &buf, &len );
1332 if ( len > 0 ) {
1333 write(fd, buf, len);
1336 xmlNanoHTTPClose(ctxt);
1337 close(fd);
1338 return(0);
1342 * xmlNanoHTTPSave:
1343 * @ctxt: the HTTP context
1344 * @filename: the filename where the content should be saved
1346 * This function saves the output of the HTTP transaction to a file
1347 * It closes and free the context at the end
1349 * Returns -1 in case of failure, 0 incase of success.
1352 xmlNanoHTTPSave(void *ctxt, const char *filename) {
1353 char *buf = NULL;
1354 int fd;
1355 int len;
1357 if (ctxt == NULL) return(-1);
1359 if (!strcmp(filename, "-"))
1360 fd = 0;
1361 else {
1362 fd = open(filename, O_CREAT | O_WRONLY);
1363 if (fd < 0) {
1364 xmlNanoHTTPClose(ctxt);
1365 return(-1);
1369 xmlNanoHTTPFetchContent( ctxt, &buf, &len );
1370 if ( len > 0 ) {
1371 write(fd, buf, len);
1374 xmlNanoHTTPClose(ctxt);
1375 return(0);
1379 * xmlNanoHTTPReturnCode:
1380 * @ctx: the HTTP context
1382 * Get the latest HTTP return code received
1384 * Returns the HTTP return code for the request.
1387 xmlNanoHTTPReturnCode(void *ctx) {
1388 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1390 if (ctxt == NULL) return(-1);
1392 return(ctxt->returnValue);
1396 * xmlNanoHTTPAuthHeader:
1397 * @ctx: the HTTP context
1399 * Get the authentication header of an HTTP context
1401 * Returns the stashed value of the WWW-Authenticate or Proxy-Authenticate
1402 * header.
1404 const char *
1405 xmlNanoHTTPAuthHeader(void *ctx) {
1406 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1408 if (ctxt == NULL) return(NULL);
1410 return(ctxt->authHeader);
1414 * xmlNanoHTTPContentLength
1415 * @ctx: the HTTP context
1417 * Return the specified content length from the HTTP header. Note that
1418 * a value of -1 indicates that the content length element was not included in
1419 * the response header.
1422 xmlNanoHTTPContentLength( void * ctx ) {
1423 xmlNanoHTTPCtxtPtr ctxt = ctx;
1425 return ( ( ctxt == NULL ) ? -1 : ctxt->ContentLength );
1429 * xmlNanoHTTPFetchContent
1430 * @ctx: the HTTP context
1431 * @ptr: pointer to set to the content buffer.
1432 * @len: integer pointer to hold the length of the content
1434 * Returns 0 if all the content was read and available, returns
1435 * -1 if received content length was less than specified or an error
1436 * occurred.
1439 xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len ) {
1440 xmlNanoHTTPCtxtPtr ctxt = ctx;
1442 int rc = 0;
1443 int cur_lgth;
1444 int rcvd_lgth;
1445 int dummy_int;
1446 char * dummy_ptr = NULL;
1448 /* Dummy up return input parameters if not provided */
1450 if ( len == NULL )
1451 len = &dummy_int;
1453 if ( ptr == NULL )
1454 ptr = &dummy_ptr;
1456 /* But can't work without the context pointer */
1458 if ( ( ctxt == NULL ) || ( ctxt->content == NULL ) ) {
1459 *len = 0;
1460 *ptr = NULL;
1461 return ( -1 );
1464 rcvd_lgth = ctxt->inptr - ctxt->content;
1466 while ( (cur_lgth = xmlNanoHTTPRecv( ctxt )) > 0 ) {
1468 rcvd_lgth += cur_lgth;
1469 if ( (ctxt->ContentLength > 0) && (rcvd_lgth >= ctxt->ContentLength) )
1470 break;
1473 *ptr = ctxt->content;
1474 *len = rcvd_lgth;
1476 if ( ( ctxt->ContentLength > 0 ) && ( rcvd_lgth < ctxt->ContentLength ) )
1477 rc = -1;
1478 else if ( rcvd_lgth == 0 )
1479 rc = -1;
1481 return ( rc );
1484 #ifdef STANDALONE
1485 int main(int argc, char **argv) {
1486 char *contentType = NULL;
1488 if (argv[1] != NULL) {
1489 if (argv[2] != NULL)
1490 xmlNanoHTTPFetch(argv[1], argv[2], &contentType);
1491 else
1492 xmlNanoHTTPFetch(argv[1], "-", &contentType);
1493 if (contentType != NULL) xmlFree(contentType);
1494 } else {
1495 xmlGenericError(xmlGenericErrorContext,
1496 "%s: minimal HTTP GET implementation\n", argv[0]);
1497 xmlGenericError(xmlGenericErrorContext,
1498 "\tusage %s [ URL [ filename ] ]\n", argv[0]);
1500 xmlNanoHTTPCleanup();
1501 xmlMemoryDump();
1502 return(0);
1504 #endif /* STANDALONE */
1505 #else /* !LIBXML_HTTP_ENABLED */
1506 #ifdef STANDALONE
1507 #include <stdio.h>
1508 int main(int argc, char **argv) {
1509 xmlGenericError(xmlGenericErrorContext,
1510 "%s : HTTP support not compiled in\n", argv[0]);
1511 return(0);
1513 #endif /* STANDALONE */
1514 #endif /* LIBXML_HTTP_ENABLED */