mirror of
https://github.com/libevent/libevent.git
synced 2025-01-31 09:12:55 +08:00
reorganization of the http functionality; we separate http handling into a
connection object and a request object; also make it clear which buffers are used for input and output; unittests not complete yet. svn:r217
This commit is contained in:
parent
00bc7e37fd
commit
ba7262ebdf
23
evhttp.h
23
evhttp.h
@ -82,9 +82,32 @@ void evhttp_send_reply(struct evhttp_request *, int, const char *,
|
|||||||
/* Interfaces for making requests */
|
/* Interfaces for making requests */
|
||||||
enum evhttp_cmd_type { EVHTTP_REQ_GET, EVHTTP_REQ_POST, EVHTTP_REQ_HEAD };
|
enum evhttp_cmd_type { EVHTTP_REQ_GET, EVHTTP_REQ_POST, EVHTTP_REQ_HEAD };
|
||||||
|
|
||||||
|
/* Creates a new request object that needs to be filled in with the request
|
||||||
|
* parameters. The callback is executed when the request completed or an
|
||||||
|
* error occurred.
|
||||||
|
*/
|
||||||
struct evhttp_request *evhttp_request_new(
|
struct evhttp_request *evhttp_request_new(
|
||||||
void (*cb)(struct evhttp_request *, void *), void *arg);
|
void (*cb)(struct evhttp_request *, void *), void *arg);
|
||||||
|
|
||||||
|
/* Frees the request object and removes associated events. */
|
||||||
void evhttp_request_free(struct evhttp_request *req);
|
void evhttp_request_free(struct evhttp_request *req);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A connection object that can be used to for making HTTP requests. The
|
||||||
|
* connection object tries to establish the connection when it is given an
|
||||||
|
* http request object.
|
||||||
|
*/
|
||||||
|
struct evhttp_connection *evhttp_connection_new(
|
||||||
|
const char *address, unsigned short port);
|
||||||
|
|
||||||
|
/* Frees an http connection */
|
||||||
|
void evhttp_connection_free(struct evhttp_connection *evcon);
|
||||||
|
|
||||||
|
/* The connection gets ownership of the request */
|
||||||
|
int evhttp_make_request(struct evhttp_connection *evcon,
|
||||||
|
struct evhttp_request *req,
|
||||||
|
enum evhttp_cmd_type type, const char *uri);
|
||||||
|
|
||||||
const char *evhttp_request_uri(struct evhttp_request *req);
|
const char *evhttp_request_uri(struct evhttp_request *req);
|
||||||
|
|
||||||
/* Interfaces for dealing with HTTP headers */
|
/* Interfaces for dealing with HTTP headers */
|
||||||
|
@ -19,16 +19,33 @@
|
|||||||
|
|
||||||
struct evbuffer;
|
struct evbuffer;
|
||||||
struct addrinfo;
|
struct addrinfo;
|
||||||
|
struct evhttp_request;
|
||||||
|
|
||||||
/* A stupid connection object - maybe make this a bufferevent later */
|
/* A stupid connection object - maybe make this a bufferevent later */
|
||||||
|
|
||||||
|
enum evhttp_connection_state {
|
||||||
|
EVCON_DISCONNECTED, /* not currently connected not trying either */
|
||||||
|
EVCON_CONNECTING, /* tries to currently connect */
|
||||||
|
EVCON_CONNECTED /* connection is established */
|
||||||
|
};
|
||||||
|
|
||||||
struct evhttp_connection {
|
struct evhttp_connection {
|
||||||
int fd;
|
int fd;
|
||||||
struct event ev;
|
struct event ev;
|
||||||
|
struct evbuffer *input_buffer;
|
||||||
|
struct evbuffer *output_buffer;
|
||||||
|
|
||||||
char *address;
|
char *address;
|
||||||
u_short port;
|
u_short port;
|
||||||
|
|
||||||
|
int flags;
|
||||||
|
#define EVHTTP_CON_INCOMING 0x0001 /* only one request on it ever */
|
||||||
|
#define EVHTTP_CON_OUTGOING 0x0002 /* multiple requests possible */
|
||||||
|
|
||||||
|
enum evhttp_connection_state state;
|
||||||
|
|
||||||
|
TAILQ_HEAD(evcon_requestq, evhttp_request) requests;
|
||||||
|
|
||||||
void (*cb)(struct evhttp_connection *, void *);
|
void (*cb)(struct evhttp_connection *, void *);
|
||||||
void *cb_arg;
|
void *cb_arg;
|
||||||
};
|
};
|
||||||
@ -36,9 +53,17 @@ struct evhttp_connection {
|
|||||||
enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE };
|
enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE };
|
||||||
|
|
||||||
struct evhttp_request {
|
struct evhttp_request {
|
||||||
|
TAILQ_ENTRY(evhttp_request) next;
|
||||||
|
|
||||||
|
/* the connection object that this request belongs to */
|
||||||
|
struct evhttp_connection *evcon;
|
||||||
|
int flags;
|
||||||
|
#define EVHTTP_REQ_OWN_CONNECTION 0x0001
|
||||||
|
|
||||||
struct evkeyvalq *input_headers;
|
struct evkeyvalq *input_headers;
|
||||||
struct evkeyvalq *output_headers;
|
struct evkeyvalq *output_headers;
|
||||||
|
|
||||||
|
/* xxx: do we still need these? */
|
||||||
char *remote_host;
|
char *remote_host;
|
||||||
u_short remote_port;
|
u_short remote_port;
|
||||||
|
|
||||||
@ -54,19 +79,14 @@ struct evhttp_request {
|
|||||||
int response_code; /* HTTP Response code */
|
int response_code; /* HTTP Response code */
|
||||||
char *response_code_line; /* Readable response */
|
char *response_code_line; /* Readable response */
|
||||||
|
|
||||||
int fd;
|
struct evbuffer *input_buffer; /* read data */
|
||||||
|
|
||||||
struct event ev;
|
|
||||||
|
|
||||||
struct evbuffer *buffer;
|
|
||||||
int ntoread;
|
int ntoread;
|
||||||
|
|
||||||
|
struct evbuffer *output_buffer; /* outgoing post or data */
|
||||||
|
|
||||||
/* Callback */
|
/* Callback */
|
||||||
void (*cb)(struct evhttp_request *, void *);
|
void (*cb)(struct evhttp_request *, void *);
|
||||||
void *cb_arg;
|
void *cb_arg;
|
||||||
|
|
||||||
void (*save_cb)(struct evhttp_request *, void *);
|
|
||||||
void *save_cbarg;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct evhttp_cb {
|
struct evhttp_cb {
|
||||||
@ -87,39 +107,31 @@ struct evhttp {
|
|||||||
void *gencbarg;
|
void *gencbarg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* resets the connection; can be reused for more requests */
|
||||||
|
void evhttp_connection_reset(struct evhttp_connection *);
|
||||||
|
|
||||||
|
/* connects if necessary */
|
||||||
|
int evhttp_connection_connect(struct evhttp_connection *);
|
||||||
|
|
||||||
|
/* notifies the current request that it failed; resets connection */
|
||||||
|
void evhttp_connection_fail(struct evhttp_connection *);
|
||||||
|
|
||||||
void evhttp_get_request(int, struct sockaddr *, socklen_t,
|
void evhttp_get_request(int, struct sockaddr *, socklen_t,
|
||||||
void (*)(struct evhttp_request *, void *), void *);
|
void (*)(struct evhttp_request *, void *), void *);
|
||||||
|
|
||||||
/*
|
|
||||||
* Starts a connection to the specified address and invokes the callback
|
|
||||||
* if everything is fine.
|
|
||||||
*/
|
|
||||||
struct evhttp_connection *evhttp_connect(
|
|
||||||
const char *address, unsigned short port,
|
|
||||||
void (*cb)(struct evhttp_connection *, void *), void *cb_arg);
|
|
||||||
|
|
||||||
/* Frees an http connection */
|
|
||||||
void evhttp_connection_free(struct evhttp_connection *evcon);
|
|
||||||
|
|
||||||
int evhttp_make_request(struct evhttp_connection *evcon,
|
|
||||||
struct evhttp_request *req,
|
|
||||||
enum evhttp_cmd_type type, const char *uri);
|
|
||||||
|
|
||||||
int evhttp_hostportfile(char *, char **, u_short *, char **);
|
int evhttp_hostportfile(char *, char **, u_short *, char **);
|
||||||
|
|
||||||
int evhttp_parse_lines(struct evhttp_request *, struct evbuffer*);
|
int evhttp_parse_lines(struct evhttp_request *, struct evbuffer*);
|
||||||
|
|
||||||
void evhttp_start_read(struct evhttp_request *);
|
void evhttp_start_read(struct evhttp_connection *);
|
||||||
void evhttp_read_header(int, short, void *);
|
void evhttp_read_header(int, short, void *);
|
||||||
void evhttp_make_header(struct evbuffer *, struct evhttp_request *);
|
void evhttp_make_header(struct evhttp_connection *, struct evhttp_request *);
|
||||||
|
|
||||||
void evhttp_form_response(struct evbuffer *, struct evhttp_request *);
|
void evhttp_write_buffer(struct evhttp_connection *,
|
||||||
void evhttp_write_buffer(struct evhttp_request *, struct evbuffer *,
|
void (*)(struct evhttp_connection *, void *), void *);
|
||||||
void (*)(struct evhttp_request *, void *), void *);
|
|
||||||
|
|
||||||
/* response sending HTML the data in the buffer */
|
/* response sending HTML the data in the buffer */
|
||||||
void evhttp_response_code(struct evhttp_request *, int, const char *);
|
void evhttp_response_code(struct evhttp_request *, int, const char *);
|
||||||
void evhttp_send_page(struct evhttp_request *, struct evbuffer *);
|
void evhttp_send_page(struct evhttp_request *, struct evbuffer *);
|
||||||
void evhttp_fail(struct evhttp_request *);
|
|
||||||
|
|
||||||
#endif /* _HTTP_H */
|
#endif /* _HTTP_H */
|
||||||
|
456
http.c
456
http.c
@ -48,6 +48,7 @@
|
|||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -162,49 +163,30 @@ evhttp_method(enum evhttp_cmd_type type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
evhttp_form_response(struct evbuffer *buf, struct evhttp_request *req)
|
evhttp_write_buffer(struct evhttp_connection *evcon,
|
||||||
{
|
void (*cb)(struct evhttp_connection *, void *), void *arg)
|
||||||
/* Clean out the buffer */
|
|
||||||
evbuffer_drain(buf, buf->off);
|
|
||||||
|
|
||||||
/* Create the header fields */
|
|
||||||
evhttp_make_header(buf, req);
|
|
||||||
|
|
||||||
/* Append the response buffer */
|
|
||||||
evbuffer_add(buf,
|
|
||||||
EVBUFFER_DATA(req->buffer), EVBUFFER_LENGTH(req->buffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
evhttp_write_buffer(struct evhttp_request *req, struct evbuffer *buffer,
|
|
||||||
void (*cb)(struct evhttp_request *, void *), void *arg)
|
|
||||||
{
|
{
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
|
|
||||||
event_debug(("%s: preparing to write buffer\n", __func__));
|
event_debug(("%s: preparing to write buffer\n", __func__));
|
||||||
|
|
||||||
if (req->buffer != buffer) {
|
|
||||||
if (req->buffer != NULL)
|
|
||||||
evbuffer_free(req->buffer);
|
|
||||||
req->buffer = buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set call back */
|
/* Set call back */
|
||||||
|
evcon->cb = cb;
|
||||||
|
evcon->cb_arg = arg;
|
||||||
|
|
||||||
req->cb = cb;
|
/* xxx: maybe check if the event is still pending? */
|
||||||
req->cb_arg = arg;
|
event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_write, evcon);
|
||||||
|
|
||||||
event_set(&req->ev, req->fd, EV_WRITE, evhttp_write, req);
|
|
||||||
timerclear(&tv);
|
timerclear(&tv);
|
||||||
tv.tv_sec = HTTP_WRITE_TIMEOUT;
|
tv.tv_sec = HTTP_WRITE_TIMEOUT;
|
||||||
event_add(&req->ev, &tv);
|
event_add(&evcon->ev, &tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create the headers need for an HTTP reply
|
* Create the headers need for an HTTP reply
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
evhttp_make_header_request(struct evbuffer *buf, struct evhttp_request *req)
|
evhttp_make_header_request(struct evhttp_connection *evcon,
|
||||||
|
struct evhttp_request *req)
|
||||||
{
|
{
|
||||||
static char line[1024];
|
static char line[1024];
|
||||||
const char *method;
|
const char *method;
|
||||||
@ -219,14 +201,14 @@ evhttp_make_header_request(struct evbuffer *buf, struct evhttp_request *req)
|
|||||||
method = evhttp_method(req->type);
|
method = evhttp_method(req->type);
|
||||||
snprintf(line, sizeof(line), "%s %s HTTP/%d.%d\r\n",
|
snprintf(line, sizeof(line), "%s %s HTTP/%d.%d\r\n",
|
||||||
method, req->uri, req->major, req->minor);
|
method, req->uri, req->major, req->minor);
|
||||||
evbuffer_add(buf, line, strlen(line));
|
evbuffer_add(evcon->output_buffer, line, strlen(line));
|
||||||
|
|
||||||
/* Add the content length on a post request if missing */
|
/* Add the content length on a post request if missing */
|
||||||
if (req->type == EVHTTP_REQ_POST &&
|
if (req->type == EVHTTP_REQ_POST &&
|
||||||
evhttp_find_header(req->output_headers, "Content-Length") == NULL){
|
evhttp_find_header(req->output_headers, "Content-Length") == NULL){
|
||||||
char size[12];
|
char size[12];
|
||||||
snprintf(size, sizeof(size), "%ld",
|
snprintf(size, sizeof(size), "%ld",
|
||||||
EVBUFFER_LENGTH(req->buffer));
|
EVBUFFER_LENGTH(req->output_buffer));
|
||||||
evhttp_add_header(req->output_headers, "Content-Length", size);
|
evhttp_add_header(req->output_headers, "Content-Length", size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -235,13 +217,14 @@ evhttp_make_header_request(struct evbuffer *buf, struct evhttp_request *req)
|
|||||||
* Create the headers needed for an HTTP reply
|
* Create the headers needed for an HTTP reply
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
evhttp_make_header_response(struct evbuffer *buf, struct evhttp_request *req)
|
evhttp_make_header_response(struct evhttp_connection *evcon,
|
||||||
|
struct evhttp_request *req)
|
||||||
{
|
{
|
||||||
static char line[1024];
|
static char line[1024];
|
||||||
snprintf(line, sizeof(line), "HTTP/%d.%d %d %s\r\n",
|
snprintf(line, sizeof(line), "HTTP/%d.%d %d %s\r\n",
|
||||||
req->major, req->minor, req->response_code,
|
req->major, req->minor, req->response_code,
|
||||||
req->response_code_line);
|
req->response_code_line);
|
||||||
evbuffer_add(buf, line, strlen(line));
|
evbuffer_add(evcon->output_buffer, line, strlen(line));
|
||||||
|
|
||||||
/* Potentially add headers */
|
/* Potentially add headers */
|
||||||
if (evhttp_find_header(req->output_headers, "Content-Type") == NULL) {
|
if (evhttp_find_header(req->output_headers, "Content-Type") == NULL) {
|
||||||
@ -251,7 +234,7 @@ evhttp_make_header_response(struct evbuffer *buf, struct evhttp_request *req)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
evhttp_make_header(struct evbuffer *buf, struct evhttp_request *req)
|
evhttp_make_header(struct evhttp_connection *evcon, struct evhttp_request *req)
|
||||||
{
|
{
|
||||||
static char line[1024];
|
static char line[1024];
|
||||||
struct evkeyval *header;
|
struct evkeyval *header;
|
||||||
@ -261,24 +244,24 @@ evhttp_make_header(struct evbuffer *buf, struct evhttp_request *req)
|
|||||||
* add some new headers or remove existing headers.
|
* add some new headers or remove existing headers.
|
||||||
*/
|
*/
|
||||||
if (req->kind == EVHTTP_REQUEST) {
|
if (req->kind == EVHTTP_REQUEST) {
|
||||||
evhttp_make_header_request(buf, req);
|
evhttp_make_header_request(evcon, req);
|
||||||
} else {
|
} else {
|
||||||
evhttp_make_header_response(buf, req);
|
evhttp_make_header_response(evcon, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
TAILQ_FOREACH(header, req->output_headers, next) {
|
TAILQ_FOREACH(header, req->output_headers, next) {
|
||||||
snprintf(line, sizeof(line), "%s: %s\r\n",
|
snprintf(line, sizeof(line), "%s: %s\r\n",
|
||||||
header->key, header->value);
|
header->key, header->value);
|
||||||
evbuffer_add(buf, line, strlen(line));
|
evbuffer_add(evcon->output_buffer, line, strlen(line));
|
||||||
}
|
}
|
||||||
evbuffer_add(buf, "\r\n", 2);
|
evbuffer_add(evcon->output_buffer, "\r\n", 2);
|
||||||
|
|
||||||
if (req->kind == EVHTTP_REQUEST) {
|
if (EVBUFFER_LENGTH(req->output_buffer) >= 0) {
|
||||||
int len = EVBUFFER_LENGTH(req->buffer);
|
/*
|
||||||
|
* For a request, we add the POST data, for a reply, this
|
||||||
/* Add the POST data */
|
* is the regular data.
|
||||||
if (len > 0)
|
*/
|
||||||
evbuffer_add(buf, EVBUFFER_DATA(req->buffer), len);
|
evbuffer_add_buffer(evcon->output_buffer, req->output_buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,45 +321,98 @@ evhttp_hostportfile(char *url, char **phost, u_short *pport, char **pfile)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
evhttp_fail(struct evhttp_request *req)
|
evhttp_connection_fail(struct evhttp_connection *evcon)
|
||||||
{
|
{
|
||||||
|
struct evhttp_request* req = TAILQ_FIRST(&evcon->requests);
|
||||||
|
assert(req != NULL);
|
||||||
|
|
||||||
|
/* reset the connection */
|
||||||
|
evhttp_connection_reset(evcon);
|
||||||
|
|
||||||
|
if (req->cb != NULL) {
|
||||||
|
/* xxx: maybe we need to pass the request here? */
|
||||||
|
(*req->cb)(NULL, req->cb_arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
TAILQ_REMOVE(&evcon->requests, req, next);
|
||||||
evhttp_request_free(req);
|
evhttp_request_free(req);
|
||||||
|
|
||||||
|
/* xxx: maybe we should fail all requests??? */
|
||||||
|
|
||||||
|
/* We are trying the next request that was queued on us */
|
||||||
|
if (TAILQ_FIRST(&evcon->requests) != NULL)
|
||||||
|
evhttp_connection_connect(evcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
evhttp_write(int fd, short what, void *arg)
|
evhttp_write(int fd, short what, void *arg)
|
||||||
{
|
{
|
||||||
struct evhttp_request *req = arg;
|
struct evhttp_connection *evcon = arg;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
if (what == EV_TIMEOUT) {
|
if (what == EV_TIMEOUT) {
|
||||||
evhttp_fail(req);
|
evhttp_connection_fail(evcon);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = evbuffer_write(req->buffer, fd);
|
n = evbuffer_write(evcon->output_buffer, fd);
|
||||||
if (n == -1) {
|
if (n == -1) {
|
||||||
event_warn("%s: evbuffer_write", __func__);
|
event_warn("%s: evbuffer_write", __func__);
|
||||||
evhttp_fail(req);
|
evhttp_connection_fail(evcon);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
event_warnx("%s: write nothing\n", __func__);
|
event_warnx("%s: write nothing\n", __func__);
|
||||||
evhttp_fail(req);
|
evhttp_connection_fail(evcon);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EVBUFFER_LENGTH(req->buffer) != 0) {
|
if (EVBUFFER_LENGTH(evcon->output_buffer) != 0) {
|
||||||
timerclear(&tv);
|
timerclear(&tv);
|
||||||
tv.tv_sec = HTTP_WRITE_TIMEOUT;
|
tv.tv_sec = HTTP_WRITE_TIMEOUT;
|
||||||
event_add(&req->ev, &tv);
|
event_add(&evcon->ev, &tv);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Activate our call back */
|
/* Activate our call back */
|
||||||
|
(*evcon->cb)(evcon, evcon->cb_arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
evhttp_connection_done(struct evhttp_connection *evcon)
|
||||||
|
{
|
||||||
|
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if this is an incoming connection, we need to leave the request
|
||||||
|
* on the connection, so that we can reply to it.
|
||||||
|
*/
|
||||||
|
if (evcon->flags & EVHTTP_CON_OUTGOING) {
|
||||||
|
TAILQ_REMOVE(&evcon->requests, req, next);
|
||||||
|
req->evcon = NULL;
|
||||||
|
|
||||||
|
if (TAILQ_FIRST(&evcon->requests) != NULL) {
|
||||||
|
/*
|
||||||
|
* We have more requests; reset the connection
|
||||||
|
* and deal with the next request. xxx: no
|
||||||
|
* persistent connection right now
|
||||||
|
*/
|
||||||
|
evhttp_connection_connect(evcon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hand what ever we read over to the request */
|
||||||
|
evbuffer_add_buffer(req->input_buffer, evcon->input_buffer);
|
||||||
|
|
||||||
|
/* notify the user of the request */
|
||||||
(*req->cb)(req, req->cb_arg);
|
(*req->cb)(req, req->cb_arg);
|
||||||
|
|
||||||
|
/* if this was an outgoing request, we own and it's done. so free it */
|
||||||
|
if (evcon->flags & EVHTTP_CON_OUTGOING) {
|
||||||
|
evhttp_request_free(req);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -389,21 +425,23 @@ evhttp_write(int fd, short what, void *arg)
|
|||||||
void
|
void
|
||||||
evhttp_read(int fd, short what, void *arg)
|
evhttp_read(int fd, short what, void *arg)
|
||||||
{
|
{
|
||||||
struct evhttp_request *req = arg;
|
struct evhttp_connection *evcon = arg;
|
||||||
|
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
if (what == EV_TIMEOUT) {
|
if (what == EV_TIMEOUT) {
|
||||||
evhttp_fail(req);
|
evhttp_connection_fail(evcon);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = evbuffer_read(req->buffer, fd, req->ntoread);
|
n = evbuffer_read(req->input_buffer, fd, req->ntoread);
|
||||||
event_debug(("%s: got %d on %d\n", __func__, n, req->fd));
|
event_debug(("%s: got %d on %d\n", __func__, n, req->fd));
|
||||||
|
|
||||||
if (n == -1) {
|
if (n == -1) {
|
||||||
event_warn("%s: evbuffer_read", __func__);
|
event_warn("%s: evbuffer_read", __func__);
|
||||||
evhttp_fail(req);
|
evhttp_connection_fail(evcon);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Adjust the amount of data that we have left to read */
|
/* Adjust the amount of data that we have left to read */
|
||||||
@ -411,28 +449,26 @@ evhttp_read(int fd, short what, void *arg)
|
|||||||
req->ntoread -= n;
|
req->ntoread -= n;
|
||||||
|
|
||||||
if (n == 0 || req->ntoread == 0) {
|
if (n == 0 || req->ntoread == 0) {
|
||||||
(*req->cb)(req, req->cb_arg);
|
evhttp_connection_done(evcon);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
timerclear(&tv);
|
timerclear(&tv);
|
||||||
tv.tv_sec = HTTP_READ_TIMEOUT;
|
tv.tv_sec = HTTP_READ_TIMEOUT;
|
||||||
event_add(&req->ev, &tv);
|
event_add(&evcon->ev, &tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
evhttp_write_requestcb(struct evhttp_request *req, void *arg)
|
evhttp_write_connectioncb(struct evhttp_connection *evcon, void *arg)
|
||||||
{
|
{
|
||||||
/* Restore the original callbacks */
|
|
||||||
req->cb = req->save_cb;
|
|
||||||
req->cb_arg = req->save_cbarg;
|
|
||||||
|
|
||||||
/* This is after writing the request to the server */
|
/* This is after writing the request to the server */
|
||||||
|
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
|
||||||
|
assert(req != NULL);
|
||||||
|
|
||||||
/* We are done writing our header and are now expecting the response */
|
/* We are done writing our header and are now expecting the response */
|
||||||
req->kind = EVHTTP_RESPONSE;
|
req->kind = EVHTTP_RESPONSE;
|
||||||
|
|
||||||
evhttp_start_read(req);
|
evhttp_start_read(evcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -442,7 +478,8 @@ evhttp_write_requestcb(struct evhttp_request *req, void *arg)
|
|||||||
void
|
void
|
||||||
evhttp_connection_free(struct evhttp_connection *evcon)
|
evhttp_connection_free(struct evhttp_connection *evcon)
|
||||||
{
|
{
|
||||||
event_del(&evcon->ev);
|
if (event_initialized(&evcon->ev))
|
||||||
|
event_del(&evcon->ev);
|
||||||
|
|
||||||
if (evcon->fd != -1)
|
if (evcon->fd != -1)
|
||||||
close(evcon->fd);
|
close(evcon->fd);
|
||||||
@ -450,9 +487,47 @@ evhttp_connection_free(struct evhttp_connection *evcon)
|
|||||||
if (evcon->address != NULL)
|
if (evcon->address != NULL)
|
||||||
free(evcon->address);
|
free(evcon->address);
|
||||||
|
|
||||||
|
if (evcon->input_buffer != NULL)
|
||||||
|
evbuffer_free(evcon->input_buffer);
|
||||||
|
|
||||||
|
if (evcon->output_buffer != NULL)
|
||||||
|
evbuffer_free(evcon->output_buffer);
|
||||||
|
|
||||||
free(evcon);
|
free(evcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
evhttp_request_dispatch(struct evhttp_connection* evcon)
|
||||||
|
{
|
||||||
|
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
|
||||||
|
|
||||||
|
/* this should not usually happy but it's possible */
|
||||||
|
if (req == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* we assume that the connection is connected already */
|
||||||
|
assert(evcon->state = EVCON_CONNECTED);
|
||||||
|
|
||||||
|
/* Create the header from the store arguments */
|
||||||
|
evhttp_make_header(evcon, req);
|
||||||
|
|
||||||
|
evhttp_write_buffer(evcon, evhttp_write_connectioncb, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset our connection state */
|
||||||
|
void
|
||||||
|
evhttp_connection_reset(struct evhttp_connection *evcon)
|
||||||
|
{
|
||||||
|
if (event_initialized(&evcon->ev))
|
||||||
|
event_del(&evcon->ev);
|
||||||
|
|
||||||
|
if (evcon->fd != -1) {
|
||||||
|
close(evcon->fd);
|
||||||
|
evcon->fd = -1;
|
||||||
|
}
|
||||||
|
evcon->state = EVCON_DISCONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Call back for asynchronous connection attempt.
|
* Call back for asynchronous connection attempt.
|
||||||
*/
|
*/
|
||||||
@ -489,16 +564,24 @@ evhttp_connectioncb(int fd, short what, void *arg)
|
|||||||
event_debug(("%s: connected to \"%s:%d\" on %d\n",
|
event_debug(("%s: connected to \"%s:%d\" on %d\n",
|
||||||
__func__, evcon->address, evcon->port, evcon->fd));
|
__func__, evcon->address, evcon->port, evcon->fd));
|
||||||
|
|
||||||
/* We are turning the connection object over to the user */
|
evcon->state = EVCON_CONNECTED;
|
||||||
(*evcon->cb)(evcon, evcon->cb_arg);
|
|
||||||
|
/* try to start requests that have queued up on this connection */
|
||||||
|
evhttp_request_dispatch(evcon);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
/* Signal error to the user */
|
evhttp_connection_reset(evcon);
|
||||||
(*evcon->cb)(NULL, evcon->cb_arg);
|
|
||||||
|
|
||||||
evhttp_connection_free(evcon);
|
/* for now, we just signal all requests by executing their callbacks */
|
||||||
return;
|
while (TAILQ_FIRST(&evcon->requests) != NULL) {
|
||||||
|
struct evhttp_request *request = TAILQ_FIRST(&evcon->requests);
|
||||||
|
TAILQ_REMOVE(&evcon->requests, request, next);
|
||||||
|
request->evcon = NULL;
|
||||||
|
|
||||||
|
/* we might want to set an error here */
|
||||||
|
request->cb(request, request->cb_arg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -538,15 +621,15 @@ evhttp_parse_response_line(struct evhttp_request *req, char *line)
|
|||||||
req->major = 1;
|
req->major = 1;
|
||||||
req->minor = 1;
|
req->minor = 1;
|
||||||
} else {
|
} else {
|
||||||
event_warnx("%s: bad protocol \"%s\" on %d\n",
|
event_warnx("%s: bad protocol \"%s\"\n",
|
||||||
__func__, protocol, req->fd);
|
__func__, protocol);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
req->response_code = atoi(number);
|
req->response_code = atoi(number);
|
||||||
if (!evhttp_valid_response_code(req->response_code)) {
|
if (!evhttp_valid_response_code(req->response_code)) {
|
||||||
event_warnx("%s: bad response code \"%s\" on %d\n",
|
event_warnx("%s: bad response code \"%s\"\n",
|
||||||
__func__, number, req->fd);
|
__func__, number);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -584,8 +667,8 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line)
|
|||||||
} else if (strcmp(method, "HEAD") == 0) {
|
} else if (strcmp(method, "HEAD") == 0) {
|
||||||
req->type = EVHTTP_REQ_HEAD;
|
req->type = EVHTTP_REQ_HEAD;
|
||||||
} else {
|
} else {
|
||||||
event_warnx("%s: bad method %s on fd %d\n",
|
event_warnx("%s: bad method %s on request %p\n",
|
||||||
__func__, method, req->fd);
|
__func__, method, req);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -596,8 +679,8 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line)
|
|||||||
req->major = 1;
|
req->major = 1;
|
||||||
req->minor = 1;
|
req->minor = 1;
|
||||||
} else {
|
} else {
|
||||||
event_warnx("%s: bad version %s on %d\n",
|
event_warnx("%s: bad version %s on request %p\n",
|
||||||
__func__, version, req->fd);
|
__func__, version, req);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -759,7 +842,7 @@ evhttp_parse_lines(struct evhttp_request *req, struct evbuffer* buffer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
evhttp_get_body(struct evhttp_request *req)
|
evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req)
|
||||||
{
|
{
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
const char *content_length;
|
const char *content_length;
|
||||||
@ -768,7 +851,7 @@ evhttp_get_body(struct evhttp_request *req)
|
|||||||
|
|
||||||
/* If this is a request without a body, then we are done */
|
/* If this is a request without a body, then we are done */
|
||||||
if (req->kind == EVHTTP_REQUEST && req->type != EVHTTP_REQ_POST) {
|
if (req->kind == EVHTTP_REQUEST && req->type != EVHTTP_REQ_POST) {
|
||||||
(*req->cb)(req, req->cb_arg);
|
evhttp_connection_done(evcon);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -783,7 +866,7 @@ evhttp_get_body(struct evhttp_request *req)
|
|||||||
event_warnx("%s: we got no content length, but the server"
|
event_warnx("%s: we got no content length, but the server"
|
||||||
" wants to keep the connection open: %s.\n",
|
" wants to keep the connection open: %s.\n",
|
||||||
__func__, connection);
|
__func__, connection);
|
||||||
evhttp_fail(req);
|
evhttp_connection_fail(evcon);
|
||||||
return;
|
return;
|
||||||
} else if (content_length == NULL)
|
} else if (content_length == NULL)
|
||||||
req->ntoread = -1;
|
req->ntoread = -1;
|
||||||
@ -791,58 +874,60 @@ evhttp_get_body(struct evhttp_request *req)
|
|||||||
req->ntoread = atoi(content_length);
|
req->ntoread = atoi(content_length);
|
||||||
|
|
||||||
event_debug(("%s: bytes to read: %d (in buffer %d)\n",
|
event_debug(("%s: bytes to read: %d (in buffer %d)\n",
|
||||||
__func__, req->ntoread, EVBUFFER_LENGTH(req->buffer)));
|
__func__, req->ntoread, EVBUFFER_LENGTH(evcon->buffer)));
|
||||||
|
|
||||||
if (req->ntoread > 0)
|
if (req->ntoread > 0)
|
||||||
req->ntoread -= EVBUFFER_LENGTH(req->buffer);
|
req->ntoread -= EVBUFFER_LENGTH(evcon->input_buffer);
|
||||||
|
|
||||||
if (req->ntoread == 0) {
|
if (req->ntoread == 0) {
|
||||||
(*req->cb)(req, req->cb_arg);
|
evhttp_connection_done(evcon);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
event_set(&req->ev, req->fd, EV_READ, evhttp_read, req);
|
event_set(&evcon->ev, evcon->fd, EV_READ, evhttp_read, evcon);
|
||||||
timerclear(&tv);
|
timerclear(&tv);
|
||||||
tv.tv_sec = HTTP_READ_TIMEOUT;
|
tv.tv_sec = HTTP_READ_TIMEOUT;
|
||||||
event_add(&req->ev, &tv);
|
event_add(&evcon->ev, &tv);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
evhttp_read_header(int fd, short what, void *arg)
|
evhttp_read_header(int fd, short what, void *arg)
|
||||||
{
|
{
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
struct evhttp_request *req = arg;
|
struct evhttp_connection *evcon = arg;
|
||||||
|
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
|
||||||
int n, res;
|
int n, res;
|
||||||
|
|
||||||
if (what == EV_TIMEOUT) {
|
if (what == EV_TIMEOUT) {
|
||||||
event_warnx("%s: timeout on %d\n", __func__, fd);
|
event_warnx("%s: timeout on %d\n", __func__, fd);
|
||||||
evhttp_request_free(req);
|
evhttp_connection_fail(evcon);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = evbuffer_read(req->buffer, fd, -1);
|
n = evbuffer_read(evcon->input_buffer, fd, -1);
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
event_warnx("%s: no more data on %d\n", __func__, fd);
|
event_warnx("%s: no more data on %d\n", __func__, fd);
|
||||||
evhttp_request_free(req);
|
evhttp_connection_fail(evcon);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (n == -1) {
|
if (n == -1) {
|
||||||
event_warnx("%s: bad read on %d\n", __func__, fd);
|
event_warnx("%s: bad read on %d\n", __func__, fd);
|
||||||
evhttp_request_free(req);
|
evhttp_connection_fail(evcon);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = evhttp_parse_lines(req, req->buffer);
|
res = evhttp_parse_lines(req, evcon->input_buffer);
|
||||||
if (res == -1) {
|
if (res == -1) {
|
||||||
/* Error while reading, terminate */
|
/* Error while reading, terminate */
|
||||||
event_warnx("%s: bad header lines on %d\n", __func__, fd);
|
event_warnx("%s: bad header lines on %d\n", __func__, fd);
|
||||||
evhttp_request_free(req);
|
evhttp_connection_fail(evcon);
|
||||||
return;
|
return;
|
||||||
} else if (res == 0) {
|
} else if (res == 0) {
|
||||||
/* Need more header lines */
|
/* Need more header lines */
|
||||||
timerclear(&tv);
|
timerclear(&tv);
|
||||||
tv.tv_sec = HTTP_READ_TIMEOUT;
|
tv.tv_sec = HTTP_READ_TIMEOUT;
|
||||||
event_add(&req->ev, &tv);
|
event_add(&evcon->ev, &tv);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -850,19 +935,19 @@ evhttp_read_header(int fd, short what, void *arg)
|
|||||||
switch (req->kind) {
|
switch (req->kind) {
|
||||||
case EVHTTP_REQUEST:
|
case EVHTTP_REQUEST:
|
||||||
event_debug(("%s: checking for post data on %d\n",
|
event_debug(("%s: checking for post data on %d\n",
|
||||||
__func__, req->fd));
|
__func__, fd));
|
||||||
evhttp_get_body(req);
|
evhttp_get_body(evcon, req);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EVHTTP_RESPONSE:
|
case EVHTTP_RESPONSE:
|
||||||
event_debug(("%s: starting to read body for \"%s\" on %d\n",
|
event_debug(("%s: starting to read body for \"%s\" on %d\n",
|
||||||
__func__, req->remote_host, req->fd));
|
__func__, req->remote_host, fd));
|
||||||
evhttp_get_body(req);
|
evhttp_get_body(evcon, req);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
event_warnx("%s: bad header on %d\n", __func__, fd);
|
event_warnx("%s: bad header on %d\n", __func__, fd);
|
||||||
evhttp_fail(req);
|
evhttp_connection_fail(evcon);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -878,11 +963,9 @@ evhttp_read_header(int fd, short what, void *arg)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
struct evhttp_connection *
|
struct evhttp_connection *
|
||||||
evhttp_connect(const char *address, unsigned short port,
|
evhttp_connection_new(const char *address, unsigned short port)
|
||||||
void (*cb)(struct evhttp_connection *, void *), void *cb_arg)
|
|
||||||
{
|
{
|
||||||
struct evhttp_connection *evcon = NULL;
|
struct evhttp_connection *evcon = NULL;
|
||||||
struct timeval tv;
|
|
||||||
|
|
||||||
event_debug(("Attempting connection to %s:%d\n", address, port));
|
event_debug(("Attempting connection to %s:%d\n", address, port));
|
||||||
|
|
||||||
@ -893,27 +976,24 @@ evhttp_connect(const char *address, unsigned short port,
|
|||||||
|
|
||||||
evcon->fd = -1;
|
evcon->fd = -1;
|
||||||
evcon->port = port;
|
evcon->port = port;
|
||||||
|
|
||||||
if ((evcon->address = strdup(address)) == NULL) {
|
if ((evcon->address = strdup(address)) == NULL) {
|
||||||
event_warn("%s: strdup failed", __func__);
|
event_warn("%s: strdup failed", __func__);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Let the user name when something interesting happened */
|
if ((evcon->input_buffer = evbuffer_new()) == NULL) {
|
||||||
evcon->cb = cb;
|
event_warn("%s: evbuffer_new failed", __func__);
|
||||||
evcon->cb_arg = cb_arg;
|
|
||||||
|
|
||||||
/* Do async connection to HTTP server */
|
|
||||||
if ((evcon->fd = make_socket(connect, address, port)) == -1) {
|
|
||||||
event_warn("%s: failed to connect to \"%s:%d\"",
|
|
||||||
__func__, address, port);
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set up a callback for successful connection setup */
|
if ((evcon->output_buffer = evbuffer_new()) == NULL) {
|
||||||
event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_connectioncb, evcon);
|
event_warn("%s: evbuffer_new failed", __func__);
|
||||||
timerclear(&tv);
|
goto error;
|
||||||
tv.tv_sec = HTTP_CONNECT_TIMEOUT;
|
}
|
||||||
event_add(&evcon->ev, &tv);
|
|
||||||
|
evcon->state = EVCON_DISCONNECTED;
|
||||||
|
TAILQ_INIT(&evcon->requests);
|
||||||
|
|
||||||
return (evcon);
|
return (evcon);
|
||||||
|
|
||||||
@ -923,11 +1003,42 @@ evhttp_connect(const char *address, unsigned short port,
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
evhttp_connection_connect(struct evhttp_connection *evcon)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
if (evcon->state == EVCON_CONNECTING)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
evhttp_connection_reset(evcon);
|
||||||
|
|
||||||
|
assert(!(evcon->flags & EVHTTP_CON_INCOMING));
|
||||||
|
evcon->flags |= EVHTTP_CON_OUTGOING;
|
||||||
|
|
||||||
|
/* Do async connection to HTTP server */
|
||||||
|
if ((evcon->fd = make_socket(
|
||||||
|
connect, evcon->address, evcon->port)) == -1) {
|
||||||
|
event_warn("%s: failed to connect to \"%s:%d\"",
|
||||||
|
__func__, evcon->address, evcon->port);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up a callback for successful connection setup */
|
||||||
|
event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_connectioncb, evcon);
|
||||||
|
timerclear(&tv);
|
||||||
|
tv.tv_sec = HTTP_CONNECT_TIMEOUT;
|
||||||
|
event_add(&evcon->ev, &tv);
|
||||||
|
|
||||||
|
evcon->state = EVCON_CONNECTING;
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Starts an HTTP request on the provided evhttp_connection object.
|
* Starts an HTTP request on the provided evhttp_connection object.
|
||||||
*
|
* If the connection object is not connected to the web server already,
|
||||||
* In theory we might use this to queue requests on the connection
|
* this will start the connection.
|
||||||
* object.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -935,19 +1046,13 @@ evhttp_make_request(struct evhttp_connection *evcon,
|
|||||||
struct evhttp_request *req,
|
struct evhttp_request *req,
|
||||||
enum evhttp_cmd_type type, const char *uri)
|
enum evhttp_cmd_type type, const char *uri)
|
||||||
{
|
{
|
||||||
struct evbuffer *evbuf = evbuffer_new();
|
|
||||||
|
|
||||||
if (evbuf == NULL)
|
|
||||||
return (-1);
|
|
||||||
|
|
||||||
/* We are making a request */
|
/* We are making a request */
|
||||||
req->fd = evcon->fd;
|
|
||||||
req->kind = EVHTTP_REQUEST;
|
req->kind = EVHTTP_REQUEST;
|
||||||
req->type = type;
|
req->type = type;
|
||||||
if (req->uri != NULL)
|
if (req->uri != NULL)
|
||||||
free(req->uri);
|
free(req->uri);
|
||||||
if ((req->uri = strdup(uri)) == NULL)
|
if ((req->uri = strdup(uri)) == NULL)
|
||||||
goto error;
|
event_err(1, "%s: strdup", __func__);
|
||||||
|
|
||||||
/* Set the protocol version if it is not supplied */
|
/* Set the protocol version if it is not supplied */
|
||||||
if (!req->major && !req->minor) {
|
if (!req->major && !req->minor) {
|
||||||
@ -955,20 +1060,25 @@ evhttp_make_request(struct evhttp_connection *evcon,
|
|||||||
req->minor = 1;
|
req->minor = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create the header from the store arguments */
|
assert(req->evcon == NULL);
|
||||||
evhttp_make_header(evbuf, req);
|
req->evcon = evcon;
|
||||||
|
assert(!(req->flags && EVHTTP_REQ_OWN_CONNECTION));
|
||||||
|
|
||||||
/* Schedule the write */
|
TAILQ_INSERT_TAIL(&evcon->requests, req, next);
|
||||||
req->save_cb = req->cb;
|
|
||||||
req->save_cbarg = req->cb_arg;
|
/* If the connection object is not connected; make it so */
|
||||||
|
if (evcon->state != EVCON_CONNECTED)
|
||||||
|
return (evhttp_connection_connect(evcon));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If it's connected already and we are the first in the queue,
|
||||||
|
* then we can dispatch this request immediately. Otherwise, it
|
||||||
|
* will be dispatched once the pending requests are completed.
|
||||||
|
*/
|
||||||
|
if (TAILQ_FIRST(&evcon->requests) == req)
|
||||||
|
evhttp_request_dispatch(evcon);
|
||||||
|
|
||||||
/* evbuf is being freed when the request finishes */
|
|
||||||
evhttp_write_buffer(req, evbuf, evhttp_write_requestcb, NULL);
|
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
error:
|
|
||||||
evbuffer_free(evbuf);
|
|
||||||
return (-1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -977,21 +1087,29 @@ evhttp_make_request(struct evhttp_connection *evcon,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
void
|
void
|
||||||
evhttp_start_read(struct evhttp_request *req)
|
evhttp_start_read(struct evhttp_connection *evcon)
|
||||||
{
|
{
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
|
|
||||||
/* Set up an event to read the headers */
|
/* Set up an event to read the headers */
|
||||||
event_set(&req->ev, req->fd, EV_READ, evhttp_read_header, req);
|
if (event_initialized(&evcon->ev))
|
||||||
|
event_del(&evcon->ev);
|
||||||
|
event_set(&evcon->ev, evcon->fd, EV_READ, evhttp_read_header, evcon);
|
||||||
|
|
||||||
timerclear(&tv);
|
timerclear(&tv);
|
||||||
tv.tv_sec = HTTP_READ_TIMEOUT;
|
tv.tv_sec = HTTP_READ_TIMEOUT;
|
||||||
event_add(&req->ev, &tv);
|
event_add(&evcon->ev, &tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
evhttp_send_done(struct evhttp_request *req, void *arg)
|
evhttp_send_done(struct evhttp_connection *evcon, void *arg)
|
||||||
{
|
{
|
||||||
|
struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
|
||||||
|
TAILQ_REMOVE(&evcon->requests, req, next);
|
||||||
|
|
||||||
|
if (req->flags & EVHTTP_REQ_OWN_CONNECTION)
|
||||||
|
evhttp_connection_free(evcon);
|
||||||
|
|
||||||
evhttp_request_free(req);
|
evhttp_request_free(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1025,16 +1143,17 @@ evhttp_send_error(struct evhttp_request *req, int error, const char *reason)
|
|||||||
static __inline void
|
static __inline void
|
||||||
evhttp_send(struct evhttp_request *req, struct evbuffer *databuf)
|
evhttp_send(struct evhttp_request *req, struct evbuffer *databuf)
|
||||||
{
|
{
|
||||||
struct evbuffer *buf = req->buffer;
|
struct evhttp_connection *evcon = req->evcon;
|
||||||
|
|
||||||
evbuffer_drain(buf, -1);
|
assert(TAILQ_FIRST(&evcon->requests) == req);
|
||||||
|
|
||||||
|
/* xxx: not sure if we really should expost the data buffer this way */
|
||||||
|
evbuffer_add_buffer(req->output_buffer, databuf);
|
||||||
|
|
||||||
/* Adds headers to the response */
|
/* Adds headers to the response */
|
||||||
evhttp_make_header(buf, req);
|
evhttp_make_header(evcon, req);
|
||||||
|
|
||||||
evbuffer_add_buffer(buf, databuf);
|
evhttp_write_buffer(evcon, evhttp_send_done, NULL);
|
||||||
|
|
||||||
evhttp_write_buffer(req, buf, evhttp_send_done, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1285,7 +1404,6 @@ evhttp_request_new(void (*cb)(struct evhttp_request *, void *), void *arg)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
req->fd = -1;
|
|
||||||
req->kind = EVHTTP_RESPONSE;
|
req->kind = EVHTTP_RESPONSE;
|
||||||
req->input_headers = calloc(1, sizeof(struct evkeyvalq));
|
req->input_headers = calloc(1, sizeof(struct evkeyvalq));
|
||||||
if (req->input_headers == NULL) {
|
if (req->input_headers == NULL) {
|
||||||
@ -1301,7 +1419,15 @@ evhttp_request_new(void (*cb)(struct evhttp_request *, void *), void *arg)
|
|||||||
}
|
}
|
||||||
TAILQ_INIT(req->output_headers);
|
TAILQ_INIT(req->output_headers);
|
||||||
|
|
||||||
req->buffer = evbuffer_new();
|
if ((req->input_buffer = evbuffer_new()) == NULL) {
|
||||||
|
event_warn("%s: evbuffer_new", __func__);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((req->output_buffer = evbuffer_new()) == NULL) {
|
||||||
|
event_warn("%s: evbuffer_new", __func__);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
req->cb = cb;
|
req->cb = cb;
|
||||||
req->cb_arg = arg;
|
req->cb_arg = arg;
|
||||||
@ -1317,8 +1443,6 @@ evhttp_request_new(void (*cb)(struct evhttp_request *, void *), void *arg)
|
|||||||
void
|
void
|
||||||
evhttp_request_free(struct evhttp_request *req)
|
evhttp_request_free(struct evhttp_request *req)
|
||||||
{
|
{
|
||||||
if (req->fd != -1)
|
|
||||||
close(req->fd);
|
|
||||||
if (req->remote_host != NULL)
|
if (req->remote_host != NULL)
|
||||||
free(req->remote_host);
|
free(req->remote_host);
|
||||||
if (req->uri != NULL)
|
if (req->uri != NULL)
|
||||||
@ -1326,17 +1450,18 @@ evhttp_request_free(struct evhttp_request *req)
|
|||||||
if (req->response_code_line != NULL)
|
if (req->response_code_line != NULL)
|
||||||
free(req->response_code_line);
|
free(req->response_code_line);
|
||||||
|
|
||||||
if (event_initialized(&req->ev))
|
|
||||||
event_del(&req->ev);
|
|
||||||
|
|
||||||
evhttp_clear_headers(req->input_headers);
|
evhttp_clear_headers(req->input_headers);
|
||||||
free(req->input_headers);
|
free(req->input_headers);
|
||||||
|
|
||||||
evhttp_clear_headers(req->output_headers);
|
evhttp_clear_headers(req->output_headers);
|
||||||
free(req->output_headers);
|
free(req->output_headers);
|
||||||
|
|
||||||
if (req->buffer != NULL)
|
if (req->input_buffer != NULL)
|
||||||
evbuffer_free(req->buffer);
|
evbuffer_free(req->input_buffer);
|
||||||
|
|
||||||
|
if (req->output_buffer != NULL)
|
||||||
|
evbuffer_free(req->output_buffer);
|
||||||
|
|
||||||
free(req);
|
free(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1360,6 +1485,7 @@ void
|
|||||||
evhttp_get_request(int fd, struct sockaddr *sa, socklen_t salen,
|
evhttp_get_request(int fd, struct sockaddr *sa, socklen_t salen,
|
||||||
void (*cb)(struct evhttp_request *, void *), void *arg)
|
void (*cb)(struct evhttp_request *, void *), void *arg)
|
||||||
{
|
{
|
||||||
|
struct evhttp_connection *evcon;
|
||||||
struct evhttp_request *req;
|
struct evhttp_request *req;
|
||||||
char *hostname, *portname;
|
char *hostname, *portname;
|
||||||
|
|
||||||
@ -1367,17 +1493,31 @@ evhttp_get_request(int fd, struct sockaddr *sa, socklen_t salen,
|
|||||||
event_debug(("%s: new request from %s:%s on %d\n",
|
event_debug(("%s: new request from %s:%s on %d\n",
|
||||||
__func__, hostname, portname, fd));
|
__func__, hostname, portname, fd));
|
||||||
|
|
||||||
if ((req = evhttp_request_new(cb, arg)) == NULL)
|
/* we need a connection object to put the http request on */
|
||||||
|
if ((evcon = evhttp_connection_new(hostname, atoi(portname))) == NULL)
|
||||||
return;
|
return;
|
||||||
|
evcon->flags |= EVHTTP_CON_INCOMING;
|
||||||
|
evcon->state = EVCON_CONNECTED;
|
||||||
|
|
||||||
|
if ((req = evhttp_request_new(cb, arg)) == NULL) {
|
||||||
|
evhttp_connection_free(evcon);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
evcon->fd = fd;
|
||||||
|
|
||||||
|
req->evcon = evcon; /* the request ends up owning the connection */
|
||||||
|
req->flags |= EVHTTP_REQ_OWN_CONNECTION;
|
||||||
|
|
||||||
|
TAILQ_INSERT_TAIL(&evcon->requests, req, next);
|
||||||
|
|
||||||
req->fd = fd;
|
|
||||||
req->kind = EVHTTP_REQUEST;
|
req->kind = EVHTTP_REQUEST;
|
||||||
|
|
||||||
if ((req->remote_host = strdup(hostname)) == NULL)
|
if ((req->remote_host = strdup(hostname)) == NULL)
|
||||||
event_err(1, "%s: strdup", __func__);
|
event_err(1, "%s: strdup", __func__);
|
||||||
req->remote_port = atoi(portname);
|
req->remote_port = atoi(portname);
|
||||||
|
|
||||||
evhttp_start_read(req);
|
evhttp_start_read(evcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ regress_SOURCES = regress.c regress.h regress_http.c \
|
|||||||
bench_SOURCES = bench.c
|
bench_SOURCES = bench.c
|
||||||
|
|
||||||
regress.gen.c regress.gen.h: regress.rpc
|
regress.gen.c regress.gen.h: regress.rpc
|
||||||
../event_rpcgen.py regress.rpc
|
../event_rpcgen.py regress.rpc || echo "No Python installed"
|
||||||
|
|
||||||
DISTCLEANFILES = *~
|
DISTCLEANFILES = *~
|
||||||
CLEANFILES = regress.gen.h regress.gen.c
|
CLEANFILES = regress.gen.h regress.gen.c
|
||||||
|
@ -214,25 +214,42 @@ http_basic_test(void)
|
|||||||
fprintf(stdout, "OK\n");
|
fprintf(stdout, "OK\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_connectcb(struct evhttp_connection *evcon, void *arg);
|
void http_request_done(struct evhttp_request *, void *);
|
||||||
|
|
||||||
void
|
void
|
||||||
http_connection_test(void)
|
http_connection_test(void)
|
||||||
{
|
{
|
||||||
short port = -1;
|
short port = -1;
|
||||||
struct evhttp_connection *evcon = NULL;
|
struct evhttp_connection *evcon = NULL;
|
||||||
|
struct evhttp_request *req = NULL;
|
||||||
|
|
||||||
test_ok = 0;
|
test_ok = 0;
|
||||||
fprintf(stdout, "Testing Basic HTTP Connection: ");
|
fprintf(stdout, "Testing Basic HTTP Connection: ");
|
||||||
|
|
||||||
http = http_setup(&port);
|
http = http_setup(&port);
|
||||||
|
|
||||||
evcon = evhttp_connect("127.0.0.1", port, http_connectcb, NULL);
|
evcon = evhttp_connection_new("127.0.0.1", port);
|
||||||
if (evcon == NULL) {
|
if (evcon == NULL) {
|
||||||
fprintf(stdout, "FAILED\n");
|
fprintf(stdout, "FAILED\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At this point, we want to schedule a request to the HTTP
|
||||||
|
* server using our make request method.
|
||||||
|
*/
|
||||||
|
|
||||||
|
req = evhttp_request_new(http_request_done, NULL);
|
||||||
|
|
||||||
|
/* Add the information that we care about */
|
||||||
|
evhttp_add_header(req->output_headers, "Host", "somehost");
|
||||||
|
|
||||||
|
/* We give ownership of the request to the connection */
|
||||||
|
if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
|
||||||
|
fprintf(stdout, "FAILED\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
event_dispatch();
|
event_dispatch();
|
||||||
|
|
||||||
evhttp_connection_free(evcon);
|
evhttp_connection_free(evcon);
|
||||||
@ -246,34 +263,6 @@ http_connection_test(void)
|
|||||||
fprintf(stdout, "OK\n");
|
fprintf(stdout, "OK\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_request_done(struct evhttp_request *, void *);
|
|
||||||
|
|
||||||
void
|
|
||||||
http_connectcb(struct evhttp_connection *evcon, void *arg)
|
|
||||||
{
|
|
||||||
struct evhttp_request *req = NULL;
|
|
||||||
|
|
||||||
if (evcon == NULL) {
|
|
||||||
fprintf(stdout, "FAILED\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* At this point, we want to schedule a request to the HTTP
|
|
||||||
* server using our make request method.
|
|
||||||
*/
|
|
||||||
|
|
||||||
req = evhttp_request_new(http_request_done, NULL);
|
|
||||||
|
|
||||||
/* Add the information that we care about */
|
|
||||||
evhttp_add_header(req->output_headers, "Host", "somehost");
|
|
||||||
|
|
||||||
if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
|
|
||||||
fprintf(stdout, "FAILED\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
http_request_done(struct evhttp_request *req, void *arg)
|
http_request_done(struct evhttp_request *req, void *arg)
|
||||||
{
|
{
|
||||||
@ -290,12 +279,12 @@ http_request_done(struct evhttp_request *req, void *arg)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EVBUFFER_LENGTH(req->buffer) != strlen(what)) {
|
if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) {
|
||||||
fprintf(stderr, "FAILED\n");
|
fprintf(stderr, "FAILED\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memcmp(EVBUFFER_DATA(req->buffer), what, strlen(what)) != 0) {
|
if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) {
|
||||||
fprintf(stderr, "FAILED\n");
|
fprintf(stderr, "FAILED\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@ -308,25 +297,48 @@ http_request_done(struct evhttp_request *req, void *arg)
|
|||||||
* HTTP POST test.
|
* HTTP POST test.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void http_connect_forpostcb(struct evhttp_connection *evcon, void *arg);
|
void http_postrequest_done(struct evhttp_request *, void *);
|
||||||
|
|
||||||
|
#define POST_DATA "Okay. Not really printf"
|
||||||
|
|
||||||
void
|
void
|
||||||
http_post_test(void)
|
http_post_test(void)
|
||||||
{
|
{
|
||||||
short port = -1;
|
short port = -1;
|
||||||
struct evhttp_connection *evcon = NULL;
|
struct evhttp_connection *evcon = NULL;
|
||||||
|
struct evhttp_request *req = NULL;
|
||||||
|
|
||||||
test_ok = 0;
|
test_ok = 0;
|
||||||
fprintf(stdout, "Testing HTTP POST Request: ");
|
fprintf(stdout, "Testing HTTP POST Request: ");
|
||||||
|
|
||||||
http = http_setup(&port);
|
http = http_setup(&port);
|
||||||
|
|
||||||
evcon = evhttp_connect("127.0.0.1", port, http_connect_forpostcb, NULL);
|
evcon = evhttp_connection_new("127.0.0.1", port);
|
||||||
if (evcon == NULL) {
|
if (evcon == NULL) {
|
||||||
fprintf(stdout, "FAILED\n");
|
fprintf(stdout, "FAILED\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At this point, we want to schedule an HTTP POST request
|
||||||
|
* server using our make request method.
|
||||||
|
*/
|
||||||
|
|
||||||
|
req = evhttp_request_new(http_postrequest_done, NULL);
|
||||||
|
if (req == NULL) {
|
||||||
|
fprintf(stdout, "FAILED\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add the information that we care about */
|
||||||
|
evhttp_add_header(req->output_headers, "Host", "somehost");
|
||||||
|
evbuffer_add_printf(req->output_buffer, POST_DATA);
|
||||||
|
|
||||||
|
if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) {
|
||||||
|
fprintf(stdout, "FAILED\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
event_dispatch();
|
event_dispatch();
|
||||||
|
|
||||||
evhttp_connection_free(evcon);
|
evhttp_connection_free(evcon);
|
||||||
@ -340,37 +352,6 @@ http_post_test(void)
|
|||||||
fprintf(stdout, "OK\n");
|
fprintf(stdout, "OK\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_postrequest_done(struct evhttp_request *, void *);
|
|
||||||
|
|
||||||
#define POST_DATA "Okay. Not really printf"
|
|
||||||
|
|
||||||
void
|
|
||||||
http_connect_forpostcb(struct evhttp_connection *evcon, void *arg)
|
|
||||||
{
|
|
||||||
struct evhttp_request *req = NULL;
|
|
||||||
|
|
||||||
if (evcon == NULL) {
|
|
||||||
fprintf(stdout, "FAILED\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* At this point, we want to schedule an HTTP POST request
|
|
||||||
* server using our make request method.
|
|
||||||
*/
|
|
||||||
|
|
||||||
req = evhttp_request_new(http_postrequest_done, NULL);
|
|
||||||
|
|
||||||
/* Add the information that we care about */
|
|
||||||
evhttp_add_header(req->output_headers, "Host", "somehost");
|
|
||||||
evbuffer_add_printf(req->buffer, POST_DATA);
|
|
||||||
|
|
||||||
if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) {
|
|
||||||
fprintf(stdout, "FAILED\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
http_post_cb(struct evhttp_request *req, void *arg)
|
http_post_cb(struct evhttp_request *req, void *arg)
|
||||||
{
|
{
|
||||||
@ -382,12 +363,12 @@ http_post_cb(struct evhttp_request *req, void *arg)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EVBUFFER_LENGTH(req->buffer) != strlen(POST_DATA)) {
|
if (EVBUFFER_LENGTH(req->input_buffer) != strlen(POST_DATA)) {
|
||||||
fprintf(stdout, "FAILED\n");
|
fprintf(stdout, "FAILED\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(EVBUFFER_DATA(req->buffer), POST_DATA)) {
|
if (strcmp(EVBUFFER_DATA(req->input_buffer), POST_DATA)) {
|
||||||
fprintf(stdout, "FAILED\n");
|
fprintf(stdout, "FAILED\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@ -416,12 +397,12 @@ http_postrequest_done(struct evhttp_request *req, void *arg)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EVBUFFER_LENGTH(req->buffer) != strlen(what)) {
|
if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) {
|
||||||
fprintf(stderr, "FAILED\n");
|
fprintf(stderr, "FAILED\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memcmp(EVBUFFER_DATA(req->buffer), what, strlen(what)) != 0) {
|
if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) {
|
||||||
fprintf(stderr, "FAILED\n");
|
fprintf(stderr, "FAILED\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user