GithubHelp home page GithubHelp logo

unosquare / embedio Goto Github PK

View Code? Open in Web Editor NEW
1.5K 65.0 175.0 14.13 MB

A tiny, cross-platform, module based web server for .NET

Home Page: http://unosquare.github.io/embedio

License: Other

C# 84.33% HTML 0.88% JavaScript 13.51% CSS 1.28%
mono webserver websocket dotnet dotnetcore websockets url-segment tiny routing-strategies embedded

embedio's Introduction

Analytics Build status Buils status NuGet version NuGet BuiltWithDotnet Slack

EmbedIO

⭐ Please star this project if you find it useful!

This README is for EmbedIO v3.x. Click here if you are still using EmbedIO v2.x.

Overview

A tiny, cross-platform, module based, MIT-licensed web server for .NET Framework and .NET Core.

  • Written entirely in C#, using our helpful library SWAN
  • Network operations use the async/await pattern: Responses are handled asynchronously
  • Multiple implementations support: EmbedIO can use Microsoft HttpListener or internal Http Listener based on Mono/websocket-sharp projects
  • Cross-platform: tested on multiple OS and runtimes. From Windows .NET Framework to Linux MONO.
  • Extensible: Write your own modules -- For example, video streaming, UPnP, etc. Check out EmbedIO Extras for additional modules
  • Small memory footprint
  • Create REST APIs quickly with the out-of-the-box Web API module
  • Serve static or embedded files with 1 line of code (also out-of-the-box)
  • Handle sessions with the built-in LocalSessionWebModule
  • WebSockets support
  • CORS support. Origin, Header and Method validation with OPTIONS preflight
  • HTTP 206 Partial Content support
  • Support Xamarin Forms
  • And many more options in the same package

EmbedIO 3.0 - What's new

The major version 3.0 includes a lot of changes in how the webserver process the incoming request and the pipeline of the Web Modules. You can check a complete list of changes and a upgrade guide for v2 users here.

Some usage scenarios:

  • Write a cross-platform GUI entirely using React/AngularJS/Vue.js or any Javascript framework
  • Write a game using Babylon.js and make EmbedIO your serve your code and assets
  • Create GUIs for Windows services or Linux daemons
  • Works well with LiteLib - add SQLite support in minutes!
  • Write client applications with real-time communication between them using WebSockets
  • Write internal web server for Xamarin Forms applications

Installation:

You can start using EmbedIO by just downloading the nuget.

Package Manager

PM> Install-Package EmbedIO

.NET CLI

> dotnet add package EmbedIO

Usage

Working with EmbedIO is pretty simple, check the follow sections to start coding right away. You can find more useful recipes and implementation details in the Cookbook.

WebServer Setup

Please note the comments are the important part here. More info is available in the samples.

namespace Unosquare
{
    using System;
    using EmbedIO;
    using EmbedIO.WebApi;

    class Program
    {
        /// <summary>
        /// Defines the entry point of the application.
        /// </summary>
        /// <param name="args">The arguments.</param>
        static void Main(string[] args)
        {
            var url = "http://localhost:9696/";
            if (args.Length > 0)
                url = args[0];

            // Our web server is disposable.
            using (var server = CreateWebServer(url))
            {
                // Once we've registered our modules and configured them, we call the RunAsync() method.
                server.RunAsync();

                var browser = new System.Diagnostics.Process()
                {
                    StartInfo = new System.Diagnostics.ProcessStartInfo(url) { UseShellExecute = true }
                };
                browser.Start();
                // Wait for any key to be pressed before disposing of our web server.
                // In a service, we'd manage the lifecycle of our web server using
                // something like a BackgroundWorker or a ManualResetEvent.
                Console.ReadKey(true);
            }
        }
	
	// Create and configure our web server.
        private static WebServer CreateWebServer(string url)
        {
            var server = new WebServer(o => o
                    .WithUrlPrefix(url)
                    .WithMode(HttpListenerMode.EmbedIO))
		 // First, we will configure our web server by adding Modules.
                .WithLocalSessionManager()
                .WithWebApi("/api", m => m
                    .WithController<PeopleController>())
                .WithModule(new WebSocketChatModule("/chat"))
                .WithModule(new WebSocketTerminalModule("/terminal"))
                .WithStaticFolder("/", HtmlRootPath, true, m => m
                    .WithContentCaching(UseFileCache)) // Add static files after other modules to avoid conflicts
                .WithModule(new ActionModule("/", HttpVerbs.Any, ctx => ctx.SendDataAsync(new { Message = "Error" })));

            // Listen for state changes.
            server.StateChanged += (s, e) => $"WebServer New State - {e.NewState}".Info();

            return server;
        }
    }
}

Reading from a POST body as a dictionary (application/x-www-form-urlencoded)

For reading a dictionary from an HTTP Request body inside a WebAPI method you can add an argument to your method with the attribute FormData.

    [Route(HttpVerbs.Post, "/data")]
    public async Task PostData([FormData] NameValueCollection data) 
    {
        // Perform an operation with the data
        await SaveData(data);
    }

Reading from a POST body as a JSON payload (application/json)

For reading a JSON payload and deserialize it to an object from an HTTP Request body you can use GetRequestDataAsync. This method works directly from IHttpContext and returns an object of the type specified in the generic type.

    [Route(HttpVerbs.Post, "/data")]
    public async Task PostJsonData() 
    {
        var data = HttpContext.GetRequestDataAsync<MyData>();
	
        // Perform an operation with the data
        await SaveData(data);
    }

Reading from a POST body as a FormData (multipart/form-data)

EmbedIO doesn't provide the functionality to read from a Multipart FormData stream. But you can check the HttpMultipartParser Nuget and connect the Request input directly to the HttpMultipartParser, very helpful and small library.

A sample code using the previous library:

    [Route(HttpVerbs.Post, "/upload")]
    public async Task UploadFile()
    {
        var parser = await MultipartFormDataParser.ParseAsync(Request.InputStream);
        // Now you can access parser.Files
    }

There is another solution but it requires this Microsoft Nuget.

Writing a binary stream

You can open the Response Output Stream with the extension OpenResponseStream.

    [Route(HttpVerbs.Get, "/binary")]
    public async Task GetBinary() 
    {
	// Call a fictional external source
	using (var stream = HttpContext.OpenResponseStream())
                await stream.WriteAsync(dataBuffer, 0, 0);
    }

WebSockets Example

Working with WebSocket is pretty simple, you just need to implement the abstract class WebSocketModule and register the module to your Web server as follow:

server..WithModule(new WebSocketChatModule("/chat"));

And our web sockets server class looks like:

namespace Unosquare
{
    using EmbedIO.WebSockets;

    /// <summary>
    /// Defines a very simple chat server.
    /// </summary>
    public class WebSocketsChatServer : WebSocketModule
    {
        public WebSocketsChatServer(string urlPath)
            : base(urlPath, true)
        {
            // placeholder
        }

        /// <inheritdoc />
        protected override Task OnMessageReceivedAsync(
            IWebSocketContext context,
            byte[] rxBuffer,
            IWebSocketReceiveResult rxResult)
            => SendToOthersAsync(context, Encoding.GetString(rxBuffer));

        /// <inheritdoc />
        protected override Task OnClientConnectedAsync(IWebSocketContext context)
            => Task.WhenAll(
                SendAsync(context, "Welcome to the chat room!"),
                SendToOthersAsync(context, "Someone joined the chat room."));

        /// <inheritdoc />
        protected override Task OnClientDisconnectedAsync(IWebSocketContext context)
            => SendToOthersAsync(context, "Someone left the chat room.");

        private Task SendToOthersAsync(IWebSocketContext context, string payload)
            => BroadcastAsync(payload, c => c != context);
    }
}

Support for SSL

Both HTTP listeners (Microsoft and Unosquare) can open a web server using SSL. This support is for Windows only (for now) and you need to manually register your certificate or use the WebServerOptions class to initialize a new WebServer instance. This section will provide some examples of how to use SSL but first a brief explanation of how SSL works on Windows.

For Windows Vista or better, Microsoft provides Network Shell (netsh). This command line tool allows to map an IP-port to a certificate, so incoming HTTP request can upgrade the connection to a secure stream using the provided certificate. EmbedIO can read or register certificates to a default store (My/LocalMachine) and use them against a netsh sslcert for binding the first https prefix registered.

For Windows XP and Mono, you can use manually the httpcfg for registering the binding.

Using a PFX file and AutoRegister option

The more practical case to use EmbedIO with SSL is the AutoRegister option. You need to create a WebServerOptions instance with the path to a PFX file and the AutoRegister flag on. This options will try to get or register the certificate to the default certificate store. Then it will use the certificate thumbprint to register with netsh the FIRST https prefix registered on the options.

Using AutoLoad option

If you already have a certificate on the default certificate store and the binding is also registered in netsh, you can use Autoload flag and optionally provide a certificate thumbprint. If the certificate thumbprint is not provided, EmbedIO will read the data from netsh. After getting successfully the certificate from the store, the raw data is passed to the WebServer.

Related Projects and Nugets

Name Author Description
Butterfly.EmbedIO Fireshark Studios, LLC Implementation of Butterfly.Core.Channel and Butterfly.Core.WebApi using the EmbedIO server
embedio-cli Unosquare A dotnet global tool that enables start any web folder or EmbedIO assembly (WebAPI or WebSocket) from command line.
EmbedIO.BearerToken Unosquare Allow to authenticate with a Bearer Token. It uses a Token endpoint (at /token path) and with a defined validation delegate create a JsonWebToken. The module can check all incoming requests or a paths
EmbedIO.LiteLibWebApi Unosquare Allow to expose a sqlite database as REST api using EmbedIO WebApi and LiteLib libraries
EmbedIO.OWIN Unosquare EmbedIO can use the OWIN platform in two different approach: You can use EmbedIO as OWIN server and use all OWIN framework with EmbedIO modules.
Microsoft.AspNetCore.Server.EmbedIO Dju EmbedIO web server support for ASP.NET Core, as a drop-in replacement for Kestrel
SambaFetcher nddipiazza A .NET tool to connect a web server with Samba

Special Thanks

YourKit

To YourKit for supports open source projects with its full-featured .NET Profiler, an amazing tool to profile CPU and Memory!

embedio's People

Contributors

alhimik45 avatar benny856694 avatar bufferunderrun avatar chyyran avatar dependabot-preview[bot] avatar desistud avatar duddo avatar edmundormz avatar geoperez avatar greciaveronica avatar israelramosm avatar jpalcala avatar jtol84 avatar k3z0 avatar kadosh avatar kuyoska avatar lwalejko avatar marcolpr avatar marcuswichelmann avatar mariodivece avatar marner2 avatar perkio avatar rdeago avatar scobie avatar serk352 avatar splamy avatar srad avatar thomaspiskol avatar unknown6656 avatar vbhavsar-alchemysystems avatar

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

embedio's Issues

Update NuGet Package

Self-explanatory. I realized the NuGet Package is not up to date because the RunAsyc method does not return a task but in the code it does.

Samples not running on NETFX 4.5.2/4.6 - Missing sqlite3

An unhandled exception of type 'System.DllNotFoundException' occurred in Microsoft.Data.Sqlite.dll

Additional information: Unable to load DLL 'sqlite3': The specified module could not be found. (Exception from HRESULT: 0x8007007E)

How do I map url to static folder?

If I start web server with url http://+:8000/ and some static folder, say D:\web, request to http://127.0.0.1:8000/ maps to D:\web\Index.html. If I start web server with url http://+:8000/SomeName/, request to http://127.0.0.1:8000/SomeName/ maps to D:\web\SomeName\Index.html. How can I configure server that url part which is supplied in constructor is not included in static file path? I.e. If I start webserver on http://+:8000/SomeName/ and static folder is D:\web, request to http://+:8000/SomeName/ would map to D:\web\index.html

support nullable parameter in the regexp engine

Hi,

my goal is to support optional parameter by the new regexp routing engine. With this
patch, you can now declare a nullable parameter :

            [WebApiHandler(HttpVerbs.Get, RelativePath + "people/{id}")]
            public bool GetPeople(WebServer server, HttpListenerContext context, int? id)
            {
                // when GET /people/, id will be forced to null and not throwing exception
                if (id == null) {
                    return context.JsonResponse(People.All());
                }

                // else use the actual parsing and cast the int to id.
                else  {
                    return context.JsonResponse(People.FirstOrDefault(p => p.Key == id));
                }
            }

It works but has some limits :

  1. when GET /people the regexp doesn't match the right url due to the fact that NormalizeRegexPath() doesn't know if a param is optional (nullable) and slash need to be ignored..
  2. only the last nullable parameter is supported, same case as (1).

Non en_US locale can cause ArgumentException on any request.

I got ArgumentException with any http request, the stack trace is:

2015-11-27 16:08:16.704 System.ArgumentException: 지정한 값에 잘못된 제어 문자가 있습니다.
매개 변수 이름: value
위치: System.Net.WebHeaderCollection.CheckBadChars(String name, Boolean isHeaderValue)
위치: System.Net.WebHeaderCollection.SetInternal(String name, String value)
위치: Unosquare.Labs.EmbedIO.Extensions.NoCache(HttpListenerContext context)
위치: Unosquare.Labs.EmbedIO.Modules.WebApiModule.<.ctor>b__2_0(WebServer server, HttpListenerContext context)
위치: Unosquare.Labs.EmbedIO.WebServer.ProcessRequest(HttpListenerContext context)

The cause is

    context.Response.AddHeader(Constants.HeaderLastModified,
            DateTime.UtcNow.ToString(Constants.BrowserTimeFormat));

in Extensions.NoCache() method.

With Korean locale, ToString("ddd, dd MMM yyyy HH:mm:ss 'GMT'") generates non-ASCII characters.
The token "ddd" is the problem.

Workaround: change the locale on program startup, like

var culture = CultureInfo.CreateSpecificCulture("en-US");
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;

I wish this problem is fixed for non-english users in the world~ :)

Including source to project leads StaticFilesModules HttpListenerException when serving static file

When I use the nuget package everything is fine, but when I use the source and add it to my project I get the following exception service a static file, but it still works:

System.Net.HttpListenerException ist aufgetreten.
  HResult=-2147467259
  Message=Der E/A-Vorgang wurde wegen eines Threadendes oder einer Anwendungsanforderung abgebrochen
(The I/O process was canceled because of a thread ending or an application request)
  Source=System
  ErrorCode=995
  NativeErrorCode=995
  StackTrace:
       bei System.Net.HttpResponseStream.Write(Byte[] buffer, Int32 offset, Int32 size)
       bei Unosquare.Labs.EmbedIO.Modules.StaticFilesModule.HandleGet(HttpListenerContext context, WebServer server, Boolean sendBuffer) in c:\...\embedio\Unosquare.Labs.EmbedIO\Modules\StaticFilesModule.cs:Zeile 277.
  InnerException:

2015-05-12_19h20_44

I have 3 Projects now:

  1. embedio Checkout
  2. WebService (adds embedio)
  3. My App (adds WebService)

I'm adding the controllers via a anonymous function, could that be the issue?, Pseudocode:

public WebWorker(Action<WebServer> doStuff = null)
{
    this.doStuff = doStuff;
    ...
}

...

private void DoWorkHandler(object sender, DoWorkEventArgs e)
{
    using (var server = new WebServer(Url))
    {
        server.RegisterModule(new StaticFilesModule(e.Argument.ToString()));
        server.RegisterModule(new WebApiModule());

        if (doStuff != null)
        {
            doStuff(server);
        }
    ...
}

In the App:

...
WebWorker = new WebService.WebWorker((Unosquare.Labs.EmbedIO.WebServer server) =>
    {
        server.Module<Unosquare.Labs.EmbedIO.Modules.WebApiModule>().RegisterController<Controller.StorageController>();
        server.Module<Unosquare.Labs.EmbedIO.Modules.WebApiModule>().RegisterController<Controller.LogController>();
    });
...

Any idea?

context.UserEndPoint - NullReferenceException

I get error when try to read context.UserEndPoint in OnClientDisconnected() and sometimes in OnMessageReceived() hooks

in Unosquare.Net.HttpConnection.get_RemoteEndPoint() in C:\Unosquare\embedio\src\Unosquare.Labs.EmbedIO\System.Net\HttpConnection.cs: 140
in Unosquare.Net.WebSocketContext.get_UserEndPoint() in C:\Unosquare\embedio\src\Unosquare.Labs.EmbedIO\System.Net\WebSocketContext.cs: 233

Client is local, connecting from the same computer

embedio version 1.4.6.

Tasks not handled correctly

I suggest you guys read this blog and change WebServer.RunAsync accordingly: http://blog.stephencleary.com/2014/05/a-tour-of-task-part-1-constructors.html

  • Use Task.Run instead of Task.Factory.StartNew, the latter shouldn't be used hardly ever
  • Support cancellation better
    • currently inelegantly handled via clientSocketTask.Wait(ct)
    • pass a CancellationTokenSource argument instead of CancellationToken
    • Check source.Token.IsCancellationRequested inside the while loop
    • Catch/throw OperationCanceledException instead of AggregateException
      *1 Use async/await if .NET 4.5 is present, allowing your RunAsync method to work concurrently as well as in parallel

We are the same developers that gave you the support initially (@marner2, @faeriedust, @Joe0), but we ourselves found out over time we were not doing it right either.

Basic example doesn't serve static files

I just installed the nuget package and created a simple index.html file an placed it under c:/web/index.html and used the first code example to get the index.html served within the web browser (which automatically opens given the demo code) and it fails because the server is not reachable.

Could you try if the code is actually working, or is anything else required here?

Allow for sessions cookies to be 'root only'

The session module will create session cookies whenever it finds no matching cookie for the requested path.
These cookies will e create for the path that has been requested.
e.g. If the request is for http://www.sample.com/foo/bar the cookie path will be something like '/foo/bar'.

This behaviour can lead to a single use having multiple vaild session cookies at once.

If a user has no valid cookie for a site and requests the path '/foo/bar', a cookie for that path will be created.
If the user then requests the path '/bar/foo', another cookie will be created, since the path of the previous one does not match.

Maintaining sessions is quite a task with mutliple sessions per path per user.

To solve issues like this, cookies can be created for the root path of a server exclusively.
This way a user will get a single cookie that is valid for the whole domain.

I'd therefore suggest to add an UseRootPathOnly property to the session module:
In ISessionWebModule.cs after line 56 insert:

bool UseRootPathOnly { get; set; }

In LocalSessionModule.cs after line 192 insert:

public bool UseRootPathOnly { get; set; }

In LocalSessionModule.cs replace line 37 with:

var sessionCookie = (UseRootPathOnly ? new Cookie(SessionCookieName, sessionId, "/") : new Cookie(SessionCookieName, sessionId));

or

Cookie sessionCookie;
if(UseRootPathOnly)
  sessionCookie = new Cookie(SessionCookieName, sessionId, "/");
else
  sessionCookie = new Cookie(SessionCookieName, sessionId);

Websocket error 404

It throws 404 when trying to access web socket examples.

2015-11-08 06:05:07,838 [12] ERROR Unosquare.Labs.EmbedIO.Samples.Program No module generated a response. Sending 404 - Not Found

Windows 8.1 Store App

Hey,
It seems that unfortunately HTPPListener is not supported on Windows 8.1 Store Apps. And no 'Capability' can solve the problem. However, I believe that socket listening is available on Windows 8.1 Store Apps tho using StreamSocketListener class. I think this is true for Windows Phone as well.

So next to .Net Core, we need to have an HTTPListener port for Windows 8.1 Store Apps, Windows 8.1 Phone Apps and Windows 8 Phone Apps.

Fix Samples Solution

Currently the Samples solution looks bad and it is kind of broken. Please fix.

Incorrect handling when URL does not contain a trailing slash.

I configured a basic static server to test EmbedIO and I found that certain pages would not load depending on if the URL contained a trailing slash or not.

For instance, assume that I'm serving files from C:\temp\ and that C:\temp\xyz\ is a subfolder that exists with an index.html file inside of it.

If I have a link that points to href="/xyz/" the page loads. However, if the link points to href="/xyz" then a 404 error is returned.

Large static file cause 'Out Of Memory' error

I just use this project for servering a large archive(1.2GB), but it raises an 'Out Of Memory' error.

500 - Internal Server Error
Message

Failing module name: Static Files Module
System.OutOfMemoryException

Stack Trace

     System.IO.File.InternalReadAllBytes(String path, Boolean checkHost)
     Unosquare.Labs.EmbedIO.Modules.StaticFilesModule.HandleGet(HttpListenerContext context, WebServer server, Boolean sendBuffer)  C:\Unosquare\embedio\Unosquare.Labs.EmbedIO\Modules\StaticFilesModule.cs: 281
     Unosquare.Labs.EmbedIO.Modules.StaticFilesModule.<.ctor>b__37_1(WebServer server, HttpListenerContext context)  C:\Unosquare\embedio\Unosquare.Labs.EmbedIO\Modules\StaticFilesModule.cs: 187
     Unosquare.Labs.EmbedIO.WebServer.ProcessRequest(HttpListenerContext context)  C:\Unosquare\embedio\Unosquare.Labs.EmbedIO\WebServer.cs: 351

how can i solve it?

Random port selection demo

I just wanted to share with you a random port choosing demo that I created for myself. Perhaps you will want to build this feature into your project one day, but the example below is working fine for me and it may serve as a helpful example for other readers.

The wrapper class (StaticWebServer) is shown last. Here is how I now use it from my WinForms demo:

[STAThread]
static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    using (var webServer = new StaticWebServer())
    {
        webServer.RunAsync();
        Application.Run(new Views.MainView(webServer.UsingBaseAddress));
    }
}

