changeset 79:4e4c600031e2

Merge FBGUI updates
author Ivo Smits <Ivo@UCIS.nl>
date Sun, 16 Feb 2014 15:02:36 +0100
parents 1a10ca0f662e (diff) 8f31b164ce7e (current diff)
children 4714531734b3
files FBGUI/FBGUI.cs
diffstat 7 files changed, 530 insertions(+), 85 deletions(-) [+]
line wrap: on
line diff
--- a/FBGUI/FBGUI.cs	Thu Nov 28 13:19:41 2013 +0100
+++ b/FBGUI/FBGUI.cs	Sun Feb 16 15:02:36 2014 +0100
@@ -95,6 +95,7 @@
 		void AddControl(IFBGControl control);
 		void RemoveControl(IFBGControl control);
 		void HandleMessage(IFBGControl sender, FBGMessage e);
+		Size ClientSize { get; }
 	}
 
 	public class FBGControl : IFBGControl {
@@ -215,9 +216,8 @@
 		protected List<IFBGControl> controls = new List<IFBGControl>();
 		protected IFBGControl mouseCaptureControl = null;
 		protected IFBGControl keyboardCaptureControl = null;
-		private Rectangle childarea = Rectangle.Empty;
-		public Rectangle ClientRectangle { get { return childarea; } protected set { childarea = value; Invalidate(); } }
-		public Size ClientSize { get { return childarea.Size; } set { Bounds = new Rectangle(Bounds.Location, Bounds.Size - childarea.Size + value); } }
+		public virtual Rectangle ClientRectangle { get { return Bounds; } }
+		public virtual Size ClientSize { get { return ClientRectangle.Size; } set { Bounds = new Rectangle(Bounds.Location, Bounds.Size - ClientRectangle.Size + value); } }
 		public FBGContainerControl(IFBGContainerControl parent) : base(parent) { }
 		void IFBGContainerControl.AddControl(IFBGControl control) { AddControl(control); }
 		protected virtual void AddControl(IFBGControl control) {
@@ -253,10 +253,10 @@
 		}
 		protected override void HandlePaintEvent(FBGPaintEvent e) {
 			base.HandlePaintEvent(e);
-			if (controls == null) return;
 			GraphicsState state2 = null;
 			Graphics g = e.Canvas;
-			if (!childarea.IsEmpty) {
+			Rectangle childarea = ClientRectangle;
+			if (childarea.X != 0 || childarea.Y != 0 || childarea.Width != Bounds.Width || childarea.Height != Bounds.Height) {
 				state2 = g.Save();
 				g.TranslateTransform(childarea.X, childarea.Y, MatrixOrder.Append);
 				g.IntersectClip(new Rectangle(Point.Empty, childarea.Size));
@@ -274,7 +274,8 @@
 			if (state2 != null) g.Restore(state2);
 		}
 		public IFBGControl FindControlAtPosition(Point p) {
-			if (!childarea.IsEmpty && !childarea.Contains(p)) return null;
+			Rectangle childarea = ClientRectangle;
+			if (!childarea.Contains(p)) return null;
 			p.Offset(-childarea.X, -childarea.Y);
 			return ((List<IFBGControl>)controls).FindLast(delegate(IFBGControl control) { return control.Visible && control.Bounds.Contains(p); });
 		}
@@ -460,26 +461,17 @@
 	}
 	public class FBGGroupBox : FBGDockContainer {
 		private String text = String.Empty;
-		public FBGGroupBox(IFBGContainerControl parent)
-			: base(parent) {
-			ClientRectangle = new Rectangle(1, 15, Bounds.Width - 2, Bounds.Height - 17);
-		}
-		public override Rectangle Bounds {
-			get { return base.Bounds; }
-			set {
-				ClientRectangle = new Rectangle(1, 15, value.Width - 2, value.Height - 17);
-				base.Bounds = value;
-			}
-		}
+		public override Rectangle ClientRectangle { get { return new Rectangle(1, 15, Bounds.Width - 2, Bounds.Height - 17); } }
+		public FBGGroupBox(IFBGContainerControl parent) : base(parent) { }
 		public String Text { get { return text; } set { if (text == value) return; text = value; Invalidate(new Rectangle(0, 0, Bounds.Width, 15)); } }
 		protected override void Paint(Graphics g) {
 			base.Paint(g);
-			g.DrawRectangle(Pens.Gray, 0, 6, Bounds.Width - 1, Bounds.Height - 7);
 			SizeF ss = g.MeasureString(Text, SystemFonts.DefaultFont, new Size(Bounds.Width - 10 - 9, 15));
-			g.FillRectangle(SystemBrushes.Control, 9, 0, ss.Width, ss.Height);
+			g.DrawLines(Pens.Gray, new PointF[] { new PointF(8 + ss.Width + 4, 6), new Point(Bounds.Width - 1, 6), new Point(Bounds.Width - 1, Bounds.Height - 1), new Point(0, Bounds.Height - 1), new Point(0, 6), new Point(8, 6) });
 			g.DrawString(Text, SystemFonts.DefaultFont, Brushes.DarkBlue, new Rectangle(9, 0, Bounds.Width - 10 - 9, 15));
 		}
 	}
+	[System.ComponentModel.DesignerCategory("")]
 	public class WinFormsFBGHost : Control, IFBGContainerControl {
 		private IFBGControl childControl = null;
 		private IFBGControl mouseCaptureControl = null;
@@ -835,20 +827,13 @@
 			ButtonClose = 32,
 			MoveResize = ResizeLeft | ResizeRight | ResizeBottom | ResizeTop | Move,
 		}
+		public override Rectangle ClientRectangle { get { return new Rectangle(4, 22, Bounds.Width - 8, Bounds.Height - 26); } }
 		public FBGForm(IFBGContainerControl parent) : base(parent) {
 			BackColor = SystemColors.Control;
-			ClientRectangle = new Rectangle(4, 22, Bounds.Width - 8, Bounds.Height - 26);
 			Sizable = true;
 			Movable = true;
 			Closable = false;
 		}
-		public override Rectangle Bounds {
-			get { return base.Bounds; }
-			set {
-				ClientRectangle = new Rectangle(4, 22, value.Width - 8, value.Height - 26);
-				base.Bounds = value;
-			}
-		}
 		public String Text { get { return text; } set { if (text == value) return; text = value; Invalidate(new Rectangle(0, 0, Bounds.Width, 20)); RaiseEvent(TextChanged); } }
 		private NonClientOps GetNonClientOperation(Point p) {
 			if ((new Rectangle(Bounds.Width - 5 - 14, 4, 14, 14)).Contains(p)) return NonClientOps.ButtonClose;
@@ -1489,15 +1474,32 @@
 			: base(parent) {
 			BackColor = Color.White;
 		}
-		public void AddItem(Object item) {
-			items.Add(item);
+		public int AddItem(Object item) {
+			int id = ((System.Collections.IList)items).Add(item);
+			Invalidate();
+			return id;
+		}
+		public void RemoveItem(Object item) {
+			items.Remove(item);
+			if (offset >= items.Count) offset = Math.Max(0, items.Count - 1);
 			Invalidate();
 		}
+		public void RemoveItemAt(int index) {
+			if (index < 0) index += items.Count;
+			items.RemoveAt(index);
+			if (offset >= index && offset > 0) offset--;
+			Invalidate();
+		}
+		public void Clear() {
+			items.Clear();
+			offset = 0;
+			Invalidate();
+		}
+		public System.Collections.IList Items { get { return items.AsReadOnly(); } }
 		protected override void Paint(Graphics g) {
 			base.Paint(g);
 			g.DrawRectangle(Pens.DarkBlue, 0, 0, Bounds.Width - 1, Bounds.Height - 1);
 			int lh = (int)Math.Ceiling(SystemFonts.DefaultFont.GetHeight());
-			int th = lh * items.Count;
 			int y = 2;
 			using (Pen dottedpen = new Pen(Brushes.Black)) {
 				dottedpen.DashStyle = DashStyle.Dot;
@@ -1513,14 +1515,14 @@
 					}
 				}
 			}
-			if (y < th) hasScrollBar = true;
+			hasScrollBar = (y < 2 + lh * items.Count) || (offset != 0);
 			if (hasScrollBar) {
 				int xoff = Bounds.Width - 17;
 				using (Brush b = new LinearGradientBrush(new Rectangle(xoff, 0, 17, 1), Color.LightGray, Color.White, LinearGradientMode.Horizontal))
 					g.FillRectangle(b, xoff, 17, 16, Bounds.Height - 17 - 17);
 				ControlPaint.DrawScrollButton(g, xoff, 1, 16, 16, ScrollButton.Up, buttonUpState);
 				ControlPaint.DrawScrollButton(g, xoff, Bounds.Height - 17, 16, 16, ScrollButton.Down, buttonDownState);
-				g.DrawRectangle(Pens.Black, new Rectangle(xoff, 17 + offset * (Bounds.Height - 17 - 17 - 20) / (items.Count - 1), 15, 20));
+				if (items.Count > 1) g.DrawRectangle(Pens.Black, new Rectangle(xoff, 17 + offset * (Bounds.Height - 17 - 17 - 20) / (items.Count - 1), 15, 20));
 			}
 		}
 		protected override void MouseDown(Point position, MouseButtons buttons) {
@@ -1528,17 +1530,17 @@
 				CaptureMouse(true);
 				if (hasScrollBar && position.X > Bounds.Width - 17) {
 					hitScrollBar = true;
+					int off = offset;
 					if (position.Y < 17) {
-						offset--;
+						off--;
 						buttonUpState = ButtonState.Pushed;
 					} else if (position.Y > Bounds.Height - 17) {
-						offset++;
+						off++;
 						buttonDownState = ButtonState.Pushed;
 					} else {
-						offset = (int)Math.Round((position.Y - 17) * (items.Count - 1) / (double)(Bounds.Height - 17 - 17 - 10));
+						off = (int)Math.Round((position.Y - 17) * (items.Count - 1) / (double)(Bounds.Height - 17 - 17 - 10));
 					}
-					if (offset < 0) offset = 0;
-					if (offset >= items.Count) offset = items.Count - 1;
+					offset = Math.Max(0, Math.Min(items.Count - 1, off));
 					Invalidate();
 				} else {
 					MouseHandler(position, buttons);
@@ -1550,9 +1552,7 @@
 				if (position.Y < 17) {
 				} else if (position.Y > Bounds.Height - 17) {
 				} else {
-					offset = (int)Math.Round((position.Y - 17) * (items.Count - 1) / (double)(Bounds.Height - 17 - 17 - 10));
-					if (offset < 0) offset = 0;
-					if (offset >= items.Count) offset = items.Count - 1;
+					offset = Math.Max(0, Math.Min(items.Count - 1,  (int)Math.Round((position.Y - 17) * (items.Count - 1) / (double)(Bounds.Height - 17 - 17 - 10))));
 					Invalidate();
 				}
 				return;
--- a/NaCl/crypto_verify/16.cs	Thu Nov 28 13:19:41 2013 +0100
+++ b/NaCl/crypto_verify/16.cs	Sun Feb 16 15:02:36 2014 +0100
@@ -5,7 +5,7 @@
 		const int BYTES = 16;
 		public static int crypto_verify(Byte* x, Byte* y) {
 			Int32 differentbits = 0;
-			for (int i = 0; i < 15; i++) differentbits |= x[i] ^ y[i];
+			for (int i = 0; i < 16; i++) differentbits |= x[i] ^ y[i];
 			return (1 & ((differentbits - 1) >> 8)) - 1;
 		}
 	}
--- a/NaCl/crypto_verify/32.cs	Thu Nov 28 13:19:41 2013 +0100
+++ b/NaCl/crypto_verify/32.cs	Sun Feb 16 15:02:36 2014 +0100
@@ -5,7 +5,7 @@
 		const int BYTES = 32;
 		public static int crypto_verify(Byte* x, Byte* y) {
 			Int32 differentbits = 0;
-			for (int i = 0; i < 31; i++) differentbits |= x[i] ^ y[i];
+			for (int i = 0; i < 32; i++) differentbits |= x[i] ^ y[i];
 			return (1 & ((differentbits - 1) >> 8)) - 1;
 		}
 	}
--- a/Net/HTTP.cs	Thu Nov 28 13:19:41 2013 +0100
+++ b/Net/HTTP.cs	Sun Feb 16 15:02:36 2014 +0100
@@ -70,6 +70,14 @@
 		}
 	}
 
+	public enum HTTPResponseStreamMode {
+		None = -1,
+		Direct = 0,
+		Buffered = 1,
+		Chunked = 2,
+		Hybrid = 3,
+	}
+
 	public class HTTPContext {
 		public HTTPServer Server { get; private set; }
 		public EndPoint LocalEndPoint { get; private set; }
@@ -88,7 +96,9 @@
 		private PrebufferingStream Reader;
 		private List<HTTPHeader> RequestHeaders;
 		private HTTPConnectionState State = HTTPConnectionState.Starting;
-
+		private KeyValuePair<String, String>[] QueryParameters = null, PostParameters = null;
+		private HTTPOutputStream ResponseStream = null;
+		private HTTPInputStream RequestStream = null;
 
 		private enum HTTPConnectionState {
 			Starting = 0,
@@ -96,7 +106,289 @@
 			ProcessingRequest = 2,
 			SendingHeaders = 3,
 			SendingContent = 4,
-			Closed = 5,
+			Completed = 5,
+			Closed = 6,
+		}
+
+		private class HTTPOutputStream : Stream {
+			public HTTPResponseStreamMode Mode { get; private set; }
+			public HTTPContext Context { get; private set; }
+			private Stream OutputStream = null;
+			private MemoryStream Buffer = null;
+			private long BytesWritten = 0;
+			private long MaxLength;
+
+			public HTTPOutputStream(HTTPContext context, HTTPResponseStreamMode mode) : this(context, mode, -1) { }
+			public HTTPOutputStream(HTTPContext context, HTTPResponseStreamMode mode, long length) {
+				this.Context = context;
+				this.Mode = mode;
+				this.MaxLength = length;
+				switch (Mode) {
+					case HTTPResponseStreamMode.Direct:
+						if (MaxLength != -1) Context.SendHeader("Content-Length", MaxLength.ToString());
+						OutputStream = Context.BeginResponseData();
+						break;
+					case HTTPResponseStreamMode.Chunked:
+						Context.SendHeader("Transfer-Encoding", "chunked");
+						OutputStream = Context.BeginResponseData();
+						break;
+					case HTTPResponseStreamMode.Buffered:
+					case HTTPResponseStreamMode.Hybrid:
+						if (Context.State != HTTPConnectionState.ProcessingRequest && Context.State != HTTPConnectionState.SendingHeaders) throw new InvalidOperationException("The response stream can not be created in the current state");
+						break;
+					default: throw new InvalidOperationException("Response stream mode is not supported");
+				}
+			}
+
+			private void WriteBuffered(byte[] buffer, int offset, int count) {
+				if (Buffer == null) Buffer = new MemoryStream();
+				Buffer.Write(buffer, offset, count);
+			}
+			private void WriteChunked(byte[] buffer, int offset, int count) {
+				Byte[] lb = Encoding.ASCII.GetBytes(count.ToString("X") + "\r\n");
+				OutputStream.Write(lb, 0, lb.Length);
+				if (count != 0) OutputStream.Write(buffer, offset, count);
+				OutputStream.Write(new Byte[] { (Byte)'\r', (Byte)'\n' }, 0, 2);
+			}
+			private void HybridSwitchToChunked() {
+				MemoryStream oldbuffer = Buffer;
+				Buffer = null;
+				Context.SendHeader("Transfer-Encoding", "chunked");
+				OutputStream = Context.BeginResponseData();
+				Mode = HTTPResponseStreamMode.Chunked;
+				oldbuffer.WriteTo(this);
+			}
+
+			public override void Write(byte[] buffer, int offset, int count) {
+				if (offset < 0 || count < 0 || offset + count > buffer.Length) throw new ArgumentOutOfRangeException("buffer", "Offset and count arguments exceed the buffer dimensions");
+				switch (Mode) {
+					case HTTPResponseStreamMode.Direct:
+						if (MaxLength != -1 && BytesWritten + count > MaxLength) throw new InvalidOperationException("The write operation exceeds the transfer length");
+						OutputStream.Write(buffer, offset, count);
+						BytesWritten += count;
+						break;
+					case HTTPResponseStreamMode.Buffered:
+						WriteBuffered(buffer, offset, count);
+						break;
+					case HTTPResponseStreamMode.Chunked:
+						if (count != 0) WriteChunked(buffer, offset, count);
+						BytesWritten += count;
+						break;
+					case HTTPResponseStreamMode.Hybrid:
+						if (count > 1024 || (Buffer != null && Buffer.Length + count > 1024)) {
+							HybridSwitchToChunked();
+							if (count != 0) WriteChunked(buffer, offset, count);
+						} else {
+							WriteBuffered(buffer, offset, count);
+						}
+						break;
+				}
+			}
+			class CompletedAsyncResult : AsyncResultBase {
+				public CompletedAsyncResult(AsyncCallback callback, Object state) : base(callback, state) {
+					SetCompleted(true, null);
+				}
+			}
+			public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) {
+				if (offset < 0 || count < 0 || offset + count > buffer.Length) throw new ArgumentOutOfRangeException("buffer", "Offset and count arguments exceed the buffer dimensions");
+				switch (Mode) {
+					case HTTPResponseStreamMode.Direct:
+						if (MaxLength != -1 && BytesWritten + count > MaxLength) throw new InvalidOperationException("The write operation exceeds the transfer length");
+						BytesWritten += count;
+						return OutputStream.BeginWrite(buffer, offset, count, callback, state);
+					case HTTPResponseStreamMode.Buffered:
+					case HTTPResponseStreamMode.Chunked:
+					case HTTPResponseStreamMode.Hybrid:
+						Write(buffer, offset, count);
+						return new CompletedAsyncResult(callback, state);
+					default: return null;
+				}
+			}
+			public override void EndWrite(IAsyncResult asyncResult) {
+				switch (Mode) {
+					case HTTPResponseStreamMode.Direct:
+						OutputStream.EndWrite(asyncResult);
+						break;
+				}
+			}
+			public override void SetLength(long value) {
+				switch (Mode) {
+					case HTTPResponseStreamMode.Buffered:
+					case HTTPResponseStreamMode.Hybrid:
+						if (value != 0) throw new InvalidOperationException("The length can only be set to zero using this method");
+						Buffer = null;
+						break;
+					default: throw new InvalidOperationException("The operation is not supported in the current mode");
+				}
+			}
+			public override long Length {
+				get { return MaxLength == -1 ? Position : MaxLength; }
+			}
+			public override long Position {
+				get { return (Buffer == null) ? BytesWritten : BytesWritten + Buffer.Length; }
+				set { throw new NotSupportedException(); }
+			}
+			public override void Flush() {
+				switch (Mode) {
+					case HTTPResponseStreamMode.Hybrid:
+						HybridSwitchToChunked();
+						break;
+				}
+			}
+			public override bool CanWrite {
+				get { return Context.State != HTTPConnectionState.Completed && Context.State != HTTPConnectionState.Closed && (OutputStream == null || OutputStream.CanWrite); }
+			}
+			protected override void Dispose(bool disposing) {
+				base.Dispose(disposing);
+				if (disposing) {
+					switch (Mode) {
+						case HTTPResponseStreamMode.Direct:
+							/*if (MaxLength != -1 && MaxLength > BytesWritten) {
+								long left = MaxLength - BytesWritten;
+								Byte[] dummy = new Byte[Math.Min(left, 1024)];
+								for (; left > 0; left -= dummy.Length) OutputStream.Write(dummy, 0, (int)Math.Min(left, dummy.Length));
+							}*/
+							break;
+						case HTTPResponseStreamMode.Chunked:
+							WriteChunked(null, 0, 0);
+							Mode = HTTPResponseStreamMode.None;
+							break;
+						case HTTPResponseStreamMode.Buffered:
+						case HTTPResponseStreamMode.Hybrid:
+							long length = (Buffer == null) ? 0 : Buffer.Length;
+							Context.SendHeader("Content-Length", length.ToString());
+							OutputStream = Context.BeginResponseData();
+							if (Buffer != null) Buffer.WriteTo(OutputStream);
+							Buffer = null;
+							Mode = HTTPResponseStreamMode.None;
+							break;
+					}
+					Context.EndResponseData();
+					if (MaxLength != -1 && MaxLength > BytesWritten) Context.Close();
+				}
+			}
+			public override bool CanTimeout {
+				get { return OutputStream == null ? true : OutputStream.CanTimeout; }
+			}
+			public override int WriteTimeout {
+				get { return OutputStream == null ? 0 : OutputStream.WriteTimeout; }
+				set { if (OutputStream != null) OutputStream.WriteTimeout = value; }
+			}
+
+			public override bool CanRead { get { return false; } }
+			public override bool CanSeek { get { return false; } }
+
+			public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); }
+			public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
+		}
+		private class HTTPInputStream : Stream {
+			public HTTPResponseStreamMode Mode { get; private set; }
+			private Stream InputStream = null;
+			private long BytesRead = 0;
+			private long BytesLeft = 0;
+			public HTTPInputStream(HTTPContext context) {
+				String TransferEncoding = context.GetRequestHeader("Transfer-Encoding");
+				String ContentLength = context.GetRequestHeader("Content-Length");
+				InputStream = context.Reader;
+				if (TransferEncoding != null && TransferEncoding.StartsWith("chunked", StringComparison.InvariantCultureIgnoreCase)) {
+					Mode = HTTPResponseStreamMode.Chunked;
+				} else if (ContentLength != null) {
+					Mode = HTTPResponseStreamMode.Direct;
+					if (!long.TryParse(ContentLength, out BytesLeft)) BytesLeft = 0;
+				} else {
+					Mode = HTTPResponseStreamMode.None;
+				}
+			}
+			private int ReadDirect(Byte[] buffer, int offset, int count) {
+				if (count > BytesLeft) count = (int)BytesLeft;
+				if (count == 0) return 0;
+				int read = InputStream.Read(buffer, offset, count);
+				if (read >= 0) {
+					BytesRead += read;
+					BytesLeft -= read;
+				}
+				return read;
+			}
+			public override int Read(byte[] buffer, int offset, int count) {
+				if (offset < 0 || count < 0 || offset + count > buffer.Length) throw new ArgumentOutOfRangeException("buffer", "Offset and count arguments exceed the buffer dimensions");
+				switch (Mode) {
+					case HTTPResponseStreamMode.None: 
+						return 0;
+					case HTTPResponseStreamMode.Direct:
+						return ReadDirect(buffer, offset, count);
+					case HTTPResponseStreamMode.Chunked:
+						if (BytesLeft == 0) {
+							String length = ReadLine(InputStream);
+							if (!long.TryParse(length, out BytesLeft)) BytesLeft = 0;
+							if (BytesLeft == 0) {
+								while (true) {
+									String line = ReadLine(InputStream);
+									if (line == null || line.Length == 0) break;
+								}
+								Mode = HTTPResponseStreamMode.None;
+								return 0;
+							}
+						}
+						return ReadDirect(buffer, offset, count);
+					default:
+						return 0;
+				}
+			}
+			class CompletedAsyncResult : AsyncResultBase {
+				public int Count { get; private set; }
+				public CompletedAsyncResult(AsyncCallback callback, Object state, int count)
+					: base(callback, state) {
+					this.Count = count;
+					SetCompleted(true, null);
+				}
+			}
+			public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) {
+				if (BytesLeft > 0) {
+					if (count > BytesLeft) count = (int)BytesLeft;
+					if (count == 0) return new CompletedAsyncResult(callback, state, 0);
+					return InputStream.BeginRead(buffer, offset, count, callback, state);
+				} else {
+					int ret = Read(buffer, offset, count);
+					return new CompletedAsyncResult(callback, state, ret);
+				}
+			}
+			public override int EndRead(IAsyncResult asyncResult) {
+				CompletedAsyncResult car = asyncResult as CompletedAsyncResult;
+				if (car != null) {
+					return car.Count;
+				} else {
+					return InputStream.EndRead(asyncResult);
+				}
+			}
+			public override long Length {
+				get { return BytesRead + BytesLeft; }
+			}
+			public override long Position {
+				get { return BytesRead; }
+				set { throw new NotSupportedException(); }
+			}
+			public override bool CanRead {
+				get { return (BytesLeft > 0 || Mode == HTTPResponseStreamMode.Chunked) && InputStream.CanRead; }
+			}
+			public override bool CanTimeout {
+				get { return InputStream.CanTimeout; }
+			}
+			public override int ReadTimeout {
+				get { return InputStream.ReadTimeout; }
+				set { InputStream.ReadTimeout = value; }
+			}
+			protected override void Dispose(bool disposing) {
+				base.Dispose(disposing);
+				Byte[] dummy = new Byte[1024];
+				while (Read(dummy, 0, dummy.Length) != 0) ;
+			}
+
+			public override bool CanSeek { get { return false; } }
+			public override bool CanWrite { get { return false; } }
+			public override void Flush() { }
+			public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
+			public override void SetLength(long value) { throw new NotSupportedException(); }
+			public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); }
 		}
 
 		public HTTPContext(HTTPServer server, TCPStream stream) : this(server, stream, stream.Socket) { }
@@ -121,10 +413,10 @@
 			Reader.BeginPrebuffering(PrebufferCallback, null);
 		}
 
-		private String ReadLine() {
+		private static String ReadLine(Stream stream) {
 			StringBuilder s = new StringBuilder();
 			while (true) {
-				int b = Reader.ReadByte();
+				int b = stream.ReadByte();
 				if (b == -1) {
 					if (s.Length == 0) return null;
 					break;
@@ -137,6 +429,9 @@
 			}
 			return s.ToString();
 		}
+		private String ReadLine() {
+			return ReadLine(Reader);
+		}
 
 		private void PrebufferCallback(IAsyncResult ar) {
 			State = HTTPConnectionState.ReceivingRequest;
@@ -160,7 +455,7 @@
 				switch (request[2]) {
 					case "HTTP/1.0": HTTPVersion = 10; break;
 					case "HTTP/1.1": HTTPVersion = 11; break;
-					default: goto SendError400AndClose;
+					default: goto SendError505AndClose;
 				}
 				request = RequestAddress.Split(new Char[] { '?' });
 				RequestPath = Uri.UnescapeDataString(request[0]);
@@ -178,6 +473,7 @@
 				if (content == null) goto SendError500AndClose;
 				State = HTTPConnectionState.ProcessingRequest;
 				content.ServeRequest(this);
+				Close();
 			} catch (Exception ex) {
 				Console.Error.WriteLine(ex);
 				switch (State) {
@@ -197,6 +493,10 @@
 			State = HTTPConnectionState.ProcessingRequest;
 			SendErrorAndClose(500);
 			return;
+		SendError505AndClose:
+			State = HTTPConnectionState.ProcessingRequest;
+			SendErrorAndClose(400);
+			return;
 		}
 
 		public String GetRequestHeader(String name) {
@@ -215,26 +515,82 @@
 			return items;
 		}
 
-		public void SendErrorAndClose(int state) {
-			try {
-				SendStatus(state);
-				GetResponseStream();
-			} catch (Exception ex) {
-				Console.Error.WriteLine(ex);
+		private static String UnescapeUrlDataString(String text) {
+			return Uri.UnescapeDataString(text.Replace('+', ' '));
+		}
+		private static KeyValuePair<String, String>[] DecodeUrlEncodedFields(String data) {
+			List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();
+			foreach (String arg in data.Split('&')) {
+				String[] parts = arg.Split(new Char[] { '=' }, 2);
+				String key = UnescapeUrlDataString(parts[0]);
+				String value = (parts.Length > 1) ? UnescapeUrlDataString(parts[1]) : String.Empty;
+				list.Add(new KeyValuePair<string, string>(key, value));
 			}
-			Close();
+			return list.ToArray();
+		}
+
+		public String GetQueryParameter(String name) {
+			foreach (KeyValuePair<String, String> kvp in GetQueryParameters()) if (kvp.Key == name) return kvp.Value;
+			return null;
+		}
+		public String[] GetQueryParameters(String name) {
+			List<String> list = new List<string>();
+			foreach (KeyValuePair<String, String> kvp in GetQueryParameters()) if (kvp.Key == name) list.Add(kvp.Value);
+			return list.ToArray();
+		}
+		public KeyValuePair<String, String>[] GetQueryParameters() {
+			if (QueryParameters == null) QueryParameters = DecodeUrlEncodedFields(RequestQuery);
+			return QueryParameters;
+		}
+
+		public String GetPostParameter(String name) {
+			foreach (KeyValuePair<String, String> kvp in GetPostParameters()) if (kvp.Key == name) return kvp.Value;
+			return null;
+		}
+		public String[] GetPostParameters(String name) {
+			List<String> list = new List<string>();
+			foreach (KeyValuePair<String, String> kvp in GetPostParameters()) if (kvp.Key == name) list.Add(kvp.Value);
+			return list.ToArray();
+		}
+		public KeyValuePair<String, String>[] GetPostParameters() {
+			if (PostParameters == null) {
+				if (RequestMethod == "POST" && GetRequestHeader("Content-Type") == "application/x-www-form-urlencoded") {
+					String data;
+					using (StreamReader reader = new StreamReader(OpenRequestStream(), Encoding.UTF8)) data = reader.ReadToEnd();
+					PostParameters = DecodeUrlEncodedFields(data);
+				} else {
+					PostParameters = new KeyValuePair<string, string>[0];
+				}
+			}
+			return PostParameters;
+		}
+
+		public Stream OpenRequestStream() {
+			if (RequestStream == null) RequestStream = new HTTPInputStream(this);
+			return RequestStream;
+		}
+
+		private static String GetMessageForStatus(int code) {
+			switch (code) {
+				case 101: return "Switching Protocols";
+				case 200: return "OK";
+				case 301: return "Moved Permanently";
+				case 302: return "Found";
+				case 303: return "See Other";
+				case 304: return "Not Modified";
+				case 307: return "Temporary Redirect";
+				case 400: return "Bad Request";
+				case 401: return "Access denied";
+				case 403: return "Forbidden";
+				case 404: return "Not Found";
+				case 500: return "Internal Server Error";
+				case 505: return "HTTP Version Not Supported";
+				default: return "Unknown Status";
+			}
 		}
 
 		public void SendStatus(int code) {
-			String message;
-			switch (code) {
-				case 101: message = "Switching Protocols"; break;
-				case 200: message = "OK"; break;
-				case 400: message = "Bad Request"; break;
-				case 404: message = "Not Found"; break;
-				case 500: message = "Internal Server Error"; break;
-				default: message = "Unknown Status"; break;
-			}
+			String message = GetMessageForStatus(code);
 			SendStatus(code, message);
 		}
 		public void SendStatus(int code, String message) {
@@ -244,7 +600,7 @@
 			switch (HTTPVersion) {
 				case 10: sb.Append("1.0"); break;
 				case 11: sb.Append("1.1"); break;
-				default: throw new ArgumentException("The HTTP version is not supported", "HTTPVersion");
+				default: sb.Append("1.0"); break;
 			}
 			sb.Append(" ");
 			sb.Append(code);
@@ -257,7 +613,7 @@
 				SendHeader("Cache-Control", "no-store, no-cache, must-revalidate");
 				SendHeader("Cache-Control", "post-check=0, pre-check=0");
 				SendHeader("Pragma", "no-cache");
-				SendHeader("Server", "UCIS Webserver");
+				SendHeader("Server", "UCIS Embedded Webserver");
 				SendHeader("Connection", "Close");
 			}
 		}
@@ -266,17 +622,63 @@
 			if (State != HTTPConnectionState.SendingHeaders) throw new InvalidOperationException();
 			Writer.WriteLine(name + ": " + value);
 		}
-		public Stream GetResponseStream() {
+		public void SendErrorResponse(int state) {
+			String message = GetMessageForStatus(state);
+			try {
+				SendStatus(state, message);
+				SendHeader("Content-Type", "text/plain");
+				WriteResponseData(Encoding.ASCII.GetBytes(String.Format("Error {0}: {1}", state, message)));
+			} catch (Exception ex) {
+				Console.Error.WriteLine(ex);
+			}
+		}
+		public Stream OpenResponseStream(HTTPResponseStreamMode mode) {
+			if (ResponseStream != null) throw new InvalidOperationException("The response stream has already been opened");
+			return ResponseStream = new HTTPOutputStream(this, mode);
+		}
+		public Stream OpenResponseStream(long length) {
+			if (ResponseStream != null) throw new InvalidOperationException("The response stream has already been opened");
+			if (length < 0) throw new ArgumentException("Response length can not be negative", "length");
+			return ResponseStream = new HTTPOutputStream(this, HTTPResponseStreamMode.Direct, length);
+		}
+		public void WriteResponseData(Byte[] buffer) {
+			WriteResponseData(buffer, 0, buffer.Length);
+		}
+		public void WriteResponseData(Byte[] buffer, int offset, int count) {
+			if (offset < 0 || count < 0 || offset + count > buffer.Length) throw new ArgumentOutOfRangeException("buffer", "Offset and count arguments exceed the buffer dimensions");
+			SendHeader("Content-Length", count.ToString());
+			Stream stream = BeginResponseData();
+			stream.Write(buffer, offset, count);
+			EndResponseData();
+		}
+		private Stream BeginResponseData() {
 			if (State == HTTPConnectionState.ProcessingRequest) SendStatus(200);
 			if (State == HTTPConnectionState.SendingHeaders) {
 				Writer.WriteLine();
 				State = HTTPConnectionState.SendingContent;
 			}
-			if (State != HTTPConnectionState.SendingContent) throw new InvalidOperationException();
+			if (State != HTTPConnectionState.SendingContent) throw new InvalidOperationException("The response stream can not be opened in the current state");
+			return Reader;
+		}
+		private void EndResponseData() {
+			if (State == HTTPConnectionState.Completed || State == HTTPConnectionState.Closed) return;
+			OpenRequestStream().Close();
+			if (State != HTTPConnectionState.SendingContent) WriteResponseData(new Byte[0]);
+			State = HTTPConnectionState.Completed;
+		}
+
+		public Stream GetDirectStream() {
+			if (State == HTTPConnectionState.Closed) throw new InvalidOperationException("The context has been closed");
+			BeginResponseData();
+			State = HTTPConnectionState.Closed;
 			return Reader;
 		}
 
-		public void Close() {
+		private void SendErrorAndClose(int code) {
+			SendErrorResponse(code);
+			Close();
+		}
+		private void Close() {
 			if (State == HTTPConnectionState.Closed) return;
 			Reader.Close();
 			State = HTTPConnectionState.Closed;
@@ -315,7 +717,7 @@
 			if (c.Value != null) {
 				c.Value.ServeRequest(context);
 			} else {
-				context.SendErrorAndClose(404);
+				context.SendErrorResponse(404);
 			}
 		}
 	}
@@ -338,16 +740,13 @@
 		public void ServeRequest(HTTPContext context) {
 			ArraySegment<Byte> content = ContentBuffer;
 			if (content.Array == null) {
-				context.SendErrorAndClose(404);
+				context.SendErrorResponse(404);
 				return;
 			}
 			String contentType = ContentType;
 			context.SendStatus(200);
 			if (contentType != null) context.SendHeader("Content-Type", contentType);
-			context.SendHeader("Content-Length", content.Count.ToString());
-			Stream response = context.GetResponseStream();
-			response.Write(content.Array, content.Offset, content.Count);
-			response.Close();
+			context.WriteResponseData(content.Array, content.Offset, content.Count);
 		}
 	}
 	public class HTTPFileProvider : IHTTPContentProvider {
@@ -363,9 +762,8 @@
 				using (FileStream fs = File.OpenRead(FileName)) {
 					context.SendStatus(200);
 					context.SendHeader("Content-Type", ContentType);
-					context.SendHeader("Content-Length", fs.Length.ToString());
 					long left = fs.Length;
-					Stream response = context.GetResponseStream();
+					Stream response = context.OpenResponseStream(fs.Length);
 					byte[] buffer = new byte[1024 * 10];
 					while (fs.CanRead) {
 						int len = fs.Read(buffer, 0, buffer.Length);
@@ -376,7 +774,7 @@
 					response.Close();
 				}
 			} else {
-				context.SendErrorAndClose(404);
+				context.SendErrorResponse(404);
 			}
 		}
 	}
@@ -387,7 +785,7 @@
 		}
 		public void ServeRequest(HTTPContext context) {
 			if (!File.Exists(TarFileName)) {
-				context.SendErrorAndClose(404);
+				context.SendErrorResponse(404);
 				return;
 			}
 			String reqname1 = context.RequestPath;
@@ -399,7 +797,6 @@
 				if (!file.IsFile) continue;
 				if (!reqname1.Equals(file.Name, StringComparison.OrdinalIgnoreCase) && !reqname2.Equals(file.Name, StringComparison.OrdinalIgnoreCase)) continue;
 				context.SendStatus(200);
-				context.SendHeader("Content-Length", file.Size.ToString());
 				String ctype = null;
 				switch (Path.GetExtension(file.Name).ToLowerInvariant()) {
 					case ".txt": ctype = "text/plain"; break;
@@ -414,7 +811,7 @@
 					case ".ico": ctype = "image/x-icon"; break;
 				}
 				if (ctype != null) context.SendHeader("Content-Type", ctype);
-				using (Stream response = context.GetResponseStream(), source = file.GetStream()) {
+				using (Stream response = context.OpenResponseStream(file.Size), source = file.GetStream()) {
 					byte[] buffer = new byte[Math.Min(source.Length, 1024 * 10)];
 					while (source.CanRead) {
 						int len = source.Read(buffer, 0, buffer.Length);
@@ -424,7 +821,7 @@
 				}
 				return;
 			}
-			context.SendErrorAndClose(404);
+			context.SendErrorResponse(404);
 		}
 	}
 }
--- a/Net/WebSocketPacketStream.cs	Thu Nov 28 13:19:41 2013 +0100
+++ b/Net/WebSocketPacketStream.cs	Sun Feb 16 15:02:36 2014 +0100
@@ -40,7 +40,7 @@
 					context.SendHeader("Upgrade", "websocket");
 					context.SendHeader("Sec-WebSocket-Accept", hashedKey);
 					context.SendHeader("Sec-WebSocket-Protocol", binaryProtocol ? "binary" : "base64");
-					baseStream = context.GetResponseStream();
+					baseStream = context.GetDirectStream();
 				} else if (SecWebSocketKey1 != null && SecWebSocketKey2 != null) {
 					wsProtocol = 100;
 					Byte[] key = new Byte[4 + 4 + 8];
@@ -53,7 +53,7 @@
 					context.SendHeader("Sec-WebSocket-Protocol", binaryProtocol ? "binary" : "base64");
 					context.SendHeader("Sec-WebSocket-Origin", context.GetRequestHeader("Origin"));
 					context.SendHeader("Sec-WebSocket-Location", "ws://" + context.GetRequestHeader("Host") + context.RequestPath);
-					baseStream = context.GetResponseStream();
+					baseStream = context.GetDirectStream();
 					ReadAllBytes(key, 8, 8);
 					using (MD5 md5 = new MD5CryptoServiceProvider()) key = md5.ComputeHash(key);
 					baseStream.Write(key, 0, key.Length);
@@ -63,7 +63,7 @@
 				if (closed) baseStream.Close();
 			} catch (Exception) {
 				closed = true;
-				context.Close();
+				if (baseStream != null) baseStream.Close();
 			} finally {
 				negotiationDone = true;
 				negotiationEvent.Set();
@@ -71,7 +71,7 @@
 			return;
 Failure:
 			closed = true;
-			context.SendErrorAndClose(400);
+			context.SendErrorResponse(400);
 			return;
 		}
 
