📄 ne_request.c
字号:
SOCK_ERR(req, ne_sock_readline(req->session->socket, req->respbuf, sizeof req->respbuf), _("Could not read interim response headers")); NE_DEBUG(NE_DBG_HTTP, "[discard] < %s", req->respbuf); } while (strcmp(req->respbuf, EOL) != 0); return NE_OK;}/* Send the request, and read the response Status-Line. Returns: * NE_RETRY connection closed by server; persistent connection * timeout * NE_OK success * NE_* error * On NE_RETRY and NE_* responses, the connection will have been * closed already. */static int send_request(ne_request *req, const ne_buffer *request){ ne_session *sess = req->session; ssize_t ret = NE_OK; int sentbody = 0; /* zero until body has been sent. */ int retry; /* non-zero whilst the request should be retried */ ne_status *status = &req->status; /* Send the Request-Line and headers */ NE_DEBUG(NE_DBG_HTTP, "Sending request-line and headers:\n"); /* Open the connection if necessary */ HTTP_ERR(open_connection(req)); /* Allow retry if a persistent connection has been used. */ retry = sess->persisted; ret = ne_sock_fullwrite(req->session->socket, request->data, ne_buffer_size(request)); if (ret < 0) { int aret = aborted(req, _("Could not send request"), ret); return RETRY_RET(retry, ret, aret); } if (!req->use_expect100 && req->body_size > 0) { /* Send request body, if not using 100-continue. */ ret = send_request_body(req); if (ret < 0) { int aret = aborted(req, _("Could not send request body"), ret); return RETRY_RET(sess, ret, aret); } } NE_DEBUG(NE_DBG_HTTP, "Request sent; retry is %d.\n", retry); /* Loop eating interim 1xx responses (RFC2616 says these MAY be * sent by the server, even if 100-continue is not used). */ while ((ret = read_status_line(req, status, retry)) == NE_OK && status->klass == 1) { NE_DEBUG(NE_DBG_HTTP, "Interim %d response.\n", status->code); retry = 0; /* successful read() => never retry now. */ /* Discard headers with the interim response. */ if ((ret = discard_headers(req)) != NE_OK) break; if (req->use_expect100 && (status->code == 100) && !sentbody) { /* Send the body after receiving the first 100 Continue */ if ((ret = send_request_body(req)) != NE_OK) break; sentbody = 1; } } return ret;}/* Read a message header from sock into buf, which has size 'buflen'. * * Returns: * NE_RETRY: Success, read a header into buf. * NE_OK: End of headers reached. * NE_ERROR: Error (session error is set). */static int read_message_header(ne_request *req, char *buf, size_t buflen){ ssize_t n; ne_socket *sock = req->session->socket; n = ne_sock_readline(sock, buf, buflen); if (n <= 0) return aborted(req, _("Error reading response headers"), n); NE_DEBUG(NE_DBG_HTTP, "[hdr] %s", buf); strip_eol(buf, &n); if (n == 0) { NE_DEBUG(NE_DBG_HTTP, "End of headers.\n"); return NE_OK; } buf += n; buflen -= n; while (buflen > 0) { char ch; /* Collect any extra lines into buffer */ SOCK_ERR(req, ne_sock_peek(sock, &ch, 1), _("Error reading response headers")); if (ch != ' ' && ch != '\t') { /* No continuation of this header: stop reading. */ return NE_RETRY; } /* Otherwise, read the next line onto the end of 'buf'. */ n = ne_sock_readline(sock, buf, buflen); if (n <= 0) { return aborted(req, _("Error reading response headers"), n); } NE_DEBUG(NE_DBG_HTTP, "[cont] %s", buf); strip_eol(buf, &n); /* assert(buf[0] == ch), which implies len(buf) > 0. * Otherwise the TCP stack is lying, but we'll be paranoid. * This might be a \t, so replace it with a space to be * friendly to applications (2616 says we MAY do this). */ if (n) buf[0] = ' '; /* ready for the next header. */ buf += n; buflen -= n; } ne_set_error(req->session, _("Response header too long")); return NE_ERROR;}/* Apache's default is 100, seems reasonable. */#define MAX_HEADER_FIELDS 100/* Read response headers. Returns NE_* code, sets session error. */static int read_response_headers(ne_request *req) { char hdr[8192]; /* max header length */ int ret, count = 0; while ((ret = read_message_header(req, hdr, sizeof hdr)) == NE_RETRY && ++count < MAX_HEADER_FIELDS) { struct header_handler *hdl; char *pnt; unsigned int hash = 0; for (hdl = req->header_catchers; hdl != NULL; hdl = hdl->next) hdl->handler(hdl->userdata, hdr); /* Strip any trailing whitespace */ pnt = hdr + strlen(hdr) - 1; while (pnt > hdr && (*pnt == ' ' || *pnt == '\t')) *pnt-- = '\0'; /* Convert the header name to lower case and hash it. */ for (pnt = hdr; (*pnt != '\0' && *pnt != ':' && *pnt != ' ' && *pnt != '\t'); pnt++) { *pnt = tolower(*pnt); hash = HH_ITERATE(hash,*pnt); } /* Skip over any whitespace before the colon. */ while (*pnt == ' ' || *pnt == '\t') *pnt++ = '\0'; /* ignore header lines which lack a ':'. */ if (*pnt != ':') continue; /* NUL-terminate at the colon (when no whitespace before) */ *pnt++ = '\0'; /* Skip any whitespace after the colon... */ while (*pnt == ' ' || *pnt == '\t') pnt++; /* pnt now points to the header value. */ NE_DEBUG(NE_DBG_HTTP, "Header Name: [%s], Value: [%s]\n", hdr, pnt); /* Iterate through the header handlers */ for (hdl = req->header_handlers[hash]; hdl != NULL; hdl = hdl->next) { if (strcmp(hdr, hdl->name) == 0) hdl->handler(hdl->userdata, pnt); } } if (count == MAX_HEADER_FIELDS) ret = aborted( req, _("Response exceeded maximum number of header fields."), 0); return ret;}static int lookup_host(ne_session *sess, struct host_info *info){ NE_DEBUG(NE_DBG_HTTP, "Doing DNS lookup on %s...\n", info->hostname); if (sess->notify_cb) sess->notify_cb(sess->notify_ud, ne_conn_namelookup, info->hostname); info->address = ne_addr_resolve(info->hostname, 0); if (ne_addr_result(info->address)) { char buf[256]; ne_set_error(sess, _("Could not resolve hostname `%s': %s"), info->hostname, ne_addr_error(info->address, buf, sizeof buf)); ne_addr_destroy(info->address); info->address = NULL; return NE_LOOKUP; } else { return NE_OK; }}int ne_begin_request(ne_request *req){ struct body_reader *rdr; struct host_info *host; ne_buffer *data; const ne_status *const st = &req->status; int ret; /* Resolve hostname if necessary. */ host = req->session->use_proxy?&req->session->proxy:&req->session->server; if (host->address == NULL) HTTP_ERR(lookup_host(req->session, host)); req->resp.mode = R_TILLEOF; /* FIXME: Determine whether to use the Expect: 100-continue header. */ req->use_expect100 = (req->session->expect100_works > -1) && (req->body_size > HTTP_EXPECT_MINSIZE) && req->session->is_http11; /* Build the request string, and send it */ data = build_request(req); DEBUG_DUMP_REQUEST(data->data); ret = send_request(req, data); /* Retry this once after a persistent connection timeout. */ if (ret == NE_RETRY && !req->session->no_persist) { NE_DEBUG(NE_DBG_HTTP, "Persistent connection timed out, retrying.\n"); ret = send_request(req, data); } ne_buffer_destroy(data); if (ret != NE_OK) return ret; /* Determine whether server claims HTTP/1.1 compliance. */ req->session->is_http11 = (st->major_version == 1 && st->minor_version > 0) || st->major_version > 1; /* Persistent connections supported implicitly in HTTP/1.1 */ if (req->session->is_http11) req->can_persist = 1; ne_set_error(req->session, "%d %s", st->code, st->reason_phrase); /* Read the headers */ HTTP_ERR(read_response_headers(req));#ifdef NEON_SSL /* Special case for CONNECT handling: the response has no body, * and the connection can persist. */ if (req->session->in_connect && st->klass == 2) { req->resp.mode = R_NO_BODY; req->can_persist = 1; }#endif /* HEAD requests and 204, 205, 304 responses have no response body, * regardless of what headers are present. */ if (req->method_is_head || st->code==204 || st->code==205 || st->code==304) req->resp.mode = R_NO_BODY; /* Prepare for reading the response entity-body. Call each of the * body readers and ask them whether they want to accept this * response or not. */ for (rdr = req->body_readers; rdr != NULL; rdr=rdr->next) { rdr->use = rdr->accept_response(rdr->userdata, req, st); } req->resp.left = req->resp.length; req->resp.chunk_left = 0; return NE_OK;}int ne_end_request(ne_request *req){ struct hook *hk; int ret = NE_OK; /* Read headers in chunked trailers */ if (req->resp.mode == R_CHUNKED) HTTP_ERR(read_response_headers(req)); NE_DEBUG(NE_DBG_HTTP, "Running post_send hooks\n"); for (hk = req->session->post_send_hooks; ret == NE_OK && hk != NULL; hk = hk->next) { ne_post_send_fn fn = (ne_post_send_fn)hk->fn; ret = fn(req, hk->userdata, &req->status); } /* Close the connection if persistent connections are disabled or * not supported by the server. */ if (req->session->no_persist || !req->can_persist) ne_close_connection(req->session); else req->session->persisted = 1; return ret;}int ne_request_dispatch(ne_request *req) { int ret; /* Loop sending the request: * Retry whilst authentication fails and we supply it. */ do { ssize_t len; HTTP_ERR(ne_begin_request(req)); do { len = ne_read_response_block(req, req->respbuf, sizeof req->respbuf); } while (len > 0); if (len < 0) { return NE_ERROR; } ret = ne_end_request(req); } while (ret == NE_RETRY); NE_DEBUG(NE_DBG_HTTP | NE_DBG_FLUSH, "Request ends, status %d class %dxx, error line:\n%s\n", req->status.code, req->status.klass, req->session->error); return ret;}const ne_status *ne_get_status(const ne_request *req){ return &req->status;}ne_session *ne_get_session(const ne_request *req){ return req->session;}#ifdef NEON_SSL/* Create a CONNECT tunnel through the proxy server. * Returns HTTP_* */static int proxy_tunnel(ne_session *sess){ /* Hack up an HTTP CONNECT request... */ ne_request *req; int ret = NE_OK; char ruri[200]; /* Can't use server.hostport here; Request-URI must include `:port' */ ne_snprintf(ruri, sizeof ruri, "%s:%u", sess->server.hostname, sess->server.port); req = ne_request_create(sess, "CONNECT", ruri); sess->in_connect = 1; ret = ne_request_dispatch(req); sess->in_connect = 0; sess->persisted = 0; /* don't treat this is a persistent connection. */ if (ret != NE_OK || !sess->connected || req->status.klass != 2) { ne_set_error (sess, _("Could not create SSL connection through proxy server")); ret = NE_ERROR; } ne_request_destroy(req); return ret;}#endif/* Make new TCP connection to server at 'host' of type 'name'. Note * that once a connection to a particular network address has * succeeded, that address will be used first for the next attempt to * connect. *//* TODO: an alternate implementation could always cycle through the * addresses: this could ease server load, but could hurt SSL session * caching for SSL sessions, which would increase server load. */static int do_connect(ne_request *req, struct host_info *host, const char *err){ ne_session *const sess = req->session; int ret; if ((sess->socket = ne_sock_create()) == NULL) { ne_set_error(sess, _("Could not create socket")); return NE_ERROR; } if (host->current == NULL) host->current = ne_addr_first(host->address); do { notify_status(sess, ne_conn_connecting, host->hostport);#ifdef NE_DEBUGGING if (ne_debug_mask & NE_DBG_HTTP) { char buf[150]; NE_DEBUG(NE_DBG_HTTP, "Connecting to %s\n", ne_iaddr_print(host->current, buf, sizeof buf)); }#endif ret = ne_sock_connect(sess->socket, host->current, host->port); } while (ret && /* try the next address... */ (host->current = ne_addr_next(host->address)) != NULL); if (ret) { ne_set_error(sess, "%s: %s", err, ne_sock_error(sess->socket)); ne_sock_close(sess->socket); return NE_CONNECT; } notify_status(sess, ne_conn_connected, sess->proxy.hostport); if (sess->rdtimeout) ne_sock_read_timeout(sess->socket, sess->rdtimeout); sess->connected = 1; /* clear persistent connection flag. */ sess->persisted = 0; return NE_OK;}static int open_connection(ne_request *req) { ne_session *sess = req->session; int ret; if (sess->connected) return NE_OK; if (!sess->use_proxy) ret = do_connect(req, &sess->server, _("Could not connect to server")); else ret = do_connect(req, &sess->proxy, _("Could not connect to proxy server")); if (ret != NE_OK) return ret;#ifdef NEON_SSL /* Negotiate SSL layer if required. */ if (sess->use_ssl && !sess->in_connect) { /* CONNECT tunnel */ if (req->session->use_proxy) ret = proxy_tunnel(sess); if (ret == NE_OK) ret = ne_negotiate_ssl(req); /* This is probably only really needed for ne_negotiate_ssl * failures as proxy_tunnel will fail via aborted(). */ if (ret != NE_OK) ne_close_connection(sess); }#endif return ret;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -