Giter VIP home page Giter VIP logo

watsonwebserver's Introduction

alt tag

Watson Webserver

Simple, scalable, fast, async web server for processing RESTful HTTP/HTTPS requests, written in C#.

Package NuGet Version Downloads
Watson NuGet Version NuGet
Watson.Lite NuGet Version NuGet
Watson.Core NuGet Version NuGet

Special thanks to @DamienDennehy for allowing us the use of the Watson.Core package name in NuGet!

.NET Foundation

This project is part of the .NET Foundation along with other projects like the .NET Runtime.

New in v6.1.x

  • Breaking change to move ContentRouteHandler into ContentRouteManager

Special Thanks

I'd like to extend a special thanks to those that have helped make Watson Webserver better.

  • @notesjor @shdwp @Tutch @GeoffMcGrath @jurkovic-nikola @joreg @Job79 @at1993
  • @MartyIX @pocsuka @orinem @deathbull @binozo @panboy75 @iain-cyborn @gamerhost31
  • @nhaberl @grgouala @sapurtcomputer30 @winkmichael @sqlnew @SaintedPsycho @Return25
  • @marcussacana @samisil @Jump-Suit @ChZhongPengCheng33 @bobaoapae @rodgers-r
  • @john144 @zedle @GitHubProUser67 @bemoty @bemon

Watson vs Watson.Lite

Watson is a webserver that operates on top of the underlying http.sys within the operating system. Watson.Lite was created by merging HttpServerLite. Watson.Lite does not have a dependency on http.sys, and is implemented using a TCP implementation provided by CavemanTcp.

The dependency on http.sys (or lack thereof) creates subtle differences between the two libraries, however, the configuration and management of each should be consistent.

Watson.Lite is generally less performant than Watson, because the HTTP implementation is in user space.

Important Notes

  • Elevation (administrative privileges) may be required if binding an IP other than 127.0.0.1 or localhost
  • For Watson:
    • The HTTP HOST header must match the specified binding
    • For SSL, the underlying computer certificate store will be used
  • For Watson.Lite:
    • Watson.Lite uses a TCP listener; your server must be started with an IP address, not a hostname
    • The HTTP HOST header does not need to match, since the listener must be defined by IP address
    • For SSL, the certificate filename, filename and password, or X509Certificate2 must be supplied

Routing

Watson and Watson.Lite always routes in the following order (configure using Webserver.Routes):

  • .Preflight - handling preflight requests (generally with HTTP method OPTIONS)
  • .PreRouting - always invoked before any routing determination is made
  • .PreAuthentication - a routing group, comprised of:
    • .Static - static routes, e.g. an HTTP method and an explicit URL
    • .Content - file serving routes, e.g. a directory where files can be read
    • .Parameter - routes where variables are specified in the path, e.g. /user/{id}
    • .Dynamic - routes where the URL is defined by a regular expression
  • .AuthenticateRequest - demarcation route between unauthenticated and authenticated routes
  • .PostAuthentication - a routing group with a structure identical to .PreAuthentication
  • .Default - the default route; all requests go here if not routed previously
  • .PostRouting - always invoked, generally for logging and telemetry

