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.
18 #ifdef LIBXML_HTTP_ENABLED
27 #ifdef HAVE_SYS_TYPES_H
28 #include <sys/types.h>
30 #ifdef HAVE_SYS_SOCKET_H
31 #include <sys/socket.h>
33 #ifdef HAVE_NETINET_IN_H
34 #include <netinet/in.h>
36 #ifdef HAVE_ARPA_INET_H
37 #include <arpa/inet.h>
43 #ifdef HAVE_ARPA_NAMESER_H
44 #include <arpa/nameser.h>
54 #ifdef HAVE_SYS_TIME_H
57 #ifdef HAVE_SYS_SELECT_H
58 #include <sys/select.h>
73 #define XML_SOCKLEN_T unsigned int
77 #if defined(__MINGW32__) || defined(_WIN32_WCE)
81 #include <wsockcompat.h>
84 #define XML_SOCKLEN_T int
88 #include <libxml/globals.h>
89 #include <libxml/xmlerror.h>
90 #include <libxml/xmlmemory.h>
91 #include <libxml/parser.h> /* for xmlStr(n)casecmp() */
92 #include <libxml/nanohttp.h>
93 #include <libxml/globals.h>
94 #include <libxml/uri.h>
97 * A couple portability macros
100 #if !defined(__BEOS__) || defined(__HAIKU__)
101 #define closesocket(s) close(s)
108 #define PF_INET AF_INET
112 #ifndef XML_SOCKLEN_T
113 #define XML_SOCKLEN_T unsigned int
121 #define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)
122 #define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b)
125 #define XML_NANO_HTTP_MAX_REDIR 10
127 #define XML_NANO_HTTP_CHUNK 4096
129 #define XML_NANO_HTTP_CLOSED 0
130 #define XML_NANO_HTTP_WRITE 1
131 #define XML_NANO_HTTP_READ 2
132 #define XML_NANO_HTTP_NONE 4
134 typedef struct xmlNanoHTTPCtxt
{
135 char *protocol
; /* the protocol name */
136 char *hostname
; /* the host name */
137 int port
; /* the port */
138 char *path
; /* the path within the URL */
139 char *query
; /* the query string */
140 SOCKET fd
; /* the file descriptor for the socket */
141 int state
; /* WRITE / READ / CLOSED */
142 char *out
; /* buffer sent (zero terminated) */
143 char *outptr
; /* index within the buffer sent */
144 char *in
; /* the receiving buffer */
145 char *content
; /* the start of the content */
146 char *inptr
; /* the next byte to read from network */
147 char *inrptr
; /* the next byte to give back to the client */
148 int inlen
; /* len of the input buffer */
149 int last
; /* return code for last operation */
150 int returnValue
; /* the protocol return value */
151 int ContentLength
; /* specified content length from HTTP header */
152 char *contentType
; /* the MIME type for the input */
153 char *location
; /* the new URL in case of redirect */
154 char *authHeader
; /* contents of {WWW,Proxy}-Authenticate header */
155 char *encoding
; /* encoding extracted from the contentType */
156 char *mimeType
; /* Mime-Type extracted from the contentType */
158 z_stream
*strm
; /* Zlib stream object */
159 int usesGzip
; /* "Content-Encoding: gzip" was detected */
161 } xmlNanoHTTPCtxt
, *xmlNanoHTTPCtxtPtr
;
163 static int initialized
= 0;
164 static char *proxy
= NULL
; /* the proxy name if any */
165 static int proxyPort
; /* the proxy port if any */
166 static unsigned int timeout
= 60;/* the select() timeout in seconds */
168 static int xmlNanoHTTPFetchContent( void * ctx
, char ** ptr
, int * len
);
172 * @extra: extra informations
174 * Handle an out of memory condition
177 xmlHTTPErrMemory(const char *extra
)
179 __xmlSimpleError(XML_FROM_HTTP
, XML_ERR_NO_MEMORY
, NULL
, NULL
, extra
);
183 * A portability function
185 static int socket_errno(void) {
187 return(WSAGetLastError());
195 int have_ipv6(void) {
198 s
= socket (AF_INET6
, SOCK_STREAM
, 0);
210 * Initialize the HTTP protocol layer.
211 * Currently it just checks for proxy informations
215 xmlNanoHTTPInit(void) {
225 if (WSAStartup(MAKEWORD(1, 1), &wsaData
) != 0)
231 env
= getenv("no_proxy");
232 if (env
&& ((env
[0] == '*') && (env
[1] == 0)))
234 env
= getenv("http_proxy");
236 xmlNanoHTTPScanProxy(env
);
239 env
= getenv("HTTP_PROXY");
241 xmlNanoHTTPScanProxy(env
);
250 * xmlNanoHTTPCleanup:
252 * Cleanup the HTTP protocol layer.
256 xmlNanoHTTPCleanup(void) {
270 * xmlNanoHTTPScanURL:
271 * @ctxt: an HTTP context
272 * @URL: The URL used to initialize the context
274 * (Re)Initialize an HTTP context by parsing the URL and finding
275 * the protocol host port and path it indicates.
279 xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt
, const char *URL
) {
282 * Clear any existing data from the context
284 if (ctxt
->protocol
!= NULL
) {
285 xmlFree(ctxt
->protocol
);
286 ctxt
->protocol
= NULL
;
288 if (ctxt
->hostname
!= NULL
) {
289 xmlFree(ctxt
->hostname
);
290 ctxt
->hostname
= NULL
;
292 if (ctxt
->path
!= NULL
) {
296 if (ctxt
->query
!= NULL
) {
297 xmlFree(ctxt
->query
);
300 if (URL
== NULL
) return;
302 uri
= xmlParseURIRaw(URL
, 1);
306 if ((uri
->scheme
== NULL
) || (uri
->server
== NULL
)) {
311 ctxt
->protocol
= xmlMemStrdup(uri
->scheme
);
312 ctxt
->hostname
= xmlMemStrdup(uri
->server
);
313 if (uri
->path
!= NULL
)
314 ctxt
->path
= xmlMemStrdup(uri
->path
);
316 ctxt
->path
= xmlMemStrdup("/");
317 if (uri
->query
!= NULL
)
318 ctxt
->query
= xmlMemStrdup(uri
->query
);
320 ctxt
->port
= uri
->port
;
326 * xmlNanoHTTPScanProxy:
327 * @URL: The proxy URL used to initialize the proxy context
329 * (Re)Initialize the HTTP Proxy context by parsing the URL and finding
330 * the protocol host port it indicates.
331 * Should be like http://myproxy/ or http://myproxy:3128/
332 * A NULL URL cleans up proxy informations.
336 xmlNanoHTTPScanProxy(const char *URL
) {
347 xmlGenericError(xmlGenericErrorContext
,
348 "Removing HTTP proxy info\n");
350 xmlGenericError(xmlGenericErrorContext
,
351 "Using HTTP proxy %s\n", URL
);
353 if (URL
== NULL
) return;
355 uri
= xmlParseURIRaw(URL
, 1);
356 if ((uri
== NULL
) || (uri
->scheme
== NULL
) ||
357 (strcmp(uri
->scheme
, "http")) || (uri
->server
== NULL
)) {
358 __xmlIOErr(XML_FROM_HTTP
, XML_HTTP_URL_SYNTAX
, "Syntax Error\n");
364 proxy
= xmlMemStrdup(uri
->server
);
366 proxyPort
= uri
->port
;
372 * xmlNanoHTTPNewCtxt:
373 * @URL: The URL used to initialize the context
375 * Allocate and initialize a new HTTP context.
377 * Returns an HTTP context or NULL in case of error.
380 static xmlNanoHTTPCtxtPtr
381 xmlNanoHTTPNewCtxt(const char *URL
) {
382 xmlNanoHTTPCtxtPtr ret
;
384 ret
= (xmlNanoHTTPCtxtPtr
) xmlMalloc(sizeof(xmlNanoHTTPCtxt
));
386 xmlHTTPErrMemory("allocating context");
390 memset(ret
, 0, sizeof(xmlNanoHTTPCtxt
));
392 ret
->returnValue
= 0;
394 ret
->ContentLength
= -1;
396 xmlNanoHTTPScanURL(ret
, URL
);
402 * xmlNanoHTTPFreeCtxt:
403 * @ctxt: an HTTP context
405 * Frees the context after closing the connection.
409 xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt
) {
410 if (ctxt
== NULL
) return;
411 if (ctxt
->hostname
!= NULL
) xmlFree(ctxt
->hostname
);
412 if (ctxt
->protocol
!= NULL
) xmlFree(ctxt
->protocol
);
413 if (ctxt
->path
!= NULL
) xmlFree(ctxt
->path
);
414 if (ctxt
->query
!= NULL
) xmlFree(ctxt
->query
);
415 if (ctxt
->out
!= NULL
) xmlFree(ctxt
->out
);
416 if (ctxt
->in
!= NULL
) xmlFree(ctxt
->in
);
417 if (ctxt
->contentType
!= NULL
) xmlFree(ctxt
->contentType
);
418 if (ctxt
->encoding
!= NULL
) xmlFree(ctxt
->encoding
);
419 if (ctxt
->mimeType
!= NULL
) xmlFree(ctxt
->mimeType
);
420 if (ctxt
->location
!= NULL
) xmlFree(ctxt
->location
);
421 if (ctxt
->authHeader
!= NULL
) xmlFree(ctxt
->authHeader
);
423 if (ctxt
->strm
!= NULL
) {
424 inflateEnd(ctxt
->strm
);
429 ctxt
->state
= XML_NANO_HTTP_NONE
;
430 if (ctxt
->fd
>= 0) closesocket(ctxt
->fd
);
437 * @ctxt: an HTTP context
439 * Send the input needed to initiate the processing on the server side
440 * Returns number of bytes sent or -1 on error.
444 xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt
, const char * xmt_ptr
, int outlen
) {
448 if ( (ctxt
->state
& XML_NANO_HTTP_WRITE
) && (xmt_ptr
!= NULL
) ) {
449 while (total_sent
< outlen
) {
450 int nsent
= send(ctxt
->fd
, xmt_ptr
+ total_sent
,
451 outlen
- total_sent
, 0);
454 else if ( ( nsent
== -1 ) &&
455 #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
456 ( socket_errno( ) != EAGAIN
) &&
458 ( socket_errno( ) != EWOULDBLOCK
) ) {
459 __xmlIOErr(XML_FROM_HTTP
, 0, "send failed\n");
460 if ( total_sent
== 0 )
467 ** Since non-blocking sockets are used, wait for
468 ** socket to be writable or default timeout prior
479 #pragma warning(push)
480 #pragma warning(disable: 4018)
482 FD_SET( ctxt
->fd
, &wfd
);
486 (void)select( ctxt
->fd
+ 1, NULL
, &wfd
, NULL
, &tv
);
496 * @ctxt: an HTTP context
498 * Read information coming from the HTTP connection.
499 * This is a blocking call (but it blocks in select(), not read()).
501 * Returns the number of byte read or -1 in case of error.
505 xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt
) {
510 while (ctxt
->state
& XML_NANO_HTTP_READ
) {
511 if (ctxt
->in
== NULL
) {
512 ctxt
->in
= (char *) xmlMallocAtomic(65000 * sizeof(char));
513 if (ctxt
->in
== NULL
) {
514 xmlHTTPErrMemory("allocating input");
519 ctxt
->inptr
= ctxt
->content
= ctxt
->inrptr
= ctxt
->in
;
521 if (ctxt
->inrptr
> ctxt
->in
+ XML_NANO_HTTP_CHUNK
) {
522 int delta
= ctxt
->inrptr
- ctxt
->in
;
523 int len
= ctxt
->inptr
- ctxt
->inrptr
;
525 memmove(ctxt
->in
, ctxt
->inrptr
, len
);
526 ctxt
->inrptr
-= delta
;
527 ctxt
->content
-= delta
;
528 ctxt
->inptr
-= delta
;
530 if ((ctxt
->in
+ ctxt
->inlen
) < (ctxt
->inptr
+ XML_NANO_HTTP_CHUNK
)) {
531 int d_inptr
= ctxt
->inptr
- ctxt
->in
;
532 int d_content
= ctxt
->content
- ctxt
->in
;
533 int d_inrptr
= ctxt
->inrptr
- ctxt
->in
;
534 char * tmp_ptr
= ctxt
->in
;
537 ctxt
->in
= (char *) xmlRealloc(tmp_ptr
, ctxt
->inlen
);
538 if (ctxt
->in
== NULL
) {
539 xmlHTTPErrMemory("allocating input buffer");
544 ctxt
->inptr
= ctxt
->in
+ d_inptr
;
545 ctxt
->content
= ctxt
->in
+ d_content
;
546 ctxt
->inrptr
= ctxt
->in
+ d_inrptr
;
548 ctxt
->last
= recv(ctxt
->fd
, ctxt
->inptr
, XML_NANO_HTTP_CHUNK
, 0);
549 if (ctxt
->last
> 0) {
550 ctxt
->inptr
+= ctxt
->last
;
553 if (ctxt
->last
== 0) {
556 if (ctxt
->last
== -1) {
557 switch (socket_errno()) {
560 #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
570 __xmlIOErr(XML_FROM_HTTP
, 0, "recv failed\n");
579 #pragma warning(push)
580 #pragma warning(disable: 4018)
582 FD_SET(ctxt
->fd
, &rfd
);
587 if ( (select(ctxt
->fd
+1, &rfd
, NULL
, NULL
, &tv
)<1)
598 * xmlNanoHTTPReadLine:
599 * @ctxt: an HTTP context
601 * Read one line in the HTTP server output, usually for extracting
602 * the HTTP protocol informations from the answer header.
604 * Returns a newly allocated string with a copy of the line, or NULL
605 * which indicate the end of the input.
609 xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt
) {
614 while (bp
- buf
< 4095) {
615 if (ctxt
->inrptr
== ctxt
->inptr
) {
616 if ( (rc
= xmlNanoHTTPRecv(ctxt
)) == 0) {
621 return(xmlMemStrdup(buf
));
623 else if ( rc
== -1 ) {
627 *bp
= *ctxt
->inrptr
++;
630 return(xmlMemStrdup(buf
));
636 return(xmlMemStrdup(buf
));
641 * xmlNanoHTTPScanAnswer:
642 * @ctxt: an HTTP context
643 * @line: an HTTP header line
645 * Try to extract useful informations from the server answer.
646 * We currently parse and process:
647 * - The HTTP revision/ return code
648 * - The Content-Type, Mime-Type and charset used
649 * - The Location for redirect processing.
651 * Returns -1 in case of failure, the file descriptor number otherwise
655 xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt
, const char *line
) {
656 const char *cur
= line
;
658 if (line
== NULL
) return;
660 if (!strncmp(line
, "HTTP/", 5)) {
665 while ((*cur
>= '0') && (*cur
<= '9')) {
667 version
+= *cur
- '0';
672 if ((*cur
>= '0') && (*cur
<= '9')) {
674 version
+= *cur
- '0';
677 while ((*cur
>= '0') && (*cur
<= '9'))
681 if ((*cur
!= ' ') && (*cur
!= '\t')) return;
682 while ((*cur
== ' ') || (*cur
== '\t')) cur
++;
683 if ((*cur
< '0') || (*cur
> '9')) return;
684 while ((*cur
>= '0') && (*cur
<= '9')) {
689 if ((*cur
!= 0) && (*cur
!= ' ') && (*cur
!= '\t')) return;
690 ctxt
->returnValue
= ret
;
691 } else if (!xmlStrncasecmp(BAD_CAST line
, BAD_CAST
"Content-Type:", 13)) {
692 const xmlChar
*charset
, *last
, *mime
;
694 while ((*cur
== ' ') || (*cur
== '\t')) cur
++;
695 if (ctxt
->contentType
!= NULL
)
696 xmlFree(ctxt
->contentType
);
697 ctxt
->contentType
= xmlMemStrdup(cur
);
698 mime
= (const xmlChar
*) cur
;
700 while ((*last
!= 0) && (*last
!= ' ') && (*last
!= '\t') &&
701 (*last
!= ';') && (*last
!= ','))
703 if (ctxt
->mimeType
!= NULL
)
704 xmlFree(ctxt
->mimeType
);
705 ctxt
->mimeType
= (char *) xmlStrndup(mime
, last
- mime
);
706 charset
= xmlStrstr(BAD_CAST ctxt
->contentType
, BAD_CAST
"charset=");
707 if (charset
!= NULL
) {
710 while ((*last
!= 0) && (*last
!= ' ') && (*last
!= '\t') &&
711 (*last
!= ';') && (*last
!= ','))
713 if (ctxt
->encoding
!= NULL
)
714 xmlFree(ctxt
->encoding
);
715 ctxt
->encoding
= (char *) xmlStrndup(charset
, last
- charset
);
717 } else if (!xmlStrncasecmp(BAD_CAST line
, BAD_CAST
"ContentType:", 12)) {
718 const xmlChar
*charset
, *last
, *mime
;
720 if (ctxt
->contentType
!= NULL
) return;
721 while ((*cur
== ' ') || (*cur
== '\t')) cur
++;
722 ctxt
->contentType
= xmlMemStrdup(cur
);
723 mime
= (const xmlChar
*) cur
;
725 while ((*last
!= 0) && (*last
!= ' ') && (*last
!= '\t') &&
726 (*last
!= ';') && (*last
!= ','))
728 if (ctxt
->mimeType
!= NULL
)
729 xmlFree(ctxt
->mimeType
);
730 ctxt
->mimeType
= (char *) xmlStrndup(mime
, last
- mime
);
731 charset
= xmlStrstr(BAD_CAST ctxt
->contentType
, BAD_CAST
"charset=");
732 if (charset
!= NULL
) {
735 while ((*last
!= 0) && (*last
!= ' ') && (*last
!= '\t') &&
736 (*last
!= ';') && (*last
!= ','))
738 if (ctxt
->encoding
!= NULL
)
739 xmlFree(ctxt
->encoding
);
740 ctxt
->encoding
= (char *) xmlStrndup(charset
, last
- charset
);
742 } else if (!xmlStrncasecmp(BAD_CAST line
, BAD_CAST
"Location:", 9)) {
744 while ((*cur
== ' ') || (*cur
== '\t')) cur
++;
745 if (ctxt
->location
!= NULL
)
746 xmlFree(ctxt
->location
);
748 xmlChar
*tmp_http
= xmlStrdup(BAD_CAST
"http://");
750 xmlStrcat(tmp_http
, (const xmlChar
*) ctxt
->hostname
);
752 (char *) xmlStrcat (tmp_loc
, (const xmlChar
*) cur
);
754 ctxt
->location
= xmlMemStrdup(cur
);
756 } else if (!xmlStrncasecmp(BAD_CAST line
, BAD_CAST
"WWW-Authenticate:", 17)) {
758 while ((*cur
== ' ') || (*cur
== '\t')) cur
++;
759 if (ctxt
->authHeader
!= NULL
)
760 xmlFree(ctxt
->authHeader
);
761 ctxt
->authHeader
= xmlMemStrdup(cur
);
762 } else if (!xmlStrncasecmp(BAD_CAST line
, BAD_CAST
"Proxy-Authenticate:", 19)) {
764 while ((*cur
== ' ') || (*cur
== '\t')) cur
++;
765 if (ctxt
->authHeader
!= NULL
)
766 xmlFree(ctxt
->authHeader
);
767 ctxt
->authHeader
= xmlMemStrdup(cur
);
769 } else if ( !xmlStrncasecmp( BAD_CAST line
, BAD_CAST
"Content-Encoding:", 17) ) {
771 while ((*cur
== ' ') || (*cur
== '\t')) cur
++;
772 if ( !xmlStrncasecmp( BAD_CAST cur
, BAD_CAST
"gzip", 4) ) {
775 ctxt
->strm
= xmlMalloc(sizeof(z_stream
));
777 if (ctxt
->strm
!= NULL
) {
778 ctxt
->strm
->zalloc
= Z_NULL
;
779 ctxt
->strm
->zfree
= Z_NULL
;
780 ctxt
->strm
->opaque
= Z_NULL
;
781 ctxt
->strm
->avail_in
= 0;
782 ctxt
->strm
->next_in
= Z_NULL
;
784 inflateInit2( ctxt
->strm
, 31 );
788 } else if ( !xmlStrncasecmp( BAD_CAST line
, BAD_CAST
"Content-Length:", 15) ) {
790 ctxt
->ContentLength
= strtol( cur
, NULL
, 10 );
795 * xmlNanoHTTPConnectAttempt:
796 * @addr: a socket address structure
798 * Attempt a connection to the given IP:port endpoint. It forces
799 * non-blocking semantic on the socket, and allow 60 seconds for
800 * the host to answer.
802 * Returns -1 in case of failure, the file descriptor number otherwise
806 xmlNanoHTTPConnectAttempt(struct sockaddr
*addr
)
818 if (addr
->sa_family
== AF_INET6
) {
819 s
= socket (PF_INET6
, SOCK_STREAM
, IPPROTO_TCP
);
820 addrlen
= sizeof (struct sockaddr_in6
);
825 s
= socket (PF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
826 addrlen
= sizeof (struct sockaddr_in
);
832 __xmlIOErr(XML_FROM_HTTP
, 0, "socket failed\n");
840 status
= ioctlsocket(s
, FIONBIO
, &one
) == SOCKET_ERROR
? -1 : 0;
842 #else /* _WINSOCKAPI_ */
846 status
= ioctl(s
, FIONBIO
, &enable
);
849 #if defined(__BEOS__) && !defined(__HAIKU__)
852 status
= setsockopt(s
, SOL_SOCKET
, SO_NONBLOCK
, &noblock
, sizeof(noblock
));
855 if ((status
= fcntl(s
, F_GETFL
, 0)) != -1) {
857 status
|= O_NONBLOCK
;
858 #else /* O_NONBLOCK */
861 #endif /* F_NDELAY */
862 #endif /* !O_NONBLOCK */
863 status
= fcntl(s
, F_SETFL
, status
);
867 perror("nonblocking");
869 __xmlIOErr(XML_FROM_HTTP
, 0, "error setting non-blocking IO\n");
873 #endif /* !__BEOS__ */
875 #endif /* !_WINSOCKAPI_ */
877 if (connect (s
, addr
, addrlen
) == -1) {
878 switch (socket_errno()) {
883 __xmlIOErr(XML_FROM_HTTP
, 0, "error connecting to HTTP server");
893 #pragma warning(push)
894 #pragma warning(disable: 4018)
903 switch(select(s
+1, NULL
, &wfd
, &xfd
, &tv
))
905 switch(select(s
+1, NULL
, &wfd
, NULL
, &tv
))
913 __xmlIOErr(XML_FROM_HTTP
, 0, "Connect attempt timed out");
918 __xmlIOErr(XML_FROM_HTTP
, 0, "Connect failed");
923 if ( FD_ISSET(s
, &wfd
)
929 len
= sizeof(status
);
931 if (getsockopt(s
, SOL_SOCKET
, SO_ERROR
, (char*)&status
, &len
) < 0 ) {
932 /* Solaris error code */
933 __xmlIOErr(XML_FROM_HTTP
, 0, "getsockopt failed\n");
938 __xmlIOErr(XML_FROM_HTTP
, 0, "Error connecting to remote host");
945 __xmlIOErr(XML_FROM_HTTP
, 0, "select failed\n");
954 * xmlNanoHTTPConnectHost:
955 * @host: the host name
956 * @port: the port number
958 * Attempt a connection to the given host:port endpoint. It tries
959 * the multiple IP provided by the DNS if available.
961 * Returns -1 in case of failure, the file descriptor number otherwise
965 xmlNanoHTTPConnectHost(const char *host
, int port
)
968 struct sockaddr
*addr
= NULL
;
970 struct sockaddr_in sockin
;
974 struct sockaddr_in6 sockin6
;
979 memset (&sockin
, 0, sizeof(sockin
));
981 memset (&sockin6
, 0, sizeof(sockin6
));
984 #if !defined(HAVE_GETADDRINFO) && defined(SUPPORT_IP6) && defined(RES_USE_INET6)
987 if (!(_res
.options
& RES_INIT
))
989 _res
.options
|= RES_USE_INET6
;
993 #if defined(HAVE_GETADDRINFO) && defined(SUPPORT_IP6) && !defined(_WIN32)
996 #if defined(HAVE_GETADDRINFO) && (defined(SUPPORT_IP6) || defined(_WIN32))
999 struct addrinfo hints
, *res
, *result
;
1002 memset (&hints
, 0,sizeof(hints
));
1003 hints
.ai_socktype
= SOCK_STREAM
;
1005 status
= getaddrinfo (host
, NULL
, &hints
, &result
);
1007 __xmlIOErr(XML_FROM_HTTP
, 0, "getaddrinfo failed\n");
1011 for (res
= result
; res
; res
= res
->ai_next
) {
1012 if (res
->ai_family
== AF_INET
) {
1013 if (res
->ai_addrlen
> sizeof(sockin
)) {
1014 __xmlIOErr(XML_FROM_HTTP
, 0, "address size mismatch\n");
1015 freeaddrinfo (result
);
1018 memcpy (&sockin
, res
->ai_addr
, res
->ai_addrlen
);
1019 sockin
.sin_port
= htons (port
);
1020 addr
= (struct sockaddr
*)&sockin
;
1022 } else if (have_ipv6 () && (res
->ai_family
== AF_INET6
)) {
1023 if (res
->ai_addrlen
> sizeof(sockin6
)) {
1024 __xmlIOErr(XML_FROM_HTTP
, 0, "address size mismatch\n");
1025 freeaddrinfo (result
);
1028 memcpy (&sockin6
, res
->ai_addr
, res
->ai_addrlen
);
1029 sockin6
.sin6_port
= htons (port
);
1030 addr
= (struct sockaddr
*)&sockin6
;
1035 s
= xmlNanoHTTPConnectAttempt (addr
);
1037 freeaddrinfo (result
);
1043 freeaddrinfo (result
);
1046 #if defined(HAVE_GETADDRINFO) && defined(SUPPORT_IP6) && !defined(_WIN32)
1049 #if !defined(HAVE_GETADDRINFO) || !defined(_WIN32)
1051 h
= gethostbyname (host
);
1055 * Okay, I got fed up by the non-portability of this error message
1056 * extraction code. it work on Linux, if it work on your platform
1057 * and one want to enable it, send me the defined(foobar) needed
1059 #if defined(HAVE_NETDB_H) && defined(HOST_NOT_FOUND) && defined(linux)
1060 const char *h_err_txt
= "";
1063 case HOST_NOT_FOUND
:
1064 h_err_txt
= "Authoritive host not found";
1069 "Non-authoritive host not found or server failure.";
1074 "Non-recoverable errors: FORMERR, REFUSED, or NOTIMP.";
1079 "Valid name, no data record of requested type.";
1083 h_err_txt
= "No error text defined.";
1086 __xmlIOErr(XML_FROM_HTTP
, 0, h_err_txt
);
1088 __xmlIOErr(XML_FROM_HTTP
, 0, "Failed to resolve host");
1093 for (i
= 0; h
->h_addr_list
[i
]; i
++) {
1094 if (h
->h_addrtype
== AF_INET
) {
1095 /* A records (IPv4) */
1096 if ((unsigned int) h
->h_length
> sizeof(ia
)) {
1097 __xmlIOErr(XML_FROM_HTTP
, 0, "address size mismatch\n");
1100 memcpy (&ia
, h
->h_addr_list
[i
], h
->h_length
);
1101 sockin
.sin_family
= h
->h_addrtype
;
1102 sockin
.sin_addr
= ia
;
1103 sockin
.sin_port
= (u_short
)htons ((unsigned short)port
);
1104 addr
= (struct sockaddr
*) &sockin
;
1106 } else if (have_ipv6 () && (h
->h_addrtype
== AF_INET6
)) {
1107 /* AAAA records (IPv6) */
1108 if ((unsigned int) h
->h_length
> sizeof(ia6
)) {
1109 __xmlIOErr(XML_FROM_HTTP
, 0, "address size mismatch\n");
1112 memcpy (&ia6
, h
->h_addr_list
[i
], h
->h_length
);
1113 sockin6
.sin6_family
= h
->h_addrtype
;
1114 sockin6
.sin6_addr
= ia6
;
1115 sockin6
.sin6_port
= htons (port
);
1116 addr
= (struct sockaddr
*) &sockin6
;
1121 s
= xmlNanoHTTPConnectAttempt (addr
);
1129 xmlGenericError(xmlGenericErrorContext
,
1130 "xmlNanoHTTPConnectHost: unable to connect to '%s'.\n",
1139 * @URL: The URL to load
1140 * @contentType: if available the Content-Type information will be
1141 * returned at that location
1143 * This function try to open a connection to the indicated resource
1146 * Returns NULL in case of failure, otherwise a request handler.
1147 * The contentType, if provided must be freed by the caller
1151 xmlNanoHTTPOpen(const char *URL
, char **contentType
) {
1152 if (contentType
!= NULL
) *contentType
= NULL
;
1153 return(xmlNanoHTTPMethod(URL
, NULL
, NULL
, contentType
, NULL
, 0));
1157 * xmlNanoHTTPOpenRedir:
1158 * @URL: The URL to load
1159 * @contentType: if available the Content-Type information will be
1160 * returned at that location
1161 * @redir: if available the redirected URL will be returned
1163 * This function try to open a connection to the indicated resource
1166 * Returns NULL in case of failure, otherwise a request handler.
1167 * The contentType, if provided must be freed by the caller
1171 xmlNanoHTTPOpenRedir(const char *URL
, char **contentType
, char **redir
) {
1172 if (contentType
!= NULL
) *contentType
= NULL
;
1173 if (redir
!= NULL
) *redir
= NULL
;
1174 return(xmlNanoHTTPMethodRedir(URL
, NULL
, NULL
, contentType
, redir
, NULL
,0));
1179 * @ctx: the HTTP context
1181 * @len: the buffer length
1183 * This function tries to read @len bytes from the existing HTTP connection
1184 * and saves them in @dest. This is a blocking call.
1186 * Returns the number of byte read. 0 is an indication of an end of connection.
1187 * -1 indicates a parameter error.
1190 xmlNanoHTTPRead(void *ctx
, void *dest
, int len
) {
1191 xmlNanoHTTPCtxtPtr ctxt
= (xmlNanoHTTPCtxtPtr
) ctx
;
1198 if (ctx
== NULL
) return(-1);
1199 if (dest
== NULL
) return(-1);
1200 if (len
<= 0) return(0);
1203 if (ctxt
->usesGzip
== 1) {
1204 if (ctxt
->strm
== NULL
) return(0);
1206 ctxt
->strm
->next_out
= dest
;
1207 ctxt
->strm
->avail_out
= len
;
1208 ctxt
->strm
->avail_in
= ctxt
->inptr
- ctxt
->inrptr
;
1210 while (ctxt
->strm
->avail_out
> 0 &&
1211 (ctxt
->strm
->avail_in
> 0 || xmlNanoHTTPRecv(ctxt
) > 0)) {
1212 orig_avail_in
= ctxt
->strm
->avail_in
=
1213 ctxt
->inptr
- ctxt
->inrptr
- bytes_read
;
1214 ctxt
->strm
->next_in
= BAD_CAST (ctxt
->inrptr
+ bytes_read
);
1216 z_ret
= inflate(ctxt
->strm
, Z_NO_FLUSH
);
1217 bytes_read
+= orig_avail_in
- ctxt
->strm
->avail_in
;
1219 if (z_ret
!= Z_OK
) break;
1222 ctxt
->inrptr
+= bytes_read
;
1223 return(len
- ctxt
->strm
->avail_out
);
1227 while (ctxt
->inptr
- ctxt
->inrptr
< len
) {
1228 if (xmlNanoHTTPRecv(ctxt
) <= 0) break;
1230 if (ctxt
->inptr
- ctxt
->inrptr
< len
)
1231 len
= ctxt
->inptr
- ctxt
->inrptr
;
1232 memcpy(dest
, ctxt
->inrptr
, len
);
1233 ctxt
->inrptr
+= len
;
1239 * @ctx: the HTTP context
1241 * This function closes an HTTP context, it ends up the connection and
1242 * free all data related to it.
1245 xmlNanoHTTPClose(void *ctx
) {
1246 xmlNanoHTTPCtxtPtr ctxt
= (xmlNanoHTTPCtxtPtr
) ctx
;
1248 if (ctx
== NULL
) return;
1250 xmlNanoHTTPFreeCtxt(ctxt
);
1254 * xmlNanoHTTPMethodRedir:
1255 * @URL: The URL to load
1256 * @method: the HTTP method to use
1257 * @input: the input string if any
1258 * @contentType: the Content-Type information IN and OUT
1259 * @redir: the redirected URL OUT
1260 * @headers: the extra headers
1261 * @ilen: input length
1263 * This function try to open a connection to the indicated resource
1264 * via HTTP using the given @method, adding the given extra headers
1265 * and the input buffer for the request content.
1267 * Returns NULL in case of failure, otherwise a request handler.
1268 * The contentType, or redir, if provided must be freed by the caller
1272 xmlNanoHTTPMethodRedir(const char *URL
, const char *method
, const char *input
,
1273 char **contentType
, char **redir
,
1274 const char *headers
, int ilen
) {
1275 xmlNanoHTTPCtxtPtr ctxt
;
1279 int nbRedirects
= 0;
1280 char *redirURL
= NULL
;
1285 if (URL
== NULL
) return(NULL
);
1286 if (method
== NULL
) method
= "GET";
1290 if (redirURL
== NULL
)
1291 ctxt
= xmlNanoHTTPNewCtxt(URL
);
1293 ctxt
= xmlNanoHTTPNewCtxt(redirURL
);
1294 ctxt
->location
= xmlMemStrdup(redirURL
);
1297 if ( ctxt
== NULL
) {
1301 if ((ctxt
->protocol
== NULL
) || (strcmp(ctxt
->protocol
, "http"))) {
1302 __xmlIOErr(XML_FROM_HTTP
, XML_HTTP_URL_SYNTAX
, "Not a valid HTTP URI");
1303 xmlNanoHTTPFreeCtxt(ctxt
);
1304 if (redirURL
!= NULL
) xmlFree(redirURL
);
1307 if (ctxt
->hostname
== NULL
) {
1308 __xmlIOErr(XML_FROM_HTTP
, XML_HTTP_UNKNOWN_HOST
,
1309 "Failed to identify host in URI");
1310 xmlNanoHTTPFreeCtxt(ctxt
);
1311 if (redirURL
!= NULL
) xmlFree(redirURL
);
1315 blen
= strlen(ctxt
->hostname
) * 2 + 16;
1316 ret
= xmlNanoHTTPConnectHost(proxy
, proxyPort
);
1319 blen
= strlen(ctxt
->hostname
);
1320 ret
= xmlNanoHTTPConnectHost(ctxt
->hostname
, ctxt
->port
);
1323 xmlNanoHTTPFreeCtxt(ctxt
);
1324 if (redirURL
!= NULL
) xmlFree(redirURL
);
1334 if (headers
!= NULL
)
1335 blen
+= strlen(headers
) + 2;
1336 if (contentType
&& *contentType
)
1337 /* reserve for string plus 'Content-Type: \r\n" */
1338 blen
+= strlen(*contentType
) + 16;
1339 if (ctxt
->query
!= NULL
)
1341 blen
+= strlen(ctxt
->query
) + 1;
1342 blen
+= strlen(method
) + strlen(ctxt
->path
) + 24;
1344 /* reserve for possible 'Accept-Encoding: gzip' string */
1347 if (ctxt
->port
!= 80) {
1348 /* reserve space for ':xxxxx', incl. potential proxy */
1354 bp
= (char*)xmlMallocAtomic(blen
);
1356 xmlNanoHTTPFreeCtxt( ctxt
);
1357 xmlHTTPErrMemory("allocating header buffer");
1364 if (ctxt
->port
!= 80) {
1365 p
+= snprintf( p
, blen
- (p
- bp
), "%s http://%s:%d%s",
1366 method
, ctxt
->hostname
,
1367 ctxt
->port
, ctxt
->path
);
1370 p
+= snprintf( p
, blen
- (p
- bp
), "%s http://%s%s", method
,
1371 ctxt
->hostname
, ctxt
->path
);
1374 p
+= snprintf( p
, blen
- (p
- bp
), "%s %s", method
, ctxt
->path
);
1376 if (ctxt
->query
!= NULL
)
1377 p
+= snprintf( p
, blen
- (p
- bp
), "?%s", ctxt
->query
);
1379 if (ctxt
->port
== 80) {
1380 p
+= snprintf( p
, blen
- (p
- bp
), " HTTP/1.0\r\nHost: %s\r\n",
1383 p
+= snprintf( p
, blen
- (p
- bp
), " HTTP/1.0\r\nHost: %s:%d\r\n",
1384 ctxt
->hostname
, ctxt
->port
);
1388 p
+= snprintf(p
, blen
- (p
- bp
), "Accept-Encoding: gzip\r\n");
1391 if (contentType
!= NULL
&& *contentType
)
1392 p
+= snprintf(p
, blen
- (p
- bp
), "Content-Type: %s\r\n", *contentType
);
1394 if (headers
!= NULL
)
1395 p
+= snprintf( p
, blen
- (p
- bp
), "%s", headers
);
1398 snprintf(p
, blen
- (p
- bp
), "Content-Length: %d\r\n\r\n", ilen
);
1400 snprintf(p
, blen
- (p
- bp
), "\r\n");
1403 xmlGenericError(xmlGenericErrorContext
,
1404 "-> %s%s", proxy
? "(Proxy) " : "", bp
);
1405 if ((blen
-= strlen(bp
)+1) < 0)
1406 xmlGenericError(xmlGenericErrorContext
,
1407 "ERROR: overflowed buffer by %d bytes\n", -blen
);
1409 ctxt
->outptr
= ctxt
->out
= bp
;
1410 ctxt
->state
= XML_NANO_HTTP_WRITE
;
1411 blen
= strlen( ctxt
->out
);
1413 xmt_bytes
= xmlNanoHTTPSend(ctxt
, ctxt
->out
, blen
);
1414 if ( xmt_bytes
!= blen
)
1415 xmlGenericError( xmlGenericErrorContext
,
1416 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1418 "bytes of HTTP headers sent to host",
1421 xmlNanoHTTPSend(ctxt
, ctxt
->out
, blen
);
1424 if ( input
!= NULL
) {
1426 xmt_bytes
= xmlNanoHTTPSend( ctxt
, input
, ilen
);
1428 if ( xmt_bytes
!= ilen
)
1429 xmlGenericError( xmlGenericErrorContext
,
1430 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1432 "bytes of HTTP content sent to host",
1435 xmlNanoHTTPSend( ctxt
, input
, ilen
);
1439 ctxt
->state
= XML_NANO_HTTP_READ
;
1442 while ((p
= xmlNanoHTTPReadLine(ctxt
)) != NULL
) {
1443 if (head
&& (*p
== 0)) {
1445 ctxt
->content
= ctxt
->inrptr
;
1449 xmlNanoHTTPScanAnswer(ctxt
, p
);
1452 xmlGenericError(xmlGenericErrorContext
, "<- %s\n", p
);
1457 if ((ctxt
->location
!= NULL
) && (ctxt
->returnValue
>= 300) &&
1458 (ctxt
->returnValue
< 400)) {
1460 xmlGenericError(xmlGenericErrorContext
,
1461 "\nRedirect to: %s\n", ctxt
->location
);
1463 while ( xmlNanoHTTPRecv(ctxt
) > 0 ) ;
1464 if (nbRedirects
< XML_NANO_HTTP_MAX_REDIR
) {
1466 if (redirURL
!= NULL
)
1468 redirURL
= xmlMemStrdup(ctxt
->location
);
1469 xmlNanoHTTPFreeCtxt(ctxt
);
1472 xmlNanoHTTPFreeCtxt(ctxt
);
1473 if (redirURL
!= NULL
) xmlFree(redirURL
);
1475 xmlGenericError(xmlGenericErrorContext
,
1476 "xmlNanoHTTPMethodRedir: Too many redirects, aborting ...\n");
1481 if (contentType
!= NULL
) {
1482 if (ctxt
->contentType
!= NULL
)
1483 *contentType
= xmlMemStrdup(ctxt
->contentType
);
1485 *contentType
= NULL
;
1488 if ((redir
!= NULL
) && (redirURL
!= NULL
)) {
1491 if (redirURL
!= NULL
)
1498 if (ctxt
->contentType
!= NULL
)
1499 xmlGenericError(xmlGenericErrorContext
,
1500 "\nCode %d, content-type '%s'\n\n",
1501 ctxt
->returnValue
, ctxt
->contentType
);
1503 xmlGenericError(xmlGenericErrorContext
,
1504 "\nCode %d, no content-type\n\n",
1508 return((void *) ctxt
);
1512 * xmlNanoHTTPMethod:
1513 * @URL: The URL to load
1514 * @method: the HTTP method to use
1515 * @input: the input string if any
1516 * @contentType: the Content-Type information IN and OUT
1517 * @headers: the extra headers
1518 * @ilen: input length
1520 * This function try to open a connection to the indicated resource
1521 * via HTTP using the given @method, adding the given extra headers
1522 * and the input buffer for the request content.
1524 * Returns NULL in case of failure, otherwise a request handler.
1525 * The contentType, if provided must be freed by the caller
1529 xmlNanoHTTPMethod(const char *URL
, const char *method
, const char *input
,
1530 char **contentType
, const char *headers
, int ilen
) {
1531 return(xmlNanoHTTPMethodRedir(URL
, method
, input
, contentType
,
1532 NULL
, headers
, ilen
));
1537 * @URL: The URL to load
1538 * @filename: the filename where the content should be saved
1539 * @contentType: if available the Content-Type information will be
1540 * returned at that location
1542 * This function try to fetch the indicated resource via HTTP GET
1543 * and save it's content in the file.
1545 * Returns -1 in case of failure, 0 incase of success. The contentType,
1546 * if provided must be freed by the caller
1549 xmlNanoHTTPFetch(const char *URL
, const char *filename
, char **contentType
) {
1555 if (filename
== NULL
) return(-1);
1556 ctxt
= xmlNanoHTTPOpen(URL
, contentType
);
1557 if (ctxt
== NULL
) return(-1);
1559 if (!strcmp(filename
, "-"))
1562 fd
= open(filename
, O_CREAT
| O_WRONLY
, 00644);
1564 xmlNanoHTTPClose(ctxt
);
1565 if ((contentType
!= NULL
) && (*contentType
!= NULL
)) {
1566 xmlFree(*contentType
);
1567 *contentType
= NULL
;
1573 xmlNanoHTTPFetchContent( ctxt
, &buf
, &len
);
1575 write(fd
, buf
, len
);
1578 xmlNanoHTTPClose(ctxt
);
1583 #ifdef LIBXML_OUTPUT_ENABLED
1586 * @ctxt: the HTTP context
1587 * @filename: the filename where the content should be saved
1589 * This function saves the output of the HTTP transaction to a file
1590 * It closes and free the context at the end
1592 * Returns -1 in case of failure, 0 incase of success.
1595 xmlNanoHTTPSave(void *ctxt
, const char *filename
) {
1600 if ((ctxt
== NULL
) || (filename
== NULL
)) return(-1);
1602 if (!strcmp(filename
, "-"))
1605 fd
= open(filename
, O_CREAT
| O_WRONLY
, 0666);
1607 xmlNanoHTTPClose(ctxt
);
1612 xmlNanoHTTPFetchContent( ctxt
, &buf
, &len
);
1614 write(fd
, buf
, len
);
1617 xmlNanoHTTPClose(ctxt
);
1621 #endif /* LIBXML_OUTPUT_ENABLED */
1624 * xmlNanoHTTPReturnCode:
1625 * @ctx: the HTTP context
1627 * Get the latest HTTP return code received
1629 * Returns the HTTP return code for the request.
1632 xmlNanoHTTPReturnCode(void *ctx
) {
1633 xmlNanoHTTPCtxtPtr ctxt
= (xmlNanoHTTPCtxtPtr
) ctx
;
1635 if (ctxt
== NULL
) return(-1);
1637 return(ctxt
->returnValue
);
1641 * xmlNanoHTTPAuthHeader:
1642 * @ctx: the HTTP context
1644 * Get the authentication header of an HTTP context
1646 * Returns the stashed value of the WWW-Authenticate or Proxy-Authenticate
1650 xmlNanoHTTPAuthHeader(void *ctx
) {
1651 xmlNanoHTTPCtxtPtr ctxt
= (xmlNanoHTTPCtxtPtr
) ctx
;
1653 if (ctxt
== NULL
) return(NULL
);
1655 return(ctxt
->authHeader
);
1659 * xmlNanoHTTPContentLength:
1660 * @ctx: the HTTP context
1662 * Provides the specified content length from the HTTP header.
1664 * Return the specified content length from the HTTP header. Note that
1665 * a value of -1 indicates that the content length element was not included in
1666 * the response header.
1669 xmlNanoHTTPContentLength( void * ctx
) {
1670 xmlNanoHTTPCtxtPtr ctxt
= (xmlNanoHTTPCtxtPtr
)ctx
;
1672 return ( ( ctxt
== NULL
) ? -1 : ctxt
->ContentLength
);
1677 * @ctx: the HTTP context
1679 * Provides the specified redirection URL if available from the HTTP header.
1681 * Return the specified redirection URL or NULL if not redirected.
1684 xmlNanoHTTPRedir( void * ctx
) {
1685 xmlNanoHTTPCtxtPtr ctxt
= (xmlNanoHTTPCtxtPtr
)ctx
;
1687 return ( ( ctxt
== NULL
) ? NULL
: ctxt
->location
);
1691 * xmlNanoHTTPEncoding:
1692 * @ctx: the HTTP context
1694 * Provides the specified encoding if specified in the HTTP headers.
1696 * Return the specified encoding or NULL if not available
1699 xmlNanoHTTPEncoding( void * ctx
) {
1700 xmlNanoHTTPCtxtPtr ctxt
= (xmlNanoHTTPCtxtPtr
)ctx
;
1702 return ( ( ctxt
== NULL
) ? NULL
: ctxt
->encoding
);
1706 * xmlNanoHTTPMimeType:
1707 * @ctx: the HTTP context
1709 * Provides the specified Mime-Type if specified in the HTTP headers.
1711 * Return the specified Mime-Type or NULL if not available
1714 xmlNanoHTTPMimeType( void * ctx
) {
1715 xmlNanoHTTPCtxtPtr ctxt
= (xmlNanoHTTPCtxtPtr
)ctx
;
1717 return ( ( ctxt
== NULL
) ? NULL
: ctxt
->mimeType
);
1721 * xmlNanoHTTPFetchContent:
1722 * @ctx: the HTTP context
1723 * @ptr: pointer to set to the content buffer.
1724 * @len: integer pointer to hold the length of the content
1726 * Check if all the content was read
1728 * Returns 0 if all the content was read and available, returns
1729 * -1 if received content length was less than specified or an error
1733 xmlNanoHTTPFetchContent( void * ctx
, char ** ptr
, int * len
) {
1734 xmlNanoHTTPCtxtPtr ctxt
= (xmlNanoHTTPCtxtPtr
)ctx
;
1740 char * dummy_ptr
= NULL
;
1742 /* Dummy up return input parameters if not provided */
1750 /* But can't work without the context pointer */
1752 if ( ( ctxt
== NULL
) || ( ctxt
->content
== NULL
) ) {
1758 rcvd_lgth
= ctxt
->inptr
- ctxt
->content
;
1760 while ( (cur_lgth
= xmlNanoHTTPRecv( ctxt
)) > 0 ) {
1762 rcvd_lgth
+= cur_lgth
;
1763 if ( (ctxt
->ContentLength
> 0) && (rcvd_lgth
>= ctxt
->ContentLength
) )
1767 *ptr
= ctxt
->content
;
1770 if ( ( ctxt
->ContentLength
> 0 ) && ( rcvd_lgth
< ctxt
->ContentLength
) )
1772 else if ( rcvd_lgth
== 0 )
1779 int main(int argc
, char **argv
) {
1780 char *contentType
= NULL
;
1782 if (argv
[1] != NULL
) {
1783 if (argv
[2] != NULL
)
1784 xmlNanoHTTPFetch(argv
[1], argv
[2], &contentType
);
1786 xmlNanoHTTPFetch(argv
[1], "-", &contentType
);
1787 if (contentType
!= NULL
) xmlFree(contentType
);
1789 xmlGenericError(xmlGenericErrorContext
,
1790 "%s: minimal HTTP GET implementation\n", argv
[0]);
1791 xmlGenericError(xmlGenericErrorContext
,
1792 "\tusage %s [ URL [ filename ] ]\n", argv
[0]);
1794 xmlNanoHTTPCleanup();
1798 #endif /* STANDALONE */
1799 #else /* !LIBXML_HTTP_ENABLED */
1802 int main(int argc
, char **argv
) {
1803 xmlGenericError(xmlGenericErrorContext
,
1804 "%s : HTTP support not compiled in\n", argv
[0]);
1807 #endif /* STANDALONE */
1808 #endif /* LIBXML_HTTP_ENABLED */
1809 #define bottom_nanohttp
1810 #include "elfgcchack.h"