Click here to Skip to main content
15,861,168 members
Articles / Database Development / NoSQL

RavenDB - An Introduction

,
Rate me:
Please Sign up or sign in to vote.
4.87/5 (38 votes)
28 Apr 2010CPOL7 min read 259.7K   2.7K   112  
An introduction to RavenDB - a new open source .NET document database using .NET 4.0 and VS 2010
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using log4net;
using Newtonsoft.Json;
using Raven.Database;
using Raven.Database.Exceptions;
using Raven.Database.Extensions;
using Raven.Server.Responders;

namespace Raven.Server
{
	public class HttpServer : IDisposable
	{
		private readonly RavenConfiguration configuration;
		private readonly HttpListener listener;

		private readonly ILog logger = LogManager.GetLogger(typeof (HttpServer));
		private readonly RequestResponder[] requestResponders;
		private int reqNum;

		public HttpServer(
			RavenConfiguration configuration,
			IEnumerable<RequestResponder> requestResponders)
		{
			this.configuration = configuration;
			this.requestResponders = requestResponders.ToArray();
			listener = new HttpListener();
			listener.Prefixes.Add("http://+:" + configuration.Port + "/" + configuration.VirtualDirectory);
			switch (configuration.AnonymousUserAccessMode)
			{
				case AnonymousUserAccessMode.None:
					listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
					break;
				default:
					listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication |
						AuthenticationSchemes.Anonymous;
					break;
			}
		}

		#region IDisposable Members

		public void Dispose()
		{
			listener.Stop();
		}

		#endregion

		public void Start()
		{
			listener.Start();
			listener.BeginGetContext(GetContext, null);
		}

		private void GetContext(IAsyncResult ar)
		{
			HttpListenerContext ctx;
			try
			{
				ctx = listener.EndGetContext(ar);
				//setup waiting for the next request
				listener.BeginGetContext(GetContext, null);
			}
			catch (HttpListenerException)
			{
				// can't get current request / end new one, probably
				// listner shutdown
				return;
			}

			var curReq = Interlocked.Increment(ref reqNum);
			try
			{
				logger.DebugFormat("Request #{0}: {1} {2}",
				                   curReq,
				                   ctx.Request.HttpMethod,
				                   ctx.Request.Url.PathAndQuery
					);
				var sw = Stopwatch.StartNew();
				HandleRequest(ctx);

				logger.DebugFormat("Request #{0}: {1}",
				                   curReq, sw.Elapsed);
			}
			catch (Exception e)
			{
				HandleException(ctx, e);
				logger.WarnFormat(e, "Error on request #{0}", curReq);
			}
			finally
			{
				try
				{
					ctx.Response.OutputStream.Flush();
					ctx.Response.Close();
				}
				catch
				{
				}
			}
		}

		private void HandleException(HttpListenerContext ctx, Exception e)
		{
			try
			{
				if (e is BadRequestException)
					HandleBadRequest(ctx, (BadRequestException) e);
				else if (e is ConcurrencyException)
					HandleConcurrencyException(ctx, (ConcurrencyException) e);
				else if (e is IndexDisabledException)
					HandleIndexDisabledException(ctx, (IndexDisabledException) e);
				else
					HandleGenericException(ctx, e);
			}
			catch (Exception)
			{
				logger.Error("Failed to properly handle error, further error handling is ignored", e);
			}
		}

		private static void HandleIndexDisabledException(HttpListenerContext ctx, IndexDisabledException e)
		{
			ctx.Response.StatusCode = 503;
			ctx.Response.StatusDescription = "Service Unavailable";
			using (var sw = new StreamWriter(ctx.Response.OutputStream))
			{
				new JsonSerializer().Serialize(sw,
				                               new
				                               {
				                               	url = ctx.Request.RawUrl,
				                               	error = e.Information.GetErrorMessage(),
				                               	index = e.Information.Name,
				                               });
			}
		}

		private static void HandleGenericException(HttpListenerContext ctx, Exception e)
		{
			ctx.Response.StatusCode = 500;
			ctx.Response.StatusDescription = "Internal Server Error";
			using (var sw = new StreamWriter(ctx.Response.OutputStream))
			{
				new JsonSerializer().Serialize(sw,
				                               new
				                               {
				                               	url = ctx.Request.RawUrl,
				                               	error = e.ToString()
				                               });
			}
		}

		private static void HandleBadRequest(HttpListenerContext ctx, BadRequestException e)
		{
			ctx.SetStatusToBadRequest();
			using (var sw = new StreamWriter(ctx.Response.OutputStream))
			{
				new JsonSerializer().Serialize(sw,
				                               new
				                               {
				                               	url = ctx.Request.RawUrl,
				                               	message = e.Message,
				                               	error = e.Message
				                               });
			}
		}

		private static void HandleConcurrencyException(HttpListenerContext ctx, ConcurrencyException e)
		{
			ctx.Response.StatusCode = 409;
			ctx.Response.StatusDescription = "Conflict";
			using (var sw = new StreamWriter(ctx.Response.OutputStream))
			{
				new JsonSerializer().Serialize(sw,
				                               new
				                               {
				                               	url = ctx.Request.RawUrl,
				                               	actualETag = e.ActualETag,
				                               	expectedETag = e.ExpectedETag,
				                               	error = e.Message
				                               });
			}
		}

		private void HandleRequest(HttpListenerContext ctx)
		{
			if (AssertSecurityRights(ctx) == false)
				return;

			foreach (var requestResponder in requestResponders)
			{
				if (requestResponder.WillRespond(ctx))
				{
					requestResponder.Respond(ctx);
					return;
				}
			}
			ctx.SetStatusToBadRequest();
			ctx.Write(
				@"
<html>
    <body>
        <h1>Could not figure out what to do</h1>
        <p>Your request didn't match anything that Raven knows to do, sorry...</p>
    </body>
</html>
");
		}

		private bool AssertSecurityRights(HttpListenerContext ctx)
		{
			if (configuration.AnonymousUserAccessMode == AnonymousUserAccessMode.Get &&
				(ctx.User == null || ctx.User.Identity == null || ctx.User.Identity.IsAuthenticated == false) &&
					ctx.Request.HttpMethod != "GET"
				)
			{
				ctx.SetStatusToUnauthorized();
				return false;
			}
			return true;
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
I've been a software developer since 1996 and have enjoyed C# since 2003. I have a Bachelor's degree in Computer Science and for some reason, a Master's degree in Business Administration. I currently do software development contracting/consulting.

Written By
Israel Israel
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions