/// <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 async Task Execute(HttpServer Server, HttpRequest Request, HttpResponse Response) { HttpRequestHeader Header = Request.Header; string Method = Request.Header.Method; if (this.UserSessions && Request.Session is null) { HttpFieldCookie Cookie; string HttpSessionID; if ((Cookie = Request.Header.Cookie) is null || string.IsNullOrEmpty(HttpSessionID = Cookie["HttpSessionID"])) { HttpSessionID = Convert.ToBase64String(Hashes.ComputeSHA512Hash(Guid.NewGuid().ToByteArray())); Response.SetCookie(new Cookie("HttpSessionID", HttpSessionID, null, "/", null, false, true)); } Request.Session = Server.GetSession(HttpSessionID); } switch (Method) { case "GET": case "HEAD": if (!(this.getRanges is 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"; await this.getRanges.GET(Request, Response, FirstInterval); } } else { Response.OnlyHeader = Method == "HEAD"; if (!(this.get is null)) { await this.get.GET(Request, Response); }
/// <summary> /// Represents a range interval in a ranged HTTP request or response. /// </summary> /// <param name="First">First byte of interval, if provided. If not provided, the interval represents the last <paramref name="Last"/> /// number of bytes of the resource.</param> /// <param name="Last">Last byte of interval, inclusive, if provided. If not provided, the interval ends at the end of the resource.</param> public ByteRangeInterval(long?First, long?Last) { this.first = First; this.last = Last; this.next = 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 async Task GET(HttpRequest Request, HttpResponse Response, ByteRangeInterval FirstInterval) { string FullPath = this.GetFullPath(Request); if (File.Exists(FullPath)) { 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; await 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; if (Response.ResponseSent) { return; } ReadProgress Progress = new ReadProgress() { Response = Response, Request = Request, f = f ?? File.Open(FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) }; 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 is 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) { await Response.SendResponse(); await Progress.Dispose(); } else { if (FirstInterval.Next != null) { await Response.WriteLine(); await Response.WriteLine("--" + Progress.Boundary); await Response.WriteLine("Content-Type: " + Progress.ContentType); await Response.WriteLine("Content-Range: " + ContentByteRangeInterval.ContentRangeToString(First, First + Progress.BytesLeft - 1, Progress.TotalLength)); await Response.WriteLine(); } Task _ = Progress.BeginRead(); } } else { await this.RaiseFileNotFound(FullPath, Request, Response); } }
public async Task BeginRead() { int NrRead; try { do { while (this.BytesLeft > 0) { NrRead = await this.f.ReadAsync(this.Buffer, 0, (int)Math.Min(this.BlockSize, this.BytesLeft)); if (NrRead <= 0) { await this.Dispose(); return; } else { await this.Response.Write(this.Buffer, 0, NrRead); this.BytesLeft -= NrRead; } } if (this.Next != null) { long First; if (this.Next.First.HasValue) { First = this.Next.First.Value; } else { First = this.TotalLength - this.Next.Last.Value; } this.f.Position = First; this.BytesLeft = this.Next.GetIntervalLength(this.TotalLength); await Response.WriteLine(); await Response.WriteLine("--" + this.Boundary); await Response.WriteLine("Content-Type: " + this.ContentType); await Response.WriteLine("Content-Range: " + ContentByteRangeInterval.ContentRangeToString(First, First + this.BytesLeft - 1, this.TotalLength)); await Response.WriteLine(); this.Next = this.Next.Next; } }while (this.BytesLeft > 0); if (!string.IsNullOrEmpty(this.Boundary)) { await Response.WriteLine(); await Response.WriteLine("--" + this.Boundary + "--"); } Variables Session; if (!(this.Request is null) && this.Request.Header.Method == "GET" && !((Session = this.Request.Session) is null) && Session.ContainsVariable(" LastPost ")) { Session.Remove(" LastPost "); Session.Remove(" LastPostResource "); Session.Remove(" LastPostReferer "); } await this.Dispose(); } catch (Exception ex) { try { if (!this.Response.HeaderSent) { await this.Response.SendResponse(ex); } else { await this.Response.Flush(); } this.Response.Dispose(); this.Response = null; await this.Dispose(); } catch (Exception) { // Ignore } } }
/// <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(); } }
public async Task BeginRead() { int NrRead; try { do { while (this.BytesLeft > 0) { NrRead = await this.f.ReadAsync(this.Buffer, 0, (int)Math.Min(this.BlockSize, this.BytesLeft)); if (NrRead <= 0) { this.Dispose(); return; } else { this.Response.Write(this.Buffer, 0, NrRead); this.BytesLeft -= NrRead; } } if (this.Next != null) { long First; if (this.Next.First.HasValue) { First = this.Next.First.Value; } else { First = this.TotalLength - this.Next.Last.Value; } this.f.Position = First; this.BytesLeft = this.Next.GetIntervalLength(this.TotalLength); Response.WriteLine(); Response.WriteLine("--" + this.Boundary); Response.WriteLine("Content-Type: " + this.ContentType); Response.WriteLine("Content-Range: " + ContentByteRangeInterval.ContentRangeToString(First, First + this.BytesLeft - 1, this.TotalLength)); Response.WriteLine(); this.Next = this.Next.Next; } }while (this.BytesLeft > 0); if (!string.IsNullOrEmpty(this.Boundary)) { Response.WriteLine(); Response.WriteLine("--" + this.Boundary + "--"); } this.Dispose(); } catch (Exception ex) { try { if (!this.Response.HeaderSent) { this.Response.SendResponse(ex); } else { this.Response.Flush(); } this.Response.Dispose(); this.Response = null; this.Dispose(); } catch (Exception) { // Ignore } } }