get rid of the readline and libhistory dependancies by default, release
* configure.in: get rid of the readline and libhistory
dependancies by default, release 2.4.1 with IA64 fix
* nanohttp.c tree.c xmlIO.c include/libxml/nanohttp.h
include/libxml/tree.h include/libxml/xmlIO.h: incorporated
John Kroll fixes to allow saving to HTTP via PUT (or
POST of needed).
* doc/html/*.html: regenerated the docs
Daniel
diff --git a/nanohttp.c b/nanohttp.c
index fb4fbd0..c29cc7a 100644
--- a/nanohttp.c
+++ b/nanohttp.c
@@ -63,6 +63,7 @@
#define SOCKET int
#endif
+#include <libxml/xmlerror.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h> /* for xmlStr(n)casecmp() */
#include <libxml/nanohttp.h>
@@ -75,6 +76,7 @@
#define SOCKET int
#endif
+
#ifdef STANDALONE
#define DEBUG_HTTP
#define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)
@@ -106,6 +108,7 @@
int inlen; /* len of the input buffer */
int last; /* return code for last operation */
int returnValue; /* the protocol return value */
+ int ContentLength; /* specified content length from HTTP header */
char *contentType; /* the MIME type for the input */
char *location; /* the new URL in case of redirect */
char *authHeader; /* contents of {WWW,Proxy}-Authenticate header */
@@ -116,6 +119,9 @@
static int proxyPort; /* the proxy port if any */
static unsigned int timeout = 60;/* the select() timeout in seconds */
+int xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len );
+int xmlNanoHTTPContentLength( void * ctx );
+
/**
* A portability function
*/
@@ -358,6 +364,7 @@
ret->port = 80;
ret->returnValue = 0;
ret->fd = -1;
+ ret->ContentLength = -1;
xmlNanoHTTPScanURL(ret, URL);
@@ -393,20 +400,52 @@
* @ctxt: an HTTP context
*
* Send the input needed to initiate the processing on the server side
+ * Returns number of bytes sent or -1 on error.
*/
-static void
-xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt) {
- if (ctxt->state & XML_NANO_HTTP_WRITE) {
- unsigned int total_sent = 0;
- while (total_sent <strlen(ctxt->outptr)) {
- unsigned int nsent = send(ctxt->fd, ctxt->outptr+total_sent,
- strlen(ctxt->outptr)-total_sent, 0);
+static int
+xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt, const char * xmt_ptr, int outlen) {
+
+ int total_sent = 0;
+
+ if ( (ctxt->state & XML_NANO_HTTP_WRITE) && (xmt_ptr != NULL ) ) {
+ while (total_sent < outlen) {
+ int nsent = send(ctxt->fd, xmt_ptr + total_sent,
+ outlen - total_sent, 0);
if (nsent>0)
total_sent += nsent;
+ else if ( ( nsent == -1 ) &&
+ ( socket_errno( ) != EAGAIN ) &&
+ ( socket_errno( ) != EWOULDBLOCK ) ) {
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPSend error: %s",
+ strerror( socket_errno( ) ) );
+
+ if ( total_sent == 0 )
+ total_sent = -1;
+ break;
+ }
+ else {
+ /*
+ ** No data sent
+ ** Since non-blocking sockets are used, wait for
+ ** socket to be writable or default timeout prior
+ ** to retrying.
+ */
+
+ struct timeval tv;
+ fd_set wfd;
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ FD_ZERO( &wfd );
+ FD_SET( ctxt->fd, &wfd );
+ (void)select( ctxt->fd + 1, NULL, &wfd, NULL, &tv );
+ }
}
- ctxt->last = total_sent;
}
+
+ return total_sent;
}
/**
@@ -430,6 +469,8 @@
ctxt->in = (char *) xmlMalloc(65000 * sizeof(char));
if (ctxt->in == NULL) {
ctxt->last = -1;
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPRecv: Error allocating input memory." );
return(-1);
}
ctxt->inlen = 65000;
@@ -448,10 +489,16 @@
int d_inptr = ctxt->inptr - ctxt->in;
int d_content = ctxt->content - ctxt->in;
int d_inrptr = ctxt->inrptr - ctxt->in;
+ char * tmp_ptr = ctxt->in;
ctxt->inlen *= 2;
- ctxt->in = (char *) xmlRealloc(ctxt->in, ctxt->inlen);
+ ctxt->in = (char *) xmlRealloc(tmp_ptr, ctxt->inlen);
if (ctxt->in == NULL) {
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPRecv: %s %d bytes.",
+ "Failed to realloc input buffer to",
+ ctxt->inlen );
+ xmlFree( tmp_ptr );
ctxt->last = -1;
return(-1);
}
@@ -475,8 +522,16 @@
case EAGAIN:
#endif
break;
+
+ case ECONNRESET:
+ case ESHUTDOWN:
+ return ( 0 );
+
default:
- return(0);
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPRecv: recv( ) failure - %s",
+ strerror( socket_errno( ) ) );
+ return(-1);
}
}
@@ -485,7 +540,7 @@
FD_ZERO(&rfd);
FD_SET(ctxt->fd, &rfd);
- if (select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1)
+ if ( (select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1) && (errno != EINTR) )
return(0);
}
return(0);
@@ -506,16 +561,20 @@
xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) {
char buf[4096];
char *bp = buf;
+ int rc;
while (bp - buf < 4095) {
if (ctxt->inrptr == ctxt->inptr) {
- if (xmlNanoHTTPRecv(ctxt) == 0) {
+ if ( (rc = xmlNanoHTTPRecv(ctxt)) == 0) {
if (bp == buf)
return(NULL);
else
*bp = 0;
return(xmlMemStrdup(buf));
}
+ else if ( rc == -1 ) {
+ return ( NULL );
+ }
}
*bp = *ctxt->inrptr++;
if (*bp == '\n') {
@@ -610,6 +669,9 @@
if (ctxt->authHeader != NULL)
xmlFree(ctxt->authHeader);
ctxt->authHeader = xmlMemStrdup(cur);
+ } else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Length:", 15) ) {
+ cur += 15;
+ ctxt->ContentLength = strtol( cur, NULL, 10 );
}
}
@@ -636,6 +698,10 @@
#ifdef DEBUG_HTTP
perror("socket");
#endif
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPConnectAttempt: %s - %s",
+ "socket creation failure",
+ strerror( socket_errno( ) ) );
return(-1);
}
@@ -666,20 +732,26 @@
#ifdef DEBUG_HTTP
perror("nonblocking");
#endif
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPConnectAttempt: %s - %s",
+ "error setting non-blocking IO",
+ strerror( socket_errno( ) ) );
closesocket(s);
return(-1);
}
#endif /* !VMS */
#endif /* !_WINSOCKAPI_ */
-
if ((connect(s, addr, sizeof(*addr))==-1)) {
switch (socket_errno()) {
case EINPROGRESS:
case EWOULDBLOCK:
break;
default:
- perror("connect");
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPConnectAttempt: %s - %s",
+ "error connecting to HTTP server",
+ strerror( socket_errno( ) ) );
closesocket(s);
return(-1);
}
@@ -695,13 +767,17 @@
{
case 0:
/* Time out */
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPConnectAttempt: %s",
+ "Connect attempt timed out." );
closesocket(s);
return(-1);
case -1:
/* Ermm.. ?? */
-#ifdef DEBUG_HTTP
- perror("select");
-#endif
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPConnectAttempt: %s - %s",
+ "Error connecting to host",
+ strerror( socket_errno( ) ) );
closesocket(s);
return(-1);
}
@@ -711,15 +787,27 @@
len = sizeof(status);
if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&status, &len) < 0 ) {
/* Solaris error code */
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPConnectAttempt: %s - %s",
+ "Error retrieving pending socket errors",
+ strerror( socket_errno( ) ) );
return (-1);
}
if ( status ) {
closesocket(s);
errno = status;
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPConnectAttempt: %s - %s",
+ "Error connecting to remote host",
+ strerror( status ) );
return (-1);
}
} else {
/* pbm */
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPConnectAttempt: %s\n",
+ "Select returned, but descriptor not set for connection.\n" );
+ closesocket(s);
return (-1);
}
@@ -759,9 +847,34 @@
h=gethostbyname(host);
if (h==NULL)
{
-#ifdef DEBUG_HTTP
- xmlGenericError(xmlGenericErrorContext,"unable to resolve '%s'.\n", host);
-#endif
+ const char * h_err_txt = "";
+ switch ( h_errno )
+ {
+ case HOST_NOT_FOUND:
+ h_err_txt = "Authoritive host not found";
+ break;
+
+ case TRY_AGAIN:
+ h_err_txt =
+ "Non-authoritive host not found or server failure.";
+ break;
+
+ case NO_RECOVERY:
+ h_err_txt =
+ "Non-recoverable errors: FORMERR, REFUSED, or NOTIMP.";
+ break;
+
+ case NO_ADDRESS:
+ h_err_txt = "Valid name, no data record of requested type.";
+ break;
+
+ default:
+ h_err_txt = "No error text defined.";
+ break;
+ }
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPConnectHost: %s '%s' - %s",
+ "Failed to resolve host", host, h_err_txt );
return(-1);
}
@@ -793,7 +906,7 @@
#ifdef DEBUG_HTTP
xmlGenericError(xmlGenericErrorContext,
- "unable to connect to '%s'.\n", host);
+ "xmlNanoHTTPConnectHost: unable to connect to '%s'.\n", host);
#endif
return(-1);
}
@@ -815,7 +928,7 @@
void*
xmlNanoHTTPOpen(const char *URL, char **contentType) {
if (contentType != NULL) *contentType = NULL;
- return(xmlNanoHTTPMethod(URL, NULL, NULL, contentType, NULL));
+ return(xmlNanoHTTPMethod(URL, NULL, NULL, contentType, NULL, 0));
}
/**
@@ -836,7 +949,7 @@
xmlNanoHTTPOpenRedir(const char *URL, char **contentType, char **redir) {
if (contentType != NULL) *contentType = NULL;
if (redir != NULL) *redir = NULL;
- return(xmlNanoHTTPMethodRedir(URL, NULL, NULL, contentType, redir, NULL));
+ return(xmlNanoHTTPMethodRedir(URL, NULL, NULL, contentType, redir, NULL,0));
}
/**
@@ -860,7 +973,7 @@
if (len <= 0) return(0);
while (ctxt->inptr - ctxt->inrptr < len) {
- if (xmlNanoHTTPRecv(ctxt) == 0) break;
+ if (xmlNanoHTTPRecv(ctxt) <= 0) break;
}
if (ctxt->inptr - ctxt->inrptr < len)
len = ctxt->inptr - ctxt->inrptr;
@@ -904,11 +1017,13 @@
void*
xmlNanoHTTPMethodRedir(const char *URL, const char *method, const char *input,
- char **contentType, char **redir, const char *headers) {
+ char **contentType, char **redir,
+ const char *headers, int ilen ) {
xmlNanoHTTPCtxtPtr ctxt;
char *bp, *p;
- int blen, ilen, ret;
+ int blen, ret;
int head;
+ int xmt_bytes;
int nbRedirects = 0;
char *redirURL = NULL;
@@ -923,12 +1038,28 @@
ctxt = xmlNanoHTTPNewCtxt(redirURL);
}
+ if ( ctxt == NULL ) {
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPMethodRedir: %s %s.",
+ "Unable to allocate HTTP context to URI",
+ ( ( redirURL == NULL ) ? URL : redirURL ) );
+ return ( NULL );
+ }
+
if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPMethodRedir: %s - %s.",
+ "Not a valid HTTP URI",
+ ( ( redirURL == NULL ) ? URL : redirURL ) );
xmlNanoHTTPFreeCtxt(ctxt);
if (redirURL != NULL) xmlFree(redirURL);
return(NULL);
}
if (ctxt->hostname == NULL) {
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPMethodRedir: %s - %s",
+ "Failed to identify host in URI",
+ ( ( redirURL == NULL ) ? URL : redirURL ) );
xmlNanoHTTPFreeCtxt(ctxt);
if (redirURL != NULL) xmlFree(redirURL);
return(NULL);
@@ -948,43 +1079,52 @@
}
ctxt->fd = ret;
- if (input != NULL) {
- ilen = strlen(input);
- blen += ilen + 32;
- }
- else
+ if (input == NULL)
ilen = 0;
+ else
+ blen += 36;
+
if (headers != NULL)
- blen += strlen(headers);
+ blen += strlen(headers) + 2;
if (contentType && *contentType)
blen += strlen(*contentType) + 16;
- blen += strlen(method) + strlen(ctxt->path) + 23;
+ blen += strlen(method) + strlen(ctxt->path) + 24;
bp = xmlMalloc(blen);
+ if ( bp == NULL ) {
+ xmlNanoHTTPFreeCtxt( ctxt );
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPMethodRedir: %s",
+ "Error allocating HTTP header buffer." );
+ return ( NULL );
+ }
+
+ p = bp;
+
if (proxy) {
if (ctxt->port != 80) {
- sprintf(bp, "%s http://%s:%d%s", method, ctxt->hostname,
- ctxt->port, ctxt->path);
+ p += sprintf( p, "%s http://%s:%d%s", method, ctxt->hostname,
+ ctxt->port, ctxt->path );
}
else
- sprintf(bp, "%s http://%s%s", method, ctxt->hostname, ctxt->path);
+ p += sprintf( p, "%s http://%s%s", method,
+ ctxt->hostname, ctxt->path);
}
else
- sprintf(bp, "%s %s", method, ctxt->path);
- p = bp + strlen(bp);
- sprintf(p, " HTTP/1.0\r\nHost: %s\r\n", ctxt->hostname);
- p += strlen(p);
- if (contentType != NULL && *contentType) {
- sprintf(p, "Content-Type: %s\r\n", *contentType);
- p += strlen(p);
- }
- if (headers != NULL) {
- strcpy(p, headers);
- p += strlen(p);
- }
+ p += sprintf( p, "%s %s", method, ctxt->path);
+
+ p += sprintf(p, " HTTP/1.0\r\nHost: %s\r\n", ctxt->hostname);
+
+ if (contentType != NULL && *contentType)
+ p += sprintf(p, "Content-Type: %s\r\n", *contentType);
+
+ if (headers != NULL)
+ p += sprintf( p, "%s", headers );
+
if (input != NULL)
- sprintf(p, "Content-Length: %d\r\n\r\n%s", ilen, input);
+ sprintf(p, "Content-Length: %d\r\n\r\n", ilen );
else
strcpy(p, "\r\n");
+
#ifdef DEBUG_HTTP
xmlGenericError(xmlGenericErrorContext,
"-> %s%s", proxy? "(Proxy) " : "", bp);
@@ -994,7 +1134,30 @@
#endif
ctxt->outptr = ctxt->out = bp;
ctxt->state = XML_NANO_HTTP_WRITE;
- xmlNanoHTTPSend(ctxt);
+ blen = strlen( ctxt->out );
+ xmt_bytes = xmlNanoHTTPSend(ctxt, ctxt->out, blen );
+#ifdef DEBUG_HTTP
+ if ( xmt_bytes != blen )
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
+ xmt_bytes, blen,
+ "bytes of HTTP headers sent to host",
+ ctxt->hostname );
+#endif
+
+ if ( input != NULL ) {
+ xmt_bytes = xmlNanoHTTPSend( ctxt, input, ilen );
+
+#ifdef DEBUG_HTTP
+ if ( xmt_bytes != ilen )
+ xmlGenericError( xmlGenericErrorContext,
+ "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
+ xmt_bytes, ilen,
+ "bytes of HTTP content sent to host",
+ ctxt->hostname );
+#endif
+ }
+
ctxt->state = XML_NANO_HTTP_READ;
head = 1;
@@ -1019,7 +1182,7 @@
xmlGenericError(xmlGenericErrorContext,
"\nRedirect to: %s\n", ctxt->location);
#endif
- while (xmlNanoHTTPRecv(ctxt)) ;
+ while ( xmlNanoHTTPRecv(ctxt) > 0 ) ;
if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
nbRedirects++;
if (redirURL != NULL)
@@ -1032,10 +1195,9 @@
if (redirURL != NULL) xmlFree(redirURL);
#ifdef DEBUG_HTTP
xmlGenericError(xmlGenericErrorContext,
- "Too many redirects, aborting ...\n");
+ "xmlNanoHTTPMethodRedir: Too many redirects, aborting ...\n");
#endif
return(NULL);
-
}
if (contentType != NULL) {
@@ -1086,9 +1248,9 @@
void*
xmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
- char **contentType, const char *headers) {
+ char **contentType, const char *headers, int ilen) {
return(xmlNanoHTTPMethodRedir(URL, method, input, contentType,
- NULL, headers));
+ NULL, headers, ilen));
}
/**
@@ -1106,8 +1268,8 @@
*/
int
xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
- void *ctxt;
- char buf[4096];
+ void *ctxt = NULL;
+ char *buf = NULL;
int fd;
int len;
@@ -1128,7 +1290,8 @@
}
}
- while ((len = xmlNanoHTTPRead(ctxt, buf, sizeof(buf))) > 0) {
+ xmlNanoHTTPFetchContent( ctxt, &buf, &len );
+ if ( len > 0 ) {
write(fd, buf, len);
}
@@ -1165,7 +1328,8 @@
}
}
- while ((len = xmlNanoHTTPRead(ctxt, buf, sizeof(buf))) > 0) {
+ xmlNanoHTTPFetchContent( ctxt, &buf, &len );
+ if ( len > 0 ) {
write(fd, buf, len);
}
@@ -1208,6 +1372,77 @@
return(ctxt->authHeader);
}
+/**
+ * xmlNanoHTTPContentLength
+ * @ctx: the HTTP context
+ *
+ * Return the specified content length from the HTTP header. Note that
+ * a value of -1 indicates that the content length element was not included in
+ * the response header.
+ */
+int
+xmlNanoHTTPContentLength( void * ctx ) {
+ xmlNanoHTTPCtxtPtr ctxt = ctx;
+
+ return ( ( ctxt == NULL ) ? -1 : ctxt->ContentLength );
+}
+
+/**
+ * xmlNanoHTTPFetchContent
+ * @ctx: the HTTP context
+ * @ptr: pointer to set to the content buffer.
+ * @len: integer pointer to hold the length of the content
+ *
+ * Returns 0 if all the content was read and available, returns
+ * -1 if received content length was less than specified or an error
+ * occurred.
+ */
+int
+xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len ) {
+ xmlNanoHTTPCtxtPtr ctxt = ctx;
+
+ int rc = 0;
+ int cur_lgth;
+ int rcvd_lgth;
+ int dummy_int;
+ char * dummy_ptr = NULL;
+
+ /* Dummy up return input parameters if not provided */
+
+ if ( len == NULL )
+ len = &dummy_int;
+
+ if ( ptr == NULL )
+ ptr = &dummy_ptr;
+
+ /* But can't work without the context pointer */
+
+ if ( ( ctxt == NULL ) || ( ctxt->content == NULL ) ) {
+ *len = 0;
+ *ptr = NULL;
+ return ( -1 );
+ }
+
+ rcvd_lgth = ctxt->inptr - ctxt->content;
+
+ while ( (cur_lgth = xmlNanoHTTPRecv( ctxt )) > 0 ) {
+
+ rcvd_lgth += cur_lgth;
+ if ( (ctxt->ContentLength > 0) && (rcvd_lgth >= ctxt->ContentLength) )
+ break;
+ }
+
+ *ptr = ctxt->content;
+ *len = rcvd_lgth;
+
+ if ( ( ctxt->ContentLength > 0 ) && ( rcvd_lgth < ctxt->ContentLength ) )
+ rc = -1;
+ else if ( rcvd_lgth == 0 )
+ rc = -1;
+
+ return ( rc );
+}
+
#ifdef STANDALONE
int main(int argc, char **argv) {
char *contentType = NULL;