initial commit
This commit is contained in:
+59
@@ -0,0 +1,59 @@
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
.recommenders
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# PyDev specific (Python IDE for Eclipse)
|
||||
*.pydevproject
|
||||
|
||||
# CDT-specific (C/C++ Development Tooling)
|
||||
.cproject
|
||||
|
||||
# CDT- autotools
|
||||
.autotools
|
||||
|
||||
# Java annotation processor (APT)
|
||||
.factorypath
|
||||
|
||||
# PDT-specific (PHP Development Tools)
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# Tern plugin
|
||||
.tern-project
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
# STS (Spring Tool Suite)
|
||||
.springBeans
|
||||
|
||||
# Code Recommenders
|
||||
.recommenders/
|
||||
|
||||
# Annotation Processing
|
||||
.apt_generated/
|
||||
|
||||
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||
.cache-main
|
||||
.scala_dependencies
|
||||
.worksheet
|
||||
/Debug/
|
||||
|
||||
# Other
|
||||
.project
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* headers.cpp
|
||||
*
|
||||
* Created on: May 16, 2019
|
||||
* Author: cmy
|
||||
*/
|
||||
|
||||
#include "headers.h"
|
||||
|
||||
using namespace std;
|
||||
namespace chttp {
|
||||
|
||||
Headers::Headers() {
|
||||
// TODO Auto-generated constructor stub
|
||||
|
||||
}
|
||||
|
||||
Headers::~Headers() {
|
||||
// TODO Auto-generated destructor stub
|
||||
}
|
||||
|
||||
void Headers::SetHeader(const std::string& key, const std::string& value) {
|
||||
content[key] = value;
|
||||
}
|
||||
|
||||
ostream& operator<<(ostream& out, const chttp::Headers &h) {
|
||||
for (auto &p : h.content) {
|
||||
out << p.first;
|
||||
out << ": ";
|
||||
out << p.second;
|
||||
out << '\n';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} /* namespace chttp */
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* headers.h
|
||||
*
|
||||
* Created on: May 16, 2019
|
||||
* Author: cmy
|
||||
*/
|
||||
|
||||
#ifndef HEADERS_H_
|
||||
#define HEADERS_H_
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
namespace chttp {
|
||||
|
||||
class Headers final {
|
||||
public:
|
||||
constexpr static char PlainText[] = "text/plain";
|
||||
constexpr static char HtmlText[] = "text/html";
|
||||
constexpr static char CSSText[] = "text/css";
|
||||
constexpr static char JavascriptText[] = "textjavascript";
|
||||
constexpr static char GifImage[] = "image/gif";
|
||||
constexpr static char PngImage[] = "image/png";
|
||||
constexpr static char JpegImage[] = "image/jpeg";
|
||||
constexpr static char BmpImage[] = "image/bmp";
|
||||
constexpr static char WebpImage[] = "image/webp";
|
||||
constexpr static char MidiAudio[] = "audio/midi";
|
||||
constexpr static char MpegAudio[] = "audio/mpeg";
|
||||
constexpr static char WebmAudio[] = "audio/webm";
|
||||
constexpr static char OggAudio[] = "audio/ogg";
|
||||
constexpr static char WavAudio[] = "audio/wav";
|
||||
constexpr static char WebmVideo[] = "video/webm";
|
||||
constexpr static char OggVideo[] = "video/ogg";
|
||||
constexpr static char XmlApplication[] = "application/xml";
|
||||
constexpr static char PDFApplication[] = "application/pdf";
|
||||
|
||||
Headers();
|
||||
virtual ~Headers();
|
||||
|
||||
void SetHeader(const std::string& key, const std::string& value);
|
||||
|
||||
inline const std::unordered_map<std::string, std::string>& Content() const {
|
||||
return content;
|
||||
}
|
||||
|
||||
inline std::unordered_map<std::string, std::string>& Content() {
|
||||
return content;
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& out, const Headers &h);
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::string> content { };
|
||||
};
|
||||
|
||||
} /* namespace chttp */
|
||||
|
||||
#endif /* HEADERS_H_ */
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Congratuations!</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello,World!</h1>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "server.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
chttp::Server s { };
|
||||
chttp::Router router { };
|
||||
router.Get("/about", [](chttp::Request req, chttp::Response res) {
|
||||
res.ServeHtml("index.html");
|
||||
});
|
||||
s.SetRouter(router);
|
||||
try {
|
||||
s.Test(1113);
|
||||
} catch (runtime_error& error) {
|
||||
cout << error.what() << endl;
|
||||
cout << "retry" << endl;
|
||||
s.Test(1114);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>404 Not Found</h1>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* reauest.cpp
|
||||
*
|
||||
* Created on: May 16, 2019
|
||||
* Author: cmy
|
||||
*/
|
||||
|
||||
#include "request.h"
|
||||
|
||||
namespace chttp {
|
||||
|
||||
|
||||
Request::~Request() {
|
||||
// TODO Auto-generated destructor stub
|
||||
}
|
||||
|
||||
} /* namespace chttp */
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* reauest.h
|
||||
*
|
||||
* Created on: May 16, 2019
|
||||
* Author: cmy
|
||||
*/
|
||||
|
||||
#ifndef REQUEST_H_
|
||||
#define REQUEST_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "headers.h"
|
||||
|
||||
namespace chttp {
|
||||
|
||||
enum class Request_Method {
|
||||
GET, POST, HEAD, PUT, DELETE, OPTIONS, CONNECT, TRACE, ALL, NONE
|
||||
};
|
||||
|
||||
enum class Http_Version {
|
||||
HTTP1_0, HTTP1_1, HTTP2_0, NONE
|
||||
};
|
||||
|
||||
using URL_type=std::string;
|
||||
|
||||
class Request final {
|
||||
public:
|
||||
using URL_type=std::string;
|
||||
|
||||
Request() = delete;
|
||||
Request(Request_Method method, URL_type url, Http_Version version,
|
||||
Headers header) :
|
||||
method(method), url(url), version(version), header(header) {
|
||||
}
|
||||
~Request();
|
||||
|
||||
inline void SetHeader(const Headers& headers) {
|
||||
this->header = headers;
|
||||
}
|
||||
|
||||
inline const URL_type& URL() const {
|
||||
return url;
|
||||
}
|
||||
|
||||
inline const Headers& Header() const {
|
||||
return header;
|
||||
}
|
||||
|
||||
inline Headers& Header() {
|
||||
return header;
|
||||
}
|
||||
|
||||
inline const Request_Method& Method() const {
|
||||
return method;
|
||||
}
|
||||
|
||||
inline const Http_Version& Version() const {
|
||||
return version;
|
||||
}
|
||||
|
||||
private:
|
||||
Request_Method method;
|
||||
URL_type url;
|
||||
Http_Version version;
|
||||
Headers header { };
|
||||
};
|
||||
|
||||
} /* namespace chttp */
|
||||
|
||||
#endif /* REQUEST_H_ */
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* response.cpp
|
||||
*
|
||||
* Created on: May 16, 2019
|
||||
* Author: cmy
|
||||
*/
|
||||
|
||||
#include "response.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace chttp {
|
||||
constexpr char Headers::PlainText[];
|
||||
constexpr char Headers::HtmlText[];
|
||||
constexpr char Headers::CSSText[];
|
||||
constexpr char Headers::JavascriptText[];
|
||||
constexpr char Headers::GifImage[];
|
||||
constexpr char Headers::PngImage[];
|
||||
constexpr char Headers::JpegImage[];
|
||||
constexpr char Headers::BmpImage[];
|
||||
constexpr char Headers::WebpImage[];
|
||||
constexpr char Headers::MidiAudio[];
|
||||
constexpr char Headers::MpegAudio[];
|
||||
constexpr char Headers::WebmAudio[];
|
||||
constexpr char Headers::OggAudio[];
|
||||
constexpr char Headers::WavAudio[];
|
||||
constexpr char Headers::WebmVideo[];
|
||||
constexpr char Headers::OggVideo[];
|
||||
constexpr char Headers::XmlApplication[];
|
||||
constexpr char Headers::PDFApplication[];
|
||||
|
||||
constexpr char HttpStatus::OK[];
|
||||
constexpr char HttpStatus::BadRequest[];
|
||||
constexpr char HttpStatus::Unauthorized[];
|
||||
constexpr char HttpStatus::Forbidden[];
|
||||
constexpr char HttpStatus::NotFound[];
|
||||
constexpr char HttpStatus::Unavailable[];
|
||||
|
||||
constexpr char HttpVersionStr::Http1_0[];
|
||||
constexpr char HttpVersionStr::Http1_1[];
|
||||
constexpr char HttpVersionStr::Http2_0[];
|
||||
|
||||
string Response::NotFoundPage = "not_found.html";
|
||||
|
||||
Response::~Response() {
|
||||
// TODO Auto-generated destructor stub
|
||||
}
|
||||
|
||||
void Response::ServeHtml(const char* file_path) {
|
||||
SendStatusLine(HttpVersionStr::Http1_1, HttpStatus::OK);
|
||||
SetContentType(Headers::HtmlText);
|
||||
SendHeader(header);
|
||||
|
||||
SendFile(clnt_sock, file_path);
|
||||
close(clnt_sock);
|
||||
}
|
||||
|
||||
void Response::SendFile(int clnt_sock, const char* file_path) {
|
||||
#ifdef __linux__
|
||||
auto file = fopen(file_path, "r");
|
||||
if (file == NULL) {
|
||||
throw "file_not_found";
|
||||
} else {
|
||||
char buffer[buffer_size];
|
||||
fgets(buffer, sizeof(buffer), file);
|
||||
while (!feof(file)) {
|
||||
WriteSocket(clnt_sock, buffer, strlen(buffer));
|
||||
fgets(buffer, sizeof(buffer), file);
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
#endif
|
||||
}
|
||||
void Response::SendHeader(const Headers& headers) {
|
||||
const auto content = headers.Content();
|
||||
for (auto & header_pair : content) {
|
||||
WriteSocket(clnt_sock, header_pair.first.c_str(), 0);
|
||||
WriteSocket(clnt_sock, ": ", 2);
|
||||
WriteSocket(clnt_sock, header_pair.second.c_str(), 0);
|
||||
WriteSocket(clnt_sock, "\r\n", 0);
|
||||
}
|
||||
WriteSocket(clnt_sock, "\r\n", 0);
|
||||
}
|
||||
|
||||
void Response::SendStatusLine(const char* http_version,
|
||||
const char* http_status) {
|
||||
WriteSocket(clnt_sock, http_version);
|
||||
WriteSocket(clnt_sock, " ");
|
||||
WriteSocket(clnt_sock, http_status);
|
||||
WriteSocket(clnt_sock, "\r\n");
|
||||
}
|
||||
|
||||
void Response::NotFound() {
|
||||
SendStatusLine(HttpVersionStr::Http1_1, HttpStatus::NotFound);
|
||||
SetContentType(Headers::HtmlText);
|
||||
SendHeader(header);
|
||||
SendFile(clnt_sock, NotFoundPage.c_str());
|
||||
}
|
||||
|
||||
void Response::WriteSocket(int sock, const char* buffer, size_t size,
|
||||
int flags) {
|
||||
#ifdef __linux__
|
||||
size_t length = size;
|
||||
if (size == 0)
|
||||
length = strlen(buffer);
|
||||
send(sock, buffer, length, flags);
|
||||
std::cout << "write to socket:";
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
cout << buffer[i];
|
||||
#endif
|
||||
}
|
||||
|
||||
} /* namespace chttp */
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* response.h
|
||||
*
|
||||
* Created on: May 16, 2019
|
||||
* Author: cmy
|
||||
*/
|
||||
|
||||
#ifndef RESPONSE_H_
|
||||
#define RESPONSE_H_
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "headers.h"
|
||||
|
||||
namespace chttp {
|
||||
|
||||
struct HttpStatus {
|
||||
constexpr static char OK[] = "200 OK";
|
||||
constexpr static char BadRequest[] = "400 Bad Request";
|
||||
constexpr static char Unauthorized[] = "401 Unauthorized";
|
||||
constexpr static char Forbidden[] = "403 Forbidden";
|
||||
constexpr static char NotFound[] = "404 Not Found";
|
||||
constexpr static char Unavailable[] = "503 Server Unavailable";
|
||||
};
|
||||
|
||||
struct HttpVersionStr {
|
||||
constexpr static char Http1_0[] = "HTTP/1.0";
|
||||
constexpr static char Http1_1[] = "HTTP/1.1";
|
||||
constexpr static char Http2_0[] = "HTTP/2.0";
|
||||
};
|
||||
|
||||
class Response final {
|
||||
public:
|
||||
Response() = delete;
|
||||
Response(int clnt_sock) :
|
||||
clnt_sock(clnt_sock) {
|
||||
}
|
||||
~Response();
|
||||
|
||||
void ServeHtml(const char * file_path);
|
||||
|
||||
void NotFound();
|
||||
|
||||
inline void SetHeaders(const Headers& headers) {
|
||||
header = headers;
|
||||
}
|
||||
|
||||
inline void SetContentType(std::string&& content_type) {
|
||||
header.Content()["Content-Type"] = content_type;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Server;
|
||||
|
||||
int clnt_sock;
|
||||
int buffer_size { 1024 };
|
||||
Headers header { };
|
||||
__uint32_t clnt_ip;
|
||||
|
||||
static std::string NotFoundPage;
|
||||
|
||||
void SendStatusLine(const char* http_version, const char* http_status);
|
||||
void SendHeader(const Headers& headers);
|
||||
void SendFile(int clnt_sock, const char* file_path);
|
||||
|
||||
void WriteSocket(int clnt_sock, const char* buffer, size_t size = 0,
|
||||
int flags = 0);
|
||||
};
|
||||
|
||||
} /* namespace chttp */
|
||||
|
||||
#endif /* RESPONSE_H_ */
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* router.cpp
|
||||
*
|
||||
* Created on: May 16, 2019
|
||||
* Author: cmy
|
||||
*/
|
||||
|
||||
#include "router.h"
|
||||
|
||||
namespace chttp {
|
||||
|
||||
Router::~Router() {
|
||||
// TODO Auto-generated destructor stub
|
||||
}
|
||||
|
||||
} /* namespace chttp */
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* router.h
|
||||
*
|
||||
* Created on: May 16, 2019
|
||||
* Author: cmy
|
||||
*/
|
||||
|
||||
#ifndef ROUTER_H_
|
||||
#define ROUTER_H_
|
||||
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
|
||||
#include "request.h"
|
||||
#include "response.h"
|
||||
|
||||
namespace chttp {
|
||||
|
||||
struct RequestInfo { //TODO:Hash func
|
||||
Request::URL_type url;
|
||||
Request_Method method;
|
||||
RequestInfo(Request::URL_type url, Request_Method method) :
|
||||
url(url), method(method) {
|
||||
}
|
||||
bool operator ==(const RequestInfo& other) const {
|
||||
return (this->url == other.url) && (this->method == other.method);
|
||||
}
|
||||
};
|
||||
|
||||
struct RequestInfoHasher {
|
||||
std::size_t operator()(const RequestInfo & info) const {
|
||||
using std::hash;
|
||||
return (hash<Request::URL_type>()(info.url)
|
||||
^ hash<int>()(static_cast<int>(info.method)));
|
||||
}
|
||||
};
|
||||
|
||||
class Router final {
|
||||
public:
|
||||
using Handler=std::function<void(Request&, Response&)>;
|
||||
using Handler_table=std::unordered_map<RequestInfo, Handler, RequestInfoHasher>;
|
||||
|
||||
Router() = default;
|
||||
virtual ~Router();
|
||||
|
||||
// there should be a out-of-range exception when no handler
|
||||
inline const Handler GetHandler(const Request::URL_type& url,
|
||||
Request_Method method) const {
|
||||
return handler_table.at(RequestInfo(url, method));
|
||||
}
|
||||
|
||||
inline const Handler GetHandler(const RequestInfo& info) const {
|
||||
return handler_table.at(info);
|
||||
}
|
||||
|
||||
inline bool Get(const Request::URL_type& url, Handler handler) {
|
||||
return AddHandler(url, Request_Method::GET, handler);
|
||||
}
|
||||
|
||||
inline bool Head(const Request::URL_type& url, Handler handler) {
|
||||
return AddHandler(url, Request_Method::HEAD, handler);
|
||||
}
|
||||
|
||||
inline bool Post(const Request::URL_type& url, Handler handler) {
|
||||
return AddHandler(url, Request_Method::POST, handler);
|
||||
}
|
||||
|
||||
inline bool Put(const Request::URL_type& url, Handler handler) {
|
||||
return AddHandler(url, Request_Method::PUT, handler);
|
||||
}
|
||||
|
||||
inline bool Delete(const Request::URL_type& url, Handler handler) {
|
||||
return AddHandler(url, Request_Method::DELETE, handler);
|
||||
}
|
||||
|
||||
inline bool Connect(const Request::URL_type& url, Handler handler) {
|
||||
return AddHandler(url, Request_Method::CONNECT, handler);
|
||||
}
|
||||
|
||||
inline bool Options(const Request::URL_type& url, Handler handler) {
|
||||
return AddHandler(url, Request_Method::OPTIONS, handler);
|
||||
}
|
||||
|
||||
inline bool Trace(const Request::URL_type& url,
|
||||
Handler handler) {
|
||||
return AddHandler(url, Request_Method::TRACE, handler);
|
||||
}
|
||||
|
||||
inline bool AddHandler(const Request::URL_type& url, Request_Method method,
|
||||
Handler handler) {
|
||||
auto result = handler_table.insert(
|
||||
std::make_pair(RequestInfo(url, method), handler));
|
||||
return result.second;
|
||||
}
|
||||
|
||||
inline bool Contains(const Request::URL_type & url,
|
||||
const Request_Method & method) const {
|
||||
return Contains( { url, method });
|
||||
}
|
||||
|
||||
inline bool Contains(const RequestInfo & info) const {
|
||||
return handler_table.find(info) != handler_table.end();
|
||||
}
|
||||
|
||||
private:
|
||||
Handler_table handler_table { };
|
||||
};
|
||||
|
||||
} /* namespace chttp */
|
||||
|
||||
#endif /* ROUTER_H_ */
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* socket.cpp
|
||||
*
|
||||
* Created on: May 16, 2019
|
||||
* Author: cmy
|
||||
*/
|
||||
|
||||
#include "server.h"
|
||||
using namespace std;
|
||||
|
||||
namespace chttp {
|
||||
|
||||
Server::Server() {
|
||||
// TODO Auto-generated constructor stub
|
||||
|
||||
}
|
||||
|
||||
Server::~Server() {
|
||||
// TODO Auto-generated destructor stub
|
||||
}
|
||||
|
||||
int Server::GetLine(int sock, char* buf, int size) {
|
||||
#ifdef __linux__
|
||||
int count { };
|
||||
char c = '\0';
|
||||
int n { };
|
||||
while ((count < size - 1) && (c != '\n')) {
|
||||
n = recv(sock, &c, 1, 0);
|
||||
if (n > 0) {
|
||||
if (c == '\r') {
|
||||
n = recv(sock, &c, 1, MSG_PEEK); //peek the next char
|
||||
if ((n > 0) && (c == '\n')) {
|
||||
recv(sock, &c, 1, 0);
|
||||
break;
|
||||
} else
|
||||
c = '\n';
|
||||
}
|
||||
buf[count] = c;
|
||||
count++;
|
||||
} else {
|
||||
c = '\n';
|
||||
}
|
||||
}
|
||||
buf[count] = '\0';
|
||||
return count;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
vector<string> Server::ReadRequest(const int & clnt_sock) {
|
||||
int char_count { 1 };
|
||||
char buffer[buffer_size];
|
||||
vector<string> content { };
|
||||
while (char_count > 0 && strcmp("\n", buffer)) {
|
||||
char_count = GetLine(clnt_sock, buffer, sizeof(buffer));
|
||||
content.push_back(buffer);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
int Server::StartUpSocket(const in_port_t& port) {
|
||||
int serv_sock { };
|
||||
sockaddr_in serv_addr { };
|
||||
|
||||
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (serv_sock == -1) {
|
||||
throw std::runtime_error { "socket_create_error" };
|
||||
}
|
||||
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_port = htons(port);
|
||||
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
if (bind(serv_sock, (sockaddr*) (&serv_addr), sizeof(serv_addr)) < 0) {
|
||||
throw std::runtime_error { "bind_error" };
|
||||
}
|
||||
|
||||
if (listen(serv_sock, 5) == -1) {
|
||||
throw std::runtime_error { "listen error" };
|
||||
|
||||
}
|
||||
return serv_sock;
|
||||
}
|
||||
|
||||
void Server::ProcessRequest(int clnt_sock) {
|
||||
auto request_string = ReadRequest(clnt_sock);
|
||||
auto request_line = ParseRequestLine(request_string);
|
||||
Request_Method method = std::get<0>(request_line);
|
||||
URL_type url = std::get<1>(request_line);
|
||||
Http_Version version = std::get<2>(request_line);
|
||||
Headers header { };
|
||||
for (auto it = request_string.begin() + 1; it + 1 != request_string.end(); // ignore the last empty line
|
||||
++it) {
|
||||
auto pair = GetKeyValuePair(*it);
|
||||
header.SetHeader(pair.first, pair.second);
|
||||
}
|
||||
cout << "url: " << url << endl;
|
||||
cout << "Headers:\n" << header << endl;
|
||||
// TODO construct an request object
|
||||
// TODO add client info to request
|
||||
Request request { method, url, version, header };
|
||||
// TODO construct an response object
|
||||
Response response { clnt_sock };
|
||||
// TODO call the handler function
|
||||
RequestInfo request_info { url, method };
|
||||
if (http_router.Contains(request_info)) {
|
||||
auto handler = http_router.GetHandler(request_info);
|
||||
handler(request, response);
|
||||
cout << "find existing routers" << endl;
|
||||
} else {
|
||||
cout << "not find existing routers" << endl;
|
||||
response.NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
std::string::const_iterator Server::NextWord(std::string::const_iterator begin,
|
||||
std::string::const_iterator end) {
|
||||
for (auto it = begin; it != end; ++it) {
|
||||
if (*it == ' ' || *it == '\0') {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
std::tuple<Request_Method, URL_type, Http_Version> Server::ParseRequestLine(
|
||||
const std::vector<std::string>& request_data) {
|
||||
auto method_string_end = NextWord(request_data[0].cbegin(),
|
||||
request_data[0].cend());
|
||||
auto url_string_end = NextWord(method_string_end + 1,
|
||||
request_data[0].end());
|
||||
auto method_string = string { request_data[0].cbegin(), method_string_end };
|
||||
auto url_string = string { method_string_end + 1, url_string_end };
|
||||
auto version_string_end = NextWord(url_string_end + 1,
|
||||
request_data[0].end());
|
||||
auto version_string = string { url_string_end + 1, version_string_end };
|
||||
|
||||
auto method = GetRequestMethod(method_string);
|
||||
auto url = GetRequestUrl(url_string);
|
||||
auto http_version = GetHttpVersion(version_string.c_str());
|
||||
|
||||
return make_tuple(method, url, http_version);
|
||||
}
|
||||
|
||||
Request_Method Server::GetRequestMethod(const std::string& str) {
|
||||
auto c_str = str.c_str();
|
||||
#ifdef __linux__
|
||||
if (strcasecmp(c_str, "get") == 0) {
|
||||
return Request_Method::GET;
|
||||
} else if (strcasecmp(c_str, "post") == 0) {
|
||||
return Request_Method::POST;
|
||||
} else if (strcasecmp(c_str, "head") == 0) {
|
||||
return Request_Method::HEAD;
|
||||
} else if (strcasecmp(c_str, "put") == 0) {
|
||||
return Request_Method::PUT;
|
||||
} else if (strcasecmp(c_str, "delete") == 0) {
|
||||
return Request_Method::DELETE;
|
||||
} else if (strcasecmp(c_str, "options") == 0) {
|
||||
return Request_Method::OPTIONS;
|
||||
} else if (strcasecmp(c_str, "connect") == 0) {
|
||||
return Request_Method::CONNECT;
|
||||
} else if (strcasecmp(c_str, "trace") == 0) {
|
||||
return Request_Method::TRACE;
|
||||
} else {
|
||||
return Request_Method::NONE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Http_Version Server::GetHttpVersion(const std::string& str) {
|
||||
auto c_str = str.c_str();
|
||||
#ifdef __linux__
|
||||
if (strcasecmp(c_str, "HTTP/1.1")) {
|
||||
return Http_Version::HTTP1_1;
|
||||
} else if (strcasecmp(c_str, "HTTP/1.0")) {
|
||||
return Http_Version::HTTP1_0;
|
||||
} else if (strcasecmp(c_str, "HTTP/2.0")) {
|
||||
return Http_Version::HTTP2_0;
|
||||
} else {
|
||||
return Http_Version::NONE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
URL_type Server::GetRequestUrl(const std::string& str) {
|
||||
return URL_type { str };
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> Server::GetKeyValuePair(
|
||||
const std::string& str) {
|
||||
auto it = str.find(": ");
|
||||
if (it == string::npos) {
|
||||
throw runtime_error { "no colon-blank separator found" };
|
||||
}
|
||||
string key { str.cbegin(), str.begin() + it };
|
||||
string value { str.begin() + it + 2, str.end() };
|
||||
return make_pair(key, value);
|
||||
}
|
||||
|
||||
} /* namespace chttp */
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* socket.h
|
||||
*
|
||||
* Created on: May 16, 2019
|
||||
* Author: cmy
|
||||
*/
|
||||
|
||||
#ifndef SERVER_H_
|
||||
#define SERVER_H_
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <iterator>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include "request.h"
|
||||
#include "response.h"
|
||||
#include "router.h"
|
||||
|
||||
namespace chttp {
|
||||
|
||||
class Server final {
|
||||
public:
|
||||
#ifdef __linux__
|
||||
using Port_type = in_port_t;
|
||||
#endif
|
||||
Server();
|
||||
~Server();
|
||||
|
||||
void Test(const in_port_t& port) {
|
||||
|
||||
auto serv_sock = StartUpSocket(port);
|
||||
std::cout << "listening on port:" << port << std::endl;
|
||||
struct sockaddr_in clnt_addr;
|
||||
unsigned int clnt_addr_len = sizeof(clnt_addr);
|
||||
while (true) {
|
||||
int clnt_sock = accept(serv_sock, (sockaddr*) &clnt_addr,
|
||||
&clnt_addr_len);
|
||||
auto clnt_ip = inet_ntoa(clnt_addr.sin_addr);
|
||||
// ntohl(clnt_addr.sin_addr.s_addr); // binary of 127.0.0.1
|
||||
|
||||
auto clnt_port = clnt_addr.sin_port;
|
||||
std::cout << "port: " << clnt_port << std::endl;
|
||||
ProcessRequest(clnt_sock);
|
||||
close(clnt_sock);
|
||||
}
|
||||
}
|
||||
|
||||
inline void SetRouter(const Router router) {
|
||||
http_router = router;
|
||||
}
|
||||
|
||||
private:
|
||||
int serv_socket { };
|
||||
int buffer_size { 1024 };
|
||||
Router http_router { };
|
||||
|
||||
int StartUpSocket(const in_port_t& port);
|
||||
void ProcessRequest(int clnt_sock);
|
||||
|
||||
std::vector<std::string> ReadRequest(const int& clnt_sock);
|
||||
|
||||
std::tuple<Request_Method, URL_type, Http_Version> ParseRequestLine(
|
||||
const std::vector<std::string>& request_data);
|
||||
Headers ParseRequestHeader(const std::vector<std::string>& request_data);
|
||||
|
||||
int GetLine(int sock, char* buf, int size);
|
||||
std::string::const_iterator NextWord(std::string::const_iterator begin,
|
||||
std::string::const_iterator end);
|
||||
|
||||
Request_Method GetRequestMethod(const std::string & str);
|
||||
Http_Version GetHttpVersion(const std::string & str);
|
||||
URL_type GetRequestUrl(const std::string & str);
|
||||
std::pair<std::string, std::string> GetKeyValuePair(const std::string& str);
|
||||
};
|
||||
|
||||
} /* namespace chttp */
|
||||
|
||||
#endif /* SERVER_H_ */
|
||||
Reference in New Issue
Block a user