From 640efb3e158a51737c4038cdc16d8c93bd355bd9 Mon Sep 17 00:00:00 2001 From: jezal Date: Mon, 15 Sep 2014 16:46:31 +0400 Subject: [PATCH] New HTTP server --- src/System/HttpParser.cpp | 215 ++++++++++++++++++++++++++++++++++++ src/System/HttpParser.h | 47 ++++++++ src/System/HttpRequest.cpp | 73 ++++++++++++ src/System/HttpRequest.h | 53 +++++++++ src/System/HttpResponse.cpp | 83 ++++++++++++++ src/System/HttpResponse.h | 57 ++++++++++ 6 files changed, 528 insertions(+) create mode 100644 src/System/HttpParser.cpp create mode 100644 src/System/HttpParser.h create mode 100644 src/System/HttpRequest.cpp create mode 100644 src/System/HttpRequest.h create mode 100644 src/System/HttpResponse.cpp create mode 100644 src/System/HttpResponse.h diff --git a/src/System/HttpParser.cpp b/src/System/HttpParser.cpp new file mode 100644 index 00000000..3e4efd2d --- /dev/null +++ b/src/System/HttpParser.cpp @@ -0,0 +1,215 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "HttpParser.h" + +#include + +namespace cryptonote { + +HttpResponse::HTTP_STATUS HttpParser::parseResponseStatusFromString(const std::string& status) { + if (status == "200 OK" || status == "200 Ok") return cryptonote::HttpResponse::STATUS_200; + else if (status == "404 Not Found") return cryptonote::HttpResponse::STATUS_404; + else if (status == "500 Internal Server Error") return cryptonote::HttpResponse::STATUS_500; + else throw std::runtime_error("Unknown HTTP status code is given"); + + return cryptonote::HttpResponse::STATUS_200; //unaccessible +} + + +void HttpParser::receiveRequest(std::istream& stream, HttpRequest& request) { + readWord(stream, request.method); + readWord(stream, request.url); + + std::string httpVersion; + readWord(stream, httpVersion); + + readHeaders(stream, request.headers); + + std::string body; + size_t bodyLen = getBodyLen(request.headers); + if (bodyLen) { + readBody(stream, request.body, bodyLen); + } +} + + +void HttpParser::receiveResponse(std::istream& stream, HttpResponse& response) { + std::string httpVersion; + readWord(stream, httpVersion); + + std::string status; + char c; + + stream.get(c); + while (stream.good() && c != '\r') { //Till the end + status += c; + stream.get(c); + } + + if (!stream.good()) { + throw std::runtime_error("Parser error: stream is not good"); + } + + if (c == '\r') { + stream.get(c); + if (c != '\n') { + throw std::runtime_error("Parser error: '\\n' symbol is expected"); + } + } + + response.setStatus(parseResponseStatusFromString(status)); + + std::string name; + std::string value; + + while (readHeader(stream, name, value)) { + response.addHeader(name, value); + name.clear(); + value.clear(); + } + + response.addHeader(name, value); + auto headers = response.getHeaders(); + size_t length = 0; + auto it = headers.find("Content-Length"); + if (it != headers.end()) { + length = std::stoul(it->second); + } + + std::string body; + if (length) { + readBody(stream, body, length); + } + + response.setBody(body); +} + + +void HttpParser::readWord(std::istream& stream, std::string& word) { + char c; + + stream.get(c); + while (stream.good() && c != ' ' && c != '\r') { + word += c; + stream.get(c); + } + + if (!stream.good()) { + throw std::runtime_error("Parser error: stream is not good"); + } + + if (c == '\r') { + stream.get(c); + if (c != '\n') { + throw std::runtime_error("Parser error: '\\n' symbol is expected"); + } + } +} + +void HttpParser::readHeaders(std::istream& stream, HttpRequest::Headers& headers) { + std::string name; + std::string value; + + while (readHeader(stream, name, value)) { + headers[name] = value; //use insert + name.clear(); + value.clear(); + } + + headers[name] = value; //use insert +} + +bool HttpParser::readHeader(std::istream& stream, std::string& name, std::string& value) { + char c; + bool isName = true; + + stream.get(c); + while (stream.good() && c != '\r') { + if (c == ':') { + if (stream.peek() == ' ') { + stream.get(c); + } + + if (name.empty()) { + throw std::runtime_error("Header name must be not empty"); + } + + if (isName) { + isName = false; + stream.get(c); + continue; + } + } + + if (isName) { + name += c; + stream.get(c); + } else { + value += c; + stream.get(c); + } + } + + if (!stream.good()) { + throw std::runtime_error("Parser error: stream is not good"); + } + + stream.get(c); + if (c != '\n') { + throw std::runtime_error("Parser error: '\\n' symbol is expected"); + } + + c = stream.peek(); + if (c == '\r') { + stream.get(c).get(c); + if (c != '\n') { + throw std::runtime_error("Parser error: '\\n' symbol is expected"); + } + + return false; //no more headers + } + + return true; +} + +size_t HttpParser::getBodyLen(const HttpRequest::Headers& headers) { + auto it = headers.find("Content-Length"); + if (it != headers.end()) { + size_t bytes = std::stoul(it->second); + return bytes; + } + + return 0; +} + +void HttpParser::readBody(std::istream& stream, std::string& body, const size_t bodyLen) { + size_t read = 0; + + while (stream.good() && read < bodyLen) { + body += stream.get(); + ++read; + } + + if (!stream.good()) { + throw std::runtime_error("stream is not good"); + } +} + +} + + diff --git a/src/System/HttpParser.h b/src/System/HttpParser.h new file mode 100644 index 00000000..75991781 --- /dev/null +++ b/src/System/HttpParser.h @@ -0,0 +1,47 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#ifndef HTTPPARSER_H_ +#define HTTPPARSER_H_ + +#include +#include +#include +#include "HttpRequest.h" +#include "HttpResponse.h" + +namespace cryptonote { + +//Blocking HttpParser +class HttpParser { +public: + HttpParser() {}; + + void receiveRequest(std::istream& stream, HttpRequest& request); + void receiveResponse(std::istream& stream, HttpResponse& response); + static HttpResponse::HTTP_STATUS parseResponseStatusFromString(const std::string& status); +private: + void readWord(std::istream& stream, std::string& word); + void readHeaders(std::istream& stream, HttpRequest::Headers &headers); + bool readHeader(std::istream& stream, std::string& name, std::string& value); + size_t getBodyLen(const HttpRequest::Headers& headers); + void readBody(std::istream& stream, std::string& body, const size_t bodyLen); +}; + +} //namespace cryptonote + +#endif /* HTTPPARSER_H_ */ diff --git a/src/System/HttpRequest.cpp b/src/System/HttpRequest.cpp new file mode 100644 index 00000000..861a918b --- /dev/null +++ b/src/System/HttpRequest.cpp @@ -0,0 +1,73 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "HttpRequest.h" + +namespace cryptonote { + + const std::string& HttpRequest::getMethod() const { + return method; + } + + const std::string& HttpRequest::getUrl() const { + return url; + } + + const HttpRequest::Headers& HttpRequest::getHeaders() const { + return headers; + } + + const std::string& HttpRequest::getBody() const { + return body; + } + + void HttpRequest::addHeader(const std::string& name, const std::string& value) { + headers[name] = value; + } + void HttpRequest::setBody(const std::string& b) { + body = b; + if (!body.empty()) { + headers["Content-Length"] = std::to_string(body.size()); + } + else { + headers.erase("Content-Length"); + } + } + + void HttpRequest::setUrl(const std::string& u) { + url = u; + } + + std::ostream& HttpRequest::printHttpRequest(std::ostream& os) const { + os << "POST " << url << " HTTP/1.1\r\n"; + auto host = headers.find("Host"); + if (host == headers.end()) { + os << "Host: " << "127.0.0.1" << "\r\n"; + } + + for (auto pair : headers) { + os << pair.first << ": " << pair.second << "\r\n"; + } + + os << "\r\n"; + if (!body.empty()) { + os << body; + } + + return os; + } +} \ No newline at end of file diff --git a/src/System/HttpRequest.h b/src/System/HttpRequest.h new file mode 100644 index 00000000..2acb8c2e --- /dev/null +++ b/src/System/HttpRequest.h @@ -0,0 +1,53 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +namespace cryptonote { + class HttpRequest { + public: + typedef std::map Headers; + + const std::string& getMethod() const; + const std::string& getUrl() const; + const Headers& getHeaders() const; + const std::string& getBody() const; + + void addHeader(const std::string& name, const std::string& value); + void setBody(const std::string& b); + void setUrl(const std::string& uri); + + private: + friend class HttpParser; + + std::string method; + std::string url; + Headers headers; + std::string body; + + friend std::ostream& operator<<(std::ostream& os, const HttpRequest& resp); + std::ostream& printHttpRequest(std::ostream& os) const; + }; + + inline std::ostream& operator<<(std::ostream& os, const HttpRequest& resp) { + return resp.printHttpRequest(os); + } +} \ No newline at end of file diff --git a/src/System/HttpResponse.cpp b/src/System/HttpResponse.cpp new file mode 100644 index 00000000..bb6ec3db --- /dev/null +++ b/src/System/HttpResponse.cpp @@ -0,0 +1,83 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "HttpResponse.h" + +#include + +namespace { + +const char* getStatusString(cryptonote::HttpResponse::HTTP_STATUS status) { + switch (status) { + case cryptonote::HttpResponse::STATUS_200: + return "200 OK"; + case cryptonote::HttpResponse::STATUS_404: + return "404 Not Found"; + case cryptonote::HttpResponse::STATUS_500: + return "500 Internal Server Error"; + default: + throw std::runtime_error("Unknown HTTP status code is given"); + } + + return ""; //unaccessible +} + + +} //namespace + +namespace cryptonote { + +HttpResponse::HttpResponse() { + status = STATUS_200; + headers["Server"] = "Cryptonote-based HTTP server"; +} + +void HttpResponse::setStatus(HTTP_STATUS s) { + status = s; +} + +void HttpResponse::addHeader(const std::string& name, const std::string& value) { + headers[name] = value; +} + +void HttpResponse::setBody(const std::string& b) { + body = b; + if (!body.empty()) { + headers["Content-Length"] = std::to_string(body.size()); + } else { + headers.erase("Content-Length"); + } +} + +std::ostream& HttpResponse::printHttpResponse(std::ostream& os) const { + os << "HTTP/1.1 " << getStatusString(status) << "\r\n"; + + for (auto pair: headers) { + os << pair.first << ": " << pair.second << "\r\n"; + } + os << "\r\n"; + + if (!body.empty()) { + os << body; + } + + return os; +} + +} //namespace cryptonote + + diff --git a/src/System/HttpResponse.h b/src/System/HttpResponse.h new file mode 100644 index 00000000..3fffd2ad --- /dev/null +++ b/src/System/HttpResponse.h @@ -0,0 +1,57 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +namespace cryptonote { + + class HttpResponse { + public: + enum HTTP_STATUS { + STATUS_200, + STATUS_404, + STATUS_500 + }; + + HttpResponse(); + + void setStatus(HTTP_STATUS s); + void addHeader(const std::string& name, const std::string& value); + void setBody(const std::string& b); + + const std::map& getHeaders() const { return headers; } + HTTP_STATUS getStatus() const { return status; } + const std::string& getBody() const { return body; } + + private: + friend std::ostream& operator<<(std::ostream& os, const HttpResponse& resp); + std::ostream& printHttpResponse(std::ostream& os) const; + + HTTP_STATUS status; + std::map headers; + std::string body; + }; + + inline std::ostream& operator<<(std::ostream& os, const HttpResponse& resp) { + return resp.printHttpResponse(os); + } + +} //namespace cryptonote