/// <summary> /// Executes the PUT method on the resource. /// </summary> /// <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 void PUT(HttpRequest Request, HttpResponse Response) { string FullPath = this.GetFullPath(Request); if (!Request.HasData) { throw new BadRequestException(); } string Folder = Path.GetDirectoryName(FullPath); if (!Directory.Exists(Folder)) { Directory.CreateDirectory(Folder); } using (FileStream f = File.Create(FullPath)) { Request.DataStream.CopyTo(f); } Response.StatusCode = 201; Response.SendResponse(); }
private static void SendResponse(Stream f, string FullPath, string ContentType, bool IsDynamic, string ETag, DateTime LastModified, HttpResponse Response, HttpRequest Request) { ReadProgress Progress = new ReadProgress() { Response = Response, Request = Request, f = f ?? File.Open(FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Next = null, Boundary = null, ContentType = null }; Progress.BytesLeft = Progress.TotalLength = Progress.f.Length; Progress.BlockSize = (int)Math.Min(BufferSize, Progress.BytesLeft); Progress.Buffer = new byte[Progress.BlockSize]; Response.ContentType = ContentType; Response.ContentLength = Progress.TotalLength; if (!IsDynamic) { Response.SetHeader("ETag", ETag); Response.SetHeader("Last-Modified", CommonTypes.EncodeRfc822(LastModified)); } if (Response.OnlyHeader || Progress.TotalLength == 0) { Response.SendResponse(); Progress.Dispose(); } else { Task _ = Progress.BeginRead(); } }
/// <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); } }
/// <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(); } }