diff options
Diffstat (limited to 'cpp/src/IceWS/Util.cpp')
-rw-r--r-- | cpp/src/IceWS/Util.cpp | 690 |
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; +} |