Skip to content

Commit

Permalink
New HTTP server
Browse files Browse the repository at this point in the history
  • Loading branch information
jezal committed Sep 15, 2014
1 parent 6d74194 commit 640efb3
Show file tree
Hide file tree
Showing 6 changed files with 528 additions and 0 deletions.
215 changes: 215 additions & 0 deletions src/System/HttpParser.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 47 additions & 0 deletions src/System/HttpParser.h
Original file line number Diff line number Diff line change
@@ -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 changes: 73 additions & 0 deletions src/System/HttpRequest.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 53 additions & 0 deletions src/System/HttpRequest.h
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading

0 comments on commit 640efb3

Please sign in to comment.