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;
|
||||
/* 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;
|
||||
}
|
||||
stream_data->path = malloc(stream_data->pathlen);
|
||||
memcpy(stream_data->path,
|
||||
&uri[u->field_data[UF_PATH].off], u->field_data[UF_PATH].len);
|
||||
memcpy(stream_data->path + u->field_data[UF_PATH].len + 1,
|
||||
&uri[u->field_data[UF_QUERY].off], u->field_data[UF_QUERY].len);
|
||||
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,
|
||||
NULL, 0);
|
||||
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,
|
||||
iv, ARRLEN(iv));
|
||||
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,
|
||||
hdrs, ARRLEN(hdrs), NULL, stream_data);
|
||||
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) {
|
||||
|
|
|
@ -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,21 +372,27 @@ static ssize_t file_read_callback
|
|||
return r;
|
||||
}
|
||||
|
||||
static void send_response(nghttp2_session *session, int32_t stream_id,
|
||||
nghttp2_nv *nva, size_t nvlen, int fd)
|
||||
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,
|
||||
http2_stream_data *stream_data)
|
||||
static int error_reply(nghttp2_session *session,
|
||||
http2_stream_data *stream_data)
|
||||
{
|
||||
int rv;
|
||||
int pipefd[2];
|
||||
|
@ -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,
|
||||
stream_data->stream_id,
|
||||
NGHTTP2_INTERNAL_ERROR);
|
||||
return;
|
||||
warn("Could not create pipe");
|
||||
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
|
||||
stream_data->stream_id,
|
||||
NGHTTP2_INTERNAL_ERROR);
|
||||
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,
|
||||
iv, ARRLEN(iv));
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue