1e5b75505Sopenharmony_ci/* 2e5b75505Sopenharmony_ci * httpread - Manage reading file(s) from HTTP/TCP socket 3e5b75505Sopenharmony_ci * Author: Ted Merrill 4e5b75505Sopenharmony_ci * Copyright 2008 Atheros Communications 5e5b75505Sopenharmony_ci * 6e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license. 7e5b75505Sopenharmony_ci * See README for more details. 8e5b75505Sopenharmony_ci * 9e5b75505Sopenharmony_ci * The files are buffered via internal callbacks from eloop, then presented to 10e5b75505Sopenharmony_ci * an application callback routine when completely read into memory. May also 11e5b75505Sopenharmony_ci * be used if no file is expected but just to get the header, including HTTP 12e5b75505Sopenharmony_ci * replies (e.g. HTTP/1.1 200 OK etc.). 13e5b75505Sopenharmony_ci * 14e5b75505Sopenharmony_ci * This does not attempt to be an optimally efficient implementation, but does 15e5b75505Sopenharmony_ci * attempt to be of reasonably small size and memory consumption; assuming that 16e5b75505Sopenharmony_ci * only small files are to be read. A maximum file size is provided by 17e5b75505Sopenharmony_ci * application and enforced. 18e5b75505Sopenharmony_ci * 19e5b75505Sopenharmony_ci * It is assumed that the application does not expect any of the following: 20e5b75505Sopenharmony_ci * -- transfer encoding other than chunked 21e5b75505Sopenharmony_ci * -- trailer fields 22e5b75505Sopenharmony_ci * It is assumed that, even if the other side requested that the connection be 23e5b75505Sopenharmony_ci * kept open, that we will close it (thus HTTP messages sent by application 24e5b75505Sopenharmony_ci * should have the connection closed field); this is allowed by HTTP/1.1 and 25e5b75505Sopenharmony_ci * simplifies things for us. 26e5b75505Sopenharmony_ci * 27e5b75505Sopenharmony_ci * Other limitations: 28e5b75505Sopenharmony_ci * -- HTTP header may not exceed a hard-coded size. 29e5b75505Sopenharmony_ci * 30e5b75505Sopenharmony_ci * Notes: 31e5b75505Sopenharmony_ci * This code would be massively simpler without some of the new features of 32e5b75505Sopenharmony_ci * HTTP/1.1, especially chunked data. 33e5b75505Sopenharmony_ci */ 34e5b75505Sopenharmony_ci 35e5b75505Sopenharmony_ci#include "includes.h" 36e5b75505Sopenharmony_ci 37e5b75505Sopenharmony_ci#include "common.h" 38e5b75505Sopenharmony_ci#include "eloop.h" 39e5b75505Sopenharmony_ci#include "httpread.h" 40e5b75505Sopenharmony_ci 41e5b75505Sopenharmony_ci 42e5b75505Sopenharmony_ci/* Tunable parameters */ 43e5b75505Sopenharmony_ci#define HTTPREAD_READBUF_SIZE 1024 /* read in chunks of this size */ 44e5b75505Sopenharmony_ci#define HTTPREAD_HEADER_MAX_SIZE 4096 /* max allowed for headers */ 45e5b75505Sopenharmony_ci#define HTTPREAD_BODYBUF_DELTA 4096 /* increase allocation by this */ 46e5b75505Sopenharmony_ci 47e5b75505Sopenharmony_ci 48e5b75505Sopenharmony_ci/* control instance -- actual definition (opaque to application) 49e5b75505Sopenharmony_ci */ 50e5b75505Sopenharmony_cistruct httpread { 51e5b75505Sopenharmony_ci /* information from creation */ 52e5b75505Sopenharmony_ci int sd; /* descriptor of TCP socket to read from */ 53e5b75505Sopenharmony_ci void (*cb)(struct httpread *handle, void *cookie, 54e5b75505Sopenharmony_ci enum httpread_event e); /* call on event */ 55e5b75505Sopenharmony_ci void *cookie; /* pass to callback */ 56e5b75505Sopenharmony_ci int max_bytes; /* maximum file size else abort it */ 57e5b75505Sopenharmony_ci int timeout_seconds; /* 0 or total duration timeout period */ 58e5b75505Sopenharmony_ci 59e5b75505Sopenharmony_ci /* dynamically used information follows */ 60e5b75505Sopenharmony_ci 61e5b75505Sopenharmony_ci int got_hdr; /* nonzero when header is finalized */ 62e5b75505Sopenharmony_ci char hdr[HTTPREAD_HEADER_MAX_SIZE+1]; /* headers stored here */ 63e5b75505Sopenharmony_ci int hdr_nbytes; 64e5b75505Sopenharmony_ci 65e5b75505Sopenharmony_ci enum httpread_hdr_type hdr_type; 66e5b75505Sopenharmony_ci int version; /* 1 if we've seen 1.1 */ 67e5b75505Sopenharmony_ci int reply_code; /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */ 68e5b75505Sopenharmony_ci int got_content_length; /* true if we know content length for sure */ 69e5b75505Sopenharmony_ci int content_length; /* body length, iff got_content_length */ 70e5b75505Sopenharmony_ci int chunked; /* nonzero for chunked data */ 71e5b75505Sopenharmony_ci char *uri; 72e5b75505Sopenharmony_ci 73e5b75505Sopenharmony_ci int got_body; /* nonzero when body is finalized */ 74e5b75505Sopenharmony_ci char *body; 75e5b75505Sopenharmony_ci int body_nbytes; 76e5b75505Sopenharmony_ci int body_alloc_nbytes; /* amount allocated */ 77e5b75505Sopenharmony_ci 78e5b75505Sopenharmony_ci int got_file; /* here when we are done */ 79e5b75505Sopenharmony_ci 80e5b75505Sopenharmony_ci /* The following apply if data is chunked: */ 81e5b75505Sopenharmony_ci int in_chunk_data; /* 0=in/at header, 1=in the data or tail*/ 82e5b75505Sopenharmony_ci int chunk_start; /* offset in body of chunk hdr or data */ 83e5b75505Sopenharmony_ci int chunk_size; /* data of chunk (not hdr or ending CRLF)*/ 84e5b75505Sopenharmony_ci int in_trailer; /* in header fields after data (chunked only)*/ 85e5b75505Sopenharmony_ci enum trailer_state { 86e5b75505Sopenharmony_ci trailer_line_begin = 0, 87e5b75505Sopenharmony_ci trailer_empty_cr, /* empty line + CR */ 88e5b75505Sopenharmony_ci trailer_nonempty, 89e5b75505Sopenharmony_ci trailer_nonempty_cr, 90e5b75505Sopenharmony_ci } trailer_state; 91e5b75505Sopenharmony_ci}; 92e5b75505Sopenharmony_ci 93e5b75505Sopenharmony_ci 94e5b75505Sopenharmony_ci/* Check words for equality, where words consist of graphical characters 95e5b75505Sopenharmony_ci * delimited by whitespace 96e5b75505Sopenharmony_ci * Returns nonzero if "equal" doing case insensitive comparison. 97e5b75505Sopenharmony_ci */ 98e5b75505Sopenharmony_cistatic int word_eq(char *s1, char *s2) 99e5b75505Sopenharmony_ci{ 100e5b75505Sopenharmony_ci int c1; 101e5b75505Sopenharmony_ci int c2; 102e5b75505Sopenharmony_ci int end1 = 0; 103e5b75505Sopenharmony_ci int end2 = 0; 104e5b75505Sopenharmony_ci for (;;) { 105e5b75505Sopenharmony_ci c1 = *s1++; 106e5b75505Sopenharmony_ci c2 = *s2++; 107e5b75505Sopenharmony_ci if (isalpha(c1) && isupper(c1)) 108e5b75505Sopenharmony_ci c1 = tolower(c1); 109e5b75505Sopenharmony_ci if (isalpha(c2) && isupper(c2)) 110e5b75505Sopenharmony_ci c2 = tolower(c2); 111e5b75505Sopenharmony_ci end1 = !isgraph(c1); 112e5b75505Sopenharmony_ci end2 = !isgraph(c2); 113e5b75505Sopenharmony_ci if (end1 || end2 || c1 != c2) 114e5b75505Sopenharmony_ci break; 115e5b75505Sopenharmony_ci } 116e5b75505Sopenharmony_ci return end1 && end2; /* reached end of both words? */ 117e5b75505Sopenharmony_ci} 118e5b75505Sopenharmony_ci 119e5b75505Sopenharmony_ci 120e5b75505Sopenharmony_cistatic void httpread_timeout_handler(void *eloop_data, void *user_ctx); 121e5b75505Sopenharmony_ci 122e5b75505Sopenharmony_ci/* httpread_destroy -- if h is non-NULL, clean up 123e5b75505Sopenharmony_ci * This must eventually be called by the application following 124e5b75505Sopenharmony_ci * call of the application's callback and may be called 125e5b75505Sopenharmony_ci * earlier if desired. 126e5b75505Sopenharmony_ci */ 127e5b75505Sopenharmony_civoid httpread_destroy(struct httpread *h) 128e5b75505Sopenharmony_ci{ 129e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "httpread_destroy(%p)", h); 130e5b75505Sopenharmony_ci if (!h) 131e5b75505Sopenharmony_ci return; 132e5b75505Sopenharmony_ci 133e5b75505Sopenharmony_ci eloop_cancel_timeout(httpread_timeout_handler, NULL, h); 134e5b75505Sopenharmony_ci eloop_unregister_sock(h->sd, EVENT_TYPE_READ); 135e5b75505Sopenharmony_ci os_free(h->body); 136e5b75505Sopenharmony_ci os_free(h->uri); 137e5b75505Sopenharmony_ci os_memset(h, 0, sizeof(*h)); /* aid debugging */ 138e5b75505Sopenharmony_ci h->sd = -1; /* aid debugging */ 139e5b75505Sopenharmony_ci os_free(h); 140e5b75505Sopenharmony_ci} 141e5b75505Sopenharmony_ci 142e5b75505Sopenharmony_ci 143e5b75505Sopenharmony_ci/* httpread_timeout_handler -- called on excessive total duration 144e5b75505Sopenharmony_ci */ 145e5b75505Sopenharmony_cistatic void httpread_timeout_handler(void *eloop_data, void *user_ctx) 146e5b75505Sopenharmony_ci{ 147e5b75505Sopenharmony_ci struct httpread *h = user_ctx; 148e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h); 149e5b75505Sopenharmony_ci (*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT); 150e5b75505Sopenharmony_ci} 151e5b75505Sopenharmony_ci 152e5b75505Sopenharmony_ci 153e5b75505Sopenharmony_ci/* Analyze options only so far as is needed to correctly obtain the file. 154e5b75505Sopenharmony_ci * The application can look at the raw header to find other options. 155e5b75505Sopenharmony_ci */ 156e5b75505Sopenharmony_cistatic int httpread_hdr_option_analyze( 157e5b75505Sopenharmony_ci struct httpread *h, 158e5b75505Sopenharmony_ci char *hbp /* pointer to current line in header buffer */ 159e5b75505Sopenharmony_ci ) 160e5b75505Sopenharmony_ci{ 161e5b75505Sopenharmony_ci if (word_eq(hbp, "CONTENT-LENGTH:")) { 162e5b75505Sopenharmony_ci while (isgraph(*hbp)) 163e5b75505Sopenharmony_ci hbp++; 164e5b75505Sopenharmony_ci while (*hbp == ' ' || *hbp == '\t') 165e5b75505Sopenharmony_ci hbp++; 166e5b75505Sopenharmony_ci if (!isdigit(*hbp)) 167e5b75505Sopenharmony_ci return -1; 168e5b75505Sopenharmony_ci h->content_length = atol(hbp); 169e5b75505Sopenharmony_ci if (h->content_length < 0 || h->content_length > h->max_bytes) { 170e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 171e5b75505Sopenharmony_ci "httpread: Unacceptable Content-Length %d", 172e5b75505Sopenharmony_ci h->content_length); 173e5b75505Sopenharmony_ci return -1; 174e5b75505Sopenharmony_ci } 175e5b75505Sopenharmony_ci h->got_content_length = 1; 176e5b75505Sopenharmony_ci return 0; 177e5b75505Sopenharmony_ci } 178e5b75505Sopenharmony_ci if (word_eq(hbp, "TRANSFER_ENCODING:") || 179e5b75505Sopenharmony_ci word_eq(hbp, "TRANSFER-ENCODING:")) { 180e5b75505Sopenharmony_ci while (isgraph(*hbp)) 181e5b75505Sopenharmony_ci hbp++; 182e5b75505Sopenharmony_ci while (*hbp == ' ' || *hbp == '\t') 183e5b75505Sopenharmony_ci hbp++; 184e5b75505Sopenharmony_ci /* There should (?) be no encodings of interest 185e5b75505Sopenharmony_ci * other than chunked... 186e5b75505Sopenharmony_ci */ 187e5b75505Sopenharmony_ci if (word_eq(hbp, "CHUNKED")) { 188e5b75505Sopenharmony_ci h->chunked = 1; 189e5b75505Sopenharmony_ci h->in_chunk_data = 0; 190e5b75505Sopenharmony_ci /* ignore possible ;<parameters> */ 191e5b75505Sopenharmony_ci } 192e5b75505Sopenharmony_ci return 0; 193e5b75505Sopenharmony_ci } 194e5b75505Sopenharmony_ci /* skip anything we don't know, which is a lot */ 195e5b75505Sopenharmony_ci return 0; 196e5b75505Sopenharmony_ci} 197e5b75505Sopenharmony_ci 198e5b75505Sopenharmony_ci 199e5b75505Sopenharmony_cistatic int httpread_hdr_analyze(struct httpread *h) 200e5b75505Sopenharmony_ci{ 201e5b75505Sopenharmony_ci char *hbp = h->hdr; /* pointer into h->hdr */ 202e5b75505Sopenharmony_ci int standard_first_line = 1; 203e5b75505Sopenharmony_ci 204e5b75505Sopenharmony_ci /* First line is special */ 205e5b75505Sopenharmony_ci h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN; 206e5b75505Sopenharmony_ci if (!isgraph(*hbp)) 207e5b75505Sopenharmony_ci goto bad; 208e5b75505Sopenharmony_ci if (os_strncmp(hbp, "HTTP/", 5) == 0) { 209e5b75505Sopenharmony_ci h->hdr_type = HTTPREAD_HDR_TYPE_REPLY; 210e5b75505Sopenharmony_ci standard_first_line = 0; 211e5b75505Sopenharmony_ci hbp += 5; 212e5b75505Sopenharmony_ci if (hbp[0] == '1' && hbp[1] == '.' && 213e5b75505Sopenharmony_ci isdigit(hbp[2]) && hbp[2] != '0') 214e5b75505Sopenharmony_ci h->version = 1; 215e5b75505Sopenharmony_ci while (isgraph(*hbp)) 216e5b75505Sopenharmony_ci hbp++; 217e5b75505Sopenharmony_ci while (*hbp == ' ' || *hbp == '\t') 218e5b75505Sopenharmony_ci hbp++; 219e5b75505Sopenharmony_ci if (!isdigit(*hbp)) 220e5b75505Sopenharmony_ci goto bad; 221e5b75505Sopenharmony_ci h->reply_code = atol(hbp); 222e5b75505Sopenharmony_ci } else if (word_eq(hbp, "GET")) 223e5b75505Sopenharmony_ci h->hdr_type = HTTPREAD_HDR_TYPE_GET; 224e5b75505Sopenharmony_ci else if (word_eq(hbp, "HEAD")) 225e5b75505Sopenharmony_ci h->hdr_type = HTTPREAD_HDR_TYPE_HEAD; 226e5b75505Sopenharmony_ci else if (word_eq(hbp, "POST")) 227e5b75505Sopenharmony_ci h->hdr_type = HTTPREAD_HDR_TYPE_POST; 228e5b75505Sopenharmony_ci else if (word_eq(hbp, "PUT")) 229e5b75505Sopenharmony_ci h->hdr_type = HTTPREAD_HDR_TYPE_PUT; 230e5b75505Sopenharmony_ci else if (word_eq(hbp, "DELETE")) 231e5b75505Sopenharmony_ci h->hdr_type = HTTPREAD_HDR_TYPE_DELETE; 232e5b75505Sopenharmony_ci else if (word_eq(hbp, "TRACE")) 233e5b75505Sopenharmony_ci h->hdr_type = HTTPREAD_HDR_TYPE_TRACE; 234e5b75505Sopenharmony_ci else if (word_eq(hbp, "CONNECT")) 235e5b75505Sopenharmony_ci h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT; 236e5b75505Sopenharmony_ci else if (word_eq(hbp, "NOTIFY")) 237e5b75505Sopenharmony_ci h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY; 238e5b75505Sopenharmony_ci else if (word_eq(hbp, "M-SEARCH")) 239e5b75505Sopenharmony_ci h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH; 240e5b75505Sopenharmony_ci else if (word_eq(hbp, "M-POST")) 241e5b75505Sopenharmony_ci h->hdr_type = HTTPREAD_HDR_TYPE_M_POST; 242e5b75505Sopenharmony_ci else if (word_eq(hbp, "SUBSCRIBE")) 243e5b75505Sopenharmony_ci h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE; 244e5b75505Sopenharmony_ci else if (word_eq(hbp, "UNSUBSCRIBE")) 245e5b75505Sopenharmony_ci h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE; 246e5b75505Sopenharmony_ci else { 247e5b75505Sopenharmony_ci } 248e5b75505Sopenharmony_ci 249e5b75505Sopenharmony_ci if (standard_first_line) { 250e5b75505Sopenharmony_ci char *rawuri; 251e5b75505Sopenharmony_ci char *uri; 252e5b75505Sopenharmony_ci /* skip type */ 253e5b75505Sopenharmony_ci while (isgraph(*hbp)) 254e5b75505Sopenharmony_ci hbp++; 255e5b75505Sopenharmony_ci while (*hbp == ' ' || *hbp == '\t') 256e5b75505Sopenharmony_ci hbp++; 257e5b75505Sopenharmony_ci /* parse uri. 258e5b75505Sopenharmony_ci * Find length, allocate memory for translated 259e5b75505Sopenharmony_ci * copy, then translate by changing %<hex><hex> 260e5b75505Sopenharmony_ci * into represented value. 261e5b75505Sopenharmony_ci */ 262e5b75505Sopenharmony_ci rawuri = hbp; 263e5b75505Sopenharmony_ci while (isgraph(*hbp)) 264e5b75505Sopenharmony_ci hbp++; 265e5b75505Sopenharmony_ci h->uri = os_malloc((hbp - rawuri) + 1); 266e5b75505Sopenharmony_ci if (h->uri == NULL) 267e5b75505Sopenharmony_ci goto bad; 268e5b75505Sopenharmony_ci uri = h->uri; 269e5b75505Sopenharmony_ci while (rawuri < hbp) { 270e5b75505Sopenharmony_ci int c = *rawuri; 271e5b75505Sopenharmony_ci if (c == '%' && 272e5b75505Sopenharmony_ci isxdigit(rawuri[1]) && isxdigit(rawuri[2])) { 273e5b75505Sopenharmony_ci *uri++ = hex2byte(rawuri + 1); 274e5b75505Sopenharmony_ci rawuri += 3; 275e5b75505Sopenharmony_ci } else { 276e5b75505Sopenharmony_ci *uri++ = c; 277e5b75505Sopenharmony_ci rawuri++; 278e5b75505Sopenharmony_ci } 279e5b75505Sopenharmony_ci } 280e5b75505Sopenharmony_ci *uri = 0; /* null terminate */ 281e5b75505Sopenharmony_ci while (*hbp == ' ' || *hbp == '\t') 282e5b75505Sopenharmony_ci hbp++; 283e5b75505Sopenharmony_ci /* get version */ 284e5b75505Sopenharmony_ci if (0 == strncmp(hbp, "HTTP/", 5)) { 285e5b75505Sopenharmony_ci hbp += 5; 286e5b75505Sopenharmony_ci if (hbp[0] == '1' && hbp[1] == '.' && 287e5b75505Sopenharmony_ci isdigit(hbp[2]) && hbp[2] != '0') 288e5b75505Sopenharmony_ci h->version = 1; 289e5b75505Sopenharmony_ci } 290e5b75505Sopenharmony_ci } 291e5b75505Sopenharmony_ci /* skip rest of line */ 292e5b75505Sopenharmony_ci while (*hbp) 293e5b75505Sopenharmony_ci if (*hbp++ == '\n') 294e5b75505Sopenharmony_ci break; 295e5b75505Sopenharmony_ci 296e5b75505Sopenharmony_ci /* Remainder of lines are options, in any order; 297e5b75505Sopenharmony_ci * or empty line to terminate 298e5b75505Sopenharmony_ci */ 299e5b75505Sopenharmony_ci for (;;) { 300e5b75505Sopenharmony_ci /* Empty line to terminate */ 301e5b75505Sopenharmony_ci if (hbp[0] == '\n' || 302e5b75505Sopenharmony_ci (hbp[0] == '\r' && hbp[1] == '\n')) 303e5b75505Sopenharmony_ci break; 304e5b75505Sopenharmony_ci if (!isgraph(*hbp)) 305e5b75505Sopenharmony_ci goto bad; 306e5b75505Sopenharmony_ci if (httpread_hdr_option_analyze(h, hbp)) 307e5b75505Sopenharmony_ci goto bad; 308e5b75505Sopenharmony_ci /* skip line */ 309e5b75505Sopenharmony_ci while (*hbp) 310e5b75505Sopenharmony_ci if (*hbp++ == '\n') 311e5b75505Sopenharmony_ci break; 312e5b75505Sopenharmony_ci } 313e5b75505Sopenharmony_ci 314e5b75505Sopenharmony_ci /* chunked overrides content-length always */ 315e5b75505Sopenharmony_ci if (h->chunked) 316e5b75505Sopenharmony_ci h->got_content_length = 0; 317e5b75505Sopenharmony_ci 318e5b75505Sopenharmony_ci /* For some types, we should not try to read a body 319e5b75505Sopenharmony_ci * This is in addition to the application determining 320e5b75505Sopenharmony_ci * that we should not read a body. 321e5b75505Sopenharmony_ci */ 322e5b75505Sopenharmony_ci switch (h->hdr_type) { 323e5b75505Sopenharmony_ci case HTTPREAD_HDR_TYPE_REPLY: 324e5b75505Sopenharmony_ci /* Some codes can have a body and some not. 325e5b75505Sopenharmony_ci * For now, just assume that any other than 200 326e5b75505Sopenharmony_ci * do not... 327e5b75505Sopenharmony_ci */ 328e5b75505Sopenharmony_ci if (h->reply_code != 200) 329e5b75505Sopenharmony_ci h->max_bytes = 0; 330e5b75505Sopenharmony_ci break; 331e5b75505Sopenharmony_ci case HTTPREAD_HDR_TYPE_GET: 332e5b75505Sopenharmony_ci case HTTPREAD_HDR_TYPE_HEAD: 333e5b75505Sopenharmony_ci /* in practice it appears that it is assumed 334e5b75505Sopenharmony_ci * that GETs have a body length of 0... ? 335e5b75505Sopenharmony_ci */ 336e5b75505Sopenharmony_ci if (h->chunked == 0 && h->got_content_length == 0) 337e5b75505Sopenharmony_ci h->max_bytes = 0; 338e5b75505Sopenharmony_ci break; 339e5b75505Sopenharmony_ci case HTTPREAD_HDR_TYPE_POST: 340e5b75505Sopenharmony_ci case HTTPREAD_HDR_TYPE_PUT: 341e5b75505Sopenharmony_ci case HTTPREAD_HDR_TYPE_DELETE: 342e5b75505Sopenharmony_ci case HTTPREAD_HDR_TYPE_TRACE: 343e5b75505Sopenharmony_ci case HTTPREAD_HDR_TYPE_CONNECT: 344e5b75505Sopenharmony_ci case HTTPREAD_HDR_TYPE_NOTIFY: 345e5b75505Sopenharmony_ci case HTTPREAD_HDR_TYPE_M_SEARCH: 346e5b75505Sopenharmony_ci case HTTPREAD_HDR_TYPE_M_POST: 347e5b75505Sopenharmony_ci case HTTPREAD_HDR_TYPE_SUBSCRIBE: 348e5b75505Sopenharmony_ci case HTTPREAD_HDR_TYPE_UNSUBSCRIBE: 349e5b75505Sopenharmony_ci default: 350e5b75505Sopenharmony_ci break; 351e5b75505Sopenharmony_ci } 352e5b75505Sopenharmony_ci 353e5b75505Sopenharmony_ci return 0; 354e5b75505Sopenharmony_ci 355e5b75505Sopenharmony_cibad: 356e5b75505Sopenharmony_ci /* Error */ 357e5b75505Sopenharmony_ci return -1; 358e5b75505Sopenharmony_ci} 359e5b75505Sopenharmony_ci 360e5b75505Sopenharmony_ci 361e5b75505Sopenharmony_ci/* httpread_read_handler -- called when socket ready to read 362e5b75505Sopenharmony_ci * 363e5b75505Sopenharmony_ci * Note: any extra data we read past end of transmitted file is ignored; 364e5b75505Sopenharmony_ci * if we were to support keeping connections open for multiple files then 365e5b75505Sopenharmony_ci * this would have to be addressed. 366e5b75505Sopenharmony_ci */ 367e5b75505Sopenharmony_cistatic void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) 368e5b75505Sopenharmony_ci{ 369e5b75505Sopenharmony_ci struct httpread *h = sock_ctx; 370e5b75505Sopenharmony_ci int nread; 371e5b75505Sopenharmony_ci char *rbp; /* pointer into read buffer */ 372e5b75505Sopenharmony_ci char *hbp; /* pointer into header buffer */ 373e5b75505Sopenharmony_ci char *bbp; /* pointer into body buffer */ 374e5b75505Sopenharmony_ci char readbuf[HTTPREAD_READBUF_SIZE]; /* temp use to read into */ 375e5b75505Sopenharmony_ci 376e5b75505Sopenharmony_ci /* read some at a time, then search for the interal 377e5b75505Sopenharmony_ci * boundaries between header and data and etc. 378e5b75505Sopenharmony_ci */ 379e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "httpread: Trying to read more data(%p)", h); 380e5b75505Sopenharmony_ci nread = read(h->sd, readbuf, sizeof(readbuf)); 381e5b75505Sopenharmony_ci if (nread < 0) { 382e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "httpread failed: %s", strerror(errno)); 383e5b75505Sopenharmony_ci goto bad; 384e5b75505Sopenharmony_ci } 385e5b75505Sopenharmony_ci wpa_hexdump_ascii(MSG_MSGDUMP, "httpread - read", readbuf, nread); 386e5b75505Sopenharmony_ci if (nread == 0) { 387e5b75505Sopenharmony_ci /* end of transmission... this may be normal 388e5b75505Sopenharmony_ci * or may be an error... in some cases we can't 389e5b75505Sopenharmony_ci * tell which so we must assume it is normal then. 390e5b75505Sopenharmony_ci */ 391e5b75505Sopenharmony_ci if (!h->got_hdr) { 392e5b75505Sopenharmony_ci /* Must at least have completed header */ 393e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h); 394e5b75505Sopenharmony_ci goto bad; 395e5b75505Sopenharmony_ci } 396e5b75505Sopenharmony_ci if (h->chunked || h->got_content_length) { 397e5b75505Sopenharmony_ci /* Premature EOF; e.g. dropped connection */ 398e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 399e5b75505Sopenharmony_ci "httpread premature eof(%p) %d/%d", 400e5b75505Sopenharmony_ci h, h->body_nbytes, 401e5b75505Sopenharmony_ci h->content_length); 402e5b75505Sopenharmony_ci goto bad; 403e5b75505Sopenharmony_ci } 404e5b75505Sopenharmony_ci /* No explicit length, hopefully we have all the data 405e5b75505Sopenharmony_ci * although dropped connections can cause false 406e5b75505Sopenharmony_ci * end 407e5b75505Sopenharmony_ci */ 408e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h); 409e5b75505Sopenharmony_ci h->got_body = 1; 410e5b75505Sopenharmony_ci goto got_file; 411e5b75505Sopenharmony_ci } 412e5b75505Sopenharmony_ci rbp = readbuf; 413e5b75505Sopenharmony_ci 414e5b75505Sopenharmony_ci /* Header consists of text lines (terminated by both CR and LF) 415e5b75505Sopenharmony_ci * and an empty line (CR LF only). 416e5b75505Sopenharmony_ci */ 417e5b75505Sopenharmony_ci if (!h->got_hdr) { 418e5b75505Sopenharmony_ci hbp = h->hdr + h->hdr_nbytes; 419e5b75505Sopenharmony_ci /* add to headers until: 420e5b75505Sopenharmony_ci * -- we run out of data in read buffer 421e5b75505Sopenharmony_ci * -- or, we run out of header buffer room 422e5b75505Sopenharmony_ci * -- or, we get double CRLF in headers 423e5b75505Sopenharmony_ci */ 424e5b75505Sopenharmony_ci for (;;) { 425e5b75505Sopenharmony_ci if (nread == 0) 426e5b75505Sopenharmony_ci goto get_more; 427e5b75505Sopenharmony_ci if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) { 428e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 429e5b75505Sopenharmony_ci "httpread: Too long header"); 430e5b75505Sopenharmony_ci goto bad; 431e5b75505Sopenharmony_ci } 432e5b75505Sopenharmony_ci *hbp++ = *rbp++; 433e5b75505Sopenharmony_ci nread--; 434e5b75505Sopenharmony_ci h->hdr_nbytes++; 435e5b75505Sopenharmony_ci if (h->hdr_nbytes >= 4 && 436e5b75505Sopenharmony_ci hbp[-1] == '\n' && 437e5b75505Sopenharmony_ci hbp[-2] == '\r' && 438e5b75505Sopenharmony_ci hbp[-3] == '\n' && 439e5b75505Sopenharmony_ci hbp[-4] == '\r' ) { 440e5b75505Sopenharmony_ci h->got_hdr = 1; 441e5b75505Sopenharmony_ci *hbp = 0; /* null terminate */ 442e5b75505Sopenharmony_ci break; 443e5b75505Sopenharmony_ci } 444e5b75505Sopenharmony_ci } 445e5b75505Sopenharmony_ci /* here we've just finished reading the header */ 446e5b75505Sopenharmony_ci if (httpread_hdr_analyze(h)) { 447e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h); 448e5b75505Sopenharmony_ci goto bad; 449e5b75505Sopenharmony_ci } 450e5b75505Sopenharmony_ci if (h->max_bytes == 0) { 451e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "httpread no body hdr end(%p)", 452e5b75505Sopenharmony_ci h); 453e5b75505Sopenharmony_ci goto got_file; 454e5b75505Sopenharmony_ci } 455e5b75505Sopenharmony_ci if (h->got_content_length && h->content_length == 0) { 456e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 457e5b75505Sopenharmony_ci "httpread zero content length(%p)", h); 458e5b75505Sopenharmony_ci goto got_file; 459e5b75505Sopenharmony_ci } 460e5b75505Sopenharmony_ci } 461e5b75505Sopenharmony_ci 462e5b75505Sopenharmony_ci /* Certain types of requests never have data and so 463e5b75505Sopenharmony_ci * must be specially recognized. 464e5b75505Sopenharmony_ci */ 465e5b75505Sopenharmony_ci if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) || 466e5b75505Sopenharmony_ci !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) || 467e5b75505Sopenharmony_ci !os_strncasecmp(h->hdr, "HEAD", 4) || 468e5b75505Sopenharmony_ci !os_strncasecmp(h->hdr, "GET", 3)) { 469e5b75505Sopenharmony_ci if (!h->got_body) { 470e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "httpread NO BODY for sp. type"); 471e5b75505Sopenharmony_ci } 472e5b75505Sopenharmony_ci h->got_body = 1; 473e5b75505Sopenharmony_ci goto got_file; 474e5b75505Sopenharmony_ci } 475e5b75505Sopenharmony_ci 476e5b75505Sopenharmony_ci /* Data can be just plain binary data, or if "chunked" 477e5b75505Sopenharmony_ci * consists of chunks each with a header, ending with 478e5b75505Sopenharmony_ci * an ending header. 479e5b75505Sopenharmony_ci */ 480e5b75505Sopenharmony_ci if (nread == 0) 481e5b75505Sopenharmony_ci goto get_more; 482e5b75505Sopenharmony_ci if (!h->got_body) { 483e5b75505Sopenharmony_ci /* Here to get (more of) body */ 484e5b75505Sopenharmony_ci /* ensure we have enough room for worst case for body 485e5b75505Sopenharmony_ci * plus a null termination character 486e5b75505Sopenharmony_ci */ 487e5b75505Sopenharmony_ci if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) { 488e5b75505Sopenharmony_ci char *new_body; 489e5b75505Sopenharmony_ci int new_alloc_nbytes; 490e5b75505Sopenharmony_ci 491e5b75505Sopenharmony_ci if (h->body_nbytes >= h->max_bytes) { 492e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 493e5b75505Sopenharmony_ci "httpread: body_nbytes=%d >= max_bytes=%d", 494e5b75505Sopenharmony_ci h->body_nbytes, h->max_bytes); 495e5b75505Sopenharmony_ci goto bad; 496e5b75505Sopenharmony_ci } 497e5b75505Sopenharmony_ci new_alloc_nbytes = h->body_alloc_nbytes + 498e5b75505Sopenharmony_ci HTTPREAD_BODYBUF_DELTA; 499e5b75505Sopenharmony_ci /* For content-length case, the first time 500e5b75505Sopenharmony_ci * through we allocate the whole amount 501e5b75505Sopenharmony_ci * we need. 502e5b75505Sopenharmony_ci */ 503e5b75505Sopenharmony_ci if (h->got_content_length && 504e5b75505Sopenharmony_ci new_alloc_nbytes < (h->content_length + 1)) 505e5b75505Sopenharmony_ci new_alloc_nbytes = h->content_length + 1; 506e5b75505Sopenharmony_ci if (new_alloc_nbytes < h->body_alloc_nbytes || 507e5b75505Sopenharmony_ci new_alloc_nbytes > h->max_bytes + 508e5b75505Sopenharmony_ci HTTPREAD_BODYBUF_DELTA) { 509e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 510e5b75505Sopenharmony_ci "httpread: Unacceptable body length %d (body_alloc_nbytes=%u max_bytes=%u)", 511e5b75505Sopenharmony_ci new_alloc_nbytes, 512e5b75505Sopenharmony_ci h->body_alloc_nbytes, 513e5b75505Sopenharmony_ci h->max_bytes); 514e5b75505Sopenharmony_ci goto bad; 515e5b75505Sopenharmony_ci } 516e5b75505Sopenharmony_ci if ((new_body = os_realloc(h->body, new_alloc_nbytes)) 517e5b75505Sopenharmony_ci == NULL) { 518e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 519e5b75505Sopenharmony_ci "httpread: Failed to reallocate buffer (len=%d)", 520e5b75505Sopenharmony_ci new_alloc_nbytes); 521e5b75505Sopenharmony_ci goto bad; 522e5b75505Sopenharmony_ci } 523e5b75505Sopenharmony_ci 524e5b75505Sopenharmony_ci h->body = new_body; 525e5b75505Sopenharmony_ci h->body_alloc_nbytes = new_alloc_nbytes; 526e5b75505Sopenharmony_ci } 527e5b75505Sopenharmony_ci /* add bytes */ 528e5b75505Sopenharmony_ci bbp = h->body + h->body_nbytes; 529e5b75505Sopenharmony_ci for (;;) { 530e5b75505Sopenharmony_ci int ncopy; 531e5b75505Sopenharmony_ci /* See if we need to stop */ 532e5b75505Sopenharmony_ci if (h->chunked && h->in_chunk_data == 0) { 533e5b75505Sopenharmony_ci /* in chunk header */ 534e5b75505Sopenharmony_ci char *cbp = h->body + h->chunk_start; 535e5b75505Sopenharmony_ci if (bbp-cbp >= 2 && bbp[-2] == '\r' && 536e5b75505Sopenharmony_ci bbp[-1] == '\n') { 537e5b75505Sopenharmony_ci /* end of chunk hdr line */ 538e5b75505Sopenharmony_ci /* hdr line consists solely 539e5b75505Sopenharmony_ci * of a hex numeral and CFLF 540e5b75505Sopenharmony_ci */ 541e5b75505Sopenharmony_ci if (!isxdigit(*cbp)) { 542e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 543e5b75505Sopenharmony_ci "httpread: Unexpected chunk header value (not a hex digit)"); 544e5b75505Sopenharmony_ci goto bad; 545e5b75505Sopenharmony_ci } 546e5b75505Sopenharmony_ci h->chunk_size = strtoul(cbp, NULL, 16); 547e5b75505Sopenharmony_ci if (h->chunk_size < 0 || 548e5b75505Sopenharmony_ci h->chunk_size > h->max_bytes) { 549e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 550e5b75505Sopenharmony_ci "httpread: Invalid chunk size %d", 551e5b75505Sopenharmony_ci h->chunk_size); 552e5b75505Sopenharmony_ci goto bad; 553e5b75505Sopenharmony_ci } 554e5b75505Sopenharmony_ci /* throw away chunk header 555e5b75505Sopenharmony_ci * so we have only real data 556e5b75505Sopenharmony_ci */ 557e5b75505Sopenharmony_ci h->body_nbytes = h->chunk_start; 558e5b75505Sopenharmony_ci bbp = cbp; 559e5b75505Sopenharmony_ci if (h->chunk_size == 0) { 560e5b75505Sopenharmony_ci /* end of chunking */ 561e5b75505Sopenharmony_ci /* trailer follows */ 562e5b75505Sopenharmony_ci h->in_trailer = 1; 563e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 564e5b75505Sopenharmony_ci "httpread end chunks(%p)", 565e5b75505Sopenharmony_ci h); 566e5b75505Sopenharmony_ci break; 567e5b75505Sopenharmony_ci } 568e5b75505Sopenharmony_ci h->in_chunk_data = 1; 569e5b75505Sopenharmony_ci /* leave chunk_start alone */ 570e5b75505Sopenharmony_ci } 571e5b75505Sopenharmony_ci } else if (h->chunked) { 572e5b75505Sopenharmony_ci /* in chunk data */ 573e5b75505Sopenharmony_ci if ((h->body_nbytes - h->chunk_start) == 574e5b75505Sopenharmony_ci (h->chunk_size + 2)) { 575e5b75505Sopenharmony_ci /* end of chunk reached, 576e5b75505Sopenharmony_ci * new chunk starts 577e5b75505Sopenharmony_ci */ 578e5b75505Sopenharmony_ci /* check chunk ended w/ CRLF 579e5b75505Sopenharmony_ci * which we'll throw away 580e5b75505Sopenharmony_ci */ 581e5b75505Sopenharmony_ci if (bbp[-1] == '\n' && 582e5b75505Sopenharmony_ci bbp[-2] == '\r') { 583e5b75505Sopenharmony_ci } else { 584e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 585e5b75505Sopenharmony_ci "httpread: Invalid chunk end"); 586e5b75505Sopenharmony_ci goto bad; 587e5b75505Sopenharmony_ci } 588e5b75505Sopenharmony_ci h->body_nbytes -= 2; 589e5b75505Sopenharmony_ci bbp -= 2; 590e5b75505Sopenharmony_ci h->chunk_start = h->body_nbytes; 591e5b75505Sopenharmony_ci h->in_chunk_data = 0; 592e5b75505Sopenharmony_ci h->chunk_size = 0; /* just in case */ 593e5b75505Sopenharmony_ci } 594e5b75505Sopenharmony_ci } else if (h->got_content_length && 595e5b75505Sopenharmony_ci h->body_nbytes >= h->content_length) { 596e5b75505Sopenharmony_ci h->got_body = 1; 597e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 598e5b75505Sopenharmony_ci "httpread got content(%p)", h); 599e5b75505Sopenharmony_ci goto got_file; 600e5b75505Sopenharmony_ci } 601e5b75505Sopenharmony_ci if (nread <= 0) 602e5b75505Sopenharmony_ci break; 603e5b75505Sopenharmony_ci /* Now transfer. Optimize using memcpy where we can. */ 604e5b75505Sopenharmony_ci if (h->chunked && h->in_chunk_data) { 605e5b75505Sopenharmony_ci /* copy up to remainder of chunk data 606e5b75505Sopenharmony_ci * plus the required CR+LF at end 607e5b75505Sopenharmony_ci */ 608e5b75505Sopenharmony_ci ncopy = (h->chunk_start + h->chunk_size + 2) - 609e5b75505Sopenharmony_ci h->body_nbytes; 610e5b75505Sopenharmony_ci } else if (h->chunked) { 611e5b75505Sopenharmony_ci /*in chunk header -- don't optimize */ 612e5b75505Sopenharmony_ci *bbp++ = *rbp++; 613e5b75505Sopenharmony_ci nread--; 614e5b75505Sopenharmony_ci h->body_nbytes++; 615e5b75505Sopenharmony_ci continue; 616e5b75505Sopenharmony_ci } else if (h->got_content_length) { 617e5b75505Sopenharmony_ci ncopy = h->content_length - h->body_nbytes; 618e5b75505Sopenharmony_ci } else { 619e5b75505Sopenharmony_ci ncopy = nread; 620e5b75505Sopenharmony_ci } 621e5b75505Sopenharmony_ci /* Note: should never be 0 */ 622e5b75505Sopenharmony_ci if (ncopy < 0) { 623e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 624e5b75505Sopenharmony_ci "httpread: Invalid ncopy=%d", ncopy); 625e5b75505Sopenharmony_ci goto bad; 626e5b75505Sopenharmony_ci } 627e5b75505Sopenharmony_ci if (ncopy > nread) 628e5b75505Sopenharmony_ci ncopy = nread; 629e5b75505Sopenharmony_ci os_memcpy(bbp, rbp, ncopy); 630e5b75505Sopenharmony_ci bbp += ncopy; 631e5b75505Sopenharmony_ci h->body_nbytes += ncopy; 632e5b75505Sopenharmony_ci rbp += ncopy; 633e5b75505Sopenharmony_ci nread -= ncopy; 634e5b75505Sopenharmony_ci } /* body copy loop */ 635e5b75505Sopenharmony_ci } /* !got_body */ 636e5b75505Sopenharmony_ci if (h->chunked && h->in_trailer) { 637e5b75505Sopenharmony_ci /* If "chunked" then there is always a trailer, 638e5b75505Sopenharmony_ci * consisting of zero or more non-empty lines 639e5b75505Sopenharmony_ci * ending with CR LF and then an empty line w/ CR LF. 640e5b75505Sopenharmony_ci * We do NOT support trailers except to skip them -- 641e5b75505Sopenharmony_ci * this is supported (generally) by the http spec. 642e5b75505Sopenharmony_ci */ 643e5b75505Sopenharmony_ci for (;;) { 644e5b75505Sopenharmony_ci int c; 645e5b75505Sopenharmony_ci if (nread <= 0) 646e5b75505Sopenharmony_ci break; 647e5b75505Sopenharmony_ci c = *rbp++; 648e5b75505Sopenharmony_ci nread--; 649e5b75505Sopenharmony_ci switch (h->trailer_state) { 650e5b75505Sopenharmony_ci case trailer_line_begin: 651e5b75505Sopenharmony_ci if (c == '\r') 652e5b75505Sopenharmony_ci h->trailer_state = trailer_empty_cr; 653e5b75505Sopenharmony_ci else 654e5b75505Sopenharmony_ci h->trailer_state = trailer_nonempty; 655e5b75505Sopenharmony_ci break; 656e5b75505Sopenharmony_ci case trailer_empty_cr: 657e5b75505Sopenharmony_ci /* end empty line */ 658e5b75505Sopenharmony_ci if (c == '\n') { 659e5b75505Sopenharmony_ci h->trailer_state = trailer_line_begin; 660e5b75505Sopenharmony_ci h->in_trailer = 0; 661e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 662e5b75505Sopenharmony_ci "httpread got content(%p)", 663e5b75505Sopenharmony_ci h); 664e5b75505Sopenharmony_ci h->got_body = 1; 665e5b75505Sopenharmony_ci goto got_file; 666e5b75505Sopenharmony_ci } 667e5b75505Sopenharmony_ci h->trailer_state = trailer_nonempty; 668e5b75505Sopenharmony_ci break; 669e5b75505Sopenharmony_ci case trailer_nonempty: 670e5b75505Sopenharmony_ci if (c == '\r') 671e5b75505Sopenharmony_ci h->trailer_state = trailer_nonempty_cr; 672e5b75505Sopenharmony_ci break; 673e5b75505Sopenharmony_ci case trailer_nonempty_cr: 674e5b75505Sopenharmony_ci if (c == '\n') 675e5b75505Sopenharmony_ci h->trailer_state = trailer_line_begin; 676e5b75505Sopenharmony_ci else 677e5b75505Sopenharmony_ci h->trailer_state = trailer_nonempty; 678e5b75505Sopenharmony_ci break; 679e5b75505Sopenharmony_ci } 680e5b75505Sopenharmony_ci } 681e5b75505Sopenharmony_ci } 682e5b75505Sopenharmony_ci goto get_more; 683e5b75505Sopenharmony_ci 684e5b75505Sopenharmony_cibad: 685e5b75505Sopenharmony_ci /* Error */ 686e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h); 687e5b75505Sopenharmony_ci (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR); 688e5b75505Sopenharmony_ci return; 689e5b75505Sopenharmony_ci 690e5b75505Sopenharmony_ciget_more: 691e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "httpread: get more (%p)", h); 692e5b75505Sopenharmony_ci return; 693e5b75505Sopenharmony_ci 694e5b75505Sopenharmony_cigot_file: 695e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "httpread got file %d bytes type %d", 696e5b75505Sopenharmony_ci h->body_nbytes, h->hdr_type); 697e5b75505Sopenharmony_ci wpa_hexdump_ascii(MSG_MSGDUMP, "httpread: body", 698e5b75505Sopenharmony_ci h->body, h->body_nbytes); 699e5b75505Sopenharmony_ci /* Null terminate for convenience of some applications */ 700e5b75505Sopenharmony_ci if (h->body) 701e5b75505Sopenharmony_ci h->body[h->body_nbytes] = 0; /* null terminate */ 702e5b75505Sopenharmony_ci h->got_file = 1; 703e5b75505Sopenharmony_ci /* Assume that we do NOT support keeping connection alive, 704e5b75505Sopenharmony_ci * and just in case somehow we don't get destroyed right away, 705e5b75505Sopenharmony_ci * unregister now. 706e5b75505Sopenharmony_ci */ 707e5b75505Sopenharmony_ci eloop_unregister_sock(h->sd, EVENT_TYPE_READ); 708e5b75505Sopenharmony_ci /* The application can destroy us whenever they feel like... 709e5b75505Sopenharmony_ci * cancel timeout. 710e5b75505Sopenharmony_ci */ 711e5b75505Sopenharmony_ci eloop_cancel_timeout(httpread_timeout_handler, NULL, h); 712e5b75505Sopenharmony_ci (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY); 713e5b75505Sopenharmony_ci} 714e5b75505Sopenharmony_ci 715e5b75505Sopenharmony_ci 716e5b75505Sopenharmony_ci/* httpread_create -- start a new reading session making use of eloop. 717e5b75505Sopenharmony_ci * The new instance will use the socket descriptor for reading (until 718e5b75505Sopenharmony_ci * it gets a file and not after) but will not close the socket, even 719e5b75505Sopenharmony_ci * when the instance is destroyed (the application must do that). 720e5b75505Sopenharmony_ci * Return NULL on error. 721e5b75505Sopenharmony_ci * 722e5b75505Sopenharmony_ci * Provided that httpread_create successfully returns a handle, 723e5b75505Sopenharmony_ci * the callback fnc is called to handle httpread_event events. 724e5b75505Sopenharmony_ci * The caller should do destroy on any errors or unknown events. 725e5b75505Sopenharmony_ci * 726e5b75505Sopenharmony_ci * Pass max_bytes == 0 to not read body at all (required for e.g. 727e5b75505Sopenharmony_ci * reply to HEAD request). 728e5b75505Sopenharmony_ci */ 729e5b75505Sopenharmony_cistruct httpread * httpread_create( 730e5b75505Sopenharmony_ci int sd, /* descriptor of TCP socket to read from */ 731e5b75505Sopenharmony_ci void (*cb)(struct httpread *handle, void *cookie, 732e5b75505Sopenharmony_ci enum httpread_event e), /* call on event */ 733e5b75505Sopenharmony_ci void *cookie, /* pass to callback */ 734e5b75505Sopenharmony_ci int max_bytes, /* maximum body size else abort it */ 735e5b75505Sopenharmony_ci int timeout_seconds /* 0; or total duration timeout period */ 736e5b75505Sopenharmony_ci ) 737e5b75505Sopenharmony_ci{ 738e5b75505Sopenharmony_ci struct httpread *h = NULL; 739e5b75505Sopenharmony_ci 740e5b75505Sopenharmony_ci h = os_zalloc(sizeof(*h)); 741e5b75505Sopenharmony_ci if (h == NULL) 742e5b75505Sopenharmony_ci goto fail; 743e5b75505Sopenharmony_ci h->sd = sd; 744e5b75505Sopenharmony_ci h->cb = cb; 745e5b75505Sopenharmony_ci h->cookie = cookie; 746e5b75505Sopenharmony_ci h->max_bytes = max_bytes; 747e5b75505Sopenharmony_ci h->timeout_seconds = timeout_seconds; 748e5b75505Sopenharmony_ci 749e5b75505Sopenharmony_ci if (timeout_seconds > 0 && 750e5b75505Sopenharmony_ci eloop_register_timeout(timeout_seconds, 0, 751e5b75505Sopenharmony_ci httpread_timeout_handler, NULL, h)) { 752e5b75505Sopenharmony_ci /* No way to recover (from malloc failure) */ 753e5b75505Sopenharmony_ci goto fail; 754e5b75505Sopenharmony_ci } 755e5b75505Sopenharmony_ci if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler, 756e5b75505Sopenharmony_ci NULL, h)) { 757e5b75505Sopenharmony_ci /* No way to recover (from malloc failure) */ 758e5b75505Sopenharmony_ci goto fail; 759e5b75505Sopenharmony_ci } 760e5b75505Sopenharmony_ci return h; 761e5b75505Sopenharmony_ci 762e5b75505Sopenharmony_cifail: 763e5b75505Sopenharmony_ci 764e5b75505Sopenharmony_ci /* Error */ 765e5b75505Sopenharmony_ci httpread_destroy(h); 766e5b75505Sopenharmony_ci return NULL; 767e5b75505Sopenharmony_ci} 768e5b75505Sopenharmony_ci 769e5b75505Sopenharmony_ci 770e5b75505Sopenharmony_ci/* httpread_hdr_type_get -- When file is ready, returns header type. */ 771e5b75505Sopenharmony_cienum httpread_hdr_type httpread_hdr_type_get(struct httpread *h) 772e5b75505Sopenharmony_ci{ 773e5b75505Sopenharmony_ci return h->hdr_type; 774e5b75505Sopenharmony_ci} 775e5b75505Sopenharmony_ci 776e5b75505Sopenharmony_ci 777e5b75505Sopenharmony_ci/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI 778e5b75505Sopenharmony_ci * or possibly NULL (which would be an error). 779e5b75505Sopenharmony_ci */ 780e5b75505Sopenharmony_cichar * httpread_uri_get(struct httpread *h) 781e5b75505Sopenharmony_ci{ 782e5b75505Sopenharmony_ci return h->uri; 783e5b75505Sopenharmony_ci} 784e5b75505Sopenharmony_ci 785e5b75505Sopenharmony_ci 786e5b75505Sopenharmony_ci/* httpread_reply_code_get -- When reply is ready, returns reply code */ 787e5b75505Sopenharmony_ciint httpread_reply_code_get(struct httpread *h) 788e5b75505Sopenharmony_ci{ 789e5b75505Sopenharmony_ci return h->reply_code; 790e5b75505Sopenharmony_ci} 791e5b75505Sopenharmony_ci 792e5b75505Sopenharmony_ci 793e5b75505Sopenharmony_ci/* httpread_length_get -- When file is ready, returns file length. */ 794e5b75505Sopenharmony_ciint httpread_length_get(struct httpread *h) 795e5b75505Sopenharmony_ci{ 796e5b75505Sopenharmony_ci return h->body_nbytes; 797e5b75505Sopenharmony_ci} 798e5b75505Sopenharmony_ci 799e5b75505Sopenharmony_ci 800e5b75505Sopenharmony_ci/* httpread_data_get -- When file is ready, returns file content 801e5b75505Sopenharmony_ci * with null byte appened. 802e5b75505Sopenharmony_ci * Might return NULL in some error condition. 803e5b75505Sopenharmony_ci */ 804e5b75505Sopenharmony_civoid * httpread_data_get(struct httpread *h) 805e5b75505Sopenharmony_ci{ 806e5b75505Sopenharmony_ci return h->body ? h->body : ""; 807e5b75505Sopenharmony_ci} 808e5b75505Sopenharmony_ci 809e5b75505Sopenharmony_ci 810e5b75505Sopenharmony_ci/* httpread_hdr_get -- When file is ready, returns header content 811e5b75505Sopenharmony_ci * with null byte appended. 812e5b75505Sopenharmony_ci * Might return NULL in some error condition. 813e5b75505Sopenharmony_ci */ 814e5b75505Sopenharmony_cichar * httpread_hdr_get(struct httpread *h) 815e5b75505Sopenharmony_ci{ 816e5b75505Sopenharmony_ci return h->hdr; 817e5b75505Sopenharmony_ci} 818e5b75505Sopenharmony_ci 819e5b75505Sopenharmony_ci 820e5b75505Sopenharmony_ci/* httpread_hdr_line_get -- When file is ready, returns pointer 821e5b75505Sopenharmony_ci * to line within header content matching the given tag 822e5b75505Sopenharmony_ci * (after the tag itself and any spaces/tabs). 823e5b75505Sopenharmony_ci * 824e5b75505Sopenharmony_ci * The tag should end with a colon for reliable matching. 825e5b75505Sopenharmony_ci * 826e5b75505Sopenharmony_ci * If not found, returns NULL; 827e5b75505Sopenharmony_ci */ 828e5b75505Sopenharmony_cichar * httpread_hdr_line_get(struct httpread *h, const char *tag) 829e5b75505Sopenharmony_ci{ 830e5b75505Sopenharmony_ci int tag_len = os_strlen(tag); 831e5b75505Sopenharmony_ci char *hdr = h->hdr; 832e5b75505Sopenharmony_ci hdr = os_strchr(hdr, '\n'); 833e5b75505Sopenharmony_ci if (hdr == NULL) 834e5b75505Sopenharmony_ci return NULL; 835e5b75505Sopenharmony_ci hdr++; 836e5b75505Sopenharmony_ci for (;;) { 837e5b75505Sopenharmony_ci if (!os_strncasecmp(hdr, tag, tag_len)) { 838e5b75505Sopenharmony_ci hdr += tag_len; 839e5b75505Sopenharmony_ci while (*hdr == ' ' || *hdr == '\t') 840e5b75505Sopenharmony_ci hdr++; 841e5b75505Sopenharmony_ci return hdr; 842e5b75505Sopenharmony_ci } 843e5b75505Sopenharmony_ci hdr = os_strchr(hdr, '\n'); 844e5b75505Sopenharmony_ci if (hdr == NULL) 845e5b75505Sopenharmony_ci return NULL; 846e5b75505Sopenharmony_ci hdr++; 847e5b75505Sopenharmony_ci } 848e5b75505Sopenharmony_ci} 849