If you do not wish to use authentication, you should map your routes in the .PreAuthentication routing group (though technically they can be placed in .PostAuthentication or .Default assuming the AuthenticateRequest method is null.

As a general rule, never try to send data to an HttpResponse while in the .PostRouting route. If a response has already been sent, the attempt inside of .PostRouting will fail.

Authentication

It is recommended that you implement authentication in .AuthenticateRequest. Should a request fail authentication, return a response within that route. The HttpContextBase class has properties that can hold authentication-related or session-related metadata, specifically, .Metadata.

Access Control

By default, Watson and Watson.Lite will permit all inbound connections.

  • If you want to block certain IPs or networks, use Server.AccessControl.DenyList.Add(ip, netmask)
  • If you only want to allow certain IPs or networks, and block all others, use:
    • Server.AccessControl.Mode = AccessControlMode.DefaultDeny
    • Server.AccessControl.PermitList.Add(ip, netmask)

Simple Example

using System.IO;
using System.Text;
using WatsonWebserver;

static void Main(string[] args)
{
  Webserver server = new Server("127.0.0.1", 9000, false, DefaultRoute);
  server.Start();
  Console.ReadLine();
}

static async Task DefaultRoute(HttpContextBase ctx) =>
  await ctx.Response.Send("Hello from the default route!");

Then, open your browser to http://127.0.0.1:9000/.

Example with Routes

Refer to Test.Routing for a full example.

using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using WatsonWebserver;

static void Main(string[] args)
{
  Webserver server = new Server("127.0.0.1", 9000, false, 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!");

Permit or Deny by IP or Network

Webserver server = new Server("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");

Chunked Transfer-Encoding

Watson supports both receiving chunked data and sending chunked data (indicated by the header Transfer-Encoding: chunked).

Refer to Test.ChunkServer for a sample implementation.

Receiving Chunked Data

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   
  }
}

Sending Chunked Data

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);
      if (bytesRead > 0)
      {
        await ctx.Response.SendChunk(buffer, bytesRead);
      }
      else
      {
        await ctx.Response.SendFinalChunk(null, 0);
        break;
      }
    }
  }

  return;
}

HostBuilder

HostBuilder helps you set up your server much more easily by introducing a chain of settings and routes instead of using the server class directly. Special thanks to @sapurtcomputer30 for producing this fine feature!

Refer to Test.HostBuilder for a full sample implementation.

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!"); 

Accessing from Outside Localhost

Watson

When you configure Watson to listen on 127.0.0.1 or localhost, it will only respond to requests received from within the local machine.

To configure access from other nodes outside of localhost, use the following:

  • Specify the exact DNS hostname upon which Watson should listen in the Server constructor
  • The HOST header on HTTP requests MUST match the supplied listener value (operating system limitation)
  • If you want to listen on more than one hostname or IP address, use * or +. You MUST run as administrator (operating system limitation)
  • If you want to use a port number less than 1024, you MUST run as administrator (operating system limitation)
  • Open a port on your firewall to permit traffic on the TCP port upon which Watson is listening
  • You may have to add URL ACLs, i.e. URL bindings, within the operating system using the netsh command:
    • Check for existing bindings using netsh http show urlacl
    • Add a binding using netsh http add urlacl url=http://[hostname]:[port]/ user=everyone listen=yes
    • Where hostname and port are the values you are using in the constructor
    • If you are using SSL, you will need to install the certificate in the certificate store and retrieve the thumbprint
    • Refer to https://github.com/jchristn/WatsonWebserver/wiki/Using-SSL-on-Windows for more information, or if you are using SSL
  • If you're still having problems, start a discussion here, and I will do my best to help and update the documentation.

Watson.Lite

When you configure Watson.Lite to listen on 127.0.0.1, it will only respond to requests received from within the local machine.

To configure access from other nodes outside of the local machine, use the following:

  • Specify the IP address of the network interface on which Watson.Lite should listen
  • If you want to listen on more than one network interface, use * or +. You MUST run as administrator (operating system limitation)
  • If you want to use a port number less than 1024, you MUST run as administrator (operating system limitation)
  • Open a port on your firewall to permit traffic on the TCP port upon which Watson is listening
  • If you are using SSL, you will need to have one of the following when instantiating:
    • The X509Certificate2 object
    • The filename and password to the certificate
  • If you're still having problems, start a discussion here, and I will do my best to help and update the documentation.

Running in Docker

Please refer to the Test.Docker project and the Docker.md file therein.

Version History

Refer to CHANGELOG.md for version history.

watsonwebserver's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

watsonwebserver's Issues

custom base directory for content routes

hei joel,

thanks for this superb library! only thing i seem to be missing so far is to be able to provide a custom directory for content routes. or is there a reason why this wouldn't make any sense?

All routes not found on MacOS

Hi, I'm make an app work fine on Windows. But on MacBook display "Not found (Not found)" any route.
Here is my code:
`
static HttpRequestService request;
static void Main(string[] args)
{
Server s = new Server("127.0.0.1", 19000, false, DefaultRoute);

        s.Routes.Static.Add(WatsonWebserver.HttpMethod.GET, "/app.js", async (ctx) => {
            ctx.Response.ContentType = "application/javascript; charset=utf-8";
            await ctx.Response.Send(Properties.Resources.app);
        });
        s.Routes.Static.Add(WatsonWebserver.HttpMethod.GET, "/app.css", async (ctx) => {
            ctx.Response.ContentType = "text/css; charset=utf-8";
            await ctx.Response.Send(Properties.Resources.app1);
        });
        s.Routes.Static.Add(WatsonWebserver.HttpMethod.GET, "/logo.png", async (ctx) => {
            ctx.Response.ContentType = "image/png";
            await ctx.Response.Send(Properties.Resources.logo);
        });
        s.Routes.Static.Add(WatsonWebserver.HttpMethod.GET, "/login", AuthorizeAsync);
        s.Start();

        Console.WriteLine("Press ENTER to exit");
        Console.ReadLine();
    }
    private static async Task AuthorizeAsync(HttpContext ctx)
    {
        var login = new Login(request);
        try
        {
            var l = await login.GetLogin();
            ctx.Response.StatusCode = 200;
            ctx.Response.ContentType = "application/json; charset=utf-8";
            await ctx.Response.Send(l.ToJson());
            return;
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }
    static async Task DefaultRoute(HttpContext ctx)
    {
        ctx.Response.StatusCode = 200;
        ctx.Response.ContentType = "text/html; charset=utf-8";
        await ctx.Response.Send(Properties.Resources.index);
        return;
    }`

Thanks!

I'm using .Net 5.0

macOS / .NET Core 2.1 not supported

I get this error.

An unhandled exception of type 'System.PlatformNotSupportedException' occurred in System.Private.CoreLib.dll: 'The named version of this synchronization primitive is not supported on this platform.'

Processing multiple GET parameters

Hello Joel,

I'm trying to parse multiple GET requests for an API, eg GET /api/params.cgi?req1=Brand&req2=Label&req3=Name&req4=Description

I've reviewed the other issues and you suggested req.ToString() to parse, but I wonder if that may have changed in some of the recent updates.

Is this still what you recommended or is there a better way that I can process multiple get values in a single request?

Thank you for you help

RunSynchronously may not be called on a task that has already completed

From the Test.Events sample

The WatsonWebserver seems to trip on System.InvalidOperationException every time a GET request comes in. The error occurs in the AcceptConnections in WatsonWebserver.cs

#region Default-Route
_DefaultRoute(ctx).RunSynchronously(); <-- error here
return;
#endregion

ResponseSent event was never fired.

Exception event

Hello,
I'm getting the following exception when static routes are called:

System.InvalidOperationException: RunSynchronously may not be called on a task that has already completed.
   at System.Threading.Tasks.Task.InternalRunSynchronously(TaskScheduler scheduler, Boolean waitForCompletion)
   at WatsonWebserver.Server.<>c__DisplayClass27_0.<AcceptConnections>b__0()

Minimal reproduction script:

        Server server = new Server("127.0.0.1", 9000, false, DefaultRoute);
        server.StaticRoutes.Add(HttpMethod.GET, "/test", test);
        server.Events.ExceptionEncountered = ExceptionEncountered;

        static async Task test(HttpContext ctx)
        {
            Console.WriteLine(ctx.ToString());
        }

        static bool ExceptionEncountered(string ip, int port, Exception e)
        {
            Console.WriteLine("ExceptionEncountered [" + ip + ":" + port + "]: " + Environment.NewLine + e.ToString());
            return true;
        }

I would guess thats a regression to:

  • Removed .RunSynchronously in favor of .Wait for the default route, thereby eliminating an InvalidOperationException (thank you @at1993)

Adding public property about the server's status

