コード例 #1
0
        public override Task ProgressTask(WebProgressTask task)
        {
            task.Response.StatusCode = HttpStateCode.NotFound;
            var sb = new StringBuilder();

            sb.Append("<html><head><title>404 NOT FOUND</title></head>");
            sb.Append("<body><h1>Error 404: Not Found</h1><p>The requested resource is not found.</p>");
            sb.AppendLine("<pre>");
            sb.AppendLine($"Protocol: {WebUtility.HtmlEncode(task.Request.HttpProtocol)}");
            sb.AppendLine($"Method:   {WebUtility.HtmlEncode(task.Request.ProtocolMethod)}");
            sb.AppendLine($"Url:      {WebUtility.HtmlEncode(task.Request.Location.Url)}");
            sb.AppendLine($"Header:");
            foreach (var(key, value) in task.Request.HeaderParameter)
            {
                sb.AppendLine($"\t{WebUtility.HtmlEncode(key)}: {WebUtility.HtmlEncode(value)}");
            }
            sb.AppendLine($"Body:");
            sb.AppendLine(WebUtility.HtmlEncode(task.Request.Post.ToString()));
            sb.Append($"</pre><p>Try to change the request to get your expected response.</p>");
            sb.Append($"<small>Created by <a href=\"https://github.com/Garados007/MaxLib.WebServer\" " +
                      $"target=\"_blank\">MaxLib.WebServer {Version}</a>: {DateTime.UtcNow:r}</small></body></html>");
            task.Document.DataSources.Add(new HttpStringDataSource(sb.ToString())
            {
                MimeType     = MimeType.TextHtml,
                TextEncoding = "utf-8",
            });
            return(Task.CompletedTask);
        }
コード例 #2
0
        public override async Task ProgressTask(WebProgressTask task)
        {
            _ = task ?? throw new ArgumentNullException(nameof(task));
            var    cookie = task.Request?.Cookie.Get("Session");
            string key;

            if (cookie == null)
            {
                key = await GenerateSessionKey().ConfigureAwait(false);

                task.Request?.Cookie.AddedCookies.Add("Session",
                                                      new HttpCookie.Cookie(
                                                          "Session",
                                                          key,
                                                          DateTime.UtcNow + MaxAge,
                                                          (int)MaxAge.TotalSeconds,
                                                          CookiePath
                                                          ));
            }
            else
            {
                key = cookie.Value.ValueString;
            }
            task.Session = await Get(key).ConfigureAwait(false);
        }
コード例 #3
0
        protected virtual async ValueTask <bool> WaitForData(WebProgressTask task)
        {
            try
            {
                if (task.NetworkStream is NetworkStream ns && !ns.DataAvailable)
                {
                    var maxDelay = MaxConnectionDelay;
                    var maxSlice = TimeSpan.FromMilliseconds(10);
                    while (maxDelay > TimeSpan.Zero && !ns.DataAvailable)
                    {
                        var slice = maxSlice < maxDelay ? maxSlice : maxDelay;
                        await Task.Delay(slice).ConfigureAwait(false);

                        maxDelay -= slice;
                    }
                    if (!ns.DataAvailable)
                    {
                        WebServerLog.Add(ServerLogType.Error, GetType(), "Header", "Request Timeout");
                        task.Request.FieldConnection = HttpConnectionType.KeepAlive;
                        task.Response.StatusCode     = HttpStateCode.RequestTimeOut;
                        task.NextStage = ServerStage.CreateResponse;
                        return(false);
                    }
                }
            }
            catch (ObjectDisposedException)
            {
                WebServerLog.Add(ServerLogType.Error, GetType(), "Header", "Connection closed by remote host");
                task.Response.StatusCode = HttpStateCode.RequestTimeOut;
                task.NextStage           = ServerStage.FINAL_STAGE;
                return(false);
            }
            return(true);
        }
コード例 #4
0
        protected virtual async ValueTask <string?> ReadLine(WebProgressTask task,
                                                             NetworkReader reader, long limit, HttpStateCode exceedState
                                                             )
        {
            string?line;

            try { line = await reader.ReadLineAsync(limit).ConfigureAwait(false); }
            catch (IO.ReadLineOverflowException e)
            {
                e.State = exceedState;
                throw;
            }
            catch
            {
                WebServerLog.Add(ServerLogType.Error, GetType(), "Header", "Connection closed by remote host");
                task.Response.StatusCode = HttpStateCode.RequestTimeOut;
                task.NextStage           = ServerStage.FINAL_STAGE;
                return(null);
            }
            if (line == null)
            {
                WebServerLog.Add(ServerLogType.Error, GetType(), "Header", "Can't read Header line");
                task.Response.StatusCode = HttpStateCode.BadRequest;
                task.NextStage           = ServerStage.CreateResponse;
            }
            return(line);
        }
