New HTTP server
This commit is contained in:
parent
6d741947cb
commit
640efb3e15
6 changed files with 528 additions and 0 deletions
215
src/System/HttpParser.cpp
Normal file
215
src/System/HttpParser.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "HttpParser.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
47
src/System/HttpParser.h
Normal file
47
src/System/HttpParser.h
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef HTTPPARSER_H_
|
||||
#define HTTPPARSER_H_
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#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_ */
|
73
src/System/HttpRequest.cpp
Normal file
73
src/System/HttpRequest.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
53
src/System/HttpRequest.h
Normal file
53
src/System/HttpRequest.h
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace cryptonote {
|
||||
class HttpRequest {
|
||||
public:
|
||||
typedef std::map<std::string, std::string> 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);
|
||||
}
|
||||
}
|
83
src/System/HttpResponse.cpp
Normal file
83
src/System/HttpResponse.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "HttpResponse.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
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
|
||||
|
||||
|
57
src/System/HttpResponse.h
Normal file
57
src/System/HttpResponse.h
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
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<std::string, std::string>& 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<std::string, std::string> headers;
|
||||
std::string body;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const HttpResponse& resp) {
|
||||
return resp.printHttpResponse(os);
|
||||
}
|
||||
|
||||
} //namespace cryptonote
|
Loading…
Reference in a new issue