summaryrefslogtreecommitdiff
path: root/csharp/src/Ice/HttpParser.cs
diff options
context:
space:
mode:
authorMatthew Newhook <matthew@zeroc.com>2015-03-21 15:35:40 -0230
committerMatthew Newhook <matthew@zeroc.com>2015-03-21 15:35:40 -0230
commit630a37d2fe66f24518299e705f958b571803c522 (patch)
tree969723791bdc4d73bb099c19d45554d0ca241ad9 /csharp/src/Ice/HttpParser.cs
parentFix some README.md markdown formatting (diff)
downloadice-630a37d2fe66f24518299e705f958b571803c522.tar.bz2
ice-630a37d2fe66f24518299e705f958b571803c522.tar.xz
ice-630a37d2fe66f24518299e705f958b571803c522.zip
py -> python
rb -> ruby objc -> objective-c cs -> csharp
Diffstat (limited to 'csharp/src/Ice/HttpParser.cs')
-rw-r--r--csharp/src/Ice/HttpParser.cs774
1 files changed, 774 insertions, 0 deletions
diff --git a/csharp/src/Ice/HttpParser.cs b/csharp/src/Ice/HttpParser.cs
new file mode 100644
index 00000000000..32399400e34
--- /dev/null
+++ b/csharp/src/Ice/HttpParser.cs
@@ -0,0 +1,774 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+namespace IceInternal
+{
+ using System;
+ using System.Diagnostics;
+ using System.Collections.Generic;
+ using System.Text;
+
+ internal sealed class WebSocketException : System.Exception
+ {
+ internal WebSocketException() :
+ base("", null)
+ {
+ }
+
+ internal WebSocketException(string message) :
+ base(message, null)
+ {
+ }
+
+ internal WebSocketException(string message, System.Exception cause) :
+ base(message, cause)
+ {
+ }
+
+ internal WebSocketException(System.Exception cause) :
+ base("", cause)
+ {
+ }
+ }
+
+ internal sealed class HttpParser
+ {
+ internal HttpParser()
+ {
+ _type = Type.Unknown;
+ _versionMajor = 0;
+ _versionMinor = 0;
+ _status = 0;
+ _state = State.Init;
+ }
+
+ internal enum Type
+ {
+ Unknown,
+ Request,
+ Response
+ };
+
+ internal int isCompleteMessage(IceInternal.ByteBuffer buf, int begin, int end)
+ {
+ byte[] raw = buf.rawBytes();
+ int p = begin;
+
+ //
+ // Skip any leading CR-LF characters.
+ //
+ while(p < end)
+ {
+ byte ch = raw[p];
+ if(ch != (byte)'\r' && ch != (byte)'\n')
+ {
+ break;
+ }
+ ++p;
+ }
+
+ //
+ // Look for adjacent CR-LF/CR-LF or LF/LF.
+ //
+ bool seenFirst = false;
+ while(p < end)
+ {
+ byte ch = raw[p++];
+ if(ch == (byte)'\n')
+ {
+ if(seenFirst)
+ {
+ return p;
+ }
+ else
+ {
+ seenFirst = true;
+ }
+ }
+ else if(ch != (byte)'\r')
+ {
+ seenFirst = false;
+ }
+ }
+
+ return -1;
+ }
+
+ internal bool parse(IceInternal.ByteBuffer buf, int begin, int end)
+ {
+ byte[] raw = buf.rawBytes();
+ int p = begin;
+ int start = 0;
+ const char CR = '\r';
+ const char LF = '\n';
+
+ if(_state == State.Complete)
+ {
+ _state = State.Init;
+ }
+
+ while(p != end && _state != State.Complete)
+ {
+ char c = (char)raw[p];
+
+ switch(_state)
+ {
+ case State.Init:
+ {
+ _method = new StringBuilder();
+ _uri = new StringBuilder();
+ _versionMajor = -1;
+ _versionMinor = -1;
+ _status = -1;
+ _reason = "";
+ _headers.Clear();
+ _state = State.Type;
+ continue;
+ }
+ case State.Type:
+ {
+ if(c == CR || c == LF)
+ {
+ break;
+ }
+ else if(c == 'H')
+ {
+ //
+ // Could be the start of "HTTP/1.1" or "HEAD".
+ //
+ _state = State.TypeCheck;
+ break;
+ }
+ else
+ {
+ _state = State.Request;
+ continue;
+ }
+ }
+ case State.TypeCheck:
+ {
+ if(c == 'T') // Continuing "H_T_TP/1.1"
+ {
+ _state = State.Response;
+ }
+ else if(c == 'E') // Expecting "HEAD"
+ {
+ _state = State.Request;
+ _method.Append('H');
+ _method.Append('E');
+ }
+ else
+ {
+ throw new WebSocketException("malformed request or response");
+ }
+ break;
+ }
+ case State.Request:
+ {
+ _type = Type.Request;
+ _state = State.RequestMethod;
+ continue;
+ }
+ case State.RequestMethod:
+ {
+ if(c == ' ' || c == CR || c == LF)
+ {
+ _state = State.RequestMethodSP;
+ continue;
+ }
+ _method.Append(c);
+ break;
+ }
+ case State.RequestMethodSP:
+ {
+ if(c == ' ')
+ {
+ break;
+ }
+ else if(c == CR || c == LF)
+ {
+ throw new WebSocketException("malformed request");
+ }
+ _state = State.RequestURI;
+ continue;
+ }
+ case State.RequestURI:
+ {
+ if(c == ' ' || c == CR || c == LF)
+ {
+ _state = State.RequestURISP;
+ continue;
+ }
+ _uri.Append(c);
+ break;
+ }
+ case State.RequestURISP:
+ {
+ if(c == ' ')
+ {
+ break;
+ }
+ else if(c == CR || c == LF)
+ {
+ throw new WebSocketException("malformed request");
+ }
+ _state = State.Version;
+ continue;
+ }
+ case State.RequestLF:
+ {
+ if(c != LF)
+ {
+ throw new WebSocketException("malformed request");
+ }
+ _state = State.HeaderFieldStart;
+ break;
+ }
+ case State.HeaderFieldStart:
+ {
+ //
+ // We've already seen a LF to reach this state.
+ //
+ // Another CR or LF indicates the end of the header fields.
+ //
+ if(c == CR)
+ {
+ _state = State.HeaderFieldEndLF;
+ break;
+ }
+ else if(c == LF)
+ {
+ _state = State.Complete;
+ break;
+ }
+ else if(c == ' ')
+ {
+ //
+ // Could be a continuation line.
+ //
+ _state = State.HeaderFieldContStart;
+ break;
+ }
+
+ _state = State.HeaderFieldNameStart;
+ continue;
+ }
+ case State.HeaderFieldContStart:
+ {
+ if(c == ' ')
+ {
+ break;
+ }
+
+ _state = State.HeaderFieldCont;
+ start = p;
+ continue;
+ }
+ case State.HeaderFieldCont:
+ {
+ if(c == CR || c == LF)
+ {
+ if(p > start)
+ {
+ if(_headerName.Length == 0)
+ {
+ throw new WebSocketException("malformed header");
+ }
+ Debug.Assert(_headers.ContainsKey(_headerName));
+ string s = _headers[_headerName];
+ StringBuilder newValue = new StringBuilder(s);
+ newValue.Append(' ');
+ for(int i = start; i < p; ++i)
+ {
+ newValue.Append((char)raw[i]);
+ }
+ _headers[_headerName] = newValue.ToString();
+ _state = c == CR ? State.HeaderFieldLF : State.HeaderFieldStart;
+ }
+ else
+ {
+ //
+ // Could mark the end of the header fields.
+ //
+ _state = c == CR ? State.HeaderFieldEndLF : State.Complete;
+ }
+ }
+
+ break;
+ }
+ case State.HeaderFieldNameStart:
+ {
+ Debug.Assert(c != ' ');
+ start = p;
+ _headerName = "";
+ _state = State.HeaderFieldName;
+ continue;
+ }
+ case State.HeaderFieldName:
+ {
+ if(c == ' ' || c == ':')
+ {
+ _state = State.HeaderFieldNameEnd;
+ continue;
+ }
+ else if(c == CR || c == LF)
+ {
+ throw new WebSocketException("malformed header");
+ }
+ break;
+ }
+ case State.HeaderFieldNameEnd:
+ {
+ if(_headerName.Length == 0)
+ {
+ StringBuilder str = new StringBuilder();
+ for(int i = start; i < p; ++i)
+ {
+ str.Append((char)raw[i]);
+ }
+ _headerName = str.ToString().ToLower();
+ //
+ // Add a placeholder entry if necessary.
+ //
+ if(!_headers.ContainsKey(_headerName))
+ {
+ _headers[_headerName] = "";
+ _headerNames[_headerName] = str.ToString();
+ }
+ }
+
+ if(c == ' ')
+ {
+ break;
+ }
+ else if(c != ':' || p == start)
+ {
+ throw new WebSocketException("malformed header");
+ }
+
+ _state = State.HeaderFieldValueStart;
+ break;
+ }
+ case State.HeaderFieldValueStart:
+ {
+ if(c == ' ')
+ {
+ break;
+ }
+
+ //
+ // Check for "Name:\r\n"
+ //
+ if(c == CR)
+ {
+ _state = State.HeaderFieldLF;
+ break;
+ }
+ else if(c == LF)
+ {
+ _state = State.HeaderFieldStart;
+ break;
+ }
+
+ start = p;
+ _state = State.HeaderFieldValue;
+ continue;
+ }
+ case State.HeaderFieldValue:
+ {
+ if(c == CR || c == LF)
+ {
+ _state = State.HeaderFieldValueEnd;
+ continue;
+ }
+ break;
+ }
+ case State.HeaderFieldValueEnd:
+ {
+ Debug.Assert(c == CR || c == LF);
+ if(p > start)
+ {
+ StringBuilder str = new StringBuilder();
+ for(int i = start; i < p; ++i)
+ {
+ str.Append((char)raw[i]);
+ }
+ string s = null;
+ if(!_headers.TryGetValue(_headerName, out s) || s.Length == 0)
+ {
+ _headers[_headerName] = str.ToString();
+ }
+ else
+ {
+ _headers[_headerName] = s + ", " + str.ToString();
+ }
+ }
+
+ if(c == CR)
+ {
+ _state = State.HeaderFieldLF;
+ }
+ else
+ {
+ _state = State.HeaderFieldStart;
+ }
+ break;
+ }
+ case State.HeaderFieldLF:
+ {
+ if(c != LF)
+ {
+ throw new WebSocketException("malformed header");
+ }
+ _state = State.HeaderFieldStart;
+ break;
+ }
+ case State.HeaderFieldEndLF:
+ {
+ if(c != LF)
+ {
+ throw new WebSocketException("malformed header");
+ }
+ _state = State.Complete;
+ break;
+ }
+ case State.Version:
+ {
+ if(c != 'H')
+ {
+ throw new WebSocketException("malformed version");
+ }
+ _state = State.VersionH;
+ break;
+ }
+ case State.VersionH:
+ {
+ if(c != 'T')
+ {
+ throw new WebSocketException("malformed version");
+ }
+ _state = State.VersionHT;
+ break;
+ }
+ case State.VersionHT:
+ {
+ if(c != 'T')
+ {
+ throw new WebSocketException("malformed version");
+ }
+ _state = State.VersionHTT;
+ break;
+ }
+ case State.VersionHTT:
+ {
+ if(c != 'P')
+ {
+ throw new WebSocketException("malformed version");
+ }
+ _state = State.VersionHTTP;
+ break;
+ }
+ case State.VersionHTTP:
+ {
+ if(c != '/')
+ {
+ throw new WebSocketException("malformed version");
+ }
+ _state = State.VersionMajor;
+ break;
+ }
+ case State.VersionMajor:
+ {
+ if(c == '.')
+ {
+ if(_versionMajor == -1)
+ {
+ throw new WebSocketException("malformed version");
+ }
+ _state = State.VersionMinor;
+ break;
+ }
+ else if(c < '0' || c > '9')
+ {
+ throw new WebSocketException("malformed version");
+ }
+ if(_versionMajor == -1)
+ {
+ _versionMajor = 0;
+ }
+ _versionMajor *= 10;
+ _versionMajor += (int)(c - '0');
+ break;
+ }
+ case State.VersionMinor:
+ {
+ if(c == CR)
+ {
+ if(_versionMinor == -1 || _type != Type.Request)
+ {
+ throw new WebSocketException("malformed version");
+ }
+ _state = State.RequestLF;
+ break;
+ }
+ else if(c == LF)
+ {
+ if(_versionMinor == -1 || _type != Type.Request)
+ {
+ throw new WebSocketException("malformed version");
+ }
+ _state = State.HeaderFieldStart;
+ break;
+ }
+ else if(c == ' ')
+ {
+ if(_versionMinor == -1 || _type != Type.Response)
+ {
+ throw new WebSocketException("malformed version");
+ }
+ _state = State.ResponseVersionSP;
+ break;
+ }
+ else if(c < '0' || c > '9')
+ {
+ throw new WebSocketException("malformed version");
+ }
+ if(_versionMinor == -1)
+ {
+ _versionMinor = 0;
+ }
+ _versionMinor *= 10;
+ _versionMinor += (int)(c - '0');
+ break;
+ }
+ case State.Response:
+ {
+ _type = Type.Response;
+ _state = State.VersionHT;
+ continue;
+ }
+ case State.ResponseVersionSP:
+ {
+ if(c == ' ')
+ {
+ break;
+ }
+
+ _state = State.ResponseStatus;
+ continue;
+ }
+ case State.ResponseStatus:
+ {
+ // TODO: Is reason string optional?
+ if(c == CR)
+ {
+ if(_status == -1)
+ {
+ throw new WebSocketException("malformed response status");
+ }
+ _state = State.ResponseLF;
+ break;
+ }
+ else if(c == LF)
+ {
+ if(_status == -1)
+ {
+ throw new WebSocketException("malformed response status");
+ }
+ _state = State.HeaderFieldStart;
+ break;
+ }
+ else if(c == ' ')
+ {
+ if(_status == -1)
+ {
+ throw new WebSocketException("malformed response status");
+ }
+ _state = State.ResponseReasonStart;
+ break;
+ }
+ else if(c < '0' || c > '9')
+ {
+ throw new WebSocketException("malformed response status");
+ }
+ if(_status == -1)
+ {
+ _status = 0;
+ }
+ _status *= 10;
+ _status += (int)(c - '0');
+ break;
+ }
+ case State.ResponseReasonStart:
+ {
+ //
+ // Skip leading spaces.
+ //
+ if(c == ' ')
+ {
+ break;
+ }
+
+ _state = State.ResponseReason;
+ start = p;
+ continue;
+ }
+ case State.ResponseReason:
+ {
+ if(c == CR || c == LF)
+ {
+ if(p > start)
+ {
+ StringBuilder str = new StringBuilder();
+ for(int i = start; i < p; ++i)
+ {
+ str.Append((char)raw[i]);
+ }
+ _reason = str.ToString();
+ }
+ _state = c == CR ? State.ResponseLF : State.HeaderFieldStart;
+ }
+
+ break;
+ }
+ case State.ResponseLF:
+ {
+ if(c != LF)
+ {
+ throw new WebSocketException("malformed status line");
+ }
+ _state = State.HeaderFieldStart;
+ break;
+ }
+ case State.Complete:
+ {
+ Debug.Assert(false); // Shouldn't reach
+ break;
+ }
+ }
+
+ ++p;
+ }
+
+ return _state == State.Complete;
+ }
+
+ internal Type type()
+ {
+ return _type;
+ }
+
+ internal string method()
+ {
+ Debug.Assert(_type == Type.Request);
+ return _method.ToString();
+ }
+
+ internal string uri()
+ {
+ Debug.Assert(_type == Type.Request);
+ return _uri.ToString();
+ }
+
+ internal int versionMajor()
+ {
+ return _versionMajor;
+ }
+
+ internal int versionMinor()
+ {
+ return _versionMinor;
+ }
+
+ internal int status()
+ {
+ return _status;
+ }
+
+ internal string reason()
+ {
+ return _reason;
+ }
+
+ internal string getHeader(string name, bool toLower)
+ {
+ string s = null;
+ if(_headers.TryGetValue(name.ToLower(), out s))
+ {
+ return toLower ? s.Trim().ToLower() : s.Trim();
+ }
+
+ return null;
+ }
+
+ internal Dictionary<string, string> getHeaders()
+ {
+ Dictionary<string, string> dict = new Dictionary<string, string>();
+ foreach(KeyValuePair<string, string> e in _headers)
+ {
+ dict[_headerNames[e.Key]] = e.Value.Trim();
+ }
+ return dict;
+ }
+
+ private Type _type;
+
+ private StringBuilder _method = new StringBuilder();
+ private StringBuilder _uri = new StringBuilder();
+
+ private Dictionary<string, string> _headers = new Dictionary<string, string>();
+ private Dictionary<string, string> _headerNames = new Dictionary<string, string>();
+ private string _headerName = "";
+
+ private int _versionMajor;
+ private int _versionMinor;
+
+ private int _status;
+ private string _reason;
+
+ private enum State
+ {
+ Init,
+ Type,
+ TypeCheck,
+ Request,
+ RequestMethod,
+ RequestMethodSP,
+ RequestURI,
+ RequestURISP,
+ RequestLF,
+ HeaderFieldStart,
+ HeaderFieldContStart,
+ HeaderFieldCont,
+ HeaderFieldNameStart,
+ HeaderFieldName,
+ HeaderFieldNameEnd,
+ HeaderFieldValueStart,
+ HeaderFieldValue,
+ HeaderFieldValueEnd,
+ HeaderFieldLF,
+ HeaderFieldEndLF,
+ Version,
+ VersionH,
+ VersionHT,
+ VersionHTT,
+ VersionHTTP,
+ VersionMajor,
+ VersionMinor,
+ Response,
+ ResponseVersionSP,
+ ResponseStatus,
+ ResponseReasonStart,
+ ResponseReason,
+ ResponseLF,
+ Complete
+ };
+ private State _state;
+ }
+}