Hello,
I'm using the WatsonWebServer in my project.
I am holding an instance of the "Server" object and i have noticed i have no indication about the Http listener status, for example, i don't know if the listener encountered an exception and closed (except to the Console printings).
As a solution, i added the public property as follow:

image

Another solution, is replacing the Boolean property with an Enum representing the status (Initializing\Listening\closedDueToException, etc...)

Disable Log Messages

Maybe I've missed it, but is there a way to disable messages printed to console? With some very large requests being printed to console, the application is being delayed a little bit.

Hosted Images Being Returned as Text/XML

Watson Webserver version: 2.1.3
.NET Framework: 4.6.2

We are having an issue where images in a directory that we host up via server.ContentRoutes.Add("/img/" + nameOfImage) are being returned with text/xml headers when our other application accesses them.

ctx.Response.Send multiple times per connection?

Hello Joel,

Me again (; Quick question, is there a way to use the Send command multiple times in a session? I am trying to stream MJPEG over HTTP 1.1 (Chunk method works perfect), but I don't want to end the session after Send.

I.e.

byte[] jpeg = null;
while (true)
{
ctx.Response.Send(Encoding.ASCII.GetBytes(MJPEGHeader));
ctx.Response.Send(Encoding.ASCII.GetBytes("Content-length: " + jpeg.Length.ToString()));
jpeg = Get_Next_JPEG();
ctx.Response.Send(jpeg);
ctx.Response.Send(Encoding.ASCII.GetBytes(MJPEGFooter));
}

Thanks and sorry to bother so much!

newtonsoft.json dependency

Hi. Sorry for the dumb question, but is it possible in dependencies to lower the version of the Newtonsoft.Json package by 11? I use this server in WinForms with TelegramBot and when updating your package, the project stops working due to different versions of Newtonsoft.Json packages. Thanks.

returning [id] to a string

Hello Joel,

Using a dynamic route I'd like to get the [id] from your main example. I don't seem to see anything in ctx.Request that would allow me to get that other than parsing the QueryString.

Any chance there is another way?

Thanks for your time and help!

Snippet of your own code ...

s.DynamicRoutes.Add(HttpMethod.GET, new Regex("^/foo/\\d+$"), GetFooWithId);  
static async Task GetFooWithId(HttpContext ctx)
{
  ctx.Response.StatusCode = 200;
  await ctx.Response.Send("Hello from the GET /foo/[id] dynamic route!");
}

Response data is stripped from certain status codes

I've skimmed through the server code and don't see anything that might be causing this, and when debug is enable the console reports that the response data is there, so I'm thoroughly stumped about the problem.

The example below should take a status code in the request body and echo it back to you using that status code for the response. For some status codes (e.g. 409 conflict) Postman reports that it gets nothing in the response body, and it's missing all of the headers that should normally be there (Access-Control-Allow-Origin, Content-Length, and Content-Type).

internal class Program
{
    public static void Main(string[] args)
    {
        var server = new Server("localhost", 8080, false, Handler, true);

        Console.ReadKey();
        server.Dispose();
    }

    private static HttpResponse Handler(HttpRequest request)
    {
        var bodyData = request.Data ?? new byte[0];
        var bodyString = Encoding.UTF8.GetString(bodyData);
        int statusCode;
        if (!int.TryParse(bodyString, out statusCode))
            statusCode = 404;

        return new HttpResponse(request, true, statusCode, null, "text/plain", bodyString, true);
    }
}

Downloading files 500 error

Hello,

case 1) When downloading files, only one connection can download the same file. The next connection will get a 500 error ( some exception occurred ).

case 2) If a client is downloading a file and the client connection is closed, the file remains open and the next connection gets the 500 error again.

I didn't want to go through downloading the source code and compiling/testing again since you seem to be fixing and pulling fast and the error is obvious:

FileStream fs = new FileStream(filePath, FileMode.Open);
ctx.Response.StatusCode = 200;
ctx.Response.ContentLength = contentLength;
ctx.Response.ContentType = GetContentType(filePath);
await ctx.Response.Send(contentLength, fs);
return;

