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); }
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); }
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); }
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); }
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; }
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); }
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)); }
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")); }
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)); }
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)); }
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); } }
public override Task ProgressTask(WebProgressTask task) { task.Document.DataSources.Add(new HttpFileDataSource(path) { MimeType = MimeType.TextHtml }); return(Task.CompletedTask); }
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?)[]
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)); }
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)); }
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?)[]
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)); }
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); }
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); } }
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)); }
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); }
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); }
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())); }
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..]);
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); }
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); }
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); }
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); }
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); }
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(); }