Use evhttp_request->fail_cb to stop streaming if the client closes the connection

This is an extension to evhttp. Without this, we cannot know that the client
closes the connection, and we keep pushing chunks while the underlying
evhttp_request has been freed, leading to a segfault.
This commit is contained in:
Julien BLACHE 2009-05-02 18:49:31 +02:00
parent 1ffcbdae27
commit 1d152decf7

View File

@ -76,6 +76,7 @@ struct content_type_map {
struct stream_chunk { struct stream_chunk {
struct evhttp_request *req; struct evhttp_request *req;
struct evbuffer *evbuf; struct evbuffer *evbuf;
struct event ev;
int id; int id;
int fd; int fd;
size_t size; size_t size;
@ -166,7 +167,7 @@ stream_chunk_xcode_cb(int fd, short event, void *arg)
continue_stream: continue_stream:
evutil_timerclear(&tv); evutil_timerclear(&tv);
ret = event_base_once(evbase_httpd, -1, EV_TIMEOUT, stream_chunk_xcode_cb, st, &tv); ret = event_add(&st->ev, &tv);
if (ret < 0) if (ret < 0)
{ {
DPRINTF(E_LOG, L_HTTPD, "Could not re-add one-shot event for streaming\n"); DPRINTF(E_LOG, L_HTTPD, "Could not re-add one-shot event for streaming\n");
@ -177,6 +178,10 @@ stream_chunk_xcode_cb(int fd, short event, void *arg)
return; return;
end_stream: end_stream:
/* This is an extension to the stock evhttp */
st->req->fail_cb = NULL;
st->req->fail_cb_arg = NULL;
evhttp_send_reply_end(st->req); evhttp_send_reply_end(st->req);
evbuffer_free(st->evbuf); evbuffer_free(st->evbuf);
@ -218,7 +223,7 @@ stream_chunk_raw_cb(int fd, short event, void *arg)
} }
evutil_timerclear(&tv); evutil_timerclear(&tv);
ret = event_base_once(evbase_httpd, -1, EV_TIMEOUT, stream_chunk_raw_cb, st, &tv); ret = event_add(&st->ev, &tv);
if (ret < 0) if (ret < 0)
{ {
DPRINTF(E_LOG, L_HTTPD, "Could not re-add one-shot event for streaming\n"); DPRINTF(E_LOG, L_HTTPD, "Could not re-add one-shot event for streaming\n");
@ -229,6 +234,10 @@ stream_chunk_raw_cb(int fd, short event, void *arg)
return; return;
end_stream: end_stream:
/* This is an extension to the stock evhttp */
st->req->fail_cb = NULL;
st->req->fail_cb_arg = NULL;
evhttp_send_reply_end(st->req); evhttp_send_reply_end(st->req);
close(st->fd); close(st->fd);
@ -236,6 +245,30 @@ stream_chunk_raw_cb(int fd, short event, void *arg)
free(st); free(st);
} }
static void
stream_fail_cb(struct evhttp_request *req, void *arg)
{
struct stream_chunk *st;
st = (struct stream_chunk *)arg;
DPRINTF(E_LOG, L_HTTPD, "Connection failed; stopping streaming of file ID %d\n", st->id);
req->fail_cb = NULL;
req->fail_cb_arg = NULL;
/* Stop streaming */
event_del(&st->ev);
/* Cleanup */
evbuffer_free(st->evbuf);
if (st->xcode)
transcode_cleanup(st->xcode);
free(st);
}
/* Thread: httpd */ /* Thread: httpd */
void void
httpd_stream_file(struct evhttp_request *req, int id) httpd_stream_file(struct evhttp_request *req, int id)
@ -411,7 +444,9 @@ httpd_stream_file(struct evhttp_request *req, int id)
} }
evutil_timerclear(&tv); evutil_timerclear(&tv);
ret = event_base_once(evbase_httpd, -1, EV_TIMEOUT, stream_cb, st, &tv); event_set(&st->ev, -1, EV_TIMEOUT, stream_cb, st);
event_base_set(evbase_httpd, &st->ev);
ret = event_add(&st->ev, &tv);
if (ret < 0) if (ret < 0)
{ {
DPRINTF(E_LOG, L_HTTPD, "Could not add one-shot event for streaming\n"); DPRINTF(E_LOG, L_HTTPD, "Could not add one-shot event for streaming\n");
@ -449,6 +484,10 @@ httpd_stream_file(struct evhttp_request *req, int id)
evhttp_send_reply_start(req, 206, "Partial Content"); evhttp_send_reply_start(req, 206, "Partial Content");
} }
/* This is an extension to the stock evhttp */
req->fail_cb = stream_fail_cb;
req->fail_cb_arg = st;
DPRINTF(E_INF, L_HTTPD, "Kicking off streaming for %s\n", mfi->path); DPRINTF(E_INF, L_HTTPD, "Kicking off streaming for %s\n", mfi->path);
db_dispose_item(mfi); db_dispose_item(mfi);