コード例 #5
0
        private void HandleCreateConnection(WebProgressTask task, string responseKey,
                                            IWebSocketEndpoint endpoint, WebSocketConnection connection)
        {
            task.Response.StatusCode = HttpStateCode.SwitchingProtocols;
            task.Response.SetHeader(
                ("Access-Control-Allow-Origin", "*"),
                ("Upgrade", "websocket"),
                ("Connection", "Upgrade"),
                ("Sec-WebSocket-Accept", responseKey),
                ("Sec-WebSocket-Protocol", endpoint.Protocol)
                );

            task.SwitchProtocols(async() =>
            {
                if (System.Diagnostics.Debugger.IsAttached)
                {
                    await connection.HandshakeFinished().ConfigureAwait(false);
                }
                else
                {
                    try
                    {
                        await connection.HandshakeFinished().ConfigureAwait(false);
                    }
                    catch (Exception e)
                    {
                        WebServerLog.Add(ServerLogType.Error, GetType(), "handshake", $"handshake error: {e}");
                    }
                }
            });
            task.NextStage = ServerStage.SendResponse;
        }
コード例 #6
0
        public override bool CheckPrecondition(WebProgressTask task)
        {
            if (!base.CheckPrecondition(task))
            {
                return(false);
            }

            Dictionary <string, object?> vars;

            if (task.Document.Information.TryGetValue(MethodService.InfoKey, out object?infoKeyObj) &&
                infoKeyObj is Dictionary <string, object?> info
                )
            {
                vars = info;
            }
            else
            {
                vars = new Dictionary <string, object?>();
            }

            foreach (var rule in Rules)
            {
                task.Monitor.Current.Log("check rule {0}", rule);
                if (!rule.CanWorkWith(task, vars))
                {
                    return(false);
                }
            }

            task.Document[MethodService.InfoKey] = vars;
            task.Monitor.Current.Log("rule success");
            return(true);
        }
コード例 #7
0
        protected override async Task <HttpDataSource> HandleRequest(WebProgressTask task, string[] location)
        {
            _ = task ?? throw new ArgumentNullException(nameof(task));
            _ = location ?? throw new ArgumentNullException(nameof(location));
            var query = GetQueryArgs(task, location);

            foreach (var endpoint in RestEndpoints)
            {
                if (endpoint == null)
                {
                    continue;
                }
                var q = endpoint.Check(query);
                if (q == null)
                {
                    continue;
                }
                var result = await endpoint.GetSource(q.ParsedArguments)
                             .ConfigureAwait(false);

                if (result != null)
                {
                    return(result);
                }
            }
            return(NoEndpoint(task, query));
        }
コード例 #8
0
 protected virtual HttpDataSource NoEndpoint(WebProgressTask task, RestQueryArgs args)
 {
     _ = task ?? throw new ArgumentNullException(nameof(task));
     _ = args ?? throw new ArgumentNullException(nameof(args));
     task.Response.StatusCode = HttpStateCode.NotFound;
     return(new HttpStringDataSource("no endpoint"));
 }
コード例 #9
0
 public override bool CanWorkWith(WebProgressTask task)
 {
     return(!OnlyWithLazy || (task.Document.DataSources.Count > 0 &&
                              task.Document.DataSources.Any((s) => s is LazySource ||
                                                            (s is Remote.MarshalSource ms && ms.IsLazy)
                                                            )) || task.Document.DataSources.Any(s => s.Length() is null));
 }
コード例 #10
0
        protected virtual ValueTask <bool> LoadContent(WebProgressTask task, NetworkReader reader)
        {
            if (!task.Request.HeaderParameter.TryGetValue("Content-Length", out string?strLength))
            {
                return(new ValueTask <bool>(true));
            }

            if (!int.TryParse(strLength, out int length) || length < 0)
            {
                WebServerLog.Add(ServerLogType.Error, GetType(), "Header", "Bad Request, invalid content length");
                task.Response.StatusCode = HttpStateCode.BadRequest;
                task.NextStage           = ServerStage.CreateResponse;
                return(new ValueTask <bool>(false));
            }

            var content = new IO.ContentStream(reader, length);

            task.Request.Post.SetPost(
                task,
                content,
                task.Request.HeaderParameter.TryGetValue("Content-Type", out string?contentType)
                    ? contentType : null
                );

            return(new ValueTask <bool>(true));
        }
