📄 ne_request.c
字号:
int ne_accept_always(void *userdata, ne_request *req, const ne_status *st){ return 1;} int ne_accept_2xx(void *userdata, ne_request *req, const ne_status *st){ return (st->klass == 2);}/* Handler for the "Transfer-Encoding" response header: treat *any* * such header as implying a chunked response, per the "Protocol * Compliance" statement in the manual. */static void te_hdr_handler(void *userdata, const char *value) { struct ne_response *resp = userdata; resp->mode = R_CHUNKED; }/* Handler for the "Connection" response header */static void connection_hdr_handler(void *userdata, const char *value){ ne_request *req = userdata; if (strcasecmp(value, "close") == 0) { req->can_persist = 0; } else if (strcasecmp(value, "Keep-Alive") == 0) { req->can_persist = 1; }}static void clength_hdr_handler(void *userdata, const char *value){ struct ne_response *resp = userdata; size_t len = strtoul(value, NULL, 10); if (len != ULONG_MAX && resp->mode == R_TILLEOF) { resp->mode = R_CLENGTH; resp->length = len; }}ne_request *ne_request_create(ne_session *sess, const char *method, const char *path) { ne_request *req = ne_calloc(sizeof *req); NE_DEBUG(NE_DBG_HTTP, "Creating request...\n"); req->session = sess; req->headers = ne_buffer_create(); /* Add in the fixed headers */ add_fixed_headers(req); /* Set the standard stuff */ req->method = ne_strdup(method); req->method_is_head = (strcmp(method, "HEAD") == 0); /* Add in handlers for all the standard HTTP headers. */ ne_add_response_header_handler(req, "Content-Length", clength_hdr_handler, &req->resp); ne_add_response_header_handler(req, "Transfer-Encoding", te_hdr_handler, &req->resp); ne_add_response_header_handler(req, "Connection", connection_hdr_handler, req); /* Only use an absoluteURI here when absolutely necessary: some * servers can't parse them. */ if (req->session->use_proxy && !req->session->use_ssl && path[0] == '/') req->uri = ne_concat(req->session->scheme, "://", req->session->server.hostport, path, NULL); else req->uri = ne_strdup(path); { struct hook *hk; NE_DEBUG(NE_DBG_HTTP, "Running request create hooks.\n"); for (hk = sess->create_req_hooks; hk != NULL; hk = hk->next) { ne_create_request_fn fn = (ne_create_request_fn)hk->fn; fn(req, hk->userdata, method, req->uri); } } NE_DEBUG(NE_DBG_HTTP, "Request created.\n"); return req;}static void set_body_size(ne_request *req, size_t size){ req->body_size = size; ne_print_request_header(req, "Content-Length", "%" NE_FMT_SIZE_T, size);}void ne_set_request_body_buffer(ne_request *req, const char *buffer, size_t size){ req->body.buf.buffer = buffer; req->body_cb = body_string_send; req->body_ud = req; set_body_size(req, size);}void ne_set_request_body_provider(ne_request *req, size_t bodysize, ne_provide_body provider, void *ud){ req->body_cb = provider; req->body_ud = ud; set_body_size(req, bodysize);}int ne_set_request_body_fd(ne_request *req, int fd){ struct stat bodyst; /* Get file length */ if (fstat(fd, &bodyst) < 0) { char err[200]; ne_strerror(errno, err, sizeof err); ne_set_error(req->session, _("Could not determine file length: %s"), err); NE_DEBUG(NE_DBG_HTTP, "Stat failed: %s\n", err); return -1; } req->body.fd = fd; req->body_cb = body_fd_send; req->body_ud = req; set_body_size(req, bodyst.st_size); return 0;}void ne_add_request_header(ne_request *req, const char *name, const char *value){ ne_buffer_concat(req->headers, name, ": ", value, EOL, NULL);}void ne_print_request_header(ne_request *req, const char *name, const char *format, ...){ va_list params; char buf[BUFSIZ]; va_start(params, format); ne_vsnprintf(buf, sizeof buf, format, params); va_end(params); ne_buffer_concat(req->headers, name, ": ", buf, EOL, NULL);}voidne_add_response_header_handler(ne_request *req, const char *name, ne_header_handler hdl, void *userdata){ struct header_handler *new = ne_calloc(sizeof *new); unsigned int hash; new->name = ne_strdup(name); new->handler = hdl; new->userdata = userdata; hash = hash_and_lower(new->name); new->next = req->header_handlers[hash]; req->header_handlers[hash] = new;}void ne_add_response_header_catcher(ne_request *req, ne_header_handler hdl, void *userdata){ struct header_handler *new = ne_calloc(sizeof *new); new->handler = hdl; new->userdata = userdata; new->next = req->header_catchers; req->header_catchers = new;}void ne_add_response_body_reader(ne_request *req, ne_accept_response acpt, ne_block_reader rdr, void *userdata){ struct body_reader *new = ne_malloc(sizeof *new); new->accept_response = acpt; new->handler = rdr; new->userdata = userdata; new->next = req->body_readers; req->body_readers = new;}void ne_request_destroy(ne_request *req) { struct body_reader *rdr, *next_rdr; struct header_handler *hdlr, *next_hdlr; struct hook *hk, *next_hk; int n; ne_free(req->uri); ne_free(req->method); for (rdr = req->body_readers; rdr != NULL; rdr = next_rdr) { next_rdr = rdr->next; ne_free(rdr); } for (hdlr = req->header_catchers; hdlr != NULL; hdlr = next_hdlr) { next_hdlr = hdlr->next; ne_free(hdlr); } for (n = 0; n < HH_HASHSIZE; n++) { for (hdlr = req->header_handlers[n]; hdlr != NULL; hdlr = next_hdlr) { next_hdlr = hdlr->next; ne_free(hdlr->name); ne_free(hdlr); } } ne_buffer_destroy(req->headers); NE_DEBUG(NE_DBG_HTTP, "Running destroy hooks.\n"); for (hk = req->session->destroy_req_hooks; hk; hk = hk->next) { ne_destroy_req_fn fn = (ne_destroy_req_fn)hk->fn; fn(req, hk->userdata); } for (hk = req->private; hk; hk = next_hk) { next_hk = hk->next; ne_free(hk); } if (req->status.reason_phrase) ne_free(req->status.reason_phrase); NE_DEBUG(NE_DBG_HTTP, "Request ends.\n"); ne_free(req);}/* Reads a block of the response into buffer, which is of size buflen. * Returns number of bytes read, 0 on end-of-response, or NE_* on error. * TODO?: only make one actual read() call in here... */static int read_response_block(ne_request *req, struct ne_response *resp, char *buffer, size_t *buflen) { size_t willread; ssize_t readlen; ne_socket *sock = req->session->socket; switch (resp->mode) { case R_CHUNKED: /* We are doing a chunked transfer-encoding. * It goes: `SIZE CRLF CHUNK CRLF SIZE CRLF CHUNK CRLF ...' * ended by a `CHUNK CRLF 0 CRLF', a 0-sized chunk. * The slight complication is that we have to cope with * partial reads of chunks. * For this reason, resp.chunk_left contains the number of * bytes left to read in the current chunk. */ if (resp->chunk_left == 0) { unsigned long int chunk_len; char *ptr; /* We are at the start of a new chunk. */ NE_DEBUG(NE_DBG_HTTP, "New chunk.\n"); SOCK_ERR(req, ne_sock_readline(sock, buffer, *buflen), _("Could not read chunk size")); NE_DEBUG(NE_DBG_HTTP, "[Chunk Size] < %s", buffer); chunk_len = strtoul(buffer, &ptr, 16); /* limit chunk size to <= UINT_MAX, so it will probably * fit in a size_t. */ if (ptr == buffer || chunk_len == ULONG_MAX || chunk_len > UINT_MAX) { return aborted(req, _("Could not parse chunk size"), 0); } NE_DEBUG(NE_DBG_HTTP, "Got chunk size: %lu\n", chunk_len); if (chunk_len == 0) { /* Zero-size chunk == end of response. */ NE_DEBUG(NE_DBG_HTTP, "Zero-size chunk.\n"); *buflen = 0; return NE_OK; } resp->chunk_left = chunk_len; } willread = resp->chunk_left; break; case R_CLENGTH: willread = resp->left; break; case R_TILLEOF: willread = *buflen; break; case R_NO_BODY: default: willread = 0; break; } if (willread > *buflen) willread = *buflen; else if (willread == 0) { *buflen = 0; return 0; } NE_DEBUG(NE_DBG_HTTP, "Reading %" NE_FMT_SIZE_T " bytes of response body.\n", willread); readlen = ne_sock_read(sock, buffer, willread); /* EOF is only valid when response body is delimited by it. * Strictly, an SSL truncation should not be treated as an EOF in * any case, but SSL servers are just too buggy. */ if (resp->mode == R_TILLEOF && (readlen == NE_SOCK_CLOSED || readlen == NE_SOCK_TRUNC)) { NE_DEBUG(NE_DBG_HTTP, "Got EOF.\n"); req->can_persist = 0; readlen = 0; } else if (readlen < 0) { return aborted(req, _("Could not read response body"), readlen); } else { NE_DEBUG(NE_DBG_HTTP, "Got %" NE_FMT_SSIZE_T " bytes.\n", readlen); } /* safe to cast: readlen guaranteed to be >= 0 above */ *buflen = (size_t)readlen; NE_DEBUG(NE_DBG_HTTPBODY, "Read block (%" NE_FMT_SSIZE_T " bytes):\n[%.*s]\n", readlen, (int)readlen, buffer); if (resp->mode == R_CHUNKED) { resp->chunk_left -= readlen; if (resp->chunk_left == 0) { char crlfbuf[2]; /* If we've read a whole chunk, read a CRLF */ readlen = ne_sock_fullread(sock, crlfbuf, 2); if (readlen < 0) return aborted(req, _("Could not read chunk delimiter"), readlen); else if (crlfbuf[0] != '\r' || crlfbuf[1] != '\n') return aborted(req, _("Chunk delimiter was invalid"), 0); } } else if (resp->mode == R_CLENGTH) { resp->left -= readlen; } return NE_OK;}ssize_t ne_read_response_block(ne_request *req, char *buffer, size_t buflen){ struct body_reader *rdr; size_t readlen = buflen; if (read_response_block(req, &req->resp, buffer, &readlen)) return -1; req->resp.total += readlen; if (req->session->progress_cb) { req->session->progress_cb(req->session->progress_ud, req->resp.total, (req->resp.mode==R_CLENGTH)?req->resp.length:-1); } /* TODO: call the readers when this fails too. */ for (rdr = req->body_readers; rdr!=NULL; rdr=rdr->next) { if (rdr->use) rdr->handler(rdr->userdata, buffer, readlen); } return readlen;}/* Build the request string, returning the buffer. */static ne_buffer *build_request(ne_request *req) { struct hook *hk; ne_buffer *buf = ne_buffer_create(); /* Add Request-Line and Host header: */ ne_buffer_concat(buf, req->method, " ", req->uri, " HTTP/1.1" EOL, "Host: ", req->session->server.hostport, EOL, NULL); /* Add custom headers: */ ne_buffer_append(buf, req->headers->data, ne_buffer_size(req->headers));#define E100 "Expect: 100-continue" EOL if (req->use_expect100) ne_buffer_append(buf, E100, strlen(E100)); NE_DEBUG(NE_DBG_HTTP, "Running pre_send hooks\n"); for (hk = req->session->pre_send_hooks; hk!=NULL; hk = hk->next) { ne_pre_send_fn fn = (ne_pre_send_fn)hk->fn; fn(req, hk->userdata, buf); } ne_buffer_append(buf, "\r\n", 2); return buf;}#ifdef NE_DEBUGGING#define DEBUG_DUMP_REQUEST(x) dump_request(x)static void dump_request(const char *request){ if ((NE_DBG_HTTPPLAIN&ne_debug_mask) == NE_DBG_HTTPPLAIN) { /* Display everything mode */ NE_DEBUG(NE_DBG_HTTP, "Sending request headers:\n%s", request); } else { /* Blank out the Authorization paramaters */ char *reqdebug = ne_strdup(request), *pnt = reqdebug; while ((pnt = strstr(pnt, "Authorization: ")) != NULL) { for (pnt += 15; *pnt != '\r' && *pnt != '\0'; pnt++) { *pnt = 'x'; } } NE_DEBUG(NE_DBG_HTTP, "Sending request headers:\n%s", reqdebug); ne_free(reqdebug); }}#else#define DEBUG_DUMP_REQUEST(x)#endif /* DEBUGGING *//* remove trailing EOL from 'buf', where strlen(buf) == *len. *len is * adjusted in accordance with any changes made to the string to * remain equal to strlen(buf). */static inline void strip_eol(char *buf, ssize_t *len){ char *pnt = &buf[*len-1]; while (pnt >= buf && (*pnt == '\r' || *pnt == '\n')) { *pnt-- = '\0'; (*len)--; }}/* For accurate persistent connection handling, for any write() or * read() operation for a new request on an already-open connection, * an EOF or RST error MUST be treated as a persistent connection * timeout, and the request retried on a new connection. Once a * read() operation has succeeded, any subsequent error MUST be * treated as fatal. A 'retry' flag is used; retry=1 represents the * first case, retry=0 the latter. *//* RETRY_RET() crafts a function return value given the 'retry' flag, * the socket error 'code', and the return value 'acode' from the * aborted() function. */#define RETRY_RET(retry, code, acode) \((((code) == NE_SOCK_CLOSED || (code) == NE_SOCK_RESET || \ (code) == NE_SOCK_TRUNC) && retry) ? NE_RETRY : (acode))/* Read and parse response status-line into 'status'. 'retry' is non-zero * if an NE_RETRY should be returned if an EOF is received. */static int read_status_line(ne_request *req, ne_status *status, int retry){ char *buffer = req->respbuf; ssize_t ret; ret = ne_sock_readline(req->session->socket, buffer, sizeof req->respbuf); if (ret <= 0) { int aret = aborted(req, _("Could not read status line"), ret); return RETRY_RET(retry, ret, aret); } NE_DEBUG(NE_DBG_HTTP, "[status-line] < %s", buffer); strip_eol(buffer, &ret); if (status->reason_phrase) ne_free(status->reason_phrase); memset(status, 0, sizeof *status); if (ne_parse_statusline(buffer, status)) return aborted(req, _("Could not parse response status line."), 0); return 0;}/* Discard a set of message headers. */static int discard_headers(ne_request *req){ do {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -