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)
79
#include <wsockcompat.h>
82
#define XML_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
99
#define closesocket(s) close(s)
106
#define PF_INET AF_INET
110
#ifndef XML_SOCKLEN_T
111
#define XML_SOCKLEN_T unsigned int
119
#define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)
120
#define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b)
123
#define XML_NANO_HTTP_MAX_REDIR 10
125
#define XML_NANO_HTTP_CHUNK 4096
127
#define XML_NANO_HTTP_CLOSED 0
128
#define XML_NANO_HTTP_WRITE 1
129
#define XML_NANO_HTTP_READ 2
130
#define XML_NANO_HTTP_NONE 4
132
typedef struct xmlNanoHTTPCtxt {
133
char *protocol; /* the protocol name */
134
char *hostname; /* the host name */
135
int port; /* the port */
136
char *path; /* the path within the URL */
137
char *query; /* the query string */
138
SOCKET fd; /* the file descriptor for the socket */
139
int state; /* WRITE / READ / CLOSED */
140
char *out; /* buffer sent (zero terminated) */
141
char *outptr; /* index within the buffer sent */
142
char *in; /* the receiving buffer */
143
char *content; /* the start of the content */
144
char *inptr; /* the next byte to read from network */
145
char *inrptr; /* the next byte to give back to the client */
146
int inlen; /* len of the input buffer */
147
int last; /* return code for last operation */
148
int returnValue; /* the protocol return value */
149
int ContentLength; /* specified content length from HTTP header */
150
char *contentType; /* the MIME type for the input */
151
char *location; /* the new URL in case of redirect */
152
char *authHeader; /* contents of {WWW,Proxy}-Authenticate header */
153
char *encoding; /* encoding extracted from the contentType */
154
char *mimeType; /* Mime-Type extracted from the contentType */
156
z_stream *strm; /* Zlib stream object */
157
int usesGzip; /* "Content-Encoding: gzip" was detected */
159
} xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;
161
static int initialized = 0;
162
static char *proxy = NULL; /* the proxy name if any */
163
static int proxyPort; /* the proxy port if any */
164
static unsigned int timeout = 60;/* the select() timeout in seconds */
166
static int xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len );
170
* @extra: extra informations
172
* Handle an out of memory condition
175
xmlHTTPErrMemory(const char *extra)
177
__xmlSimpleError(XML_FROM_HTTP, XML_ERR_NO_MEMORY, NULL, NULL, extra);
181
* A portability function
183
static int socket_errno(void) {
185
return(WSAGetLastError());
193
int have_ipv6(void) {
196
s = socket (AF_INET6, SOCK_STREAM, 0);
208
* Initialize the HTTP protocol layer.
209
* Currently it just checks for proxy informations
213
xmlNanoHTTPInit(void) {
223
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
229
env = getenv("no_proxy");
230
if (env && ((env[0] == '*') && (env[1] == 0)))
232
env = getenv("http_proxy");
234
xmlNanoHTTPScanProxy(env);
237
env = getenv("HTTP_PROXY");
239
xmlNanoHTTPScanProxy(env);
248
* xmlNanoHTTPCleanup:
250
* Cleanup the HTTP protocol layer.
254
xmlNanoHTTPCleanup(void) {
268
* xmlNanoHTTPScanURL:
269
* @ctxt: an HTTP context
270
* @URL: The URL used to initialize the context
272
* (Re)Initialize an HTTP context by parsing the URL and finding
273
* the protocol host port and path it indicates.
277
xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) {
280
* Clear any existing data from the context
282
if (ctxt->protocol != NULL) {
283
xmlFree(ctxt->protocol);
284
ctxt->protocol = NULL;
286
if (ctxt->hostname != NULL) {
287
xmlFree(ctxt->hostname);
288
ctxt->hostname = NULL;
290
if (ctxt->path != NULL) {
294
if (ctxt->query != NULL) {
295
xmlFree(ctxt->query);
298
if (URL == NULL) return;
300
uri = xmlParseURIRaw(URL, 1);
304
if ((uri->scheme == NULL) || (uri->server == NULL)) {
309
ctxt->protocol = xmlMemStrdup(uri->scheme);
310
ctxt->hostname = xmlMemStrdup(uri->server);
311
if (uri->path != NULL)
312
ctxt->path = xmlMemStrdup(uri->path);
314
ctxt->path = xmlMemStrdup("/");
315
if (uri->query != NULL)
316
ctxt->query = xmlMemStrdup(uri->query);
318
ctxt->port = uri->port;
324
* xmlNanoHTTPScanProxy:
325
* @URL: The proxy URL used to initialize the proxy context
327
* (Re)Initialize the HTTP Proxy context by parsing the URL and finding
328
* the protocol host port it indicates.
329
* Should be like http://myproxy/ or http://myproxy:3128/
330
* A NULL URL cleans up proxy informations.
334
xmlNanoHTTPScanProxy(const char *URL) {
345
xmlGenericError(xmlGenericErrorContext,
346
"Removing HTTP proxy info\n");
348
xmlGenericError(xmlGenericErrorContext,
349
"Using HTTP proxy %s\n", URL);
351
if (URL == NULL) return;
353
uri = xmlParseURIRaw(URL, 1);
354
if ((uri == NULL) || (uri->scheme == NULL) ||
355
(strcmp(uri->scheme, "http")) || (uri->server == NULL)) {
356
__xmlIOErr(XML_FROM_HTTP, XML_HTTP_URL_SYNTAX, "Syntax Error\n");
362
proxy = xmlMemStrdup(uri->server);
364
proxyPort = uri->port;
370
* xmlNanoHTTPNewCtxt:
371
* @URL: The URL used to initialize the context
373
* Allocate and initialize a new HTTP context.
375
* Returns an HTTP context or NULL in case of error.
378
static xmlNanoHTTPCtxtPtr
379
xmlNanoHTTPNewCtxt(const char *URL) {
380
xmlNanoHTTPCtxtPtr ret;
382
ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt));
384
xmlHTTPErrMemory("allocating context");
388
memset(ret, 0, sizeof(xmlNanoHTTPCtxt));
390
ret->returnValue = 0;
392
ret->ContentLength = -1;
394
xmlNanoHTTPScanURL(ret, URL);
400
* xmlNanoHTTPFreeCtxt:
401
* @ctxt: an HTTP context
403
* Frees the context after closing the connection.
407
xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) {
408
if (ctxt == NULL) return;
409
if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
410
if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
411
if (ctxt->path != NULL) xmlFree(ctxt->path);
412
if (ctxt->query != NULL) xmlFree(ctxt->query);
413
if (ctxt->out != NULL) xmlFree(ctxt->out);
414
if (ctxt->in != NULL) xmlFree(ctxt->in);
415
if (ctxt->contentType != NULL) xmlFree(ctxt->contentType);
416
if (ctxt->encoding != NULL) xmlFree(ctxt->encoding);
417
if (ctxt->mimeType != NULL) xmlFree(ctxt->mimeType);
418
if (ctxt->location != NULL) xmlFree(ctxt->location);
419
if (ctxt->authHeader != NULL) xmlFree(ctxt->authHeader);
421
if (ctxt->strm != NULL) {
422
inflateEnd(ctxt->strm);
427
ctxt->state = XML_NANO_HTTP_NONE;
428
if (ctxt->fd >= 0) closesocket(ctxt->fd);
435
* @ctxt: an HTTP context
437
* Send the input needed to initiate the processing on the server side
438
* Returns number of bytes sent or -1 on error.
442
xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt, const char * xmt_ptr, int outlen) {
446
if ( (ctxt->state & XML_NANO_HTTP_WRITE) && (xmt_ptr != NULL ) ) {
447
while (total_sent < outlen) {
448
int nsent = send(ctxt->fd, xmt_ptr + total_sent,
449
outlen - total_sent, 0);
452
else if ( ( nsent == -1 ) &&
453
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
454
( socket_errno( ) != EAGAIN ) &&
456
( socket_errno( ) != EWOULDBLOCK ) ) {
457
__xmlIOErr(XML_FROM_HTTP, 0, "send failed\n");
458
if ( total_sent == 0 )
465
** Since non-blocking sockets are used, wait for
466
** socket to be writable or default timeout prior
477
#pragma warning(push)
478
#pragma warning(disable: 4018)
480
FD_SET( ctxt->fd, &wfd );
484
(void)select( ctxt->fd + 1, NULL, &wfd, NULL, &tv );
494
* @ctxt: an HTTP context
496
* Read information coming from the HTTP connection.
497
* This is a blocking call (but it blocks in select(), not read()).
499
* Returns the number of byte read or -1 in case of error.
503
xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt) {
508
while (ctxt->state & XML_NANO_HTTP_READ) {
509
if (ctxt->in == NULL) {
510
ctxt->in = (char *) xmlMallocAtomic(65000 * sizeof(char));
511
if (ctxt->in == NULL) {
512
xmlHTTPErrMemory("allocating input");
517
ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in;
519
if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) {
520
int delta = ctxt->inrptr - ctxt->in;
521
int len = ctxt->inptr - ctxt->inrptr;
523
memmove(ctxt->in, ctxt->inrptr, len);
524
ctxt->inrptr -= delta;
525
ctxt->content -= delta;
526
ctxt->inptr -= delta;
528
if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) {
529
int d_inptr = ctxt->inptr - ctxt->in;
530
int d_content = ctxt->content - ctxt->in;
531
int d_inrptr = ctxt->inrptr - ctxt->in;
532
char * tmp_ptr = ctxt->in;
535
ctxt->in = (char *) xmlRealloc(tmp_ptr, ctxt->inlen);
536
if (ctxt->in == NULL) {
537
xmlHTTPErrMemory("allocating input buffer");
542
ctxt->inptr = ctxt->in + d_inptr;
543
ctxt->content = ctxt->in + d_content;
544
ctxt->inrptr = ctxt->in + d_inrptr;
546
ctxt->last = recv(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK, 0);
547
if (ctxt->last > 0) {
548
ctxt->inptr += ctxt->last;
551
if (ctxt->last == 0) {
554
if (ctxt->last == -1) {
555
switch (socket_errno()) {
558
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
568
__xmlIOErr(XML_FROM_HTTP, 0, "recv failed\n");
577
#pragma warning(push)
578
#pragma warning(disable: 4018)
580
FD_SET(ctxt->fd, &rfd);
585
if ( (select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1)
596
* xmlNanoHTTPReadLine:
597
* @ctxt: an HTTP context
599
* Read one line in the HTTP server output, usually for extracting
600
* the HTTP protocol informations from the answer header.
602
* Returns a newly allocated string with a copy of the line, or NULL
603
* which indicate the end of the input.
607
xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) {
612
while (bp - buf < 4095) {
613
if (ctxt->inrptr == ctxt->inptr) {
614
if ( (rc = xmlNanoHTTPRecv(ctxt)) == 0) {
619
return(xmlMemStrdup(buf));
621
else if ( rc == -1 ) {
625
*bp = *ctxt->inrptr++;
628
return(xmlMemStrdup(buf));
634
return(xmlMemStrdup(buf));
639
* xmlNanoHTTPScanAnswer:
640
* @ctxt: an HTTP context
641
* @line: an HTTP header line
643
* Try to extract useful informations from the server answer.
644
* We currently parse and process:
645
* - The HTTP revision/ return code
646
* - The Content-Type, Mime-Type and charset used
647
* - The Location for redirect processing.
649
* Returns -1 in case of failure, the file descriptor number otherwise
653
xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) {
654
const char *cur = line;
656
if (line == NULL) return;
658
if (!strncmp(line, "HTTP/", 5)) {
663
while ((*cur >= '0') && (*cur <= '9')) {
665
version += *cur - '0';
670
if ((*cur >= '0') && (*cur <= '9')) {
672
version += *cur - '0';
675
while ((*cur >= '0') && (*cur <= '9'))
679
if ((*cur != ' ') && (*cur != '\t')) return;
680
while ((*cur == ' ') || (*cur == '\t')) cur++;
681
if ((*cur < '0') || (*cur > '9')) return;
682
while ((*cur >= '0') && (*cur <= '9')) {
687
if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return;
688
ctxt->returnValue = ret;
689
} else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Type:", 13)) {
690
const xmlChar *charset, *last, *mime;
692
while ((*cur == ' ') || (*cur == '\t')) cur++;
693
if (ctxt->contentType != NULL)
694
xmlFree(ctxt->contentType);
695
ctxt->contentType = xmlMemStrdup(cur);
696
mime = (const xmlChar *) cur;
698
while ((*last != 0) && (*last != ' ') && (*last != '\t') &&
699
(*last != ';') && (*last != ','))
701
if (ctxt->mimeType != NULL)
702
xmlFree(ctxt->mimeType);
703
ctxt->mimeType = (char *) xmlStrndup(mime, last - mime);
704
charset = xmlStrstr(BAD_CAST ctxt->contentType, BAD_CAST "charset=");
705
if (charset != NULL) {
708
while ((*last != 0) && (*last != ' ') && (*last != '\t') &&
709
(*last != ';') && (*last != ','))
711
if (ctxt->encoding != NULL)
712
xmlFree(ctxt->encoding);
713
ctxt->encoding = (char *) xmlStrndup(charset, last - charset);
715
} else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"ContentType:", 12)) {
716
const xmlChar *charset, *last, *mime;
718
if (ctxt->contentType != NULL) return;
719
while ((*cur == ' ') || (*cur == '\t')) cur++;
720
ctxt->contentType = xmlMemStrdup(cur);
721
mime = (const xmlChar *) cur;
723
while ((*last != 0) && (*last != ' ') && (*last != '\t') &&
724
(*last != ';') && (*last != ','))
726
if (ctxt->mimeType != NULL)
727
xmlFree(ctxt->mimeType);
728
ctxt->mimeType = (char *) xmlStrndup(mime, last - mime);
729
charset = xmlStrstr(BAD_CAST ctxt->contentType, BAD_CAST "charset=");
730
if (charset != NULL) {
733
while ((*last != 0) && (*last != ' ') && (*last != '\t') &&
734
(*last != ';') && (*last != ','))
736
if (ctxt->encoding != NULL)
737
xmlFree(ctxt->encoding);
738
ctxt->encoding = (char *) xmlStrndup(charset, last - charset);
740
} else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Location:", 9)) {
742
while ((*cur == ' ') || (*cur == '\t')) cur++;
743
if (ctxt->location != NULL)
744
xmlFree(ctxt->location);
746
xmlChar *tmp_http = xmlStrdup(BAD_CAST "http://");
748
xmlStrcat(tmp_http, (const xmlChar *) ctxt->hostname);
750
(char *) xmlStrcat (tmp_loc, (const xmlChar *) cur);
752
ctxt->location = xmlMemStrdup(cur);
754
} else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"WWW-Authenticate:", 17)) {
756
while ((*cur == ' ') || (*cur == '\t')) cur++;
757
if (ctxt->authHeader != NULL)
758
xmlFree(ctxt->authHeader);
759
ctxt->authHeader = xmlMemStrdup(cur);
760
} else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Proxy-Authenticate:", 19)) {
762
while ((*cur == ' ') || (*cur == '\t')) cur++;
763
if (ctxt->authHeader != NULL)
764
xmlFree(ctxt->authHeader);
765
ctxt->authHeader = xmlMemStrdup(cur);
767
} else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Encoding:", 17) ) {
769
while ((*cur == ' ') || (*cur == '\t')) cur++;
770
if ( !xmlStrncasecmp( BAD_CAST cur, BAD_CAST"gzip", 4) ) {
773
ctxt->strm = xmlMalloc(sizeof(z_stream));
775
if (ctxt->strm != NULL) {
776
ctxt->strm->zalloc = Z_NULL;
777
ctxt->strm->zfree = Z_NULL;
778
ctxt->strm->opaque = Z_NULL;
779
ctxt->strm->avail_in = 0;
780
ctxt->strm->next_in = Z_NULL;
782
inflateInit2( ctxt->strm, 31 );
786
} else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Length:", 15) ) {
788
ctxt->ContentLength = strtol( cur, NULL, 10 );
793
* xmlNanoHTTPConnectAttempt:
794
* @addr: a socket address structure
796
* Attempt a connection to the given IP:port endpoint. It forces
797
* non-blocking semantic on the socket, and allow 60 seconds for
798
* the host to answer.
800
* Returns -1 in case of failure, the file descriptor number otherwise
804
xmlNanoHTTPConnectAttempt(struct sockaddr *addr)
816
if (addr->sa_family == AF_INET6) {
817
s = socket (PF_INET6, SOCK_STREAM, IPPROTO_TCP);
818
addrlen = sizeof (struct sockaddr_in6);
823
s = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
824
addrlen = sizeof (struct sockaddr_in);
830
__xmlIOErr(XML_FROM_HTTP, 0, "socket failed\n");
838
status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0;
840
#else /* _WINSOCKAPI_ */
844
status = ioctl(s, FIONBIO, &enable);
847
#if defined(__BEOS__)
850
status = setsockopt(s, SOL_SOCKET, SO_NONBLOCK, &noblock, sizeof(noblock));
853
if ((status = fcntl(s, F_GETFL, 0)) != -1) {
855
status |= O_NONBLOCK;
856
#else /* O_NONBLOCK */
859
#endif /* F_NDELAY */
860
#endif /* !O_NONBLOCK */
861
status = fcntl(s, F_SETFL, status);
865
perror("nonblocking");
867
__xmlIOErr(XML_FROM_HTTP, 0, "error setting non-blocking IO\n");
871
#endif /* !__BEOS__ */
873
#endif /* !_WINSOCKAPI_ */
875
if (connect (s, addr, addrlen) == -1) {
876
switch (socket_errno()) {
881
__xmlIOErr(XML_FROM_HTTP, 0, "error connecting to HTTP server");
891
#pragma warning(push)
892
#pragma warning(disable: 4018)
901
switch(select(s+1, NULL, &wfd, &xfd, &tv))
903
switch(select(s+1, NULL, &wfd, NULL, &tv))
911
__xmlIOErr(XML_FROM_HTTP, 0, "Connect attempt timed out");
916
__xmlIOErr(XML_FROM_HTTP, 0, "Connect failed");
921
if ( FD_ISSET(s, &wfd)
927
len = sizeof(status);
929
if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&status, &len) < 0 ) {
930
/* Solaris error code */
931
__xmlIOErr(XML_FROM_HTTP, 0, "getsockopt failed\n");
936
__xmlIOErr(XML_FROM_HTTP, 0, "Error connecting to remote host");
943
__xmlIOErr(XML_FROM_HTTP, 0, "select failed\n");
952
* xmlNanoHTTPConnectHost:
953
* @host: the host name
954
* @port: the port number
956
* Attempt a connection to the given host:port endpoint. It tries
957
* the multiple IP provided by the DNS if available.
959
* Returns -1 in case of failure, the file descriptor number otherwise
963
xmlNanoHTTPConnectHost(const char *host, int port)
966
struct sockaddr *addr = NULL;
968
struct sockaddr_in sockin;
972
struct sockaddr_in6 sockin6;
977
memset (&sockin, 0, sizeof(sockin));
979
memset (&sockin6, 0, sizeof(sockin6));
982
#if !defined(HAVE_GETADDRINFO) && defined(SUPPORT_IP6) && defined(RES_USE_INET6)
985
if (!(_res.options & RES_INIT))
987
_res.options |= RES_USE_INET6;
991
#if defined(HAVE_GETADDRINFO) && defined(SUPPORT_IP6) && !defined(_WIN32)
994
#if defined(HAVE_GETADDRINFO) && (defined(SUPPORT_IP6) || defined(_WIN32))
997
struct addrinfo hints, *res, *result;
1000
memset (&hints, 0,sizeof(hints));
1001
hints.ai_socktype = SOCK_STREAM;
1003
status = getaddrinfo (host, NULL, &hints, &result);
1005
__xmlIOErr(XML_FROM_HTTP, 0, "getaddrinfo failed\n");
1009
for (res = result; res; res = res->ai_next) {
1010
if (res->ai_family == AF_INET) {
1011
if (res->ai_addrlen > sizeof(sockin)) {
1012
__xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n");
1013
freeaddrinfo (result);
1016
memcpy (&sockin, res->ai_addr, res->ai_addrlen);
1017
sockin.sin_port = htons (port);
1018
addr = (struct sockaddr *)&sockin;
1020
} else if (have_ipv6 () && (res->ai_family == AF_INET6)) {
1021
if (res->ai_addrlen > sizeof(sockin6)) {
1022
__xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n");
1023
freeaddrinfo (result);
1026
memcpy (&sockin6, res->ai_addr, res->ai_addrlen);
1027
sockin6.sin6_port = htons (port);
1028
addr = (struct sockaddr *)&sockin6;
1033
s = xmlNanoHTTPConnectAttempt (addr);
1035
freeaddrinfo (result);
1041
freeaddrinfo (result);
1044
#if defined(HAVE_GETADDRINFO) && defined(SUPPORT_IP6) && !defined(_WIN32)
1047
#if !defined(HAVE_GETADDRINFO) || !defined(_WIN32)
1049
h = gethostbyname (host);
1053
* Okay, I got fed up by the non-portability of this error message
1054
* extraction code. it work on Linux, if it work on your platform
1055
* and one want to enable it, send me the defined(foobar) needed
1057
#if defined(HAVE_NETDB_H) && defined(HOST_NOT_FOUND) && defined(linux)
1058
const char *h_err_txt = "";
1061
case HOST_NOT_FOUND:
1062
h_err_txt = "Authoritive host not found";
1067
"Non-authoritive host not found or server failure.";
1072
"Non-recoverable errors: FORMERR, REFUSED, or NOTIMP.";
1077
"Valid name, no data record of requested type.";
1081
h_err_txt = "No error text defined.";
1084
__xmlIOErr(XML_FROM_HTTP, 0, h_err_txt);
1086
__xmlIOErr(XML_FROM_HTTP, 0, "Failed to resolve host");
1091
for (i = 0; h->h_addr_list[i]; i++) {
1092
if (h->h_addrtype == AF_INET) {
1093
/* A records (IPv4) */
1094
if ((unsigned int) h->h_length > sizeof(ia)) {
1095
__xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n");
1098
memcpy (&ia, h->h_addr_list[i], h->h_length);
1099
sockin.sin_family = h->h_addrtype;
1100
sockin.sin_addr = ia;
1101
sockin.sin_port = (u_short)htons ((unsigned short)port);
1102
addr = (struct sockaddr *) &sockin;
1104
} else if (have_ipv6 () && (h->h_addrtype == AF_INET6)) {
1105
/* AAAA records (IPv6) */
1106
if ((unsigned int) h->h_length > sizeof(ia6)) {
1107
__xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n");
1110
memcpy (&ia6, h->h_addr_list[i], h->h_length);
1111
sockin6.sin6_family = h->h_addrtype;
1112
sockin6.sin6_addr = ia6;
1113
sockin6.sin6_port = htons (port);
1114
addr = (struct sockaddr *) &sockin6;
1119
s = xmlNanoHTTPConnectAttempt (addr);
1127
xmlGenericError(xmlGenericErrorContext,
1128
"xmlNanoHTTPConnectHost: unable to connect to '%s'.\n",
1137
* @URL: The URL to load
1138
* @contentType: if available the Content-Type information will be
1139
* returned at that location
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
xmlNanoHTTPOpen(const char *URL, char **contentType) {
1150
if (contentType != NULL) *contentType = NULL;
1151
return(xmlNanoHTTPMethod(URL, NULL, NULL, contentType, NULL, 0));
1155
* xmlNanoHTTPOpenRedir:
1156
* @URL: The URL to load
1157
* @contentType: if available the Content-Type information will be
1158
* returned at that location
1159
* @redir: if available the redirected URL will be returned
1161
* This function try to open a connection to the indicated resource
1164
* Returns NULL in case of failure, otherwise a request handler.
1165
* The contentType, if provided must be freed by the caller
1169
xmlNanoHTTPOpenRedir(const char *URL, char **contentType, char **redir) {
1170
if (contentType != NULL) *contentType = NULL;
1171
if (redir != NULL) *redir = NULL;
1172
return(xmlNanoHTTPMethodRedir(URL, NULL, NULL, contentType, redir, NULL,0));
1177
* @ctx: the HTTP context
1179
* @len: the buffer length
1181
* This function tries to read @len bytes from the existing HTTP connection
1182
* and saves them in @dest. This is a blocking call.
1184
* Returns the number of byte read. 0 is an indication of an end of connection.
1185
* -1 indicates a parameter error.
1188
xmlNanoHTTPRead(void *ctx, void *dest, int len) {
1189
xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1196
if (ctx == NULL) return(-1);
1197
if (dest == NULL) return(-1);
1198
if (len <= 0) return(0);
1201
if (ctxt->usesGzip == 1) {
1202
if (ctxt->strm == NULL) return(0);
1204
ctxt->strm->next_out = dest;
1205
ctxt->strm->avail_out = len;
1206
ctxt->strm->avail_in = ctxt->inptr - ctxt->inrptr;
1208
while (ctxt->strm->avail_out > 0 &&
1209
(ctxt->strm->avail_in > 0 || xmlNanoHTTPRecv(ctxt) > 0)) {
1210
orig_avail_in = ctxt->strm->avail_in =
1211
ctxt->inptr - ctxt->inrptr - bytes_read;
1212
ctxt->strm->next_in = BAD_CAST (ctxt->inrptr + bytes_read);
1214
z_ret = inflate(ctxt->strm, Z_NO_FLUSH);
1215
bytes_read += orig_avail_in - ctxt->strm->avail_in;
1217
if (z_ret != Z_OK) break;
1220
ctxt->inrptr += bytes_read;
1221
return(len - ctxt->strm->avail_out);
1225
while (ctxt->inptr - ctxt->inrptr < len) {
1226
if (xmlNanoHTTPRecv(ctxt) <= 0) break;
1228
if (ctxt->inptr - ctxt->inrptr < len)
1229
len = ctxt->inptr - ctxt->inrptr;
1230
memcpy(dest, ctxt->inrptr, len);
1231
ctxt->inrptr += len;
1237
* @ctx: the HTTP context
1239
* This function closes an HTTP context, it ends up the connection and
1240
* free all data related to it.
1243
xmlNanoHTTPClose(void *ctx) {
1244
xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1246
if (ctx == NULL) return;
1248
xmlNanoHTTPFreeCtxt(ctxt);
1252
* xmlNanoHTTPMethodRedir:
1253
* @URL: The URL to load
1254
* @method: the HTTP method to use
1255
* @input: the input string if any
1256
* @contentType: the Content-Type information IN and OUT
1257
* @redir: the redirected URL OUT
1258
* @headers: the extra headers
1259
* @ilen: input length
1261
* This function try to open a connection to the indicated resource
1262
* via HTTP using the given @method, adding the given extra headers
1263
* and the input buffer for the request content.
1265
* Returns NULL in case of failure, otherwise a request handler.
1266
* The contentType, or redir, if provided must be freed by the caller
1270
xmlNanoHTTPMethodRedir(const char *URL, const char *method, const char *input,
1271
char **contentType, char **redir,
1272
const char *headers, int ilen ) {
1273
xmlNanoHTTPCtxtPtr ctxt;
1277
int nbRedirects = 0;
1278
char *redirURL = NULL;
1283
if (URL == NULL) return(NULL);
1284
if (method == NULL) method = "GET";
1288
if (redirURL == NULL)
1289
ctxt = xmlNanoHTTPNewCtxt(URL);
1291
ctxt = xmlNanoHTTPNewCtxt(redirURL);
1292
ctxt->location = xmlMemStrdup(redirURL);
1295
if ( ctxt == NULL ) {
1299
if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
1300
__xmlIOErr(XML_FROM_HTTP, XML_HTTP_URL_SYNTAX, "Not a valid HTTP URI");
1301
xmlNanoHTTPFreeCtxt(ctxt);
1302
if (redirURL != NULL) xmlFree(redirURL);
1305
if (ctxt->hostname == NULL) {
1306
__xmlIOErr(XML_FROM_HTTP, XML_HTTP_UNKNOWN_HOST,
1307
"Failed to identify host in URI");
1308
xmlNanoHTTPFreeCtxt(ctxt);
1309
if (redirURL != NULL) xmlFree(redirURL);
1313
blen = strlen(ctxt->hostname) * 2 + 16;
1314
ret = xmlNanoHTTPConnectHost(proxy, proxyPort);
1317
blen = strlen(ctxt->hostname);
1318
ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
1321
xmlNanoHTTPFreeCtxt(ctxt);
1322
if (redirURL != NULL) xmlFree(redirURL);
1332
if (headers != NULL)
1333
blen += strlen(headers) + 2;
1334
if (contentType && *contentType)
1335
/* reserve for string plus 'Content-Type: \r\n" */
1336
blen += strlen(*contentType) + 16;
1337
if (ctxt->query != NULL)
1339
blen += strlen(ctxt->query) + 1;
1340
blen += strlen(method) + strlen(ctxt->path) + 24;
1342
/* reserve for possible 'Accept-Encoding: gzip' string */
1345
if (ctxt->port != 80) {
1346
/* reserve space for ':xxxxx', incl. potential proxy */
1352
bp = (char*)xmlMallocAtomic(blen);
1354
xmlNanoHTTPFreeCtxt( ctxt );
1355
xmlHTTPErrMemory("allocating header buffer");
1362
if (ctxt->port != 80) {
1363
p += snprintf( p, blen - (p - bp), "%s http://%s:%d%s",
1364
method, ctxt->hostname,
1365
ctxt->port, ctxt->path );
1368
p += snprintf( p, blen - (p - bp), "%s http://%s%s", method,
1369
ctxt->hostname, ctxt->path);
1372
p += snprintf( p, blen - (p - bp), "%s %s", method, ctxt->path);
1374
if (ctxt->query != NULL)
1375
p += snprintf( p, blen - (p - bp), "?%s", ctxt->query);
1377
if (ctxt->port == 80) {
1378
p += snprintf( p, blen - (p - bp), " HTTP/1.0\r\nHost: %s\r\n",
1381
p += snprintf( p, blen - (p - bp), " HTTP/1.0\r\nHost: %s:%d\r\n",
1382
ctxt->hostname, ctxt->port);
1386
p += snprintf(p, blen - (p - bp), "Accept-Encoding: gzip\r\n");
1389
if (contentType != NULL && *contentType)
1390
p += snprintf(p, blen - (p - bp), "Content-Type: %s\r\n", *contentType);
1392
if (headers != NULL)
1393
p += snprintf( p, blen - (p - bp), "%s", headers );
1396
snprintf(p, blen - (p - bp), "Content-Length: %d\r\n\r\n", ilen );
1398
snprintf(p, blen - (p - bp), "\r\n");
1401
xmlGenericError(xmlGenericErrorContext,
1402
"-> %s%s", proxy? "(Proxy) " : "", bp);
1403
if ((blen -= strlen(bp)+1) < 0)
1404
xmlGenericError(xmlGenericErrorContext,
1405
"ERROR: overflowed buffer by %d bytes\n", -blen);
1407
ctxt->outptr = ctxt->out = bp;
1408
ctxt->state = XML_NANO_HTTP_WRITE;
1409
blen = strlen( ctxt->out );
1411
xmt_bytes = xmlNanoHTTPSend(ctxt, ctxt->out, blen );
1412
if ( xmt_bytes != blen )
1413
xmlGenericError( xmlGenericErrorContext,
1414
"xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1416
"bytes of HTTP headers sent to host",
1419
xmlNanoHTTPSend(ctxt, ctxt->out, blen );
1422
if ( input != NULL ) {
1424
xmt_bytes = xmlNanoHTTPSend( ctxt, input, ilen );
1426
if ( xmt_bytes != ilen )
1427
xmlGenericError( xmlGenericErrorContext,
1428
"xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1430
"bytes of HTTP content sent to host",
1433
xmlNanoHTTPSend( ctxt, input, ilen );
1437
ctxt->state = XML_NANO_HTTP_READ;
1440
while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
1441
if (head && (*p == 0)) {
1443
ctxt->content = ctxt->inrptr;
1447
xmlNanoHTTPScanAnswer(ctxt, p);
1450
xmlGenericError(xmlGenericErrorContext, "<- %s\n", p);
1455
if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
1456
(ctxt->returnValue < 400)) {
1458
xmlGenericError(xmlGenericErrorContext,
1459
"\nRedirect to: %s\n", ctxt->location);
1461
while ( xmlNanoHTTPRecv(ctxt) > 0 ) ;
1462
if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
1464
if (redirURL != NULL)
1466
redirURL = xmlMemStrdup(ctxt->location);
1467
xmlNanoHTTPFreeCtxt(ctxt);
1470
xmlNanoHTTPFreeCtxt(ctxt);
1471
if (redirURL != NULL) xmlFree(redirURL);
1473
xmlGenericError(xmlGenericErrorContext,
1474
"xmlNanoHTTPMethodRedir: Too many redirects, aborting ...\n");
1479
if (contentType != NULL) {
1480
if (ctxt->contentType != NULL)
1481
*contentType = xmlMemStrdup(ctxt->contentType);
1483
*contentType = NULL;
1486
if ((redir != NULL) && (redirURL != NULL)) {
1489
if (redirURL != NULL)
1496
if (ctxt->contentType != NULL)
1497
xmlGenericError(xmlGenericErrorContext,
1498
"\nCode %d, content-type '%s'\n\n",
1499
ctxt->returnValue, ctxt->contentType);
1501
xmlGenericError(xmlGenericErrorContext,
1502
"\nCode %d, no content-type\n\n",
1506
return((void *) ctxt);
1510
* xmlNanoHTTPMethod:
1511
* @URL: The URL to load
1512
* @method: the HTTP method to use
1513
* @input: the input string if any
1514
* @contentType: the Content-Type information IN and OUT
1515
* @headers: the extra headers
1516
* @ilen: input length
1518
* This function try to open a connection to the indicated resource
1519
* via HTTP using the given @method, adding the given extra headers
1520
* and the input buffer for the request content.
1522
* Returns NULL in case of failure, otherwise a request handler.
1523
* The contentType, if provided must be freed by the caller
1527
xmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
1528
char **contentType, const char *headers, int ilen) {
1529
return(xmlNanoHTTPMethodRedir(URL, method, input, contentType,
1530
NULL, headers, ilen));
1535
* @URL: The URL to load
1536
* @filename: the filename where the content should be saved
1537
* @contentType: if available the Content-Type information will be
1538
* returned at that location
1540
* This function try to fetch the indicated resource via HTTP GET
1541
* and save it's content in the file.
1543
* Returns -1 in case of failure, 0 incase of success. The contentType,
1544
* if provided must be freed by the caller
1547
xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
1553
if (filename == NULL) return(-1);
1554
ctxt = xmlNanoHTTPOpen(URL, contentType);
1555
if (ctxt == NULL) return(-1);
1557
if (!strcmp(filename, "-"))
1560
fd = open(filename, O_CREAT | O_WRONLY, 00644);
1562
xmlNanoHTTPClose(ctxt);
1563
if ((contentType != NULL) && (*contentType != NULL)) {
1564
xmlFree(*contentType);
1565
*contentType = NULL;
1571
xmlNanoHTTPFetchContent( ctxt, &buf, &len );
1573
write(fd, buf, len);
1576
xmlNanoHTTPClose(ctxt);
1581
#ifdef LIBXML_OUTPUT_ENABLED
1584
* @ctxt: the HTTP context
1585
* @filename: the filename where the content should be saved
1587
* This function saves the output of the HTTP transaction to a file
1588
* It closes and free the context at the end
1590
* Returns -1 in case of failure, 0 incase of success.
1593
xmlNanoHTTPSave(void *ctxt, const char *filename) {
1598
if ((ctxt == NULL) || (filename == NULL)) return(-1);
1600
if (!strcmp(filename, "-"))
1603
fd = open(filename, O_CREAT | O_WRONLY, 0666);
1605
xmlNanoHTTPClose(ctxt);
1610
xmlNanoHTTPFetchContent( ctxt, &buf, &len );
1612
write(fd, buf, len);
1615
xmlNanoHTTPClose(ctxt);
1619
#endif /* LIBXML_OUTPUT_ENABLED */
1622
* xmlNanoHTTPReturnCode:
1623
* @ctx: the HTTP context
1625
* Get the latest HTTP return code received
1627
* Returns the HTTP return code for the request.
1630
xmlNanoHTTPReturnCode(void *ctx) {
1631
xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1633
if (ctxt == NULL) return(-1);
1635
return(ctxt->returnValue);
1639
* xmlNanoHTTPAuthHeader:
1640
* @ctx: the HTTP context
1642
* Get the authentication header of an HTTP context
1644
* Returns the stashed value of the WWW-Authenticate or Proxy-Authenticate
1648
xmlNanoHTTPAuthHeader(void *ctx) {
1649
xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1651
if (ctxt == NULL) return(NULL);
1653
return(ctxt->authHeader);
1657
* xmlNanoHTTPContentLength:
1658
* @ctx: the HTTP context
1660
* Provides the specified content length from the HTTP header.
1662
* Return the specified content length from the HTTP header. Note that
1663
* a value of -1 indicates that the content length element was not included in
1664
* the response header.
1667
xmlNanoHTTPContentLength( void * ctx ) {
1668
xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr)ctx;
1670
return ( ( ctxt == NULL ) ? -1 : ctxt->ContentLength );
1675
* @ctx: the HTTP context
1677
* Provides the specified redirection URL if available from the HTTP header.
1679
* Return the specified redirection URL or NULL if not redirected.
1682
xmlNanoHTTPRedir( void * ctx ) {
1683
xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr)ctx;
1685
return ( ( ctxt == NULL ) ? NULL : ctxt->location );
1689
* xmlNanoHTTPEncoding:
1690
* @ctx: the HTTP context
1692
* Provides the specified encoding if specified in the HTTP headers.
1694
* Return the specified encoding or NULL if not available
1697
xmlNanoHTTPEncoding( void * ctx ) {
1698
xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr)ctx;
1700
return ( ( ctxt == NULL ) ? NULL : ctxt->encoding );
1704
* xmlNanoHTTPMimeType:
1705
* @ctx: the HTTP context
1707
* Provides the specified Mime-Type if specified in the HTTP headers.
1709
* Return the specified Mime-Type or NULL if not available
1712
xmlNanoHTTPMimeType( void * ctx ) {
1713
xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr)ctx;
1715
return ( ( ctxt == NULL ) ? NULL : ctxt->mimeType );
1719
* xmlNanoHTTPFetchContent:
1720
* @ctx: the HTTP context
1721
* @ptr: pointer to set to the content buffer.
1722
* @len: integer pointer to hold the length of the content
1724
* Check if all the content was read
1726
* Returns 0 if all the content was read and available, returns
1727
* -1 if received content length was less than specified or an error
1731
xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len ) {
1732
xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr)ctx;
1738
char * dummy_ptr = NULL;
1740
/* Dummy up return input parameters if not provided */
1748
/* But can't work without the context pointer */
1750
if ( ( ctxt == NULL ) || ( ctxt->content == NULL ) ) {
1756
rcvd_lgth = ctxt->inptr - ctxt->content;
1758
while ( (cur_lgth = xmlNanoHTTPRecv( ctxt )) > 0 ) {
1760
rcvd_lgth += cur_lgth;
1761
if ( (ctxt->ContentLength > 0) && (rcvd_lgth >= ctxt->ContentLength) )
1765
*ptr = ctxt->content;
1768
if ( ( ctxt->ContentLength > 0 ) && ( rcvd_lgth < ctxt->ContentLength ) )
1770
else if ( rcvd_lgth == 0 )
1777
int main(int argc, char **argv) {
1778
char *contentType = NULL;
1780
if (argv[1] != NULL) {
1781
if (argv[2] != NULL)
1782
xmlNanoHTTPFetch(argv[1], argv[2], &contentType);
1784
xmlNanoHTTPFetch(argv[1], "-", &contentType);
1785
if (contentType != NULL) xmlFree(contentType);
1787
xmlGenericError(xmlGenericErrorContext,
1788
"%s: minimal HTTP GET implementation\n", argv[0]);
1789
xmlGenericError(xmlGenericErrorContext,
1790
"\tusage %s [ URL [ filename ] ]\n", argv[0]);
1792
xmlNanoHTTPCleanup();
1796
#endif /* STANDALONE */
1797
#else /* !LIBXML_HTTP_ENABLED */
1800
int main(int argc, char **argv) {
1801
xmlGenericError(xmlGenericErrorContext,
1802
"%s : HTTP support not compiled in\n", argv[0]);
1805
#endif /* STANDALONE */
1806
#endif /* LIBXML_HTTP_ENABLED */
1807
#define bottom_nanohttp
1808
#include "elfgcchack.h"