mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2025-05-12 06:01:40 +00:00
Supported GET routing.
This commit is contained in:
parent
7953abff07
commit
107a2b9b5d
@ -8,22 +8,52 @@
|
|||||||
#include <httpsvrkit.h>
|
#include <httpsvrkit.h>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
using namespace httpsvrkit;
|
using namespace httpsvrkit;
|
||||||
|
|
||||||
|
int dump_request(Context& cxt)
|
||||||
|
{
|
||||||
|
auto& body = cxt.response.body;
|
||||||
|
char buf[BUFSIZ];
|
||||||
|
|
||||||
|
body += "================================\n";
|
||||||
|
|
||||||
|
sprintf(buf, "Method: %s, URL: %s\n",
|
||||||
|
cxt.request.method.c_str(),
|
||||||
|
cxt.request.url.c_str());
|
||||||
|
|
||||||
|
body += buf;
|
||||||
|
|
||||||
|
//for (const auto& x : cxt.request.headers) {
|
||||||
|
for (auto it = cxt.request.headers.begin(); it != cxt.request.headers.end(); ++it) {
|
||||||
|
const auto& x = *it;
|
||||||
|
sprintf(buf, "%s: %s\n", x.first.c_str(), x.second.c_str());
|
||||||
|
body += buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
body += "================================\n";
|
||||||
|
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
Server svr;
|
Server svr;
|
||||||
|
|
||||||
svr.get("/", [](Context& cxt) {
|
svr.get("/", [](Context& cxt) -> int {
|
||||||
cxt.response.body = "<html><head></head><body><ul></ul></body></html>";
|
dump_request(cxt);
|
||||||
|
return 200;
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.post("/item", [](Context& cxt) {
|
svr.post("/item", [](Context& cxt) -> int {
|
||||||
cxt.response.body = cxt.request.pattern;
|
dump_request(cxt);
|
||||||
|
cxt.response.body += cxt.request.url;
|
||||||
|
return 200;
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.get("/item/:name", [](Context& cxt) {
|
svr.get("/item/([^/]+)", [](Context& cxt) -> int {
|
||||||
cxt.response.body = cxt.request.params.at("name");
|
dump_request(cxt);
|
||||||
|
cxt.response.body += cxt.request.params[0];
|
||||||
|
return 200;
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.run("localhost", 1234);
|
svr.run("localhost", 1234);
|
||||||
|
117
httpsvrkit.h
117
httpsvrkit.h
@ -35,14 +35,17 @@ namespace httpsvrkit
|
|||||||
{
|
{
|
||||||
|
|
||||||
typedef std::map<std::string, std::string> Map;
|
typedef std::map<std::string, std::string> Map;
|
||||||
|
typedef std::vector<std::string> Array;
|
||||||
typedef std::multimap<std::string, std::string> MultiMap;
|
typedef std::multimap<std::string, std::string> MultiMap;
|
||||||
|
|
||||||
// HTTP request
|
// HTTP request
|
||||||
struct Request {
|
struct Request {
|
||||||
|
std::string method;
|
||||||
|
std::string url;
|
||||||
Map headers;
|
Map headers;
|
||||||
std::string body;
|
std::string body;
|
||||||
std::string pattern;
|
Map query;
|
||||||
Map params;
|
Array params;
|
||||||
};
|
};
|
||||||
|
|
||||||
// HTTP response
|
// HTTP response
|
||||||
@ -52,14 +55,14 @@ struct Response {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Context {
|
struct Context {
|
||||||
const Request request;
|
Request request;
|
||||||
Response response;
|
Response response;
|
||||||
};
|
};
|
||||||
|
|
||||||
// HTTP server
|
// HTTP server
|
||||||
class Server {
|
class Server {
|
||||||
public:
|
public:
|
||||||
typedef std::function<void (Context& context)> Handler;
|
typedef std::function<int (Context& context)> Handler;
|
||||||
|
|
||||||
Server();
|
Server();
|
||||||
~Server();
|
~Server();
|
||||||
@ -74,11 +77,31 @@ private:
|
|||||||
void process_request(FILE* fp_read, FILE* fp_write);
|
void process_request(FILE* fp_read, FILE* fp_write);
|
||||||
|
|
||||||
socket_t sock_;
|
socket_t sock_;
|
||||||
std::multimap<std::string, Handler> handlers_;
|
std::vector<std::pair<std::regex, Handler>> get_handlers_;
|
||||||
|
std::vector<std::pair<std::string, Handler>> post_handlers_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Implementation
|
// Implementation
|
||||||
|
|
||||||
|
template <class Fn>
|
||||||
|
void split(const char* b, const char* e, char d, Fn fn)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
int beg = 0;
|
||||||
|
|
||||||
|
while (e ? (b + i != e) : (b[i] != '\0')) {
|
||||||
|
if (b[i] == d) {
|
||||||
|
fn(&b[beg], &b[i]);
|
||||||
|
beg = i + 1;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i != 0) {
|
||||||
|
fn(&b[beg], &b[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline socket_t create_server_socket(const const char* ipaddr_or_hostname, int port)
|
inline socket_t create_server_socket(const const char* ipaddr_or_hostname, int port)
|
||||||
{
|
{
|
||||||
// Create a server socket
|
// Create a server socket
|
||||||
@ -154,12 +177,12 @@ inline Server::~Server()
|
|||||||
|
|
||||||
inline void Server::get(const char* pattern, Handler handler)
|
inline void Server::get(const char* pattern, Handler handler)
|
||||||
{
|
{
|
||||||
handlers_.insert(std::make_pair(pattern, handler));
|
get_handlers_.push_back(std::make_pair(pattern, handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Server::post(const char* pattern, Handler handler)
|
inline void Server::post(const char* pattern, Handler handler)
|
||||||
{
|
{
|
||||||
handlers_.insert(std::make_pair(pattern, handler));
|
post_handlers_.push_back(std::make_pair(pattern, handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Server::run(const const char*ipaddr_or_hostname, int port)
|
inline bool Server::run(const const char*ipaddr_or_hostname, int port)
|
||||||
@ -205,9 +228,9 @@ inline void Server::stop()
|
|||||||
sock_ = -1;
|
sock_ = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool read_request_line(FILE* fp, std::string& method, std::string& url)
|
inline bool read_request_line(FILE* fp, Request& request)
|
||||||
{
|
{
|
||||||
static std::regex re("(GET|POST) (.+) HTTP/1\\.1\r\n");
|
static std::regex re("(GET|POST) ([^?]+)(?:\\?(.+?))? HTTP/1\\.1\r\n");
|
||||||
|
|
||||||
const size_t BUFSIZ_REQUESTLINE = 2048;
|
const size_t BUFSIZ_REQUESTLINE = 2048;
|
||||||
char buf[BUFSIZ_REQUESTLINE];
|
char buf[BUFSIZ_REQUESTLINE];
|
||||||
@ -215,8 +238,27 @@ inline bool read_request_line(FILE* fp, std::string& method, std::string& url)
|
|||||||
|
|
||||||
std::cmatch m;
|
std::cmatch m;
|
||||||
if (std::regex_match(buf, m, re)) {
|
if (std::regex_match(buf, m, re)) {
|
||||||
method = std::string(m[1]);
|
request.method = std::string(m[1]);
|
||||||
url = std::string(m[2]);
|
request.url = std::string(m[2]);
|
||||||
|
|
||||||
|
// Parse query text
|
||||||
|
auto len = std::distance(m[3].first, m[3].second);
|
||||||
|
if (len > 0) {
|
||||||
|
const auto& pos = m[3];
|
||||||
|
split(pos.first, pos.second, '&', [&](const char* b, const char* e) {
|
||||||
|
std::string key;
|
||||||
|
std::string val;
|
||||||
|
split(b, e, '=', [&](const char* b, const char* e) {
|
||||||
|
if (key.empty()) {
|
||||||
|
key.assign(b, e);
|
||||||
|
} else {
|
||||||
|
val.assign(b, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
request.query[key] = val;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,11 +291,11 @@ inline void write_plain_text(FILE* fp, const char* s)
|
|||||||
fprintf(fp, "%s", s);
|
fprintf(fp, "%s", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void write_error(FILE* fp, int code)
|
inline void write_error(FILE* fp, int status)
|
||||||
{
|
{
|
||||||
const char* msg = NULL;
|
const char* msg = NULL;
|
||||||
|
|
||||||
switch (code) {
|
switch (status) {
|
||||||
case 400:
|
case 400:
|
||||||
msg = "Bad Request";
|
msg = "Bad Request";
|
||||||
break;
|
break;
|
||||||
@ -261,47 +303,60 @@ inline void write_error(FILE* fp, int code)
|
|||||||
msg = "Not Found";
|
msg = "Not Found";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
code = 500;
|
status = 500;
|
||||||
msg = "Internal Server Error";
|
msg = "Internal Server Error";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(msg);
|
assert(msg);
|
||||||
|
|
||||||
fprintf(fp, "HTTP/1.0 %d %s\r\n", code, msg);
|
fprintf(fp, "HTTP/1.0 %d %s\r\n", status, msg);
|
||||||
fprintf(fp, "Content-type: text/plain\r\n");
|
fprintf(fp, "Content-type: text/plain\r\n");
|
||||||
fprintf(fp, "Connection: close\r\n");
|
fprintf(fp, "Connection: close\r\n");
|
||||||
fprintf(fp, "\r\n");
|
fprintf(fp, "\r\n");
|
||||||
fprintf(fp, "Status: %d\r\n", code);
|
fprintf(fp, "Status: %d\r\n", status);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Server::process_request(FILE* fp_read, FILE* fp_write)
|
inline void Server::process_request(FILE* fp_read, FILE* fp_write)
|
||||||
{
|
{
|
||||||
|
Context cxt;
|
||||||
|
|
||||||
// Read and parse request line
|
// Read and parse request line
|
||||||
std::string method, url;
|
if (!read_request_line(fp_read, cxt.request)) {
|
||||||
if (!read_request_line(fp_read, method, url)) {
|
|
||||||
write_error(fp_write, 400);
|
write_error(fp_write, 400);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read headers
|
// Read headers
|
||||||
Map headers;
|
read_headers(fp_read, cxt.request.headers);
|
||||||
read_headers(fp_read, headers);
|
|
||||||
|
|
||||||
// Write content
|
// Routing
|
||||||
char buf[BUFSIZ];
|
int status = 404;
|
||||||
std::string content;
|
|
||||||
sprintf(buf, "Method: %s, URL: %s\n", method.c_str(), url.c_str());
|
|
||||||
content += buf;
|
|
||||||
|
|
||||||
//for (const auto& x : headers) {
|
if (cxt.request.method == "GET") {
|
||||||
for (auto it = headers.begin(); it != headers.end(); ++it) {
|
for (auto it = get_handlers_.begin(); it != get_handlers_.end(); ++it) {
|
||||||
const auto& x = *it;
|
const auto& pattern = it->first;
|
||||||
sprintf(buf, "%s: %s\n", x.first.c_str(), x.second.c_str());
|
const auto& handler = it->second;
|
||||||
content += buf;
|
|
||||||
|
std::smatch m;
|
||||||
|
if (std::regex_match(cxt.request.url, m, pattern)) {
|
||||||
|
for (size_t i = 1; i < m.size(); i++) {
|
||||||
|
cxt.request.params.push_back(m[i]);
|
||||||
|
}
|
||||||
|
status = handler(cxt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (cxt.request.method == "POST") {
|
||||||
|
// TODO: parse body
|
||||||
|
} else {
|
||||||
|
status = 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_plain_text(fp_write, content.c_str());
|
if (status == 200) {
|
||||||
|
write_plain_text(fp_write, cxt.response.body.c_str());
|
||||||
|
} else {
|
||||||
|
write_error(fp_write, status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace httpsvrkit
|
} // namespace httpsvrkit
|
||||||
|
Loading…
x
Reference in New Issue
Block a user