--- a/UCIS.Core.csproj	Thu Nov 28 13:19:41 2013 +0100
+++ b/UCIS.Core.csproj	Sun Feb 16 15:02:36 2014 +0100
@@ -176,6 +176,7 @@
     <Compile Include="Util\PrebufferingStream.cs" />
     <Compile Include="Util\QueuedPacketStream.cs" />
     <Compile Include="Util\TapeArchive.cs" />
+    <Compile Include="Util\InteropUtil.cs" />
     <Compile Include="Util\WorkQueue.cs" />
     <Compile Include="VNCServer\IFramebuffer.cs" />
     <Compile Include="VNCServer\VNCServer.cs" />
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Util/InteropUtil.cs	Sun Feb 16 15:02:36 2014 +0100
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace UCIS.Util {
+	public class PinnedObject : SafeHandle {
+		GCHandle gch;
+		public PinnedObject(Object obj)
+			: base(IntPtr.Zero, true) {
+			gch = GCHandle.Alloc(obj, GCHandleType.Pinned);
+			SetHandle(gch.AddrOfPinnedObject());
+		}
+		public override bool IsInvalid { get { return handle == IntPtr.Zero; } }
+		protected override bool ReleaseHandle() {
+			if (gch.IsAllocated) {
+				gch.Free();
+				return true;
+			} else {
+				return false;
+			}
+		}
+		public static implicit operator IntPtr(PinnedObject p) { return p.DangerousGetHandle(); }
+		public static implicit operator PinnedObject(Array o) { return new PinnedObject(o); }
+	}
+	public class PinnedString : SafeHandle {
+		public PinnedString(String str, Boolean unicode)
+			: base(IntPtr.Zero, true) {
+			SetHandle(unicode ? Marshal.StringToHGlobalUni(str) : Marshal.StringToHGlobalAnsi(str));
+		}
+		public override bool IsInvalid { get { return handle == IntPtr.Zero; } }
+		protected override bool ReleaseHandle() {
+			Marshal.FreeHGlobal(handle);
+			return true;
+		}
+		public static implicit operator IntPtr(PinnedString p) { return p.DangerousGetHandle(); }
+	}
+	public class PinnedStringAnsi : PinnedString {
+		public PinnedStringAnsi(String str) : base(str, false) { }
+		public static implicit operator PinnedStringAnsi(String s) { return new PinnedStringAnsi(s); }
+	}
+	public class PinnedStringUni : PinnedString {
+		public PinnedStringUni(String str) : base(str, true) { }
+		public static implicit operator PinnedStringUni(String s) { return new PinnedStringUni(s); }
+	}
+}