
Einfacher, skalierbarer, schneller asynchrischer Webserver zur Verarbeitung von HTTP/HTTPS -Anforderungen, geschrieben in C#.
| Paket | Nuget -Version | Downloads |
|---|---|---|
| Watson | ||
| Watson.lite | ||
| Watson.core |
Besonderer Dank geht an @damiendennehy für die Verwendung des Paketnamens von Watson.Core in Nuget!
Dieses Projekt ist Teil der .NET Foundation zusammen mit anderen Projekten wie der .NET -Laufzeit.
SendChunk akzeptiert jetzt isFinal als Boolean EigenschaftTest.ServerSentEvents -ProjektIch möchte einen besonderen Dank an diejenigen ausdehnen, die dazu beigetragen haben, Watson Webserver besser zu machen.
Watson ist ein Webserver, der über den zugrunde liegenden http.sys innerhalb des Betriebssystems arbeitet. Watson.lite wurde durch Zusammenführen von httpserverlite erstellt. Watson.lite hat keine Abhängigkeit von http.sys und wird unter Verwendung einer von CavemantCP bereitgestellten TCP -Implementierung implementiert.
Die Abhängigkeit von http.sys (oder fehlend davon) schafft subtile Unterschiede zwischen den beiden Bibliotheken. Die Konfiguration und Verwaltung der einzelnen sollte jedoch konsistent sein.
Watson.lite ist im Allgemeinen weniger leistungsfähig als Watson, da sich die HTTP -Implementierung im Benutzerbereich befindet.
127.0.0.1 oder localhost Bindung geblieben istX509Certificate2 geliefert werden Watson und Watson.lite wechselt immer in der folgenden Reihenfolge (konfigurieren Sie mit Webserver.Routes ):
.Preflight - Bearbeitung von Vorfluganforderungen (im Allgemeinen mit HTTP OPTIONS ).PreRouting - immer aufgerufen, bevor eine Routing -Entschlossenheit getroffen wird.PreAuthentication - eine Routing -Gruppe, die aus:.Static - Statische Routen, z. B. eine HTTP -Methode und eine explizite URL.Content - Dateibedienungsrouten, z. B. ein Verzeichnis, in dem Dateien gelesen werden können.Parameter - Routen, auf denen Variablen im Pfad angegeben sind, z /user/{id}.Dynamic - Routen, auf denen die URL durch einen regulären Ausdruck definiert wird.AuthenticateRequest - Abgrenzungsroute zwischen nicht authentifizierter und authentifizierter Routen.PostAuthentication - eine Routing -Gruppe mit einer Struktur, die mit .PreAuthentication identisch ist.Default - die Standardroute; Alle Anfragen gehen hier, wenn sie nicht zuvor weitergeleitet werden.PostRouting Wenn Sie keine Authentifizierung verwenden möchten, sollten Sie Ihre Routen in der Routinggruppe .PreAuthentication zuordnen (obwohl sie technisch gesehen in .PostAuthentication oder .Default angenommen werden können, dass die AuthenticateRequest -Methode null ist.
Versuchen Sie in der Regel niemals, Daten auf der .PostRouting -Route an eine HttpResponse zu senden. Wenn bereits eine Antwort gesendet wurde, fällt der Versuch innerhalb von .PostRouting fehl.
Es wird empfohlen, dass Sie Authentifizierung in .AuthenticateRequest implementieren. Sollte eine Anfrage keine Authentifizierung fehlenden, senden Sie eine Antwort innerhalb dieser Route zurück. Die HttpContextBase -Klasse verfügt über Eigenschaften, die authentifizierungsbezogene oder Sitzungsmetadaten, insbesondere. .Metadata , aufbewahren können.
Standardmäßig werden Watson und Watson.lite alle eingehenden Verbindungen zulassen.
Server.AccessControl.DenyList.Add(ip, netmask)Server.AccessControl.Mode = AccessControlMode.DefaultDenyServer.AccessControl.PermitList.Add(ip, netmask) Ein vollständiges Beispiel finden Sie in Test.Default .
using System . IO ;
using System . Text ;
using WatsonWebserver ;
static void Main ( string [ ] args )
{
WebserverSettings settings = new WebserverSettings ( "127.0.0.1" , 9000 ) ;
Webserver server = new Webserver ( settings , DefaultRoute ) ;
server . Start ( ) ;
Console . ReadLine ( ) ;
}
static async Task DefaultRoute ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Hello from the default route!" ) ; Öffnen Sie dann Ihren Browser für http://127.0.0.1:9000/ .
Weitere Informationen finden Sie unter Test.Routing für ein vollständiges Beispiel.
using System . IO ;
using System . Text ;
using System . Text . RegularExpressions ;
using WatsonWebserver ;
static void Main ( string [ ] args )
{
WebserverSettings settings = new WebserverSettings ( "127.0.0.1" , 9000 ) ;
Webserver server = new Webserver ( settings , DefaultRoute ) ;
// add content routes
server . Routes . PreAuthentication . Content . Add ( "/html/" , true ) ;
server . Routes . PreAuthentication . Content . Add ( "/img/watson.jpg" , false ) ;
// add static routes
server . Routes . PreAuthentication . Static . Add ( HttpMethod . GET , "/hello/" , GetHelloRoute ) ;
server . Routes . PreAuthentication . Static . Add ( HttpMethod . GET , "/howdy/" , async ( HttpContextBase ctx ) =>
{
await ctx . Response . Send ( "Hello from the GET /howdy static route!" ) ;
return ;
} ) ;
// add parameter routes
server . Routes . PreAuthentication . Parameter . Add ( HttpMethod . GET , "/{version}/bar" , GetBarRoute ) ;
// add dynamic routes
server . Routes . PreAuthentication . Dynamic . Add ( HttpMethod . GET , new Regex ( "^/foo/ \ d+$" ) , GetFooWithId ) ;
server . Routes . PreAuthentication . Dynamic . Add ( HttpMethod . GET , new Regex ( "^/foo/?$" ) , GetFoo ) ;
// start the server
server . Start ( ) ;
Console . WriteLine ( "Press ENTER to exit" ) ;
Console . ReadLine ( ) ;
}
static async Task GetHelloRoute ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Hello from the GET /hello static route!" ) ;
static async Task GetBarRoute ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Hello from the GET /" + ctx . Request . Url . Parameters [ "version" ] + "/bar route!" ) ;
static async Task GetFooWithId ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Hello from the GET /foo/[id] dynamic route!" ) ;
static async Task GetFoo ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Hello from the GET /foo/ dynamic route!" ) ;
static async Task DefaultRoute ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Hello from the default route!" ) ; server . Routes . PreAuthentication . Static . Add ( HttpMethod . GET , "/hello/" , GetHelloRoute , MyExceptionRoute ) ;
static async Task GetHelloRoute ( HttpContextBase ctx ) => throw new Exception ( "Whoops!" ) ;
static async Task MyExceptionRoute ( HttpContextBase ctx , Exception e )
{
ctx . Response . StatusCode = 500 ;
await ctx . Response . Send ( e . Message ) ;
} Webserver server = new Webserver ( "127.0.0.1" , 9000 , false , DefaultRoute ) ;
// set default permit (permit any) with deny list to block specific IP addresses or networks
server . Settings . AccessControl . Mode = AccessControlMode . DefaultPermit ;
server . Settings . AccessControl . DenyList . Add ( "127.0.0.1" , "255.255.255.255" ) ;
// set default deny (deny all) with permit list to permit specific IP addresses or networks
server . Settings . AccessControl . Mode = AccessControlMode . DefaultDeny ;
server . Settings . AccessControl . PermitList . Add ( "127.0.0.1" , "255.255.255.255" ) ; Watson unterstützt sowohl chunked-Daten als auch das Senden von Chunked-Daten (angezeigt durch die Header Transfer-Encoding: chunked ).
Siehe Test.ChunkServer für eine Beispielimplementierung.
static async Task UploadData ( HttpContextBase ctx )
{
if ( ctx . Request . ChunkedTransfer )
{
bool finalChunk = false ;
while ( ! finalChunk )
{
Chunk chunk = await ctx . Request . ReadChunk ( ) ;
// work with chunk.Length and chunk.Data (byte[])
finalChunk = chunk . IsFinalChunk ;
}
}
else
{
// read from ctx.Request.Data stream
}
} static async Task DownloadChunkedFile ( HttpContextBase ctx )
{
using ( FileStream fs = new FileStream ( "./img/watson.jpg" , , FileMode . Open , FileAccess . Read ) )
{
ctx . Response . StatusCode = 200 ;
ctx . Response . ChunkedTransfer = true ;
byte [ ] buffer = new byte [ 4096 ] ;
while ( true )
{
int bytesRead = await fs . ReadAsync ( buffer , 0 , buffer . Length ) ;
byte [ ] data = new byte [ bytesRead ] ;
Buffer . BlockCopy ( buffer , 0 , bytesRead , data , 0 ) ; // only copy the read data
if ( bytesRead > 0 )
{
await ctx . Response . SendChunk ( data , false ) ;
}
else
{
await ctx . Response . SendChunk ( Array . Empty < byte > ( ) , true ) ;
break ;
}
}
}
return ;
} Watson unterstützt das Senden von Server-Sent-Ereignissen. Siehe Test.ServerSentEvents für eine Beispielimplementierung. Die SendEvent -Methode erledigt die Vorbereitung data: und die folgende nn zu Ihrer Nachricht.
static async Task SendEvents ( HttpContextBase ctx )
{
ctx . Response . StatusCode = 200 ;
ctx . Response . ServerSentEvents = true ;
while ( true )
{
string data = GetNextEvent ( ) ; // your implementation
if ( ! String . IsNullOrEmpty ( data ) )
{
await ctx . Response . SendEvent ( data ) ;
}
else
{
break ;
}
}
return ;
} HostBuilder hilft Ihnen dabei, Ihren Server viel einfacher einzurichten, indem Sie eine Kette von Einstellungen und Routen einführen, anstatt die Serverklasse direkt zu verwenden. Besonderer Dank geht an @sapurtcomputer30 für die Erstellung dieser guten Funktion!
Weitere Beispiele finden Sie unter Test.HostBuilder für eine vollständige Beispielimplementierung.
using WatsonWebserver . Extensions . HostBuilderExtension ;
Webserver server = new HostBuilder ( "127.0.0.1" , 8000 , false , DefaultRoute )
. MapStaticRoute ( HttpMethod . GET , GetUrlsRoute , "/links" )
. MapStaticRoute ( HttpMethod . POST , CheckLoginRoute , "/login" )
. MapStaticRoute ( HttpMethod . POST , TestRoute , "/test" )
. Build ( ) ;
server . Start ( ) ;
Console . WriteLine ( "Server started" ) ;
Console . ReadKey ( ) ;
static async Task DefaultRoute ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Hello from default route!" ) ;
static async Task GetUrlsRoute ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Here are your links!" ) ;
static async Task CheckLoginRoute ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Checking your login!" ) ;
static async Task TestRoute ( HttpContextBase ctx ) =>
await ctx . Response . Send ( "Hello from the test route!" ) ; Wenn Sie Watson so konfigurieren, dass sie 127.0.0.1 oder localhost zuhören, wird nur auf Anfragen geantwortet, die von der lokalen Maschine empfangen werden.
Verwenden Sie Folgendes, um den Zugriff von anderen Knoten außerhalb von localhost zu konfigurieren:
* oder + . Sie müssen als Administrator ausgeführt (Betriebssystembeschränkung)netsh hinzufügen:netsh http show urlaclnetsh http add urlacl url=http://[hostname]:[port]/ user=everyone listen=yeshostname und port die Werte sind, die Sie im Konstruktor verwenden Wenn Sie Watson.lite so konfigurieren, dass sie unter 127.0.0.1 zuhören, wird nur auf Anfragen geantwortet, die von der lokalen Maschine empfangen werden.
Verwenden Sie Folgendes, um den Zugriff von anderen Knoten außerhalb der lokalen Maschine zu konfigurieren:
* oder + . Sie müssen als Administrator ausgeführt (Betriebssystembeschränkung)X509Certificate2 -Objekt Weitere Informationen finden Sie im Test.Docker -Projekt und die darin Docker.md -Datei.
Weitere Informationen finden Sie in ChangeLog.md für die Versionsgeschichte.