Reply to update requests periodically to avoid 30-minute iTunes timeout

Craig Markwardt <craig.markwardt@gmail.com> found out that the 30-minute
timeout in iTunes was caused by the lack of reply to update requests.

We now send out replies every 5 minutes, avoiding the timeout and
disconnection.

Thanks to Craig for digging into this, producing code to demonstrate the fix
and trying out a few more ideas for update support beyond this fix.
This commit is contained in:
Julien BLACHE 2011-09-10 18:37:16 +02:00
parent ad4e15c362
commit 9f06848d43

View File

@ -59,6 +59,8 @@ extern struct event_base *evbase_httpd;
/* Session timeout in seconds */
#define DAAP_SESSION_TIMEOUT 1800
/* Update requests refresh interval in seconds */
#define DAAP_UPDATE_REFRESH 300
struct uri_map {
@ -76,6 +78,9 @@ struct daap_session {
struct daap_update_request {
struct evhttp_request *req;
/* Refresh tiemout */
struct event timeout;
struct daap_update_request *next;
};
@ -98,6 +103,7 @@ static avl_tree_t *daap_sessions;
static int next_session_id;
/* Update requests */
static int current_rev;
static struct daap_update_request *update_requests;
@ -248,18 +254,10 @@ daap_session_find(struct evhttp_request *req, struct evkeyvalq *query, struct ev
/* Update requests helpers */
static void
update_fail_cb(struct evhttp_connection *evcon, void *arg)
update_remove(struct daap_update_request *ur)
{
struct daap_update_request *ur;
struct daap_update_request *p;
ur = (struct daap_update_request *)arg;
DPRINTF(E_DBG, L_DAAP, "Update request: client closed connection\n");
if (ur->req->evcon)
evhttp_connection_set_closecb(ur->req->evcon, NULL, NULL);
if (ur == update_requests)
update_requests = ur->next;
else
@ -275,10 +273,70 @@ update_fail_cb(struct evhttp_connection *evcon, void *arg)
p->next = ur->next;
}
}
static void
update_free(struct daap_update_request *ur)
{
if (event_initialized(&ur->timeout))
evtimer_del(&ur->timeout);
free(ur);
}
static void
update_refresh_cb(int fd, short event, void *arg)
{
struct daap_update_request *ur;
struct evbuffer *evbuf;
int ret;
ur = (struct daap_update_request *)arg;
evbuf = evbuffer_new();
if (!evbuf)
{
DPRINTF(E_LOG, L_DAAP, "Could not allocate evbuffer for DAAP update data\n");
return;
}
ret = evbuffer_expand(evbuf, 32);
if (ret < 0)
{
DPRINTF(E_LOG, L_DAAP, "Could not expand evbuffer for DAAP update data\n");
return;
}
/* Send back current revision */
dmap_add_container(evbuf, "mupd", 24);
dmap_add_int(evbuf, "mstt", 200); /* 12 */
dmap_add_int(evbuf, "musr", current_rev); /* 12 */
evhttp_connection_set_closecb(ur->req->evcon, NULL, NULL);
httpd_send_reply(ur->req, HTTP_OK, "OK", evbuf);
update_remove(ur);
update_free(ur);
}
static void
update_fail_cb(struct evhttp_connection *evcon, void *arg)
{
struct daap_update_request *ur;
ur = (struct daap_update_request *)arg;
DPRINTF(E_DBG, L_DAAP, "Update request: client closed connection\n");
if (ur->req->evcon)
evhttp_connection_set_closecb(ur->req->evcon, NULL, NULL);
update_remove(ur);
update_free(ur);
}
/* DAAP sort headers helpers */
static struct sort_ctx *
@ -780,10 +838,10 @@ daap_reply_logout(struct evhttp_request *req, struct evbuffer *evbuf, char **uri
static void
daap_reply_update(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query)
{
struct timeval tv;
struct daap_session *s;
struct daap_update_request *ur;
const char *param;
int current_rev = 2;
int reqd_rev;
int ret;
@ -839,6 +897,24 @@ daap_reply_update(struct evhttp_request *req, struct evbuffer *evbuf, char **uri
dmap_send_error(req, "mupd", "Out of memory");
return;
}
memset(ur, 0, sizeof(struct daap_update_request));
evtimer_set(&ur->timeout, update_refresh_cb, ur);
event_base_set(evbase_httpd, &ur->timeout);
evutil_timerclear(&tv);
tv.tv_sec = DAAP_UPDATE_REFRESH;
ret = evtimer_add(&ur->timeout, &tv);
if (ret < 0)
{
DPRINTF(E_LOG, L_DAAP, "Could not add update timeout event\n");
dmap_send_error(req, "mupd", "Could not register timer");
update_free(ur);
return;
}
/* NOTE: we may need to keep reqd_rev in there too */
ur->req = req;
@ -2428,6 +2504,7 @@ daap_init(void)
int ret;
next_session_id = 100; /* gotta start somewhere, right? */
current_rev = 2;
update_requests = NULL;
for (i = 0; daap_handlers[i].handler; i++)
@ -2480,6 +2557,6 @@ daap_deinit(void)
evhttp_connection_free(ur->req->evcon);
}
free(ur);
update_free(ur);
}
}