httpread.c

来自「最新的Host AP 新添加了许多pcmcia 的驱动」· C语言 代码 · 共 859 行 · 第 1/2 页

C
859
字号
/** * httpread - Manage reading file(s) from HTTP/TCP socket * Author: Ted Merrill * Copyright 2008 Atheros Communications * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. * * The files are buffered via internal callbacks from eloop, then presented to * an application callback routine when completely read into memory. May also * be used if no file is expected but just to get the header, including HTTP * replies (e.g. HTTP/1.1 200 OK etc.). * * This does not attempt to be an optimally efficient implementation, but does * attempt to be of reasonably small size and memory consumption; assuming that * only small files are to be read. A maximum file size is provided by * application and enforced. * * It is assumed that the application does not expect any of the following: * -- transfer encoding other than chunked * -- trailer fields * It is assumed that, even if the other side requested that the connection be * kept open, that we will close it (thus HTTP messages sent by application * should have the connection closed field); this is allowed by HTTP/1.1 and * simplifies things for us. * * Other limitations: * -- HTTP header may not exceed a hard-coded size. * * Notes: * This code would be massively simpler without some of the new features of * HTTP/1.1, especially chunked data. */#include "includes.h"#include "common.h"#include "eloop.h"#include "httpread.h"/* Tunable parameters */#define HTTPREAD_READBUF_SIZE 1024      /* read in chunks of this size */#define HTTPREAD_HEADER_MAX_SIZE 4096   /* max allowed for headers */#define HTTPREAD_BODYBUF_DELTA 4096     /* increase allocation by this */#if 0/* httpread_debug -- set this global variable > 0 e.g. from debugger * to enable debugs (larger numbers for more debugs) * Make this a #define of 0 to eliminate the debugging code. */int httpread_debug = 99;#else#define httpread_debug 0        /* eliminates even the debugging code */#endif/* control instance -- actual definition (opaque to application) */struct httpread {	/* information from creation */	int sd;         /* descriptor of TCP socket to read from */	void (*cb)(struct httpread *handle, void *cookie,		    enum httpread_event e);  /* call on event */	void *cookie;   /* pass to callback */	int max_bytes;          /* maximum file size else abort it */	int timeout_seconds;            /* 0 or total duration timeout period */	/* dynamically used information follows */	int sd_registered;      /* nonzero if we need to unregister socket */	int to_registered;      /* nonzero if we need to unregister timeout */	int got_hdr;            /* nonzero when header is finalized */	char hdr[HTTPREAD_HEADER_MAX_SIZE+1];   /* headers stored here */	int hdr_nbytes;	enum httpread_hdr_type hdr_type;	int version;            /* 1 if we've seen 1.1 */	int reply_code;         /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */	int got_content_length; /* true if we know content length for sure */	int content_length;     /* body length,  iff got_content_length */	int chunked;            /* nonzero for chunked data */	char *uri;	int got_body;           /* nonzero when body is finalized */	char *body;	int body_nbytes;	int body_alloc_nbytes;  /* amount allocated */	int got_file;           /* here when we are done */	/* The following apply if data is chunked: */	int in_chunk_data;      /* 0=in/at header, 1=in the data or tail*/	int chunk_start;        /* offset in body of chunk hdr or data */	int chunk_size;         /* data of chunk (not hdr or ending CRLF)*/	int in_trailer;         /* in header fields after data (chunked only)*/	enum trailer_state {		trailer_line_begin = 0,		trailer_empty_cr,       /* empty line + CR */		trailer_nonempty,		trailer_nonempty_cr,	} trailer_state;};/* Check words for equality, where words consist of graphical characters * delimited by whitespace * Returns nonzero if "equal" doing case insensitive comparison. */static int word_eq(char *s1, char *s2){	int c1;	int c2;	int end1 = 0;	int end2 = 0;	for (;;) {		c1 = *s1++;		c2 = *s2++;		if (isalpha(c1) && isupper(c1))			c1 = tolower(c1);		if (isalpha(c2) && isupper(c2))			c2 = tolower(c2);		end1 = !isgraph(c1);		end2 = !isgraph(c2);		if (end1 || end2 || c1 != c2)			break;	}	return end1 && end2;  /* reached end of both words? */}/* convert hex to binary * Requires that c have been previously tested true with isxdigit(). */static int hex_value(int c){	if (isdigit(c))		return c - '0';	if (islower(c))		return 10 + c - 'a';	return 10 + c - 'A';}static void httpread_timeout_handler(void *eloop_data, void *user_ctx);/* httpread_destroy -- if h is non-NULL, clean up * This must eventually be called by the application following * call of the application's callback and may be called * earlier if desired. */void httpread_destroy(struct httpread *h){	if (httpread_debug >= 10)		wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h);	if (!h)		return;	if (h->to_registered)		eloop_cancel_timeout(httpread_timeout_handler, NULL, h);	h->to_registered = 0;	if (h->sd_registered)		eloop_unregister_sock(h->sd, EVENT_TYPE_READ);	h->sd_registered = 0;	os_free(h->body);	os_free(h->uri);	os_memset(h, 0, sizeof(*h));  /* aid debugging */	h->sd = -1;     /* aid debugging */	os_free(h);}/* httpread_timeout_handler -- called on excessive total duration */static void httpread_timeout_handler(void *eloop_data, void *user_ctx){	struct httpread *h = user_ctx;	wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);	h->to_registered = 0;   /* is self-cancelling */	(*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);}/* Analyze options only so far as is needed to correctly obtain the file. * The application can look at the raw header to find other options. */static int httpread_hdr_option_analyze(	struct httpread *h,	char *hbp       /* pointer to current line in header buffer */	){	if (word_eq(hbp, "CONTENT-LENGTH:")) {		while (isgraph(*hbp))			hbp++;		while (*hbp == ' ' || *hbp == '\t')			hbp++;		if (!isdigit(*hbp))			return -1;		h->content_length = atol(hbp);		h->got_content_length = 1;		return 0;	}	if (word_eq(hbp, "TRANSFER_ENCODING:")) {		while (isgraph(*hbp))			hbp++;		while (*hbp == ' ' || *hbp == '\t')			hbp++;		/* There should (?) be no encodings of interest		 * other than chunked...		 */		if (os_strncmp(hbp, "CHUNKED", 7)) {			h->chunked = 1;			h->in_chunk_data = 0;			/* ignore possible ;<parameters> */		}		return 0;	}	/* skip anything we don't know, which is a lot */	return 0;}static int httpread_hdr_analyze(struct httpread *h){	char *hbp = h->hdr;      /* pointer into h->hdr */	int standard_first_line = 1;	/* First line is special */	h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;	if (!isgraph(*hbp))		goto bad;	if (os_strncmp(hbp, "HTTP/", 5) == 0) {		h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;		standard_first_line = 0;		hbp += 5;		if (hbp[0] == '1' && hbp[1] == '.' &&		    isdigit(hbp[2]) && hbp[2] != '0')			h->version = 1;		while (isgraph(*hbp))			hbp++;		while (*hbp == ' ' || *hbp == '\t')			hbp++;		if (!isdigit(*hbp))			goto bad;		h->reply_code = atol(hbp);	} else if (word_eq(hbp, "GET"))		h->hdr_type = HTTPREAD_HDR_TYPE_GET;	else if (word_eq(hbp, "HEAD"))		h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;	else if (word_eq(hbp, "POST"))		h->hdr_type = HTTPREAD_HDR_TYPE_POST;	else if (word_eq(hbp, "PUT"))		h->hdr_type = HTTPREAD_HDR_TYPE_PUT;	else if (word_eq(hbp, "DELETE"))		h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;	else if (word_eq(hbp, "TRACE"))		h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;	else if (word_eq(hbp, "CONNECT"))		h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;	else if (word_eq(hbp, "NOTIFY"))		h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;	else if (word_eq(hbp, "M-SEARCH"))		h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;	else if (word_eq(hbp, "M-POST"))		h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;	else if (word_eq(hbp, "SUBSCRIBE"))		h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;	else if (word_eq(hbp, "UNSUBSCRIBE"))		h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;	else {	}	if (standard_first_line) {		char *rawuri;		char *uri;		/* skip type */		while (isgraph(*hbp))			hbp++;		while (*hbp == ' ' || *hbp == '\t')			hbp++;		/* parse uri.		 * Find length, allocate memory for translated		 * copy, then translate by changing %<hex><hex>		 * into represented value.		 */		rawuri = hbp;		while (isgraph(*hbp))			hbp++;		h->uri = os_malloc((hbp - rawuri) + 1);		if (h->uri == NULL)			goto bad;		uri = h->uri;		while (rawuri < hbp) {			int c = *rawuri;			if (c == '%' &&			    isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {				*uri++ = (hex_value(rawuri[1]) << 4) |					hex_value(rawuri[2]);				rawuri += 3;			} else {				*uri++ = c;				rawuri++;			}		}		*uri = 0;       /* null terminate */		while (isgraph(*hbp))			hbp++;		while (*hbp == ' ' || *hbp == '\t')			hbp++;		/* get version */		if (0 == strncmp(hbp, "HTTP/", 5)) {			hbp += 5;			if (hbp[0] == '1' && hbp[1] == '.' &&			    isdigit(hbp[2]) && hbp[2] != '0')				h->version = 1;		}	}	/* skip rest of line */	while (*hbp)		if (*hbp++ == '\n')			break;	/* Remainder of lines are options, in any order;	 * or empty line to terminate	 */	for (;;) {		/* Empty line to terminate */		if (hbp[0] == '\n' ||		    (hbp[0] == '\r' && hbp[1] == '\n'))			break;		if (!isgraph(*hbp))			goto bad;		if (httpread_hdr_option_analyze(h, hbp))			goto bad;		/* skip line */		while (*hbp)			if (*hbp++ == '\n')				break;	}	/* chunked overrides content-length always */	if (h->chunked)		h->got_content_length = 0;	/* For some types, we should not try to read a body	 * This is in addition to the application determining	 * that we should not read a body.	 */	switch (h->hdr_type) {	case HTTPREAD_HDR_TYPE_REPLY:		/* Some codes can have a body and some not.		 * For now, just assume that any other than 200		 * do not...		 */		if (h->reply_code != 200)			h->max_bytes = 0;		break;	case HTTPREAD_HDR_TYPE_GET:	case HTTPREAD_HDR_TYPE_HEAD:		/* in practice it appears that it is assumed		 * that GETs have a body length of 0... ?		 */		if (h->chunked == 0 && h->got_content_length == 0)			h->max_bytes = 0;		break;	case HTTPREAD_HDR_TYPE_POST:	case HTTPREAD_HDR_TYPE_PUT:	case HTTPREAD_HDR_TYPE_DELETE:	case HTTPREAD_HDR_TYPE_TRACE:	case HTTPREAD_HDR_TYPE_CONNECT:	case HTTPREAD_HDR_TYPE_NOTIFY:	case HTTPREAD_HDR_TYPE_M_SEARCH:	case HTTPREAD_HDR_TYPE_M_POST:	case HTTPREAD_HDR_TYPE_SUBSCRIBE:	case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:	default:		break;	}	return 0;bad:	/* Error */	return -1;}/* httpread_read_handler -- called when socket ready to read * * Note: any extra data we read past end of transmitted file is ignored; * if we were to support keeping connections open for multiple files then * this would have to be addressed. */static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx){	struct httpread *h = sock_ctx;	int nread;	char *rbp;      /* pointer into read buffer */	char *hbp;      /* pointer into header buffer */	char *bbp;      /* pointer into body buffer */	char readbuf[HTTPREAD_READBUF_SIZE];  /* temp use to read into */	if (httpread_debug >= 20)		wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h);	/* read some at a time, then search for the interal	 * boundaries between header and data and etc.	 */	nread = read(h->sd, readbuf, sizeof(readbuf));	if (nread < 0)		goto bad;	if (nread == 0) {		/* end of transmission... this may be normal		 * or may be an error... in some cases we can't		 * tell which so we must assume it is normal then.		 */		if (!h->got_hdr) {			/* Must at least have completed header */			wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);			goto bad;		}		if (h->chunked || h->got_content_length) {			/* Premature EOF; e.g. dropped connection */			wpa_printf(MSG_DEBUG,

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?