Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
RamonZhou committed Sep 19, 2024
1 parent 756b79b commit 0d26c95
Show file tree
Hide file tree
Showing 31 changed files with 1,175 additions and 1 deletion.
21 changes: 21 additions & 0 deletions .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"windowsSdkVersion": "10.0.19041.0",
"compilerPath": "D:/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.31.31103/bin/Hostx64/x64/cl.exe",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "windows-msvc-x64"
}
],
"version": 4
}
74 changes: 74 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
"files.associations": {
"ostream": "cpp",
"string": "cpp",
"algorithm": "cpp",
"atomic": "cpp",
"bit": "cpp",
"cctype": "cpp",
"charconv": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"concepts": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"deque": "cpp",
"exception": "cpp",
"format": "cpp",
"forward_list": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"ios": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"iterator": "cpp",
"limits": "cpp",
"locale": "cpp",
"map": "cpp",
"memory": "cpp",
"mutex": "cpp",
"new": "cpp",
"optional": "cpp",
"queue": "cpp",
"ratio": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"system_error": "cpp",
"thread": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeinfo": "cpp",
"utility": "cpp",
"vector": "cpp",
"xfacet": "cpp",
"xiosbase": "cpp",
"xlocale": "cpp",
"xlocbuf": "cpp",
"xlocinfo": "cpp",
"xlocmes": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xloctime": "cpp",
"xmemory": "cpp",
"xstddef": "cpp",
"xstring": "cpp",
"xtr1common": "cpp",
"xtree": "cpp",
"xutility": "cpp",
"list": "cpp",
"unordered_map": "cpp",
"xhash": "cpp",
"functional": "cpp",
"fstream": "cpp"
}
}
18 changes: 18 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.PHONY: clean

CC = g++
CCFLAGS = -std=c++17 -Wall -lpthread -lm
SOURCE = main.cpp

all: $(SOURCE)
$(CC) $(SOURCE) -o main $(CCFLAGS)

run: $(SOURCE)
$(CC) $(SOURCE) -o main $(CCFLAGS)
./main

debug: $(SOURCE)
$(CC) $(SOURCE) -o main $(CCFLAGS) -DDEBUG

clean:
rm main
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
# Mini-HTTP-Server
# Mini HTTP Server

Implementation of a simple HTTP server listening at port 80 on local machine. It supports:

- multiple TCP connections
- GET and POST methods
- multiple content types such as `text/html`, `image/jpeg`, `text/plain`, `application/octet-stream`, forms, etc.
- easy way to configure more paths because of functional programming
- flexibility since we used a thread pool
21 changes: 21 additions & 0 deletions include/enums.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

enum class HTTPMethod {
mUnknown,
mGET,
mPOST,
mPUT,
mDELETE
};

enum class HTTPStatusCode {
mOk = 200,
mBadRequest = 400,
mUnauthorized = 401,
mForbidden = 403,
mNotFound = 404,
mInternalServerError = 500,
mBadGateway = 502,
mServiceUnavailable = 503,
mGatewayTimeout = 504
};
64 changes: 64 additions & 0 deletions include/handlers.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#pragma once

#include <string>
#include <iostream>
#include <fstream>
#include "request.hpp"
#include "response.hpp"
#include "enums.h"

#define BUFFERSIZE 65535

void badRequest(HTTPResponse &res) {
res.setStatusCode(HTTPStatusCode::mBadRequest);
res.setHeader("Content-Length", "0");
}

void handleGET(const HTTPRequest &req, HTTPResponse &res){
std::string fileurl = req.getUrl();
std::ifstream fin(std::string("/mnt/d/courses/network/lab3/test") + fileurl, std::ios::binary);
if (!fin) {
fileurl = req.getUrl() + ".html";
fin.open(std::string("/mnt/d/courses/network/lab3/test") + fileurl, std::ios::binary);
}
if (!fin) {
fileurl = req.getUrl() + "/index.html";
fin.open(std::string("/mnt/d/courses/network/lab3/test") + fileurl, std::ios::binary);
}
if (!fin) {
res.setStatusCode(HTTPStatusCode::mNotFound);
res.setHeader("Content-Type", "application/json");
res.setHeader("Content-Length", "0");
return;
}
res.setStatusCode(HTTPStatusCode::mOk);
size_t splitpos = fileurl.find_last_of(".");
std::string format = splitpos == std::string::npos ? "no" : fileurl.substr(splitpos + 1);
if (format == "html") res.setHeader("Content-Type", "text/html;charset=utf-8");
else if (format == "jpg" || format == "jpeg") res.setHeader("Content-Type", "image/jpeg");
else if (format == "txt") res.setHeader("Content-Type", "text/plain");
else res.setHeader("Content-Type", "application/octet-stream");
char *buffer = new char[BUFFERSIZE];
fin.seekg(0, std::ios::end);
std::streampos endpos = fin.tellg();
res.setHeader("Content-Length", std::to_string(endpos));
fin.seekg(0, std::ios::beg);
fin.read(buffer, endpos);
fin.close();
res.appendContent(std::string(buffer, endpos));
delete buffer;
}