...and in the main view:

public MainView(string webViewUrl = "about:blank")
{
    InitializeComponent();
    browser.Url = new Uri(webViewUrl);
    mainStatusLabel.Text = "";
}

And finally, here is the StaticWebServer class, which chooses the port to use dynamically:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unosquare.Labs.EmbedIO;
using Unosquare.Labs.EmbedIO.Log;
using Unosquare.Labs.EmbedIO.Modules;

namespace YourOwnNamespace
{
    class StaticWebServer : IDisposable
    {
        public StaticWebServer() { }

        static Random _portGenerator = new Random();
        static List<int> _portsUsed = new List<int>();

        /// <summary>
        /// An instance of the (awesome!) EmbedIO WebServer.
        /// </summary>
        WebServer _server;

        /// <summary>
        /// String format template to merge the randomly generated port into.
        /// Default: "http://127.0.0.1:{0}/"
        /// </summary>
        public string BaseAddressTemplate { get; set; } = "http://127.0.0.1:{0}/";
        public int PortRangeMin { get; set; } = 51001;
        public int PortRangeMax { get; set; } = 65001;
        /// <summary>
        /// Relative or absolute path to serve static files from.
        /// </summary>
        public string RootFilesystemPath { get; set; } = "browse";

        /// <summary>
        /// The base address currently being used by the server.
        /// </summary>
        public string UsingBaseAddress { get; private set; }
        /// <summary>
        /// The port currently being used by the server.
        /// </summary>
        public int UsingPort { get; private set; }
        /// <summary>
        /// The root filesystem path currently being used by the server.
        /// </summary>
        public string UsingRootFilesystemPath { get; private set; }

        WebServer CreateServer(string baseAddress, string rootPath)
        {
            var logger = new DebugLogger();
            var server = new WebServer(baseAddress, logger);

            var headers = new Dictionary<string, string>()
            {
#if DEBUG
                // The following is mostly useful for debugging.
                { Constants.HeaderCacheControl, "no-cache, no-store, must-revalidate" },
                { Constants.HeaderPragma, "no-cache" },
                { Constants.HeaderExpires, "0" }
#endif
            };

            var staticFileMod = new StaticFilesModule(rootPath, headers);
            staticFileMod.DefaultExtension = ".html";
            server.RegisterModule(staticFileMod);

            return server;
        }

        string GetAbsoluteRootDirectoryPath()
        {
            if (Path.IsPathRooted(RootFilesystemPath))
                return RootFilesystemPath;
            var baseDir = Path.GetDirectoryName(
                System.Reflection.Assembly.GetEntryAssembly()
                .Location);
            return Path.Combine(baseDir, RootFilesystemPath);
        }

        public void RunAsync()
        {
            UsingRootFilesystemPath = GetAbsoluteRootDirectoryPath();
            Debug.Print("Serving static files from: {0}", UsingRootFilesystemPath);

            // Random port selection adapted from http://stackoverflow.com/a/223188/16387
            UsingPort = -1;
            UsingBaseAddress = null;
            while (true)
            {
                UsingPort = _portGenerator.Next(PortRangeMin, PortRangeMax);
                if (_portsUsed.Contains(UsingPort))
                    continue;

                UsingBaseAddress = String.Format(BaseAddressTemplate, UsingPort.ToString());
                _server = CreateServer(UsingBaseAddress, UsingRootFilesystemPath);
                try
                {
                    _server.RunAsync();
                } catch (System.Net.HttpListenerException)
                {
                    _server.Dispose();
                    _server = null;
                    continue;
                }
                _portsUsed.Add(UsingPort);
                break;
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }

        void Dispose(bool disposing)
        {
            if (!disposing)
                return;

            var server = _server;
            _server = null;

            if (server == null)
                return;
            server.Dispose();
        }

        #region DebugLogger

        /// <summary>
        /// Provides a simple logger for Debug output.
        /// </summary>
        class DebugLogger : ILog
        {
            private static void WriteLine(string format, params object[] args)
            {
                var d = DateTime.Now;
                var dateTimeString = string.Format("{0}-{1}-{2} {3}:{4}:{5}.{6}",
                    d.Year.ToString("0000"), d.Month.ToString("00"), d.Day.ToString("00"), d.Hour.ToString("00"),
                    d.Minute.ToString("00"), d.Second.ToString("00"), d.Millisecond.ToString("000"));

                format = dateTimeString + "\t" + format;

                if (args != null)
                    Debug.Print(format, args);
                else
                    Debug.Print(format);
            }

            public virtual void Info(object message)
            {
                InfoFormat(message.ToString(), null);
            }

            public virtual void Error(object message)
            {
                ErrorFormat(message.ToString(), null);
            }

            public virtual void Error(object message, Exception exception)
            {
                ErrorFormat(message.ToString(), null);
                ErrorFormat(exception.ToString(), null);
            }

            public virtual void InfoFormat(string format, params object[] args)
            {
                WriteLine(format, args);
            }

            public virtual void WarnFormat(string format, params object[] args)
            {
                WriteLine(format, args);
            }

            public virtual void ErrorFormat(string format, params object[] args)
            {
                WriteLine(format, args);
            }

            public virtual void DebugFormat(string format, params object[] args)
            {
                WriteLine(format, args);
            }
        }

        #endregion
    }
}

SimpleConsoleLogger not asynchronous

The SimpleConsoleLogger is completely synchronous. The calls to the console are degrading the performance of the request-response cycles. Pick a thread from the thread pool and use a ConcurrentQueue (https://msdn.microsoft.com/en-us/library/dd267265%28v=vs.110%29.aspx) or use other means such as a BlockingCollection as described here (http://stackoverflow.com/questions/3670057/does-console-writeline-block)

Please note that the above StackOverflow answer is not using a ThredPool Thread. Use a Thread Pool Thread instead.

Problem with MVC application

I have a mvc website and i would like to embed it with embedio...
I tried above code but no luck.

screenshot_4

Unosquare.Labs.EmbedIO.WebServer server = new Unosquare.Labs.EmbedIO.WebServer(port, new Unosquare.Labs.EmbedIO.Log.SimpleConsoleLog());
server.RegisterModule(new Unosquare.Labs.EmbedIO.Modules.LocalSessionModule());
server.RegisterModule(new Unosquare.Labs.EmbedIO.Modules.StaticFilesModule(path));
server.Module<Unosquare.Labs.EmbedIO.Modules.StaticFilesModule>().UseRamCache = true;
server.RunAsync();

embedio set read lock in static files

Hi,

I don't know if it's a bug or a feature but since i upgrade from v1.0.17 to v1.1.0, the statics files served by embedio are read lock.

In the past version, i used to modify html, css or js file while embedio were working and just refresh browser to view result. Now, it's not possible due to some lock set by embedio. I'm forced to rexec the process and then refresh browser which is a killer workflow.

Unable to access twice to request.InputStream

How to reproduce (in the ResApiSample.cs) :

            [WebApiHandler(HttpVerbs.Post, RelativePath + "people/*")]
            public async Task<bool> PostPeople(WebServer server, HttpListenerContext context)
            {
                    ...
                    var post = context.RequestFormData(); // OK
                    var model = context.ParseJson<GridDataRequest>(); // will be null
                    ...
            }

OR

            [WebApiHandler(HttpVerbs.Post, RelativePath + "people/*")]
            public async Task<bool> PostPeople(WebServer server, HttpListenerContext context)
            {
                    ...
                    var model = context.ParseJson<GridDataRequest>(); // OK
                    var post = context.RequestFormData(); // will be null
                    ...
            }

The problem come from the underlying inputStream : when StreamReader is disposed, it lets the cursor position to the end. So the next call to StreamReader(inputStream) will be null.

         using (var body = request.InputStream) {
                using (var reader = new StreamReader(body, request.ContentEncoding)) {
                    var stringData = reader.ReadToEnd();

                    if (string.IsNullOrWhiteSpace(stringData)) return null;
                    return stringData.Split('&')
                        .ToDictionary(c => c.Split('=')[0],
                            c => WebUtility.UrlDecode(c.Split('=')[1]));
                }
            }

I've tried to use Read(pos, len) or force body.Position = 0 but this stream cannot be seek or written. Same problem using a MemoryStream and CopyTo... Do you have an idea to fix it ?

Strong Name the nuget assembly

Can the nuget package be updated to contain a strong named assembly, please? My main code needs to be strong named and therefore this is a blocker for me to use the embedio lib.

Add PCL Compatibility for Custom Modules

Hey again,
Unfortunately, because of using System.Net.HttpListenerContext in "ResponseHandler" delegate for new 'Map's it is now impossible to write custom and also portable modules for the WebServer.

We should probably contain that class in a local type so we can access it.

Implement Deep Object Parsing for ParseFormData Extension Method

I'm using a bootstrap-select2 to support ajax tagging on my webapp.
The underlying html is
<select name="tags" multiple="multiple"></select>

When form is submitted, browser send multiple values for the same input name.
The exception is thrown by ParseFormData() which is a Dictionary<string, string>.

Do you plan to support multiple values ?

Thanks

WebSockets Support on Windows 7 and Mono 3.x

Hi,

It's not an issue or pr, just asking question. As i don't know how to contact you (a mailing list ?) let's do this here. Feel free to delete if it is not appropriate.

Before Embedio, i used the websocket-sharp project to serve websocket in my app. Yesterday, i've just understood that the WebsocketModule provide by Embebio is using the Microsoft http.sys and so, starts with Windows 8 and higher. Little punch in my face as my dev env is always in Win7 (yes, i hate the win8&10 UI) and my app have to support Win7.

According to https://msdn.microsoft.com/fr-fr/library/system.net.websockets.websocket

The classes and class elements in the N:System.Net.WebSockets namespace that are supported on Windows 7, Windows Vista SP2, and Windows Server 2008 are abstract classes. This allows an application developer to inherit and extend these abstract classes with an actual implementation of client WebSockets.

Do you known if it exists an implementation of this class or at least, if the websocket-sharp can be integrated in Embedio with little effort to support websocket for Win7 ?

Thanks for your answer.

Upgrade error to embedio v1.2.5 with nuget

Hi,

I can't upgrade to the last v1.2.5 release from nuget due to a dependency error :

Attempting to resolve dependency 'Newtonsoft.Json (≥ 9.0.1)'.
'EmbedIO' already has a dependency defined for 'System.Security.Cryptography.Primitives'.

The regression seems to have been introduced in the commit 0e333d0?diff=split

Escaping issue in Extension.RequestFormData()

Hi,

i've found a error in the way Extension.RequestFormData() is escaping characters, in particular the "plus" sign.

In one of my forms, i use an ajax call with jQuery to send data to a RestApi Controller. When jQuery calls $form.serialize(), it encodes space as "plus" sign. Example : field1=value1withoutspace&field2=value2+with+spaces.

The problem is that the Uri.UnescapeDataString() do not correctly handle the "plus" sign as a space but as a "plus" sign... You can see more details to blog msdn or rick strahl blog.

A workaround consist of using WebUtility.UrlDecode and so you can see a proposal patch below.

--- C:\www\embedio-master\Unosquare.Labs.EmbedIO\Extensions.cs.
+++ C:\www\embedio-master\Unosquare.Labs.EmbedIO\Extensions.cs
@@ -281,7 +281,7 @@

                     return stringData.Split('&')
                         .ToDictionary(c => c.Split('=')[0],
-                            c => Uri.UnescapeDataString(c.Split('=')[1]));
+                            c => WebUtility.UrlDecode(c.Split('=')[1]));
                 }
             }
         }

