Hi, is there an example of how to implement the HttpFileDispatcher in a .Net server? I want to build a small middleware server that feeds html and javascript files back to the browser.
Thanks,
Thom
Hi, is there an example of how to implement the HttpFileDispatcher in a .Net server? I want to build a small middleware server that feeds html and javascript files back to the browser.
Thanks,
Thom
Hello
You can either take a look at the HttpFileDispatcher
source code and use it a s a starting point. Or you can just use the HttpFileDispatcher
instance and hook its Request
event to provide required data.
Regards
Hi Anton,
So I found this code. The browser URL I am going to use is http://localhost:8099/pllogin.html. How would I change the below code to handle this URL:
static void Main(string[] args)
{
var serverChannel = new IpHttpServerChannel();
serverChannel.Port = 8099;
var dispatcher = new HttpFileDispatcher();
dispatcher.Path = "/pdf";
dispatcher.Request += Dispatcher_Request;
dispatcher.Server = serverChannel;
serverChannel.Open();
Console.WriteLine("Press ENTER to exit");
Console.ReadLine();
}
private static void Dispatcher_Request(object sender, RequestEventArgs e)
{
// Parse path here and read the corresponding PDF file contents
// then put that content into e.Data
e.Path = "file.pdf";
e.Data = new MemoryStream(File.ReadAllBytes(...here should be a path to a sample PDF file...));
}
// Thom
Hello
Unfortunately HttpFileDispatcher
cannot serve requests without path. This means that it can be configured to serve requests like http://localhost:8099/html/pllogin.html
, but it won’t allow to configure it for serve request like http://localhost:8099/pllogin.html
. Otherwise it would conflict with the Server Channel infrastructure.
Instead you could implement your own simple HTTP server (even with TLS/SSL support) using code like this one:
using System;
using System.IO;
using System.Net;
using RemObjects.SDK;
using RemObjects.SDK.Http;
using RemObjects.SDK.Server;
namespace SimpleHttpServer
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting server");
var server = new SimpleHttpServer();
server.Port = 8000;
server.RootPath = @"r:\Tutorials\Bootstrap"; // Path to the source folder
/*
// Enable TLS protection
server.SslOptions.UseTls = true;
server.SslOptions.Enabled = true;
server.SslOptions.CertificateFileName = @"..certificate file name ...";
*/
server.Open();
Console.WriteLine($"Server is listening to port {server.Port}");
Console.WriteLine("Press ENTER to stop the server");
Console.ReadLine();
server.Close();
}
}
public class SimpleHttpServer : HttpServer
{
public String RootPath { get; set; }
#region Overriden Methods
protected override void HandleHttpRequest(Connection connection, HttpServerRequest request, HttpServerResponse response)
{
base.HandleHttpRequest(connection, request, response);
if (response.ContentSource != ContentSource.ContentNone)
{
return;
}
if (request.Header.RequestType != "GET")
{
response.SendError(HttpStatusCode.BadRequest, String.Format("Request Type '{0}' not supported.", request.Header.RequestType));
return;
}
// TODO Check HttpFileDispatcher.TryServeFile for a requested file path check code
// Or just extract requested file from request.Header.RequestPath and serve it from some preloaded files cache
// This cache would contain a link between requested filename and file contents and its ContentType
String filename = this.RootPath + request.Header.RequestPath.Replace('/', Path.DirectorySeparatorChar);
if (filename.IndexOf("..", StringComparison.Ordinal) != -1)
{
response.SendError(HttpStatusCode.Forbidden, String.Format("Bad Request: Path '{0}' contains '..' which is invalid.", filename));
return;
}
if (!File.Exists(filename))
{
response.SendError(HttpStatusCode.NotFound, String.Format("File '{0}' not found.", filename));
return;
}
response.Header.ContentType = "text/html"; // This ContentType is hardcoded!
response.ContentStream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); // response.ContentBytes can be used here as well
response.CloseStream = true; /* Response will close stream once it's been sent */
}
#endregion
}
}
This sample code uses the same internal infrastructure as the IpHttpServerChannel
class
Thanks for the detailed answer.
I do not have a problem with changing the URL to something like http://localhost:8099/html/pllogin.html. I would like to use one of your template generated servers since it is easy to expand. For example I would also like to use HttpApi to receive a POST request which contains a JSON in the request body and returns a JSON. Also it is easy to switch from testing in GUI mode to running it as a service.
So with regards to previous code I can use this URL:
http://localhost:8099/html/pllogin.html
And change this to:
var dispatcher = new HttpFileDispatcher();
dispatcher.Path = “/html”;
dispatcher.Request += Dispatcher_Request;
dispatcher.Server = serverChannel;
But it is what to assign to the e.Path below that I am not sure about:
private static void Dispatcher_Request(object sender, RequestEventArgs e)
{
// Parse path here and read the corresponding PDF file contents
// then put that content into e.Data
e.Path = "file.pdf";
e.Data = new MemoryStream(File.ReadAllBytes(...here should be a path to a sample PDF file...));
}
// Thom
Sample code:
using System;
using System.IO;
using RemObjects.SDK.Helpers;
using RemObjects.SDK.Server;
namespace ROServer4
{
static class Program
{
public static int Main(string[] args)
{
ApplicationServer server = new ApplicationServer("ROServer4");
var channel = new IpHttpServerChannel();
var dispatcher = new HttpFileDispatcher();
dispatcher.Server = channel;
dispatcher.Path = "/html";
dispatcher.Request += Dispatcher_Request;
dispatcher.DefaultFile = "index.html";
server.NetworkServer.ServerChannel = channel;
server.NetworkServer.Port = 8090;
server.Run(args);
return 0;
}
private static void Dispatcher_Request(object sender, RequestEventArgs e)
{
e.Path = "index.html";
e.Data = new MemoryStream();
StreamHelpers.Utf8StringToStream("Test Value", e.Data);
e.Data.Position = 0;
}
}
}
The e.Path
is used to determine ContentType of the response. In other words dispatcher extracts file extension form e.Path and uses it to determine proper ContentType.
Predefined content types are:
“.xml” -> “text/xml”
.html
-> text/html
.htm
-> text/html
.js
-> application/x-javascript
.css
-> text/css
.jpg
-> image/jpeg
.jpeg
-> image/jpeg
.png
-> image/png
.pdf
-> application/pdf
.gz
-> application/x-gzip
.zip
-> application/zip
Unknown extensions are resolved into ContentType via Microsoft.Win32.Registry.ClassesRoot
registry node.
While this approach works for a file-based dispatcher, it might be an overkill for the case when Request
event handler is used to provide some generated data. So starting next Remoting SDK build it will allow to directly set ContentType in the Request
event handler
Regards
Why do I need this line which converts a string to a stream?
StreamHelpers.Utf8StringToStream(“Test Value”, e.Data);
I just want to read the html file and send it over as is. Can I do something like this?
var fileName = @“C:\Temp\pllogin.html”;
e.Data = new MemoryStream(File.ReadAllBytes(@fileName));
or
e.Data = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
// Thom