diff --git a/src/inputs/librespot-c/src/connection.c b/src/inputs/librespot-c/src/connection.c index 95117d1a2f..aa41db9b0e 100644 --- a/src/inputs/librespot-c/src/connection.c +++ b/src/inputs/librespot-c/src/connection.c @@ -188,8 +188,10 @@ file_select(uint8_t *out, size_t out_len, Track *track, enum sp_bitrates bitrate /* --------------------------- Connection handling -------------------------- */ +// Connects to access point resolver and selects the first access point (unless +// it matches "avoid", i.e. an access point that previously failed) static int -ap_resolve(char **address, unsigned short *port) +ap_resolve(char **address, unsigned short *port, const char *avoid) { char *body; json_object *jresponse = NULL; @@ -199,6 +201,7 @@ ap_resolve(char **address, unsigned short *port) char *ap_port; int ap_num; int ret; + int i; free(*address); *address = NULL; @@ -215,12 +218,22 @@ ap_resolve(char **address, unsigned short *port) RETURN_ERROR(SP_ERR_NOCONNECTION, "Unexpected reply from access point resolver"); ap_num = json_object_array_length(ap_list); - ap = json_object_array_get_idx(ap_list, rand() % ap_num); - if (! (ap && json_object_get_type(ap) == json_type_string)) - RETURN_ERROR(SP_ERR_NOCONNECTION, "Unexpected reply from access point resolver"); - ap_address = strdup(json_object_get_string(ap)); + for (i = 0; i < ap_num; i++) + { + ap = json_object_array_get_idx(ap_list, i); + if (! (ap && json_object_get_type(ap) == json_type_string)) + RETURN_ERROR(SP_ERR_NOCONNECTION, "Unexpected reply from access point resolver"); + + if (avoid && strncmp(avoid, json_object_get_string(ap), strlen(avoid)) == 0) + continue; // This AP has failed on us previously, so avoid + + ap_address = strdup(json_object_get_string(ap)); + break; + } + if (!ap_address) + RETURN_ERROR(SP_ERR_NOCONNECTION, "Unexpected reply from access point resolver, no suitable access point"); if (! (ap_port = strchr(ap_address, ':'))) RETURN_ERROR(SP_ERR_NOCONNECTION, "Unexpected reply from access point resolver, missing port"); *ap_port = '\0'; @@ -285,14 +298,14 @@ connection_idle_cb(int fd, short what, void *arg) } static int -connection_make(struct sp_connection *conn, struct sp_conn_callbacks *cb, void *response_cb_arg) +connection_make(struct sp_connection *conn, const char *ap_avoid, struct sp_conn_callbacks *cb, void *response_cb_arg) { int response_fd; int ret; if (!conn->ap_address || !conn->ap_port) { - ret = ap_resolve(&conn->ap_address, &conn->ap_port); + ret = ap_resolve(&conn->ap_address, &conn->ap_port, ap_avoid); if (ret < 0) RETURN_ERROR(ret, sp_errmsg); } @@ -330,7 +343,7 @@ connection_make(struct sp_connection *conn, struct sp_conn_callbacks *cb, void * } enum sp_error -ap_connect(struct sp_connection *conn, enum sp_msg_type type, time_t *cooldown_ts, struct sp_conn_callbacks *cb, void *cb_arg) +ap_connect(struct sp_connection *conn, enum sp_msg_type type, time_t *cooldown_ts, const char *ap_avoid, struct sp_conn_callbacks *cb, void *cb_arg) { int ret; time_t now; @@ -348,7 +361,7 @@ ap_connect(struct sp_connection *conn, enum sp_msg_type type, time_t *cooldown_t else RETURN_ERROR(SP_ERR_NOCONNECTION, "Cannot connect to access point, cooldown after disconnect is in effect"); - ret = connection_make(conn, cb, cb_arg); + ret = connection_make(conn, ap_avoid, cb, cb_arg); if (ret < 0) RETURN_ERROR(ret, sp_errmsg); } @@ -363,6 +376,12 @@ ap_connect(struct sp_connection *conn, enum sp_msg_type type, time_t *cooldown_t return ret; } +const char * +ap_address_get(struct sp_connection *conn) +{ + return conn->ap_address; +} + /* ------------------------------ Raw packets ------------------------------- */ static ssize_t diff --git a/src/inputs/librespot-c/src/connection.h b/src/inputs/librespot-c/src/connection.h index 77877e0e25..5f1aa8bf24 100644 --- a/src/inputs/librespot-c/src/connection.h +++ b/src/inputs/librespot-c/src/connection.h @@ -2,7 +2,10 @@ void ap_disconnect(struct sp_connection *conn); enum sp_error -ap_connect(struct sp_connection *conn, enum sp_msg_type type, time_t *cooldown_ts, struct sp_conn_callbacks *cb, void *cb_arg); +ap_connect(struct sp_connection *conn, enum sp_msg_type type, time_t *cooldown_ts, const char *ap_address, struct sp_conn_callbacks *cb, void *cb_arg); + +const char * +ap_address_get(struct sp_connection *conn); enum sp_error response_read(struct sp_session *session); diff --git a/src/inputs/librespot-c/src/librespot-c-internal.h b/src/inputs/librespot-c/src/librespot-c-internal.h index bc9d964ac1..decffb5b27 100644 --- a/src/inputs/librespot-c/src/librespot-c-internal.h +++ b/src/inputs/librespot-c/src/librespot-c-internal.h @@ -322,6 +322,9 @@ struct sp_session struct sp_connection conn; time_t cooldown_ts; + // Address of an access point we want to avoid due to previous failure + char *ap_avoid; + bool is_logged_in; struct sp_credentials credentials; char country[3]; // Incl null term diff --git a/src/inputs/librespot-c/src/librespot-c.c b/src/inputs/librespot-c/src/librespot-c.c index 3319b6a960..8a001aedde 100644 --- a/src/inputs/librespot-c/src/librespot-c.c +++ b/src/inputs/librespot-c/src/librespot-c.c @@ -96,6 +96,8 @@ session_free(struct sp_session *session) ap_disconnect(&session->conn); event_free(session->continue_ev); + + free(session->ap_avoid); free(session); } @@ -248,12 +250,16 @@ session_retry(struct sp_session *session) { struct sp_channel *channel = session->now_streaming_channel; enum sp_msg_type type = session->msg_type_last; + const char *ap_address = ap_address_get(&session->conn); int ret; sp_cb.logmsg("Retrying after disconnect (occurred at msg %d)\n", type); channel_retry(channel); + free(session->ap_avoid); + session->ap_avoid = strdup(ap_address); + ap_disconnect(&session->conn); // If we were in the middle of a handshake when disconnected we must restart @@ -443,7 +449,7 @@ request_make(enum sp_msg_type type, struct sp_session *session) // sp_cb.logmsg("Making request %d\n", type); // Make sure the connection is in a state suitable for sending this message - ret = ap_connect(&session->conn, type, &session->cooldown_ts, &cb, session); + ret = ap_connect(&session->conn, type, &session->cooldown_ts, session->ap_avoid, &cb, session); if (ret == SP_OK_WAIT) return relogin(type, session); // Can't proceed right now, the handshake needs to complete first else if (ret < 0) diff --git a/src/inputs/librespot-c/tests/test1.c b/src/inputs/librespot-c/tests/test1.c index 6d64514327..4bd24bd79c 100644 --- a/src/inputs/librespot-c/tests/test1.c +++ b/src/inputs/librespot-c/tests/test1.c @@ -180,6 +180,8 @@ tcp_connect(const char *address, unsigned short port) return -1; } + printf("Connected to %s (port %u)\n", address, port); + return fd; }