http-client-request.c revision 840abb812d2e8edc42b5e2a4e3838b8d5e759e6a
/* Copyright (c) 2013-2014 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "net.h"
#include "str.h"
#include "hash.h"
#include "array.h"
#include "time-util.h"
#include "istream.h"
#include "ostream.h"
#include "dns-lookup.h"
#include "http-url.h"
#include "http-date.h"
#include "http-response-parser.h"
#include "http-transfer.h"
#include "http-client-private.h"
const char *http_request_state_names[] = {
"new",
"queued",
"payload_out",
"waiting",
"got_response",
"payload_in",
"finished",
"aborted"
};
/*
* Logging
*/
static inline void
static inline void
const char *format, ...)
{
i_debug("http-client: request %s: %s",
}
}
/*
* Request
*/
static struct http_client_request *
{
struct http_client_request *req;
return req;
}
struct http_client_request *
{
struct http_client_request *req;
return req;
}
struct http_client_request *
{
struct http_client_request *req;
return req;
}
struct http_client_request *
void *context)
{
struct http_client_request *req;
return req;
}
struct http_client_request *
void *context)
{
struct http_client_request *req;
return req;
}
{
}
{
return;
/* cannot be destroyed while it is still pending */
}
/* only decrease pending request counter if this request was submitted */
}
{
}
bool ssl)
{
}
{
}
{
/* allow calling for retries */
/* mark presence of special headers */
switch (key[0]) {
case 'c': case 'C':
break;
case 'd': case 'D':
break;
case 'e': case 'E':
break;
case 'h': case 'H':
break;
case 't': case 'T':
break;
case 'u': case 'U':
break;
}
}
const char *key)
{
const unsigned char *data, *p;
/* allow calling for retries */
/* key was found from header, replace its value */
break;
}
}
}
{
}
{
int ret;
if (ret < 0) {
i_error("i_stream_get_size(%s) failed: %s",
}
req->payload_size = 0;
}
/* prepare request payload sync using 100 Continue response from server */
}
unsigned int msecs)
{
}
{
req->timeout_msecs = 0;
}
{
}
{
}
unsigned int msecs)
{
}
const struct http_response *response)
{
unsigned int max;
return 0; /* no delay */
if (retry_after < ioloop_time)
return 0; /* delay already expired */
return -1; /* delay too long */
return 1; /* valid delay */
}
{
}
{
}
enum http_request_state
{
}
{
/* RFC 7230, Section 3.3:
The presence of a message body in a response depends on both the
request method to which it is responding and the response status code
(Section 3.1.2 of [RFC7230]). Responses to the HEAD request method
(Section 4.3.2 of [RFC7231]) never include a message body because the
associated response header fields (e.g., Transfer-Encoding,
Content-Length, etc.), if present, indicate only what their values
would have been if the request method had been GET (Section 4.3.1 of
[RFC7231]). 2xx (Successful) responses to a CONNECT request method
(Section 4.3.6 of [RFC7231]) switch to tunnel mode instead of having a
message body.
*/
}
{
struct http_client_host *host;
if (req->connect_tunnel) {
/* connect requests require authority form for request target */
} else {
/* absolute target url */
}
/* determine what host to contact to submit this request */
!req->connect_tunnel) {
} else {
}
} else {
}
/* use submission date if no date is set explicitly */
/* prepare value for Host header */
/* debug label */
/* update request target */
/* if we don't have a proxy, CONNECT requests are handled by creating
the requested connection directly */
if (req->connect_direct)
}
if (req->timeout_msecs > 0) {
}
}
}
{
}
static void
{
/* drop payload output stream */
}
/* advance state only when request didn't get aborted in the mean time */
}
/* release connection */
}
static int
{
int ret;
} else {
}
req->payload_size = 0;
/* Wait for payload data to be written */
break;
}
}
ret = 1;
break;
ret = -1;
break;
default:
ret = 0;
break;
}
/* callback may have messed with our pointer,
so unref using local variable */
/* Return status */
return ret;
}
{
}
{
}
{
(void)http_client_connection_output(conn);
}
const char **error_r)
{
/* chunked ostream needs to write to the parent stream's buffer */
/* the payload stream assigned to this request is broken,
fail this the request immediately */
"Broken payload stream");
/* we're in the middle of sending a request, so the connection
will also have to be aborted */
return -1;
} else if (output->stream_errno != 0) {
/* failed to send request */
return -1;
}
if (!req->payload_chunked &&
return -1;
}
if (req->payload_wait) {
} else {
}
/* output is blocking */
} else {
/* input is blocking */
}
return 0;
}
const char **error_r)
{
int ret = 0;
/* create request line */
/* create special headers implicitly if not set explicitly using
http_client_request_add_header() */
if (!req->have_hdr_host) {
}
if (!req->have_hdr_date) {
}
}
}
if (req->payload_chunked) {
// FIXME: can't do this for a HTTP/1.0 server
if (!req->have_hdr_body_spec)
} else {
/* send Content-Length if we have specified a payload,
even if it's 0 bytes. */
if (!req->have_hdr_body_spec) {
req->payload_size);
}
}
}
Section 19.7.1:
A client MUST NOT send the Keep-Alive connection token to a proxy
server as HTTP/1.0 proxy servers do not obey the rules of HTTP/1.1
for parsing the Connection header field.
*/
}
/* request line + implicit headers */
/* explicit headers */
} else {
}
/* end of header */
ret = -1;
} else {
if (!req->payload_sync) {
ret = -1;
} else {
}
} else {
}
ret = -1;
}
}
return ret;
}
const char **error_r)
{
int ret;
T_BEGIN {
} T_END;
return ret;
}
struct http_response *response)
{
/* retrying */
return FALSE;
} else {
}
}
return TRUE;
}
void
{
return;
struct http_response response;
}
}
{
return;
req->delayed_error);
}
{
return;
/* we're still in http_client_request_submit(). delay
reporting the error, so the caller doesn't have to handle
immediate callbacks. */
} else {
}
}
{
return;
}
{
return;
}
{
/* parse URL */
return;
}
t_strdup_printf("Redirected more than %d times",
} else {
"Redirect refused");
}
return;
}
/* rewind payload stream */
"Redirect failed: Cannot resend payload; stream is not seekable");
return;
} else {
}
}
/* drop payload output stream from previous attempt */
}
origin_url, target);
/* RFC 7231, Section 6.4.4:
-> A 303 `See Other' redirect status response is handled a bit differently.
Basically, the response content is located elsewhere, but the original
(POST) request is handled already.
*/
// FIXME: should we provide the means to skip this step? The original
// request was already handled at this point.
/* drop payload */
req->payload_size = 0;
req->payload_offset = 0;
}
/* resubmit */
}
{
/* rewind payload stream */
"Resubmission failed: Cannot resend payload; stream is not seekable");
return;
} else {
}
}
/* rewind payload stream */
"Resubmission failed: Cannot resend payload; stream is not seekable");
return;
} else {
}
}
/* drop payload output stream from previous attempt */
}
{
if (!http_client_request_try_retry(req))
}
{
/* don't ever retry if we're sending data in small blocks via
http_client_request_send_payload() and we're not waiting for a
100 continue (there's no way to rewind the payload for a retry)
*/
if (req->payload_wait &&
return FALSE;
/* limit the number of attempts for each request */
return FALSE;
return TRUE;
}
void (*callback)(void *),
void *context)
{
}
struct http_client_tunnel *tunnel)
{
}