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_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>
69 #define SOCKLEN_T unsigned int
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>
81 * A couple portability macros
84 #define closesocket(s) close(s)
89 #define SOCKLEN_T unsigned int
97 #define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)
98 #define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b)
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) {
145 return(WSAGetLastError());
154 * Initialize the HTTP protocol layer.
155 * Currently it just checks for proxy informations
159 xmlNanoHTTPInit(void) {
169 if (WSAStartup(MAKEWORD(1, 1), &wsaData
) != 0)
175 env
= getenv("no_proxy");
178 env
= getenv("http_proxy");
180 xmlNanoHTTPScanProxy(env
);
183 env
= getenv("HTTP_PROXY");
185 xmlNanoHTTPScanProxy(env
);
194 * xmlNanoHTTPCleanup:
196 * Cleanup the HTTP protocol layer.
200 xmlNanoHTTPCleanup(void) {
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.
221 xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt
, const char *URL
) {
222 const char *cur
= URL
;
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
) {
239 if (URL
== NULL
) return;
242 if ((cur
[0] == ':') && (cur
[1] == '/') && (cur
[2] == '/')) {
244 ctxt
->protocol
= xmlMemStrdup(buf
);
249 buf
[indx
++] = *cur
++;
251 if (*cur
== 0) return;
257 ctxt
->hostname
= xmlMemStrdup(buf
);
260 while ((*cur
>= '0') && (*cur
<= '9')) {
265 if (port
!= 0) ctxt
->port
= port
;
266 while ((cur
[0] != '/') && (*cur
!= 0))
270 if ((*cur
== '/') || (*cur
== 0)) {
272 ctxt
->hostname
= xmlMemStrdup(buf
);
276 buf
[indx
++] = *cur
++;
279 ctxt
->path
= xmlMemStrdup("/");
284 buf
[indx
++] = *cur
++;
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.
301 xmlNanoHTTPScanProxy(const char *URL
) {
302 const char *cur
= URL
;
311 if (proxyPort
!= 0) {
316 xmlGenericError(xmlGenericErrorContext
,
317 "Removing HTTP proxy info\n");
319 xmlGenericError(xmlGenericErrorContext
,
320 "Using HTTP proxy %s\n", URL
);
322 if (URL
== NULL
) return;
325 if ((cur
[0] == ':') && (cur
[1] == '/') && (cur
[2] == '/')) {
331 buf
[indx
++] = *cur
++;
333 if (*cur
== 0) return;
339 proxy
= xmlMemStrdup(buf
);
342 while ((*cur
>= '0') && (*cur
<= '9')) {
347 if (port
!= 0) proxyPort
= port
;
348 while ((cur
[0] != '/') && (*cur
!= 0))
352 if ((*cur
== '/') || (*cur
== 0)) {
354 proxy
= xmlMemStrdup(buf
);
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
));
380 ret
->returnValue
= 0;
382 ret
->ContentLength
= -1;
384 xmlNanoHTTPScanURL(ret
, URL
);
390 * xmlNanoHTTPFreeCtxt:
391 * @ctxt: an HTTP context
393 * Frees the context after closing the connection.
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
);
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.
422 xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt
, const char * xmt_ptr
, int outlen
) {
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);
432 else if ( ( nsent
== -1 ) &&
433 #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
434 ( socket_errno( ) != EAGAIN
) &&
436 ( socket_errno( ) != EWOULDBLOCK
) ) {
437 xmlGenericError( xmlGenericErrorContext
,
438 "xmlNanoHTTPSend error: %s",
439 strerror( socket_errno( ) ) );
441 if ( total_sent
== 0 )
448 ** Since non-blocking sockets are used, wait for
449 ** socket to be writable or default timeout prior
459 FD_SET( ctxt
->fd
, &wfd
);
460 (void)select( ctxt
->fd
+ 1, NULL
, &wfd
, NULL
, &tv
);
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.
479 xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt
) {
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
) {
489 xmlGenericError( xmlGenericErrorContext
,
490 "xmlNanoHTTPRecv: Error allocating input memory." );
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
;
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",
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
;
531 if (ctxt
->last
== 0) {
534 if (ctxt
->last
== -1) {
535 switch (socket_errno()) {
538 #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
548 xmlGenericError( xmlGenericErrorContext
,
549 "xmlNanoHTTPRecv: recv( ) failure - %s",
550 strerror( socket_errno( ) ) );
558 FD_SET(ctxt
->fd
, &rfd
);
560 if ( (select(ctxt
->fd
+1, &rfd
, NULL
, NULL
, &tv
)<1)
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.
582 xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt
) {
587 while (bp
- buf
< 4095) {
588 if (ctxt
->inrptr
== ctxt
->inptr
) {
589 if ( (rc
= xmlNanoHTTPRecv(ctxt
)) == 0) {
594 return(xmlMemStrdup(buf
));
596 else if ( rc
== -1 ) {
600 *bp
= *ctxt
->inrptr
++;
603 return(xmlMemStrdup(buf
));
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
622 * - The Location for redirect processing.
624 * Returns -1 in case of failure, the file descriptor number otherwise
628 xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt
, const char *line
) {
629 const char *cur
= line
;
631 if (line
== NULL
) return;
633 if (!strncmp(line
, "HTTP/", 5)) {
638 while ((*cur
>= '0') && (*cur
<= '9')) {
640 version
+= *cur
- '0';
645 if ((*cur
>= '0') && (*cur
<= '9')) {
647 version
+= *cur
- '0';
650 while ((*cur
>= '0') && (*cur
<= '9'))
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')) {
662 if ((*cur
!= 0) && (*cur
!= ' ') && (*cur
!= '\t')) return;
663 ctxt
->returnValue
= ret
;
664 } else if (!xmlStrncasecmp(BAD_CAST line
, BAD_CAST
"Content-Type:", 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)) {
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)) {
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)) {
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)) {
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) ) {
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
711 xmlNanoHTTPConnectAttempt(struct sockaddr
*addr
)
713 SOCKET s
= socket(PF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
722 xmlGenericError( xmlGenericErrorContext
,
723 "xmlNanoHTTPConnectAttempt: %s - %s",
724 "socket creation failure",
725 strerror( socket_errno( ) ) );
733 status
= ioctlsocket(s
, FIONBIO
, &one
) == SOCKET_ERROR
? -1 : 0;
735 #else /* _WINSOCKAPI_ */
739 status
= ioctl(s
, FIONBIO
, &enable
);
742 if ((status
= fcntl(s
, F_GETFL
, 0)) != -1) {
744 status
|= O_NONBLOCK
;
745 #else /* O_NONBLOCK */
748 #endif /* F_NDELAY */
749 #endif /* !O_NONBLOCK */
750 status
= fcntl(s
, F_SETFL
, status
);
754 perror("nonblocking");
756 xmlGenericError( xmlGenericErrorContext
,
757 "xmlNanoHTTPConnectAttempt: %s - %s",
758 "error setting non-blocking IO",
759 strerror( socket_errno( ) ) );
764 #endif /* !_WINSOCKAPI_ */
766 if ((connect(s
, addr
, sizeof(*addr
))==-1)) {
767 switch (socket_errno()) {
772 xmlGenericError( xmlGenericErrorContext
,
773 "xmlNanoHTTPConnectAttempt: %s - %s",
774 "error connecting to HTTP server",
775 strerror( socket_errno( ) ) );
787 switch(select(s
+1, NULL
, &wfd
, NULL
, &tv
))
791 xmlGenericError( xmlGenericErrorContext
,
792 "xmlNanoHTTPConnectAttempt: %s",
793 "Connect attempt timed out." );
798 xmlGenericError( xmlGenericErrorContext
,
799 "xmlNanoHTTPConnectAttempt: %s - %s",
800 "Error connecting to host",
801 strerror( socket_errno( ) ) );
806 if ( FD_ISSET(s
, &wfd
) ) {
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( ) ) );
820 xmlGenericError( xmlGenericErrorContext
,
821 "xmlNanoHTTPConnectAttempt: %s - %s",
822 "Error connecting to remote host",
823 strerror( status
) );
828 xmlGenericError( xmlGenericErrorContext
,
829 "xmlNanoHTTPConnectAttempt: %s\n",
830 "Select returned, but descriptor not set for connection.\n" );
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
850 xmlNanoHTTPConnectHost(const char *host
, int port
)
853 struct sockaddr
*addr
;
855 struct sockaddr_in sockin
;
859 struct sockaddr_in6 sockin6
;
864 #if defined(SUPPORT_IP6) && defined(RES_USE_INET6)
865 if (!(_res
.options
& RES_INIT
))
867 _res
.options
|= RES_USE_INET6
;
869 h
= gethostbyname(host
);
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
= "";
882 h_err_txt
= "Authoritive host not found";
887 "Non-authoritive host not found or server failure.";
892 "Non-recoverable errors: FORMERR, REFUSED, or NOTIMP.";
897 "Valid name, no data record of requested type.";
901 h_err_txt
= "No error text defined.";
904 xmlGenericError(xmlGenericErrorContext
,
905 "xmlNanoHTTPConnectHost: %s '%s' - %s",
906 "Failed to resolve host", host
, h_err_txt
);
908 xmlGenericError(xmlGenericErrorContext
,
909 "xmlNanoHTTPConnectHost: %s '%s'",
910 "Failed to resolve host", host
);
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
;
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
;
935 s
= xmlNanoHTTPConnectAttempt(addr
);
941 xmlGenericError(xmlGenericErrorContext
,
942 "xmlNanoHTTPConnectHost: unable to connect to '%s'.\n",
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
958 * Returns NULL in case of failure, otherwise a request handler.
959 * The contentType, if provided must be freed by the caller
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
978 * Returns NULL in case of failure, otherwise a request handler.
979 * The contentType, if provided must be freed by the caller
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));
991 * @ctx: the HTTP context
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
;
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.
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
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
;
1062 int nbRedirects
= 0;
1063 char *redirURL
= NULL
;
1065 if (URL
== NULL
) return(NULL
);
1066 if (method
== NULL
) method
= "GET";
1070 if (redirURL
== NULL
)
1071 ctxt
= xmlNanoHTTPNewCtxt(URL
);
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
) );
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
);
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
);
1103 blen
= strlen(ctxt
->hostname
) * 2 + 16;
1104 ret
= xmlNanoHTTPConnectHost(proxy
, proxyPort
);
1107 blen
= strlen(ctxt
->hostname
);
1108 ret
= xmlNanoHTTPConnectHost(ctxt
->hostname
, ctxt
->port
);
1111 xmlNanoHTTPFreeCtxt(ctxt
);
1112 if (redirURL
!= NULL
) xmlFree(redirURL
);
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
);
1129 xmlNanoHTTPFreeCtxt( ctxt
);
1130 xmlGenericError( xmlGenericErrorContext
,
1131 "xmlNanoHTTPMethodRedir: %s",
1132 "Error allocating HTTP header buffer." );
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
);
1145 p
+= snprintf( p
, blen
- (p
- bp
), "%s http://%s%s", method
,
1146 ctxt
->hostname
, ctxt
->path
);
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",
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
);
1161 snprintf(p
, blen
- (p
- bp
), "Content-Length: %d\r\n\r\n", ilen
);
1163 snprintf(p
, blen
- (p
- bp
), "\r\n");
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
);
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
);
1177 if ( xmt_bytes
!= blen
)
1178 xmlGenericError( xmlGenericErrorContext
,
1179 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1181 "bytes of HTTP headers sent to host",
1185 if ( input
!= NULL
) {
1186 xmt_bytes
= xmlNanoHTTPSend( ctxt
, input
, ilen
);
1189 if ( xmt_bytes
!= ilen
)
1190 xmlGenericError( xmlGenericErrorContext
,
1191 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1193 "bytes of HTTP content sent to host",
1198 ctxt
->state
= XML_NANO_HTTP_READ
;
1201 while ((p
= xmlNanoHTTPReadLine(ctxt
)) != NULL
) {
1202 if (head
&& (*p
== 0)) {
1204 ctxt
->content
= ctxt
->inrptr
;
1208 xmlNanoHTTPScanAnswer(ctxt
, p
);
1211 xmlGenericError(xmlGenericErrorContext
, "<- %s\n", p
);
1216 if ((ctxt
->location
!= NULL
) && (ctxt
->returnValue
>= 300) &&
1217 (ctxt
->returnValue
< 400)) {
1219 xmlGenericError(xmlGenericErrorContext
,
1220 "\nRedirect to: %s\n", ctxt
->location
);
1222 while ( xmlNanoHTTPRecv(ctxt
) > 0 ) ;
1223 if (nbRedirects
< XML_NANO_HTTP_MAX_REDIR
) {
1225 if (redirURL
!= NULL
)
1227 redirURL
= xmlMemStrdup(ctxt
->location
);
1228 xmlNanoHTTPFreeCtxt(ctxt
);
1231 xmlNanoHTTPFreeCtxt(ctxt
);
1232 if (redirURL
!= NULL
) xmlFree(redirURL
);
1234 xmlGenericError(xmlGenericErrorContext
,
1235 "xmlNanoHTTPMethodRedir: Too many redirects, aborting ...\n");
1240 if (contentType
!= NULL
) {
1241 if (ctxt
->contentType
!= NULL
)
1242 *contentType
= xmlMemStrdup(ctxt
->contentType
);
1244 *contentType
= NULL
;
1247 if ((redir
!= NULL
) && (redirURL
!= NULL
)) {
1250 if (redirURL
!= NULL
)
1257 if (ctxt
->contentType
!= NULL
)
1258 xmlGenericError(xmlGenericErrorContext
,
1259 "\nCode %d, content-type '%s'\n\n",
1260 ctxt
->returnValue
, ctxt
->contentType
);
1262 xmlGenericError(xmlGenericErrorContext
,
1263 "\nCode %d, no content-type\n\n",
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
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
));
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
) {
1314 ctxt
= xmlNanoHTTPOpen(URL
, contentType
);
1315 if (ctxt
== NULL
) return(-1);
1317 if (!strcmp(filename
, "-"))
1320 fd
= open(filename
, O_CREAT
| O_WRONLY
, 00644);
1322 xmlNanoHTTPClose(ctxt
);
1323 if ((contentType
!= NULL
) && (*contentType
!= NULL
)) {
1324 xmlFree(*contentType
);
1325 *contentType
= NULL
;
1331 xmlNanoHTTPFetchContent( ctxt
, &buf
, &len
);
1333 write(fd
, buf
, len
);
1336 xmlNanoHTTPClose(ctxt
);
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
) {
1357 if (ctxt
== NULL
) return(-1);
1359 if (!strcmp(filename
, "-"))
1362 fd
= open(filename
, O_CREAT
| O_WRONLY
);
1364 xmlNanoHTTPClose(ctxt
);
1369 xmlNanoHTTPFetchContent( ctxt
, &buf
, &len
);
1371 write(fd
, buf
, len
);
1374 xmlNanoHTTPClose(ctxt
);
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
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
1439 xmlNanoHTTPFetchContent( void * ctx
, char ** ptr
, int * len
) {
1440 xmlNanoHTTPCtxtPtr ctxt
= ctx
;
1446 char * dummy_ptr
= NULL
;
1448 /* Dummy up return input parameters if not provided */
1456 /* But can't work without the context pointer */
1458 if ( ( ctxt
== NULL
) || ( ctxt
->content
== NULL
) ) {
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
) )
1473 *ptr
= ctxt
->content
;
1476 if ( ( ctxt
->ContentLength
> 0 ) && ( rcvd_lgth
< ctxt
->ContentLength
) )
1478 else if ( rcvd_lgth
== 0 )
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
);
1492 xmlNanoHTTPFetch(argv
[1], "-", &contentType
);
1493 if (contentType
!= NULL
) xmlFree(contentType
);
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();
1504 #endif /* STANDALONE */
1505 #else /* !LIBXML_HTTP_ENABLED */
1508 int main(int argc
, char **argv
) {
1509 xmlGenericError(xmlGenericErrorContext
,
1510 "%s : HTTP support not compiled in\n", argv
[0]);
1513 #endif /* STANDALONE */
1514 #endif /* LIBXML_HTTP_ENABLED */