I assume all you have to do is fs.close() to close the files stream although i have not tested this. It should at least be enough for case 2.

Windows 10, only 127.0.0.1 works as IP

When running the library on a Windows 10 System, the only IP I can give the HTTP Server is "127.0.0.1", the real IP of the Host as well as "0.0.0.0" get ignored and the Server never starts to listen.
Thus, even if my server is opened on 127.0.0.1, only the host system can actually send a request to the server, but any other client system can't.

Perhaps it's an issue with our PCs in the office (Windows 7 systems work fine btw), but I thought I'd let you know about this.

"Data" member is null when using HttpMethod.POST

I found the program’s strange behavior and want to let you know about it.
Here is an example:

public void Main()
{
Server s = ...
s.AddStaticRoute(HttpMethod.POST, "/employee/", PostEmployee);
}

then I define a method

internal static HttpResponse PostEmployee(HttpRequest arg)
{
... body of the method ...
// --- oh no ! arg.Data == null !
}

The issue is:

when I try to read data from the arg.Data member, it turns out that this variable is null !

I invented a way to get around this annoying feature - that's what I'm doing (HttpListenerContext class helps me).

internal static object ExtractObject(HttpRequest arg)
{
    HttpListenerContext context = arg.ListenerContext;
    MemoryStream ms = new MemoryStream();
    context.Request.InputStream.CopyTo(ms);
    byte[] array = ms.ToArray();
    return TTConvert.ByteArrayToObject(array); // Here I used my own static class TTConvert to convert byte array to an object
}

This method returns non-empty byte-array (at the same time arg.Data is null) and after that I can extract an object of my class from this array.

Could you fix your program such way that the variable arg.Data returns a non-null value?

Project NET Framework 4.6.2 upgrade Watson Webserver to 2.1.2 failed

Hello, Joel !

My problem is: I could not upgrade Watson Webserver in my project from 2.0.2 to 2.1.2 because Package Manager reports me an error:

Could not install package 'ChunkDecoder 1.0.2'. You are trying to install this package into a project that targets '.NETFramework,Version=v4.6.2', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author.

Throttling Number of active connection - Information

Hello,
Is it possible to throttling number of active connection
i want to use droplet which is quite small in resources, and I know that it can handle only x number of active request, by using above parameter I can just drop request if number of active request become more than that.

Web server stops immediately after await StartAsync()

Hi,

I think there is a bug in the new 4.x Major release. Where are coming from 3.x. If I call await server.StartAsync() the server stops immediately.

The bug might be this line.

return Task.Delay(1);

Should it not be so?

return Task.Delay(-1)

Readme should be updated

The readme still contains the LoadRoutes() call in the "Example using Route Attributes" blog. This is confusing because the method has been removed in the new release.

Unable to run complex code inside of Routes

Hello all,

I'm finding if I try to run complicated code such as MySQL and JSON queries inside my routes I get dead locks and can't seem to understand why or find a solution. Here is a snippet of my code;

It is especially odd because the method doesn't do anything, I literally do not see the output of console.writeline at the very top of the method.

