summaryrefslogtreecommitdiff
path: root/cpp/src/IceWS/Util.cpp
diff options
context:
space:
mode:
authorMark Spruiell <mes@zeroc.com>2014-03-19 12:45:55 -0700
committerMark Spruiell <mes@zeroc.com>2014-03-19 12:45:55 -0700
commitcdcffbcc3c3c052afdeb772ff0167e7a90b525bb (patch)
tree4f16ee41ef7d33394c44e9db81e4d6cd89908250 /cpp/src/IceWS/Util.cpp
parentfixing testicedist.py for 5487 (diff)
downloadice-cdcffbcc3c3c052afdeb772ff0167e7a90b525bb.tar.bz2
ice-cdcffbcc3c3c052afdeb772ff0167e7a90b525bb.tar.xz
ice-cdcffbcc3c3c052afdeb772ff0167e7a90b525bb.zip
merging javascript branch
Diffstat (limited to 'cpp/src/IceWS/Util.cpp')
-rw-r--r--cpp/src/IceWS/Util.cpp690
1 files changed, 690 insertions, 0 deletions
diff --git a/cpp/src/IceWS/Util.cpp b/cpp/src/IceWS/Util.cpp
new file mode 100644
index 00000000000..417acc68cb2
--- /dev/null
+++ b/cpp/src/IceWS/Util.cpp
@@ -0,0 +1,690 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2014 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.
+//
+// **********************************************************************
+
+#include <IceUtil/Config.h>
+#include <IceWS/Util.h>
+#include <Ice/LocalException.h>
+#include <IceUtil/StringUtil.h>
+
+#include <openssl/err.h>
+#include <openssl/sha.h>
+
+#include <IceUtil/DisableWarnings.h>
+
+using namespace std;
+using namespace Ice;
+using namespace IceWS;
+
+vector<unsigned char>
+IceWS::calcSHA1(const vector<unsigned char>& data)
+{
+ vector<unsigned char> hash(SHA_DIGEST_LENGTH);
+ ::SHA1(&data[0], static_cast<unsigned long>(data.size()), &hash[0]);
+ return hash;
+}
+
+WebSocketException::WebSocketException(const string& r) :
+ reason(r)
+{
+}
+
+IceWS::HttpParser::HttpParser() :
+ _type(TypeUnknown),
+ _versionMajor(0),
+ _versionMinor(0),
+ _status(0),
+ _state(StateInit)
+{
+}
+
+const Ice::Byte*
+IceWS::HttpParser::isCompleteMessage(const Ice::Byte* begin, const Ice::Byte* end) const
+{
+ const Ice::Byte* p = begin;
+
+ //
+ // Skip any leading CR-LF characters.
+ //
+ while(p < end)
+ {
+ char ch = static_cast<char>(*p);
+ if(ch != '\r' && ch != '\n')
+ {
+ break;
+ }
+ ++p;
+ }
+
+ //
+ // Look for adjacent CR-LF/CR-LF or LF/LF.
+ //
+ bool seenFirst = false;
+ while(p < end)
+ {
+ char ch = static_cast<char>(*p++);
+ if(ch == '\n')
+ {
+ if(seenFirst)
+ {
+ return p;
+ }
+ else
+ {
+ seenFirst = true;
+ }
+ }
+ else if(ch != '\r')
+ {
+ seenFirst = false;
+ }
+ }
+
+ return 0;
+}
+
+bool
+IceWS::HttpParser::parse(const Ice::Byte* begin, const Ice::Byte* end)
+{
+ const Ice::Byte* p = begin;
+ const Ice::Byte* start = 0;
+ const string::value_type CR = '\r';
+ const string::value_type LF = '\n';
+
+ if(_state == StateComplete)
+ {
+ _state = StateInit;
+ }
+
+ while(p != end && _state != StateComplete)
+ {
+ char c = static_cast<char>(*p);
+
+ switch(_state)
+ {
+ case StateInit:
+ {
+ _method.clear();
+ _uri.clear();
+ _versionMajor = -1;
+ _versionMinor = -1;
+ _status = -1;
+ _reason.clear();
+ _headers.clear();
+ _state = StateType;
+ continue;
+ }
+ case StateType:
+ {
+ if(c == CR || c == LF)
+ {
+ break;
+ }
+ else if(c == 'H')
+ {
+ //
+ // Could be the start of "HTTP/1.1" or "HEAD".
+ //
+ _state = StateTypeCheck;
+ break;
+ }
+ else
+ {
+ _state = StateRequest;
+ continue;
+ }
+ }
+ case StateTypeCheck:
+ {
+ if(c == 'T') // Continuing "H_T_TP/1.1"
+ {
+ _state = StateResponse;
+ }
+ else if(c == 'E') // Expecting "HEAD"
+ {
+ _state = StateRequest;
+ _method.push_back('H');
+ _method.push_back('E');
+ }
+ else
+ {
+ throw WebSocketException("malformed request or response");
+ }
+ break;
+ }
+ case StateRequest:
+ {
+ _type = TypeRequest;
+ _state = StateRequestMethod;
+ continue;
+ }
+ case StateRequestMethod:
+ {
+ if(c == ' ' || c == CR || c == LF)
+ {
+ _state = StateRequestMethodSP;
+ continue;
+ }
+ _method.push_back(c);
+ break;
+ }
+ case StateRequestMethodSP:
+ {
+ if(c == ' ')
+ {
+ break;
+ }
+ else if(c == CR || c == LF)
+ {
+ throw WebSocketException("malformed request");
+ }
+ _state = StateRequestURI;
+ continue;
+ }
+ case StateRequestURI:
+ {
+ if(c == ' ' || c == CR || c == LF)
+ {
+ _state = StateRequestURISP;
+ continue;
+ }
+ _uri.push_back(c);
+ break;
+ }
+ case StateRequestURISP:
+ {
+ if(c == ' ')
+ {
+ break;
+ }
+ else if(c == CR || c == LF)
+ {
+ throw WebSocketException("malformed request");
+ }
+ _state = StateVersion;
+ continue;
+ }
+ case StateRequestLF:
+ {
+ if(c != LF)
+ {
+ throw WebSocketException("malformed request");
+ }
+ _state = StateHeaderFieldStart;
+ break;
+ }
+ case StateHeaderFieldStart:
+ {
+ //
+ // 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 = StateHeaderFieldEndLF;
+ break;
+ }
+ else if(c == LF)
+ {
+ _state = StateComplete;
+ break;
+ }
+ else if(c == ' ')
+ {
+ //
+ // Could be a continuation line.
+ //
+ _state = StateHeaderFieldContStart;
+ break;
+ }
+
+ _state = StateHeaderFieldNameStart;
+ continue;
+ }
+ case StateHeaderFieldContStart:
+ {
+ if(c == ' ')
+ {
+ break;
+ }
+
+ _state = StateHeaderFieldCont;
+ start = p;
+ continue;
+ }
+ case StateHeaderFieldCont:
+ {
+ if(c == CR || c == LF)
+ {
+ if(p > start)
+ {
+ if(_headerName.empty())
+ {
+ throw WebSocketException("malformed header");
+ }
+ HeaderMap::iterator q = _headers.find(_headerName);
+ assert(q != _headers.end());
+ string newValue = q->second + " " + string(start, p);
+ _headers[_headerName] = newValue;
+ _state = c == CR ? StateHeaderFieldLF : StateHeaderFieldStart;
+ }
+ else
+ {
+ //
+ // Could mark the end of the header fields.
+ //
+ _state = c == CR ? StateHeaderFieldEndLF : StateComplete;
+ }
+ }
+
+ break;
+ }
+ case StateHeaderFieldNameStart:
+ {
+ assert(c != ' ');
+ start = p;
+ _headerName.clear();
+ _state = StateHeaderFieldName;
+ continue;
+ }
+ case StateHeaderFieldName:
+ {
+ if(c == ' ' || c == ':')
+ {
+ _state = StateHeaderFieldNameEnd;
+ continue;
+ }
+ else if(c == CR || c == LF)
+ {
+ throw WebSocketException("malformed header");
+ }
+ break;
+ }
+ case StateHeaderFieldNameEnd:
+ {
+ if(_headerName.empty())
+ {
+ _headerName = IceUtilInternal::toLower(string(start, p));
+ HeaderMap::iterator q = _headers.find(_headerName);
+ //
+ // Add a placeholder entry if necessary.
+ //
+ if(q == _headers.end())
+ {
+ _headers[_headerName] = "";
+ }
+ }
+
+ if(c == ' ')
+ {
+ break;
+ }
+ else if(c != ':' || p == start)
+ {
+ throw WebSocketException("malformed header");
+ }
+
+ _state = StateHeaderFieldValueStart;
+ break;
+ }
+ case StateHeaderFieldValueStart:
+ {
+ if(c == ' ')
+ {
+ break;
+ }
+
+ //
+ // Check for "Name:\r\n"
+ //
+ if(c == CR)
+ {
+ _state = StateHeaderFieldLF;
+ break;
+ }
+ else if(c == LF)
+ {
+ _state = StateHeaderFieldStart;
+ break;
+ }
+
+ start = p;
+ _state = StateHeaderFieldValue;
+ continue;
+ }
+ case StateHeaderFieldValue:
+ {
+ if(c == CR || c == LF)
+ {
+ _state = StateHeaderFieldValueEnd;
+ continue;
+ }
+ break;
+ }
+ case StateHeaderFieldValueEnd:
+ {
+ assert(c == CR || c == LF);
+ if(p > start)
+ {
+ HeaderMap::iterator q = _headers.find(_headerName);
+ if(q == _headers.end() || q->second.empty())
+ {
+ _headers[_headerName] = string(start, p);
+ }
+ else
+ {
+ _headers[_headerName] = q->second + ", " + string(start, p);
+ }
+ }
+
+ if(c == CR)
+ {
+ _state = StateHeaderFieldLF;
+ }
+ else
+ {
+ _state = StateHeaderFieldStart;
+ }
+ break;
+ }
+ case StateHeaderFieldLF:
+ {
+ if(c != LF)
+ {
+ throw WebSocketException("malformed header");
+ }
+ _state = StateHeaderFieldStart;
+ break;
+ }
+ case StateHeaderFieldEndLF:
+ {
+ if(c != LF)
+ {
+ throw WebSocketException("malformed header");
+ }
+ _state = StateComplete;
+ break;
+ }
+ case StateVersion:
+ {
+ if(c != 'H')
+ {
+ throw WebSocketException("malformed version");
+ }
+ _state = StateVersionH;
+ break;
+ }
+ case StateVersionH:
+ {
+ if(c != 'T')
+ {
+ throw WebSocketException("malformed version");
+ }
+ _state = StateVersionHT;
+ break;
+ }
+ case StateVersionHT:
+ {
+ if(c != 'T')
+ {
+ throw WebSocketException("malformed version");
+ }
+ _state = StateVersionHTT;
+ break;
+ }
+ case StateVersionHTT:
+ {
+ if(c != 'P')
+ {
+ throw WebSocketException("malformed version");
+ }
+ _state = StateVersionHTTP;
+ break;
+ }
+ case StateVersionHTTP:
+ {
+ if(c != '/')
+ {
+ throw WebSocketException("malformed version");
+ }
+ _state = StateVersionMajor;
+ break;
+ }
+ case StateVersionMajor:
+ {
+ if(c == '.')
+ {
+ if(_versionMajor == -1)
+ {
+ throw WebSocketException("malformed version");
+ }
+ _state = StateVersionMinor;
+ break;
+ }
+ else if(c < '0' || c > '9')
+ {
+ throw WebSocketException("malformed version");
+ }
+ if(_versionMajor == -1)
+ {
+ _versionMajor = 0;
+ }
+ _versionMajor *= 10;
+ _versionMajor += (c - '0');
+ break;
+ }
+ case StateVersionMinor:
+ {
+ if(c == CR)
+ {
+ if(_versionMinor == -1 || _type != TypeRequest)
+ {
+ throw WebSocketException("malformed version");
+ }
+ _state = StateRequestLF;
+ break;
+ }
+ else if(c == LF)
+ {
+ if(_versionMinor == -1 || _type != TypeRequest)
+ {
+ throw WebSocketException("malformed version");
+ }
+ _state = StateHeaderFieldStart;
+ break;
+ }
+ else if(c == ' ')
+ {
+ if(_versionMinor == -1 || _type != TypeResponse)
+ {
+ throw WebSocketException("malformed version");
+ }
+ _state = StateResponseVersionSP;
+ break;
+ }
+ else if(c < '0' || c > '9')
+ {
+ throw WebSocketException("malformed version");
+ }
+ if(_versionMinor == -1)
+ {
+ _versionMinor = 0;
+ }
+ _versionMinor *= 10;
+ _versionMinor += (c - '0');
+ break;
+ }
+ case StateResponse:
+ {
+ _type = TypeResponse;
+ _state = StateVersionHT;
+ continue;
+ }
+ case StateResponseVersionSP:
+ {
+ if(c == ' ')
+ {
+ break;
+ }
+
+ _state = StateResponseStatus;
+ continue;
+ }
+ case StateResponseStatus:
+ {
+ // TODO: Is reason string optional?
+ if(c == CR)
+ {
+ if(_status == -1)
+ {
+ throw WebSocketException("malformed response status");
+ }
+ _state = StateResponseLF;
+ break;
+ }
+ else if(c == LF)
+ {
+ if(_status == -1)
+ {
+ throw WebSocketException("malformed response status");
+ }
+ _state = StateHeaderFieldStart;
+ break;
+ }
+ else if(c == ' ')
+ {
+ if(_status == -1)
+ {
+ throw WebSocketException("malformed response status");
+ }
+ _state = StateResponseReasonStart;
+ break;
+ }
+ else if(c < '0' || c > '9')
+ {
+ throw WebSocketException("malformed response status");
+ }
+ if(_status == -1)
+ {
+ _status = 0;
+ }
+ _status *= 10;
+ _status += (c - '0');
+ break;
+ }
+ case StateResponseReasonStart:
+ {
+ //
+ // Skip leading spaces.
+ //
+ if(c == ' ')
+ {
+ break;
+ }
+
+ _state = StateResponseReason;
+ start = p;
+ continue;
+ }
+ case StateResponseReason:
+ {
+ if(c == CR || c == LF)
+ {
+ if(p > start)
+ {
+ _reason = string(start, p);
+ }
+ _state = c == CR ? StateResponseLF : StateHeaderFieldStart;
+ }
+
+ break;
+ }
+ case StateResponseLF:
+ {
+ if(c != LF)
+ {
+ throw WebSocketException("malformed status line");
+ }
+ _state = StateHeaderFieldStart;
+ break;
+ }
+ case StateComplete:
+ {
+ assert(false); // Shouldn't reach
+ }
+ }
+
+ ++p;
+ }
+
+ return _state == StateComplete;
+}
+
+IceWS::HttpParser::Type
+IceWS::HttpParser::type() const
+{
+ return _type;
+}
+
+string
+IceWS::HttpParser::method() const
+{
+ assert(_type == TypeRequest);
+ return _method;
+}
+
+string
+IceWS::HttpParser::uri() const
+{
+ assert(_type == TypeRequest);
+ return _uri;
+}
+
+int
+IceWS::HttpParser::versionMajor() const
+{
+ return _versionMajor;
+}
+
+int
+IceWS::HttpParser::versionMinor() const
+{
+ return _versionMinor;
+}
+
+int
+IceWS::HttpParser::status() const
+{
+ return _status;
+}
+
+string
+IceWS::HttpParser::reason() const
+{
+ return _reason;
+}
+
+bool
+IceWS::HttpParser::getHeader(const string& name, string& value, bool toLower) const
+{
+ HeaderMap::const_iterator q = _headers.find(IceUtilInternal::toLower(name));
+ if(q != _headers.end())
+ {
+ value = toLower ? IceUtilInternal::toLower(IceUtilInternal::trim(q->second)) : IceUtilInternal::trim(q->second);
+ return true;
+ }
+
+ return false;
+}
+
+const IceWS::HttpParser::HeaderMap&
+IceWS::HttpParser::headers() const
+{
+ return _headers;
+}