Example #1
0
        public void Start()
        {
            var Root    = FileNameHandling.GetDirectoryPathWithTailingSeparator(FileNameHandling.GetAbsolutePath(PhysicalPath, Environment.CurrentDirectory));
            var Indices = this.Indices;

            var MimeTypes = new Dictionary <String, String>(StringComparer.OrdinalIgnoreCase);

            MimeTypes.Add("htm", "text/html");
            MimeTypes.Add("html", "text/html");
            MimeTypes.Add("shtml", "text/html");
            MimeTypes.Add("xml", "text/xml");
            MimeTypes.Add("css", "text/css");
            MimeTypes.Add("js", "application/x-javascript");
            MimeTypes.Add("txt", "text/plain");
            MimeTypes.Add("gif", "image/gif");
            MimeTypes.Add("jpg", "image/jpeg");
            MimeTypes.Add("jpeg", "image/jpeg");
            MimeTypes.Add("png", "image/png");
            MimeTypes.Add("tif", "image/tiff");
            MimeTypes.Add("tiff", "image/tiff");
            MimeTypes.Add("ico", "image/x-icon");
            MimeTypes.Add("mp3", "audio/mpeg");
            MimeTypes.Add("wav", "audio/wav");
            MimeTypes.Add("mid", "audio/midi");
            MimeTypes.Add("midi", "audio/midi");

            Func <String, String> GetMimeType = Extension =>
            {
                if (MimeTypes.ContainsKey(Extension))
                {
                    return(MimeTypes[Extension]);
                }
                return("application/octet-stream");
            };

            Func <String, Int64, Optional <KeyValuePair <Int64, Int64> > > TryGetRange = (RangeStrs, FileLength) =>
            {
                // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

                Int64 Start            = 0;
                Int64 Length           = FileLength;
                var   RangeStrsTrimmed = RangeStrs.Trim(' ');
                if (RangeStrsTrimmed.StartsWith("bytes="))
                {
                    RangeStrsTrimmed = RangeStrsTrimmed.Substring("bytes=".Length);
                }
                else
                {
                    return(Optional <KeyValuePair <Int64, Int64> > .Empty);
                }
                var RangeStrsSplitted = RangeStrsTrimmed.Split(',');
                if (RangeStrsSplitted.Length != 1)
                {
                    return(Optional <KeyValuePair <Int64, Int64> > .Empty);
                }
                var Parts = RangeStrsSplitted.Single().Trim(' ').Split('-').Select(p => p.Trim(' ')).ToArray();
                if (Parts.Length != 2)
                {
                    return(Optional <KeyValuePair <Int64, Int64> > .Empty);
                }
                if (Parts[0] == "")
                {
                    // 不支持suffix-byte-range-spec = "-" suffix-length
                    return(Optional <KeyValuePair <Int64, Int64> > .Empty);
                }
                if (!Int64.TryParse(Parts[0], out Start))
                {
                    return(Optional <KeyValuePair <Int64, Int64> > .Empty);
                }
                if ((Start < 0) || (Start > FileLength))
                {
                    return(Optional <KeyValuePair <Int64, Int64> > .Empty);
                }
                if (Parts[1] == "")
                {
                    Length = FileLength - Start;
                }
                else
                {
                    Int64 End;
                    if (!Int64.TryParse(Parts[1], out End))
                    {
                        return(Optional <KeyValuePair <Int64, Int64> > .Empty);
                    }
                    if (End < Start)
                    {
                        return(Optional <KeyValuePair <Int64, Int64> > .Empty);
                    }
                    Length = Math.Min(End + 1 - Start, FileLength - Start);
                }
                return(new KeyValuePair <Int64, Int64>(Start, Length));
            };

            Action <String, HttpListenerContext, IPEndPoint, Action, Action <Exception> > RequestHandler = (RelativePath, a, e, OnSuccess, OnFailure) =>
            {
                if (a.Request.ContentLength64 > 0)
                {
                    a.Response.StatusCode = 400;
                    OnSuccess();
                    return;
                }

                if (RelativePathTranslator != null)
                {
                    var oRelativePath = RelativePathTranslator(RelativePath);
                    if (oRelativePath.OnNone)
                    {
                        a.Response.StatusCode = 404;
                        OnSuccess();
                        return;
                    }
                    RelativePath = oRelativePath.Value;
                }
                var Path = FileNameHandling.GetAbsolutePath(RelativePath, Root);
                if ((RelativePath != "") && !Path.StartsWith(Root))
                {
                    a.Response.StatusCode = 400;
                    OnSuccess();
                    return;
                }

                if (!File.Exists(Path))
                {
                    var Found = false;

                    var UnescapedPath = Uri.UnescapeDataString(Path);
                    if (File.Exists(UnescapedPath))
                    {
                        Path  = UnescapedPath;
                        Found = true;
                    }

                    if (!Found)
                    {
                        foreach (var Index in Indices)
                        {
                            var IndexPath = FileNameHandling.GetPath(Path, Index);
                            if (File.Exists(IndexPath))
                            {
                                Path  = IndexPath;
                                Found = true;
                                break;
                            }
                        }
                        foreach (var Index in Indices)
                        {
                            var IndexPath = FileNameHandling.GetPath(UnescapedPath, Index);
                            if (File.Exists(IndexPath))
                            {
                                Path  = IndexPath;
                                Found = true;
                                break;
                            }
                        }
                    }

                    if (!Found)
                    {
                        a.Response.StatusCode = 404;
                        OnSuccess();
                        return;
                    }
                }

                if (!Path.StartsWith(Root))
                {
                    a.Response.StatusCode = 400;
                    OnSuccess();
                    return;
                }

                var Buffer = new Byte[512 * 1024];
                using (var fs = Streams.OpenReadable(Path))
                {
                    var LastWriteTime    = File.GetLastWriteTimeUtc(Path);
                    var LastWriteTimeStr = LastWriteTime.ToString("R");
                    var ETag             = "\"" + Times.DateTimeUtcToString(LastWriteTime) + "\"";

                    var IfModifiedSinceStr = a.Request.Headers["If-Modified-Since"];
                    if ((IfModifiedSinceStr != null) && (LastWriteTimeStr == IfModifiedSinceStr))
                    {
                        a.Response.StatusCode = 304;
                        OnSuccess();
                        return;
                    }

                    var oRange     = Optional <KeyValuePair <Int64, Int64> > .Empty;
                    var IfRangeStr = a.Request.Headers["If-Range"];
                    if ((IfRangeStr == null) || (ETag == IfRangeStr) || (LastWriteTimeStr == IfRangeStr))
                    {
                        var RangeStrs = a.Request.Headers["Range"];
                        if (RangeStrs != null)
                        {
                            oRange = TryGetRange(RangeStrs, fs.Length);
                        }
                    }

                    Int64 Start;
                    Int64 Length;
                    if (oRange.OnSome)
                    {
                        var Range = oRange.Value;
                        Start  = Range.Key;
                        Length = Range.Value;
                        a.Response.StatusCode = 206;
                        a.Response.Headers.Add("Content-Range", "bytes {0}-{1}/{2}".Formats(Range.Key, Range.Key + Range.Value - 1, fs.Length));
                    }
                    else
                    {
                        Start  = 0;
                        Length = fs.Length;
                        a.Response.StatusCode = 200;
                    }

                    a.Response.ContentLength64 = Length;
                    a.Response.ContentType     = GetMimeType(FileNameHandling.GetExtendedFileName(Path));
                    a.Response.Headers.Add("Accept-Ranges", "bytes");
                    a.Response.Headers.Add("Last-Modified", LastWriteTimeStr);
                    a.Response.Headers.Add("ETag", ETag);
                    try
                    {
                        using (var ros = a.Response.OutputStream)
                        {
                            var Count = (Length + Buffer.Length - 1) / Buffer.Length;
                            fs.Position = Start;
                            for (int k = 0; k < Count; k += 1)
                            {
                                var ChunkSize = (int)(Math.Min(Buffer.Length, Length - Buffer.Length * k));
                                fs.Read(Buffer, 0, ChunkSize);
                                ros.Write(Buffer, 0, ChunkSize);
                            }
                        }
                    }
                    catch (System.Net.HttpListenerException)
                    {
                    }
                }

                OnSuccess();
            };

            Inner                         = new ExternalHttpServer(ServerContext, RequestHandler, QueueUserWorkItem, 8 * 1024);
            Inner.Bindings                = Bindings;
            Inner.SessionIdleTimeout      = SessionIdleTimeout;
            Inner.MaxConnections          = MaxConnections;
            Inner.MaxConnectionsPerIP     = MaxConnectionsPerIP;
            Inner.MaxBadCommands          = MaxBadCommands;
            Inner.MaxUnauthenticatedPerIP = MaxUnauthenticatedPerIP;
            Inner.ServiceVirtualPath      = ServiceVirtualPath;

            Inner.Start();
        }