void handleDOPOST(const HTTPRequest &req, HTTPResponse &res){
std::string login = req.getBodyVal("login");
std::string pass = req.getBodyVal("pass");
res.setStatusCode(HTTPStatusCode::mOk);
res.setHeader("Content-Type", "text/plain;charset=utf-8");
if (login.length() == 10 && login.substr(login.length() - 4) == pass) {
res.setHeader("Content-Length", std::to_string(strlen("登录成功")));
res.appendContent("登录成功");
} else {
res.setHeader("Content-Length", std::to_string(strlen("登录失败")));
res.appendContent("登录失败");
}
}
148 changes: 148 additions & 0 deletions include/request.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#pragma once

#include <iostream>
#include <sstream>
#include <unordered_map>
#include "enums.h"

#define BUFFERSIZE 65535

class HTTPRequest {
public:
HTTPRequest() {}
HTTPRequest(const char* buf, const size_t &bufsize) {
parseRequest(buf, bufsize);
}
std::string parseRequest(const char* buf, const int64_t &bufsize) {
// 先判断是不是 http
bool ishttp = false;
for (int i = 0; i < bufsize - 3; ++ i) {
if (buf[i] == '\r' && buf[i + 1] == '\n' &&
buf[i + 2] == '\r' && buf[i + 3] == '\n') {
ishttp = true;
break;
}
}
if (!ishttp) return std::string("Not HTTP");

char linebuf[BUFFERSIZE];
std::stringstream ss;
ss << std::string(buf, bufsize);
ss.getline(linebuf, BUFFERSIZE);
std::stringstream parserss(linebuf);
std::string method, encodedurl, version;
parserss >> method >> encodedurl >> version;

if (version != "HTTP/1.1") {
return std::string("Unsupported http version");
}

// 方法
if (method == "GET") {
_method = HTTPMethod::mGET;
} else if (method == "POST") {
_method = HTTPMethod::mPOST;
} else if (method == "PUT") {
_method = HTTPMethod::mPUT;
} else if (method == "DELETE") {
_method = HTTPMethod::mDELETE;
} else {
_method = HTTPMethod::mUnknown;
}
if (_method != HTTPMethod::mGET && _method != HTTPMethod::mPOST) {
return std::string("Unsupported method " + method);
}

// url 和参数
size_t splitpos = encodedurl.find_first_of("?#");
if (splitpos == std::string::npos) _url = encodedurl;
else {
_url = encodedurl.substr(0, splitpos);
std::string paramstr;
if (encodedurl[splitpos] == '?') {
do {
size_t prepos = splitpos;
splitpos = encodedurl.find_first_of("&#", splitpos + 1);
if (splitpos == std::string::npos) splitpos = encodedurl.length();
paramstr = encodedurl.substr(prepos + 1, (splitpos - prepos - 1));
size_t spl = paramstr.find_first_of("=");
_params[paramstr.substr(0, spl)] = paramstr.substr(spl + 1);
} while (splitpos != encodedurl.length() && encodedurl[splitpos] != '#');
}
}

if (_url[_url.length() - 1] == '/') _url.pop_back();

// http headers
for(ss.getline(linebuf, BUFFERSIZE);;ss.getline(linebuf, BUFFERSIZE)) {
if (linebuf[0] == '\r') break;
std::string headerstr(linebuf);
size_t splitpos = headerstr.find_first_of(":");
_headers[headerstr.substr(0, splitpos)] = headerstr.substr(splitpos + 2);
}

#ifdef DEBUG
std::cerr << "Method: " << method << "; URL: " << _url << "; Params: " << _params.size()
<< "; Headers: " << _headers.size() << std::endl;
#endif

// post body
std::string content = "";
int contentlen = _headers["Content-Length"] == "" ? 0 : std::stoi(_headers["Content-Length"]);
if (contentlen == 0) return "Ok";
while(ss.getline(linebuf, BUFFERSIZE)) {
content += linebuf;
}
if (_headers["Content-Type"].find("application/x-www-form-urlencoded") != std::string::npos) {
std::string paramstr = "";
do {
size_t prepos = splitpos;
splitpos = content.find_first_of("&", splitpos + 1);
if (splitpos == std::string::npos) splitpos = content.length();
paramstr = content.substr(prepos + 1, (splitpos - prepos - 1));
size_t spl = paramstr.find_first_of("=");
_body[paramstr.substr(0, spl)] = paramstr.substr(spl + 1);
} while (splitpos != content.length() && content[splitpos] != '#');
} else if (_headers["Content-Type"].find("application/json") != std::string::npos) {
// TO-DO
if (_method != HTTPMethod::mGET) return "Unsupported Content-Type: Neglected";
else return "Ok";
} else {
if (_method != HTTPMethod::mGET) return "Unsupported Content-Type: Neglected";
else return "Ok";
}

return "Ok";
}

HTTPMethod getMethod() const {
return _method;
}

std::string getUrl() const {
return _url;
}

std::string getHeaderVal(const std::string &key) const {
return _headers.find(key)->second;
}

std::string getParamVal(const std::string &key) const {
return _params.find(key)->second;
}

std::string getBodyVal(const std::string &key) const {
return _body.find(key)->second;
}

std::unordered_map<std::string, std::string>* getBodyMapPtr() {
return &_body;
}

private:
HTTPMethod _method;
std::string _url;
std::unordered_map<std::string, std::string> _headers;
std::unordered_map<std::string, std::string> _params;
std::unordered_map<std::string, std::string> _body;
};
Loading

0 comments on commit 0d26c95

Please sign in to comment.