# HG changeset patch # User Ivo Smits # Date 1392559356 -3600 # Node ID 4e4c600031e27f5027375dda560235958c46fbac # Parent 1a10ca0f662e6a373bfc9b349fa59be3120536ad# Parent 8f31b164ce7e6b3e653d819889b703667299a464 Merge FBGUI updates diff -r 8f31b164ce7e -r 4e4c600031e2 FBGUI/FBGUI.cs --- 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 controls = new List(); 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)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; diff -r 8f31b164ce7e -r 4e4c600031e2 NaCl/crypto_verify/16.cs --- 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; } } diff -r 8f31b164ce7e -r 4e4c600031e2 NaCl/crypto_verify/32.cs --- 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; } } diff -r 8f31b164ce7e -r 4e4c600031e2 Net/HTTP.cs --- 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 RequestHeaders; private HTTPConnectionState State = HTTPConnectionState.Starting; - + private KeyValuePair[] 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[] DecodeUrlEncodedFields(String data) { + List> list = new List>(); + 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(key, value)); } - Close(); + return list.ToArray(); + } + + public String GetQueryParameter(String name) { + foreach (KeyValuePair kvp in GetQueryParameters()) if (kvp.Key == name) return kvp.Value; + return null; + } + public String[] GetQueryParameters(String name) { + List list = new List(); + foreach (KeyValuePair kvp in GetQueryParameters()) if (kvp.Key == name) list.Add(kvp.Value); + return list.ToArray(); + } + public KeyValuePair[] GetQueryParameters() { + if (QueryParameters == null) QueryParameters = DecodeUrlEncodedFields(RequestQuery); + return QueryParameters; + } + + public String GetPostParameter(String name) { + foreach (KeyValuePair kvp in GetPostParameters()) if (kvp.Key == name) return kvp.Value; + return null; + } + public String[] GetPostParameters(String name) { + List list = new List(); + foreach (KeyValuePair kvp in GetPostParameters()) if (kvp.Key == name) list.Add(kvp.Value); + return list.ToArray(); + } + public KeyValuePair[] 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[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 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); } } } diff -r 8f31b164ce7e -r 4e4c600031e2 Net/WebSocketPacketStream.cs --- 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; } diff -r 8f31b164ce7e -r 4e4c600031e2 UCIS.Core.csproj --- 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 @@ + diff -r 8f31b164ce7e -r 4e4c600031e2 Util/InteropUtil.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); } + } +}