コード例 #11
0
        public override async Task ProgressTask(WebProgressTask task)
        {
            if (task.NetworkStream == null)
            {
                return;
            }

            var protocols = (task.Request.GetHeader("Sec-WebSocket-Protocol")?.ToLower() ?? "")
                            .Split(new char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);

            var key     = task.Request.GetHeader("Sec-WebSocket-Key");
            var version = task.Request.GetHeader("Sec-WebSocket-Version"); // MUST be 13 according RFC 6455

            if (key == null || version != "13")
            {
                task.Response.StatusCode = HttpStateCode.BadRequest;
                task.Response.SetHeader("Sec-WebSocket-Version", "13");
                task.NextStage = ServerStage.CreateResponse;
                return;
            }

            var responseKey = Convert.ToBase64String(
                System.Security.Cryptography.SHA1.Create().ComputeHash(
                    Encoding.UTF8.GetBytes(
                        $"{key.Trim()}258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
                        )
                    )
                );


            foreach (var endpoint in Endpoints)
            {
                if (protocols.Length > 0 && (endpoint.Protocol == null || !protocols.Contains(endpoint.Protocol)))
                {
                    continue;
                }

                var connection = await endpoint.Create(task.NetworkStream, task.Request).ConfigureAwait(false);

                if (connection == null)
                {
                    continue;
                }

                HandleCreateConnection(task, responseKey, endpoint, connection);
                return;
            }

            if (CloseEndpoint is WebSocketCloserEndpoint ep)
            {
                var connection = await ep.Create(task.NetworkStream, task.Request).ConfigureAwait(false);

                if (connection == null)
                {
                    return;
                }
                HandleCreateConnection(task, responseKey, ep, connection);
            }
        }
コード例 #12
0
 public override Task ProgressTask(WebProgressTask task)
 {
     task.Document.DataSources.Add(new HttpFileDataSource(path)
     {
         MimeType = MimeType.TextHtml
     });
     return(Task.CompletedTask);
 }
