📄 nanohttp.c.svn-base
字号:
/* * nanohttp.c: minimalist HTTP GET implementation to fetch external subsets. * focuses on size, streamability, reentrancy and portability * * This is clearly not a general purpose HTTP implementation * If you look for one, check: * http://www.w3.org/Library/ * * See Copyright for the status of this software. * * daniel@veillard.com */ /* TODO add compression support, Send the Accept- , and decompress on the fly with ZLIB if found at compile-time */#define NEED_SOCKETS#define IN_LIBXML#include "libxml.h"#ifdef LIBXML_HTTP_ENABLED#include <string.h>#ifdef HAVE_STDLIB_H#include <stdlib.h>#endif#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#ifdef HAVE_SYS_TYPES_H#include <sys/types.h>#endif#ifdef HAVE_SYS_SOCKET_H#include <sys/socket.h>#endif#ifdef HAVE_NETINET_IN_H#include <netinet/in.h>#endif#ifdef HAVE_ARPA_INET_H#include <arpa/inet.h>#endif#ifdef HAVE_NETDB_H#include <netdb.h>#endif#ifdef HAVE_RESOLV_H#ifdef HAVE_ARPA_NAMESER_H#include <arpa/nameser.h>#endif#include <resolv.h>#endif#ifdef HAVE_FCNTL_H#include <fcntl.h> #endif#ifdef HAVE_ERRNO_H#include <errno.h>#endif#ifdef HAVE_SYS_TIME_H#include <sys/time.h>#endif#ifdef HAVE_SYS_SELECT_H#include <sys/select.h>#endif#ifdef HAVE_STRINGS_H#include <strings.h>#endif#ifdef SUPPORT_IP6#include <resolv.h>#endif#ifdef VMS#include <stropts>#define SOCKLEN_T unsigned int#define SOCKET int#endif#ifdef __MINGW32__#define _WINSOCKAPI_#include <wsockcompat.h>#include <winsock2.h>#undef SOCKLEN_T#define SOCKLEN_T unsigned int#endif#include <libxml/globals.h>#include <libxml/xmlerror.h>#include <libxml/xmlmemory.h>#include <libxml/parser.h> /* for xmlStr(n)casecmp() */#include <libxml/nanohttp.h>#include <libxml/globals.h>#include <libxml/uri.h>/** * A couple portability macros */#ifndef _WINSOCKAPI_#ifndef __BEOS__#define closesocket(s) close(s)#endif#define SOCKET int#endif#ifdef __BEOS__#ifndef PF_INET#define PF_INET AF_INET#endif#endif#ifndef SOCKLEN_T#define SOCKLEN_T unsigned int#endif#ifndef SOCKET#define SOCKET int#endif#ifdef STANDALONE#define DEBUG_HTTP#define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)#define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b)#endif#define XML_NANO_HTTP_MAX_REDIR 10#define XML_NANO_HTTP_CHUNK 4096#define XML_NANO_HTTP_CLOSED 0#define XML_NANO_HTTP_WRITE 1#define XML_NANO_HTTP_READ 2#define XML_NANO_HTTP_NONE 4typedef struct xmlNanoHTTPCtxt { char *protocol; /* the protocol name */ char *hostname; /* the host name */ int port; /* the port */ char *path; /* the path within the URL */ SOCKET fd; /* the file descriptor for the socket */ int state; /* WRITE / READ / CLOSED */ char *out; /* buffer sent (zero terminated) */ char *outptr; /* index within the buffer sent */ char *in; /* the receiving buffer */ char *content; /* the start of the content */ char *inptr; /* the next byte to read from network */ char *inrptr; /* the next byte to give back to the client */ 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 */ char *encoding; /* encoding extracted from the contentType */ char *mimeType; /* Mime-Type extracted from the contentType */} xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;static int initialized = 0;static char *proxy = NULL; /* the proxy name if any */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 );/** * xmlHTTPErrMemory: * @extra: extra informations * * Handle an out of memory condition */static voidxmlHTTPErrMemory(const char *extra){ __xmlSimpleError(XML_FROM_HTTP, XML_ERR_NO_MEMORY, NULL, NULL, extra);}/** * A portability function */static int socket_errno(void) {#ifdef _WINSOCKAPI_ return(WSAGetLastError());#else return(errno);#endif}#ifdef SUPPORT_IP6staticint have_ipv6(void) { int s; s = socket (AF_INET6, SOCK_STREAM, 0); if (s != -1) { close (s); return (1); } return (0);}#endif/** * xmlNanoHTTPInit: * * Initialize the HTTP protocol layer. * Currently it just checks for proxy informations */voidxmlNanoHTTPInit(void) { const char *env;#ifdef _WINSOCKAPI_ WSADATA wsaData; #endif if (initialized) return;#ifdef _WINSOCKAPI_ if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) return;#endif if (proxy == NULL) { proxyPort = 80; env = getenv("no_proxy"); if (env != NULL) goto done; env = getenv("http_proxy"); if (env != NULL) { xmlNanoHTTPScanProxy(env); goto done; } env = getenv("HTTP_PROXY"); if (env != NULL) { xmlNanoHTTPScanProxy(env); goto done; } }done: initialized = 1;}/** * xmlNanoHTTPCleanup: * * Cleanup the HTTP protocol layer. */voidxmlNanoHTTPCleanup(void) { if (proxy != NULL) xmlFree(proxy);#ifdef _WINSOCKAPI_ if (initialized) WSACleanup();#endif initialized = 0; return;}/** * xmlNanoHTTPScanURL: * @ctxt: an HTTP context * @URL: The URL used to initialize the context * * (Re)Initialize an HTTP context by parsing the URL and finding * the protocol host port and path it indicates. */static voidxmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) { const char *cur = URL; char buf[4096]; int indx = 0; const int indxMax = 4096 - 1; int port = 0; if (ctxt->protocol != NULL) { xmlFree(ctxt->protocol); ctxt->protocol = NULL; } if (ctxt->hostname != NULL) { xmlFree(ctxt->hostname); ctxt->hostname = NULL; } if (ctxt->path != NULL) { xmlFree(ctxt->path); ctxt->path = NULL; } if (URL == NULL) return; buf[indx] = 0; while ((*cur != 0) && (indx < indxMax)) { if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) { buf[indx] = 0; ctxt->protocol = xmlMemStrdup(buf); indx = 0; cur += 3; break; } buf[indx++] = *cur++; } if (*cur == 0) return; buf[indx] = 0; while (indx < indxMax) { if ((strchr (cur, '[') && !strchr (cur, ']')) || (!strchr (cur, '[') && strchr (cur, ']'))) { __xmlIOErr(XML_FROM_HTTP, XML_HTTP_URL_SYNTAX, "Syntax Error\n"); return; } if (cur[0] == '[') { cur++; while ((cur[0] != ']') && (indx < indxMax)) buf[indx++] = *cur++; if (!strchr (buf, ':')) { __xmlIOErr(XML_FROM_HTTP, XML_HTTP_USE_IP, "Use [IPv6]/IPv4 format\n"); return; } buf[indx] = 0; ctxt->hostname = xmlMemStrdup (buf); indx = 0; cur += 1; if (cur[0] == ':') { cur++; while (*cur >= '0' && *cur <= '9') { port *= 10; port += *cur - '0'; cur++; } if (port != 0) ctxt->port = port; while ((cur[0] != '/') && (*cur != 0)) cur++; } break; } else { if (cur[0] == ':') { buf[indx] = 0; ctxt->hostname = xmlMemStrdup (buf); indx = 0; cur += 1; while ((*cur >= '0') && (*cur <= '9')) { port *= 10; port += *cur - '0'; cur++; } if (port != 0) ctxt->port = port; while ((cur[0] != '/') && (*cur != 0)) cur++; break; } if ((*cur == '/') || (*cur == 0)) { buf[indx] = 0; ctxt->hostname = xmlMemStrdup (buf); indx = 0; break; } } buf[indx++] = *cur++; } if (*cur == 0) ctxt->path = xmlMemStrdup("/"); else { indx = 0; buf[indx] = 0; while ((*cur != 0) && (indx < indxMax)) buf[indx++] = *cur++; buf[indx] = 0; ctxt->path = xmlMemStrdup(buf); } }/** * xmlNanoHTTPScanProxy: * @URL: The proxy URL used to initialize the proxy context * * (Re)Initialize the HTTP Proxy context by parsing the URL and finding * the protocol host port it indicates. * Should be like http://myproxy/ or http://myproxy:3128/ * A NULL URL cleans up proxy informations. */voidxmlNanoHTTPScanProxy(const char *URL) { const char *cur = URL; char buf[4096]; int indx = 0; const int indxMax = 4096 - 1; int port = 0; if (proxy != NULL) { xmlFree(proxy); proxy = NULL; } if (proxyPort != 0) { proxyPort = 0; }#ifdef DEBUG_HTTP if (URL == NULL) xmlGenericError(xmlGenericErrorContext, "Removing HTTP proxy info\n"); else xmlGenericError(xmlGenericErrorContext, "Using HTTP proxy %s\n", URL);#endif if (URL == NULL) return; buf[indx] = 0; while ((*cur != 0) && (indx < indxMax)) { if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) { buf[indx] = 0; indx = 0; cur += 3; break; } buf[indx++] = *cur++; } if (*cur == 0) return; buf[indx] = 0; while (indx < indxMax) { if ((strchr (cur, '[') && !strchr (cur, ']')) || (!strchr (cur, '[') && strchr (cur, ']'))) { __xmlIOErr(XML_FROM_HTTP, XML_HTTP_URL_SYNTAX, "Syntax Error\n"); return; } if (cur[0] == '[') { cur++; while ((cur[0] != ']') && (indx < indxMax)) buf[indx++] = *cur++; if (!strchr (buf, ':')) { __xmlIOErr(XML_FROM_HTTP, XML_HTTP_USE_IP, "Use [IPv6]/IPv4 format\n"); return; } buf[indx] = 0; proxy = xmlMemStrdup (buf); indx = 0; cur += 1; if (cur[0] == ':') { cur++; while (*cur >= '0' && *cur <= '9') { port *= 10; port += *cur - '0'; cur++; } if (port != 0) proxyPort = port; while ((cur[0] != '/') && (*cur != 0)) cur ++; } break; } else { if (cur[0] == ':') { buf[indx] = 0; proxy = xmlMemStrdup (buf); indx = 0; cur += 1; while ((*cur >= '0') && (*cur <= '9')) { port *= 10; port += *cur - '0'; cur++; } if (port != 0) proxyPort = port; while ((cur[0] != '/') && (*cur != 0)) cur++; break; } if ((*cur == '/') || (*cur == 0)) { buf[indx] = 0; proxy = xmlMemStrdup (buf); indx = 0; break; } } buf[indx++] = *cur++; }}/** * xmlNanoHTTPNewCtxt: * @URL: The URL used to initialize the context * * Allocate and initialize a new HTTP context. * * Returns an HTTP context or NULL in case of error. */static xmlNanoHTTPCtxtPtrxmlNanoHTTPNewCtxt(const char *URL) { xmlNanoHTTPCtxtPtr ret; ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt)); if (ret == NULL) { xmlHTTPErrMemory("allocating context"); return(NULL); } memset(ret, 0, sizeof(xmlNanoHTTPCtxt)); ret->port = 80; ret->returnValue = 0; ret->fd = -1; ret->ContentLength = -1; xmlNanoHTTPScanURL(ret, URL); return(ret);}/** * xmlNanoHTTPFreeCtxt: * @ctxt: an HTTP context * * Frees the context after closing the connection. */static voidxmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) { if (ctxt == NULL) return; if (ctxt->hostname != NULL) xmlFree(ctxt->hostname); if (ctxt->protocol != NULL) xmlFree(ctxt->protocol); if (ctxt->path != NULL) xmlFree(ctxt->path); if (ctxt->out != NULL) xmlFree(ctxt->out); if (ctxt->in != NULL) xmlFree(ctxt->in); if (ctxt->contentType != NULL) xmlFree(ctxt->contentType); if (ctxt->encoding != NULL) xmlFree(ctxt->encoding); if (ctxt->mimeType != NULL) xmlFree(ctxt->mimeType); if (ctxt->location != NULL) xmlFree(ctxt->location); if (ctxt->authHeader != NULL) xmlFree(ctxt->authHeader); ctxt->state = XML_NANO_HTTP_NONE; if (ctxt->fd >= 0) closesocket(ctxt->fd); ctxt->fd = -1; xmlFree(ctxt);}/** * xmlNanoHTTPSend: * @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 intxmlNanoHTTPSend(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 ) && #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK ( socket_errno( ) != EAGAIN ) &&#endif ( socket_errno( ) != EWOULDBLOCK ) ) { __xmlIOErr(XML_FROM_HTTP, 0, "send failed\n"); 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 ); } } } return total_sent;}/**
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -