예제 #1
0
        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);
        }
예제 #2
0
        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);
        }
예제 #3
0
        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));
        }
예제 #4
0
        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()
                };
            }
        }
예제 #5
0
        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);
        }
예제 #6
0
        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);
        }
예제 #7
0
        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;
        }
예제 #8
0
        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;
        }