-
Notifications
You must be signed in to change notification settings - Fork 0
/
http-server.h
223 lines (190 loc) · 7.21 KB
/
http-server.h
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#ifndef _HTTP_SERVER_H_
#define _HTTP_SERVER_H_
///debug levels///
#define DEBUG_NONE 0x00 // No debugging. Error messages will still be displayed.
#define DEBUG_ERROR 0x00 // Only display error messages.
#define DEBUG_WARNING 0x01 // Display warning and error messages.
#define DEBUG_INFO 0x02 // Display all messages.
///connection opt defines///
#define CXN_CGI 0x02
#define CXN_FORTUNE 0x04
///return codes///
#define SIZE_CHANGE -1
#define STRUCT_NOT_FOUND -2
#define FDARR_MODIFIED -3
#define BAD_SOCKET -4
#define POSIX_ERROR -5
#define INTERNAL_RESPONSE -6
#define HTTP_CGI -7
#define HTTP_FORTUNE -8
///all other defines///
#define SERVER_PORT 5050
#define BUF_LEN 4096
#define NAME_BUF_LEN 64
#define ENV_BUF_LEN 64
#define FORTUNE_BUF_INIT_LEN 256
#define AVG_LISTING_LEN 100
#define MAX_TOKS 100
#define FDARR_INIT_LEN 100
#define NUM_ENV_VARS 8
#define NUM_DEBUG_LEVELS 3
#define NUM_HTTP_REQUEST_TYPES 4
#define NUM_HTTP_RESPONSE_TYPES 6
#define NUM_REQUEST_STATES 4
#define NUM_REQ_DECL_PARTS 3
#define DEBUG_SET 0xAAAAAAAA
#define USEC_PER_SEC 100000.0
///http error messages//
#define ERROR_BODY "<html><head><title>%s</title></head><body><h1>%s" \
"</h1>%s</body></html>\n"
#define BODY_LISTING_BEGIN "<HTML>\n<HEAD>\n<TITLE>Directory Listing" \
"</TITLE>\n</HEAD>\n<BODY>\n<H2>Directory Listing</H2><BR>\n<UL>\n"
#define BODY_LISTING_END "</UL>\n</BODY>\n</HTML>\n"
#define BODY_STATUS_BEGIN "<HTML>\n<HEAD>\n<TITLE>Server Status</TITLE>\n" \
"</HEAD>\n<BODY>\nAuthor: Jacob Hladky<BR>\n"
#define BODY_STATUS_END "<BR>\n<FORM METHOD=\"GET\" ACTION=\"quit\">\n<INPUT " \
"TYPE=\"submit\" VALUE=\"Quit Server\"/>\n<INPUT TYPE=\"HIDDEN\" NAME=\"" \
"confirm\" VALUE=\"1\"/>\n</FORM>\n</BODY>\n</HTML>\n"
///json messages///
#define BODY_FORTUNE_BEGIN "{\n\"fortune\": \""
#define BODY_FORTUNE_END "\"\n}"
#define BODY_JSON_ABOUT "{\n\"author\":\"Jacob Hladky\", " \
"\"email\":\"[email protected]\", \"major\": \"CPE\"}"
#define BODY_JSON_IMPLEMENTED "[\n{ \"feature\": \"about\", " \
"\"URL\": \"/json/about.json\"},{ \"feature\": \"quit\", " \
"\"URL\": \"/json/quit\"},{ \"feature\": \"status\", " \
"\"URL\": \"/json/status.json\"},{ \"feature\": \"fortune\", " \
"\"URL\": \"/json/fortune\"}]"
///structs, unions, and enums///
enum CGI_CMD {
CGI_STATUS,
CGI_DO
};
enum JSON_CMD {
JSON_ABOUT,
JSON_IMPLEMENTED,
JSON_FORTUNE
};
enum FDARR_CMD {
FDARR_GET,
FDARR_ADD,
FDARR_MODIFY,
FDARR_REMOVE,
FDARR_CLEAN_UP
};
enum CXN_STATE {
ST_REQUEST,
ST_INTERNAL,
ST_CGI_FIL,
ST_RESPONSE_READ,
ST_RESPONSE_WRIT
};
enum REQUEST_STATE {
ST_ZERO_READ,
ST_ERROR,
ST_INCOMPLETE,
ST_FINISHED
};
enum REQUEST_TYPE {
REQUEST_GET,
REQUEST_PUT,
REQUEST_POST,
REQUEST_DELETE
};
enum RESPONSE_STATE {
// Fill me in!
};
enum RESPONSE_TYPE {
RESPONSE_OK, // 200
RESPONSE_BAD_REQUEST, // 400
RESPONSE_METHOD_NOT_ALLOWED, // 405
RESPONSE_FORBIDDEN, // 403
RESPONSE_NOT_FOUND, // 404
RESPONSE_INTERNAL_ERROR // 500
};
struct buf {
char data[BUF_LEN];
unsigned short bytesUsed;
unsigned short bytesToWrite;
};
struct request {
char buffer[BUF_LEN];
unsigned int bytesUsed;
enum REQUEST_STATE state;
enum REQUEST_TYPE type; //this and the two below come from the request declaration
int httpVersion; //-1 for HTTP/0.9, 0 for HTTP/1.0, 1 for HTTP/1.1
char filepath[NAME_BUF_LEN]; //we have our own buffer for this so we don't mess up the main buffer 64B
const char* referer; //these point to locations in buffer
const char* userAgent;
bool noKeepAlive;
bool acceptDeflate;
};
struct response {
struct buf buffer;
enum RESPONSE_TYPE type;
int contentLength;
unsigned int headerLength;
const char* contentType; //get this from the file ext
bool eofFound;
bool noKeepAlive;
bool usingDeflate;
};
struct env {
const char* method;
char* filepath;
char* query;
char* envvars[NUM_ENV_VARS + 1]; //add 1 for the NULL terminator
int childPid;
int mySocket; //a copy of the connection socket... rename to cxnSocket
int serverSocket; //a copy of the server's socket
};
struct connection {
enum CXN_STATE state; // the current state of the connection
int socket; // fd of our socket
int file; // fd of our file
struct request request; //
struct response response; //
struct env env; //
int opts; // various connection flags
};
///function prototypes///
static int cgi_response(struct response* response, enum CGI_CMD cmd);
static int json_response(struct response* response, enum JSON_CMD cmd);
static int error_response(struct response* response, enum RESPONSE_TYPE type);
static int generate_listing(char* filepath, struct response* response);
static int fdarr_cntl(enum FDARR_CMD cmd, ...);
static int do_cgi(struct connection* cxn);
static int do_fortune(struct connection* cxn);
static int fortune_decode(struct buf* buf);
static int process_request(struct connection* cxn);
static int cgi_request(struct connection* cxn);
static int json_request(struct connection* cxn);
static int file_request(struct connection* cxn);
static int process_mysock_events(int socket, short revents);
static int process_cxfile_events(struct connection* cxn, short revents);
static int process_cxsock_events(struct connection* cxn, short revents);
static int url_decode(char* url);
static void make_response_header(struct response* response);
static void print_request(struct request* request);
static int finish_response(struct connection* cxn, bool keepAlive);
static void clean_exit(int unused);
static void add_handler(int signal, void (*handlerFunc)(int));
static void log_access(struct connection* cxn);
static void debug(uint32_t debug, const char* msg, ...);
static char* request_declaration_to_string(const struct request* request);
static enum RESPONSE_TYPE parse_cgi_request(struct env* env, enum CGI_CMD* cmd);
static enum RESPONSE_TYPE make_request_header(struct request* request);
static enum RESPONSE_TYPE parse_request_declaration(struct request* request, char** filepath);
static enum REQUEST_STATE fill_request_buffer(int socket, char* buffer, unsigned int* bytesUsed);
static inline const char* bool_to_string(bool b);
static inline void set_debug_level(uintptr_t debugLevel);
static inline void pexit(const char* str);
///request state prototypes///
static int request_state_zero_read(struct connection* cxn);
static int request_state_error(struct connection* cxn);
static int request_state_incomplete(struct connection* cxn);
static int request_state_finished(struct connection* cxn);
///c++ typedefs///
typedef std::unordered_map<std::string, std::string> ext_x_mime_t;
typedef std::unordered_map<int, struct connection *> connections_t;
#endif