HTTP clients at once. Default one.\n"
90 | " -9|--http09 Use HTTP/0.9 style requests.\n"
91 | " -1|--http10 Use HTTP/1.0 protocol.\n"
92 | " -2|--http11 Use HTTP/1.1 protocol.\n"
93 | " --get Use GET request method.\n"
94 | " --head Use HEAD request method.\n"
95 | " --options Use OPTIONS request method.\n"
96 | " --trace Use TRACE request method.\n"
97 | " -?|-h|--help This information.\n"
98 | " -V|--version Display program version.\n");
99 | }
100 |
101 | int main(int argc, char *argv[]) {
102 | int opt = 0;
103 | int options_index = 0;
104 | char *tmp = NULL;
105 |
106 | if (argc == 1) {
107 | usage();
108 | return 2;
109 | }
110 |
111 | while ((opt = getopt_long(argc, argv, "912Vfrt:p:c:?h", long_options, &options_index)) != EOF) {
112 | switch (opt) {
113 | case 0:
114 | break;
115 | case 'f':
116 | force = 1;
117 | break;
118 | case 'r':
119 | force_reload = 1;
120 | break;
121 | case '9':
122 | http10 = 0;
123 | break;
124 | case '1':
125 | http10 = 1;
126 | break;
127 | case '2':
128 | http10 = 2;
129 | break;
130 | case 'V':
131 | printf(PROGRAM_VERSION "\n");
132 | exit(0);
133 | case 't':
134 | benchtime = atoi(optarg);
135 | break;
136 | case 'p':
137 | /* proxy server parsing server:port */
138 | tmp = strrchr(optarg, ':');
139 | proxyhost = optarg;
140 | if (tmp == NULL) {
141 | break;
142 | }
143 | if (tmp == optarg) {
144 | fprintf(stderr, "Error in option --proxy %s: Missing hostname.\n", optarg);
145 | return 2;
146 | }
147 | if (tmp == optarg + strlen(optarg) - 1) {
148 | fprintf(stderr, "Error in option --proxy %s Port number is missing.\n", optarg);
149 | return 2;
150 | }
151 | *tmp = '\0';
152 | proxyport = atoi(tmp + 1);
153 | break;
154 | case ':':
155 | case 'h':
156 | case '?':
157 | usage();
158 | return 2;
159 | break;
160 | case 'c':
161 | clients = atoi(optarg);
162 | break;
163 | }
164 | }
165 |
166 | if (optind == argc) {
167 | fprintf(stderr, "webbench: Missing URL!\n");
168 | usage();
169 | return 2;
170 | }
171 |
172 | if (clients == 0) clients = 1;
173 | if (benchtime == 0) benchtime = 30;
174 |
175 | /* Copyright */
176 | fprintf(stderr, "Webbench - Simple Web Benchmark " PROGRAM_VERSION
177 | "\n"
178 | "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n");
179 |
180 | build_request(argv[optind]);
181 |
182 | // print request info ,do it in function build_request
183 | /*printf("Benchmarking: ");
184 |
185 | switch(method)
186 | {
187 | case METHOD_GET:
188 | default:
189 | printf("GET");break;
190 | case METHOD_OPTIONS:
191 | printf("OPTIONS");break;
192 | case METHOD_HEAD:
193 | printf("HEAD");break;
194 | case METHOD_TRACE:
195 | printf("TRACE");break;
196 | }
197 |
198 | printf(" %s",argv[optind]);
199 |
200 | switch(http10)
201 | {
202 | case 0: printf(" (using HTTP/0.9)");break;
203 | case 2: printf(" (using HTTP/1.1)");break;
204 | }
205 |
206 | printf("\n");
207 | */
208 |
209 | printf("Runing info: ");
210 |
211 | if (clients == 1)
212 | printf("1 client");
213 | else
214 | printf("%d clients", clients);
215 |
216 | printf(", running %d sec", benchtime);
217 |
218 | if (force) printf(", early socket close");
219 | if (proxyhost != NULL) printf(", via proxy server %s:%d", proxyhost, proxyport);
220 | if (force_reload) printf(", forcing reload");
221 |
222 | printf(".\n");
223 |
224 | return bench();
225 | }
226 |
227 | void build_request(const char *url) {
228 | char tmp[10];
229 | int i;
230 |
231 | // bzero(host,MAXHOSTNAMELEN);
232 | // bzero(request,REQUEST_SIZE);
233 | memset(host, 0, MAXHOSTNAMELEN);
234 | memset(request, 0, REQUEST_SIZE);
235 |
236 | if (force_reload && proxyhost != NULL && http10 < 1) http10 = 1;
237 | if (method == METHOD_HEAD && http10 < 1) http10 = 1;
238 | if (method == METHOD_OPTIONS && http10 < 2) http10 = 2;
239 | if (method == METHOD_TRACE && http10 < 2) http10 = 2;
240 |
241 | switch (method) {
242 | default:
243 | case METHOD_GET:
244 | strcpy(request, "GET");
245 | break;
246 | case METHOD_HEAD:
247 | strcpy(request, "HEAD");
248 | break;
249 | case METHOD_OPTIONS:
250 | strcpy(request, "OPTIONS");
251 | break;
252 | case METHOD_TRACE:
253 | strcpy(request, "TRACE");
254 | break;
255 | }
256 |
257 | strcat(request, " ");
258 |
259 | if (NULL == strstr(url, "://")) {
260 | fprintf(stderr, "\n%s: is not a valid URL.\n", url);
261 | exit(2);
262 | }
263 | if (strlen(url) > 1500) {
264 | fprintf(stderr, "URL is too long.\n");
265 | exit(2);
266 | }
267 | if (0 != strncasecmp("http://", url, 7)) {
268 | fprintf(stderr,
269 | "\nOnly HTTP protocol is directly supported, set --proxy for "
270 | "others.\n");
271 | exit(2);
272 | }
273 |
274 | /* protocol/host delimiter */
275 | i = strstr(url, "://") - url + 3;
276 |
277 | if (strchr(url + i, '/') == NULL) {
278 | fprintf(stderr, "\nInvalid URL syntax - hostname don't ends with '/'.\n");
279 | exit(2);
280 | }
281 |
282 | if (proxyhost == NULL) {
283 | /* get port from hostname */
284 | if (index(url + i, ':') != NULL && index(url + i, ':') < index(url + i, '/')) {
285 | strncpy(host, url + i, strchr(url + i, ':') - url - i);
286 | // bzero(tmp,10);
287 | memset(tmp, 0, 10);
288 | strncpy(tmp, index(url + i, ':') + 1, strchr(url + i, '/') - index(url + i, ':') - 1);
289 | /* printf("tmp=%s\n",tmp); */
290 | proxyport = atoi(tmp);
291 | if (proxyport == 0) proxyport = 80;
292 | } else {
293 | strncpy(host, url + i, strcspn(url + i, "/"));
294 | }
295 | // printf("Host=%s\n",host);
296 | strcat(request + strlen(request), url + i + strcspn(url + i, "/"));
297 | } else {
298 | // printf("ProxyHost=%s\nProxyPort=%d\n",proxyhost,proxyport);
299 | strcat(request, url);
300 | }
301 |
302 | if (http10 == 1)
303 | strcat(request, " HTTP/1.0");
304 | else if (http10 == 2)
305 | strcat(request, " HTTP/1.1");
306 |
307 | strcat(request, "\r\n");
308 |
309 | if (http10 > 0) strcat(request, "User-Agent: WebBench " PROGRAM_VERSION "\r\n");
310 | if (proxyhost == NULL && http10 > 0) {
311 | strcat(request, "Host: ");
312 | strcat(request, host);
313 | strcat(request, "\r\n");
314 | }
315 |
316 | if (force_reload && proxyhost != NULL) {
317 | strcat(request, "Pragma: no-cache\r\n");
318 | }
319 |
320 | if (http10 > 1) strcat(request, "Connection: close\r\n");
321 |
322 | /* add empty line at end */
323 | if (http10 > 0) strcat(request, "\r\n");
324 |
325 | printf("\nRequest:\n%s\n", request);
326 | }
327 |
328 | /* vraci system rc error kod */
329 | static int bench(void) {
330 | int i, j, k;
331 | pid_t pid = 0;
332 | FILE *f;
333 |
334 | /* check avaibility of target server */
335 | i = Socket(proxyhost == NULL ? host : proxyhost, proxyport);
336 | if (i < 0) {
337 | fprintf(stderr, "\nConnect to server failed. Aborting benchmark.\n");
338 | return 1;
339 | }
340 | close(i);
341 |
342 | /* create pipe */
343 | if (pipe(mypipe)) {
344 | perror("pipe failed.");
345 | return 3;
346 | }
347 |
348 | /* not needed, since we have alarm() in childrens */
349 | /* wait 4 next system clock tick */
350 | /*
351 | cas=time(NULL);
352 | while(time(NULL)==cas)
353 | sched_yield();
354 | */
355 |
356 | /* fork childs */
357 | for (i = 0; i < clients; i++) {
358 | pid = fork();
359 | if (pid <= (pid_t)0) {
360 | /* child process or error*/
361 | sleep(1); /* make childs faster */
362 | break;
363 | }
364 | }
365 |
366 | if (pid < (pid_t)0) {
367 | fprintf(stderr, "problems forking worker no. %d\n", i);
368 | perror("fork failed.");
369 | return 3;
370 | }
371 |
372 | if (pid == (pid_t)0) {
373 | /* I am a child */
374 | if (proxyhost == NULL)
375 | benchcore(host, proxyport, request);
376 | else
377 | benchcore(proxyhost, proxyport, request);
378 |
379 | /* write results to pipe */
380 | f = fdopen(mypipe[1], "w");
381 | if (f == NULL) {
382 | perror("open pipe for writing failed.");
383 | return 3;
384 | }
385 | /* fprintf(stderr,"Child - %d %d\n",speed,failed); */
386 | fprintf(f, "%d %d %d\n", speed, failed, bytes);
387 | fclose(f);
388 |
389 | return 0;
390 | } else {
391 | f = fdopen(mypipe[0], "r");
392 | if (f == NULL) {
393 | perror("open pipe for reading failed.");
394 | return 3;
395 | }
396 |
397 | setvbuf(f, NULL, _IONBF, 0);
398 |
399 | speed = 0;
400 | failed = 0;
401 | bytes = 0;
402 |
403 | while (1) {
404 | pid = fscanf(f, "%d %d %d", &i, &j, &k);
405 | if (pid < 2) {
406 | fprintf(stderr, "Some of our childrens died.\n");
407 | break;
408 | }
409 |
410 | speed += i;
411 | failed += j;
412 | bytes += k;
413 |
414 | /* fprintf(stderr,"*Knock* %d %d read=%d\n",speed,failed,pid); */
415 | if (--clients == 0) break;
416 | }
417 |
418 | fclose(f);
419 |
420 | printf(
421 | "\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d "
422 | "failed.\n",
423 | (int)((speed + failed) / (benchtime / 60.0f)), (int)(bytes / (float)benchtime), speed, failed);
424 | }
425 |
426 | return i;
427 | }
428 |
429 | void benchcore(const char *host, const int port, const char *req) {
430 | int rlen;
431 | char buf[1500];
432 | int s, i;
433 | struct sigaction sa;
434 |
435 | /* setup alarm signal handler */
436 | sa.sa_handler = alarm_handler;
437 | sa.sa_flags = 0;
438 | if (sigaction(SIGALRM, &sa, NULL)) exit(3);
439 |
440 | alarm(benchtime); // after benchtime,then exit
441 |
442 | rlen = strlen(req);
443 | nexttry:
444 | while (1) {
445 | if (timerexpired) {
446 | if (failed > 0) {
447 | /* fprintf(stderr,"Correcting failed by signal\n"); */
448 | failed--;
449 | }
450 | return;
451 | }
452 |
453 | s = Socket(host, port);
454 | if (s < 0) {
455 | failed++;
456 | continue;
457 | }
458 | if (rlen != write(s, req, rlen)) {
459 | failed++;
460 | close(s);
461 | continue;
462 | }
463 | if (http10 == 0)
464 | if (shutdown(s, 1)) {
465 | failed++;
466 | close(s);
467 | continue;
468 | }
469 | if (force == 0) {
470 | /* read all available data from socket */
471 | while (1) {
472 | if (timerexpired) break;
473 | i = read(s, buf, 1500);
474 | /* fprintf(stderr,"%d\n",i); */
475 | if (i < 0) {
476 | failed++;
477 | close(s);
478 | goto nexttry;
479 | } else if (i == 0)
480 | break;
481 | else
482 | bytes += i;
483 | }
484 | }
485 | if (close(s)) {
486 | failed++;
487 | continue;
488 | }
489 | speed++;
490 | }
491 | }
492 |
--------------------------------------------------------------------------------
/config.ini:
--------------------------------------------------------------------------------
1 | [Worker]
2 | thread_num=4
3 | port=8080
4 | daemon=1
5 |
6 | [Server]
7 | server_name=how2cs.cn
8 | root=/root/www/
9 |
--------------------------------------------------------------------------------
/pages/403.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 403 Forbidden
6 |
13 |
14 |
15 |
16 |
17 | 403 Forbidden
18 |
19 |
20 | LCWebserver/0.3 (Ubuntu)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/pages/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 404 Not Found
6 |
13 |
14 |
15 |
16 |
17 | 404 Not Found
18 |
19 |
20 | CSGuide WebServer/0.3 (Ubuntu)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/pages/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Welcome to CSGuide!
6 |
13 |
14 |
15 | Welcome to CSGuide WebServer !
16 | If you see this page, the CSGuide WebServer is successfully installed and
17 | working.
18 |
19 | For online documentation and support please refer to
20 | CSGuide WebServer.
21 |
22 |
Thank you for using CSGuide WebServer.
23 | By 编程指北
24 |
25 |
--------------------------------------------------------------------------------
/version_0.1/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imarvinle/WebServer/e94c6af586082e773527deaf8cde75e05177ca4f/version_0.1/.DS_Store
--------------------------------------------------------------------------------
/version_0.1/include/httpparse.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 | #ifndef WEBSERVER_HTTPPARSE_H
6 | #define WEBSERVER_HTTPPARSE_H
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | #define CR '\r'
13 | #define LF '\n'
14 | #define LINE_END '\0'
15 | #define PASS
16 |
17 | namespace http {
18 |
19 | class HttpRequest;
20 |
21 | std::ostream &operator<<(std::ostream &, const HttpRequest &);
22 |
23 | class HttpRequestParser {
24 | public:
25 | enum LINE_STATE { LINE_OK = 0, LINE_BAD, LINE_MORE };
26 | enum PARSE_STATE { PARSE_REQUESTLINE = 0, PARSE_HEADER, PARSE_BODY };
27 | enum HTTP_CODE { NO_REQUEST, GET_REQUEST, BAD_REQUEST, FORBIDDEN_REQUEST, INTERNAL_ERROR, CLOSED_CONNECTION };
28 |
29 | static LINE_STATE parse_line(char *buffer, int &checked_index, int &read_index);
30 | static HTTP_CODE parse_requestline(char *line, PARSE_STATE &parse_state, HttpRequest &request);
31 | static HTTP_CODE parse_headers(char *line, PARSE_STATE &parse_state, HttpRequest &request);
32 | static HTTP_CODE parse_body(char *body, HttpRequest &request);
33 | static HTTP_CODE parse_content(char *buffer, int &check_index, int &read_index, PARSE_STATE &parse_state,
34 | int &start_line, HttpRequest &request);
35 | };
36 |
37 | struct HttpRequest {
38 | friend std::ostream &operator<<(std::ostream &, const HttpRequest &);
39 |
40 | enum HTTP_VERSION { HTTP_10 = 0, HTTP_11, VERSION_NOT_SUPPORT };
41 | enum HTTP_METHOD { GET = 0, POST, PUT, DELETE, METHOD_NOT_SUPPORT };
42 | enum HTTP_HEADER {
43 | Host = 0,
44 | User_Agent,
45 | Connection,
46 | Accept_Encoding,
47 | Accept_Language,
48 | Accept,
49 | Cache_Control,
50 | Upgrade_Insecure_Requests
51 | };
52 | struct EnumClassHash {
53 | template
54 | std::size_t operator()(T t) const {
55 | return static_cast(t);
56 | }
57 | };
58 |
59 | static std::unordered_map header_map;
60 |
61 | HttpRequest(std::string url = std::string(""), HTTP_METHOD method = METHOD_NOT_SUPPORT,
62 | HTTP_VERSION version = VERSION_NOT_SUPPORT)
63 | : mMethod(method),
64 | mVersion(version),
65 | mUri(url),
66 | mContent(nullptr),
67 | mHeaders(std::unordered_map()){};
68 |
69 | HTTP_METHOD mMethod;
70 | HTTP_VERSION mVersion;
71 | std::string mUri;
72 | char *mContent;
73 | std::unordered_map mHeaders;
74 | };
75 |
76 | } // namespace http
77 |
78 | #endif // WEBSERVER_HTTPPARSE_H
79 |
--------------------------------------------------------------------------------
/version_0.1/include/httpresponse.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 | #ifndef WEBSERVER_HTTPRESPONSE_H
6 | #define WEBSERVER_HTTPRESPONSE_H
7 |
8 | #include
9 | #include
10 |
11 | #include "httpparse.h"
12 |
13 | #define BASEPATH
14 |
15 | namespace http {
16 |
17 | struct MimeType {
18 | MimeType(const std::string &str) : type(str){};
19 | MimeType(const char *str) : type(str){};
20 |
21 | std::string type;
22 | };
23 |
24 | extern std::unordered_map Mime_map;
25 |
26 | class HttpResponse {
27 | public:
28 | enum HttpStatusCode { Unknow, k200Ok = 200, k403forbiden = 403, k404NotFound = 404 };
29 |
30 | explicit HttpResponse(bool close)
31 | : mStatusCode(Unknow),
32 | mCloseConnection(close),
33 | mMime("text/html"),
34 | mBody(nullptr),
35 | mVersion(HttpRequest::HTTP_11) {}
36 |
37 | void setStatusCode(HttpStatusCode code) { mStatusCode = code; }
38 | void setBody(const char *buf) { mBody = buf; }
39 | void setContentLength(int len) { mContentLength = len; }
40 | void setVersion(const HttpRequest::HTTP_VERSION &version) { mVersion = version; }
41 |
42 | void setStatusMsg(const std::string &msg) { mStatusMsg = msg; }
43 | void setFilePath(const std::string &path) { mFilePath = path; }
44 | void setMime(const MimeType &mime) { mMime = mime; }
45 |
46 | void addHeader(const std::string &key, const std::string &value) { mHeaders[key] = value; }
47 | bool closeConnection() const { return mCloseConnection; }
48 | const HttpRequest::HTTP_VERSION version() const { return mVersion; }
49 | const std::string &filePath() const { return mFilePath; }
50 | HttpStatusCode statusCode() const { return mStatusCode; }
51 | const std::string &statusMsg() const { return mStatusMsg; }
52 |
53 | void appenBuffer(char *) const;
54 |
55 | ~HttpResponse() {
56 | if (mBody != nullptr) delete[] mBody;
57 | }
58 |
59 | private:
60 | HttpStatusCode mStatusCode;
61 | HttpRequest::HTTP_VERSION mVersion;
62 | std::string mStatusMsg;
63 | bool mCloseConnection;
64 | MimeType mMime;
65 | const char *mBody;
66 | int mContentLength;
67 | std::string mFilePath;
68 | std::unordered_map mHeaders;
69 | };
70 |
71 | } // namespace http
72 |
73 | #endif // WEBSERVER_HTTPRESPONSE_H
74 |
--------------------------------------------------------------------------------
/version_0.1/include/server.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #ifndef WEBSERVER_SERVER_H
7 | #define WEBSERVER_SERVER_H
8 |
9 | #include "httpparse.h"
10 | #include "httpresponse.h"
11 | #include "ssocket.h"
12 |
13 | #define BUFFERSIZE 1024
14 |
15 | namespace server {
16 |
17 | class HttpServer {
18 | public:
19 | explicit HttpServer(int port = 80, const char *ip = nullptr) : serverSocket(port, ip) {
20 | serverSocket.bind();
21 | serverSocket.listen();
22 | }
23 |
24 | void run();
25 |
26 | private:
27 | void do_request(const nsocket::ClientSocket &);
28 | void header(const http::HttpRequest &, http::HttpResponse &);
29 | void static_file(http::HttpResponse &, const char *);
30 | void send(const http::HttpResponse &, const nsocket::ClientSocket &);
31 | void getMime(const http::HttpRequest &, http::HttpResponse &);
32 |
33 | nsocket::ServerSocket serverSocket;
34 | };
35 | } // namespace server
36 |
37 | #endif // WEBSERVER_SERVER_H
38 |
--------------------------------------------------------------------------------
/version_0.1/include/ssocket.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #ifndef WEBSERVER_SOCKET_H
7 | #define WEBSERVER_SOCKET_H
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include
15 |
16 | namespace nsocket {
17 | class ClientSocket;
18 |
19 | void setReusePort(int fd);
20 |
21 | class ServerSocket {
22 | public:
23 | ServerSocket(int port = 8080, const char *ip = nullptr);
24 | ~ServerSocket();
25 | void bind();
26 |
27 | void listen();
28 |
29 | int accept(ClientSocket &);
30 |
31 | public:
32 | sockaddr_in mAddr;
33 | int fd;
34 | int mPort;
35 | const char *mIp;
36 | };
37 |
38 | class ClientSocket {
39 | public:
40 | ~ClientSocket();
41 |
42 | socklen_t mLen;
43 | sockaddr_in mAddr;
44 | int fd;
45 | };
46 | } // namespace nsocket
47 | #endif // WEBSERVER_SOCKET_H
48 |
--------------------------------------------------------------------------------
/version_0.1/include/utils.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 | #ifndef WEBSERVER_UTILS_H
6 | #define WEBSERVER_UTILS_H
7 |
8 | #include
9 |
10 | namespace util {
11 | using namespace std;
12 | std::string& ltrim(string&);
13 | std::string& rtrim(string&);
14 | std::string& trim(string&);
15 | } // namespace util
16 |
17 | #endif // WEBSERVER_UTILS_H
18 |
--------------------------------------------------------------------------------
/version_0.1/src/base/noncopyable.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #ifndef WEBSERVER_NONCOPYABLE_H
7 | #define WEBSERVER_NONCOPYABLE_H
8 |
9 | class noncopyable {
10 | public:
11 | noncopyable(const noncopyable&) = delete;
12 | void operator=(const noncopyable&) = delete;
13 |
14 | protected:
15 | noncopyable() = default;
16 | ~noncopyable() = default;
17 | };
18 | #endif // WEBSERVER_NONCOPYABLE_H
19 |
--------------------------------------------------------------------------------
/version_0.1/src/httpparse.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #include "../include/httpparse.h"
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | #include "../include/utils.h"
13 |
14 | std::unordered_map http::HttpRequest::header_map = {
15 | {"HOST", http::HttpRequest::Host},
16 | {"USER-AGENT", http::HttpRequest::User_Agent},
17 | {"CONNECTION", http::HttpRequest::Connection},
18 | {"ACCEPT-ENCODING", http::HttpRequest::Accept_Encoding},
19 | {"ACCEPT-LANGUAGE", http::HttpRequest::Accept_Language},
20 | {"ACCEPT", http::HttpRequest::Accept},
21 | {"CACHE-CONTROL", http::HttpRequest::Cache_Control},
22 | {"UPGRADE-INSECURE-REQUESTS", http::HttpRequest::Upgrade_Insecure_Requests}};
23 |
24 | // 解析一行内容, buffer[checked_index, read_index)
25 | // check_index是需要分析的第一个字符, read_index已经读取数据末尾下一个字符
26 | http::HttpRequestParser::LINE_STATE http::HttpRequestParser::parse_line(char *buffer, int &checked_index,
27 | int &read_index) {
28 | char temp;
29 | for (; checked_index < read_index; checked_index++) {
30 | temp = buffer[checked_index];
31 | if (temp == CR) {
32 | // 到末尾,需要读入
33 | if (checked_index + 1 == read_index) return LINE_MORE;
34 | // 完整 "\r\n"
35 | if (buffer[checked_index + 1] == LF) {
36 | buffer[checked_index++] = LINE_END;
37 | buffer[checked_index++] = LINE_END;
38 | return LINE_OK;
39 | }
40 |
41 | return LINE_BAD;
42 | }
43 | }
44 | // 需要读入更多
45 | return LINE_MORE;
46 | }
47 |
48 | // 解析请求行
49 | http::HttpRequestParser::HTTP_CODE http::HttpRequestParser::parse_requestline(char *line, PARSE_STATE &parse_state,
50 | HttpRequest &request) {
51 | char *url = strpbrk(line, " \t");
52 | if (!url) {
53 | return BAD_REQUEST;
54 | }
55 |
56 | // 分割 method 和 url
57 | *url++ = '\0';
58 |
59 | char *method = line;
60 |
61 | if (strcasecmp(method, "GET") == 0) {
62 | request.mMethod = HttpRequest::GET;
63 | } else if (strcasecmp(method, "POST") == 0) {
64 | request.mMethod = HttpRequest::POST;
65 | } else if (strcasecmp(method, "PUT") == 0) {
66 | request.mMethod = HttpRequest::PUT;
67 | } else {
68 | return BAD_REQUEST;
69 | }
70 |
71 | url += strspn(url, " \t");
72 | char *version = strpbrk(url, " \t");
73 | if (!version) {
74 | return BAD_REQUEST;
75 | }
76 | *version++ = '\0';
77 | version += strspn(version, " \t");
78 |
79 | // HTTP/1.1 后面可能还存在空白字符
80 | if (strncasecmp("HTTP/1.1", version, 8) == 0) {
81 | request.mVersion = HttpRequest::HTTP_11;
82 | } else if (strncasecmp("HTTP/1.0", version, 8) == 0) {
83 | request.mVersion = HttpRequest::HTTP_10;
84 | } else {
85 | return BAD_REQUEST;
86 | }
87 |
88 | if (strncasecmp(url, "http://", 7) == 0) {
89 | url += 7;
90 | url = strchr(url, '/');
91 | } else if (strncasecmp(url, "/", 1) == 0) {
92 | PASS;
93 | } else {
94 | return BAD_REQUEST;
95 | }
96 |
97 | if (!url || *url != '/') {
98 | return BAD_REQUEST;
99 | }
100 | request.mUri = std::string(url);
101 | // 分析头部字段
102 | parse_state = PARSE_HEADER;
103 | return NO_REQUEST;
104 | }
105 |
106 | // 分析头部字段
107 | http::HttpRequestParser::HTTP_CODE http::HttpRequestParser::parse_headers(char *line, PARSE_STATE &parse_state,
108 | HttpRequest &request) {
109 | if (*line == '\0') {
110 | if (request.mMethod == HttpRequest::GET) {
111 | return GET_REQUEST;
112 | }
113 | parse_state = PARSE_BODY;
114 | return NO_REQUEST;
115 | }
116 |
117 | // char key[20]曾被缓冲区溢出
118 | char key[100], value[100];
119 |
120 | // 需要修改有些value里也包含了':'符号
121 | sscanf(line, "%[^:]:%[^:]", key, value);
122 |
123 | decltype(HttpRequest::header_map)::iterator it;
124 | std::string key_s(key);
125 | std::transform(key_s.begin(), key_s.end(), key_s.begin(), ::toupper);
126 | std::string value_s(value);
127 | if (key_s == std::string("UPGRADE-INSECURE-REQUESTS")) {
128 | return NO_REQUEST;
129 | }
130 |
131 | if ((it = HttpRequest::header_map.find(util::trim(key_s))) != (HttpRequest::header_map.end())) {
132 | request.mHeaders.insert(std::make_pair(it->second, util::trim(value_s)));
133 | } else {
134 | std::cout << "Header no support: " << key << " : " << value << std::endl;
135 | }
136 |
137 | return NO_REQUEST;
138 | }
139 |
140 | // 解析body
141 | http::HttpRequestParser::HTTP_CODE http::HttpRequestParser::parse_body(char *body, http::HttpRequest &request) {
142 | request.mContent = body;
143 | return GET_REQUEST;
144 | }
145 |
146 | // http 请求入口
147 | http::HttpRequestParser::HTTP_CODE http::HttpRequestParser::parse_content(
148 | char *buffer, int &check_index, int &read_index, http::HttpRequestParser::PARSE_STATE &parse_state, int &start_line,
149 | HttpRequest &request) {
150 | LINE_STATE line_state = LINE_OK;
151 | HTTP_CODE retcode = NO_REQUEST;
152 | while ((line_state = parse_line(buffer, check_index, read_index)) == LINE_OK) {
153 | char *temp = buffer + start_line; // 这一行在buffer中的起始位置
154 | start_line = check_index; // 下一行起始位置
155 |
156 | switch (parse_state) {
157 | case PARSE_REQUESTLINE: {
158 | retcode = parse_requestline(temp, parse_state, request);
159 | if (retcode == BAD_REQUEST) return BAD_REQUEST;
160 |
161 | break;
162 | }
163 |
164 | case PARSE_HEADER: {
165 | retcode = parse_headers(temp, parse_state, request);
166 | if (retcode == BAD_REQUEST) {
167 | return BAD_REQUEST;
168 | } else if (retcode == GET_REQUEST) {
169 | return GET_REQUEST;
170 | }
171 | break;
172 | }
173 |
174 | case PARSE_BODY: {
175 | retcode = parse_body(temp, request);
176 | if (retcode == GET_REQUEST) {
177 | return GET_REQUEST;
178 | }
179 | return BAD_REQUEST;
180 | }
181 | default:
182 | return INTERNAL_ERROR;
183 | }
184 | }
185 | if (line_state == LINE_MORE) {
186 | return NO_REQUEST;
187 | } else {
188 | return BAD_REQUEST;
189 | }
190 | }
191 | // 重载HttpRequest <<
192 |
193 | std::ostream &http::operator<<(std::ostream &os, const http::HttpRequest &request) {
194 | os << "method:" << request.mMethod << std::endl;
195 | os << "uri:" << request.mUri << std::endl;
196 | os << "version:" << request.mVersion << std::endl;
197 | // os << "content:" << request.mContent << std::endl;
198 | for (auto it = request.mHeaders.begin(); it != request.mHeaders.end(); it++) {
199 | os << it->first << ":" << it->second << std::endl;
200 | }
201 | return os;
202 | }
203 |
--------------------------------------------------------------------------------
/version_0.1/src/httpresponse.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #include "../include/httpresponse.h"
7 |
8 | #include
9 |
10 | std::unordered_map http::Mime_map = {{".html", "text/html"},
11 | {".xml", "text/xml"},
12 | {".xhtml", "application/xhtml+xml"},
13 | {".txt", "text/plain"},
14 | {".rtf", "application/rtf"},
15 | {".pdf", "application/pdf"},
16 | {".word", "application/msword"},
17 | {".png", "image/png"},
18 | {".gif", "image/gif"},
19 | {".jpg", "image/jpeg"},
20 | {".jpeg", "image/jpeg"},
21 | {".au", "audio/basic"},
22 | {".mpeg", "video/mpeg"},
23 | {".mpg", "video/mpeg"},
24 | {".avi", "video/x-msvideo"},
25 | {".gz", "application/x-gzip"},
26 | {".tar", "application/x-tar"},
27 | {".css", "text/css"},
28 | {"", "text/plain"},
29 | {"default", "text/plain"}};
30 |
31 | void http::HttpResponse::appenBuffer(char *buffer) const {
32 | if (mVersion == HttpRequest::HTTP_11) {
33 | sprintf(buffer, "HTTP/1.1 %d %s\r\n", mStatusCode, mStatusMsg.c_str());
34 | } else {
35 | sprintf(buffer, "HTTP/1.0 %d %s\r\n", mStatusCode, mStatusMsg.c_str());
36 | }
37 |
38 | for (auto it = mHeaders.begin(); it != mHeaders.end(); it++) {
39 | sprintf(buffer, "%s%s: %s\r\n", buffer, it->first.c_str(), it->second.c_str());
40 | }
41 | sprintf(buffer, "%sContent-type: %s\r\n", buffer, mMime.type.c_str());
42 | if (mCloseConnection) {
43 | sprintf(buffer, "%sConnection: close\r\n", buffer);
44 | } else {
45 | sprintf(buffer, "%sConnection: keep-alive\r\n", buffer);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/version_0.1/src/main.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #include
7 |
8 | #include
9 | #include
10 |
11 | #include
12 | #include
13 |
14 | #include "../include/server.h"
15 |
16 | int main(int argc, const char *argv[]) {
17 | char *buffer;
18 | //也可以将buffer作为输出参数
19 | if ((buffer = getcwd(NULL, 0)) == NULL) {
20 | perror("getcwd error");
21 | } else {
22 | printf("%s\n", buffer);
23 | free(buffer);
24 | }
25 |
26 | server::HttpServer httpServer(80);
27 | httpServer.run();
28 | }
29 |
--------------------------------------------------------------------------------
/version_0.1/src/server.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #include "../include/server.h"
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include
15 | #include
16 |
17 | #include "../include/httpparse.h"
18 | #include "../include/httpresponse.h"
19 |
20 | using namespace server;
21 | using namespace nsocket;
22 | using namespace http;
23 |
24 | const char *basePath = "/Users/lichunlin/CLionProjects/webserver/";
25 |
26 | void HttpServer::run() {
27 | while (true) {
28 | ClientSocket clientSocket;
29 | serverSocket.accept(clientSocket);
30 | do_request(clientSocket);
31 | }
32 | }
33 |
34 | void HttpServer::do_request(const ClientSocket &clientSocket) {
35 | char buffer[BUFFERSIZE];
36 |
37 | bzero(buffer, BUFFERSIZE);
38 | int check_index = 0, read_index = 0, start_line = 0;
39 | ssize_t recv_data;
40 | http::HttpRequestParser::PARSE_STATE parse_state = http::HttpRequestParser::PARSE_REQUESTLINE;
41 |
42 | while (true) {
43 | http::HttpRequest request;
44 |
45 | recv_data = recv(clientSocket.fd, buffer + read_index, BUFFERSIZE - read_index, 0);
46 | if (recv_data == -1) {
47 | std::cout << "reading faild" << std::endl;
48 | exit(0);
49 | }
50 | if (recv_data == 0) {
51 | std::cout << "connection closed by peer" << std::endl;
52 | break;
53 | }
54 | read_index += recv_data;
55 |
56 | http::HttpRequestParser::HTTP_CODE retcode =
57 | http::HttpRequestParser::parse_content(buffer, check_index, read_index, parse_state, start_line, request);
58 |
59 | if (retcode == http::HttpRequestParser::NO_REQUEST) {
60 | continue;
61 | }
62 |
63 | if (retcode == http::HttpRequestParser::GET_REQUEST) {
64 | HttpResponse response(true);
65 | header(request, response);
66 | getMime(request, response);
67 | static_file(response, "/Users/lichunlin/CLionProjects/webserver/version_0.1");
68 | send(response, clientSocket);
69 | } else {
70 | std::cout << "Bad Request" << std::endl;
71 | }
72 | }
73 | }
74 |
75 | void HttpServer::header(const HttpRequest &request, HttpResponse &response) {
76 | if (request.mVersion == HttpRequest::HTTP_11) {
77 | response.setVersion(HttpRequest::HTTP_11);
78 | } else {
79 | response.setVersion(HttpRequest::HTTP_10);
80 | }
81 | response.addHeader("Server", "LC WebServer");
82 | }
83 |
84 | // 获取Mime 同时设置path到response
85 | void HttpServer::getMime(const http::HttpRequest &request, http::HttpResponse &response) {
86 | std::string filepath = request.mUri;
87 | std::string mime;
88 | int pos;
89 | if ((pos = filepath.rfind('?')) != std::string::npos) {
90 | filepath.erase(filepath.rfind('?'));
91 | }
92 |
93 | if (filepath.rfind('.') != std::string::npos) {
94 | mime = filepath.substr(filepath.rfind('.'));
95 | }
96 | decltype(http::Mime_map)::iterator it;
97 |
98 | if ((it = http::Mime_map.find(mime)) != http::Mime_map.end()) {
99 | response.setMime(it->second);
100 | } else {
101 | response.setMime(http::Mime_map.find("default")->second);
102 | }
103 | response.setFilePath(filepath);
104 | }
105 |
106 | void HttpServer::static_file(HttpResponse &response, const char *basepath) {
107 | struct stat file_stat;
108 | char file[strlen(basepath) + strlen(response.filePath().c_str()) + 1];
109 | strcpy(file, basepath);
110 | strcat(file, response.filePath().c_str());
111 |
112 | if (stat(file, &file_stat) < 0) {
113 | response.setStatusCode(HttpResponse::k404NotFound);
114 | response.setStatusMsg("Not Found");
115 | response.setFilePath(std::string(basepath) + "/404.html");
116 | return;
117 | }
118 |
119 | if (!S_ISREG(file_stat.st_mode)) {
120 | response.setStatusCode(HttpResponse::k403forbiden);
121 | response.setStatusMsg("ForBidden");
122 | response.setFilePath(std::string(basepath) + "/403.html");
123 | return;
124 | }
125 |
126 | response.setStatusCode(HttpResponse::k200Ok);
127 | response.setStatusMsg("OK");
128 | response.setFilePath(file);
129 | return;
130 | }
131 |
132 | void HttpServer::send(const http::HttpResponse &response, const nsocket::ClientSocket &clientSocket) {
133 | char header[BUFFERSIZE];
134 | bzero(header, '\0');
135 | const char *internal_error = "Internal Error";
136 | struct stat file_stat;
137 | response.appenBuffer(header);
138 | if (stat(response.filePath().c_str(), &file_stat) < 0) {
139 | sprintf(header, "%sContent-length: %d\r\n\r\n", header, strlen(internal_error));
140 | sprintf(header, "%s%s", header, internal_error);
141 | ::send(clientSocket.fd, header, strlen(header), 0);
142 | return;
143 | }
144 |
145 | int filefd = ::open(response.filePath().c_str(), O_RDONLY);
146 | if (filefd < 0) {
147 | sprintf(header, "%sContent-length: %d\r\n\r\n", header, strlen(internal_error));
148 | sprintf(header, "%s%s", header, internal_error);
149 | ::send(clientSocket.fd, header, strlen(header), 0);
150 | return;
151 | }
152 |
153 | sprintf(header, "%sContent-length: %d\r\n\r\n", header, file_stat.st_size);
154 | ::send(clientSocket.fd, header, strlen(header), 0);
155 | void *mapbuf = mmap(NULL, file_stat.st_size, PROT_READ, MAP_PRIVATE, filefd, 0);
156 | ::send(clientSocket.fd, mapbuf, file_stat.st_size, 0);
157 | munmap(mapbuf, file_stat.st_size);
158 | close(filefd);
159 | return;
160 | err:
161 | sprintf(header, "%sContent-length: %d\r\n\r\n", header, strlen(internal_error));
162 | sprintf(header, "%s%s", header, internal_error);
163 | ::send(clientSocket.fd, header, strlen(header), 0);
164 | return;
165 | }
--------------------------------------------------------------------------------
/version_0.1/src/ssocket.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #include "../include/ssocket.h"
7 |
8 | void nsocket::setReusePort(int fd) {
9 | int opt = 1;
10 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt));
11 | }
12 |
13 | nsocket::ServerSocket::ServerSocket(int port, const char *ip) : mPort(port), mIp(ip) {
14 | bzero(&mAddr, sizeof(mAddr));
15 | mAddr.sin_family = AF_INET;
16 | mAddr.sin_port = htons(port);
17 | if (ip != nullptr) {
18 | ::inet_pton(AF_INET, ip, &mAddr.sin_addr);
19 | } else {
20 | mAddr.sin_addr.s_addr = htonl(INADDR_ANY);
21 | }
22 | fd = socket(AF_INET, SOCK_STREAM, 0);
23 | if (fd == -1) {
24 | std::cout << "creat socket error in file <" << __FILE__ << "> "
25 | << "at " << __LINE__ << std::endl;
26 | exit(0);
27 | }
28 | setReusePort(fd);
29 | }
30 |
31 | void nsocket::ServerSocket::bind() {
32 | int ret = ::bind(fd, (struct sockaddr *)&mAddr, sizeof(mAddr));
33 | if (ret == -1) {
34 | std::cout << "bind error in file <" << __FILE__ << "> "
35 | << "at " << __LINE__ << std::endl;
36 | exit(0);
37 | }
38 | }
39 |
40 | void nsocket::ServerSocket::listen() {
41 | int ret = ::listen(fd, 5);
42 | if (ret == -1) {
43 | std::cout << "listen error in file <" << __FILE__ << "> "
44 | << "at " << __LINE__ << std::endl;
45 | exit(0);
46 | }
47 | }
48 |
49 | int nsocket::ServerSocket::accept(ClientSocket &clientSocket) {
50 | int clientfd = ::accept(fd, (struct sockaddr *)&clientSocket.mAddr, &clientSocket.mLen);
51 | if (clientfd < 0) {
52 | std::cout << "accept error in file <" << __FILE__ << "> "
53 | << "at " << __LINE__ << std::endl;
54 | exit(0);
55 | }
56 | clientSocket.fd = clientfd;
57 | std::cout << "accept a client" << std::endl;
58 | return clientfd;
59 | }
60 |
61 | nsocket::ServerSocket::~ServerSocket() { ::close(fd); }
62 |
63 | nsocket::ClientSocket::~ClientSocket() { ::close(fd); }
64 |
--------------------------------------------------------------------------------
/version_0.1/src/utils.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #include "../include/utils.h"
7 |
8 | #include
9 |
10 | std::string& util::ltrim(std::string& str) {
11 | if (str.empty()) {
12 | return str;
13 | }
14 |
15 | str.erase(0, str.find_first_not_of(" \t"));
16 | return str;
17 | }
18 |
19 | std::string& util::rtrim(std::string& str) {
20 | if (str.empty()) {
21 | return str;
22 | }
23 | str.erase(str.find_last_not_of(" \t") + 1);
24 | return str;
25 | }
26 |
27 | std::string& util::trim(std::string& str) {
28 | if (str.empty()) {
29 | return str;
30 | }
31 |
32 | util::ltrim(str);
33 | util::rtrim(str);
34 | return str;
35 | }
36 |
--------------------------------------------------------------------------------
/version_0.1/test/test.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by marvinle on 2019/2/3 9:13 AM.
3 | //
4 |
5 | #include
6 |
7 | #include
8 | #include
9 |
10 | #include
11 | #include
12 |
13 | using namespace std;
14 |
15 | int main() {
16 | // string s = "/helo/world/index?word=12";
17 | // s.erase(s.rfind('?'));
18 | // string filetype;
19 | // if (s.rfind('.') != string::npos){
20 | // filetype = s.substr(s.rfind('.'));
21 | // }
22 | //
23 | //
24 | // cout << s << endl;
25 | // if (filetype != "") {
26 | // cout << filetype << endl;
27 | // } else {
28 | // cout << " no file type" << endl;
29 | // }
30 | char *buffer;
31 | //也可以将buffer作为输出参数
32 | if ((buffer = getcwd(NULL, 0)) == NULL) {
33 | perror("getcwd error");
34 | } else {
35 | printf("%s\n", buffer);
36 | free(buffer);
37 | }
38 |
39 | enum Code { ok = 201, notfound = 404 };
40 |
41 | Code code = ok;
42 | printf("%d\n", code);
43 | }
--------------------------------------------------------------------------------
/version_0.1/test/test_utils.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by marvinle on 2019/2/1 12:11 PM.
3 | //
4 |
5 | #include
6 | #include
7 |
8 | #include "../include/utils.h"
9 |
10 | using namespace std;
11 |
12 | void test_ltrim() {
13 | std::string s = " \thelloworld\t ";
14 | util::ltrim(s);
15 | if (s == "helloworld\t ") {
16 | cout << "ltrim ok" << endl;
17 | } else {
18 | cout << "test ltrim faild" << endl;
19 | }
20 | }
21 | void test_rtrim() {
22 | std::string s = " \thelloworld\t ";
23 | util::rtrim(s);
24 | if (s == " \thelloworld") {
25 | cout << "rtrim ok" << endl;
26 | } else {
27 | cout << "test rtrim faild" << endl;
28 | }
29 | }
30 |
31 | void test_trim() {
32 | std::string s = " 1";
33 | util::trim(s);
34 | if (s == "1") {
35 | cout << "trim ok" << endl;
36 | } else {
37 | cout << "test trim faild" << endl;
38 | }
39 | }
40 |
41 | int main() {
42 | test_ltrim();
43 | test_rtrim();
44 | test_trim();
45 | }
46 |
--------------------------------------------------------------------------------
/version_0.2/403.html:
--------------------------------------------------------------------------------
1 |
2 | 403 Forbidden
3 |
4 | 403 Forbidden
5 |
nginx/1.10.3 (Ubuntu)
6 |
7 |
--------------------------------------------------------------------------------
/version_0.2/404.html:
--------------------------------------------------------------------------------
1 |
2 | 404 Not Found
3 |
4 | 404 Not Found
5 |
nginx/1.10.3 (Ubuntu)
6 |
7 |
--------------------------------------------------------------------------------
/version_0.2/include/Condition.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #ifndef WEBSERVER_CONDITION_H
7 | #define WEBSERVER_CONDITION_H
8 |
9 | #include
10 |
11 | #include "MutexLock.h"
12 | #include "noncopyable.h"
13 |
14 | class Condition : public noncopyable {
15 | public:
16 | explicit Condition(MutexLock &mutex) : mutex_(mutex) { pthread_cond_init(&cond_, NULL); }
17 | ~Condition() { pthread_cond_destroy(&cond_); }
18 |
19 | void wait() { pthread_cond_wait(&cond_, mutex_.getMutex()); }
20 |
21 | void notify() { pthread_cond_signal(&cond_); }
22 |
23 | void notifyAll() { pthread_cond_broadcast(&cond_); }
24 |
25 | private:
26 | MutexLock &mutex_;
27 | pthread_cond_t cond_;
28 | };
29 |
30 | #endif // WEBSERVER_CONDITION_H
31 |
--------------------------------------------------------------------------------
/version_0.2/include/HttpResponse.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 | #ifndef WEBSERVER_HTTPRESPONSE_H
6 | #define WEBSERVER_HTTPRESPONSE_H
7 |
8 | #include
9 | #include
10 |
11 | #include "httpparse.h"
12 |
13 | #define BASEPATH
14 |
15 | namespace http {
16 |
17 | struct MimeType {
18 | MimeType(const std::string &str) : type(str){};
19 | MimeType(const char *str) : type(str){};
20 |
21 | std::string type;
22 | };
23 |
24 | extern std::unordered_map Mime_map;
25 |
26 | class HttpResponse {
27 | public:
28 | enum HttpStatusCode { Unknow, k200Ok = 200, k403forbiden = 403, k404NotFound = 404 };
29 |
30 | explicit HttpResponse(bool close)
31 | : mStatusCode(Unknow),
32 | mCloseConnection(close),
33 | mMime("text/html"),
34 | mBody(nullptr),
35 | mVersion(HttpRequest::HTTP_11) {}
36 |
37 | void setStatusCode(HttpStatusCode code) { mStatusCode = code; }
38 | void setBody(const char *buf) { mBody = buf; }
39 | void setContentLength(int len) { mContentLength = len; }
40 | void setVersion(const HttpRequest::HTTP_VERSION &version) { mVersion = version; }
41 |
42 | void setStatusMsg(const std::string &msg) { mStatusMsg = msg; }
43 | void setFilePath(const std::string &path) { mFilePath = path; }
44 | void setMime(const MimeType &mime) { mMime = mime; }
45 |
46 | void addHeader(const std::string &key, const std::string &value) { mHeaders[key] = value; }
47 | bool closeConnection() const { return mCloseConnection; }
48 | const HttpRequest::HTTP_VERSION version() const { return mVersion; }
49 | const std::string &filePath() const { return mFilePath; }
50 | HttpStatusCode statusCode() const { return mStatusCode; }
51 | const std::string &statusMsg() const { return mStatusMsg; }
52 |
53 | void appenBuffer(char *) const;
54 |
55 | ~HttpResponse() {
56 | if (mBody != nullptr) delete[] mBody;
57 | }
58 |
59 | private:
60 | HttpStatusCode mStatusCode;
61 | HttpRequest::HTTP_VERSION mVersion;
62 | std::string mStatusMsg;
63 | bool mCloseConnection;
64 | MimeType mMime;
65 | const char *mBody;
66 | int mContentLength;
67 | std::string mFilePath;
68 | std::unordered_map mHeaders;
69 | };
70 |
71 | } // namespace http
72 |
73 | #endif // WEBSERVER_HTTPRESPONSE_H
74 |
--------------------------------------------------------------------------------
/version_0.2/include/MutexLock.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #ifndef WEBSERVER_MUTEXLOCK_H
7 | #define WEBSERVER_MUTEXLOCK_H
8 | #include
9 |
10 | #include "noncopyable.h"
11 |
12 | class MutexLock : public noncopyable {
13 | public:
14 | MutexLock() { pthread_mutex_init(&mutex_, NULL); }
15 | ~MutexLock() { pthread_mutex_destroy(&mutex_); }
16 | void lock() { pthread_mutex_lock(&mutex_); }
17 | void unlock() { pthread_mutex_unlock(&mutex_); }
18 | pthread_mutex_t *getMutex() { return &mutex_; }
19 |
20 | private:
21 | pthread_mutex_t mutex_;
22 | };
23 |
24 | class MutexLockGuard : public noncopyable {
25 | public:
26 | explicit MutexLockGuard(MutexLock &mutex) : mutex_(mutex) { mutex_.lock(); }
27 |
28 | ~MutexLockGuard() { mutex_.unlock(); }
29 |
30 | private:
31 | MutexLock &mutex_;
32 | };
33 |
34 | #endif // WEBSERVER_MUTEXLOCK_H
35 |
--------------------------------------------------------------------------------
/version_0.2/include/ThreadPool.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #ifndef WEBSERVER_THREADPOLL_H
7 | #define WEBSERVER_THREADPOLL_H
8 |
9 | #include
10 |
11 | #include
12 | #include
13 | #include
14 |
15 | #include "Condition.h"
16 | #include "MutexLock.h"
17 | #include "noncopyable.h"
18 |
19 | namespace thread {
20 | const int MAX_THREAD_SIZE = 1024;
21 | const int MAX_QUEUE_SIZE = 10000;
22 |
23 | typedef enum { immediate_mode = 1, graceful_mode = 2 } ShutdownMode;
24 |
25 | struct ThreadTask {
26 | std::function process;
27 | void* arg;
28 | };
29 |
30 | class ThreadPool {
31 | public:
32 | ThreadPool(int thread_s, int max_queue_s);
33 | ~ThreadPool();
34 | bool append(thread::ThreadTask* request);
35 | void shutdown(bool graceful);
36 |
37 | private:
38 | static void* worker(void* args);
39 | void run();
40 |
41 | private:
42 | // 线程同步互斥, mutex_ 在 condition_前面
43 | MutexLock mutex_;
44 | Condition condition_;
45 |
46 | // 线程池属性
47 | int thread_size;
48 | int max_queue_size;
49 | int started;
50 | int shutdown_;
51 | std::vector threads;
52 | std::list request_queue;
53 | };
54 |
55 | } // namespace thread
56 |
57 | #endif // WEBSERVER_THREADPOLL_H
58 |
--------------------------------------------------------------------------------
/version_0.2/include/Util.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #ifndef WEBSERVER_UTILS_H
7 | #define WEBSERVER_UTILS_H
8 |
9 | #include
10 |
11 | namespace util {
12 | using namespace std;
13 | std::string& ltrim(string&);
14 | std::string& rtrim(string&);
15 | std::string& trim(string&);
16 | } // namespace util
17 |
18 | #endif // WEBSERVER_UTILS_H
19 |
--------------------------------------------------------------------------------
/version_0.2/include/httpparse.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 | #ifndef WEBSERVER_HTTPPARSE_H
6 | #define WEBSERVER_HTTPPARSE_H
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | #define CR '\r'
13 | #define LF '\n'
14 | #define LINE_END '\0'
15 | #define PASS
16 |
17 | namespace http {
18 |
19 | class HttpRequest;
20 |
21 | std::ostream &operator<<(std::ostream &, const HttpRequest &);
22 |
23 | class HttpRequestParser {
24 | public:
25 | enum LINE_STATE { LINE_OK = 0, LINE_BAD, LINE_MORE };
26 | enum PARSE_STATE { PARSE_REQUESTLINE = 0, PARSE_HEADER, PARSE_BODY };
27 | enum HTTP_CODE { NO_REQUEST, GET_REQUEST, BAD_REQUEST, FORBIDDEN_REQUEST, INTERNAL_ERROR, CLOSED_CONNECTION };
28 |
29 | static LINE_STATE parse_line(char *buffer, int &checked_index, int &read_index);
30 | static HTTP_CODE parse_requestline(char *line, PARSE_STATE &parse_state, HttpRequest &request);
31 | static HTTP_CODE parse_headers(char *line, PARSE_STATE &parse_state, HttpRequest &request);
32 | static HTTP_CODE parse_body(char *body, HttpRequest &request);
33 | static HTTP_CODE parse_content(char *buffer, int &check_index, int &read_index, PARSE_STATE &parse_state,
34 | int &start_line, HttpRequest &request);
35 | };
36 |
37 | struct HttpRequest {
38 | friend std::ostream &operator<<(std::ostream &, const HttpRequest &);
39 |
40 | enum HTTP_VERSION { HTTP_10 = 0, HTTP_11, VERSION_NOT_SUPPORT };
41 | enum HTTP_METHOD { GET = 0, POST, PUT, DELETE, METHOD_NOT_SUPPORT };
42 | enum HTTP_HEADER {
43 | Host = 0,
44 | User_Agent,
45 | Connection,
46 | Accept_Encoding,
47 | Accept_Language,
48 | Accept,
49 | Cache_Control,
50 | Upgrade_Insecure_Requests
51 | };
52 | struct EnumClassHash {
53 | template
54 | std::size_t operator()(T t) const {
55 | return static_cast(t);
56 | }
57 | };
58 |
59 | static std::unordered_map header_map;
60 |
61 | HttpRequest(std::string url = std::string(""), HTTP_METHOD method = METHOD_NOT_SUPPORT,
62 | HTTP_VERSION version = VERSION_NOT_SUPPORT)
63 | : mMethod(method),
64 | mVersion(version),
65 | mUri(url),
66 | mContent(nullptr),
67 | mHeaders(std::unordered_map()){};
68 |
69 | HTTP_METHOD mMethod;
70 | HTTP_VERSION mVersion;
71 | std::string mUri;
72 | char *mContent;
73 | std::unordered_map mHeaders;
74 | };
75 |
76 | } // namespace http
77 |
78 | #endif // WEBSERVER_HTTPPARSE_H
79 |
--------------------------------------------------------------------------------
/version_0.2/include/noncopyable.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 | #ifndef WEBSERVER_NONCOPYABLE_H
6 | #define WEBSERVER_NONCOPYABLE_H
7 |
8 | class noncopyable {
9 | public:
10 | noncopyable(const noncopyable&) = delete;
11 | noncopyable& operator=(const noncopyable&) = delete;
12 |
13 | protected:
14 | noncopyable() = default;
15 | ~noncopyable() = default;
16 | };
17 | #endif // WEBSERVER_NONCOPYABLE_H
18 |
--------------------------------------------------------------------------------
/version_0.2/include/server.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #ifndef WEBSERVER_SERVER_H
7 | #define WEBSERVER_SERVER_H
8 |
9 | #include "HttpResponse.h"
10 | #include "httpparse.h"
11 | #include "ssocket.h"
12 |
13 | #define BUFFERSIZE 1024
14 |
15 | namespace server {
16 |
17 | class HttpServer {
18 | public:
19 | explicit HttpServer(int port = 80, const char *ip = nullptr) : serverSocket(port, ip) {
20 | serverSocket.bind();
21 | serverSocket.listen();
22 | }
23 |
24 | void run();
25 | void do_request(void *args);
26 |
27 | private:
28 | void header(const http::HttpRequest &, http::HttpResponse &);
29 | void static_file(http::HttpResponse &, const char *);
30 | void send(const http::HttpResponse &, const nsocket::ClientSocket &);
31 | void getMime(const http::HttpRequest &, http::HttpResponse &);
32 |
33 | nsocket::ServerSocket serverSocket;
34 | };
35 | } // namespace server
36 |
37 | #endif // WEBSERVER_SERVER_H
38 |
--------------------------------------------------------------------------------
/version_0.2/include/ssocket.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #ifndef WEBSERVER_SOCKET_H
7 | #define WEBSERVER_SOCKET_H
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include
15 |
16 | namespace nsocket {
17 | class ClientSocket;
18 |
19 | void setReusePort(int fd);
20 |
21 | class ServerSocket {
22 | public:
23 | ServerSocket(int port = 8080, const char *ip = nullptr);
24 | ~ServerSocket();
25 | void bind();
26 |
27 | void listen();
28 |
29 | int accept(ClientSocket &);
30 |
31 | public:
32 | sockaddr_in mAddr;
33 | int fd;
34 | int mPort;
35 | const char *mIp;
36 | };
37 |
38 | class ClientSocket {
39 | public:
40 | ~ClientSocket();
41 |
42 | socklen_t mLen;
43 | sockaddr_in mAddr;
44 | int fd;
45 | };
46 | } // namespace nsocket
47 | #endif // WEBSERVER_SOCKET_H
48 |
--------------------------------------------------------------------------------
/version_0.2/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Welcome to LC!
5 |
12 |
13 |
14 | Welcome to LC !
15 | If you see this page, the lc webserver is successfully installed and
16 | working.
17 |
18 | For online documentation and support please refer to
19 | LC WebServer.
20 |
21 |
Thank you for using LC WebServer.
22 |
23 |
--------------------------------------------------------------------------------
/version_0.2/src/ThreadPool.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #include "../include/ThreadPool.h"
7 |
8 | #include
9 |
10 | #include
11 |
12 | using namespace thread;
13 |
14 | ThreadPool::ThreadPool(int thread_s, int max_queue_s)
15 | : max_queue_size(max_queue_s), thread_size(thread_s), condition_(mutex_), started(0), shutdown_(0) {
16 | if (thread_s <= 0 || thread_s > MAX_THREAD_SIZE) {
17 | thread_size = 4;
18 | }
19 |
20 | if (max_queue_s <= 0 || max_queue_s > MAX_QUEUE_SIZE) {
21 | max_queue_size = MAX_QUEUE_SIZE;
22 | }
23 | // 分配空间
24 | threads.resize(thread_size);
25 |
26 | for (int i = 0; i < thread_size; i++) {
27 | // 后期可扩展出单独的Thread类,只需要该类拥有run方法即可
28 |
29 | if (pthread_create(&threads[i], NULL, worker, this) != 0) {
30 | std::cout << "ThreadPool init error" << std::endl;
31 | throw std::exception();
32 | }
33 | started++;
34 | }
35 | }
36 |
37 | ThreadPool::~ThreadPool() {}
38 |
39 | bool ThreadPool::append(ThreadTask *request) {
40 | if (request == nullptr) return false;
41 |
42 | if (shutdown_) {
43 | std::cout << "ThreadPool has shutdown" << std::endl;
44 | return false;
45 | }
46 |
47 | MutexLockGuard guard(this->mutex_);
48 | if (request_queue.size() > max_queue_size) {
49 | std::cout << max_queue_size;
50 | std::cout << "ThreadPool too many requests" << std::endl;
51 | return false;
52 | }
53 | request_queue.push_back(request);
54 | if (request_queue.size() == 1) {
55 | condition_.notify();
56 | }
57 | return true;
58 | }
59 |
60 | void ThreadPool::shutdown(bool graceful) {
61 | {
62 | MutexLockGuard guard(this->mutex_);
63 | if (shutdown_) {
64 | std::cout << "has shutdown" << std::endl;
65 | }
66 | shutdown_ = graceful ? graceful_mode : immediate_mode;
67 | condition_.notifyAll();
68 | }
69 | for (int i = 0; i < thread_size; i++) {
70 | if (pthread_join(threads[i], NULL) != 0) {
71 | std::cout << "pthread_join error" << std::endl;
72 | }
73 | }
74 | }
75 |
76 | void *ThreadPool::worker(void *args) {
77 | ThreadPool *pool = static_cast(args);
78 | // 退出线程
79 | if (pool == nullptr) return NULL;
80 | // 执行线程主方法
81 | pool->run();
82 | return NULL;
83 | }
84 |
85 | void ThreadPool::run() {
86 | while (true) {
87 | ThreadTask *request = nullptr;
88 | {
89 | MutexLockGuard guard(this->mutex_);
90 | // 无任务 且未shutdown 则条件等待, 注意此处应使用while而非if
91 | while (request_queue.empty() && !shutdown_) {
92 | condition_.wait();
93 | }
94 |
95 | if ((shutdown_ == immediate_mode) || (shutdown_ == graceful_mode && request_queue.empty())) {
96 | break;
97 | }
98 | // FIFO
99 | request = request_queue.front();
100 | request_queue.pop_front();
101 | }
102 | if (request == nullptr) continue;
103 |
104 | request->process(request->arg);
105 | delete request;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/version_0.2/src/Util.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 | #include "../include/Util.h"
6 |
7 | #include
8 |
9 | std::string& util::ltrim(std::string& str) {
10 | if (str.empty()) {
11 | return str;
12 | }
13 |
14 | str.erase(0, str.find_first_not_of(" \t"));
15 | return str;
16 | }
17 |
18 | std::string& util::rtrim(std::string& str) {
19 | if (str.empty()) {
20 | return str;
21 | }
22 | str.erase(str.find_last_not_of(" \t") + 1);
23 | return str;
24 | }
25 |
26 | std::string& util::trim(std::string& str) {
27 | if (str.empty()) {
28 | return str;
29 | }
30 |
31 | util::ltrim(str);
32 | util::rtrim(str);
33 | return str;
34 | }
35 |
--------------------------------------------------------------------------------
/version_0.2/src/httpparse.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 | #include "../include/httpparse.h"
6 |
7 | #include
8 | #include
9 | #include
10 |
11 | #include "../include/Util.h"
12 |
13 | using namespace http;
14 |
15 | std::unordered_map http::HttpRequest::header_map = {
16 | {"HOST", http::HttpRequest::Host},
17 | {"USER-AGENT", http::HttpRequest::User_Agent},
18 | {"CONNECTION", http::HttpRequest::Connection},
19 | {"ACCEPT-ENCODING", http::HttpRequest::Accept_Encoding},
20 | {"ACCEPT-LANGUAGE", http::HttpRequest::Accept_Language},
21 | {"ACCEPT", http::HttpRequest::Accept},
22 | {"CACHE-CONTROL", http::HttpRequest::Cache_Control},
23 | {"UPGRADE-INSECURE-REQUESTS", http::HttpRequest::Upgrade_Insecure_Requests}};
24 |
25 | // 解析一行内容, buffer[checked_index, read_index)
26 | // check_index是需要分析的第一个字符, read_index已经读取数据末尾下一个字符
27 | HttpRequestParser::LINE_STATE HttpRequestParser::parse_line(char *buffer, int &checked_index, int &read_index) {
28 | char temp;
29 | for (; checked_index < read_index; checked_index++) {
30 | temp = buffer[checked_index];
31 | if (temp == CR) {
32 | // 到末尾,需要读入
33 | if (checked_index + 1 == read_index) return LINE_MORE;
34 | // 完整 "\r\n"
35 | if (buffer[checked_index + 1] == LF) {
36 | buffer[checked_index++] = LINE_END;
37 | buffer[checked_index++] = LINE_END;
38 | return LINE_OK;
39 | }
40 |
41 | return LINE_BAD;
42 | }
43 | }
44 | // 需要读入更多
45 | return LINE_MORE;
46 | }
47 |
48 | // 解析请求行
49 | HttpRequestParser::HTTP_CODE HttpRequestParser::parse_requestline(char *line, PARSE_STATE &parse_state,
50 | HttpRequest &request) {
51 | char *url = strpbrk(line, " \t");
52 | if (!url) {
53 | return BAD_REQUEST;
54 | }
55 |
56 | // 分割 method 和 url
57 | *url++ = '\0';
58 |
59 | char *method = line;
60 |
61 | if (strcasecmp(method, "GET") == 0) {
62 | request.mMethod = HttpRequest::GET;
63 | } else if (strcasecmp(method, "POST") == 0) {
64 | request.mMethod = HttpRequest::POST;
65 | } else if (strcasecmp(method, "PUT") == 0) {
66 | request.mMethod = HttpRequest::PUT;
67 | } else {
68 | return BAD_REQUEST;
69 | }
70 |
71 | url += strspn(url, " \t");
72 | char *version = strpbrk(url, " \t");
73 | if (!version) {
74 | return BAD_REQUEST;
75 | }
76 | *version++ = '\0';
77 | version += strspn(version, " \t");
78 |
79 | // HTTP/1.1 后面可能还存在空白字符
80 | if (strncasecmp("HTTP/1.1", version, 8) == 0) {
81 | request.mVersion = HttpRequest::HTTP_11;
82 | } else if (strncasecmp("HTTP/1.0", version, 8) == 0) {
83 | request.mVersion = HttpRequest::HTTP_10;
84 | } else {
85 | return BAD_REQUEST;
86 | }
87 |
88 | if (strncasecmp(url, "http://", 7) == 0) {
89 | url += 7;
90 | url = strchr(url, '/');
91 | } else if (strncasecmp(url, "/", 1) == 0) {
92 | PASS;
93 | } else {
94 | return BAD_REQUEST;
95 | }
96 |
97 | if (!url || *url != '/') {
98 | return BAD_REQUEST;
99 | }
100 | request.mUri = std::string(url);
101 | // 分析头部字段
102 | parse_state = PARSE_HEADER;
103 | return NO_REQUEST;
104 | }
105 |
106 | // 分析头部字段
107 | HttpRequestParser::HTTP_CODE HttpRequestParser::parse_headers(char *line, PARSE_STATE &parse_state,
108 | HttpRequest &request) {
109 | if (*line == '\0') {
110 | if (request.mMethod == HttpRequest::GET) {
111 | return GET_REQUEST;
112 | }
113 | parse_state = PARSE_BODY;
114 | return NO_REQUEST;
115 | }
116 |
117 | // char key[20]曾被缓冲区溢出
118 | char key[100], value[100];
119 |
120 | // 需要修改有些value里也包含了':'符号
121 | sscanf(line, "%[^:]:%[^:]", key, value);
122 |
123 | decltype(HttpRequest::header_map)::iterator it;
124 | std::string key_s(key);
125 | std::transform(key_s.begin(), key_s.end(), key_s.begin(), ::toupper);
126 | std::string value_s(value);
127 | if (key_s == std::string("UPGRADE-INSECURE-REQUESTS")) {
128 | return NO_REQUEST;
129 | }
130 |
131 | if ((it = HttpRequest::header_map.find(util::trim(key_s))) != (HttpRequest::header_map.end())) {
132 | request.mHeaders.insert(std::make_pair(it->second, util::trim(value_s)));
133 | } else {
134 | std::cout << "Header no support: " << key << " : " << value << std::endl;
135 | }
136 |
137 | return NO_REQUEST;
138 | }
139 |
140 | // 解析body
141 | HttpRequestParser::HTTP_CODE HttpRequestParser::parse_body(char *body, http::HttpRequest &request) {
142 | request.mContent = body;
143 | return GET_REQUEST;
144 | }
145 |
146 | // http 请求入口
147 | HttpRequestParser::HTTP_CODE HttpRequestParser::parse_content(char *buffer, int &check_index, int &read_index,
148 | http::HttpRequestParser::PARSE_STATE &parse_state,
149 | int &start_line, HttpRequest &request) {
150 | LINE_STATE line_state = LINE_OK;
151 | HTTP_CODE retcode = NO_REQUEST;
152 | while ((line_state = parse_line(buffer, check_index, read_index)) == LINE_OK) {
153 | char *temp = buffer + start_line; // 这一行在buffer中的起始位置
154 | start_line = check_index; // 下一行起始位置
155 |
156 | switch (parse_state) {
157 | case PARSE_REQUESTLINE: {
158 | retcode = parse_requestline(temp, parse_state, request);
159 | if (retcode == BAD_REQUEST) return BAD_REQUEST;
160 |
161 | break;
162 | }
163 |
164 | case PARSE_HEADER: {
165 | retcode = parse_headers(temp, parse_state, request);
166 | if (retcode == BAD_REQUEST) {
167 | return BAD_REQUEST;
168 | } else if (retcode == GET_REQUEST) {
169 | return GET_REQUEST;
170 | }
171 | break;
172 | }
173 |
174 | case PARSE_BODY: {
175 | retcode = parse_body(temp, request);
176 | if (retcode == GET_REQUEST) {
177 | return GET_REQUEST;
178 | }
179 | return BAD_REQUEST;
180 | }
181 | default:
182 | return INTERNAL_ERROR;
183 | }
184 | }
185 | if (line_state == LINE_MORE) {
186 | return NO_REQUEST;
187 | } else {
188 | return BAD_REQUEST;
189 | }
190 | }
191 |
192 | // 重载HttpRequest <<
193 | std::ostream &http::operator<<(std::ostream &os, const HttpRequest &request) {
194 | os << "method:" << request.mMethod << std::endl;
195 | os << "uri:" << request.mUri << std::endl;
196 | os << "version:" << request.mVersion << std::endl;
197 | // os << "content:" << request.mContent << std::endl;
198 | for (auto it = request.mHeaders.begin(); it != request.mHeaders.end(); it++) {
199 | os << it->first << ":" << it->second << std::endl;
200 | }
201 | return os;
202 | }
203 |
--------------------------------------------------------------------------------
/version_0.2/src/httpresponse.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #include "../include/HttpResponse.h"
7 |
8 | #include
9 |
10 | std::unordered_map http::Mime_map = {{".html", "text/html"},
11 | {".xml", "text/xml"},
12 | {".xhtml", "application/xhtml+xml"},
13 | {".txt", "text/plain"},
14 | {".rtf", "application/rtf"},
15 | {".pdf", "application/pdf"},
16 | {".word", "application/msword"},
17 | {".png", "image/png"},
18 | {".gif", "image/gif"},
19 | {".jpg", "image/jpeg"},
20 | {".jpeg", "image/jpeg"},
21 | {".au", "audio/basic"},
22 | {".mpeg", "video/mpeg"},
23 | {".mpg", "video/mpeg"},
24 | {".avi", "video/x-msvideo"},
25 | {".gz", "application/x-gzip"},
26 | {".tar", "application/x-tar"},
27 | {".css", "text/css"},
28 | {"", "text/plain"},
29 | {"default", "text/plain"}};
30 |
31 | void http::HttpResponse::appenBuffer(char *buffer) const {
32 | if (mVersion == HttpRequest::HTTP_11) {
33 | sprintf(buffer, "HTTP/1.1 %d %s\r\n", mStatusCode, mStatusMsg.c_str());
34 | } else {
35 | sprintf(buffer, "HTTP/1.0 %d %s\r\n", mStatusCode, mStatusMsg.c_str());
36 | }
37 |
38 | for (auto it = mHeaders.begin(); it != mHeaders.end(); it++) {
39 | sprintf(buffer, "%s%s: %s\r\n", buffer, it->first.c_str(), it->second.c_str());
40 | }
41 | sprintf(buffer, "%sContent-type: %s\r\n", buffer, mMime.type.c_str());
42 | if (mCloseConnection) {
43 | sprintf(buffer, "%sConnection: close\r\n", buffer);
44 | } else {
45 | sprintf(buffer, "%sConnection: keep-alive\r\n", buffer);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/version_0.2/src/main.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #include
7 |
8 | #include
9 | #include
10 |
11 | #include
12 | #include
13 |
14 | #include "../include/server.h"
15 |
16 | char basePath[300] = "/Users/lichunlin/CLionProjects/webserver/";
17 |
18 | int main(int argc, const char *argv[]) {
19 | //也可以将buffer作为输出参数
20 | if ((getcwd(basePath, 300)) == NULL) {
21 | perror("getcwd error");
22 | } else {
23 | printf("%s\n", basePath);
24 | }
25 |
26 | server::HttpServer httpServer(80);
27 | httpServer.run();
28 | }
29 |
--------------------------------------------------------------------------------
/version_0.2/src/server.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #include "../include/server.h"
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include
15 | #include
16 | #include
17 |
18 | #include "../include/HttpResponse.h"
19 | #include "../include/ThreadPool.h"
20 | #include "../include/httpparse.h"
21 |
22 | using namespace server;
23 | using namespace nsocket;
24 | using namespace http;
25 |
26 | extern char basePath[300];
27 |
28 | void HttpServer::run() {
29 | thread::ThreadPool threadPool(4, 1000);
30 | while (true) {
31 | ClientSocket *clientSocket = new ClientSocket;
32 | serverSocket.accept(*clientSocket);
33 | thread::ThreadTask *threadTask = new thread::ThreadTask;
34 | threadTask->process = std::bind(&HttpServer::do_request, this, std::placeholders::_1);
35 | threadTask->arg = static_cast(clientSocket);
36 | threadPool.append(threadTask);
37 | }
38 | }
39 |
40 | void HttpServer::do_request(void *arg) {
41 | ClientSocket clientSocket = *static_cast(arg);
42 |
43 | char buffer[BUFFERSIZE];
44 |
45 | bzero(buffer, BUFFERSIZE);
46 | int check_index = 0, read_index = 0, start_line = 0;
47 | ssize_t recv_data;
48 | http::HttpRequestParser::PARSE_STATE parse_state = http::HttpRequestParser::PARSE_REQUESTLINE;
49 |
50 | while (true) {
51 | http::HttpRequest request;
52 |
53 | recv_data = recv(clientSocket.fd, buffer + read_index, BUFFERSIZE - read_index, 0);
54 | if (recv_data == -1) {
55 | std::cout << "reading faild" << std::endl;
56 | return;
57 | }
58 | if (recv_data == 0) {
59 | std::cout << "connection closed by peer" << std::endl;
60 | break;
61 | }
62 | read_index += recv_data;
63 |
64 | http::HttpRequestParser::HTTP_CODE retcode =
65 | http::HttpRequestParser::parse_content(buffer, check_index, read_index, parse_state, start_line, request);
66 |
67 | if (retcode == http::HttpRequestParser::NO_REQUEST) {
68 | continue;
69 | }
70 | std::cout << request << std::endl;
71 |
72 | if (retcode == http::HttpRequestParser::GET_REQUEST) {
73 | HttpResponse response(true);
74 | header(request, response);
75 | getMime(request, response);
76 | static_file(response, basePath);
77 | send(response, clientSocket);
78 | } else {
79 | std::cout << "Bad Request" << std::endl;
80 | }
81 | }
82 | }
83 |
84 | void HttpServer::header(const HttpRequest &request, HttpResponse &response) {
85 | if (request.mVersion == HttpRequest::HTTP_11) {
86 | response.setVersion(HttpRequest::HTTP_11);
87 | } else {
88 | response.setVersion(HttpRequest::HTTP_10);
89 | }
90 | response.addHeader("Server", "LC WebServer");
91 | }
92 |
93 | // 获取Mime 同时设置path到response
94 | void HttpServer::getMime(const http::HttpRequest &request, http::HttpResponse &response) {
95 | std::string filepath = request.mUri;
96 | std::string mime;
97 | int pos;
98 | if ((pos = filepath.rfind('?')) != std::string::npos) {
99 | filepath.erase(filepath.rfind('?'));
100 | }
101 |
102 | if (filepath.rfind('.') != std::string::npos) {
103 | mime = filepath.substr(filepath.rfind('.'));
104 | }
105 | decltype(http::Mime_map)::iterator it;
106 |
107 | if ((it = http::Mime_map.find(mime)) != http::Mime_map.end()) {
108 | response.setMime(it->second);
109 | } else {
110 | response.setMime(http::Mime_map.find("default")->second);
111 | }
112 | response.setFilePath(filepath);
113 | }
114 |
115 | void HttpServer::static_file(HttpResponse &response, const char *basepath) {
116 | struct stat file_stat;
117 | char file[strlen(basepath) + strlen(response.filePath().c_str()) + 1];
118 | strcpy(file, basepath);
119 | strcat(file, response.filePath().c_str());
120 |
121 | if (stat(file, &file_stat) < 0) {
122 | response.setStatusCode(HttpResponse::k404NotFound);
123 | response.setStatusMsg("Not Found");
124 | response.setFilePath(std::string(basepath) + "/404.html");
125 | return;
126 | }
127 |
128 | if (!S_ISREG(file_stat.st_mode)) {
129 | response.setStatusCode(HttpResponse::k403forbiden);
130 | response.setStatusMsg("ForBidden");
131 | response.setFilePath(std::string(basepath) + "/403.html");
132 | return;
133 | }
134 |
135 | response.setStatusCode(HttpResponse::k200Ok);
136 | response.setStatusMsg("OK");
137 | response.setFilePath(file);
138 | return;
139 | }
140 |
141 | void HttpServer::send(const http::HttpResponse &response, const nsocket::ClientSocket &clientSocket) {
142 | char header[BUFFERSIZE];
143 | bzero(header, '\0');
144 | const char *internal_error = "Internal Error";
145 | struct stat file_stat;
146 | response.appenBuffer(header);
147 | if (stat(response.filePath().c_str(), &file_stat) < 0) {
148 | sprintf(header, "%sContent-length: %d\r\n\r\n", header, strlen(internal_error));
149 | sprintf(header, "%s%s", header, internal_error);
150 | ::send(clientSocket.fd, header, strlen(header), 0);
151 | return;
152 | }
153 |
154 | int filefd = ::open(response.filePath().c_str(), O_RDONLY);
155 | if (filefd < 0) {
156 | sprintf(header, "%sContent-length: %d\r\n\r\n", header, strlen(internal_error));
157 | sprintf(header, "%s%s", header, internal_error);
158 | ::send(clientSocket.fd, header, strlen(header), 0);
159 | return;
160 | }
161 |
162 | sprintf(header, "%sContent-length: %d\r\n\r\n", header, file_stat.st_size);
163 | ::send(clientSocket.fd, header, strlen(header), 0);
164 | void *mapbuf = mmap(NULL, file_stat.st_size, PROT_READ, MAP_PRIVATE, filefd, 0);
165 | ::send(clientSocket.fd, mapbuf, file_stat.st_size, 0);
166 | munmap(mapbuf, file_stat.st_size);
167 | close(filefd);
168 | return;
169 | err:
170 | sprintf(header, "%sContent-length: %d\r\n\r\n", header, strlen(internal_error));
171 | sprintf(header, "%s%s", header, internal_error);
172 | ::send(clientSocket.fd, header, strlen(header), 0);
173 | return;
174 | }
--------------------------------------------------------------------------------
/version_0.2/src/ssocket.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 | #include "../include/ssocket.h"
6 |
7 | void nsocket::setReusePort(int fd) {
8 | int opt = 1;
9 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt));
10 | }
11 |
12 | nsocket::ServerSocket::ServerSocket(int port, const char *ip) : mPort(port), mIp(ip) {
13 | bzero(&mAddr, sizeof(mAddr));
14 | mAddr.sin_family = AF_INET;
15 | mAddr.sin_port = htons(port);
16 | if (ip != nullptr) {
17 | ::inet_pton(AF_INET, ip, &mAddr.sin_addr);
18 | } else {
19 | mAddr.sin_addr.s_addr = htonl(INADDR_ANY);
20 | }
21 | fd = socket(AF_INET, SOCK_STREAM, 0);
22 | if (fd == -1) {
23 | std::cout << "creat socket error in file <" << __FILE__ << "> "
24 | << "at " << __LINE__ << std::endl;
25 | exit(0);
26 | }
27 | setReusePort(fd);
28 | }
29 |
30 | void nsocket::ServerSocket::bind() {
31 | int ret = ::bind(fd, (struct sockaddr *)&mAddr, sizeof(mAddr));
32 | if (ret == -1) {
33 | std::cout << "bind error in file <" << __FILE__ << "> "
34 | << "at " << __LINE__ << std::endl;
35 | exit(0);
36 | }
37 | }
38 |
39 | void nsocket::ServerSocket::listen() {
40 | int ret = ::listen(fd, 5);
41 | if (ret == -1) {
42 | std::cout << "listen error in file <" << __FILE__ << "> "
43 | << "at " << __LINE__ << std::endl;
44 | exit(0);
45 | }
46 | }
47 |
48 | int nsocket::ServerSocket::accept(ClientSocket &clientSocket) {
49 | int clientfd = ::accept(fd, (struct sockaddr *)&clientSocket.mAddr, &clientSocket.mLen);
50 | if (clientfd < 0) {
51 | std::cout << "accept error in file <" << __FILE__ << "> "
52 | << "at " << __LINE__ << std::endl;
53 | exit(0);
54 | }
55 | clientSocket.fd = clientfd;
56 | std::cout << "accept a client" << std::endl;
57 | return clientfd;
58 | }
59 |
60 | nsocket::ServerSocket::~ServerSocket() { ::close(fd); }
61 |
62 | nsocket::ClientSocket::~ClientSocket() { ::close(fd); }
63 |
--------------------------------------------------------------------------------
/version_0.2/test/test.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #include
7 |
8 | #include
9 | #include
10 |
11 | #include
12 | #include
13 |
14 | using namespace std;
15 |
16 | int main() {
17 | // string s = "/helo/world/index?word=12";
18 | // s.erase(s.rfind('?'));
19 | // string filetype;
20 | // if (s.rfind('.') != string::npos){
21 | // filetype = s.substr(s.rfind('.'));
22 | // }
23 | //
24 | //
25 | // cout << s << endl;
26 | // if (filetype != "") {
27 | // cout << filetype << endl;
28 | // } else {
29 | // cout << " no file type" << endl;
30 | // }
31 | char *buffer;
32 | //也可以将buffer作为输出参数
33 | if ((buffer = getcwd(NULL, 0)) == NULL) {
34 | perror("getcwd error");
35 | } else {
36 | printf("%s\n", buffer);
37 | free(buffer);
38 | }
39 |
40 | enum Code { ok = 201, notfound = 404 };
41 |
42 | Code code = ok;
43 | printf("%d\n", code);
44 | }
--------------------------------------------------------------------------------
/version_0.2/test/test_utils.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #include
7 | #include
8 |
9 | #include "../include/Util.h"
10 |
11 | using namespace std;
12 |
13 | void test_ltrim() {
14 | std::string s = " \thelloworld\t ";
15 | util::ltrim(s);
16 | if (s == "helloworld\t ") {
17 | cout << "ltrim ok" << endl;
18 | } else {
19 | cout << "test ltrim faild" << endl;
20 | }
21 | }
22 | void test_rtrim() {
23 | std::string s = " \thelloworld\t ";
24 | util::rtrim(s);
25 | if (s == " \thelloworld") {
26 | cout << "rtrim ok" << endl;
27 | } else {
28 | cout << "test rtrim faild" << endl;
29 | }
30 | }
31 |
32 | void test_trim() {
33 | std::string s = " 1";
34 | util::trim(s);
35 | if (s == "1") {
36 | cout << "trim ok" << endl;
37 | } else {
38 | cout << "test trim faild" << endl;
39 | }
40 | }
41 |
42 | int main() {
43 | test_ltrim();
44 | test_rtrim();
45 | test_trim();
46 | }
47 |
--------------------------------------------------------------------------------
/version_0.3/include/condition.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #pragma once
7 |
8 | #include
9 |
10 | #include "mutex_lock.h"
11 | #include "noncopyable.h"
12 |
13 | namespace csguide_webserver {
14 |
15 | class Condition : public Noncopyable {
16 | public:
17 | explicit Condition(MutexLock &mutex) : mutex_(mutex) { pthread_cond_init(&cond_, NULL); }
18 |
19 | ~Condition() { pthread_cond_destroy(&cond_); }
20 |
21 | void inline Wait() { pthread_cond_wait(&cond_, mutex_.GetMutex()); }
22 |
23 | void inline Notify() { pthread_cond_signal(&cond_); }
24 |
25 | void inline NotifyAll() { pthread_cond_broadcast(&cond_); }
26 |
27 | private:
28 | MutexLock &mutex_;
29 | pthread_cond_t cond_;
30 | };
31 | } // namespace csguide_webserver
--------------------------------------------------------------------------------
/version_0.3/include/epoll.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #pragma once
7 |
8 | #include
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | #include "http_data.h"
16 | #include "socket.h"
17 | #include "timer.h"
18 |
19 | namespace csguide_webserver {
20 |
21 | class Epoll {
22 |
23 | public:
24 | static int Init(int max_events);
25 |
26 | static int Addfd(int epoll_fd, int fd, __uint32_t events, std::shared_ptr http_data);
27 |
28 | static int Modfd(int epoll_fd, int fd, __uint32_t events, std::shared_ptr http_data);
29 |
30 | static int Delfd(int epoll_fd, int fd, __uint32_t events);
31 |
32 | static std::vector> Poll(const ServerSocket &server_socket, int max_event, int timeout);
33 |
34 | static void HandleConnection(const ServerSocket &server_socket);
35 |
36 | public:
37 | static std::unordered_map> http_data_map_;
38 | static const int MAX_EVENTS;
39 | static epoll_event *events_;
40 | static TimerManager timer_manager_;
41 | const static __uint32_t DEFAULT_EVENTS;
42 | };
43 | } // namespace csguide_webserver
--------------------------------------------------------------------------------
/version_0.3/include/http_data.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #pragma once
7 |
8 | #include
9 |
10 | #include "http_parse.h"
11 | #include "http_response.h"
12 | #include "socket.h"
13 | #include "timer.h"
14 |
15 | namespace csguide_webserver {
16 |
17 | class TimerNode;
18 |
19 | class HttpData : public std::enable_shared_from_this {
20 | public:
21 | HttpData() : epoll_fd(-1) {}
22 |
23 | public:
24 | std::shared_ptr request_;
25 | std::shared_ptr response_;
26 | std::shared_ptr client_socket_;
27 | int epoll_fd;
28 |
29 | public:
30 |
31 | void CloseTimer();
32 |
33 | void SetTimer(std::shared_ptr);
34 |
35 | private:
36 | std::weak_ptr timer_;
37 | };
38 |
39 | } // namespace csguide_webserver
--------------------------------------------------------------------------------
/version_0.3/include/http_parse.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #pragma once
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | namespace csguide_webserver {
13 |
14 | #define CR '\r'
15 | #define LF '\n'
16 | #define LINE_END '\0'
17 | #define PASS
18 |
19 | class HttpRequest;
20 |
21 | std::ostream &operator<<(std::ostream &, const HttpRequest &);
22 |
23 | class HttpRequestParser {
24 | public:
25 | enum LINE_STATE { LINE_OK = 0, LINE_BAD, LINE_MORE };
26 | enum PARSE_STATE { PARSE_REQUESTLINE = 0, PARSE_HEADER, PARSE_BODY };
27 | enum HTTP_CODE { NO_REQUEST, GET_REQUEST, BAD_REQUEST, FORBIDDEN_REQUEST, INTERNAL_ERROR, CLOSED_CONNECTION };
28 |
29 | static LINE_STATE ParseLine(char *buffer, int &checked_index, int &read_index);
30 |
31 | static HTTP_CODE ParseRequestline(char *line, PARSE_STATE &parse_state, HttpRequest &request);
32 |
33 | static HTTP_CODE ParseHeaders(char *line, PARSE_STATE &parse_state, HttpRequest &request);
34 |
35 | static HTTP_CODE ParseBody(char *body, HttpRequest &request);
36 |
37 | static HTTP_CODE ParseContent(char *buffer, int &check_index, int &read_index, PARSE_STATE &parse_state,
38 | int &start_line, HttpRequest &request);
39 | };
40 | } // namespace csguide_webserver
41 |
--------------------------------------------------------------------------------
/version_0.3/include/http_request.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #pragma once
7 |
8 | #include
9 | #include
10 |
11 | namespace csguide_webserver {
12 |
13 | class HttpRequest;
14 |
15 | std::ostream &operator<<(std::ostream &, const HttpRequest &);
16 |
17 | struct HttpRequest {
18 | friend std::ostream &operator<<(std::ostream &, const HttpRequest &);
19 |
20 | enum HTTP_VERSION { HTTP_10 = 0, HTTP_11, VERSION_NOT_SUPPORT };
21 | enum HTTP_METHOD { GET = 0, POST, PUT, DELETE, METHOD_NOT_SUPPORT };
22 | enum HTTP_HEADER {
23 | Host = 0,
24 | User_Agent,
25 | Connection,
26 | Accept_Encoding,
27 | Accept_Language,
28 | Accept,
29 | Cache_Control,
30 | Upgrade_Insecure_Requests
31 | };
32 |
33 | struct EnumClassHash {
34 | template
35 | std::size_t operator()(T t) const {
36 | return static_cast(t);
37 | }
38 | };
39 |
40 | static std::unordered_map header_map;
41 |
42 | HttpRequest(std::string url = std::string(""), HTTP_METHOD method = METHOD_NOT_SUPPORT,
43 | HTTP_VERSION version = VERSION_NOT_SUPPORT)
44 | : mMethod(method),
45 | mVersion(version),
46 | mUri(url),
47 | mContent(nullptr),
48 | mHeaders(std::unordered_map()){};
49 |
50 | HTTP_METHOD mMethod;
51 | HTTP_VERSION mVersion;
52 | std::string mUri;
53 | char *mContent;
54 | std::unordered_map mHeaders;
55 | };
56 |
57 | } // namespace csguide_webserver
58 |
--------------------------------------------------------------------------------
/version_0.3/include/http_response.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | */
5 |
6 | #pragma once
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | #include "http_request.h"
13 |
14 | namespace csguide_webserver {
15 |
16 | struct MimeType {
17 | MimeType(const std::string &str) : type(str){};
18 |
19 | MimeType(const char *str) : type(str){};
20 |
21 | std::string type;
22 | };
23 |
24 | extern std::unordered_map MimeMap;
25 |
26 | class HttpResponse {
27 | public:
28 | enum HttpStatusCode { Unknow, k200Ok = 200, k403forbiden = 403, k404NotFound = 404 };
29 |
30 | explicit HttpResponse(bool mkeep = true)
31 | : status_code_(Unknow), keep_alive_(mkeep), mime_("text/html"), body_buufer(nullptr), version_(HttpRequest::HTTP_11) {}
32 |
33 | void SetStatusCode(HttpStatusCode code) { status_code_ = code; }
34 |
35 | void SetBody(const char *buf) { body_buufer = buf; }
36 |
37 | void SetContentLength(int len) { content_length_ = len; }
38 |
39 | void SetVersion(const HttpRequest::HTTP_VERSION &version) { version_ = version; }
40 |
41 | void SetStatusMsg(const std::string &msg) { status_msg_ = msg; }
42 |
43 | void SetFilePath(const std::string &path) { file_path_ = path; }
44 |
45 | void SetMime(const MimeType &mime) { mime_ = mime; }
46 |
47 | void SetKeepAlive(bool isalive) { keep_alive_ = isalive; }
48 |
49 | void AddHeader(const std::string &key, const std::string &value) { headers_[key] = value; }
50 |
51 | bool KeepAlive() const { return keep_alive_; }
52 |
53 | const HttpRequest::HTTP_VERSION Version() const { return version_; }
54 |
55 | const std::string &FilePath() const { return file_path_; }
56 |
57 | HttpStatusCode StatusCode() const { return status_code_; }
58 |
59 | const std::string &StatusMsg() const { return status_msg_; }
60 |
61 | void AppenBuffer(char *) const;
62 |
63 | ~HttpResponse() {
64 | if (body_buufer != nullptr) delete[] body_buufer;
65 | }
66 |
67 | private:
68 | HttpStatusCode status_code_;
69 | HttpRequest::HTTP_VERSION version_;
70 | std::string status_msg_;
71 | bool keep_alive_;
72 | MimeType mime_;
73 | const char *body_buufer;
74 | int content_length_;
75 | std::string file_path_;
76 | std::unordered_map headers_;
77 | };
78 |
79 | } // namespace csguide_webserver
80 |
--------------------------------------------------------------------------------
/version_0.3/include/ini_file.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | * Brief: INI 格式的配置文件解析器
5 | */
6 |
7 | #pragma once
8 |
9 | #include
10 |
11 | #include "ini_section.h"
12 |
13 | namespace csguide_webserver {
14 |
15 | class INIFile {
16 | public:
17 | bool Load(const std::string& file_path);
18 | bool Save(const std::string& file_path) const;
19 |
20 | const INISection* GetSection(const std::string& name) const;
21 | void AddSection(const INISection& section);
22 |
23 | private:
24 | std::vector sections_;
25 | };
26 | } // namespace csguide_webserver
--------------------------------------------------------------------------------
/version_0.3/include/ini_section.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 CSGuide(https://csguide.cn)
3 | * Author: xiaobei (https://github.com/imarvinle)
4 | * Brief: INI 格式的配置文件解析器
5 | */
6 |
7 | #pragma once
8 |
9 | #include
10 | #include