Annotation of embedaddon/libpdel/http/http_message.c, revision 1.1
1.1 ! misho 1:
! 2: /*
! 3: * Copyright (c) 2001-2002 Packet Design, LLC.
! 4: * All rights reserved.
! 5: *
! 6: * Subject to the following obligations and disclaimer of warranty,
! 7: * use and redistribution of this software, in source or object code
! 8: * forms, with or without modifications are expressly permitted by
! 9: * Packet Design; provided, however, that:
! 10: *
! 11: * (i) Any and all reproductions of the source or object code
! 12: * must include the copyright notice above and the following
! 13: * disclaimer of warranties; and
! 14: * (ii) No rights are granted, in any manner or form, to use
! 15: * Packet Design trademarks, including the mark "PACKET DESIGN"
! 16: * on advertising, endorsements, or otherwise except as such
! 17: * appears in the above copyright notice or in the software.
! 18: *
! 19: * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
! 20: * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
! 21: * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
! 22: * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
! 23: * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
! 24: * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
! 25: * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
! 26: * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
! 27: * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE
! 28: * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
! 29: * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
! 30: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
! 31: * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
! 32: * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
! 33: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 34: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
! 35: * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
! 36: * THE POSSIBILITY OF SUCH DAMAGE.
! 37: *
! 38: * Author: Archie Cobbs <archie@freebsd.org>
! 39: */
! 40:
! 41: #include <sys/types.h>
! 42: #include <sys/queue.h>
! 43:
! 44: #include <netinet/in.h>
! 45:
! 46: #include <stdio.h>
! 47: #include <stdlib.h>
! 48: #include <stdarg.h>
! 49: #include <string.h>
! 50: #include <pthread.h>
! 51: #include <limits.h>
! 52: #include <syslog.h>
! 53: #include <errno.h>
! 54: #include <ctype.h>
! 55:
! 56: #include <openssl/ssl.h>
! 57:
! 58: #include "structs/structs.h"
! 59: #include "structs/type/array.h"
! 60:
! 61: #include "util/typed_mem.h"
! 62: #include "http/http_defs.h"
! 63: #include "http/http_server.h"
! 64: #include "http/http_internal.h"
! 65:
! 66: /*
! 67: * Internal functions
! 68: */
! 69: static int http_message_input_read(void *cookie, char *buf, int len);
! 70: static int http_message_input_close(void *cookie);
! 71: static int http_message_output_write(void *cookie,
! 72: const char *buf, int len);
! 73: static int http_message_output_close(void *cookie);
! 74:
! 75: /*********************************************************************
! 76: MAIN ROUTINES
! 77: *********************************************************************/
! 78:
! 79: /*
! 80: * Create a new message structure.
! 81: */
! 82: struct http_message *
! 83: _http_message_new(void)
! 84: {
! 85: struct http_message *msg;
! 86:
! 87: if ((msg = MALLOC("http_message", sizeof(*msg))) == NULL)
! 88: return (NULL);
! 89: memset(msg, 0, sizeof(*msg));
! 90: if ((msg->head = _http_head_new()) == NULL) {
! 91: FREE("http_message", msg);
! 92: return (NULL);
! 93: }
! 94: return (msg);
! 95: }
! 96:
! 97: /*
! 98: * Initialize a message by loading in HTTP headers from a stream,
! 99: * and set msg->input to be the entity input stream.
! 100: *
! 101: * This reads any Content-Length header and sets msg->input_len
! 102: * accordingly, or UINT_MAX if none found. Messages that by definition
! 103: * have no body must reset msg->input_len to zero.
! 104: */
! 105: int
! 106: _http_message_read(struct http_message *msg, int req)
! 107: {
! 108: struct http_connection *const conn = msg->conn;
! 109: const char *s;
! 110:
! 111: /* Slurp in HTTP headers */
! 112: if (_http_head_read(msg->head, conn->fp, req) == -1)
! 113: return (-1);
! 114:
! 115: /* Get content length, if any */
! 116: msg->input_len = UINT_MAX;
! 117: if ((s = _http_head_get(msg->head,
! 118: HTTP_HEADER_CONTENT_LENGTH)) != NULL) {
! 119: if (sscanf(s, "%u", &msg->input_len) != 1) {
! 120: errno = EINVAL;
! 121: return (-1);
! 122: }
! 123: }
! 124:
! 125: /* Create input stream */
! 126: if ((msg->input = funopen(msg, http_message_input_read,
! 127: NULL, NULL, http_message_input_close)) == NULL)
! 128: return (-1);
! 129:
! 130: /* Debugging */
! 131: if (PDEL_DEBUG_ENABLED(HTTP_HDRS)) {
! 132: DBG(HTTP_HDRS, "dumping %s headers:",
! 133: msg->conn->server ? "REQUEST" : "RESPONSE");
! 134: _http_head_write(msg->head, stdout);
! 135: }
! 136:
! 137: /* Done */
! 138: return (0);
! 139: }
! 140:
! 141: /*
! 142: * Free a message structure.
! 143: */
! 144: void
! 145: _http_message_free(struct http_message **msgp)
! 146: {
! 147: struct http_message *const msg = *msgp;
! 148:
! 149: if (msg == NULL)
! 150: return;
! 151: _http_head_free(&msg->head);
! 152: if (msg->input != NULL)
! 153: fclose(msg->input);
! 154: if (msg->output != NULL)
! 155: fclose(msg->output);
! 156: if (msg->output_buf != NULL)
! 157: FREE("http_message.output_buf", msg->output_buf);
! 158: FREE("http_message.query", msg->query);
! 159: FREE("http_message.host", msg->host);
! 160: FREE("http_message.path", msg->path);
! 161: FREE("http_message", msg);
! 162: *msgp = NULL;
! 163: }
! 164:
! 165: /*
! 166: * Get message output stream.
! 167: *
! 168: * If "buffer" is true, the entire output will be buffered unless
! 169: * the headers have already been sent.
! 170: */
! 171: FILE *
! 172: _http_message_get_output(struct http_message *msg, int buffer)
! 173: {
! 174: if (msg->output == NULL) {
! 175: if ((msg->output = funopen(msg, NULL,
! 176: http_message_output_write, NULL,
! 177: http_message_output_close)) == NULL)
! 178: return (NULL);
! 179: msg->buffered = buffer;
! 180: }
! 181: return (msg->output);
! 182: }
! 183:
! 184: /*
! 185: * Get raw i/o stream as a file descriptor.
! 186: */
! 187: int
! 188: _http_message_get_raw_socket(struct http_message *msg)
! 189: {
! 190: if (msg->conn->ssl != NULL) {
! 191: errno = EPROTOTYPE;
! 192: return (-1);
! 193: }
! 194: return (msg->conn->sock);
! 195: }
! 196:
! 197: /*
! 198: * Send message headers, if not sent already.
! 199: */
! 200: void
! 201: _http_message_send_headers(struct http_message *msg, int unbuffer)
! 202: {
! 203: /* Do nothing if nothing to do */
! 204: if (msg->hdrs_sent)
! 205: return;
! 206:
! 207: /* If we're unbuffering message, turn off buffered flag */
! 208: if (!msg->buffered)
! 209: unbuffer = 0;
! 210: else if (unbuffer) {
! 211: if (msg->output != NULL)
! 212: fflush(msg->output);
! 213: msg->buffered = 0;
! 214: }
! 215:
! 216: /* In buffered mode, set Content-Length header from buffer length.
! 217: In non-buffed mode, turn off connection keep-alive. If no body,
! 218: remove content related headers. */
! 219: if (msg->no_body) {
! 220: _http_head_remove(msg->head, HTTP_HEADER_CONTENT_ENCODING);
! 221: _http_head_remove(msg->head, HTTP_HEADER_CONTENT_LENGTH);
! 222: _http_head_remove(msg->head, HTTP_HEADER_CONTENT_TYPE);
! 223: _http_head_remove(msg->head, HTTP_HEADER_LAST_MODIFIED);
! 224: } else if (msg->buffered) {
! 225: if (msg->output != NULL)
! 226: fflush(msg->output);
! 227: _http_head_set(msg->head, 0, HTTP_HEADER_CONTENT_LENGTH,
! 228: "%u", msg->output_len);
! 229: } else if (_http_head_get(msg->head,
! 230: HTTP_HEADER_CONTENT_LENGTH) == NULL) {
! 231: _http_head_set(msg->head, 0,
! 232: _http_message_connection_header(msg), "Close");
! 233: }
! 234:
! 235: /* Debugging */
! 236: if (PDEL_DEBUG_ENABLED(HTTP_HDRS)) {
! 237: DBG(HTTP_HDRS, "dumping %s headers:",
! 238: msg->conn->server ? "RESPONSE" : "REQUEST");
! 239: _http_head_write(msg->head, stdout);
! 240: }
! 241:
! 242: /* Send headers */
! 243: if (!msg->no_headers)
! 244: _http_head_write(msg->head, msg->conn->fp);
! 245: msg->hdrs_sent = 1;
! 246:
! 247: /* If unbuffering, send and release any output buffered so far */
! 248: if (unbuffer) {
! 249: _http_message_send_body(msg);
! 250: FREE("http_message.output_buf", msg->output_buf);
! 251: msg->output_buf = NULL;
! 252: msg->output_len = 0;
! 253: }
! 254: }
! 255:
! 256: /*
! 257: * Send message body.
! 258: */
! 259: void
! 260: _http_message_send_body(struct http_message *msg)
! 261: {
! 262: struct http_connection *const conn = msg->conn;
! 263:
! 264: /* Flush output stream data */
! 265: if (msg->output != NULL)
! 266: fflush(msg->output);
! 267:
! 268: /* Send buffered output (if it was buffered) */
! 269: if (msg->output_buf != NULL) {
! 270: if (!msg->skip_body)
! 271: fwrite(msg->output_buf, 1, msg->output_len, conn->fp);
! 272: fflush(conn->fp);
! 273: }
! 274: }
! 275:
! 276: /*
! 277: * Set a message header.
! 278: */
! 279: int
! 280: _http_message_vset_header(struct http_message *msg, int append,
! 281: const char *name, const char *valfmt, va_list args)
! 282: {
! 283:
! 284: /* Check if we already sent the headers */
! 285: if (msg->hdrs_sent) {
! 286: errno = EALREADY;
! 287: return (-1);
! 288: }
! 289:
! 290: /* Set header */
! 291: return (_http_head_vset(msg->head, append, name, valfmt, args));
! 292: }
! 293:
! 294: /*
! 295: * Remove a message header.
! 296: */
! 297: int
! 298: _http_message_remove_header(struct http_message *msg, const char *name)
! 299: {
! 300: return (_http_head_remove(msg->head, name));
! 301: }
! 302:
! 303: /*
! 304: * Get the "Connection" header name, which will be either "Connection"
! 305: * or "Proxy-Connection".
! 306: */
! 307: const char *
! 308: _http_message_connection_header(struct http_message *msg)
! 309: {
! 310: struct http_connection *const conn = msg->conn;
! 311: const char *hval;
! 312:
! 313: if (!conn->proxy
! 314: || (hval = _http_head_get(msg->head, conn->server ?
! 315: HDR_REQUEST_VERSION : HDR_REPLY_VERSION)) == NULL
! 316: || strcmp(hval, HTTP_PROTO_1_1) >= 0)
! 317: return (HTTP_HEADER_CONNECTION);
! 318: return (HTTP_HEADER_PROXY_CONNECTION);
! 319: }
! 320:
! 321: /*
! 322: * Get remote IP address.
! 323: */
! 324: struct in_addr
! 325: _http_message_get_remote_ip(struct http_message *msg)
! 326: {
! 327: return (msg->conn->remote_ip);
! 328: }
! 329:
! 330: /*
! 331: * Get remote port.
! 332: */
! 333: u_int16_t
! 334: _http_message_get_remote_port(struct http_message *msg)
! 335: {
! 336: return (msg->conn->remote_port);
! 337: }
! 338:
! 339: /*
! 340: * Figure out whether anything is in the message at all.
! 341: */
! 342: int
! 343: _http_message_has_anything(struct http_message *msg)
! 344: {
! 345: return (_http_head_has_anything(msg->head));
! 346: }
! 347:
! 348: /*********************************************************************
! 349: INPUT STREAM METHODS
! 350: *********************************************************************/
! 351:
! 352: /*
! 353: * Read message input stream.
! 354: */
! 355: static int
! 356: http_message_input_read(void *cookie, char *buf, int len)
! 357: {
! 358: struct http_message *const msg = cookie;
! 359: int ret;
! 360:
! 361: if (msg->input_read == msg->input_len || len < 0)
! 362: return (0);
! 363: if (len > msg->input_len - msg->input_read)
! 364: len = msg->input_len - msg->input_read;
! 365: if ((ret = fread(buf, 1, len, msg->conn->fp)) != len) {
! 366: if (ferror(msg->conn->fp))
! 367: return (-1);
! 368: }
! 369: msg->input_read += ret;
! 370: return (ret);
! 371: }
! 372:
! 373: /*
! 374: * Close message input stream.
! 375: */
! 376: static int
! 377: http_message_input_close(void *cookie)
! 378: {
! 379: struct http_message *const msg = cookie;
! 380:
! 381: msg->input = NULL;
! 382: return (0);
! 383: }
! 384:
! 385: /*********************************************************************
! 386: OUTPUT STREAM METHODS
! 387: *********************************************************************/
! 388:
! 389: /*
! 390: * Write to message output stream.
! 391: */
! 392: static int
! 393: http_message_output_write(void *cookie, const char *buf, int len)
! 394: {
! 395: struct http_message *const msg = cookie;
! 396: struct http_connection *const conn = msg->conn;
! 397: int totlen;
! 398: int ret;
! 399: void *mem;
! 400:
! 401: /* Ignore zero length writes */
! 402: if (len == 0)
! 403: return (0);
! 404:
! 405: /* Check whether to allow an entity body at all */
! 406: if (msg->no_body) {
! 407: errno = EINVAL;
! 408: return (-1);
! 409: }
! 410:
! 411: /* If not buffered, check if headers have been sent then write data */
! 412: if (!msg->buffered) {
! 413: if (!msg->hdrs_sent)
! 414: _http_message_send_headers(msg, 0);
! 415: if (msg->skip_body)
! 416: return (len);
! 417: if ((ret = fwrite(buf, 1, len, conn->fp)) != len)
! 418: return (-1);
! 419: return (ret);
! 420: }
! 421:
! 422: /* Expand buffer and write data into it */
! 423: totlen = msg->output_len + len;
! 424: if ((mem = REALLOC("http_message.output_buf",
! 425: msg->output_buf, totlen)) == NULL) {
! 426: (*conn->logger)(LOG_ERR, "%s: %s", "realloc", strerror(errno));
! 427: return (-1);
! 428: }
! 429: msg->output_buf = mem;
! 430: memcpy(msg->output_buf + msg->output_len, buf, len);
! 431: msg->output_len += len;
! 432: return (len);
! 433: }
! 434:
! 435: /*
! 436: * Close message output stream.
! 437: */
! 438: static int
! 439: http_message_output_close(void *cookie)
! 440: {
! 441: struct http_message *const msg = cookie;
! 442:
! 443: msg->output = NULL;
! 444: return (0);
! 445: }
! 446:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>