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
145
146
147
|
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include "file.h"
#include "http.h"
#include "net.h"
#define CFWS_DEFAULT_PORT 8080
static int serverfd;
int flag_verbose = 0;
static void handle_request(const struct http_request *, int);
static void handle_sigint(int signum)
{
(void)signum;
shutdown(serverfd, SHUT_RDWR);
}
static void print_usage(const char *program_name)
{
printf("USAGE: %s [OPTION]\n", program_name);
printf("\t-h\tPrint this help and exit\n\t-v\tPrint detailed information for each request and response\n");
}
int main(int argc, char *argv[])
{
int port = CFWS_DEFAULT_PORT;
int clientfd;
struct http_request request;
int optchar;
while ((optchar = getopt(argc, argv, "hv")) != -1) {
switch (optchar) {
case 'h':
print_usage(argv[0]);
exit(EXIT_SUCCESS);
case 'v':
flag_verbose = 1;
break;
default:
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
}
signal(SIGINT, handle_sigint);
/* Prevent the program from quitting if it attempts to write to a closed
* socket. */
signal(SIGPIPE, SIG_IGN);
serverfd = net_init_server(port);
if (serverfd == -1)
return 1;
if (flag_verbose)
printf("Serving a directory at localhost:%d\n", port);
while (1) {
int rc = net_next_request(serverfd, &clientfd, &request);
if (rc != 0)
break;
handle_request(&request, clientfd);
http_free_request(&request);
close(clientfd);
}
if (flag_verbose)
printf("Shutting down server...\n");
close(serverfd);
return 0;
}
static void handle_request(const struct http_request *req, int sockfd)
{
char *filepath = NULL;
enum http_res_code res_code;
enum serve_method method;
if (flag_verbose) {
switch (req->method) {
case HTTP_METHOD_GET:
printf("GET :: %s\n", req->uri);
break;
case HTTP_METHOD_POST:
printf("POST :: %s\n", req->uri);
break;
default:
printf("??? :: %s\n", req->uri);
break;
}
}
if (req->method == HTTP_METHOD_UNKNOWN) {
method = SERVE_METHOD_ERROR;
res_code = HTTP_RESPONSE_NOTIMPLEMENTED;
} else if (req->uri == NULL) {
method = SERVE_METHOD_ERROR;
res_code = HTTP_RESPONSE_BADREQUEST;
} else {
/* Find the local path for the resource and decide how to serve it. */
filepath = file_path_for_uri(req->uri);
method = file_method_for_path(filepath, &res_code);
}
/* Write the status line and (TODO) extra headers */
http_response_statusline(res_code, sockfd);
/* Use the chosen method to fill in the rest of the response */
switch (method) {
case SERVE_METHOD_FILE:
if (flag_verbose)
printf(" -> FILE %s\n", filepath);
file_read(filepath, sockfd);
break;
case SERVE_METHOD_PHP:
if (flag_verbose)
printf(" -> PHP %s\n", filepath);
file_read_cgi(filepath, "/usr/bin/php-cgi", req, sockfd);
break;
case SERVE_METHOD_CGI:
if (flag_verbose)
printf(" -> CGI %s\n", filepath);
file_read_cgi(filepath, filepath, req, sockfd);
break;
case SERVE_METHOD_ERROR: {
if (flag_verbose)
printf(" -> ERROR %d\n", res_code);
const char *errmsg = "Content-Type: text/plain\r\n\r\nEpic fail";
write(sockfd, errmsg, strlen(errmsg));
break;
}
}
if (filepath)
free(filepath);
}
|