private bool BinaryHeaderReceived(byte[] Data, int Offset, int NrRead) { string Header = null; int i, c; byte b; c = Offset + NrRead; for (i = Offset; i < c; i++) { b = Data[i]; if (this.b1 == CR && this.b2 == LF && this.b3 == CR && b == LF) // RFC 2616, §2.2 { if (this.headerStream is null) { Header = InternetContent.ISO_8859_1.GetString(Data, Offset, i - Offset - 3); } else { this.headerStream.Write(Data, Offset, i - Offset - 3); Header = InternetContent.ISO_8859_1.GetString(this.headerStream.ToArray(), 0, (int)this.headerStream.Position); this.headerStream = null; } } else if (this.b3 == LF && b == LF) // RFC 2616, §19.3 { if (this.headerStream is null) { Header = InternetContent.ISO_8859_1.GetString(Data, Offset, i - Offset - 1); } else { this.headerStream.Write(Data, Offset, i - Offset - 1); Header = InternetContent.ISO_8859_1.GetString(this.headerStream.ToArray(), 0, (int)this.headerStream.Position); this.headerStream = null; } } else { this.b1 = this.b2; this.b2 = this.b3; this.b3 = b; continue; } this.ReceiveText(Header); this.header = new HttpRequestHeader(Header, this.encrypted ? "https" : "http"); this.lastResource = this.header.Resource; if (this.header.HttpVersion < 1) { this.SendResponse(null, 505, "HTTP Version Not Supported", true); return(false); } else if (this.header.ContentLength != null && (this.header.ContentLength.ContentLength > MaxEntitySize)) { this.SendResponse(null, 413, "Request Entity Too Large", true); return(false); } else if (i + 1 < NrRead) { return(this.BinaryDataReceived(Data, i + 1, NrRead - i - 1)); } else if (!this.header.HasMessageBody) { return(this.RequestReceived()); } else { return(true); } } if (this.headerStream is null) { this.headerStream = new MemoryStream(); } this.headerStream.Write(Data, Offset, NrRead); if (this.headerStream.Position < MaxHeaderSize) { return(true); } else { if (this.HasSniffers) { int d = (int)this.headerStream.Position; byte[] Data2 = new byte[d]; this.headerStream.Position = 0; this.headerStream.Read(Data2, 0, d); this.ReceiveBinary(Data2); } this.SendResponse(null, 431, "Request Header Fields Too Large", true); return(false); } }
/// <summary> /// Executes a method on the resource. The default behaviour is to call the corresponding execution methods defined in the specialized /// interfaces <see cref="IHttpGetMethod"/>, <see cref="IHttpPostMethod"/>, <see cref="IHttpPutMethod"/> and <see cref="IHttpDeleteMethod"/> /// if they are defined for the resource. /// </summary> /// <param name="Server">HTTP Server</param> /// <param name="Request">HTTP Request</param> /// <param name="Response">HTTP Response</param> /// <exception cref="HttpException">If an error occurred when processing the method.</exception> public virtual void Execute(HttpServer Server, HttpRequest Request, HttpResponse Response) { HttpRequestHeader Header = Request.Header; string Method = Request.Header.Method; if (this.UserSessions) { HttpFieldCookie Cookie; string HttpSessionID; if ((Cookie = Request.Header.Cookie) is null || string.IsNullOrEmpty(HttpSessionID = Cookie["HttpSessionID"])) { HttpSessionID = System.Convert.ToBase64String(Hashes.ComputeSHA512Hash(Guid.NewGuid().ToByteArray())); Response.SetCookie(new HTTP.Cookie("HttpSessionID", HttpSessionID, null, "/", null, false, true)); } Request.Session = Server.GetSession(HttpSessionID); } switch (Method) { case "GET": case "HEAD": if (this.getRanges != null) { Response.SetHeader("Accept-Ranges", "bytes"); if (Header.Range != null) { ByteRangeInterval FirstInterval = Header.Range.FirstInterval; if (FirstInterval is null) { throw new RangeNotSatisfiableException(); } else { Response.OnlyHeader = Method == "HEAD"; Response.StatusCode = 206; Response.StatusMessage = "Partial Content"; this.getRanges.GET(Request, Response, FirstInterval); } } else { Response.OnlyHeader = Method == "HEAD"; if (this.get != null) { this.get.GET(Request, Response); } else { this.getRanges.GET(Request, Response, new ByteRangeInterval(0, null)); } } } else if (this.get != null) { Response.OnlyHeader = Method == "HEAD"; this.get.GET(Request, Response); } else { throw new MethodNotAllowedException(this.allowedMethods); } break; case "POST": if (this.postRanges != null) { if (Header.ContentRange != null) { ContentByteRangeInterval Interval = Header.ContentRange.Interval; if (Interval is null) { throw new RangeNotSatisfiableException(); } else { this.postRanges.POST(Request, Response, Interval); } } else { if (this.post != null) { this.post.POST(Request, Response); } else { long Total; if (Header.ContentLength != null) { Total = Header.ContentLength.ContentLength; } else if (Request.DataStream != null) { Total = Request.DataStream.Position; } else { Total = 0; } this.postRanges.POST(Request, Response, new ContentByteRangeInterval(0, Total - 1, Total)); } } } else if (this.post != null) { this.post.POST(Request, Response); } else { throw new MethodNotAllowedException(this.allowedMethods); } break; case "PUT": if (this.putRanges != null) { if (Header.ContentRange != null) { ContentByteRangeInterval Interval = Header.ContentRange.Interval; if (Interval is null) { throw new RangeNotSatisfiableException(); } else { this.putRanges.PUT(Request, Response, Interval); } } else { if (this.put != null) { this.put.PUT(Request, Response); } else { long Total; if (Header.ContentLength != null) { Total = Header.ContentLength.ContentLength; } else if (Request.DataStream != null) { Total = Request.DataStream.Position; } else { Total = 0; } this.putRanges.PUT(Request, Response, new ContentByteRangeInterval(0, Total - 1, Total)); } } } else if (this.put != null) { this.put.PUT(Request, Response); } else { throw new MethodNotAllowedException(this.allowedMethods); } break; case "DELETE": if (this.delete is null) { throw new MethodNotAllowedException(this.allowedMethods); } else { this.delete.DELETE(Request, Response); } break; case "OPTIONS": if (this.options is null) { throw new MethodNotAllowedException(this.allowedMethods); } else { this.options.OPTIONS(Request, Response); } break; case "TRACE": if (this.trace is null) { throw new MethodNotAllowedException(this.allowedMethods); } else { this.trace.TRACE(Request, Response); } break; default: throw new MethodNotAllowedException(this.allowedMethods); } if (this.Synchronous) { Response.SendResponse(); Response.Dispose(); } }
private Stream CheckAcceptable(HttpRequest Request, HttpResponse Response, ref string ContentType, out bool Dynamic, string FullPath, string ResourceName) { HttpRequestHeader Header = Request.Header; Dynamic = false; if (Header.Accept != null) { bool Acceptable = Header.Accept.IsAcceptable(ContentType, out double Quality, out ContentTypeAcceptance TypeAcceptance, null); if ((!Acceptable || TypeAcceptance == ContentTypeAcceptance.Wildcard) && (this.allowTypeConversionFrom == null || (this.allowTypeConversionFrom.TryGetValue(ContentType, out bool Allowed) && Allowed))) { IContentConverter Converter = null; string NewContentType = null; foreach (AcceptRecord AcceptRecord in Header.Accept.Records) { NewContentType = AcceptRecord.Item; if (NewContentType.EndsWith("/*")) { continue; } if (InternetContent.CanConvert(ContentType, NewContentType, out Converter)) { Acceptable = true; break; } } if (Acceptable && Converter != null) { Stream f2 = null; Stream f = File.OpenRead(FullPath); bool Ok = false; try { f2 = f.Length < HttpClientConnection.MaxInmemoryMessageSize ? (Stream) new MemoryStream() : new TemporaryFile(); if (Request.Session != null) { Request.Session["Request"] = Request; Request.Session["Response"] = Response; } if (Converter.Convert(ContentType, f, FullPath, ResourceName, Request.Header.GetURL(false, false), NewContentType, f2, Request.Session)) { Dynamic = true; } ContentType = NewContentType; Ok = true; } finally { if (f2 == null) { f.Dispose(); } else if (!Ok) { f2.Dispose(); f.Dispose(); } else { f.Dispose(); f = f2; f2 = null; f.Position = 0; } } return(f); } } if (!Acceptable) { throw new NotAcceptableException(); } } return(null); }
/// <summary> /// Executes the ranged GET method on the resource. /// </summary> /// <param name="Request">HTTP Request</param> /// <param name="Response">HTTP Response</param> /// <param name="FirstInterval">First byte range interval.</param> /// <exception cref="HttpException">If an error occurred when processing the method.</exception> public void GET(HttpRequest Request, HttpResponse Response, ByteRangeInterval FirstInterval) { string FullPath = this.GetFullPath(Request); if (!File.Exists(FullPath)) { throw new NotFoundException(); } HttpRequestHeader Header = Request.Header; DateTime LastModified = File.GetLastWriteTime(FullPath).ToUniversalTime(); DateTimeOffset? Limit; CacheRec Rec; if (Header.IfRange != null && (Limit = Header.IfRange.Timestamp).HasValue && !LessOrEqual(LastModified, Limit.Value.ToUniversalTime())) { Response.StatusCode = 200; this.GET(Request, Response); // No ranged request. return; } Rec = this.CheckCacheHeaders(FullPath, LastModified, Request); string ContentType = InternetContent.GetContentType(Path.GetExtension(FullPath)); Stream f = CheckAcceptable(Request, Response, ref ContentType, out bool Dynamic, FullPath, Request.Header.Resource); Rec.IsDynamic = Dynamic; ReadProgress Progress = new ReadProgress() { Response = Response, f = f ?? File.OpenRead(FullPath) }; ByteRangeInterval Interval = FirstInterval; Progress.TotalLength = Progress.f.Length; long i = 0; long j; long First; if (FirstInterval.First.HasValue) { First = FirstInterval.First.Value; } else { First = Progress.TotalLength - FirstInterval.Last.Value; } Progress.f.Position = First; Progress.BytesLeft = Interval.GetIntervalLength(Progress.TotalLength); Progress.Next = Interval.Next; while (Interval != null) { j = Interval.GetIntervalLength(Progress.TotalLength); if (j > i) { i = j; } Interval = Interval.Next; } Progress.BlockSize = (int)Math.Min(BufferSize, i); Progress.Buffer = new byte[Progress.BlockSize]; if (FirstInterval.Next == null) { Progress.Boundary = null; Progress.ContentType = null; Response.ContentType = ContentType; Response.ContentLength = FirstInterval.GetIntervalLength(Progress.f.Length); Response.SetHeader("Content-Range", ContentByteRangeInterval.ContentRangeToString(First, First + Progress.BytesLeft - 1, Progress.TotalLength)); } else { Progress.Boundary = Guid.NewGuid().ToString().Replace("-", string.Empty); Progress.ContentType = ContentType; Response.ContentType = "multipart/byteranges; boundary=" + Progress.Boundary; // chunked transfer encoding will be used } if (!Rec.IsDynamic) { Response.SetHeader("ETag", Rec.ETag); Response.SetHeader("Last-Modified", CommonTypes.EncodeRfc822(LastModified)); } if (Response.OnlyHeader || Progress.BytesLeft == 0) { Response.SendResponse(); Progress.Dispose(); } else { if (FirstInterval.Next != null) { Response.WriteLine(); Response.WriteLine("--" + Progress.Boundary); Response.WriteLine("Content-Type: " + Progress.ContentType); Response.WriteLine("Content-Range: " + ContentByteRangeInterval.ContentRangeToString(First, First + Progress.BytesLeft - 1, Progress.TotalLength)); Response.WriteLine(); } Task T = Progress.BeginRead(); } }
private Stream CheckAcceptable(HttpRequest Request, HttpResponse Response, ref string ContentType, out bool Dynamic, string FullPath, string ResourceName) { HttpRequestHeader Header = Request.Header; Dynamic = false; if (Header.Accept != null) { bool Acceptable = Header.Accept.IsAcceptable(ContentType, out double Quality, out AcceptanceLevel TypeAcceptance, null); if ((!Acceptable || TypeAcceptance == AcceptanceLevel.Wildcard) && (this.allowTypeConversionFrom is null || (this.allowTypeConversionFrom.TryGetValue(ContentType, out bool Allowed) && Allowed))) { IContentConverter Converter = null; string NewContentType = null; foreach (AcceptRecord AcceptRecord in Header.Accept.Records) { NewContentType = AcceptRecord.Item; if (NewContentType.EndsWith("/*")) { NewContentType = null; continue; } if (InternetContent.CanConvert(ContentType, NewContentType, out Converter)) { Acceptable = true; break; } } if (Converter is null) { IContentConverter[] Converters = InternetContent.GetConverters(ContentType); if (!(Converters is null)) { string BestContentType = null; double BestQuality = 0; IContentConverter Best = null; bool Found; foreach (IContentConverter Converter2 in InternetContent.Converters) { Found = false; foreach (string FromContentType in Converter2.FromContentTypes) { if (ContentType == FromContentType) { Found = true; break; } } if (!Found) { continue; } foreach (string ToContentType in Converter2.ToContentTypes) { if (Header.Accept.IsAcceptable(ToContentType, out double Quality2) && Quality > BestQuality) { BestContentType = ToContentType; BestQuality = Quality; Best = Converter2; } } } if (Best != null && (!Acceptable || BestQuality >= Quality)) { Acceptable = true; Converter = Best; NewContentType = BestContentType; } } } if (Acceptable && Converter != null) { Stream f2 = null; Stream f = File.Open(FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); bool Ok = false; try { f2 = f.Length < HttpClientConnection.MaxInmemoryMessageSize ? (Stream) new MemoryStream() : new TemporaryFile(); if (Request.Session != null) { Request.Session["Request"] = Request; Request.Session["Response"] = Response; } List <string> Alternatives = null; string[] Range = Converter.ToContentTypes; foreach (AcceptRecord AcceptRecord in Header.Accept.Records) { if (AcceptRecord.Item.EndsWith("/*") || AcceptRecord.Item == NewContentType) { continue; } if (Array.IndexOf <string>(Range, AcceptRecord.Item) >= 0) { if (Alternatives is null) { Alternatives = new List <string>(); } Alternatives.Add(AcceptRecord.Item); } } if (Converter.Convert(ContentType, f, FullPath, ResourceName, Request.Header.GetURL(false, false), ref NewContentType, f2, Request.Session, Alternatives?.ToArray())) { Dynamic = true; } ContentType = NewContentType; Ok = true; } finally { if (f2 is null) { f.Dispose(); } else if (!Ok) { f2.Dispose(); f.Dispose(); } else { f.Dispose(); f = f2; f.Position = 0; } } return(f); } } if (!Acceptable) { throw new NotAcceptableException(); } } return(null); }