Click here to Skip to main content
Email Password   helpLost your password?

Introduction

Originally I was intrigued what it would take to write a simple webserver. But as I progressed I realized that a tiny web server could be quite useful for a number of applications that need to serve specialized web pages and where the overhead of writing an ASP.NET application is not warranted (or where it is not possible to host ASP.NET).

A good example is a news aggregator, which serves a single page containing the current news feeds. This is included as an example in this project.

Using the code

The core of the application is the class TinyServer. This class provides a simple web server that only supports GET requests (no forms) and serves web pages from a directory.

To run the sample webserver, you need to build the WebServer project and configure its App.Config file.

<appSettings>
    <add key="WebRoot" value="E:\src\DotNet\WebServer\root" />        
      <!-- location of the web pages to serve -->
    <add key="Default" value="default.html" />                        
      <!-- name of the default page -->
    <add key="TemplatePath" value="E:\src\DotNet\WebServer\html" />   
      <!-- location of special templates -->
    <add key="Port" value="81" />                                     
      <!-- Port to server on -->
    <add key="LogFile" value="" />                                    
      <!-- filepath, set to "" for console logging -->
    <add key="LogLevel" value="All" />                                
      <!--All, Warning, Error, None -->
</appSettings>
Once the webServer application starts it instantiates TinyServer and calls Run(). This spins off the server in a separate thread. Calling Stop() terminates the thread.

Building your own WebServer

Most likely you would want to build your own version of this webserver. You need to subclass TinyServer and then override the necessary functions. The most important to override is the method doGet() . In this method you can interrogate the GET command and send back anything that is necessary.

This is the default implementation:

protected virtual void doGet(string argument)
{
  string url = getUrl(argument);
  if (url.StartsWith("/"))
    url = url.Substring(1);
  if (url.Length == 0)
    url = defaultPageName;
    
  string path = Path.Combine(webRootPath, url);
  if (File.Exists(path))
  {
    sendOk();
    sendfile(path);
  }
  else
    sendError(404, "File Not Found");
}

To implement your version there are a number of utility functions at hand:

The RssAggregator Sample Application

To demonstrate this ability I have written a simple news aggregator that regularly downloads the RSS feeds from the sources.

The RssAggregator does two things:

  1. download and keep up to date a list of selected RSS Feeds
  2. run a web server that returns a web page containing the feed detail.

The first part uses the RssReader class created by smallguy78. It runs in its own thread and will download feeds once the current copy is older than one hour.

The second part is implemented by a subclass of TinyServer called AggServer. AggServer only ever returns one page that contains the newsfeeds abstracts and links to the articles. So the doGet() is pretty dumb:

protected override void doGet(string argument)
{
  this.sendOk();
  this.sendString(writeLinkPage());
}

The smarts to create the webpage is in the method writeLinkPage() which in turn relies on helper function RssReader.CreateHtml(). The whole example (excluding RssReader) just takes 80 lines of code.

Points of Interest

Acknowledgements to smallguy78 whose RssReader code I used. You can find more about it in this RSS Reader article .

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
QuestionTrying to use remoting on HTTPServerChannel
aarudra
5:50 3 Dec '09  
Hi,

I am trying to use your server in a test app i am experimenting with. The problem is that the test app has used remoting in it. A TCP channel is already registered in it and I was trying to register a HTTP channel for the tiny server. But it does not seem to work.

TinyServerLib.TinyServer server = null;
_url2 = "http://localhost:81/abc";
_channel2 = new HttpServerChannel(81);
ChannelServices.RegisterChannel(_channel2);

server = new TinyServerLib.TinyServer();
server.Templates = System.Configuration.ConfigurationSettings.AppSettings["TemplatePath"];
server.DefaultPage = System.Configuration.ConfigurationSettings.AppSettings["Default"];
server.WebRootPath = System.Configuration.ConfigurationSettings.AppSettings["WebRoot"];
server.Port = int.Parse(System.Configuration.ConfigurationSettings.AppSettings["WebServerPort"]);
server.LogFile = System.Configuration.ConfigurationSettings.AppSettings["LogFile"];
string logLevel = System.Configuration.ConfigurationSettings.AppSettings["LogLevel"].ToUpper();
switch (logLevel)
{
case "ALL": server.LogLevel = TinyServerLib.LogKind.Informational;
break;
case "WARNING": server.LogLevel = TinyServerLib.LogKind.Warning;
break;
case "ERROR": server.LogLevel = TinyServerLib.LogKind.Error;
break;
case "NONE": server.LogLevel = TinyServerLib.LogKind.None;
break;
}

RemotingServices.Marshal(server, _url2);

server.Run();
Where WebServerPort is 81 in App.config

I have also derived the tiny server class from MarshalByRefObject
public class TinyServer : MarshalByRefObject

However, I end up with the following exception
System.ArgumentNullException: No message was deserialized prior to calling the DispatchChannelSink.
Parameter name: requestMsg
at System.Runtime.Remoting.Channels.DispatchChannelSink.ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, IMessage& responseMsg, ITransportHeaders& responseHeaders, Stream& responseStream)
at System.Runtime.Remoting.Channels.BinaryServerFormatterSink.ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, IMessage& responseMsg, ITransportHeaders& responseHeaders, Stream& responseStream)
at System.Runtime.Remoting.Channels.SoapServerFormatterSink.ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, IMessage& responseMsg, ITransportHeaders& responseHeaders, Stream& responseStream)
at System.Runtime.Remoting.MetadataServices.SdlChannelSink.ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, IMessage& responseMsg, ITransportHeaders& responseHeaders, Stream& responseStream)
at System.Runtime.Remoting.Channels.Http.HttpServerTransportSink.ServiceRequest(Object state)
at System.Runtime.Remoting.Channels.SocketHandler.ProcessRequestNow()

I am new to remoting, so I don't have much idea about it. Am simply following what I gathered from various posts on forums.

The tiny server works like a charm when I try the example that you have provided.

Can you help?
Thanks.
Generalneed some modification
cooldeo18t
8:09 19 Jul '09  
I like this concept very much.
I am developing a installer for app, which is excepting only http protocol , hence I need to put my cab file on tiny webserver programatically, then i will ask api to make a call to http path.What i saw in your code was that the whole file dumped as raw stream without headers or mime type. how accomplish this.
I actually want to host a single cab for few seconds once my operation is done I want to stop web server.

interesting person to chat..........

Generalnot very secure
invader82
12:10 25 Feb '09  
Nice tiny webserver .... except

If I type http://localhost:81/c:\\test.txt,
I can read the test file c:\test.txt
so anybody can read almost any file on your computer
if you run this program!!! Smile

There should be some extra check on the string pad
in the doGet method instead of just sending the file if it exists!

Still, a nice and tiny server Smile
GeneralRe: not very secure
Stephan Meyn
10:08 26 Feb '09  
oh absolutely - you wouldn't use this as a public webserver. This is intended only if you need a very lightweight http interface to an application. For instance we used it recently to provide a REST interface for the UI of an application where each and every UI element (form or control) has a URI that allows you to interrogate that element and modify it - the result is an interface for automated UI testing.
QuestionSilly Question: How to I request a page?
flipdoubt
3:01 28 Apr '07  
I've downloaded the sample and started the server. Now how to I request a page? I've tried http://root:81 in the browser, but of course it is unknown.
AnswerRe: Silly Question: How to I request a page?
GoofMan
2:46 2 Oct '07  
Try http://localhost:81
GeneralASP.NET Runtime
dzCepheus
11:51 31 Oct '04  
Have you considered adding support for hosting the ASP.NET runtime? It's actually pretty simple from what I've read.

Support your local gravity testers -- Skydive!
GeneralRe: ASP.NET Runtime
Stephan Meyn
11:59 31 Oct '04  
I did and it is. But I wanted to build a very light weight web server without the baggage ASP.NET requires.

GeneralRe: ASP.NET Runtime
WallePuh
22:10 5 Dec '04  
So how do you do to add support for ASP.NET in your own web server? Where can I read about this?
Generalother resources
MadHatter ¢
21:14 25 Oct '04  
nice article.

do you know of any way to use any of .NET's http objects to serve pages (httpwebrequest/response) or respond to requests?

very shortly i'll be writing my own implementation of a web server and before really looking at it, i'm wondering if there was a way to use them instead of parsing out the protocol myself.

thanks (actually fairly amazed that there are no comments here yet).




/bb|[^b]{2}/

GeneralRe: other resources
Stephan Meyn
3:23 27 Oct '04  
Not really. I have used the client side counterparts (WebRequest and WebResponse).
I would look at HttpWorkerRequest and SimpleWorkerRequest classes for guidance.

Stephan
GeneralRe: other resources
cooldeo18t
7:43 19 Jul '09  
How to do add mime type to file located in server,
I will be hosting a cab file,
when request should ask user to save it not display it as junk characters in page itself. please guide

interesting person to chat..........

GeneralRe: other resources
cooldeo18t
8:08 19 Jul '09  
I like this concept very much.
I am developing a installer for app, which is excepting only http protocol , hence I need to put my cab file on tiny webserver programatically, then i will ask api to make a call to http path.What i saw in your code was that the whole file dumped as raw stream without headers or mime type. how accomplish this.
I actually want to host a single cab for few seconds once my operation is done I want to stop web server.

interesting person to chat..........


Last Updated 19 Oct 2004 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010