Thanks,

Implement DeleteSession method in LocalSessionModule

Hi,

I am facing an issue (not really a bug) with the LocalSessionModule and i will trying to explain clearly, not easy.

  1. When a user connects to my app (ie: index.html), Embedio always starts a session (in memory) and so a cookie "__session" is created in his browser. No problem.
  2. Then, the user login and i add some extras infos to check security (roles based) to the embedio session (session.Data.TryAdd("user-roles", new List<String>() { "admin", "debug" })). No problem.
  3. At the end, the user logout and because there is no "DeleteSesssion()" method, i simply delete all extras infos i've added with session.Data.Clear(). The session in Embedio is always enabled but do no rely on extras infos i used to check securty (this user is not admin anymore). A new session is started for the user, new session in Embedio and the browser get a new "__session" AND the old one. No problem.
  4. BUT, when the user refresh his browser, the 2 "__session" cookies are send (";" separed) and Embedio take the first valid, the old one because always in memory and not the new...

Could you add a new method DeleteSession(SessionInfo session) in the LocalSessionModule to properly delete a session in embedio ?

Thanks.

Not able to set NullLog!

I did install from NuGet (1.4.6).
In this binary, there's no way to set NullLog as ILog in Webserver constructor.
Looking at the source code, you're using conditional tag for compilation.
Is there any reason for that?
Is it safe just download all source code and set COMPAT in "Conditional compilation symbols" at build properties?

staticfilemodule: possible invalid char exception

Cause:
the call to " context.Response.AddHeader(Constants.HeaderLastModified, utcFileDateString);" will trigger invalid char exception if running on non-english locale, since 'utcFileDataString' possibly has invalid chars (Chinese character in my case).

Possible fix:
var utcFileDateString = fileDate.ToUniversalTime().ToString(Constants.BrowserTimeFormat, CultureInfo.InvariantCulture) - add InvariantCulture to the format function.

Thanks for the great project.

Async Controller Actions

Since the controller actions are not tasks I wonder if you have another general notion on how to wait for I/O code in actions to complete?

Currently I'm using task.wait() for a task to complete, which doesn't seem to be 100% working, I need to debug a few cases yet. However, can I declare actions as Tasks?

Rename RegEx to Regex

The more common term is Regex rather than RegEx. Please rename these in the source, readme, documentation, and samples. Thanks and sorry for causing the extra work.

How to control caching headers?

Is there an easy way to tell the static file server to set all the proper headers to disable browser cache for every request?

For instance:

Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0

Failing Local Session Module because of KeyNotFoundException

I keep seeing the following error.

Failing module name: Local Session Module
The given key was not present in the dictionary.

Stack Trace:

System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
   at System.Collections.Concurrent.ConcurrentDictionary`2[TKey,TValue].get_Item (System.Collections.Concurrent.TKey key) in /private/tmp/source-mono-mac-4.2.0-branch/bockbuild-mono-4.2.0-pre2-branch/profiles/mono-mac-xamarin/build-root/mono-4.2.0/external/referencesource/mscorlib/system/collections/Concurrent/ConcurrentDictionary.cs:line 955
   at Unosquare.Labs.EmbedIO.Modules.LocalSessionModule.<.ctor>b__4_0 (Unosquare.Labs.EmbedIO.WebServer server, System.Net.HttpListenerContext context) in <filename unknown>:line 0
   at Unosquare.Labs.EmbedIO.WebServer.ProcessRequest (System.Net.HttpListenerContext context) in <filename unknown>:line 0

Wildcards only supported at the end of a URL?

Correct me if I'm mistaken, but it seems like the "*" wildcard is only supported at the end of a URL rather than anywhere within it. Is this intentional? Looking through the documentation, I don't see any examples of a wildcard not at the end of a URL. Some of my endpoints require a wildcard in the middle of a URL, and I'm sure other developers have the same requirements.

StaticFilesModule - Check for byte range requests before reading the entire file

Line 251, by the time the check for byte range requests is performed, the entire file has already been read which could potentially be a very large file. Byte range requests should not use RAM caches either. Additionally, the response must contain a 206 Partial content header.

Make sure you use a FileStream with shared read access so the file is not locked when it is being read.

.Net 2.0

Hello,

is it possible (with reasonable amount of work) to compile embedio against .NET 2.0.
I would like to use it within Unity3D.
Or could you tell me which modules I could remove to compile it against .Net 2.0 (I'm just using the "WebApiModule"-Module).

kind regards

Support file upload

Hi,

Do you have a code snippet to handle file uploading from a html form <input type="file" /> ?

Thanks,

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.