📄 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@w3.org */ /* TODO add compression support, Send the Accept- , and decompress on the fly with ZLIB if found at compile-time */#include "global.h"#ifdef WIN32#define INCLUDE_WINSOCK#include "win32config.h"#else#include "config.h"#endif#include "xmlversion.h"#ifdef LIBXML_HTTP_ENABLED#include <stdio.h>#include <string.h>#ifdef HAVE_STDLIB_H#include <stdlib.h>#endif#ifdef HAVE_UNISTD_H#include <unistd.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_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#include <libxml/xmlmemory.h>#include <libxml/nanohttp.h>#ifdef STANDALONE#define DEBUG_HTTP#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 */ int 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 */ char *contentType; /* the MIME type for the input */ char *location; /* the new URL in case of redirect */} xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;static int initialized = 0;static char *proxy = NULL; /* the proxy name if any */static int proxyPort; /* the proxy port if any *//** * xmlNanoHTTPInit: * * Initialize the HTTP protocol layer. * Currently it just checks for proxy informations */voidxmlNanoHTTPInit(void) { const char *env; if (initialized) return; 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;}/** * xmlNanoHTTPClenup: * * Cleanup the HTTP protocol layer. */voidxmlNanoHTTPCleanup(void) { if (proxy != NULL) xmlFree(proxy); 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 index = 0; 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[index] = 0; while (*cur != 0) { if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) { buf[index] = 0; ctxt->protocol = xmlMemStrdup(buf); index = 0; cur += 3; break; } buf[index++] = *cur++; } if (*cur == 0) return; buf[index] = 0; while (1) { if (cur[0] == ':') { buf[index] = 0; ctxt->hostname = xmlMemStrdup(buf); index = 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[index] = 0; ctxt->hostname = xmlMemStrdup(buf); index = 0; break; } buf[index++] = *cur++; } if (*cur == 0) ctxt->path = xmlMemStrdup("/"); else { index = 0; buf[index] = 0; while (*cur != 0) buf[index++] = *cur++; buf[index] = 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 index = 0; int port = 0; if (proxy != NULL) { xmlFree(proxy); proxy = NULL; } if (proxyPort != 0) { proxyPort = 0; }#ifdef DEBUG_HTTP if (URL == NULL) printf("Removing HTTP proxy info\n"); else printf("Using HTTP proxy %s\n", URL);#endif if (URL == NULL) return; buf[index] = 0; while (*cur != 0) { if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) { buf[index] = 0; index = 0; cur += 3; break; } buf[index++] = *cur++; } if (*cur == 0) return; buf[index] = 0; while (1) { if (cur[0] == ':') { buf[index] = 0; proxy = xmlMemStrdup(buf); index = 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[index] = 0; proxy = xmlMemStrdup(buf); index = 0; break; } buf[index++] = *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) return(NULL); memset(ret, 0, sizeof(xmlNanoHTTPCtxt)); ret->port = 80; ret->returnValue = 0; 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->location != NULL) xmlFree(ctxt->location); ctxt->state = XML_NANO_HTTP_NONE; if (ctxt->fd >= 0) close(ctxt->fd); ctxt->fd = -1; xmlFree(ctxt);}/** * xmlNanoHTTPSend: * @ctxt: an HTTP context * * Send the input needed to initiate the processing on the server side */static voidxmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt) { if (ctxt->state & XML_NANO_HTTP_WRITE) ctxt->last = write(ctxt->fd, ctxt->outptr, strlen(ctxt->outptr));}/** * 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 *) xmlMalloc(65000 * sizeof(char)); if (ctxt->in == NULL) { 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; ctxt->inlen *= 2; ctxt->in = (char *) xmlRealloc(ctxt->in, ctxt->inlen); if (ctxt->in == NULL) { 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 = read(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK); if (ctxt->last > 0) { ctxt->inptr += ctxt->last; return(ctxt->last); } if (ctxt->last == 0) { return(0); }#ifdef EWOULDBLOCK if ((ctxt->last == -1) && (errno != EWOULDBLOCK)) { return(0); }#endif tv.tv_sec=10; tv.tv_usec=0; FD_ZERO(&rfd); FD_SET(ctxt->fd, &rfd); if(select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1) return(0); } return(0);}/** * xmlNanoHTTPReadLine: * @ctxt: an HTTP context * * Read one line in the HTTP server output, usually for extracting * the HTTP protocol informations from the answer header. * * Returns a newly allocated string with a copy of the line, or NULL * which indicate the end of the input. */static char *xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) { char buf[4096]; char *bp=buf; while(bp - buf < 4095) { if(ctxt->inrptr == ctxt->inptr) { if (xmlNanoHTTPRecv(ctxt) == 0) { if (bp == buf) return(NULL); else *bp = 0; return(xmlMemStrdup(buf)); } } *bp = *ctxt->inrptr++; if(*bp == '\n') { *bp = 0; return(xmlMemStrdup(buf)); } if(*bp != '\r') bp++; } buf[4095] = 0; return(xmlMemStrdup(buf));}/** * xmlNanoHTTPScanAnswer: * @ctxt: an HTTP context * @line: an HTTP header line * * Try to extract useful informations from the server answer. * We currently parse and process: * - The HTTP revision/ return code * - The Content-Type * - The Location for redirrect processing. * * Returns -1 in case of failure, the file descriptor number otherwise */static voidxmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) { const char *cur = line; if (line == NULL) return; if (!strncmp(line, "HTTP/", 5)) { int version = 0; int ret = 0; cur += 5; while ((*cur >= '0') && (*cur <= '9')) { version *= 10; version += *cur - '0'; cur++; } if (*cur == '.') { cur++; if ((*cur >= '0') && (*cur <= '9')) { version *= 10; version += *cur - '0'; cur++; } while ((*cur >= '0') && (*cur <= '9')) cur++; } else version *= 10; if ((*cur != ' ') && (*cur != '\t')) return; while ((*cur == ' ') || (*cur == '\t')) cur++; if ((*cur < '0') || (*cur > '9')) return; while ((*cur >= '0') && (*cur <= '9')) { ret *= 10; ret += *cur - '0'; cur++; } if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return; ctxt->returnValue = ret; } else if (!strncmp(line, "Content-Type:", 13)) { cur += 13; while ((*cur == ' ') || (*cur == '\t')) cur++; if (ctxt->contentType != NULL) xmlFree(ctxt->contentType); ctxt->contentType = xmlMemStrdup(cur); } else if (!strncmp(line, "ContentType:", 12)) { cur += 12; if (ctxt->contentType != NULL) return; while ((*cur == ' ') || (*cur == '\t')) cur++; ctxt->contentType = xmlMemStrdup(cur); } else if (!strncmp(line, "content-type:", 13)) { cur += 13; if (ctxt->contentType != NULL) return; while ((*cur == ' ') || (*cur == '\t')) cur++; ctxt->contentType = xmlMemStrdup(cur); } else if (!strncmp(line, "contenttype:", 12)) { cur += 12; if (ctxt->contentType != NULL) return; while ((*cur == ' ') || (*cur == '\t')) cur++; ctxt->contentType = xmlMemStrdup(cur); } else if (!strncmp(line, "Location:", 9)) { cur += 9; while ((*cur == ' ') || (*cur == '\t')) cur++; if (ctxt->location != NULL) xmlFree(ctxt->location); ctxt->location = xmlMemStrdup(cur); } else if (!strncmp(line, "location:", 9)) { cur += 9; if (ctxt->location != NULL) return; while ((*cur == ' ') || (*cur == '\t')) cur++; ctxt->location = xmlMemStrdup(cur); }}/** * xmlNanoHTTPConnectAttempt: * @ia: an internet adress structure * @port: the port number * * Attempt a connection to the given IP:port endpoint. It forces * non-blocking semantic on the socket, and allow 60 seconds for * the host to answer. * * Returns -1 in case of failure, the file descriptor number otherwise */static intxmlNanoHTTPConnectAttempt(struct in_addr ia, int port){ int s=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); struct sockaddr_in sin; fd_set wfd; struct timeval tv; int status; if(s==-1) {#ifdef DEBUG_HTTP perror("socket");#endif return(-1); } #ifdef _WINSOCKAPI_ { long levents = FD_READ | FD_WRITE | FD_ACCEPT | FD_CONNECT | FD_CLOSE ; int rv = 0 ; u_long one = 1; status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0; }#else /* _WINSOCKAPI_ */#if defined(VMS) { int enable = 1; status = IOCTL(s, FIONBIO, &enable); }#else /* VMS */ if((status = fcntl(s, F_GETFL, 0)) != -1) {#ifdef O_NONBLOCK status |= O_NONBLOCK;#else /* O_NONBLOCK */#ifdef F_NDELAY status |= F_NDELAY;#endif /* F_NDELAY */#endif /* !O_NONBLOCK */ status = fcntl(s, F_SETFL, status); } if(status < 0) {#ifdef DEBUG_HTTP perror("nonblocking");#endif close(s); return(-1); }#endif /* !VMS */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -