return (method);
}
+/**
+ * Determines if a response should have a body.
+ * Follows the rules in RFC 2616 section 4.3.
+ * @return 1 if the response MUST have a body;
+ * 0 if the response MUST NOT have a body.
+ */
+static int
+evhttp_response_needs_body(struct evhttp_request *req)
+{
+ return (req->response_code != HTTP_NOCONTENT &&
+ req->response_code != HTTP_NOTMODIFIED &&
+ (req->response_code < 100 || req->response_code >= 200) &&
+ req->type != EVHTTP_REQ_HEAD);
+}
+
static void
evhttp_add_event(struct event *ev, int timeout, int default_timeout)
{
evhttp_add_header(req->output_headers,
"Connection", "keep-alive");
- if (req->minor == 1 || is_keepalive) {
+ if ((req->minor == 1 || is_keepalive) &&
+ evhttp_response_needs_body(req)) {
/*
* we need to add the content length if the
* user did not give it, this is required for
}
/* Potentially add headers for unidentified content. */
- if (EVBUFFER_LENGTH(req->output_buffer)) {
+ if (evhttp_response_needs_body(req)) {
if (evhttp_find_header(req->output_headers,
"Content-Type") == NULL) {
evhttp_add_header(req->output_headers,
break;
case EVHTTP_RESPONSE:
- if (req->response_code == HTTP_NOCONTENT ||
- req->response_code == HTTP_NOTMODIFIED ||
- (req->response_code >= 100 && req->response_code < 200)) {
+ if (!evhttp_response_needs_body(req)) {
event_debug(("%s: skipping body for code %d\n",
__func__, req->response_code));
evhttp_connection_done(evcon);
/* set up to watch for client close */
evhttp_connection_start_detectclose(req->evcon);
evhttp_response_code(req, code, reason);
- if (req->major == 1 && req->minor == 1) {
- /* use chunked encoding for HTTP/1.1 */
+ if (evhttp_find_header(req->output_headers, "Content-Length") == NULL &&
+ req->major == 1 && req->minor == 1 &&
+ evhttp_response_needs_body(req)) {
+ /*
+ * prefer HTTP/1.1 chunked encoding to closing the connection;
+ * note RFC 2616 section 4.4 forbids it with Content-Length:
+ * and it's not necessary then anyway.
+ */
evhttp_add_header(req->output_headers, "Transfer-Encoding",
"chunked");
req->chunked = 1;
evhttp_send_reply_chunk(struct evhttp_request *req, struct evbuffer *databuf)
{
struct evbuffer *output = bufferevent_get_output(req->evcon->bufev);
+ if (EVBUFFER_LENGTH(databuf) == 0)
+ return;
+ if (!evhttp_response_needs_body(req))
+ return;
if (req->chunked) {
evbuffer_add_printf(output, "%x\r\n",
(unsigned)EVBUFFER_LENGTH(databuf));
/* set if a test needs to call loopexit on a base */
static struct event_base *base;
+static char const BASIC_REQUEST_BODY[] = "This is funny";
+
void http_suite(void);
static void http_basic_cb(struct evhttp_request *req, void *arg);
/* Register a callback for certain types of requests */
evhttp_set_cb(myhttp, "/test", http_basic_cb, NULL);
evhttp_set_cb(myhttp, "/chunked", http_chunked_cb, NULL);
+ evhttp_set_cb(myhttp, "/streamed", http_chunked_cb, NULL);
evhttp_set_cb(myhttp, "/postit", http_post_cb, NULL);
evhttp_set_cb(myhttp, "/putit", http_put_cb, NULL);
evhttp_set_cb(myhttp, "/deleteit", http_delete_cb, NULL);
static void
http_readcb(struct bufferevent *bev, void *arg)
{
- const char *what = "This is funny";
+ const char *what = BASIC_REQUEST_BODY;
event_debug(("%s: %s\n", __func__, EVBUFFER_DATA(EVBUFFER_INPUT(bev))));
struct evbuffer *evb = evbuffer_new();
int empty = evhttp_find_header(req->input_headers, "Empty") != NULL;
event_debug(("%s: called\n", __func__));
- evbuffer_add_printf(evb, "This is funny");
+ evbuffer_add_printf(evb, BASIC_REQUEST_BODY);
/* For multi-line headers test */
{
memset(state, 0, sizeof(struct chunk_req_state));
state->req = req;
- /* generate a chunked reply */
+ if (strcmp(evhttp_request_uri(req), "/streamed") == 0) {
+ evhttp_add_header(req->output_headers, "Content-Length", "39");
+ }
+
+ /* generate a chunked/streamed reply */
evhttp_send_reply_start(req, HTTP_OK, "Everything is fine");
/* but trickle it across several iterations to ensure we're not
}
event_debug(("%s: called\n", __func__));
- evbuffer_add_printf(evb, "This is funny");
+ evbuffer_add_printf(evb, BASIC_REQUEST_BODY);
/* allow sending of an empty reply */
evhttp_send_reply(req, HTTP_OK, "Everything is fine",
* server using our make request method.
*/
- req = evhttp_request_new(http_request_done, NULL);
+ req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
/* Add the information that we care about */
evhttp_add_header(req->output_headers, "Host", "somehost");
/* try to make another request over the same connection */
test_ok = 0;
- req = evhttp_request_new(http_request_done, NULL);
+ req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
/* Add the information that we care about */
evhttp_add_header(req->output_headers, "Host", "somehost");
/* try to make another request over the same connection */
test_ok = 0;
- req = evhttp_request_new(http_request_done, NULL);
+ req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
/* Add the information that we care about */
evhttp_add_header(req->output_headers, "Host", "somehost");
static void
http_request_done(struct evhttp_request *req, void *arg)
{
- const char *what = "This is funny";
+ const char *what = arg;
if (req->response_code != HTTP_OK) {
fprintf(stderr, "FAILED\n");
test_ok = 0;
/* make a request with the right host and expect a response */
- req = evhttp_request_new(http_request_done, NULL);
+ req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
/* Add the information that we care about */
evhttp_add_header(req->output_headers, "Host", "foo.com");
test_ok = 0;
/* make a request with the right host and expect a response */
- req = evhttp_request_new(http_request_done, NULL);
+ req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
/* Add the information that we care about */
evhttp_add_header(req->output_headers, "Host", "bar.magic.foo.com");
}
evb = evbuffer_new();
- evbuffer_add_printf(evb, "This is funny");
+ evbuffer_add_printf(evb, BASIC_REQUEST_BODY);
evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb);
void
http_postrequest_done(struct evhttp_request *req, void *arg)
{
- const char *what = "This is funny";
+ const char *what = BASIC_REQUEST_BODY;
if (req == NULL) {
fprintf(stderr, "FAILED (timeout)\n");
}
static void
-http_chunked_test(void)
+http_chunk_out_test(void)
{
struct bufferevent *bev;
int fd;
fprintf(stdout, "OK\n");
}
+static void
+http_stream_out_test(void)
+{
+ short port = -1;
+ struct evhttp_connection *evcon = NULL;
+ struct evhttp_request *req = NULL;
+
+ test_ok = 0;
+ printf("Tests streaming responses out: ");
+
+ http = http_setup(&port, NULL);
+
+ evcon = evhttp_connection_new("127.0.0.1", port);
+ 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,
+ (void *)"This is funnybut not hilarious.bwv 1052");
+
+ /* 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, "/streamed")
+ == -1) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ event_dispatch();
+
+ if (test_ok != 1) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ evhttp_connection_free(evcon);
+ evhttp_free(http);
+
+ fprintf(stdout, "OK\n");
+}
+
static void
http_stream_in_chunk(struct evhttp_request *req, void *arg)
{
"This is funnybut not hilarious.bwv 1052");
printf("Testing streamed reading of non-chunked response: ");
- _http_stream_in_test("/test", 13, "This is funny");
+ _http_stream_in_test("/test", strlen(BASIC_REQUEST_BODY),
+ BASIC_REQUEST_BODY);
}
static void
http_incomplete_test(0 /* use_timeout */);
http_incomplete_test(1 /* use_timeout */);
- http_chunked_test();
+ http_chunk_out_test();
+ http_stream_out_test();
http_stream_in_test();
http_stream_in_cancel_test();