-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhttp_server.cpp
144 lines (118 loc) · 4.75 KB
/
http_server.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstring>
#include <unistd.h>
#include <netinet/in.h>
#include <thread>
#include <ctime>
#include <filesystem>
#define PORT 8083
// Get the absolute path to the project root
const std::string PROJECT_ROOT = std::filesystem::current_path().string();
void handleClient(int client_socket);
std::string getCurrentTimestamp();
std::string serveFile(const std::string& path);
std::string getContentType(const std::string& path);
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
// Creating socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("Socket failed");
exit(EXIT_FAILURE);
}
// Binding to the specified port
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("Bind failed");
exit(EXIT_FAILURE);
}
// Listen for incoming connections
if (listen(server_fd, 10) < 0) {
perror("Listen failed");
exit(EXIT_FAILURE);
}
std::cout << "Server started on port " << PORT << "\n";
// Main server loop
while (true) {
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("Accept failed");
exit(EXIT_FAILURE);
}
// Handle each client in a new thread
std::thread clientThread(handleClient, new_socket);
clientThread.detach();
}
return 0;
}
// Handle individual client connections
void handleClient(int client_socket) {
char buffer[1024] = {0};
read(client_socket, buffer, 1024);
// Log request with timestamp and client info
std::string request(buffer);
std::cout << "[" << getCurrentTimestamp() << "] Received request: " << request << std::endl;
// Parse request and route
std::istringstream requestStream(request);
std::string method, url;
requestStream >> method >> url;
// If the request is for '/', serve 'index.html'
if (url == "/") {
url = "/index.html"; // Redirect root requests to 'index.html'
}
// Serve the requested file
std::string response = serveFile(url.substr(1)); // Remove leading '/' to get file path
if (response.empty()) {
// 404 Not Found if the file is not found
response = "HTTP/1.1 404 Not Found\nContent-Type: text/html\nContent-Length: 97\n\n"
"<html><body><h1>404 Not Found</h1><p>The page you are looking for does not exist.</p></body></html>";
}
// Send the response
send(client_socket, response.c_str(), response.size(), 0);
close(client_socket);
}
// Serve static files (HTML, CSS, JS, Images)
std::string serveFile(const std::string& path) {
// We assume that PROJECT_ROOT already contains the current directory,
// so no need to concatenate it again
std::string fullPath = std::filesystem::absolute(path).string();
std::cout << "Serving file from: " << fullPath << std::endl; // Debug print
std::ifstream file(fullPath, std::ios::binary); // Open the file in binary mode for images
if (!file.is_open()) {
std::cout << "File not found: " << fullPath << std::endl; // Debug print for file not found
return "";
}
// Read the file into a string
std::stringstream buffer;
buffer << file.rdbuf();
file.close();
// Determine the content type based on the file extension
std::string contentType = getContentType(path);
// Create the HTTP response
std::string fileContents = buffer.str();
std::string response = "HTTP/1.1 200 OK\nContent-Type: " + contentType + "\nContent-Length: " +
std::to_string(fileContents.size()) + "\n\n" + fileContents;
return response;
}
// Get the content type based on the file extension
std::string getContentType(const std::string& path) {
if (path.find(".html") != std::string::npos) return "text/html";
if (path.find(".css") != std::string::npos) return "text/css";
if (path.find(".js") != std::string::npos) return "application/javascript";
if (path.find(".png") != std::string::npos) return "image/png";
if (path.find(".jpg") != std::string::npos || path.find(".jpeg") != std::string::npos) return "image/jpeg";
if (path.find(".ico") != std::string::npos) return "image/x-icon";
return "text/plain"; // Default to plain text
}
// Get current timestamp for logging
std::string getCurrentTimestamp() {
std::time_t now = std::time(nullptr);
std::tm* localTime = std::localtime(&now);
char buffer[80];
std::strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", localTime);
return std::string(buffer);
}