📄 nanohttp.c
字号:
/* * 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 XML_SOCKLEN_T unsigned int#define SOCKET int#endif#ifdef __MINGW32__#define _WINSOCKAPI_#include <wsockcompat.h>#include <winsock2.h>#undef XML_SOCKLEN_T#define XML_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 XML_SOCKLEN_T#define XML_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 */static int xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len );/** * 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 && ((env[0] == '*') && (env[1] == 0))) 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) { xmlURIPtr uri; /* * Clear any existing data from the context */ 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; uri = xmlParseURI(URL); if (uri == NULL) return; if ((uri->scheme == NULL) || (uri->server == NULL)) { xmlFreeURI(uri); return; } ctxt->protocol = xmlMemStrdup(uri->scheme); ctxt->hostname = xmlMemStrdup(uri->server); if (uri->path != NULL) ctxt->path = xmlMemStrdup(uri->path); else ctxt->path = xmlMemStrdup("/"); if (uri->port != 0) ctxt->port = uri->port; xmlFreeURI(uri);}/** * 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) { xmlURIPtr uri; if (proxy != NULL) { xmlFree(proxy); proxy = NULL; } 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; uri = xmlParseURI(URL); if ((uri == NULL) || (uri->scheme == NULL) || (strcmp(uri->scheme, "http")) || (uri->server == NULL)) { __xmlIOErr(XML_FROM_HTTP, XML_HTTP_URL_SYNTAX, "Syntax Error\n"); if (uri != NULL) xmlFreeURI(uri); return; } proxy = xmlMemStrdup(uri->server); if (uri->port != 0) proxyPort = uri->port; xmlFreeURI(uri);}/** * 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;}/** * xmlNanoHTTPRecv: * @ctxt: an HTTP context * * Read information coming from the HTTP connection. * This is a blocking call (but it blocks in select(), not read()). * * Returns the number of byte read or -1 in case of error. */static intxmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt) { fd_set rfd; struct timeval tv; while (ctxt->state & XML_NANO_HTTP_READ) { if (ctxt->in == NULL) { ctxt->in = (char *) xmlMallocAtomic(65000 * sizeof(char)); if (ctxt->in == NULL) { xmlHTTPErrMemory("allocating input"); ctxt->last = -1; return(-1); } ctxt->inlen = 65000; ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in; } if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) { int delta = ctxt->inrptr - ctxt->in; int len = ctxt->inptr - ctxt->inrptr; memmove(ctxt->in, ctxt->inrptr, len); ctxt->inrptr -= delta; ctxt->content -= delta; ctxt->inptr -= delta; } if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) { 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(tmp_ptr, ctxt->inlen); if (ctxt->in == NULL) { xmlHTTPErrMemory("allocating input buffer"); xmlFree( tmp_ptr ); ctxt->last = -1; return(-1); } ctxt->inptr = ctxt->in + d_inptr; ctxt->content = ctxt->in + d_content; ctxt->inrptr = ctxt->in + d_inrptr; } ctxt->last = recv(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK, 0); if (ctxt->last > 0) { ctxt->inptr += ctxt->last; return(ctxt->last); } if (ctxt->last == 0) { return(0); } if (ctxt->last == -1) { switch (socket_errno()) { case EINPROGRESS: case EWOULDBLOCK:#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK case EAGAIN:#endif break; case ECONNRESET: case ESHUTDOWN: return ( 0 ); default: __xmlIOErr(XML_FROM_HTTP, 0, "recv failed\n"); return(-1); } } tv.tv_sec = timeout; tv.tv_usec = 0; FD_ZERO(&rfd); FD_SET(ctxt->fd, &rfd); if ( (select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1)#if defined(EINTR) && (errno != EINTR)#endif ) return(0); } return(0);}/**
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -