examples: Add minimum error handling for API functions
This commit is contained in:
parent
9fc9ef9a40
commit
946d3150ba
|
@ -47,9 +47,10 @@ typedef struct {
|
||||||
const char *uri;
|
const char *uri;
|
||||||
/* Parsed result of the |uri| */
|
/* Parsed result of the |uri| */
|
||||||
struct http_parser_url *u;
|
struct http_parser_url *u;
|
||||||
/* The authroity portion of the |uri|, NULL-terminated */
|
/* The authroity portion of the |uri|, not NULL-terminated */
|
||||||
char *authority;
|
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;
|
char *path;
|
||||||
/* The length of the |authority| */
|
/* The length of the |authority| */
|
||||||
size_t authoritylen;
|
size_t authoritylen;
|
||||||
|
@ -87,6 +88,7 @@ static http2_stream_data* create_http2_stream_data(const char *uri,
|
||||||
memcpy(stream_data->authority,
|
memcpy(stream_data->authority,
|
||||||
&uri[u->field_data[UF_HOST].off], u->field_data[UF_HOST].len);
|
&uri[u->field_data[UF_HOST].off], u->field_data[UF_HOST].len);
|
||||||
|
|
||||||
|
stream_data->pathlen = 0;
|
||||||
if(u->field_set & (1 << UF_PATH)) {
|
if(u->field_set & (1 << UF_PATH)) {
|
||||||
stream_data->pathlen = u->field_data[UF_PATH].len;
|
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 */
|
/* +1 for '?' character */
|
||||||
stream_data->pathlen += u->field_data[UF_QUERY].len + 1;
|
stream_data->pathlen += u->field_data[UF_QUERY].len + 1;
|
||||||
}
|
}
|
||||||
|
if(stream_data->pathlen > 0) {
|
||||||
stream_data->path = malloc(stream_data->pathlen);
|
stream_data->path = malloc(stream_data->pathlen);
|
||||||
|
if(u->field_set & (1 << UF_PATH)) {
|
||||||
memcpy(stream_data->path,
|
memcpy(stream_data->path,
|
||||||
&uri[u->field_data[UF_PATH].off], u->field_data[UF_PATH].len);
|
&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,
|
memcpy(stream_data->path + u->field_data[UF_PATH].len + 1,
|
||||||
&uri[u->field_data[UF_QUERY].off], u->field_data[UF_QUERY].len);
|
&uri[u->field_data[UF_QUERY].off], u->field_data[UF_QUERY].len);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stream_data->path = NULL;
|
||||||
|
}
|
||||||
return stream_data;
|
return stream_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,11 +244,16 @@ static int on_stream_close_callback(nghttp2_session *session,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||||
|
int rv;
|
||||||
|
|
||||||
if(session_data->stream_data->stream_id == stream_id) {
|
if(session_data->stream_data->stream_id == stream_id) {
|
||||||
fprintf(stderr, "Stream %d closed with error_code=%d\n",
|
fprintf(stderr, "Stream %d closed with error_code=%d\n",
|
||||||
stream_id, error_code);
|
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);
|
NULL, 0);
|
||||||
|
if(rv != 0) {
|
||||||
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -302,11 +317,16 @@ static void send_client_connection_header(http2_session_data *session_data)
|
||||||
nghttp2_settings_entry iv[1] = {
|
nghttp2_settings_entry iv[1] = {
|
||||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
||||||
};
|
};
|
||||||
|
int rv;
|
||||||
|
|
||||||
bufferevent_write(session_data->bev,
|
bufferevent_write(session_data->bev,
|
||||||
NGHTTP2_CLIENT_CONNECTION_HEADER,
|
NGHTTP2_CLIENT_CONNECTION_HEADER,
|
||||||
NGHTTP2_CLIENT_CONNECTION_HEADER_LEN);
|
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));
|
iv, ARRLEN(iv));
|
||||||
|
if(rv != 0) {
|
||||||
|
errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAKE_NV(NAME, VALUE, VALUELEN) \
|
#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 */
|
/* Send HTTP request to the remote peer */
|
||||||
static void submit_request(http2_session_data *session_data)
|
static void submit_request(http2_session_data *session_data)
|
||||||
{
|
{
|
||||||
|
int rv;
|
||||||
http2_stream_data *stream_data = session_data->stream_data;
|
http2_stream_data *stream_data = session_data->stream_data;
|
||||||
const char *uri = stream_data->uri;
|
const char *uri = stream_data->uri;
|
||||||
const struct http_parser_url *u = stream_data->u;
|
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");
|
fprintf(stderr, "Request headers:\n");
|
||||||
print_headers(stderr, hdrs, ARRLEN(hdrs));
|
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);
|
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
|
/* Serialize the frame and send (or buffer) the data to
|
||||||
bufferevent. */
|
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
|
/* 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) {
|
if(rv < 0) {
|
||||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
} else {
|
return;
|
||||||
evbuffer_drain(input, rv);
|
}
|
||||||
|
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
|
/* 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);
|
initialize_nghttp2_session(session_data);
|
||||||
send_client_connection_header(session_data);
|
send_client_connection_header(session_data);
|
||||||
submit_request(session_data);
|
submit_request(session_data);
|
||||||
session_send(session_data);
|
if(session_send(session_data) != 0) {
|
||||||
|
delete_http2_session_data(session_data);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(events & BEV_EVENT_EOF) {
|
if(events & BEV_EVENT_EOF) {
|
||||||
|
|
|
@ -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
|
/* Serialize the frame and send (or buffer) the data to
|
||||||
bufferevent. */
|
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
|
/* Read the data in the bufferevent and feed them into nghttp2 library
|
||||||
function. Invocation of nghttp2_session_mem_recv() may make
|
function. Invocation of nghttp2_session_mem_recv() may make
|
||||||
additional pending frames, so call session_send() at the end of the
|
additional pending frames, so call session_send() at the end of the
|
||||||
function. */
|
function. */
|
||||||
static void session_recv(http2_session_data *session_data)
|
static int session_recv(http2_session_data *session_data)
|
||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
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);
|
rv = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||||
if(rv < 0) {
|
if(rv < 0) {
|
||||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||||
delete_http2_session_data(session_data);
|
return -1;
|
||||||
} else {
|
|
||||||
evbuffer_drain(input, rv);
|
|
||||||
}
|
}
|
||||||
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,
|
static ssize_t send_callback(nghttp2_session *session,
|
||||||
|
@ -364,20 +372,26 @@ static ssize_t file_read_callback
|
||||||
return r;
|
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)
|
nghttp2_nv *nva, size_t nvlen, int fd)
|
||||||
{
|
{
|
||||||
|
int rv;
|
||||||
nghttp2_data_provider data_prd;
|
nghttp2_data_provider data_prd;
|
||||||
data_prd.source.fd = fd;
|
data_prd.source.fd = fd;
|
||||||
data_prd.read_callback = file_read_callback;
|
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>"
|
const char ERROR_HTML[] = "<html><head><title>404</title></head>"
|
||||||
"<body><h1>404 Not Found</h1></body></html>";
|
"<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)
|
http2_stream_data *stream_data)
|
||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
|
@ -388,16 +402,25 @@ static void error_reply(nghttp2_session *session,
|
||||||
|
|
||||||
rv = pipe(pipefd);
|
rv = pipe(pipefd);
|
||||||
if(rv != 0) {
|
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,
|
stream_data->stream_id,
|
||||||
NGHTTP2_INTERNAL_ERROR);
|
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);
|
write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
|
||||||
close(pipefd[1]);
|
close(pipefd[1]);
|
||||||
stream_data->fd = pipefd[0];
|
stream_data->fd = pipefd[0];
|
||||||
send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
|
if(send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
|
||||||
pipefd[0]);
|
pipefd[0]) != 0) {
|
||||||
|
close(pipefd[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Minimum check for directory traversal. Returns nonzero if it is
|
/* 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
|
stream_data = (http2_stream_data*)nghttp2_session_get_stream_user_data
|
||||||
(session, stream_id);
|
(session, stream_id);
|
||||||
if(!stream_data->request_path) {
|
if(!stream_data->request_path) {
|
||||||
error_reply(session, stream_data);
|
if(error_reply(session, stream_data) != 0) {
|
||||||
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
fprintf(stderr, "%s GET %s\n", session_data->client_addr,
|
fprintf(stderr, "%s GET %s\n", session_data->client_addr,
|
||||||
stream_data->request_path);
|
stream_data->request_path);
|
||||||
if(!check_path(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;
|
return 0;
|
||||||
}
|
}
|
||||||
for(rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path);
|
for(rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path);
|
||||||
fd = open(rel_path, O_RDONLY);
|
fd = open(rel_path, O_RDONLY);
|
||||||
if(fd == -1) {
|
if(fd == -1) {
|
||||||
error_reply(session, stream_data);
|
if(error_reply(session, stream_data) != 0) {
|
||||||
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
stream_data->fd = fd;
|
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;
|
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
|
/* Send HTTP/2.0 client connection header, which includes 24 bytes
|
||||||
magic octets and SETTINGS frame */
|
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_entry iv[1] = {
|
||||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
{ 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));
|
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
|
/* readcb for bufferevent after client connection header was
|
||||||
checked. */
|
checked. */
|
||||||
static void readcb(struct bufferevent *bev, void *ptr)
|
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
|
/* 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);
|
delete_http2_session_data(session_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
session_send(session_data);
|
if(session_send(session_data) != 0) {
|
||||||
|
delete_http2_session_data(session_data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eventcb for bufferevent */
|
/* 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
|
/* Process pending data in buffer since they are not notified
|
||||||
further */
|
further */
|
||||||
initialize_nghttp2_session(session_data);
|
initialize_nghttp2_session(session_data);
|
||||||
send_server_connection_header(session_data);
|
if(send_server_connection_header(session_data) != 0) {
|
||||||
session_recv(session_data);
|
delete_http2_session_data(session_data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(session_recv(session_data) != 0) {
|
||||||
|
delete_http2_session_data(session_data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue