Language Envy - Juicy, a simple webserver

Many times when I'm writing JavaScript code with Ajax calls I don't have an URL to call and get the data yet. Or sometimes I have an URL that gives me valid data and I need to test the code against invalid data or some other variation that I don't have handy.

WEBrick

In these situations, instead of creating a temporary ASP.NET application, I learned to love using Ruby and the adorable simplicity of WEBrick. Imagine that I needed an URL that simulated latency on the server.

require 'webrick'
include WEBrick

server = HTTPServer.new( :Port => 2000 )

server.mount_proc("/getData"){|req, res|
  res.body = "{result: 'ok', accountId: 123}"
  res['Content-Type'] = "application/json"
  delay = req.query['delay'] || "0"
  sleep(delay.to_i)
}

trap("INT"){ server.shutdown }
server.start

After running the script I can open use the URL http://localhost:2000/getData?delay=5 in my Ajax call and, after a delay of five seconds, the JSON string {result: 'ok', accountId: 123} will be returned. The server will stay there, in a console window, running until I kill it with CTRL+C.

I could add more mount points on that server for as many test URLs as I wish. I find this incredibly practical.

I wish there was something as simple in .NET

Well, I couldn't find anything as easy in .NET. The few offerings I found were not as straight forward. Some of the alternatives are distributed in .msi files, which makes them less convenient, especially for integration testing or JavaScript unit testing (which is my end goal.)

Hello Juicy

Envy sometimes makes you move forward. I played around with sockets and the HTTP protocol and ended up with a library that looks promising. See that same code written using the Juicy.DirtCheapDaemons.Http.HttpServer class, a.k.a. the Juicy Web Server.

using System;
using System.IO;
using System.Threading;
using Juicy.DirtCheapDaemons.Http;

  class Program
  {
    static void Main(string[] args)
    {
      HttpServer server = new HttpServer{ PortNumber = 2000 };
      server.Start();

      server.Mount("/getData", 
        (req, resp) =>
          {
            resp.Output.Write("{result: 'ok', accountId: 123}");
            resp["Content-Type"] = "application/json";
            var delay = int.Parse(req.QueryString["delay"] ?? "0");
            Thread.Sleep(1000 * delay);
          }
      );

      Console.WriteLine("Press enter to stop server");
      Console.ReadLine();
      server.Shutdown();
    }
  }

There are a few things more that can be done with HttpServer.

//mounting virtual path with a lambda (shown in previous example)
server.Mount("/virtual/path", (req, resp) => DoSomethingWith(req, resp));

//mounting virtual path to serve static files
server.Mount("/virtual/path", @"c:\some\directory");

//hiding a virtual path
server.Mount("/virtual/path/subdir", new ResourceNotFoundHandler());

As it is, HttpServer supports only GET requests. The plan is to add more support (for features that make sense in a unit testing scenario) with time.

I obviously don't envision Juicy becoming a full-fledged web server that I can recommend using in a live web site. But I can see it being helpful in the scenarios I've just described and for testing in general.

I'm sold. Where can I get it?

This project is hosted at GitHub: http://github.com/sergiopereira/juicy/tree/master, where you can download the source directly or even clone the repository :

c:\projects>git clone git://github.com/sergiopereira/juicy.git

Posted 04-13-2009 11:02 PM by sergiopereira

[Advertisement]

Comments

Scott wrote re: Language Envy - Juicy, a simple webserver
on 04-14-2009 1:59 AM

What's the difference between using this and using Cassini? What problems does this solve that Cassini doesn't?

I've been investigating using Waitin inside of an MSBuild task to run JS  unit tests using JSpec. It's looking promising so far.

Michael D. Hall wrote re: Language Envy - Juicy, a simple webserver
on 04-14-2009 2:14 AM

I really think this is a cool idea, and after we spoke it got me thinking. I briefly checked out your source code and noticed that you were rolling-your-own HttpListener. Aside from fun and educational value, was there a reason you didn't use the framework's HttpListener?

I wrote a very contrived example that is shamelessly derived from the MSDN docs.

using System;

using System.IO;

using System.Net;

using System.Text;

namespace Praktyka.Http

{

internal static class Program

{

private static void Main()

{

//

//Shamelessly derived from the MSDN documentation...

// ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/fxref_system/html/47081967-df6f-bee5-f039-2f5584123322.htm

//

//run the program and then enter this value in the brower.

//the current time will be displayed and then the program will exit.

const string url = "http://localhost:9834/praktyka/

var http = new HttpListener();

http.Prefixes.Add(url);

http.Start();

HttpListenerContext context = http.GetContext();

HttpListenerRequest request = context.Request;

HttpListenerResponse response = context.Response;

//a simple function to render a string that will be output to the response stream and

//sent to the client.

Func<string> func =

() => string.Format("<html><head><body>{0}</body></html>", DateTime.Now);

byte[] buffer = Encoding.UTF8.GetBytes(func());

response.ContentLength64 = buffer.Length;

Stream output = response.OutputStream;

output.Write(buffer, 0, buffer.Length);

output.Close();

http.Stop();

}

}

}

Michael D. Hall wrote re: Language Envy - Juicy, a simple webserver
on 04-14-2009 2:16 AM

@Scott Because you have to install the Cassini web server. I was trying to do the same thing a while back but gave up because the new Cassini that supports .NET 3.5 is a PITA to deploy, and has been taken over by a 3rd party company. I think that was the .MSI solution that Sergio was referring to.

Michael D. Hall wrote re: Language Envy - Juicy, a simple webserver
on 04-14-2009 2:23 AM

More info on the .NET 2.0+ version of Cassini can be found on Wikipedia, en.wikipedia.org/.../UltiDev_Cassini_Web_Server.

sergiopereira wrote re: Language Envy - Juicy, a simple webserver
on 04-14-2009 8:15 AM

@Mike, Why didn't I use HttpListener?

I had some prior art to guide me (the Ruby implementation) and the only time I looked at the HttpListener*.* family of classes, a bunch of them were sealed and did not implement interfaces or anything that could become handy for mocking or extensions.

sergiopereira wrote re: Language Envy - Juicy, a simple webserver
on 04-14-2009 8:24 AM

@Scott, as I mentioned in the post and Mike confirmed in his comment, Cassini installs via an .msi file, which can make it less desirable in scenarios where you just want to pull the source from the SCM and run your build+tests without any extra steps.

The Visual Studio internal webserver, which my understanding is that is a Cassini derivative, basically mimics IIS, not allowing me to write the kinds of "on the fly", lambda-based responders. It's also an .exe that is much harder to configure and integrate than an HttpServer class in a dll.

DotNetKicks.com wrote Juicy, a simple webserver
on 04-14-2009 8:28 AM

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Rick Strahl wrote re: Language Envy - Juicy, a simple webserver
on 04-14-2009 8:52 AM

Cassini (V2.0 anyway) was published on CodePlex and you can repackage and deploy it. I've done this for a non-.NET project actually and it works fine just with xCopy deploy.

You can grab these from Dmitry's blog:

blogs.msdn.com/.../548131.aspx

sergiopereira wrote re: Language Envy - Juicy, a simple webserver
on 04-14-2009 9:24 AM

@Rick. I hadn't heard about that maintained version of Cassini. After looking at it briefly (there's some brand new source code in codeplex,) I'd say that it's still not as convenient as WEBrick (and hopefully, this HttpServer that I'm working on.) It also seems to be specifically built for ASP.NET, whereas I don't plan to have any critical ASP.NET dependencies (like HttpContext or HttpResponse, etc).

I'll take a more careful look at it nut I don't think I'm I'm headed to the same direction with this project.

DotNetShoutout wrote Language Envy - Juicy, a simple webserver - Sergio Pereira - Devlicio.us
on 04-14-2009 9:51 AM

Thank you for submitting this cool story - Trackback from DotNetShoutout

Michael D. Hall wrote re: Language Envy - Juicy, a simple webserver
on 04-14-2009 9:59 AM

Maybe just wrap the HttpListener with an adapter/wrapper?

sergiopereira wrote re: Language Envy - Juicy, a simple webserver
on 04-14-2009 10:15 AM

@Mike, that's an idea. I normally don't like putting wrappers around things that I'm not convinced of their value yet. I suspect that wrapping the HttpListener*.* classes will be a never ending job that will eventually make me write as much code as I'd have if I created all from scratch as I'm doing.

If I hit some obstacle that is easier resolved by moving to HttpListener, I'll consider it for sure instead of wrting more sockets and HTTP logic.

sach wrote re: Language Envy - Juicy, a simple webserver
on 04-14-2009 1:54 PM

There's a feature in VS2008sp1 which can help out called wcftestclient:blogs.msdn.com/.../how-to-use-wcftestclient-as-svc-file-debugger-for-vs2008-sp1.aspx

sach wrote re: Language Envy - Juicy, a simple webserver
on 04-14-2009 2:04 PM

btw that tool doesnt solve your problem, it just helps out with writing services in other ways...you could also perhaps use fiddler to send yourself custom responses from existing services

sergiopereira wrote re: Language Envy - Juicy, a simple webserver
on 04-14-2009 2:19 PM

@sach, the problem I mentioned in the first paragraph is just an example of a problem that such web server can help with, but it's not the sole reason that made me code it up.

That hidden feature is visual studio is "cute" but I'm thinking it doesn't help me with my CI server.

Regarding Fiddler, if it worked well on localhost, I would probably use it for its saved responses. Again, most importantly, doesn't help my CI server.

Andy Pook wrote re: Language Envy - Juicy, a simple webserver
on 04-15-2009 7:47 AM

At least one useful thing about HttpListener is that it works via http.sys. So you can sure ports with IIS.

On the trivia side... it would be nice if the code was consistent in the use of tags or spaces for indenting (ctrl-K,D is your friend) :)

sergiopereira wrote re: Language Envy - Juicy, a simple webserver
on 04-15-2009 8:29 AM

@Andy, that's one of the joys of working in multiple projects with different formatting requirements. I guess I should use my own suggestions in my personal projects as well (see devlicio.us/.../coding-style-per-project.aspx). Thanks for the heads up, though. I'll try to be more consistent.

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)