⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ne_request.c

📁 linux subdivision ying gai ke yi le ba
💻 C
📖 第 1 页 / 共 3 页
字号:
/*    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 + -