diff options
author | Matthew Newhook <matthew@zeroc.com> | 2015-03-21 15:35:40 -0230 |
---|---|---|
committer | Matthew Newhook <matthew@zeroc.com> | 2015-03-21 15:35:40 -0230 |
commit | 630a37d2fe66f24518299e705f958b571803c522 (patch) | |
tree | 969723791bdc4d73bb099c19d45554d0ca241ad9 /csharp/src/Ice/HttpParser.cs | |
parent | Fix some README.md markdown formatting (diff) | |
download | ice-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.cs | 774 |
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; + } +} |