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 + -
显示快捷键?