forked from hardkoded/puppeteer-sharp
/
SimpleServer.cs
136 lines (121 loc) · 5.14 KB
/
SimpleServer.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Microsoft.AspNetCore.Builder;
using System.Net;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using System.Text;
namespace PuppeteerSharp.TestServer
{
public class SimpleServer
{
private readonly IDictionary<string, Action<HttpRequest>> _requestSubscribers;
private readonly IDictionary<string, RequestDelegate> _routes;
private readonly IDictionary<string, (string username, string password)> _auths;
private readonly IWebHost _webHost;
public static SimpleServer Create(int port, string contentRoot) => new SimpleServer(port, contentRoot, isHttps: false);
public static SimpleServer CreateHttps(int port, string contentRoot) => new SimpleServer(port, contentRoot, isHttps: true);
public SimpleServer(int port, string contentRoot, bool isHttps)
{
_requestSubscribers = new ConcurrentDictionary<string, Action<HttpRequest>>();
_routes = new ConcurrentDictionary<string, RequestDelegate>();
_auths = new ConcurrentDictionary<string, (string username, string password)>();
_webHost = new WebHostBuilder()
.ConfigureAppConfiguration((context, builder) => builder
.SetBasePath(context.HostingEnvironment.ContentRootPath)
.AddEnvironmentVariables()
)
.Configure(app => app.Use((context, next) =>
{
if (_auths.TryGetValue(context.Request.Path, out var auth) && !Authenticate(auth.username, auth.password, context))
{
context.Response.Headers.Add("WWW-Authenticate", "Basic realm=\"Secure Area\"");
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return context.Response.WriteAsync("HTTP Error 401 Unauthorized: Access is denied");
}
if (_requestSubscribers.TryGetValue(context.Request.Path, out var subscriber))
{
subscriber(context.Request);
}
if (_routes.TryGetValue(context.Request.Path, out var handler))
{
return handler(context);
}
return next();
})
.UseStaticFiles())
.UseKestrel(options =>
{
if (isHttps)
{
options.Listen(IPAddress.Loopback, port, listenOptions => listenOptions.UseHttps("testCert.cer"));
}
else
{
options.Listen(IPAddress.Loopback, port);
}
})
.UseContentRoot(contentRoot)
.Build();
}
public void SetAuth(string path, string username, string password)
{
_auths.Add(path, (username, password));
}
public Task StartAsync() => _webHost.StartAsync();
public async Task StopAsync()
{
Reset();
await _webHost.StopAsync();
}
public void Reset()
{
_routes.Clear();
_auths.Clear();
foreach (var subscriber in _requestSubscribers.Values)
{
subscriber(null);
}
_requestSubscribers.Clear();
}
public void SetRoute(string path, RequestDelegate handler)
{
_routes.Add(path, handler);
}
public void SetRedirect(string from, string to)
{
SetRoute(from, context =>
{
context.Response.Redirect(to);
return Task.CompletedTask;
});
}
public async Task<T> WaitForRequest<T>(string path, Func<HttpRequest, T> selector)
{
var taskCompletion = new TaskCompletionSource<T>();
_requestSubscribers.Add(path, (httpRequest) =>
{
taskCompletion.SetResult(selector(httpRequest));
});
var request = await taskCompletion.Task;
_requestSubscribers.Remove(path);
return request;
}
private static bool Authenticate(string username, string password, HttpContext context)
{
string authHeader = context.Request.Headers["Authorization"];
if (authHeader != null && authHeader.StartsWith("Basic", StringComparison.Ordinal))
{
string encodedUsernamePassword = authHeader.Substring("Basic ".Length).Trim();
var encoding = Encoding.GetEncoding("iso-8859-1");
string auth = encoding.GetString(Convert.FromBase64String(encodedUsernamePassword));
return auth == $"{username}:{password}";
}
return false;
}
}
}