コード例 #13
0
        public override async Task ProgressTask(WebProgressTask task)
        {
            var request  = task.Request;
            var response = task.Response;

            response.FieldContentType = task.Document.PrimaryMime;
            response.SetActualDate();
            response.HttpProtocol = request.HttpProtocol;
            response.SetHeader(new (string, string?)[]
コード例 #14
0
 public override Result <object?> GetValue(WebProgressTask task, string field,
                                           Dictionary <string, object?> vars
                                           )
 {
     if (!vars.TryGetValue(Name ?? field, out object?value))
     {
         return(new Result <object?>());
     }
     return(new Result <object?>(value));
 }
コード例 #15
0
 public override Result <object?> GetValue(WebProgressTask task, string field,
                                           Dictionary <string, object?> vars
                                           )
 {
     if (!task.Request.Location.GetParameter.TryGetValue(Name ?? field, out string?value))
     {
         return(new Result <object?>());
     }
     return(new Result <object?>(value));
 }
コード例 #16
0
        public override async Task ProgressTask(WebProgressTask task)
        {
            _ = task ?? throw new ArgumentNullException(nameof(task));

            var request  = task.Request;
            var response = task.Response;

            response.FieldContentType = task.Document.PrimaryMime;
            response.SetActualDate();
            response.HttpProtocol = request.HttpProtocol;
            response.SetHeader(new (string, string?)[]
コード例 #17
0
        public override Task ProgressTask(WebProgressTask task, object?[]?data)
        {
            if (data is null)
            {
                return(Task.CompletedTask);
            }
            using var watch = task.Monitor.Watch(MethodClass, $"Execute {Method.Name}()");
            var result = Method.Invoke(MethodClass, data);

            GC.KeepAlive(watch);
            return(Result(task, result));
        }
コード例 #18
0
 public override bool CanWorkWith(WebProgressTask task)
 {
     if (task.Request.Location.StartsUrlWith(new[] { "game" }))
     {
         return(true);
     }
     if (task.Request.Location.IsUrl(new[] { "script.js" }))
     {
         return(true);
     }
     return(false);
 }
コード例 #19
0
        public override bool CanWorkWith(WebProgressTask task)
        {
            _ = task ?? throw new ArgumentNullException(nameof(task));

            switch (task.Request.ProtocolMethod)
            {
            case HttpProtocolMethod.Head: return(true);

            case HttpProtocolMethod.Options: return(true);

            default: return(false);
            }
        }
コード例 #20
0
        public override Result <object?> GetValue(WebProgressTask task, string field, Dictionary <string, object?> vars)
        {
            var post = task.Request.Post.Data;

            if (!(post is Post.UrlEncodedData data))
            {
                return(new Result <object?>());
            }
            if (!data.Parameter.TryGetValue(Name ?? field, out string?value))
            {
                return(new Result <object?>());
            }
            return(new Result <object?>(value));
        }
コード例 #21
0
        public override async Task ProgressTask(WebProgressTask task)
        {
            _ = task ?? throw new ArgumentNullException(nameof(task));

            var source = new HttpStringDataSource(Document)
            {
                MimeType = MimeType.TextHtml
            };

            task.Response.StatusCode = HttpStateCode.OK;
            task.Document.DataSources.Add(source);
            task.Document.PrimaryEncoding = "utf-8";

            await Task.CompletedTask.ConfigureAwait(false);
        }
コード例 #22
0
        public async Task TestMethodExec()
        {
            var method = MaxLib.WebServer.Builder.Tools.Generator.GenerateMethod(
                typeof(SingleService).GetMethod("Foo") !
                );
            var task = new WebProgressTask();

            task.Request.Url = "/foo/2?bar=bar";
            Assert.IsTrue(method !.CanWorkWith(task, out object?[]? data));
            await method.ProgressTask(task, data);

            Assert.AreEqual(1, task.Document.DataSources.Count);
            Assert.IsTrue(task.Document.DataSources[0] is HttpStringDataSource);
            var source = (HttpStringDataSource)task.Document.DataSources[0];

            Assert.AreEqual("test", source.Data);
        }
コード例 #23
0
        public override Result <object?> GetValue(WebProgressTask task, string field, Dictionary <string, object?> vars)
        {
            var post = task.Request.Post.Data;

            if (!(post is MaxLib.WebServer.Post.UnknownPostData data))
            {
                return(new Result <object?>());
            }
            using var reader = new StreamReader(
                      data.Data,
                      System.Text.Encoding.UTF8,
                      bufferSize: -1,
                      detectEncodingFromByteOrderMarks: false,
                      leaveOpen: true
                      );
            return(new Result <object?>(reader.ReadToEnd()));
        }
コード例 #24
0
            public override bool MapRequest(ReadOnlySpan <string> path, WebProgressTask task)
            {
                var loc = task.Request.Location;

                if (loc.DocumentPath.EndsWith('/') || path.Length < UrlPath.Length)
                {
                    return(false);
                }
                for (int i = 0; i < UrlPath.Length; ++i)
                {
                    if (UrlPath.Span[i] != path[i])
                    {
                        return(false);
                    }
                }
                Span <string> localPathTiles = new string[path.Length - UrlPath.Length + 1];

                localPathTiles[0] = LocalBasePath;
                path[UrlPath.Length..].CopyTo(localPathTiles[1..]);
コード例 #25
0
        private async ValueTask DebugConnection(WebProgressTask task)
        {
            if (DebugLogConnectionFile == null)
            {
                return;
            }

            var sb = new StringBuilder();

            sb.AppendLine($"{WebServerUtils.GetDateString(DateTime.UtcNow)} " +
                          $"{task.Connection?.NetworkClient?.Client.RemoteEndPoint}");
            var host = task.Request.HeaderParameter.TryGetValue("Host", out string?host_)
                ? host_ : "";

            sb.AppendLine("    " + host + task.Request.Location.DocumentPath);
            sb.AppendLine();

            await File.AppendAllTextAsync(DebugLogConnectionFile, sb.ToString()).ConfigureAwait(false);
        }
コード例 #26
0
        protected virtual bool ParseFirstHeaderLine(WebProgressTask task, string line)
        {
            WebServerLog.Add(ServerLogType.Debug, GetType(), "Header", line);
            var parts = line.Split(' ');

            if (parts.Length != 3)
            {
                WebServerLog.Add(ServerLogType.Error, GetType(), "Header", "Bad Request");
                task.Response.StatusCode = HttpStateCode.BadRequest;
                task.NextStage           = ServerStage.CreateResponse;
                return(false);
            }

            task.Request.ProtocolMethod = parts[0];
            task.Request.Url            = parts[1];
            task.Request.HttpProtocol   = parts[2];

            return(true);
        }
コード例 #27
0
        protected virtual bool ParseOtherHeaderLine(WebProgressTask task, string line)
        {
            var ind = line.IndexOf(':');

            if (ind < 0)
            {
                WebServerLog.Add(ServerLogType.Error, GetType(), "Header", "Bad Request");
                task.Response.StatusCode = HttpStateCode.BadRequest;
                task.NextStage           = ServerStage.CreateResponse;
                return(false);
            }

            var key   = line.Remove(ind).Trim();
            var value = line.Substring(ind + 1).Trim();

            task.Request.HeaderParameter.Add(key, value);

            return(true);
        }
コード例 #28
0
        public override bool CanWorkWith(WebProgressTask task, out object?[]?data)
        {
            using var watch = task.Monitor.Watch(MethodClass, $"Check {Method.Name}()");
            data            = new object[Parameters.Count];
            Dictionary <string, object?> vars;

            if (task.Document.Information.TryGetValue(InfoKey, out object?infoKeyObj) &&
                infoKeyObj is Dictionary <string, object?> info
                )
            {
                vars = info;
            }
            else
            {
                vars = new Dictionary <string, object?>();
            }
            // verify rules
            foreach (var rule in Rules)
            {
                watch.Log("verify rule {0}", rule);
                if (!rule.CanWorkWith(task, vars))
                {
                    return(false);
                }
            }
            // execute parameter
            for (int i = 0; i < Parameters.Count; ++i)
            {
                watch.Log("execute parameter {0}", Parameters[i]);
                var res = Parameters[i].GetValue(task, vars);
                if (!res.HasValue)
                {
                    return(false);
                }
                data[i] = res.Value;
            }
            // method is ready to call
            GC.KeepAlive(watch);
            return(true);
        }
コード例 #29
0
        public override async Task ProgressTask(WebProgressTask task)
        {
            _ = task ?? throw new ArgumentNullException(nameof(task));

            var header = task.Request;

            //Accept
            if (header.HeaderParameter.TryGetValue("Accept", out string?value))
            {
                header.FieldAccept.AddRange(value.Split(
                                                new[] { ',', ' ', ';' }, StringSplitOptions.RemoveEmptyEntries));
            }
            //Accept-Encoding
            if (header.HeaderParameter.TryGetValue("Accept-Encoding", out value))
            {
                header.FieldAcceptEncoding.AddRange(value.Split(
                                                        new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries));
            }
            //Connection
            if (header.HeaderParameter.TryGetValue("Connection", out value))
            {
                if (value.ToLower() == "keep-alive")
                {
                    header.FieldConnection = HttpConnectionType.KeepAlive;
                }
            }
            //Host
            if (header.HeaderParameter.TryGetValue("Host", out value))
            {
                header.Host = value;
            }
            //Cookie
            if (header.HeaderParameter.TryGetValue("Cookie", out value))
            {
                header.Cookie.SetRequestCookieString(value);
            }

            await Task.CompletedTask.ConfigureAwait(false);
        }
コード例 #30
0
        internal async Task Save(string path, DateTime started, WebProgressTask task)
        {
            var callName = SanitizePath(task.Request.Location.DocumentPath);

            if (callName.Length == 0)
            {
                callName = "_";
            }
            var date = started.ToString("yyyy-MM-dd_HH-mm-ss-fffffff");

            var dir = $"{path}/{callName}";

            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
            var inc  = 1;
            var file = $"{dir}/{date}.log";

            while (File.Exists(file))
            {
                inc++;
                file = $"{dir}/{date}.{inc}.log";
            }

            using var stream = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read);
            using var writer = new StreamWriter(stream, Encoding.UTF8);
            try { WriteTo(writer); }
            catch (Exception e)
            {
                WebServerLog.Add(ServerLogType.FatalError, GetType(), "write logs", e.ToString());
                writer.WriteLine(e);
            }
            await writer.FlushAsync();

            writer.Flush();
            await stream.FlushAsync();
        }