/// <summary> /// Parses 'rangeStr' for HTTP range sets, and adds the sets into /// 'rangeSets'... should an overlapping range set be provided /// or if an otherwise invalid range is requested, then we clear /// the 'rangeSets'... behavior is taken from http://www.freesoft.org/CIE/RFC/2068/178.htm. /// /// <para> /// If the server ignores a byte-range-spec because it is invalid, /// the server should treat the request as if the invalid Range header /// field did not exist. /// (Normally, this means return a 200 response containing the full entity). /// The reason is that the only time a client will make such an invalid /// request is when the entity is smaller than the entity retrieved by a prior request. /// [source: http://www.freesoft.org/CIE/RFC/2068/178.htm] /// </para> /// </summary> /// <param name="rangeSets">this ArrayList has range sets added to it</param> /// <param name="rangeStr"> /// This is the HTTP header with the desired ranges. /// Text is assumed to be all lower case and trimmed. /// </param> /// <param name="contentLength"> /// The entire length of the content, from byte 0. /// </param> public static void AddRange(this List <HTTPSession.Range> rangeSets, string rangeStr, long contentLength) { if (String.IsNullOrEmpty(rangeStr)) { return; } bool errorEncountered = true; errorEncountered = false; DText dt = new DText(); dt.ATTRMARK = "="; dt.MULTMARK = ","; dt.SUBVMARK = "-"; dt[0] = rangeStr; int numSets = dt.DCOUNT(2); for (int i = 1; i <= numSets; i++) { string sOffset = dt[2, i, 1].Trim(); string sEnd = dt[2, i, 2].Trim(); long offset = -1, length = -1, end = -1; if ((sOffset == "") && (sEnd == "")) { // royally screwed up request errorEncountered = true; break; } else if ((sOffset == "") && (sEnd != "")) { // retrieve the last set of bytes identified by sEnd try { offset = 0; end = long.Parse(sEnd); length = end + 1; } catch { errorEncountered = true; break; } } else if ((sOffset != "") && (sEnd == "")) { // retrieve all bytes starting from sOffset try { offset = long.Parse(sOffset); end = contentLength - 1; length = contentLength - offset; } catch { errorEncountered = true; break; } } else { // retrieve bytes from sOffset through sEnd, // inclusive so be sure to add 1 to difference try { offset = long.Parse(sOffset); end = long.Parse(sEnd); if (offset <= end) { length = end - offset + 1; } else { errorEncountered = true; } } catch { errorEncountered = true; break; } } if (errorEncountered == false) { Debug.Assert(offset >= 0); Debug.Assert(length >= 0); Debug.Assert(end >= 0); HTTPSession.Range newRange = new HTTPSession.Range(offset, length); rangeSets.Add(newRange); } } if (errorEncountered) { // error parsing value, this is invalid so clear and return rangeSets.Clear(); } }
private void SendStreamObject(HTTPSession webSession, HTTPMessage msg, string fileName) { Stream stream = null; try { if (!fileName.Contains("/")) { fileName = HttpUtility.UrlDecode(fileName); } string str = (fileName[0] == '1' ? fileName.Substring(2, fileName.LastIndexOf('/') - 2) : fileName.Substring(2)); int idx = str.Length; string freeboxV5 = GetString(str, ref idx); string file = str.Substring(0, idx); string mimeType = null; long contentLength = -1; bool liveStream = MimeType.IsLiveStream(file); string rangeStr = (msg == null ? null : msg.GetTag("RANGE")); List <HTTPSession.Range> rangeSets = new List <HTTPSession.Range>(); if (!String.IsNullOrEmpty(rangeStr)) { rangeStr = rangeStr.Trim().ToLower(); } if (liveStream) { Web.CreateWebRequest(rangeSets, rangeStr, file, ref stream, ref contentLength, ref mimeType); if (!String.IsNullOrEmpty(mimeType) && mimeType.ToUpperInvariant() == "AUDIO/X-SCPLS") { file = new PLSPlaylist().GetMediaUrl(Web.GetStringAndCloseStream(stream, Encoding.UTF8)); Web.CreateWebRequest(rangeSets, rangeStr, file, ref stream, ref contentLength, ref mimeType); } } else { stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read); mimeType = MimeType.GetMimeTypeFromFileName(file); } if (stream == null) { SendErrorMessage(webSession, null); return; } bool canSeek = stream.CanSeek; if (canSeek) { contentLength = stream.Length; } PreventSystemStandBy(); if (msg != null && String.Compare(msg.Directive, "HEAD", true) == 0) { HTTPMessage head = new HTTPMessage(); head.StatusCode = 200; head.StatusData = "OK"; head.ContentType = mimeType; if (contentLength >= 0) { head.OverrideContentLength = true; if (String.IsNullOrEmpty(rangeStr)) { head.AddTag("Content-Length", contentLength.ToString()); head.AddTag("Accept-Ranges", "bytes"); } else { head.StatusCode = 206; rangeSets.AddRange(rangeStr.Trim().ToLower(), contentLength); if (rangeSets.Count == 1) { HTTPSession.Range range = rangeSets[0]; head.AddTag("Content-Range", "bytes " + range.Position.ToString() + "-" + ((long)(range.Position + range.Length - 1)).ToString() + "/" + contentLength.ToString()); head.AddTag("Content-Length", range.Length.ToString()); } } } else { // Can't calculate length => can't do range head.AddTag("Accept-Ranges", "none"); } webSession.Send(head); } else { if (canSeek && contentLength >= 0 && rangeSets.Count == 0) { rangeSets.AddRange(rangeStr, contentLength); } lock (webSession) { webSession.OnStreamDone += new HTTPSession.StreamDoneHandler(WebSession_OnStreamDone); if (freeboxV5 == "1" && mimeType == "audio/mpeg") { webSession.StateObject = file; } bool image = (MimeType.GetMediaKind(mimeType) == MediaKind.Image); if (freeboxV5 == "1") { if (!image) { webSession.BUFFER_SIZE = Settings.Default.V5BufferSize; } } else { webSession.BUFFER_SIZE = Settings.Default.V6BufferSize; } if (rangeSets.Count > 0) { webSession.SendStreamObject(stream, rangeSets.ToArray(), mimeType); } else { if (canSeek) { stream.Seek(0, SeekOrigin.Begin); } if (contentLength >= 0) { webSession.SendStreamObject(stream, contentLength, mimeType, image); } else { webSession.SendStreamObject(stream, mimeType); } } } } } catch (Exception ex) { Utils.WriteException(ex); SendErrorMessage(webSession, stream); } }