examples: Add minimum error handling for API functions

This commit is contained in:
Tatsuhiro Tsujikawa 2013-12-24 21:30:49 +09:00
parent 9fc9ef9a40
commit 946d3150ba
2 changed files with 136 additions and 48 deletions

View File

@ -47,9 +47,10 @@ typedef struct {
const char *uri;
/* Parsed result of the |uri| */
struct http_parser_url *u;
/* The authroity portion of the |uri|, NULL-terminated */
/* The authroity portion of the |uri|, not NULL-terminated */
char *authority;
/* The path portion of the |uri|, including query, NULL-terminated */
/* The path portion of the |uri|, including query, not
NULL-terminated */
char *path;
/* The length of the |authority| */
size_t authoritylen;
@ -87,6 +88,7 @@ static http2_stream_data* create_http2_stream_data(const char *uri,
memcpy(stream_data->authority,
&uri[u->field_data[UF_HOST].off], u->field_data[UF_HOST].len);
stream_data->pathlen = 0;
if(u->field_set & (1 << UF_PATH)) {
stream_data->pathlen = u->field_data[UF_PATH].len;
}
@ -94,11 +96,19 @@ static http2_stream_data* create_http2_stream_data(const char *uri,
/* +1 for '?' character */
stream_data->pathlen += u->field_data[UF_QUERY].len + 1;
}
if(stream_data->pathlen > 0) {
stream_data->path = malloc(stream_data->pathlen);
if(u->field_set & (1 << UF_PATH)) {
memcpy(stream_data->path,
&uri[u->field_data[UF_PATH].off], u->field_data[UF_PATH].len);
}
if(u->field_set & (1 << UF_QUERY)) {
memcpy(stream_data->path + u->field_data[UF_PATH].len + 1,
&uri[u->field_data[UF_QUERY].off], u->field_data[UF_QUERY].len);
}
} else {
stream_data->path = NULL;
}
return stream_data;
}
@ -234,11 +244,16 @@ static int on_stream_close_callback(nghttp2_session *session,
void *user_data)
{
http2_session_data *session_data = (http2_session_data*)user_data;
int rv;
if(session_data->stream_data->stream_id == stream_id) {
fprintf(stderr, "Stream %d closed with error_code=%d\n",
stream_id, error_code);
nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, NGHTTP2_NO_ERROR,
rv = nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, NGHTTP2_NO_ERROR,
NULL, 0);
if(rv != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
}
return 0;
}
@ -302,11 +317,16 @@ static void send_client_connection_header(http2_session_data *session_data)
nghttp2_settings_entry iv[1] = {
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
};
int rv;
bufferevent_write(session_data->bev,
NGHTTP2_CLIENT_CONNECTION_HEADER,
NGHTTP2_CLIENT_CONNECTION_HEADER_LEN);
nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
iv, ARRLEN(iv));
if(rv != 0) {
errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
}
}
#define MAKE_NV(NAME, VALUE, VALUELEN) \
@ -318,6 +338,7 @@ static void send_client_connection_header(http2_session_data *session_data)
/* Send HTTP request to the remote peer */
static void submit_request(http2_session_data *session_data)
{
int rv;
http2_stream_data *stream_data = session_data->stream_data;
const char *uri = stream_data->uri;
const struct http_parser_url *u = stream_data->u;
@ -330,15 +351,25 @@ static void submit_request(http2_session_data *session_data)
};
fprintf(stderr, "Request headers:\n");
print_headers(stderr, hdrs, ARRLEN(hdrs));
nghttp2_submit_request(session_data->session, NGHTTP2_PRI_DEFAULT,
rv = nghttp2_submit_request(session_data->session, NGHTTP2_PRI_DEFAULT,
hdrs, ARRLEN(hdrs), NULL, stream_data);
if(rv != 0) {
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(rv));
}
}
/* Serialize the frame and send (or buffer) the data to
bufferevent. */
static void session_send(http2_session_data *session_data)
static int session_send(http2_session_data *session_data)
{
nghttp2_session_send(session_data->session);
int rv;
rv = nghttp2_session_send(session_data->session);
if(rv != 0) {
warnx("Fatal error: %s", nghttp2_strerror(rv));
return -1;
}
return 0;
}
/* readcb for bufferevent. Here we get the data from the input buffer
@ -356,10 +387,13 @@ static void readcb(struct bufferevent *bev, void *ptr)
if(rv < 0) {
warnx("Fatal error: %s", nghttp2_strerror(rv));
delete_http2_session_data(session_data);
} else {
evbuffer_drain(input, rv);
return;
}
evbuffer_drain(input, rv);
if(session_send(session_data) != 0) {
delete_http2_session_data(session_data);
return;
}
session_send(session_data);
}
/* writecb for bufferevent. To greaceful shutdown after sending or
@ -392,7 +426,9 @@ static void eventcb(struct bufferevent *bev, short events, void *ptr)
initialize_nghttp2_session(session_data);
send_client_connection_header(session_data);
submit_request(session_data);
session_send(session_data);
if(session_send(session_data) != 0) {
delete_http2_session_data(session_data);
}
return;
}
if(events & BEV_EVENT_EOF) {

View File

@ -222,16 +222,22 @@ static void delete_http2_session_data(http2_session_data *session_data)
/* Serialize the frame and send (or buffer) the data to
bufferevent. */
static void session_send(http2_session_data *session_data)
static int session_send(http2_session_data *session_data)
{
nghttp2_session_send(session_data->session);
int rv;
rv = nghttp2_session_send(session_data->session);
if(rv != 0) {
warnx("Fatal error: %s", nghttp2_strerror(rv));
return -1;
}
return 0;
}
/* Read the data in the bufferevent and feed them into nghttp2 library
function. Invocation of nghttp2_session_mem_recv() may make
additional pending frames, so call session_send() at the end of the
function. */
static void session_recv(http2_session_data *session_data)
static int session_recv(http2_session_data *session_data)
{
int rv;
struct evbuffer *input = bufferevent_get_input(session_data->bev);
@ -240,11 +246,13 @@ static void session_recv(http2_session_data *session_data)
rv = nghttp2_session_mem_recv(session_data->session, data, datalen);
if(rv < 0) {
warnx("Fatal error: %s", nghttp2_strerror(rv));
delete_http2_session_data(session_data);
} else {
evbuffer_drain(input, rv);
return -1;
}
session_send(session_data);
evbuffer_drain(input, rv);
if(session_send(session_data) != 0) {
return -1;
}
return 0;
}
static ssize_t send_callback(nghttp2_session *session,
@ -364,20 +372,26 @@ static ssize_t file_read_callback
return r;
}
static void send_response(nghttp2_session *session, int32_t stream_id,
static int send_response(nghttp2_session *session, int32_t stream_id,
nghttp2_nv *nva, size_t nvlen, int fd)
{
int rv;
nghttp2_data_provider data_prd;
data_prd.source.fd = fd;
data_prd.read_callback = file_read_callback;
nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
if(rv != 0) {
warnx("Fatal error: %s", nghttp2_strerror(rv));
return -1;
}
return 0;
}
const char ERROR_HTML[] = "<html><head><title>404</title></head>"
"<body><h1>404 Not Found</h1></body></html>";
static void error_reply(nghttp2_session *session,
static int error_reply(nghttp2_session *session,
http2_stream_data *stream_data)
{
int rv;
@ -388,16 +402,25 @@ static void error_reply(nghttp2_session *session,
rv = pipe(pipefd);
if(rv != 0) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
warn("Could not create pipe");
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
stream_data->stream_id,
NGHTTP2_INTERNAL_ERROR);
return;
if(rv != 0) {
warnx("Fatal error: %s", nghttp2_strerror(rv));
return -1;
}
return 0;
}
write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
close(pipefd[1]);
stream_data->fd = pipefd[0];
send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
pipefd[0]);
if(send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
pipefd[0]) != 0) {
close(pipefd[0]);
return -1;
}
return 0;
}
/* Minimum check for directory traversal. Returns nonzero if it is
@ -426,24 +449,33 @@ static int on_request_recv_callback(nghttp2_session *session,
stream_data = (http2_stream_data*)nghttp2_session_get_stream_user_data
(session, stream_id);
if(!stream_data->request_path) {
error_reply(session, stream_data);
if(error_reply(session, stream_data) != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
fprintf(stderr, "%s GET %s\n", session_data->client_addr,
stream_data->request_path);
if(!check_path(stream_data->request_path)) {
error_reply(session, stream_data);
if(error_reply(session, stream_data) != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
for(rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path);
fd = open(rel_path, O_RDONLY);
if(fd == -1) {
error_reply(session, stream_data);
if(error_reply(session, stream_data) != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
stream_data->fd = fd;
send_response(session, stream_id, hdrs, ARRLEN(hdrs), fd);
if(send_response(session, stream_id, hdrs, ARRLEN(hdrs), fd) != 0) {
close(fd);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
@ -474,20 +506,31 @@ static void initialize_nghttp2_session(http2_session_data *session_data)
/* Send HTTP/2.0 client connection header, which includes 24 bytes
magic octets and SETTINGS frame */
static void send_server_connection_header(http2_session_data *session_data)
static int send_server_connection_header(http2_session_data *session_data)
{
nghttp2_settings_entry iv[1] = {
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
};
nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
int rv;
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
iv, ARRLEN(iv));
if(rv != 0) {
warnx("Fatal error: %s", nghttp2_strerror(rv));
return -1;
}
return 0;
}
/* readcb for bufferevent after client connection header was
checked. */
static void readcb(struct bufferevent *bev, void *ptr)
{
session_recv((http2_session_data*)ptr);
http2_session_data *session_data = (http2_session_data*)ptr;
if(session_recv(session_data) != 0) {
delete_http2_session_data(session_data);
return;
}
}
/* writecb for bufferevent. To greaceful shutdown after sending or
@ -509,7 +552,10 @@ static void writecb(struct bufferevent *bev, void *ptr)
delete_http2_session_data(session_data);
return;
}
session_send(session_data);
if(session_send(session_data) != 0) {
delete_http2_session_data(session_data);
return;
}
}
/* eventcb for bufferevent */
@ -551,8 +597,14 @@ static void handshake_readcb(struct bufferevent *bev, void *ptr)
/* Process pending data in buffer since they are not notified
further */
initialize_nghttp2_session(session_data);
send_server_connection_header(session_data);
session_recv(session_data);
if(send_server_connection_header(session_data) != 0) {
delete_http2_session_data(session_data);
return;
}
if(session_recv(session_data) != 0) {
delete_http2_session_data(session_data);
return;
}
}
}