📄 ne_request.c
字号:
/* HTTP request/response handling Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA*//* This is the HTTP client request/response implementation. * The goal of this code is to be modular and simple. */#include "config.h"#include <sys/types.h>#include <sys/stat.h>#ifdef __EMX__#include <sys/select.h>#endif#ifdef HAVE_LIMITS_H#include <limits.h> /* for UINT_MAX etc */#endif#include <ctype.h>#include <errno.h>#include <fcntl.h>#ifdef HAVE_STRING_H#include <string.h>#endif#ifdef HAVE_STRINGS_H#include <strings.h>#endif #ifdef HAVE_STDLIB_H#include <stdlib.h>#endif#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#include "ne_i18n.h"#include "ne_alloc.h"#include "ne_request.h"#include "ne_string.h" /* for ne_buffer */#include "ne_utils.h"#include "ne_socket.h"#include "ne_uri.h"#include "ne_private.h"#define HTTP_EXPECT_TIMEOUT 15/* 100-continue only used if size > HTTP_EXPECT_MINSIZ */#define HTTP_EXPECT_MINSIZE 1024/* with thanks to Jim Blandy; this macro simplified loads of code. */#define HTTP_ERR(x) do { int _ret = (x); if (_ret != NE_OK) return _ret; } while (0)#define SOCK_ERR(req, op, msg) do { ssize_t sret = (op); \if (sret < 0) return aborted(req, msg, sret); } while (0)/* This is called with each of the headers in the response */struct header_handler { char *name; ne_header_handler handler; void *userdata; struct header_handler *next;};/* TODO: could unify these all into a generic callback list */struct body_reader { ne_block_reader handler; ne_accept_response accept_response; unsigned int use:1; void *userdata; struct body_reader *next;};struct ne_request_s { char *method, *uri; /* method and Request-URI */ ne_buffer *headers; /* request headers */ /* Request body. */ ne_provide_body body_cb; void *body_ud; /* Comes from either an fd or a buffer. */ union { int fd; struct { const char *buffer, *pnt; size_t left; } buf; } body; size_t body_size, body_progress; /* temporary store for response lines. */ char respbuf[BUFSIZ]; /**** Response ***/ /* The transfer encoding types */ struct ne_response { int length; /* Response entity-body content-length */ size_t left; /* Bytes left to read */ size_t chunk_left; /* Bytes of chunk left to read */ size_t total; /* total bytes read so far. */ /* how the message length is detemined: */ enum { R_TILLEOF = 0, /* read till eof */ R_NO_BODY, /* implicitly no body (HEAD, 204, 205, 304) */ R_CHUNKED, /* using chunked transfer-encoding */ R_CLENGTH /* using given content-length */ } mode; } resp; /* List of callbacks which are passed response headers */ struct header_handler *header_catchers; struct hook *private; /* We store response header handlers in a hash table. The hash is * derived from the header name in lower case. */ /* FIXME: this comment is WAY out of date, and no longer in any * way true. */ /* 53 is magic, of course. For a standard ne_get() (with * redirects), 9 header handlers are defined. Two of these are * for Content-Length (which is a bug, and should be fixed * really). Ignoring that hash clash, the 8 *different* handlers * all hash uniquely into the hash table of size 53. */#define HH_HASHSIZE 53 struct header_handler *header_handlers[HH_HASHSIZE]; /* List of callbacks which are passed response body blocks */ struct body_reader *body_readers; /*** Miscellaneous ***/ unsigned int method_is_head:1; unsigned int use_expect100:1; unsigned int can_persist:1; ne_session *session; ne_status status;};static int open_connection(ne_request *req);/* The iterative step used to produce the hash value. This is DJB's * magic "*33" hash function. Ralf Engelschall has done some amazing * statistical analysis to show that *33 really is a good hash * function: check the new-httpd list archives, or his 'str' library * source code, for the details. * * TODO: due to limited range of characters used in header names, * could maybe get a better hash function to use? */ #define HH_ITERATE(hash, ch) (((hash)*33 + (ch)) % HH_HASHSIZE);/* Returns hash value for header 'name', converting it to lower-case * in-place. */static inline unsigned int hash_and_lower(char *name){ char *pnt; unsigned int hash = 0; for (pnt = name; *pnt != '\0'; pnt++) { *pnt = tolower(*pnt); hash = HH_ITERATE(hash,*pnt); } return hash;}/* Abort a request due to an non-recoverable HTTP protocol error, * whilst doing 'doing'. 'code', if non-zero, is the socket error * code, NE_SOCK_*, or if zero, is ignored. */static int aborted(ne_request *req, const char *doing, ssize_t code){ ne_session *sess = req->session; int ret = NE_ERROR; NE_DEBUG(NE_DBG_HTTP, "Aborted request (%" NE_FMT_SSIZE_T "): %s\n", code, doing); switch(code) { case NE_SOCK_CLOSED: if (sess->use_proxy) { ne_set_error(sess, _("%s: connection was closed by proxy server."), doing); } else { ne_set_error(sess, _("%s: connection was closed by server."), doing); } break; case NE_SOCK_TIMEOUT: ne_set_error(sess, _("%s: connection timed out."), doing); ret = NE_TIMEOUT; break; case NE_SOCK_ERROR: case NE_SOCK_RESET: case NE_SOCK_TRUNC: ne_set_error(sess, "%s: %s", doing, ne_sock_error(sess->socket)); break; case 0: ne_set_error(sess, "%s", doing); break; } ne_close_connection(sess); return ret;}static void notify_status(ne_session *sess, ne_conn_status status, const char *info){ if (sess->notify_cb) { sess->notify_cb(sess->notify_ud, status, info); }}void ne_duplicate_header(void *userdata, const char *value){ char **location = userdata; *location = ne_strdup(value);}void ne_handle_numeric_header(void *userdata, const char *value){ int *location = userdata; *location = atoi(value);}static void *get_private(const struct hook *hk, const char *id){ for (; hk != NULL; hk = hk->next) if (strcmp(hk->id, id) == 0) return hk->userdata; return NULL;}void *ne_get_request_private(ne_request *req, const char *id){ return get_private(req->private, id);}void *ne_get_session_private(ne_session *sess, const char *id){ return get_private(sess->private, id);}typedef void (*void_fn)(void);#define ADD_HOOK(hooks, fn, ud) add_hook(&(hooks), NULL, (void_fn)(fn), (ud))static void add_hook(struct hook **hooks, const char *id, void_fn fn, void *ud){ struct hook *hk = ne_malloc(sizeof (struct hook)), *pos; if (*hooks != NULL) { for (pos = *hooks; pos->next != NULL; pos = pos->next) /* nullop */; pos->next = hk; } else { *hooks = hk; } hk->id = id; hk->fn = fn; hk->userdata = ud; hk->next = NULL;}void ne_hook_create_request(ne_session *sess, ne_create_request_fn fn, void *userdata){ ADD_HOOK(sess->create_req_hooks, fn, userdata);}void ne_hook_pre_send(ne_session *sess, ne_pre_send_fn fn, void *userdata){ ADD_HOOK(sess->pre_send_hooks, fn, userdata);}void ne_hook_post_send(ne_session *sess, ne_post_send_fn fn, void *userdata){ ADD_HOOK(sess->post_send_hooks, fn, userdata);}void ne_hook_destroy_request(ne_session *sess, ne_destroy_req_fn fn, void *userdata){ ADD_HOOK(sess->destroy_req_hooks, fn, userdata); }void ne_hook_destroy_session(ne_session *sess, ne_destroy_sess_fn fn, void *userdata){ ADD_HOOK(sess->destroy_sess_hooks, fn, userdata);}/* 0.24.x hack to fix ne_compress layer problems */void ne_kill_pre_send(ne_session *sess, ne_pre_send_fn fn, void *userdata){ struct hook **last, *hk; last = &sess->pre_send_hooks; hk = *last; while (hk) { if (hk->fn == (void_fn)fn && hk->userdata == userdata) { *last = hk->next; ne_free(hk); return; } last = &hk->next; hk = *last; }}void ne_set_session_private(ne_session *sess, const char *id, void *userdata){ add_hook(&sess->private, id, NULL, userdata);}void ne_set_request_private(ne_request *req, const char *id, void *userdata){ add_hook(&req->private, id, NULL, userdata);}static ssize_t body_string_send(void *userdata, char *buffer, size_t count){ ne_request *req = userdata; if (count == 0) { req->body.buf.left = req->body_size; req->body.buf.pnt = req->body.buf.buffer; } else { /* if body_left == 0 we fall through and return 0. */ if (req->body.buf.left < count) count = req->body.buf.left; memcpy(buffer, req->body.buf.pnt, count); req->body.buf.pnt += count; req->body.buf.left -= count; } return count;} static ssize_t body_fd_send(void *userdata, char *buffer, size_t count){ ne_request *req = userdata; if (count) { return read(req->body.fd, buffer, count); } else { /* rewind since we may have to send it again */ return lseek(req->body.fd, SEEK_SET, 0); }}/* Pulls the request body from the source and pushes it to the given * callback. Returns 0 on success, or NE_* code */int ne_pull_request_body(ne_request *req, ne_push_fn fn, void *ud){ int ret = 0; char buffer[BUFSIZ]; ssize_t bytes; /* tell the source to start again from the beginning. */ (void) req->body_cb(req->body_ud, NULL, 0); /* TODO: should this attempt to pull exactly the number of bytes * they specified were in the body? Currently it just pulls until * they return zero. That makes it possible to extend this to do * chunked request bodies (i.e. indefinitely long, no C-L), so * this is probably a better long-term interface. */ while ((bytes = req->body_cb(req->body_ud, buffer, sizeof buffer)) > 0) { ret = fn(ud, buffer, bytes); if (ret < 0) break; NE_DEBUG(NE_DBG_HTTPBODY, "Body block (%" NE_FMT_SSIZE_T " bytes):\n[%.*s]\n", bytes, (int)bytes, buffer); } if (bytes < 0) { ne_set_error(req->session, _("Error reading request body.")); ret = NE_ERROR; } return ret;}static int send_with_progress(void *userdata, const char *data, size_t n){ ne_request *req = userdata; int ret; ret = ne_sock_fullwrite(req->session->socket, data, n); if (ret == 0) { req->body_progress += n; req->session->progress_cb(req->session->progress_ud, req->body_progress, req->body_size); } return ret; }/* Sends the request body down the socket. * Returns 0 on success, or NE_* code */static int send_request_body(ne_request *req){ int ret; NE_DEBUG(NE_DBG_HTTP, "Sending request body...\n"); if (req->session->progress_cb) { /* with progress callbacks. */ req->body_progress = 0; ret = ne_pull_request_body(req, send_with_progress, req); } else { /* without progress callbacks. */ ret = ne_pull_request_body(req, (ne_push_fn)ne_sock_fullwrite, req->session->socket); } NE_DEBUG(NE_DBG_HTTP, "Request body sent: %s.\n", ret?"failed":"okay"); return ret;}/* Lob the User-Agent, connection and host headers in to the request * headers */static void add_fixed_headers(ne_request *req) { if (req->session->user_agent) { ne_buffer_zappend(req->headers, req->session->user_agent); } /* Send Connection: Keep-Alive to pre-1.1 origin servers to try * harder to get a persistent connection, except if using a proxy * as per 2068 sec 19.7.1. Always add TE: trailers since those * are understood. */ if (!req->session->is_http11 && !req->session->use_proxy) { ne_buffer_zappend(req->headers, "Keep-Alive: " EOL "Connection: TE, Keep-Alive" EOL "TE: trailers" EOL); } else { ne_buffer_zappend(req->headers, "Connection: TE" EOL "TE: trailers" EOL); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -