http-client-request.c revision d3d941cc89a8ef5fe0de16bd89e50030e5d22f5b
/* 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 "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;
}
/* 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 */
}
{
}
{
}
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
{
}
{
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.
Responses to the HEAD request method never include a message body
because the associated response header fields, if present, indicate only
what their values would have been if the request method had been GET
2xx (Successful) responses to CONNECT switch to tunnel mode instead of
having a message body (Section 4.3.6 of [Part2]).
*/
}
{
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)
}
}
{
}
static void
{
}
}
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;
}
if (!req->payload_sync) {
ret = -1;
} else {
}
} else {
}
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;
}
}
{
req->delayed_error);
}
{
/* 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);
Section-7.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)
{
}