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 evhttp_request *req;
struct evbuffer *evbuf;
struct event ev;
int id;
int fd;
size_t size;
@ -166,7 +167,7 @@ stream_chunk_xcode_cb(int fd, short event, void *arg)
continue_stream:
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)
{
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;
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);
evbuffer_free(st->evbuf);
@ -218,7 +223,7 @@ stream_chunk_raw_cb(int fd, short event, void *arg)
}
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)
{
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;
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);
close(st->fd);
@ -236,6 +245,30 @@ stream_chunk_raw_cb(int fd, short event, void *arg)
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 */
void
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);
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)
{
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");
}
/* 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);
db_dispose_item(mfi);