private bool UpdateFileCache( HttpListenerContext context, Stream buffer, DateTime fileDate, string requestHash, string localPath) { var currentHash = buffer.ComputeMD5().ToUpperHex() + '-' + fileDate.Ticks; if (!string.IsNullOrWhiteSpace(requestHash) && requestHash == currentHash) { return(true); } if (UseRamCache && buffer.Length <= MaxRamCacheFileSize) { using (var memoryStream = new MemoryStream()) { buffer.Position = 0; buffer.CopyTo(memoryStream); RamCache[localPath] = new RamCacheEntry() { LastModified = fileDate, Buffer = memoryStream.ToArray() }; } } context.Response.AddHeader(Headers.ETag, currentHash); return(false); }
private async Task <bool> SendFileList(HttpListenerContext context) { StringBuilder stb = new StringBuilder(); stb.AppendLine("<!DOCTYPE html><html><head>" + "<style>"); stb.AppendLine("table{ border-collapse: collapse; width: 100 %; }"); stb.AppendLine("th, td{text-align: left;padding: 8px;}"); stb.AppendLine("tr:nth-child(even){background-color: #f2f2f2}"); stb.AppendLine("th{background-color: #808080;color: white}"); stb.AppendLine("</style >" + "</head ><body>"); stb.AppendLine(@"<table>"); stb.AppendLine(@"<tr><th>File</th><th>Size</th><th>Last Modified</th></tr>"); foreach (var file in RamCache.OrderBy((key) => ((RamCacheEntry)key.Value).LastModified)) { RamCacheEntry entry = (RamCacheEntry)file.Value; stb.AppendLine(Invariant($"<tr><td><a href=\"{file.Key}\">{file.Key}</a></td>")); stb.AppendLine(Invariant($"<td>{entry.Buffer.Length} bytes</td></td><td>{entry.LastModified}</td></tr>")); } stb.AppendLine(@"</table>"); stb.AppendLine(@"</body></html>"); context.Response.ContentType = "text/html"; context.Response.StatusCode = (int)HttpStatusCode.OK; byte[] data = Encoding.UTF8.GetBytes(stb.ToString()); context.Response.ContentLength64 = data.LongLength; await context.Response.OutputStream.WriteAsync(data, 0, data.Length); context.Response.ContentEncoding = Encoding.UTF8; return(true); }
private async Task <bool> SendFileList(IHttpContext context, CancellationToken ct) { StringBuilder stb = new StringBuilder(); stb.AppendLine("<!DOCTYPE html><html><head>" + "<style>"); stb.AppendLine("table{ border-collapse: collapse; width: 100 %; }"); stb.AppendLine("th, td{text-align: left;padding: 8px;}"); stb.AppendLine("tr:nth-child(even){background-color: #f2f2f2}"); stb.AppendLine("th{background-color: #808080;color: white}"); stb.AppendLine("</style >" + "</head ><body>"); stb.AppendLine(@"<table>"); stb.AppendLine(@"<tr><th>File</th><th>Size</th><th>Last Modified</th></tr>"); foreach (var file in RamCache.OrderBy((key) => ((RamCacheEntry)key.Value).LastModified)) { RamCacheEntry entry = (RamCacheEntry)file.Value; stb.AppendLine(Invariant($"<tr><td><a href=\"{WebUtility.HtmlEncode(file.Key)}\">{file.Key}</a></td>")); stb.AppendLine(Invariant($"<td>{entry.Buffer.Length} bytes</td></td><td>{entry.LastModified}</td></tr>")); } stb.AppendLine(@"</table>"); stb.AppendLine(@"</body></html>"); return(await context.HtmlResponseAsync(stb.ToString(), cancellationToken : ct).ConfigureAwait(false)); }
internal void Add(Stream buffer, string localPath, DateTime fileDate) { using (var memoryStream = new MemoryStream()) { buffer.Position = 0; buffer.CopyTo(memoryStream); this[localPath] = new RamCacheEntry { LastModified = fileDate, Buffer = memoryStream.ToArray() }; } }
private async Task <bool> HandleGet(IHttpContext context, CancellationToken ct, bool sendBuffer = true) { Trace.WriteLine($"Request Type {context.RequestVerb()} from {context.Request.RemoteEndPoint} for {context.Request.Url}"); var requestedPath = GetUrlPath(context); if (string.IsNullOrEmpty(requestedPath)) { return(await SendFileList(context, ct).ConfigureAwait(false)); } var eTagValid = false; var partialHeader = context.RequestHeader(Headers.Range); var usingPartial = string.IsNullOrWhiteSpace(partialHeader) == false && partialHeader.StartsWith("bytes=", StringComparison.Ordinal); var requestHash = context.RequestHeader(Headers.IfNotMatch); CacheItem cacheItem = RamCache.GetCacheItem(requestedPath); if (cacheItem == null || cacheItem.Value == null) { context.Response.StatusCode = (int)HttpStatusCode.NotFound; Trace.WriteLine($"Request From {context.Request.RemoteEndPoint} for {context.Request.Url} returned with {context.Response.StatusCode}"); return(true); } RamCacheEntry cacheEntry = (RamCacheEntry)cacheItem.Value; if (string.IsNullOrWhiteSpace(requestHash) || requestHash != cacheEntry.Hash) { context.Response.AddHeader(Headers.ETag, cacheEntry.Hash); } else { eTagValid = true; } // check to see if the file was modified or e-tag is the same var utcFileDateString = cacheEntry.LastModified.ToUniversalTime() .ToString(BrowserTimeFormat, StandardHeaderCultureInfo); if (usingPartial == false && (eTagValid || context.RequestHeader(Headers.IfModifiedSince).Equals(utcFileDateString))) { SetStatusCode304(context); Trace.WriteLine($"Request From {context.Request.RemoteEndPoint} for {context.Request.Url} returned with {context.Response.StatusCode}"); return(true); } SetHeaders(context, requestedPath, utcFileDateString); var fileSize = cacheEntry.Buffer.Length; if (sendBuffer == false) { context.Response.ContentLength64 = fileSize; return(true); } var lowerByteIndex = 0; var upperByteIndex = 0; long byteLength; var isPartial = usingPartial && CalculateRange(partialHeader, fileSize, out lowerByteIndex, out upperByteIndex); if (isPartial) { if (upperByteIndex > (fileSize - 1)) { context.Response.StatusCode = 416; context.Response.AddHeader(Headers.ContentRanges, Invariant($"bytes */{fileSize}")); Trace.WriteLine($"Request From {context.Request.RemoteEndPoint} for {context.Request.Url} returned with {context.Response.StatusCode}"); return(true); } byteLength = upperByteIndex - lowerByteIndex + 1; context.Response.AddHeader(Headers.ContentRanges, Invariant($"bytes {lowerByteIndex}-{upperByteIndex}/{fileSize}")); if (byteLength != fileSize) { context.Response.StatusCode = 206; } } else { byteLength = fileSize; } context.Response.ContentLength64 = byteLength; Trace.WriteLine($"Serving to {context.Request.RemoteEndPoint} for {context.Request.Url} with bytes {byteLength} at offset {lowerByteIndex}"); try { await WriteToOutputMemoryStream(context, byteLength, cacheEntry.Buffer, lowerByteIndex, ct).ConfigureAwait(false); } catch (HttpListenerException ex) { Trace.WriteLine($"Request Type {context.RequestVerb()} from {context.Request.RemoteEndPoint} for {context.Request.Url} bytes {byteLength} at offset {lowerByteIndex} failed with {ex.Message}"); return(true); } Trace.WriteLine($"Finished Serving to {context.Request.RemoteEndPoint} for {context.Request.Url} bytes {byteLength} at offset {lowerByteIndex}"); return(true); }
private bool HandleGet(HttpListenerContext context, WebServer server, bool sendBuffer = true) { var urlPath = context.Request.Url.LocalPath.Replace('/', Path.DirectorySeparatorChar); // adjust the path to see if we've got a default document if (urlPath.Last() == Path.DirectorySeparatorChar) { urlPath = urlPath + DefaultDocument; } urlPath = urlPath.TrimStart(new char[] { Path.DirectorySeparatorChar }); var localPath = Path.Combine(FileSystemPath, urlPath); var eTagValid = false; byte[] buffer = null; var fileDate = DateTime.Today; var partialHeader = context.RequestHeader(Constants.HeaderRange); var usingPartial = String.IsNullOrWhiteSpace(partialHeader) == false && partialHeader.StartsWith("bytes="); if (string.IsNullOrWhiteSpace(DefaultExtension) == false && DefaultExtension.StartsWith(".") && File.Exists(localPath) == false) { var newPath = localPath + DefaultExtension; if (File.Exists(newPath)) { localPath = newPath; } } if (File.Exists(localPath) == false) { return(false); } if (usingPartial == false) { fileDate = File.GetLastWriteTime(localPath); var requestHash = context.RequestHeader(Constants.HeaderIfNotMatch); if (RamCache.ContainsKey(localPath) && RamCache[localPath].LastModified == fileDate) { server.Log.DebugFormat("RAM Cache: {0}", localPath); var currentHash = Extensions.ComputeMd5Hash(RamCache[localPath].Buffer) + '-' + fileDate.Ticks; if (String.IsNullOrWhiteSpace(requestHash) || requestHash != currentHash) { buffer = RamCache[localPath].Buffer; context.Response.AddHeader(Constants.HeaderETag, currentHash); } else { eTagValid = true; } } else { server.Log.DebugFormat("File System: {0}", localPath); if (sendBuffer) { buffer = File.ReadAllBytes(localPath); var currentHash = Extensions.ComputeMd5Hash(buffer) + '-' + fileDate.Ticks; if (String.IsNullOrWhiteSpace(requestHash) || requestHash != currentHash) { if (UseRamCache && buffer.Length <= MaxRamCacheFileSize) { RamCache[localPath] = new RamCacheEntry() { LastModified = fileDate, Buffer = buffer }; } context.Response.AddHeader(Constants.HeaderETag, currentHash); } else { eTagValid = true; } } } } // check to see if the file was modified or etag is the same var utcFileDateString = fileDate.ToUniversalTime() .ToString(Constants.BrowserTimeFormat, CultureInfo.InvariantCulture); if (usingPartial == false && (eTagValid || context.RequestHeader(Constants.HeaderIfModifiedSince).Equals(utcFileDateString))) { context.Response.AddHeader(Constants.HeaderCacheControl, "private"); context.Response.AddHeader(Constants.HeaderPragma, string.Empty); context.Response.AddHeader(Constants.HeaderExpires, string.Empty); context.Response.ContentType = string.Empty; context.Response.StatusCode = 304; } else { var extension = Path.GetExtension(localPath).ToLowerInvariant(); if (MimeTypes.ContainsKey(extension)) { context.Response.ContentType = MimeTypes[extension]; } context.Response.AddHeader(Constants.HeaderCacheControl, "private"); context.Response.AddHeader(Constants.HeaderPragma, string.Empty); context.Response.AddHeader(Constants.HeaderExpires, string.Empty); context.Response.AddHeader(Constants.HeaderLastModified, utcFileDateString); context.Response.AddHeader(Constants.HeaderAcceptRanges, "bytes"); if (sendBuffer) { var lrange = 0; var urange = 0; var size = (long)0; var isPartial = false; var fileSize = new FileInfo(localPath).Length; if (usingPartial) { var range = partialHeader.Replace("bytes=", "").Split('-'); if (range.Length == 2 && int.TryParse(range[0], out lrange) && int.TryParse(range[1], out urange)) { isPartial = true; } if ((range.Length == 2 && int.TryParse(range[0], out lrange) && string.IsNullOrWhiteSpace(range[1])) || (range.Length == 1 && int.TryParse(range[0], out lrange))) { urange = (int)fileSize - 1; isPartial = true; } if (range.Length == 2 && string.IsNullOrWhiteSpace(range[0]) && int.TryParse(range[1], out urange)) { lrange = (int)fileSize - urange; urange = (int)fileSize - 1; isPartial = true; } } if (isPartial) { if (urange > fileSize) { context.Response.StatusCode = 416; context.Response.AddHeader(Constants.HeaderContentRanges, string.Format("bytes */{0}", fileSize)); return(true); } size = (urange - lrange) + 1; context.Response.AddHeader(Constants.HeaderContentRanges, string.Format("bytes {0}-{1}/{2}", lrange, urange, fileSize)); context.Response.StatusCode = 206; server.Log.DebugFormat("Opening stream {0} bytes {1}-{2} size {3}", localPath, lrange, urange, size); buffer = new byte[size]; // Open FileStream with FileShare using (var fs = new FileStream(localPath, FileMode.Open, FileAccess.Read, FileShare.Read)) { if (lrange + size > fs.Length) { size = fs.Length - lrange; } fs.Seek(lrange, SeekOrigin.Begin); fs.Read(buffer, 0, (int)size); fs.Close(); } // Reset lower range lrange = 0; } else { size = buffer.LongLength; // Perform compression if available if (context.RequestHeader(Constants.HeaderAcceptEncoding).Contains("gzip")) { buffer = buffer.Compress(); context.Response.AddHeader(Constants.HeaderContentEncoding, "gzip"); size = buffer.LongLength; lrange = 0; } } context.Response.ContentLength64 = size; try { context.Response.OutputStream.Write(buffer, lrange, (int)size); } catch (HttpListenerException) { // Connection error, nothing else to do } } else { context.Response.ContentLength64 = buffer == null ? new FileInfo(localPath).Length : buffer.LongLength; } } return(true); }
private bool HandleGet(HttpListenerContext context, WebServer server, bool sendBuffer = true) { var urlPath = context.Request.Url.LocalPath.Replace('/', Path.DirectorySeparatorChar); // adjust the path to see if we've got a default document if (urlPath.Last() == Path.DirectorySeparatorChar) urlPath = urlPath + DefaultDocument; urlPath = urlPath.TrimStart(new char[] {Path.DirectorySeparatorChar}); var localPath = Path.Combine(FileSystemPath, urlPath); var eTagValid = false; byte[] buffer = null; var fileDate = DateTime.Today; var partialHeader = context.RequestHeader(Constants.HeaderRange); var usingPartial = String.IsNullOrWhiteSpace(partialHeader) == false && partialHeader.StartsWith("bytes="); if (string.IsNullOrWhiteSpace(DefaultExtension) == false && DefaultExtension.StartsWith(".") && File.Exists(localPath) == false) { var newPath = localPath + DefaultExtension; if (File.Exists(newPath)) localPath = newPath; } if (File.Exists(localPath) == false) return false; if (usingPartial == false) { fileDate = File.GetLastWriteTime(localPath); var requestHash = context.RequestHeader(Constants.HeaderIfNotMatch); if (RamCache.ContainsKey(localPath) && RamCache[localPath].LastModified == fileDate) { server.Log.DebugFormat("RAM Cache: {0}", localPath); var currentHash = Extensions.ComputeMd5Hash(RamCache[localPath].Buffer) + '-' + fileDate.Ticks; if (String.IsNullOrWhiteSpace(requestHash) || requestHash != currentHash) { buffer = RamCache[localPath].Buffer; context.Response.AddHeader(Constants.HeaderETag, currentHash); } else { eTagValid = true; } } else { server.Log.DebugFormat("File System: {0}", localPath); if (sendBuffer) { buffer = File.ReadAllBytes(localPath); var currentHash = Extensions.ComputeMd5Hash(buffer) + '-' + fileDate.Ticks; if (String.IsNullOrWhiteSpace(requestHash) || requestHash != currentHash) { if (UseRamCache && buffer.Length <= MaxRamCacheFileSize) { RamCache[localPath] = new RamCacheEntry() {LastModified = fileDate, Buffer = buffer}; } context.Response.AddHeader(Constants.HeaderETag, currentHash); } else { eTagValid = true; } } } } // check to see if the file was modified or etag is the same var utcFileDateString = fileDate.ToUniversalTime() .ToString(Constants.BrowserTimeFormat, CultureInfo.InvariantCulture); if (usingPartial == false && (eTagValid || context.RequestHeader(Constants.HeaderIfModifiedSince).Equals(utcFileDateString))) { context.Response.AddHeader(Constants.HeaderCacheControl, "private"); context.Response.AddHeader(Constants.HeaderPragma, string.Empty); context.Response.AddHeader(Constants.HeaderExpires, string.Empty); context.Response.ContentType = string.Empty; context.Response.StatusCode = 304; } else { var extension = Path.GetExtension(localPath).ToLowerInvariant(); if (MimeTypes.ContainsKey(extension)) context.Response.ContentType = MimeTypes[extension]; context.Response.AddHeader(Constants.HeaderCacheControl, "private"); context.Response.AddHeader(Constants.HeaderPragma, string.Empty); context.Response.AddHeader(Constants.HeaderExpires, string.Empty); context.Response.AddHeader(Constants.HeaderLastModified, utcFileDateString); context.Response.AddHeader(Constants.HeaderAcceptRanges, "bytes"); if (sendBuffer) { var lrange = 0; var urange = 0; var size = (long) 0; var isPartial = false; var fileSize = new FileInfo(localPath).Length; if (usingPartial) { var range = partialHeader.Replace("bytes=", "").Split('-'); if (range.Length == 2 && int.TryParse(range[0], out lrange) && int.TryParse(range[1], out urange)) { isPartial = true; } if ((range.Length == 2 && int.TryParse(range[0], out lrange) && string.IsNullOrWhiteSpace(range[1])) || (range.Length == 1 && int.TryParse(range[0], out lrange))) { urange = (int) fileSize - 1; isPartial = true; } if (range.Length == 2 && string.IsNullOrWhiteSpace(range[0]) && int.TryParse(range[1], out urange)) { lrange = (int) fileSize - urange; urange = (int)fileSize - 1; isPartial = true; } } if (isPartial) { if (urange > fileSize) { context.Response.StatusCode = 416; context.Response.AddHeader(Constants.HeaderContentRanges, string.Format("bytes */{0}", fileSize)); return true; } size = (urange - lrange) + 1; context.Response.AddHeader(Constants.HeaderContentRanges, string.Format("bytes {0}-{1}/{2}", lrange, urange, fileSize)); context.Response.StatusCode = 206; server.Log.DebugFormat("Opening stream {0} bytes {1}-{2} size {3}", localPath, lrange, urange, size); buffer = new byte[size]; // Open FileStream with FileShare using (var fs = new FileStream(localPath, FileMode.Open, FileAccess.Read, FileShare.Read)) { if (lrange + size > fs.Length) size = fs.Length - lrange; fs.Seek(lrange, SeekOrigin.Begin); fs.Read(buffer, 0, (int) size); fs.Close(); } // Reset lower range lrange = 0; } else { size = buffer.LongLength; // Perform compression if available if (context.RequestHeader(Constants.HeaderAcceptEncoding).Contains("gzip")) { buffer = buffer.Compress(); context.Response.AddHeader(Constants.HeaderContentEncoding, "gzip"); size = buffer.LongLength; lrange = 0; } } context.Response.ContentLength64 = size; try { context.Response.OutputStream.Write(buffer, lrange, (int) size); } catch (HttpListenerException) { // Connection error, nothing else to do } } else { context.Response.ContentLength64 = buffer == null ? new FileInfo(localPath).Length : buffer.LongLength; } } return true; }
private bool UpdateFileCache(HttpListenerContext context, Stream buffer, DateTime fileDate, string requestHash, string localPath) { var currentHash = buffer.ComputeMD5().ToUpperHex() + '-' + fileDate.Ticks; if (!string.IsNullOrWhiteSpace(requestHash) && requestHash == currentHash) { return true; } if (UseRamCache && buffer.Length <= MaxRamCacheFileSize) { using (var memoryStream = new MemoryStream()) { buffer.Position = 0; buffer.CopyTo(memoryStream); RamCache[localPath] = new RamCacheEntry() { LastModified = fileDate, Buffer = memoryStream.ToArray() }; } } context.Response.AddHeader(Constants.HeaderETag, currentHash); return false; }