#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "log.hpp"
#include <string.h>
#include <unistd.h>
using namespace std;
Log lg;
const int backlog = 10;
enum { SocketErr = 2, BindErr, ListenErr };
class Sock {
public:
Sock() {}
~Sock() {}
void Socket() {
sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd_ < 0) {
lg(Fatal, "socket error, strerror: %s, errno: %d", strerror(errno), errno);
exit(SocketErr);
}
}
void Bind(uint16_t port) {
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = INADDR_ANY;
if (bind(sockfd_, (struct sockaddr*)&local, sizeof(local)) < 0) {
lg(Fatal, "Bind error, strerror: %s, errno: %d", strerror(errno), errno);
exit(BindErr);
}
}
void Listen() {
if (listen(sockfd_, backlog) < 0) {
lg(Fatal, "Listen Error, strerror: %s, errno: %d", strerror(errno), errno);
exit(ListenErr);
}
}
int Accept(std::string* ip, uint16_t* port) {
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int newfd = accept(sockfd_, (struct sockaddr*)&peer, &len);
if (newfd < 0) {
lg(Warning, "accept error, strerror: %s, errno: %d", strerror(errno), errno);
return -1;
}
char ipstr[64];
inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr));
*ip = ipstr;
*port = ntohs(peer.sin_port);
return newfd;
}
bool Connect(const string& ip, const uint16_t& port) {
struct sockaddr_in peer;
memset(&peer, 0, sizeof(peer));
peer.sin_family = AF_INET;
peer.sin_port = htons(port);
inet_pton(AF_INET, ip.c_str(), &(peer.sin_addr));
int n = connect(sockfd_, (struct sockaddr*)&peer, sizeof(peer));
if (n < 0) {
cerr << "Connect to" << ip << ":" << port << endl;
return false;
}
return true;
}
void Close() { close(sockfd_); }
int Fd() { return sockfd_; }
private:
int sockfd_;
};
#pragma once
#include <iostream>
#include "Socket.hpp"
#include <string>
#include <pthread.h>
#include <fstream>
#include <vector>
#include <sstream>
#include <sys/types.h>
#include <sys/socket.h>
#include <unordered_map>
std::string blank_line = "\r\n";
std::string wwwroot = "./wwwroot";
std::string homepage = "index.html";
class HttpServer;
class ThreadData {
public:
ThreadData(int sockfd, HttpServer* hs) : _sockfd(sockfd), _hs(hs) {}
public:
int _sockfd;
HttpServer* _hs;
};
class HttpResponse {
public:
void Deserialize(std::string req) {
while (true) {
std::size_t pos = req.find(blank_line);
if (pos == std::string::npos) break;
std::string temp = req.substr(0, pos);
if (temp.empty())
break;
vc.push_back(temp);
req.erase(0, pos + blank_line.size());
}
text += req;
}
void Parse() {
std::stringstream ss(vc[0]);
ss >> method >> url >> version;
file_path = wwwroot;
if (url == "/" || url == "index.html") {
file_path += "/";
file_path += homepage;
} else {
file_path += url;
}
}
void DebugPrint() {
for (auto line : vc) {
std::cout << line << std::endl;
std::cout << "------------" << std::endl;
}
std::cout << "method: " << method << std::endl;
std::cout << "url: " << url << std::endl;
std::cout << "version: " << version << std::endl;
std::cout << "file_path: " << file_path << std::endl;
std::cout << "text: " << text << std::endl;
}
public:
vector<string> vc;
string text;
std::string method;
std::string url;
std::string version;
std::string file_path;
};
class HttpServer {
public:
HttpServer(uint16_t port) : _port(port) {}
void start() {
_listen.Socket();
_listen.Bind(_port);
_listen.Listen();
lg(Info, "server create success");
for (;;) {
std::string clientip;
uint16_t port;
int sockfd = _listen.Accept(&clientip, &port);
lg(Info, "get a newfd:%d", sockfd);
pthread_t tid;
ThreadData* td = new ThreadData(sockfd, this);
pthread_create(&tid, nullptr, ThreadRun, td);
}
}
static std::string ReadHtmlContent(const std::string& htmlpath) {
std::ifstream in(htmlpath);
if (!in.is_open()) return "404";
std::string content;
std::string line;
while (getline(in, line)) {
content += line;
}
in.close();
return content;
}
static void HandlerHttp(int sockfd) {
char buffer[10240];
ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
if (n > 0) {
buffer[n] = 0;
HttpResponse rep;
rep.Deserialize(buffer);
rep.Parse();
rep.DebugPrint();
std::string text = ReadHtmlContent(rep.file_path);
std::string response_line = "HTTP/1.0 200 OK\r\n";
std::string response_header = "Content-Length: ";
response_header += std::to_string(text.size());
std::string response = response_line;
response += response_header;
response += "\r\n";
response += blank_line;
response += text;
send(sockfd, response.c_str(), response.size(), 0);
}
close(sockfd);
}
static void* ThreadRun(void* args) {
pthread_detach(pthread_self());
ThreadData* td = static_cast<ThreadData*>(args);
HandlerHttp(td->_sockfd);
close(td->_sockfd);
delete td;
return nullptr;
}
Sock _listen;
uint16_t _port;
};