From 7af2dc9b109d60ab5280152b2d0966afe4c61747 Mon Sep 17 00:00:00 2001
From: Frank Terbeck <ft@bewatermyfriend.org>
Date: Mon, 25 Feb 2008 17:43:58 +0100
Subject: [PATCH] Improving streaming code situation

Implementing redirection codes (http 3xx).
  3xx codes do not mean, that the uri we tried is invalid. They
  indicate, that the location of a servive has currently moved to
  another place (temporarily or permanently).

Trying all URIs from remote playlist (until one works).
  Getting only the 1st URI may break load balancing for the server(s)
  we're connecting to. It would also rip us of the ability to have
  multiple servers to try, in case one server is full at the time.
---

  There is still room for improvement.

  We don't do anything with the metainformation, streaming servers
  send in their http resonse (which could and IMO should be used to
  replace the, at times, cryptic URI when displaying the item).

  Also, we currently seem to ignore metainformation from .ogg streams
  completely. For other formats I get log entries like:

  metadata_changed: metadata changed: StreamTitle='Fueled By Fire - Spread The Fire!!!';StreamUrl='http://www.snakenet.com';

  ...which updates the information used by cmus in its display. This
  doesn't seem to get triggered for .ogg streams. No idea why that is.

  Oh, and yes, the .ogg streams I tried do send the meta information.
  At least mplayer reads it.

  Please review. I think this makes streams in cmus a lot more usable.
  I hope this doesn't introduce too many bugs. :-)

  This patch increases the verbosity of the code that handles remote
  locations a bit.

 input.c |  107 ++++++++++++++++++++++++++++++++++++++++++++------------------
 1 files changed, 76 insertions(+), 31 deletions(-)

diff --git a/input.c b/input.c
index 27353bf..5f9464f 100644
--- a/input.c
+++ b/input.c
@@ -145,7 +145,7 @@ static int do_http_get(const char *uri, struct http_header **headersp, int *code
 	struct http_uri u;
 	struct http_header *h;
 	int sock, i, rc, code;
-	char *reason;
+	char *reason, *redirloc;
 
 	*headersp = NULL;
 	*codep = -1;
@@ -154,7 +154,7 @@ static int do_http_get(const char *uri, struct http_header **headersp, int *code
 	if (http_parse_uri(uri, &u))
 		return -IP_ERROR_INVALID_URI;
 
-/* 	d_print("%s -> '%s':'%s'@'%s':%d'%s'\n", uri, user, pass, host, port, path); */
+ 	d_print("%s -> '%s':'%s'@'%s':%d'%s'\n", uri, u.user, u.pass, u.host, u.port, u.path);
 
 	sock = http_open(u.host, u.port, http_connection_timeout);
 	if (sock == -1) {
@@ -206,8 +206,41 @@ static int do_http_get(const char *uri, struct http_header **headersp, int *code
 		close(sock);
 		return -IP_ERROR_HTTP_RESPONSE;
 	}
+
+	/*
+	 * FIXME: Use information from the headers, we read.
+	 *
+	 * especially interesting:
+	 *  + icy-name
+	 *  + icy-url
+	 */
 	d_print("HTTP response: %d %s\n", code, reason);
-	if (code != 200) {
+	for (i = 0; (*headersp)[i].key != NULL; i++)
+		d_print("%s: %s\n", (*headersp)[i].key, (*headersp)[i].val);
+
+	switch (code) {
+	case 200: /* OK */
+		break;
+
+	/*
+	 * 3xx Codes (Redirections)
+	 *     unhandled: 300 Multiple Choices
+	 */
+	case 301: /* Moved Permanently */
+	case 302: /* Found */
+	case 303: /* See Other */
+	case 307: /* Temporary Redirect */
+		redirloc = xstrdup(http_headers_get_value(*headersp, "location"));
+		http_headers_free(*headersp);
+
+		close(sock);
+		d_print("Redirected to %s\n", redirloc);
+		sock = do_http_get(redirloc, headersp, codep, reasonp);
+		free(redirloc);
+
+		break;
+
+	default:
 		*codep = code;
 		*reasonp = reason;
 		close(sock);
@@ -257,50 +290,62 @@ static int setup_remote(struct input_plugin *ip, const struct http_header *heade
 	return 0;
 }
 
+struct read_playlist_data {
+	struct input_plugin *ip;
+	int rc;
+};
+
 static int handle_line(void *data, const char *line)
 {
-	char **firstp = data;
+	struct read_playlist_data *rpd;
+	struct http_header *headers;
+	int sock, code;
+	char *reason;
+	const char *uri = line;
+
+	rpd = (struct read_playlist_data *)data;
+
+	if (uri == NULL) {
+		d_print("empty playlist\n");
+		rpd->rc = -IP_ERROR_HTTP_RESPONSE;
+		return 1;
+	}
 
-	*firstp = xstrdup(line);
-	/* ignore other lines */
+	sock = do_http_get(uri, &headers, &code, &reason);
+	if (sock < 0) {
+		/*
+		 * URI in the _playlist_ is invalid, not our fault
+		 * Try next.
+		 */
+		rpd->ip->http_code = code;
+		rpd->ip->http_reason = reason;
+		if (sock == -IP_ERROR_INVALID_URI)
+			sock = -IP_ERROR_HTTP_RESPONSE;
+		rpd->rc = sock;
+		return 0;
+	}
+
+	rpd->rc = setup_remote(rpd->ip, headers, sock);
+	http_headers_free(headers);
 	return 1;
 }
 
 static int read_playlist(struct input_plugin *ip, int sock)
 {
-	struct http_header *headers;
-	char *body, *reason, *first;
-	int rc, code;
+	struct read_playlist_data rpd;
+	char *body;
+	int rc;
 
 	rc = http_read_body(sock, &body, http_read_timeout);
 	close(sock);
 	if (rc)
 		return -IP_ERROR_ERRNO;
 
-	/* get only first URL from the playlist */
-	first = NULL;
-	cmus_playlist_for_each(body, strlen(body), 0, handle_line, &first);
+	rpd.ip = ip;
+	cmus_playlist_for_each(body, strlen(body), 0, handle_line, &rpd);
 	free(body);
 
-	if (first == NULL) {
-		d_print("empty playlist\n");
-		return -IP_ERROR_HTTP_RESPONSE;
-	}
-
-	sock = do_http_get(first, &headers, &code, &reason);
-	free(first);
-	if (sock < 0) {
-		ip->http_code = code;
-		ip->http_reason = reason;
-		/* URI in the _playlist_ is invalid, not our fault */
-		if (sock == -IP_ERROR_INVALID_URI)
-			sock = -IP_ERROR_HTTP_RESPONSE;
-		return sock;
-	}
-
-	rc = setup_remote(ip, headers, sock);
-	http_headers_free(headers);
-	return rc;
+	return rpd.rc;
 }
 
 static int open_remote(struct input_plugin *ip)
-- 
1.5.4.3.192.g923d4