static HttpResponse P_DBLookup(HttpRequest req)
      {
          Console.WriteLine("==== P_DBLookup ==== ");
       

          wMySQL mydb = new wMySQL();
          string ResponseUnencrypted = "";
          string encoded_submitted_data = System.Text.Encoding.UTF8.GetString(req.Data);
          try
          {

              Console.WriteLine("Request: P_wBootKey, ClientIP: " + req.SourceIp + " ClientPort: " + req.SourcePort + " URL: " + req.FullUrl +
                                       " Headers: " + DictToString(req.Headers) + " Agent: " + req.Useragent + " KeepAlive: " + req.Keepalive);
              Console.WriteLine("Data Payload: " + encoded_submitted_data);

          dynamic decoded_JSON = JsonConvert.DeserializeObject<dynamic>(encoded_submitted_data);
          string Username = decoded_JSON.LicenseID;
          string Password = decoded_JSON.LicenseKey;
          var Request = decoded_JSON.Request;

Thanks for any help here, been recompiling for the past day now adding and removing code trying to figure out what I can put in and it seems slightly random even.

Use with TelegramBot

I cant use WatsonWebserver with TelegramBot (https://github.com/TelegramBots/Telegram.Bot), at same time (watson need Newtonsoft.Json v12, telegrambot v11). Nuget packages installing correctly but after start application, i get exception:

Could not load file or assembly "Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed" or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

any ideas how fix this? thx for any help

Issue Hosting Images Larger Than ~200MB

Watson Version: 1.2.11
.NET Framework: 4.6
Issue:
I am building an application which is meant to host up files to be uploaded to a web interface. Recently we were running tests with large .tif files and we noticed the application would hang when we tried to access the content route associated with that image. For example, the user would select an image which would get copied to a cache folder next to the .exe which is where the image would get hosted from (127.0.0.1/img/testimage.tif) whenever we try to access this URL the application hangs and we have to force exit. We noticed that with 175MB images it's able to chug through but once we go past 200MB images we start experiencing this issue. Is this package capable of hosting up such large files?

Publish from ffmpeg

Hello Joel,

I'm trying to use your project to publish from ffmpeg, however I am struggling to accept a raw post.

FFMPEG will simply "send data" to a http address, no intro, no hello nothing just http and ... data.
Example: ffmpeg -i rtsp://camera/ -f mpegts -c:v mpeg1video -s 768x432 -b:v 1670k -bf 0 -c:a mp2 -ar 44100 -ac 1 -b:a 64k -muxdelay 0.001 http://watsonserver/watsonrocks

I can't seem to get the raw data to fly. Ideas or suggestions on how I might just accept the raw data.

In short, I am trying to implement https://github.com/phoboslab/jsmpeg/blob/master/websocket-relay.js with your amazing project.

Thank you!

Cant acces server from anywhere but pc its running on

I've successfully created server which works perfectly fine on localhost until i try to connect to it from any other computer in network...

static void Main(string[] args)
        {
            Server server = new Server(_hostname, _port, false, DefaultRoute);
            Database database = new Database();

            server.AccessControl.Mode = AccessControlMode.DefaultPermit;

            for (; ; )
            {
                switch (Console.ReadLine())
                {
                    default:

                        break;
                    case "exit":
                        return;
                }
            }
        }

public static async Task DefaultRoute(HttpContext context)
        {
                context.Response.StatusCode = 200;
                await context.Response.Send("Succesful Request");
                return;
        }

Database class does communication with mysql database stuff..

I've created exception in firewall and i've tried running program with admin privileges..

SSL support

I've had WatsonWebserver running for a while without SSL. It's pretty common to run webservices (even simple ones) under SSL nowadays. I've set the boolean in the constructor to true but I have no way to specify a certificate. Is there any how-to on this specific topic?

Thanks.

No way to catch port in use

In the AcceptConnections method, the httplisterner.Start method may fail (ex. port in use). Since the HttpListernerException is not signaled to the application. It is sometime difficult to figure out why the application is not working. In the meantime I added the try/catch around the _HttpListener.Start() method

try
{
_HttpListener.Start();
}
catch (HttpListenerException er)
{
Events.ExceptionEncountered?.Invoke(null, 0, er);
}

@jchristn, am I correct or am I missing something?

Thanks.

Non Loopback Port

Hello, I am having an issue using any IP other than 127.0.0.1. When I use the IP of my local machine I do not get any response. I also turned off all firewalls to check if that was the issue but that didn't help. I also am on subnet of 255.255.255.0 if that makes any difference. Lastly I also am using Postman to test the API if that is a problem
I used your static route test as a template the this is the only line I changed:
Server s = new Server("192.168.100.97", 8082, false, DefaultRoute);

Any clarity would be much appreciated!
Thank you for your time,
Ethan

HTTP/2 support

Does the Watson Webserver support HTTP/2, and if not, are there any plans to add support for it? Thanks.

Dot Net Core support

When is dot net core support coming?

By the way...Thank you for creating this awesome project. This should have way more stars. Very useful.

Stop WebServer

Hello, thank you for your work!
How can i stop webserver ?

Folder path wrong

Hello,

I recently started using your library and had issues with ContentRoutes missing a slash at the end of the path. Example:

Server s = new Server("127.0.0.1", 9000, false, DefaultRoute);
s.ContentRoutes.Add("/someFolder/", true);
// ContentRouteProcessor.cs --> Process method creates the path as C:/myProjectsomeFolder instead of C:/myProject/someFolder.

I have fixed it by using a custom build with the following changes inside ContentRouteProcessor.cs:

var appDomain = AppDomain.CurrentDomain.BaseDirectory;

string filePath = ctx.Request.RawUrlWithoutQuery;
if (!String.IsNullOrEmpty(filePath))
{
        while (filePath.StartsWith("/") && (appDomain.EndsWith(@"\") || appDomain.EndsWith(@"/")))
        {
                filePath = filePath.Substring(1);
        }
}

The above checks for both slashes so it should be cross platform compatible.
I would appreciate merging a fix for this or any similar implementation so that i don't have to compile my own every time you update. Thank you.

Need code sample for localhost http echo server.

Hello:
I need a simple http echo server, which can accept localhost http post data, and show the data posted by http client.
I looked at your repo., and found it rather complicated.
Can you provide a simple http echo server code sample?
By the way, I am using Visual Studio 2019 version 16.9.0 targeting .net 5 on Windows 10.
Thanks,

Can you ran this server standalone or should you proxy it?

Hi,

first of all also a very cool project. I am interessted to replace my work with asp.net with this. I am currently conncerned how safe it is to use, like does it have dos prevention or any security features? Or is it better just to proxy it trough e.g. apache2?

Thanks!

Dynamic file destinations?

Hi JChristn,

I was wondering if there is a way to have dynamic file routes? For example we generate a package using $filename.$epoch.zip (profile.1553088047.zip) This file typically only exists for a short period of time etc.

Any suggestions on how to make a route like this?

Thank you ever so much!

hosting watsonwebserver on windows dervice

Static root does not work because root directory is c:\windows\system32
in ContetRouteProcesson.cs after line 44 add the following
44) if (!String.IsNullOrEmpty(filePath) && filePath.StartsWith("/")) filePath = filePath.Substring(1);
45) filePath = AppDomain.CurrentDomain.BaseDirectory + filePath;

then works like a charm :)

IP works, but not hostname

Using IP address works

Works:
Server s = new Server("192.168.1.50, 80, false, DefaultRoute, debug_mode);
I get the output from the Default method fine

However, I have a DNS entry to API.mydomain.com pointing at 192.168.1.50 and when I try to do the same get command I get

Bad Request (Invalid host)

Nothing shows up in the debug logs either.

Ideas, or suggestions?

Accessing from outside response "Bad Request-Invalid Hostname"

HI!
I want to access the WebServer from outside, but it response "Bad Request-Invalid Hostname"; I have read the ReadMe about "Accessing from Outside Localhost", but I cannot understand the actual mean of " exact DNS hostname ".
Here is my constructor, I just use localhost as hostname

    Server s = new Server("localhost", 2333, false, DefaultRoute);
    s.Start();

And here is my POST request, I added the headers named "Host" and the value is "localhost":
image

I have already binding the URL like below:
image

And this is my public ip address : 39.99.135.10 , I just want to access the webserver from outer ip address, what should I do?
Thanks!

Use SSL cert in code

Hi! Thanks a lot for WatsonWebServer, it save my project on WinForms. Do you have plans to add support for using a certificate in code? It would be very convenient to use the server instead of the netsh commands.

Xamarin Android

There's a compile error when trying to build a project for Xamarin Android. It's probably related to the System.Web library as seen here.

You use it for the SerializeJsonBuiltIn function in WatsonCommons, but you already provide Newtonsoft.Json that also has serialization. Maybe you could provide a branch containing only Newtonsoft so the build for Android is possible? Thanks in advance!

Library/Frameworks/Mono.framework/External/xbuild/Xamarin/Android/Xamarin.Android.Common.targets(1391,2): error : Exception while loading assemblies: System.IO.FileNotFoundException: Could not load assembly 'System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. Perhaps it doesn't exist in the Mono for Android profile?
/Library/Frameworks/Mono.framework/External/xbuild/Xamarin/Android/Xamarin.Android.Common.targets(1391,2): error : File name: 'System.Web.Extensions.dll'
/Library/Frameworks/Mono.framework/External/xbuild/Xamarin/Android/Xamarin.Android.Common.targets(1391,2): error : at Java.Interop.Tools.Cecil.DirectoryAssemblyResolver.Resolve (Mono.Cecil.AssemblyNameReference reference, Mono.Cecil.ReaderParameters parameters) [0x000c2] in /Users/builder/data/lanes/4695/9dbc4c53/source/xamarin-android/external/Java.Interop/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/DirectoryAssemblyResolver.cs:225
/Library/Frameworks/Mono.framework/External/xbuild/Xamarin/Android/Xamarin.Android.Common.targets(1391,2): error : at Java.Interop.Tools.Cecil.DirectoryAssemblyResolver.Resolve (Mono.Cecil.AssemblyNameReference reference) [0x00001] in /Users/builder/data/lanes/4695/9dbc4c53/source/xamarin-android/external/Java.Interop/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/DirectoryAssemblyResolver.cs:175
/Library/Frameworks/Mono.framework/External/xbuild/Xamarin/Android/Xamarin.Android.Common.targets(1391,2): error : at Xamarin.Android.Tasks.ResolveAssemblies.AddAssemblyReferences (Java.Interop.Tools.Cecil.DirectoryAssemblyResolver resolver, System.Collections.Generic.ICollection`1[T] assemblies, Mono.Cecil.AssemblyDefinition assembly, System.Boolean topLevel) [0x00189] in /Users/builder/data/lanes/4695/9dbc4c53/source/xamarin-android/src/Xamarin.Android.Build.Tasks/Tasks/ResolveAssemblies.cs:143
/Library/Frameworks/Mono.framework/External/xbuild/Xamarin/Android/Xamarin.Android.Common.targets(1391,2): error : at Xamarin.Android.Tasks.ResolveAssemblies.Execute (Java.Interop.Tools.Cecil.DirectoryAssemblyResolver resolver) [0x001c5] in /Users/builder/data/lanes/4695/9dbc4c53/source/xamarin-android/src/Xamarin.Android.Build.Tasks/Tasks/ResolveAssemblies.cs:88
0 Warning(s)
1 Error(s)

Common incoming http-request handler

Hello Joel!

Thank you for continuing to develop and improve Watson Web Server.
This my topic is not an issue but suggestion.

Is it possible to introduce yet another event handler very similar to [Event.RequestReceived] but with more advanced functionality?

for example like this:

HttpResponse  PreliminaryCommonHandler(HttpRequest req)
{
....
/* It may returns null */
....
}

The principle of this handler's action is as follows:

  1. Any incoming request comes into this method.
  2. If this method returns [null], then further processing occurs as usual in accordance with the route.
  3. If this method returns a not-null value, then processing along the usual route is not performed and the client will receive exactly the value that this method returned.

Why such a handler might be useful?

The fact is that in many route handlers the programmer will be forced to write the same standard apikey (or ACCESS TOKEN) verification code and issue standard answers if an authorization violation is detected.

For example, if apikey is incorrect, then return the value "401" (Unauthorized), if the apikey is correct, but its validity has expired, then we will return some other error code.

And thus, the same code is repeated many times in many route handlers.
If introduce the universal http-request pre-processor that I propose, then the apikey verification code will be written only one time in this pre-processor, which will save the programmer from annoying duplicating the code.

with respect
Timur

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.