mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-08 03:44:22 -04:00
make a simple test for HTTP POST requests
svn:r205
This commit is contained in:
parent
60192b4625
commit
38b33048eb
30
event.3
30
event.3
@ -75,7 +75,9 @@
|
||||
.Nm evbuffer_write ,
|
||||
.Nm evbuffer_read ,
|
||||
.Nm evbuffer_find ,
|
||||
.Nm evbuffer_readline
|
||||
.Nm evbuffer_readline ,
|
||||
.Nm evhttp_start ,
|
||||
.Nm evhttp_free
|
||||
.Nd execute a function when a specific event occurs
|
||||
.Sh SYNOPSIS
|
||||
.Fd #include <sys/time.h>
|
||||
@ -172,6 +174,10 @@
|
||||
.Fn "evbuffer_find" "struct evbuffer *buf" "u_char *data" "size_t size"
|
||||
.Ft "char *"
|
||||
.Fn "evbuffer_readline" "struct evbuffer *buf"
|
||||
.Ft "struct evhttp *"
|
||||
.Fn "evhttp_start" "const char *address" "u_short port"
|
||||
.Ft "void"
|
||||
.Fn "evhttp_free" "struct evhttp* http"
|
||||
.Ft int
|
||||
.Fa (*event_sigcb)(void) ;
|
||||
.Ft int
|
||||
@ -513,6 +519,28 @@ Both functions return the amount of data written or read.
|
||||
.Pp
|
||||
If multiple bases are in use, bufferevent_base_set() must be called before
|
||||
enabling the bufferevent for the first time.
|
||||
.Sh NON-BLOCKING HTTP SUPPORT
|
||||
.Nm libevent
|
||||
provides a very thin HTTP layer that can be used both to host an HTTP
|
||||
server and also to make HTTP requests.
|
||||
An HTTP server can be created by calling
|
||||
.Fn evhttp_start .
|
||||
When the HTTP server is no longer used, it can be freed via
|
||||
.Fn evhttp_free .
|
||||
.Pp
|
||||
To be notified of HTTP requests, a user needs to register callbacks with the
|
||||
HTTP server.
|
||||
This can be done by calling
|
||||
.Fn evhttp_set_cb .
|
||||
The second argument is the URI for which a callback is being registered.
|
||||
The corresponding callback will receive an
|
||||
.Va struct evhttp_request
|
||||
object that contains all information about the request.
|
||||
.Pp
|
||||
This section does not document all the possible function calls, please
|
||||
check
|
||||
.Va event.h
|
||||
for the public interfaces.
|
||||
.Sh RETURN VALUES
|
||||
Upon successful completion
|
||||
.Fn event_add
|
||||
|
9
event.h
9
event.h
@ -353,7 +353,7 @@ struct evhttp_request;
|
||||
struct evhttp *evhttp_start(const char *address, u_short port);
|
||||
|
||||
/*
|
||||
* Free the previously create HTTP server. Works only if not requests are
|
||||
* Free the previously create HTTP server. Works only if no requests are
|
||||
* currently being served.
|
||||
*/
|
||||
void evhttp_free(struct evhttp* http);
|
||||
@ -376,15 +376,18 @@ enum evhttp_cmd_type { EVHTTP_REQ_GET, EVHTTP_REQ_POST, EVHTTP_REQ_HEAD };
|
||||
struct evhttp_request *evhttp_request_new(
|
||||
void (*cb)(struct evhttp_request *, void *), void *arg);
|
||||
void evhttp_request_free(struct evhttp_request *req);
|
||||
const char *evhttp_request_uri(struct evhttp_request *req);
|
||||
|
||||
/* Interfaces for dealing with HTTP headers */
|
||||
|
||||
char *evhttp_find_header(struct evkeyvalq *, const char *);
|
||||
void evhttp_remove_header(struct evkeyvalq *, const char *);
|
||||
const char *evhttp_find_header(struct evkeyvalq *, const char *);
|
||||
int evhttp_remove_header(struct evkeyvalq *, const char *);
|
||||
int evhttp_add_header(struct evkeyvalq *, const char *, const char *);
|
||||
void evhttp_clear_headers(struct evkeyvalq *);
|
||||
|
||||
/* Miscellaneous utility functions */
|
||||
void evhttp_parse_query(const char *uri, struct evkeyvalq *);
|
||||
char *evhttp_htmlescape(const char *html);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
90
http.c
90
http.c
@ -1,7 +1,28 @@
|
||||
/*
|
||||
* Copyright 2002, 2003, 2005 Niels Provos <provos@citi.umich.edu>
|
||||
* Copyright (c) 2002-2006 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
@ -149,7 +170,8 @@ evhttp_form_response(struct evbuffer *buf, struct evhttp_request *req)
|
||||
evhttp_make_header(buf, req);
|
||||
|
||||
/* Append the response buffer */
|
||||
evbuffer_add(buf, req->buffer->buffer, req->buffer->off);
|
||||
evbuffer_add(buf,
|
||||
EVBUFFER_DATA(req->buffer), EVBUFFER_LENGTH(req->buffer));
|
||||
}
|
||||
|
||||
void
|
||||
@ -177,6 +199,9 @@ evhttp_write_buffer(struct evhttp_request *req, struct evbuffer *buffer,
|
||||
event_add(&req->ev, &tv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the headers need for an HTTP reply
|
||||
*/
|
||||
static void
|
||||
evhttp_make_header_request(struct evbuffer *buf, struct evhttp_request *req)
|
||||
{
|
||||
@ -205,6 +230,9 @@ evhttp_make_header_request(struct evbuffer *buf, struct evhttp_request *req)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the headers needed for an HTTP reply
|
||||
*/
|
||||
static void
|
||||
evhttp_make_header_response(struct evbuffer *buf, struct evhttp_request *req)
|
||||
{
|
||||
@ -249,7 +277,7 @@ evhttp_make_header(struct evbuffer *buf, struct evhttp_request *req)
|
||||
|
||||
/* Add the POST data */
|
||||
if (len > 0)
|
||||
evbuffer_add(buf, req->buffer->buffer, len);
|
||||
evbuffer_add(buf, EVBUFFER_DATA(req->buffer), len);
|
||||
}
|
||||
}
|
||||
|
||||
@ -339,7 +367,7 @@ evhttp_write(int fd, short what, void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->buffer->off != 0) {
|
||||
if (EVBUFFER_LENGTH(req->buffer) != 0) {
|
||||
timerclear(&tv);
|
||||
tv.tv_sec = HTTP_WRITE_TIMEOUT;
|
||||
event_add(&req->ev, &tv);
|
||||
@ -580,7 +608,7 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line)
|
||||
return (0);
|
||||
}
|
||||
|
||||
char *
|
||||
const char *
|
||||
evhttp_find_header(struct evkeyvalq *headers, const char *key)
|
||||
{
|
||||
struct evkeyval *header;
|
||||
@ -608,7 +636,12 @@ evhttp_clear_headers(struct evkeyvalq *headers)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
/*
|
||||
* Returns 0, if the header was successfully removed.
|
||||
* Returns -1, if the header could not be found.
|
||||
*/
|
||||
|
||||
int
|
||||
evhttp_remove_header(struct evkeyvalq *headers, const char *key)
|
||||
{
|
||||
struct evkeyval *header;
|
||||
@ -619,13 +652,15 @@ evhttp_remove_header(struct evkeyvalq *headers, const char *key)
|
||||
}
|
||||
|
||||
if (header == NULL)
|
||||
return;
|
||||
return (-1);
|
||||
|
||||
/* Free and remove the header that we found */
|
||||
TAILQ_REMOVE(headers, header, next);
|
||||
free(header->key);
|
||||
free(header->value);
|
||||
free(header);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
@ -639,10 +674,13 @@ evhttp_add_header(struct evkeyvalq *headers, const char *key, const char *value)
|
||||
return (-1);
|
||||
}
|
||||
if ((header->key = strdup(key)) == NULL) {
|
||||
free(header);
|
||||
event_warn("%s: strdup", __func__);
|
||||
return (-1);
|
||||
}
|
||||
if ((header->value = strdup(value)) == NULL) {
|
||||
free(header->key);
|
||||
free(header);
|
||||
event_warn("%s: strdup", __func__);
|
||||
return (-1);
|
||||
}
|
||||
@ -672,7 +710,7 @@ evhttp_parse_lines(struct evhttp_request *req, struct evbuffer* buffer)
|
||||
while ((endp = evbuffer_find(buffer, "\r\n", 2)) != NULL) {
|
||||
char *skey, *svalue;
|
||||
|
||||
if (strncmp(buffer->buffer, "\r\n", 2) == 0) {
|
||||
if (strncmp(EVBUFFER_DATA(buffer), "\r\n", 2) == 0) {
|
||||
evbuffer_drain(buffer, 2);
|
||||
/* Last header - Done */
|
||||
done = 1;
|
||||
@ -682,17 +720,17 @@ evhttp_parse_lines(struct evhttp_request *req, struct evbuffer* buffer)
|
||||
*endp = '\0';
|
||||
endp += 2;
|
||||
|
||||
event_debug(("%s: Got: %s\n", __func__, buffer->buffer));
|
||||
event_debug(("%s: Got: %s\n", __func__, EVBUFFER_DATA(buffer)));
|
||||
|
||||
/* Processing of header lines */
|
||||
if (req->got_firstline == 0) {
|
||||
switch (req->kind) {
|
||||
case EVHTTP_REQUEST:
|
||||
if (evhttp_parse_request_line(req, buffer->buffer) == -1)
|
||||
if (evhttp_parse_request_line(req, EVBUFFER_DATA(buffer)) == -1)
|
||||
return (-1);
|
||||
break;
|
||||
case EVHTTP_RESPONSE:
|
||||
if (evhttp_parse_response_line(req, buffer->buffer) == -1)
|
||||
if (evhttp_parse_response_line(req, EVBUFFER_DATA(buffer)) == -1)
|
||||
return (-1);
|
||||
break;
|
||||
default:
|
||||
@ -701,7 +739,7 @@ evhttp_parse_lines(struct evhttp_request *req, struct evbuffer* buffer)
|
||||
req->got_firstline = 1;
|
||||
} else {
|
||||
/* Regular header */
|
||||
svalue = buffer->buffer;
|
||||
svalue = EVBUFFER_DATA(buffer);
|
||||
skey = strsep(&svalue, ":");
|
||||
if (svalue == NULL)
|
||||
return (-1);
|
||||
@ -713,7 +751,7 @@ evhttp_parse_lines(struct evhttp_request *req, struct evbuffer* buffer)
|
||||
}
|
||||
|
||||
/* Move the uncompleted headers forward */
|
||||
evbuffer_drain(buffer, endp - buffer->buffer);
|
||||
evbuffer_drain(buffer, endp - EVBUFFER_DATA(buffer));
|
||||
}
|
||||
|
||||
return (done);
|
||||
@ -723,8 +761,8 @@ void
|
||||
evhttp_get_body(struct evhttp_request *req)
|
||||
{
|
||||
struct timeval tv;
|
||||
char *content_length;
|
||||
char *connection;
|
||||
const char *content_length;
|
||||
const char *connection;
|
||||
struct evkeyvalq *headers = req->input_headers;
|
||||
|
||||
/* If this is a request without a body, then we are done */
|
||||
@ -752,10 +790,10 @@ evhttp_get_body(struct evhttp_request *req)
|
||||
req->ntoread = atoi(content_length);
|
||||
|
||||
event_debug(("%s: bytes to read: %d (in buffer %d)\n",
|
||||
__func__, req->ntoread, req->buffer->off));
|
||||
__func__, req->ntoread, EVBUFFER_LENGTH(req->buffer)));
|
||||
|
||||
if (req->ntoread > 0)
|
||||
req->ntoread -= req->buffer->off;
|
||||
req->ntoread -= EVBUFFER_LENGTH(req->buffer);
|
||||
|
||||
if (req->ntoread == 0) {
|
||||
(*req->cb)(req, req->cb_arg);
|
||||
@ -918,13 +956,6 @@ evhttp_make_request(struct evhttp_connection *evcon,
|
||||
/* Create the header from the store arguments */
|
||||
evhttp_make_header(evbuf, req);
|
||||
|
||||
/*
|
||||
* If this was a post request or for other reasons we need to append
|
||||
* our post data to the request.
|
||||
*/
|
||||
evbuffer_add_buffer(evbuf, req->buffer);
|
||||
|
||||
|
||||
/* Schedule the write */
|
||||
req->save_cb = req->cb;
|
||||
req->save_cbarg = req->cb_arg;
|
||||
@ -1307,6 +1338,17 @@ evhttp_request_free(struct evhttp_request *req)
|
||||
free(req);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allows for inspection of the request URI
|
||||
*/
|
||||
|
||||
const char *
|
||||
evhttp_request_uri(struct evhttp_request *req) {
|
||||
if (req->uri == NULL)
|
||||
event_debug(("%s: request %p has no uri\n", req));
|
||||
return (req->uri);
|
||||
}
|
||||
|
||||
/*
|
||||
* Takes a file descriptor to read a request from.
|
||||
* The callback is executed once the whole request has been read.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2004 Niels Provos <provos@citi.umich.edu>
|
||||
* Copyright (c) 2003-2006 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -62,6 +62,7 @@ extern int test_ok;
|
||||
static struct evhttp *http;
|
||||
|
||||
void http_basic_cb(struct evhttp_request *req, void *arg);
|
||||
void http_post_cb(struct evhttp_request *req, void *arg);
|
||||
|
||||
struct evhttp *
|
||||
http_setup(short *pport)
|
||||
@ -84,6 +85,7 @@ http_setup(short *pport)
|
||||
|
||||
/* Register a callback for certain types of requests */
|
||||
evhttp_set_cb(myhttp, "/test", http_basic_cb, NULL);
|
||||
evhttp_set_cb(myhttp, "/postit", http_post_cb, NULL);
|
||||
|
||||
*pport = port;
|
||||
return (myhttp);
|
||||
@ -232,11 +234,6 @@ http_connection_test(void)
|
||||
|
||||
event_dispatch();
|
||||
|
||||
/*
|
||||
* At this point, we want to schedule a request to the HTTP
|
||||
* server using our start request method.
|
||||
*/
|
||||
|
||||
evhttp_connection_free(evcon);
|
||||
evhttp_free(http);
|
||||
|
||||
@ -260,6 +257,11 @@ http_connectcb(struct evhttp_connection *evcon, void *arg)
|
||||
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 */
|
||||
@ -282,8 +284,7 @@ http_request_done(struct evhttp_request *req, void *arg)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (evhttp_find_header(req->input_headers,
|
||||
"Content-Type") == NULL) {
|
||||
if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) {
|
||||
fprintf(stderr, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
@ -302,9 +303,137 @@ http_request_done(struct evhttp_request *req, void *arg)
|
||||
event_loopexit(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* HTTP POST test.
|
||||
*/
|
||||
|
||||
void http_connect_forpostcb(struct evhttp_connection *evcon, void *arg);
|
||||
|
||||
void
|
||||
http_post_test(void)
|
||||
{
|
||||
short port = -1;
|
||||
struct evhttp_connection *evcon = NULL;
|
||||
|
||||
test_ok = 0;
|
||||
fprintf(stdout, "Testing HTTP POST Request: ");
|
||||
|
||||
http = http_setup(&port);
|
||||
|
||||
evcon = evhttp_connect("127.0.0.1", port, http_connect_forpostcb, NULL);
|
||||
if (evcon == NULL) {
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
event_dispatch();
|
||||
|
||||
evhttp_connection_free(evcon);
|
||||
evhttp_free(http);
|
||||
|
||||
if (test_ok != 1) {
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
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
|
||||
http_post_cb(struct evhttp_request *req, void *arg)
|
||||
{
|
||||
event_debug((stderr, "%s: called\n", __func__));
|
||||
|
||||
/* Yes, we are expecting a post request */
|
||||
if (req->type != EVHTTP_REQ_POST) {
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (EVBUFFER_LENGTH(req->buffer) != strlen(POST_DATA)) {
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (strcmp(EVBUFFER_DATA(req->buffer), POST_DATA)) {
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct evbuffer *evb = evbuffer_new();
|
||||
evbuffer_add_printf(evb, "This is funny");
|
||||
|
||||
evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb);
|
||||
|
||||
evbuffer_free(evb);
|
||||
}
|
||||
|
||||
void
|
||||
http_postrequest_done(struct evhttp_request *req, void *arg)
|
||||
{
|
||||
const char *what = "This is funny";
|
||||
|
||||
if (req->response_code != HTTP_OK) {
|
||||
|
||||
fprintf(stderr, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) {
|
||||
fprintf(stderr, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (EVBUFFER_LENGTH(req->buffer) != strlen(what)) {
|
||||
fprintf(stderr, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (memcmp(EVBUFFER_DATA(req->buffer), what, strlen(what)) != 0) {
|
||||
fprintf(stderr, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
test_ok = 1;
|
||||
event_loopexit(NULL);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
http_suite(void)
|
||||
{
|
||||
http_basic_test();
|
||||
http_connection_test();
|
||||
http_post_test();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user