/// <summary> /// Parses the Shoutcast specific headers. This method is different because of how Shoutcast responds to an HttpWebRequest. The headers need to be parsed, then removed from the initialBuffer. /// </summary> /// <param name="initialBuffer">Initial data buffer from the audio stream.</param> /// <returns>ShoutcastStreamInformation containing information about the audio stream.</returns> private ShoutcastStreamInformation ParseShoutcastHeaders(ref byte[] initialBuffer) { ShoutcastStreamInformation result = null; int byteCount = 0; Dictionary<string, string> responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); // We may have a REAL ICY stream MemoryStream stream = null; try { stream = new MemoryStream(initialBuffer, false); using (StreamReader reader = new StreamReader(stream, this.metadataEncoding, true)) { // This is to resolve CA2202. stream = null; // Read until we get a blank line. This is SUCH a bad and unsafe way to parse "http" headers. List<string> headerLines = new List<string>(); string line; string responseHeader; HttpStatusCode status = HttpStatusCode.NotFound; string statusDescription = string.Empty; // Get the ICY header responseHeader = reader.ReadLine(); string[] headerParts = responseHeader.Split(' '); if (headerParts.Length >= 2) { string s = headerParts[1]; status = (HttpStatusCode)int.Parse(s); if (headerParts.Length >= 3) { string str3 = headerParts[2]; for (int i = 3; i < headerParts.Length; i++) { str3 = str3 + " " + headerParts[i]; } statusDescription = str3; } } if (status != HttpStatusCode.OK) { // Bail! return result; } byteCount = responseHeader.Length + 2; while (!string.IsNullOrEmpty((line = reader.ReadLine()))) { headerLines.Add(line); } // We should be pointing right at the data now! :) // Parse the headers foreach (string headerLine in headerLines) { byteCount += this.metadataEncoding.GetByteCount(headerLine) + 2; int colonIndex = headerLine.IndexOf(':'); string key = headerLine.Substring(0, colonIndex); string value = headerLine.Substring(colonIndex + 1).Trim(); // We are going to not duplicate headers for now, as this requires order parsing, comma appending, etc. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 if (!responseHeaders.ContainsKey(key)) { responseHeaders.Add(key, value); } } // Add the last CRLF byteCount += 2; } } finally { if (stream != null) { stream.Dispose(); } } // Resize the initialBuffer to reflect the headers we've read. int newBufferLength = initialBuffer.Length - byteCount; byte[] tempBuffer = initialBuffer; initialBuffer = new byte[newBufferLength]; Array.Copy(tempBuffer, byteCount, initialBuffer, 0, newBufferLength); result = new ShoutcastStreamInformation(responseHeaders); if (result.MetadataInterval == -1) { // TODO - Fix this!!! return null; } return result; }
/// <summary> /// Parses the headers from the audio stream. /// </summary> /// <param name="httpWebResponse">HttpWebResponse from the server sending the audio stream.</param> /// <param name="initialBuffer">Initial data buffer from the audio stream.</param> /// <returns>ShoutcastStreamInformation containing information about the audio stream.</returns> private ShoutcastStreamInformation FindStreamInformation(HttpWebResponse httpWebResponse, ref byte[] initialBuffer) { if (httpWebResponse == null) { throw new ArgumentNullException("httpWebResponse"); } if (initialBuffer == null) { throw new ArgumentNullException("initialBuffer"); } ShoutcastStreamInformation result = null; // See if we are a Shoutcast stream. if (string.IsNullOrEmpty(httpWebResponse.Headers[HttpRequestHeader.ContentType])) { // We may have a REAL ICY stream result = this.ParseShoutcastHeaders(ref initialBuffer); } else { // We are a non-Shoutcast server stream, so we can assign the information here. result = new ShoutcastStreamInformation(httpWebResponse.Headers.ToDictionary()); } return result; }