#include "ServerParser.h"
#include <sstream>
#include <exception>
namespace panda { namespace protocol { namespace websocket {
struct RequestFactory : http::RequestParser::IFactory {
http::RequestSP new_request () override {
return make_iptr<ConnectRequest>();
}
};
static RequestFactory request_factory;
ServerParser::ServerParser (const Parser::Config& cfg) : Parser(true, cfg), _connect_parser(&request_factory) {
_connect_parser.max_body_size = 0;
}
ConnectRequestSP ServerParser::accept (const string& buf) {
if (_flags[ACCEPT_PARSED]) throw Error("already parsed accept");
_connect_parser.max_headers_size = _max_handshake_size;
http::RequestParser::Result res = _connect_parser.parse(buf);
_connect_request = dynamic_pointer_cast<ConnectRequest>(res.request);
if (res.error) {
_flags.set(ACCEPT_PARSED);
_connect_request->error(res.error);
return _connect_request;
} else if (res.state != http::State::done) {
return nullptr;
}
_connect_request->process_headers();
_flags.set(ACCEPT_PARSED);
if (!_connect_request->error()) {
if (res.position != buf.size()) {
_connect_request->error(errc::garbage_after_connect);
} else {
_flags.set(ACCEPTED);
}
}
return _connect_request;
}
string ServerParser::accept_error () {
if (!_flags[ACCEPT_PARSED]) throw Error("accept not parsed yet");
if (established()) throw Error("already established");
if (!_connect_request->error()) throw Error("no errors found");
http::ResponseSP res = new http::Response();
res->headers.add("Content-Type", "text/plain");
if (!_connect_request->ws_version_supported()) {
res->code = 426;
res->message = "Upgrade Required";
res->body.parts.push_back("426 Upgrade Required");
string svers(50);
for (int v : supported_ws_versions) {
svers += string::from_number(v);
svers += ", ";
}
if (svers) svers.length(svers.length()-2);
res->headers.add("Sec-WebSocket-Version", svers);
}
else {
res->code = 400;
res->message = "Bad Request";
res->body.parts.push_back("400 Bad Request\n");
res->body.parts.push_back(_connect_request->error().what());
}
res->headers.set("Content-Length", panda::to_string(res->body.length()));
return res->to_string(_connect_request);
}
string ServerParser::accept_error (http::Response* res) {
if (!_flags[ACCEPT_PARSED]) throw Error("accept not parsed yet");
if (established()) throw Error("already established");
if (_connect_request->error()) return accept_error();
if (!res->code) {
res->code = 400;
res->message = "Bad Request";
}
else if (!res->message) res->message = "Unknown";
if (res->body.empty()) {
res->body.parts.push_back(string::from_number(res->code) + ' ' + res->message);
}
if (!res->headers.has("Content-Type")) res->headers.add("Content-Type", "text/plain");
if (!res->headers.has("Content-Length")) res->headers.add("Content-Length", panda::to_string(res->body.length()));
return res->to_string(_connect_request);
}
string ServerParser::accept_response (ConnectResponse* res) {
if (!accepted()) throw Error("client has not been accepted");
if (established()) throw Error("already established");
res->_ws_key = _connect_request->ws_key();
if (!res->ws_protocol()) res->ws_protocol(_connect_request->ws_protocol());
if (!res->ws_extensions_set()) res->ws_extensions(_connect_request->ws_extensions());
const auto& exts = res->ws_extensions();
HeaderValues used_extensions;
if (_deflate_cfg && exts.size()) {
// filter extensions
auto role = DeflateExt::Role::SERVER;
auto deflate_matches = DeflateExt::select(exts, *_deflate_cfg, role);
if (deflate_matches) {
_deflate_ext.reset(DeflateExt::uplift(deflate_matches, used_extensions, role));
}
}
res->ws_extensions(std::move(used_extensions));
_flags.set(ESTABLISHED);
_connect_request = NULL;
return res->to_string();
}
void ServerParser::reset () {
_connect_request = NULL;
_connect_parser.reset();
Parser::reset();
}
ServerParser::~ServerParser() {}
}}}