-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathhttp_server.c
More file actions
172 lines (158 loc) · 4.21 KB
/
Copy pathhttp_server.c
File metadata and controls
172 lines (158 loc) · 4.21 KB
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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libdill.h>
#include <assert.h>
#include "http_server.h"
#include "http_response.h"
#define CONTENT_LENGTH "Content-Length"
typedef struct {
char c;
int peeked;
int s;
} parser_t;
char parser_next_char(parser_t* parser) {
if (parser->peeked) {
parser->peeked = 0;
return parser->c;
} else {
char c;
brecv(parser->s, &c, 1, now() + 60000);
return c;
}
}
char parser_peek(parser_t* parser) {
brecv(parser->s, &parser->c, 1, -1);
parser->peeked = 1;
return parser->c;
}
void read_to_crlf(parser_t* parser, char** buf, int offset, int* size) {
char c;
int i;
errno = 0;
char* b = *buf + offset;
for (i = 0; (c = parser_next_char(parser)) != '\r' && c != '\n'; i++) {
if (errno != 0) return;
if (offset + i == *size) {
*buf = realloc(*buf, *size *= 2);
b = *buf + offset;
}
b[i] = c;
}
b[i] = '\0';
if (c == '\r' && parser_peek(parser) == '\n') {
parser_next_char(parser);
}
}
void parse_request_line(parser_t* parser, http_request_t* request) {
int rl_size = 128;
request->request_line = malloc(rl_size);
read_to_crlf(parser, &request->request_line, 0, &rl_size);
if (errno != 0) {
free(request->request_line);
}
}
#define eat_white_space() \
do { \
while ((c = parser_next_char(parser)) == ' ' || c == '\t'); \
} while(0)
#define store_char(buf, size) \
if (i == size) { \
buf = realloc(buf, size *= 2); \
} \
buf[i++] = c
#define eat_separator() \
do { \
if (c == ':') { \
eat_white_space(); \
} else if (c == ' ' || c == '\t') { \
eat_white_space(); \
if (c == ':') { \
eat_white_space(); \
} \
} \
} while (0)
void parse_header(parser_t* parser, http_request_t* request) {
int i;
char c;
http_header_t* h = http_request_alloc_header(request);
for (i = 0; (c = parser_next_char(parser)) == CONTENT_LENGTH[i]; ) {
store_char(h->key, h->ksize);
}
//If header is Content-Length
if (i == (int)sizeof(CONTENT_LENGTH)-1) {
h->key[i] = '\0';
eat_separator();
i = 0;
//Get body length from Content-Length header
do {
store_char(h->value, h->vsize);
request->body_length *= 10;
request->body_length += c - '0';
} while ((c = parser_next_char(parser)) >= '0' && c <= '9');
store_char(h->value, h->vsize);
h->value[i-1] = '\0';
if (c != '\r' && c != '\n') {
eat_white_space();
}
if (c == '\r' && parser_peek(parser) == '\n') {
parser_next_char(parser);
}
//If not Content-Length just read in line
} else {
do {
store_char(h->key, h->ksize);
} while ((c = parser_next_char(parser)) != ' ' && c != '\t' && c != ':');
h->key[i] = '\0';
eat_separator();
i = 0;
store_char(h->value, h->vsize);
read_to_crlf(parser, &h->value, 1, &h->vsize);
}
}
coroutine void http_session(http_server_t* server, int s) {
char c;
http_request_t request;
parser_t parser;
parser.s = s;
while (1) {
parse_request_line(&parser, &request);
if (errno != 0) return; //Usually because the other end closed the socket
while ((c = parser_peek(&parser)) != '\r' && c != '\n') {
parse_header(&parser, &request);
}
if (c == '\r' && parser_peek(&parser) == '\n') {
parser_next_char(&parser);
}
//Read in the body of the request
if (request.body_length > 0) {
request.body = malloc(request.body_length + 1);
brecv(s, request.body, request.body_length, -1);
request.body[request.body_length] = '\0';
}
http_response_t response;
http_response_init(&response, s);
//Call the application callback
server->request_cb(&request, &response);
http_request_free(&request);
}
}
int http_server_listen(http_server_t* server, void (*f)(http_request_t*, http_response_t*), int port) {
server->request_cb = f;
struct ipaddr addr;
int rc = ipaddr_local(&addr, NULL, port, 0);
assert(rc == 0);
int ls = tcp_listen(&addr, 10);
if (ls < 0) {
perror("Can't open listening socket");
return 1;
}
while (1) {
int s = tcp_accept(ls, NULL, -1);
if (s <= 0) {
continue;
}
//Spin off coroutine for new http session
go(http_session(server, s));
}
}