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.
14 /* TODO add compression support, Send the Accept- , and decompress on the
15 fly with ZLIB if found at compile-time */
21 #ifdef LIBXML_HTTP_ENABLED
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
33 #ifdef HAVE_SYS_SOCKET_H
34 #include <sys/socket.h>
36 #ifdef HAVE_NETINET_IN_H
37 #include <netinet/in.h>
39 #ifdef HAVE_ARPA_INET_H
40 #include <arpa/inet.h>
46 #ifdef HAVE_ARPA_NAMESER_H
47 #include <arpa/nameser.h>
57 #ifdef HAVE_SYS_TIME_H
60 #ifdef HAVE_SYS_SELECT_H
61 #include <sys/select.h>
72 #define SOCKLEN_T unsigned int
79 #include <wsockcompat.h>
82 #define SOCKLEN_T unsigned int
86 #include <libxml/globals.h>
87 #include <libxml/xmlerror.h>
88 #include <libxml/xmlmemory.h>
89 #include <libxml/parser.h> /* for xmlStr(n)casecmp() */
90 #include <libxml/nanohttp.h>
91 #include <libxml/globals.h>
92 #include <libxml/uri.h>
95 * A couple portability macros
98 #define closesocket(s) close(s)
103 #define SOCKLEN_T unsigned int
111 #define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)
112 #define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b)
115 #define XML_NANO_HTTP_MAX_REDIR 10
117 #define XML_NANO_HTTP_CHUNK 4096
119 #define XML_NANO_HTTP_CLOSED 0
120 #define XML_NANO_HTTP_WRITE 1
121 #define XML_NANO_HTTP_READ 2
122 #define XML_NANO_HTTP_NONE 4
124 typedef struct xmlNanoHTTPCtxt {
125 char *protocol; /* the protocol name */
126 char *hostname; /* the host name */
127 int port; /* the port */
128 char *path; /* the path within the URL */
129 SOCKET fd; /* the file descriptor for the socket */
130 int state; /* WRITE / READ / CLOSED */
131 char *out; /* buffer sent (zero terminated) */
132 char *outptr; /* index within the buffer sent */
133 char *in; /* the receiving buffer */
134 char *content; /* the start of the content */
135 char *inptr; /* the next byte to read from network */
136 char *inrptr; /* the next byte to give back to the client */
137 int inlen; /* len of the input buffer */
138 int last; /* return code for last operation */
139 int returnValue; /* the protocol return value */
140 int ContentLength; /* specified content length from HTTP header */
141 char *contentType; /* the MIME type for the input */
142 char *location; /* the new URL in case of redirect */
143 char *authHeader; /* contents of {WWW,Proxy}-Authenticate header */
144 } xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;
146 static int initialized = 0;
147 static char *proxy = NULL; /* the proxy name if any */
148 static int proxyPort; /* the proxy port if any */
149 static unsigned int timeout = 60;/* the select() timeout in seconds */
151 int xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len );
152 int xmlNanoHTTPContentLength( void * ctx );
155 * A portability function
157 static int socket_errno(void) {
159 return(WSAGetLastError());
167 int have_ipv6(void) {
170 s = socket (AF_INET6, SOCK_STREAM, 0);
182 * Initialize the HTTP protocol layer.
183 * Currently it just checks for proxy informations
187 xmlNanoHTTPInit(void) {
197 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
203 env = getenv("no_proxy");
206 env = getenv("http_proxy");
208 xmlNanoHTTPScanProxy(env);
211 env = getenv("HTTP_PROXY");
213 xmlNanoHTTPScanProxy(env);
222 * xmlNanoHTTPCleanup:
224 * Cleanup the HTTP protocol layer.
228 xmlNanoHTTPCleanup(void) {
240 * xmlNanoHTTPScanURL:
241 * @ctxt: an HTTP context
242 * @URL: The URL used to initialize the context
244 * (Re)Initialize an HTTP context by parsing the URL and finding
245 * the protocol host port and path it indicates.
249 xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) {
250 const char *cur = URL;
255 if (ctxt->protocol != NULL) {
256 xmlFree(ctxt->protocol);
257 ctxt->protocol = NULL;
259 if (ctxt->hostname != NULL) {
260 xmlFree(ctxt->hostname);
261 ctxt->hostname = NULL;
263 if (ctxt->path != NULL) {
267 if (URL == NULL) return;
270 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
272 ctxt->protocol = xmlMemStrdup(buf);
277 buf[indx++] = *cur++;
279 if (*cur == 0) return;
283 if ((strchr (cur, '[') && !strchr (cur, ']')) ||
284 (!strchr (cur, '[') && strchr (cur, ']'))) {
285 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoHTTPScanURL: %s",
292 while (cur[0] != ']')
293 buf[indx++] = *cur++;
295 if (!strchr (buf, ':')) {
296 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoHTTPScanURL: %s",
297 "Use [IPv6]/IPv4 format\n");
302 ctxt->hostname = xmlMemStrdup (buf);
307 while (*cur >= '0' && *cur <= '9') {
313 if (port != 0) ctxt->port = port;
314 while ((cur[0] != '/') && (*cur != 0))
322 ctxt->hostname = xmlMemStrdup (buf);
325 while ((*cur >= '0') && (*cur <= '9')) {
330 if (port != 0) ctxt->port = port;
331 while ((cur[0] != '/') && (*cur != 0))
335 if ((*cur == '/') || (*cur == 0)) {
337 ctxt->hostname = xmlMemStrdup (buf);
342 buf[indx++] = *cur++;
345 ctxt->path = xmlMemStrdup("/");
350 buf[indx++] = *cur++;
352 ctxt->path = xmlMemStrdup(buf);
357 * xmlNanoHTTPScanProxy:
358 * @URL: The proxy URL used to initialize the proxy context
360 * (Re)Initialize the HTTP Proxy context by parsing the URL and finding
361 * the protocol host port it indicates.
362 * Should be like http://myproxy/ or http://myproxy:3128/
363 * A NULL URL cleans up proxy informations.
367 xmlNanoHTTPScanProxy(const char *URL) {
368 const char *cur = URL;
377 if (proxyPort != 0) {
382 xmlGenericError(xmlGenericErrorContext,
383 "Removing HTTP proxy info\n");
385 xmlGenericError(xmlGenericErrorContext,
386 "Using HTTP proxy %s\n", URL);
388 if (URL == NULL) return;
391 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
397 buf[indx++] = *cur++;
399 if (*cur == 0) return;
403 if ((strchr (cur, '[') && !strchr (cur, ']')) ||
404 (!strchr (cur, '[') && strchr (cur, ']'))) {
405 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoHTTPScanProxy: %s",
412 while (cur[0] != ']')
413 buf[indx++] = *cur++;
415 if (!strchr (buf, ':')) {
416 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoHTTPScanProxy: %s",
417 "Use [IPv6]/IPv4 format\n");
422 proxy = xmlMemStrdup (buf);
427 while (*cur >= '0' && *cur <= '9') {
433 if (port != 0) proxyPort = port;
434 while ((cur[0] != '/') && (*cur != 0))
442 proxy = xmlMemStrdup (buf);
445 while ((*cur >= '0') && (*cur <= '9')) {
450 if (port != 0) proxyPort = port;
451 while ((cur[0] != '/') && (*cur != 0))
455 if ((*cur == '/') || (*cur == 0)) {
457 proxy = xmlMemStrdup (buf);
462 buf[indx++] = *cur++;
467 * xmlNanoHTTPNewCtxt:
468 * @URL: The URL used to initialize the context
470 * Allocate and initialize a new HTTP context.
472 * Returns an HTTP context or NULL in case of error.
475 static xmlNanoHTTPCtxtPtr
476 xmlNanoHTTPNewCtxt(const char *URL) {
477 xmlNanoHTTPCtxtPtr ret;
479 ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt));
480 if (ret == NULL) return(NULL);
482 memset(ret, 0, sizeof(xmlNanoHTTPCtxt));
484 ret->returnValue = 0;
486 ret->ContentLength = -1;
488 xmlNanoHTTPScanURL(ret, URL);
494 * xmlNanoHTTPFreeCtxt:
495 * @ctxt: an HTTP context
497 * Frees the context after closing the connection.
501 xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) {
502 if (ctxt == NULL) return;
503 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
504 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
505 if (ctxt->path != NULL) xmlFree(ctxt->path);
506 if (ctxt->out != NULL) xmlFree(ctxt->out);
507 if (ctxt->in != NULL) xmlFree(ctxt->in);
508 if (ctxt->contentType != NULL) xmlFree(ctxt->contentType);
509 if (ctxt->location != NULL) xmlFree(ctxt->location);
510 if (ctxt->authHeader != NULL) xmlFree(ctxt->authHeader);
511 ctxt->state = XML_NANO_HTTP_NONE;
512 if (ctxt->fd >= 0) closesocket(ctxt->fd);
519 * @ctxt: an HTTP context
521 * Send the input needed to initiate the processing on the server side
522 * Returns number of bytes sent or -1 on error.
526 xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt, const char * xmt_ptr, int outlen) {
530 if ( (ctxt->state & XML_NANO_HTTP_WRITE) && (xmt_ptr != NULL ) ) {
531 while (total_sent < outlen) {
532 int nsent = send(ctxt->fd, xmt_ptr + total_sent,
533 outlen - total_sent, 0);
536 else if ( ( nsent == -1 ) &&
537 #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
538 ( socket_errno( ) != EAGAIN ) &&
540 ( socket_errno( ) != EWOULDBLOCK ) ) {
541 xmlGenericError( xmlGenericErrorContext,
542 "xmlNanoHTTPSend error: %s",
543 strerror( socket_errno( ) ) );
545 if ( total_sent == 0 )
552 ** Since non-blocking sockets are used, wait for
553 ** socket to be writable or default timeout prior
563 FD_SET( ctxt->fd, &wfd );
564 (void)select( ctxt->fd + 1, NULL, &wfd, NULL, &tv );
574 * @ctxt: an HTTP context
576 * Read information coming from the HTTP connection.
577 * This is a blocking call (but it blocks in select(), not read()).
579 * Returns the number of byte read or -1 in case of error.
583 xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt) {
588 while (ctxt->state & XML_NANO_HTTP_READ) {
589 if (ctxt->in == NULL) {
590 ctxt->in = (char *) xmlMallocAtomic(65000 * sizeof(char));
591 if (ctxt->in == NULL) {
593 xmlGenericError( xmlGenericErrorContext,
594 "xmlNanoHTTPRecv: Error allocating input memory." );
598 ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in;
600 if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) {
601 int delta = ctxt->inrptr - ctxt->in;
602 int len = ctxt->inptr - ctxt->inrptr;
604 memmove(ctxt->in, ctxt->inrptr, len);
605 ctxt->inrptr -= delta;
606 ctxt->content -= delta;
607 ctxt->inptr -= delta;
609 if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) {
610 int d_inptr = ctxt->inptr - ctxt->in;
611 int d_content = ctxt->content - ctxt->in;
612 int d_inrptr = ctxt->inrptr - ctxt->in;
613 char * tmp_ptr = ctxt->in;
616 ctxt->in = (char *) xmlRealloc(tmp_ptr, ctxt->inlen);
617 if (ctxt->in == NULL) {
618 xmlGenericError( xmlGenericErrorContext,
619 "xmlNanoHTTPRecv: %s %d bytes.",
620 "Failed to realloc input buffer to",
626 ctxt->inptr = ctxt->in + d_inptr;
627 ctxt->content = ctxt->in + d_content;
628 ctxt->inrptr = ctxt->in + d_inrptr;
630 ctxt->last = recv(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK, 0);
631 if (ctxt->last > 0) {
632 ctxt->inptr += ctxt->last;
635 if (ctxt->last == 0) {
638 if (ctxt->last == -1) {
639 switch (socket_errno()) {
642 #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
652 xmlGenericError( xmlGenericErrorContext,
653 "xmlNanoHTTPRecv: recv( ) failure - %s",
654 strerror( socket_errno( ) ) );
662 FD_SET(ctxt->fd, &rfd);
664 if ( (select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1)
675 * xmlNanoHTTPReadLine:
676 * @ctxt: an HTTP context
678 * Read one line in the HTTP server output, usually for extracting
679 * the HTTP protocol informations from the answer header.
681 * Returns a newly allocated string with a copy of the line, or NULL
682 * which indicate the end of the input.
686 xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) {
691 while (bp - buf < 4095) {
692 if (ctxt->inrptr == ctxt->inptr) {
693 if ( (rc = xmlNanoHTTPRecv(ctxt)) == 0) {
698 return(xmlMemStrdup(buf));
700 else if ( rc == -1 ) {
704 *bp = *ctxt->inrptr++;
707 return(xmlMemStrdup(buf));
713 return(xmlMemStrdup(buf));
718 * xmlNanoHTTPScanAnswer:
719 * @ctxt: an HTTP context
720 * @line: an HTTP header line
722 * Try to extract useful informations from the server answer.
723 * We currently parse and process:
724 * - The HTTP revision/ return code
726 * - The Location for redirect processing.
728 * Returns -1 in case of failure, the file descriptor number otherwise
732 xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) {
733 const char *cur = line;
735 if (line == NULL) return;
737 if (!strncmp(line, "HTTP/", 5)) {
742 while ((*cur >= '0') && (*cur <= '9')) {
744 version += *cur - '0';
749 if ((*cur >= '0') && (*cur <= '9')) {
751 version += *cur - '0';
754 while ((*cur >= '0') && (*cur <= '9'))
758 if ((*cur != ' ') && (*cur != '\t')) return;
759 while ((*cur == ' ') || (*cur == '\t')) cur++;
760 if ((*cur < '0') || (*cur > '9')) return;
761 while ((*cur >= '0') && (*cur <= '9')) {
766 if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return;
767 ctxt->returnValue = ret;
768 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Type:", 13)) {
770 while ((*cur == ' ') || (*cur == '\t')) cur++;
771 if (ctxt->contentType != NULL)
772 xmlFree(ctxt->contentType);
773 ctxt->contentType = xmlMemStrdup(cur);
774 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"ContentType:", 12)) {
776 if (ctxt->contentType != NULL) return;
777 while ((*cur == ' ') || (*cur == '\t')) cur++;
778 ctxt->contentType = xmlMemStrdup(cur);
779 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Location:", 9)) {
781 while ((*cur == ' ') || (*cur == '\t')) cur++;
782 if (ctxt->location != NULL)
783 xmlFree(ctxt->location);
784 ctxt->location = xmlMemStrdup(cur);
785 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"WWW-Authenticate:", 17)) {
787 while ((*cur == ' ') || (*cur == '\t')) cur++;
788 if (ctxt->authHeader != NULL)
789 xmlFree(ctxt->authHeader);
790 ctxt->authHeader = xmlMemStrdup(cur);
791 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Proxy-Authenticate:", 19)) {
793 while ((*cur == ' ') || (*cur == '\t')) cur++;
794 if (ctxt->authHeader != NULL)
795 xmlFree(ctxt->authHeader);
796 ctxt->authHeader = xmlMemStrdup(cur);
797 } else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Length:", 15) ) {
799 ctxt->ContentLength = strtol( cur, NULL, 10 );
804 * xmlNanoHTTPConnectAttempt:
805 * @addr: a socket address structure
807 * Attempt a connection to the given IP:port endpoint. It forces
808 * non-blocking semantic on the socket, and allow 60 seconds for
809 * the host to answer.
811 * Returns -1 in case of failure, the file descriptor number otherwise
815 xmlNanoHTTPConnectAttempt(struct sockaddr *addr)
824 if (addr->sa_family == AF_INET6) {
825 s = socket (PF_INET6, SOCK_STREAM, IPPROTO_TCP);
826 addrlen = sizeof (struct sockaddr_in6);
831 s = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
832 addrlen = sizeof (struct sockaddr_in);
838 xmlGenericError( xmlGenericErrorContext,
839 "xmlNanoHTTPConnectAttempt: %s - %s",
840 "socket creation failure",
841 strerror( socket_errno( ) ) );
849 status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0;
851 #else /* _WINSOCKAPI_ */
855 status = ioctl(s, FIONBIO, &enable);
858 if ((status = fcntl(s, F_GETFL, 0)) != -1) {
860 status |= O_NONBLOCK;
861 #else /* O_NONBLOCK */
864 #endif /* F_NDELAY */
865 #endif /* !O_NONBLOCK */
866 status = fcntl(s, F_SETFL, status);
870 perror("nonblocking");
872 xmlGenericError( xmlGenericErrorContext,
873 "xmlNanoHTTPConnectAttempt: %s - %s",
874 "error setting non-blocking IO",
875 strerror( socket_errno( ) ) );
880 #endif /* !_WINSOCKAPI_ */
882 if (connect (s, addr, addrlen) == -1) {
883 switch (socket_errno()) {
888 xmlGenericError( xmlGenericErrorContext,
889 "xmlNanoHTTPConnectAttempt: %s - %s",
890 "error connecting to HTTP server",
891 strerror( socket_errno( ) ) );
903 switch(select(s+1, NULL, &wfd, NULL, &tv))
907 xmlGenericError( xmlGenericErrorContext,
908 "xmlNanoHTTPConnectAttempt: %s",
909 "Connect attempt timed out." );
914 xmlGenericError( xmlGenericErrorContext,
915 "xmlNanoHTTPConnectAttempt: %s - %s",
916 "Error connecting to host",
917 strerror( socket_errno( ) ) );
922 if ( FD_ISSET(s, &wfd) ) {
924 len = sizeof(status);
925 if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&status, &len) < 0 ) {
926 /* Solaris error code */
927 xmlGenericError( xmlGenericErrorContext,
928 "xmlNanoHTTPConnectAttempt: %s - %s",
929 "Error retrieving pending socket errors",
930 strerror( socket_errno( ) ) );
936 xmlGenericError( xmlGenericErrorContext,
937 "xmlNanoHTTPConnectAttempt: %s - %s",
938 "Error connecting to remote host",
939 strerror( status ) );
944 xmlGenericError( xmlGenericErrorContext,
945 "xmlNanoHTTPConnectAttempt: %s\n",
946 "Select returned, but descriptor not set for connection.\n" );
955 * xmlNanoHTTPConnectHost:
956 * @host: the host name
957 * @port: the port number
959 * Attempt a connection to the given host:port endpoint. It tries
960 * the multiple IP provided by the DNS if available.
962 * Returns -1 in case of failure, the file descriptor number otherwise
966 xmlNanoHTTPConnectHost(const char *host, int port)
969 struct sockaddr *addr = NULL;
971 struct sockaddr_in sockin;
975 struct sockaddr_in6 sockin6;
980 memset (&sockin, 0, sizeof(sockin));
982 memset (&sockin6, 0, sizeof(sockin6));
984 #if !defined(HAVE_GETADDRINFO) && defined(RES_USE_INET6)
986 if (!(_res.options & RES_INIT))
988 _res.options |= RES_USE_INET6;
990 #elif defined(HAVE_GETADDRINFO)
993 struct addrinfo hints, *res, *result;
996 memset (&hints, 0,sizeof(hints));
997 hints.ai_socktype = SOCK_STREAM;
999 status = getaddrinfo (host, NULL, &hints, &result);
1001 xmlGenericError (xmlGenericErrorContext,
1002 "xmlNanoHTTPConnectHost: %s '%s' - %s",
1003 "Failed to resolve host", host, gai_strerror (status));
1008 for (res = result; res; res = res->ai_next) {
1009 if (res->ai_family == AF_INET || res->ai_family == AF_INET6) {
1010 if (res->ai_family == AF_INET6) {
1011 memcpy (&sockin6, res->ai_addr, res->ai_addrlen);
1012 sockin6.sin6_port = htons (port);
1013 addr = (struct sockaddr *)&sockin6;
1016 memcpy (&sockin, res->ai_addr, res->ai_addrlen);
1017 sockin.sin_port = htons (port);
1018 addr = (struct sockaddr *)&sockin;
1021 s = xmlNanoHTTPConnectAttempt (addr);
1023 freeaddrinfo (result);
1029 freeaddrinfo (result);
1035 h = gethostbyname (host);
1039 * Okay, I got fed up by the non-portability of this error message
1040 * extraction code. it work on Linux, if it work on your platform
1041 * and one want to enable it, send me the defined(foobar) needed
1043 #if defined(HAVE_NETDB_H) && defined(HOST_NOT_FOUND) && defined(linux)
1044 const char *h_err_txt = "";
1047 case HOST_NOT_FOUND:
1048 h_err_txt = "Authoritive host not found";
1053 "Non-authoritive host not found or server failure.";
1058 "Non-recoverable errors: FORMERR, REFUSED, or NOTIMP.";
1063 "Valid name, no data record of requested type.";
1067 h_err_txt = "No error text defined.";
1070 xmlGenericError (xmlGenericErrorContext,
1071 "xmlNanoHTTPConnectHost: %s '%s' - %s",
1072 "Failed to resolve host", host, h_err_txt);
1074 xmlGenericError (xmlGenericErrorContext,
1075 "xmlNanoHTTPConnectHost: %s '%s'",
1076 "Failed to resolve host", host);
1081 for (i = 0; h->h_addr_list[i]; i++) {
1082 if (h->h_addrtype == AF_INET) {
1083 /* A records (IPv4) */
1084 memcpy (&ia, h->h_addr_list[i], h->h_length);
1085 sockin.sin_family = h->h_addrtype;
1086 sockin.sin_addr = ia;
1087 sockin.sin_port = htons (port);
1088 addr = (struct sockaddr *) &sockin;
1090 } else if (have_ipv6 () && (h->h_addrtype == AF_INET6)) {
1091 /* AAAA records (IPv6) */
1092 memcpy (&ia6, h->h_addr_list[i], h->h_length);
1093 sockin6.sin6_family = h->h_addrtype;
1094 sockin6.sin6_addr = ia6;
1095 sockin6.sin6_port = htons (port);
1096 addr = (struct sockaddr *) &sockin6;
1101 s = xmlNanoHTTPConnectAttempt (addr);
1107 xmlGenericError(xmlGenericErrorContext,
1108 "xmlNanoHTTPConnectHost: unable to connect to '%s'.\n",
1117 * @URL: The URL to load
1118 * @contentType: if available the Content-Type information will be
1119 * returned at that location
1121 * This function try to open a connection to the indicated resource
1124 * Returns NULL in case of failure, otherwise a request handler.
1125 * The contentType, if provided must be freed by the caller
1129 xmlNanoHTTPOpen(const char *URL, char **contentType) {
1130 if (contentType != NULL) *contentType = NULL;
1131 return(xmlNanoHTTPMethod(URL, NULL, NULL, contentType, NULL, 0));
1135 * xmlNanoHTTPOpenRedir:
1136 * @URL: The URL to load
1137 * @contentType: if available the Content-Type information will be
1138 * returned at that location
1139 * @redir: if available the redirected URL will be returned
1141 * This function try to open a connection to the indicated resource
1144 * Returns NULL in case of failure, otherwise a request handler.
1145 * The contentType, if provided must be freed by the caller
1149 xmlNanoHTTPOpenRedir(const char *URL, char **contentType, char **redir) {
1150 if (contentType != NULL) *contentType = NULL;
1151 if (redir != NULL) *redir = NULL;
1152 return(xmlNanoHTTPMethodRedir(URL, NULL, NULL, contentType, redir, NULL,0));
1157 * @ctx: the HTTP context
1159 * @len: the buffer length
1161 * This function tries to read @len bytes from the existing HTTP connection
1162 * and saves them in @dest. This is a blocking call.
1164 * Returns the number of byte read. 0 is an indication of an end of connection.
1165 * -1 indicates a parameter error.
1168 xmlNanoHTTPRead(void *ctx, void *dest, int len) {
1169 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1171 if (ctx == NULL) return(-1);
1172 if (dest == NULL) return(-1);
1173 if (len <= 0) return(0);
1175 while (ctxt->inptr - ctxt->inrptr < len) {
1176 if (xmlNanoHTTPRecv(ctxt) <= 0) break;
1178 if (ctxt->inptr - ctxt->inrptr < len)
1179 len = ctxt->inptr - ctxt->inrptr;
1180 memcpy(dest, ctxt->inrptr, len);
1181 ctxt->inrptr += len;
1187 * @ctx: the HTTP context
1189 * This function closes an HTTP context, it ends up the connection and
1190 * free all data related to it.
1193 xmlNanoHTTPClose(void *ctx) {
1194 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1196 if (ctx == NULL) return;
1198 xmlNanoHTTPFreeCtxt(ctxt);
1202 * xmlNanoHTTPMethodRedir:
1203 * @URL: The URL to load
1204 * @method: the HTTP method to use
1205 * @input: the input string if any
1206 * @contentType: the Content-Type information IN and OUT
1207 * @redir: the redirected URL OUT
1208 * @headers: the extra headers
1209 * @ilen: input length
1211 * This function try to open a connection to the indicated resource
1212 * via HTTP using the given @method, adding the given extra headers
1213 * and the input buffer for the request content.
1215 * Returns NULL in case of failure, otherwise a request handler.
1216 * The contentType, or redir, if provided must be freed by the caller
1220 xmlNanoHTTPMethodRedir(const char *URL, const char *method, const char *input,
1221 char **contentType, char **redir,
1222 const char *headers, int ilen ) {
1223 xmlNanoHTTPCtxtPtr ctxt;
1227 int nbRedirects = 0;
1228 char *redirURL = NULL;
1233 if (URL == NULL) return(NULL);
1234 if (method == NULL) method = "GET";
1238 if (redirURL == NULL)
1239 ctxt = xmlNanoHTTPNewCtxt(URL);
1241 ctxt = xmlNanoHTTPNewCtxt(redirURL);
1244 if ( ctxt == NULL ) {
1245 xmlGenericError( xmlGenericErrorContext,
1246 "xmlNanoHTTPMethodRedir: %s %s.",
1247 "Unable to allocate HTTP context to URI",
1248 ( ( redirURL == NULL ) ? URL : redirURL ) );
1252 if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
1253 xmlGenericError( xmlGenericErrorContext,
1254 "xmlNanoHTTPMethodRedir: %s - %s.",
1255 "Not a valid HTTP URI",
1256 ( ( redirURL == NULL ) ? URL : redirURL ) );
1257 xmlNanoHTTPFreeCtxt(ctxt);
1258 if (redirURL != NULL) xmlFree(redirURL);
1261 if (ctxt->hostname == NULL) {
1262 xmlGenericError( xmlGenericErrorContext,
1263 "xmlNanoHTTPMethodRedir: %s - %s",
1264 "Failed to identify host in URI",
1265 ( ( redirURL == NULL ) ? URL : redirURL ) );
1266 xmlNanoHTTPFreeCtxt(ctxt);
1267 if (redirURL != NULL) xmlFree(redirURL);
1271 blen = strlen(ctxt->hostname) * 2 + 16;
1272 ret = xmlNanoHTTPConnectHost(proxy, proxyPort);
1275 blen = strlen(ctxt->hostname);
1276 ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
1279 xmlNanoHTTPFreeCtxt(ctxt);
1280 if (redirURL != NULL) xmlFree(redirURL);
1290 if (headers != NULL)
1291 blen += strlen(headers) + 2;
1292 if (contentType && *contentType)
1293 blen += strlen(*contentType) + 16;
1294 blen += strlen(method) + strlen(ctxt->path) + 24;
1295 bp = xmlMallocAtomic(blen);
1297 xmlNanoHTTPFreeCtxt( ctxt );
1298 xmlGenericError( xmlGenericErrorContext,
1299 "xmlNanoHTTPMethodRedir: %s",
1300 "Error allocating HTTP header buffer." );
1307 if (ctxt->port != 80) {
1308 p += snprintf( p, blen - (p - bp), "%s http://%s:%d%s",
1309 method, ctxt->hostname,
1310 ctxt->port, ctxt->path );
1313 p += snprintf( p, blen - (p - bp), "%s http://%s%s", method,
1314 ctxt->hostname, ctxt->path);
1317 p += snprintf( p, blen - (p - bp), "%s %s", method, ctxt->path);
1319 p += snprintf( p, blen - (p - bp), " HTTP/1.0\r\nHost: %s\r\n",
1322 if (contentType != NULL && *contentType)
1323 p += snprintf(p, blen - (p - bp), "Content-Type: %s\r\n", *contentType);
1325 if (headers != NULL)
1326 p += snprintf( p, blen - (p - bp), "%s", headers );
1329 snprintf(p, blen - (p - bp), "Content-Length: %d\r\n\r\n", ilen );
1331 snprintf(p, blen - (p - bp), "\r\n");
1334 xmlGenericError(xmlGenericErrorContext,
1335 "-> %s%s", proxy? "(Proxy) " : "", bp);
1336 if ((blen -= strlen(bp)+1) < 0)
1337 xmlGenericError(xmlGenericErrorContext,
1338 "ERROR: overflowed buffer by %d bytes\n", -blen);
1340 ctxt->outptr = ctxt->out = bp;
1341 ctxt->state = XML_NANO_HTTP_WRITE;
1342 blen = strlen( ctxt->out );
1344 xmt_bytes = xmlNanoHTTPSend(ctxt, ctxt->out, blen );
1345 if ( xmt_bytes != blen )
1346 xmlGenericError( xmlGenericErrorContext,
1347 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1349 "bytes of HTTP headers sent to host",
1352 xmlNanoHTTPSend(ctxt, ctxt->out, blen );
1355 if ( input != NULL ) {
1357 xmt_bytes = xmlNanoHTTPSend( ctxt, input, ilen );
1359 if ( xmt_bytes != ilen )
1360 xmlGenericError( xmlGenericErrorContext,
1361 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1363 "bytes of HTTP content sent to host",
1366 xmlNanoHTTPSend( ctxt, input, ilen );
1370 ctxt->state = XML_NANO_HTTP_READ;
1373 while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
1374 if (head && (*p == 0)) {
1376 ctxt->content = ctxt->inrptr;
1380 xmlNanoHTTPScanAnswer(ctxt, p);
1383 xmlGenericError(xmlGenericErrorContext, "<- %s\n", p);
1388 if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
1389 (ctxt->returnValue < 400)) {
1391 xmlGenericError(xmlGenericErrorContext,
1392 "\nRedirect to: %s\n", ctxt->location);
1394 while ( xmlNanoHTTPRecv(ctxt) > 0 ) ;
1395 if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
1397 if (redirURL != NULL)
1399 redirURL = xmlMemStrdup(ctxt->location);
1400 xmlNanoHTTPFreeCtxt(ctxt);
1403 xmlNanoHTTPFreeCtxt(ctxt);
1404 if (redirURL != NULL) xmlFree(redirURL);
1406 xmlGenericError(xmlGenericErrorContext,
1407 "xmlNanoHTTPMethodRedir: Too many redirects, aborting ...\n");
1412 if (contentType != NULL) {
1413 if (ctxt->contentType != NULL)
1414 *contentType = xmlMemStrdup(ctxt->contentType);
1416 *contentType = NULL;
1419 if ((redir != NULL) && (redirURL != NULL)) {
1422 if (redirURL != NULL)
1429 if (ctxt->contentType != NULL)
1430 xmlGenericError(xmlGenericErrorContext,
1431 "\nCode %d, content-type '%s'\n\n",
1432 ctxt->returnValue, ctxt->contentType);
1434 xmlGenericError(xmlGenericErrorContext,
1435 "\nCode %d, no content-type\n\n",
1439 return((void *) ctxt);
1443 * xmlNanoHTTPMethod:
1444 * @URL: The URL to load
1445 * @method: the HTTP method to use
1446 * @input: the input string if any
1447 * @contentType: the Content-Type information IN and OUT
1448 * @headers: the extra headers
1449 * @ilen: input length
1451 * This function try to open a connection to the indicated resource
1452 * via HTTP using the given @method, adding the given extra headers
1453 * and the input buffer for the request content.
1455 * Returns NULL in case of failure, otherwise a request handler.
1456 * The contentType, if provided must be freed by the caller
1460 xmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
1461 char **contentType, const char *headers, int ilen) {
1462 return(xmlNanoHTTPMethodRedir(URL, method, input, contentType,
1463 NULL, headers, ilen));
1468 * @URL: The URL to load
1469 * @filename: the filename where the content should be saved
1470 * @contentType: if available the Content-Type information will be
1471 * returned at that location
1473 * This function try to fetch the indicated resource via HTTP GET
1474 * and save it's content in the file.
1476 * Returns -1 in case of failure, 0 incase of success. The contentType,
1477 * if provided must be freed by the caller
1480 xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
1486 ctxt = xmlNanoHTTPOpen(URL, contentType);
1487 if (ctxt == NULL) return(-1);
1489 if (!strcmp(filename, "-"))
1492 fd = open(filename, O_CREAT | O_WRONLY, 00644);
1494 xmlNanoHTTPClose(ctxt);
1495 if ((contentType != NULL) && (*contentType != NULL)) {
1496 xmlFree(*contentType);
1497 *contentType = NULL;
1503 xmlNanoHTTPFetchContent( ctxt, &buf, &len );
1505 write(fd, buf, len);
1508 xmlNanoHTTPClose(ctxt);
1515 * @ctxt: the HTTP context
1516 * @filename: the filename where the content should be saved
1518 * This function saves the output of the HTTP transaction to a file
1519 * It closes and free the context at the end
1521 * Returns -1 in case of failure, 0 incase of success.
1524 xmlNanoHTTPSave(void *ctxt, const char *filename) {
1529 if (ctxt == NULL) return(-1);
1531 if (!strcmp(filename, "-"))
1534 fd = open(filename, O_CREAT | O_WRONLY);
1536 xmlNanoHTTPClose(ctxt);
1541 xmlNanoHTTPFetchContent( ctxt, &buf, &len );
1543 write(fd, buf, len);
1546 xmlNanoHTTPClose(ctxt);
1551 * xmlNanoHTTPReturnCode:
1552 * @ctx: the HTTP context
1554 * Get the latest HTTP return code received
1556 * Returns the HTTP return code for the request.
1559 xmlNanoHTTPReturnCode(void *ctx) {
1560 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1562 if (ctxt == NULL) return(-1);
1564 return(ctxt->returnValue);
1568 * xmlNanoHTTPAuthHeader:
1569 * @ctx: the HTTP context
1571 * Get the authentication header of an HTTP context
1573 * Returns the stashed value of the WWW-Authenticate or Proxy-Authenticate
1577 xmlNanoHTTPAuthHeader(void *ctx) {
1578 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1580 if (ctxt == NULL) return(NULL);
1582 return(ctxt->authHeader);
1586 * xmlNanoHTTPContentLength:
1587 * @ctx: the HTTP context
1589 * Provides the specified content length from the HTTP header.
1591 * Return the specified content length from the HTTP header. Note that
1592 * a value of -1 indicates that the content length element was not included in
1593 * the response header.
1596 xmlNanoHTTPContentLength( void * ctx ) {
1597 xmlNanoHTTPCtxtPtr ctxt = ctx;
1599 return ( ( ctxt == NULL ) ? -1 : ctxt->ContentLength );
1603 * xmlNanoHTTPFetchContent:
1604 * @ctx: the HTTP context
1605 * @ptr: pointer to set to the content buffer.
1606 * @len: integer pointer to hold the length of the content
1608 * Check if all the content was read
1610 * Returns 0 if all the content was read and available, returns
1611 * -1 if received content length was less than specified or an error
1615 xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len ) {
1616 xmlNanoHTTPCtxtPtr ctxt = ctx;
1622 char * dummy_ptr = NULL;
1624 /* Dummy up return input parameters if not provided */
1632 /* But can't work without the context pointer */
1634 if ( ( ctxt == NULL ) || ( ctxt->content == NULL ) ) {
1640 rcvd_lgth = ctxt->inptr - ctxt->content;
1642 while ( (cur_lgth = xmlNanoHTTPRecv( ctxt )) > 0 ) {
1644 rcvd_lgth += cur_lgth;
1645 if ( (ctxt->ContentLength > 0) && (rcvd_lgth >= ctxt->ContentLength) )
1649 *ptr = ctxt->content;
1652 if ( ( ctxt->ContentLength > 0 ) && ( rcvd_lgth < ctxt->ContentLength ) )
1654 else if ( rcvd_lgth == 0 )
1661 int main(int argc, char **argv) {
1662 char *contentType = NULL;
1664 if (argv[1] != NULL) {
1665 if (argv[2] != NULL)
1666 xmlNanoHTTPFetch(argv[1], argv[2], &contentType);
1668 xmlNanoHTTPFetch(argv[1], "-", &contentType);
1669 if (contentType != NULL) xmlFree(contentType);
1671 xmlGenericError(xmlGenericErrorContext,
1672 "%s: minimal HTTP GET implementation\n", argv[0]);
1673 xmlGenericError(xmlGenericErrorContext,
1674 "\tusage %s [ URL [ filename ] ]\n", argv[0]);
1676 xmlNanoHTTPCleanup();
1680 #endif /* STANDALONE */
1681 #else /* !LIBXML_HTTP_ENABLED */
1684 int main(int argc, char **argv) {
1685 xmlGenericError(xmlGenericErrorContext,
1686 "%s : HTTP support not compiled in\n", argv[0]);
1689 #endif /* STANDALONE */
1690 #endif /* LIBXML_HTTP_ENABLED */