Subversion Repositories Tewi

Rev

Rev 17 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
16 nishi 1
/* $Id: http.c 16 2024-09-13 15:09:52Z nishi $ */
2
 
3
#define SOURCE
4
 
5
#include "tw_http.h"
6
 
7
#include "tw_server.h"
8
 
9
#include <cm_log.h>
10
#include <cm_string.h>
11
 
12
#include <stdbool.h>
13
#include <stdlib.h>
14
#include <sys/socket.h>
15
 
16
void tw_free_request(struct tw_http_request* req) {
17
	if(req->method != NULL) free(req->method);
18
	if(req->path != NULL) free(req->path);
19
	if(req->headers != NULL) {
20
		int i;
21
		for(i = 0; req->headers[i] != NULL; i++) free(req->headers[i]);
22
		free(req->headers);
23
	}
24
	if(req->body != NULL) free(req->body);
25
	if(req->version != NULL) free(req->version);
26
}
27
 
28
int tw_http_parse(SSL* ssl, int sock, struct tw_http_request* req) {
29
	char buffer[512];
30
	char cbuf[2];
31
	int phase = 0;
32
	fd_set fds;
33
 
34
	bool bad = false;
35
 
36
	cbuf[1] = 0;
37
 
38
	req->method = NULL;
39
	req->path = NULL;
40
	req->headers = NULL;
41
	req->body = NULL;
42
	req->version = NULL;
43
 
44
	char* header = malloc(1);
45
	header[0] = 0;
46
	int nl = 0;
47
 
48
	while(1) {
49
		FD_ZERO(&fds);
50
		FD_SET(sock, &fds);
51
		struct timeval tv;
52
		tv.tv_sec = 5;
53
		tv.tv_usec = 0;
54
		int n = select(FD_SETSIZE, &fds, NULL, NULL, &tv);
55
		if(n == 0) break;
56
		int len = tw_read(ssl, sock, buffer, 512);
57
		if(len <= 0) break;
58
		int i;
59
		for(i = 0; i < len; i++) {
60
			char c = buffer[i];
61
			if(phase == 0) {
62
				if(c == ' ') {
63
					if(req->method == NULL) {
64
						tw_free_request(req);
65
						bad = true;
66
						goto getout;
67
					} else {
68
						phase++;
69
					}
70
				} else {
71
					if(req->method == NULL) {
72
						req->method = malloc(1);
73
						req->method[0] = 0;
74
					}
75
					cbuf[0] = c;
76
					char* tmp = req->method;
77
					req->method = cm_strcat(tmp, cbuf);
78
					free(tmp);
79
				}
80
			} else if(phase == 1) {
81
				if(c == ' ') {
82
					if(req->path == NULL) {
83
						tw_free_request(req);
84
						bad = true;
85
						goto getout;
86
					} else {
87
						phase++;
88
					}
89
				} else {
90
					if(req->path == NULL) {
91
						req->path = malloc(1);
92
						req->path[0] = 0;
93
					}
94
					cbuf[0] = c;
95
					char* tmp = req->path;
96
					req->path = cm_strcat(tmp, cbuf);
97
					free(tmp);
98
				}
99
			} else if(phase == 2) {
100
				if(c == '\n') {
101
					if(req->version == NULL) {
102
						tw_free_request(req);
103
						bad = true;
104
						goto getout;
105
					} else {
106
						/* We have Method, Path, Version now */
107
 
108
						if(strcmp(req->version, "HTTP/1.1") != 0 && strcmp(req->version, "HTTP/1.0") != 0) {
109
							cm_log("HTTP", "Bad HTTP Version");
110
							bad = true;
111
							goto getout;
112
						}
113
 
114
						int j;
115
						char* p = malloc(1);
116
						p[0] = 0;
117
						for(j = 0; req->path[j] != 0; j++) {
118
							if(req->path[j] == '/') {
119
								cbuf[0] = '/';
120
								for(; req->path[j] != 0 && req->path[j] == '/'; j++)
121
									;
122
								j--;
123
							} else {
124
								cbuf[0] = req->path[j];
125
							}
126
							char* tmp = p;
127
							p = cm_strcat(tmp, cbuf);
128
							free(tmp);
129
						}
130
						free(req->path);
131
						req->path = p;
132
 
133
						int incr = 0;
134
						p = malloc(1);
135
						p[0] = 0;
136
						for(j = 0;; j++) {
137
							if(req->path[j] == '/' || req->path[j] == 0) {
138
								char oldc = req->path[j];
139
								cbuf[0] = oldc;
140
								req->path[j] = 0;
141
 
142
								char* pth = req->path + incr;
143
 
144
								if(strcmp(pth, "..") == 0) {
145
									int k;
146
									if(p[strlen(p) - 1] == '/') p[strlen(p) - 1] = 0;
147
									for(k = strlen(p) - 1; k >= 0; k--) {
148
										if(p[k] == '/') {
149
											p[k + 1] = 0;
150
											break;
151
										}
152
									}
153
									if(strlen(p) == 0) {
154
										free(p);
155
										p = cm_strdup("/");
156
									}
157
								} else if(strcmp(pth, ".") == 0) {
158
								} else {
159
									char* tmp = p;
160
									p = cm_strcat3(tmp, pth, cbuf);
161
									free(tmp);
162
								}
163
 
164
								incr = j + 1;
165
								if(oldc == 0) break;
166
							}
167
						}
168
						free(req->path);
169
						req->path = p;
170
 
171
						cm_log("HTTP", "Request: %s %s %s", req->method, req->path, req->version);
172
 
173
						phase++;
174
					}
175
				} else if(c != '\r') {
176
					if(req->version == NULL) {
177
						req->version = malloc(1);
178
						req->version[0] = 0;
179
					}
180
					cbuf[0] = c;
181
					char* tmp = req->version;
182
					req->version = cm_strcat(tmp, cbuf);
183
					free(tmp);
184
				}
185
			} else if(phase == 3) {
186
				if(c == '\n') {
187
					nl++;
188
					if(nl == 2) {
189
						phase++;
190
						goto getout;
191
					} else {
192
						if(req->headers == NULL) {
193
							req->headers = malloc(sizeof(*req->headers));
194
							req->headers[0] = NULL;
195
						}
196
						int j;
197
						for(j = 0; header[j] != 0; j++) {
198
							if(header[j] == ':') {
199
								header[j] = 0;
200
								j++;
201
								for(; header[j] != 0 && (header[j] == ' ' || header[j] == '\t'); j++)
202
									;
203
								char* kv = header;
204
								char* vv = header + j;
205
 
206
								char** old = req->headers;
207
								int k;
208
								for(k = 0; old[k] != NULL; k++)
209
									;
210
								req->headers = malloc(sizeof(*req->headers) * (k + 3));
211
								for(k = 0; old[k] != NULL; k++) req->headers[k] = old[k];
212
								req->headers[k] = cm_strdup(kv);
213
								req->headers[k + 1] = cm_strdup(vv);
214
								req->headers[k + 2] = NULL;
215
								free(old);
216
 
217
								cm_log("HTTP", "Header: %s: %s", kv, vv);
218
 
219
								break;
220
							}
221
						}
222
						free(header);
223
						header = malloc(1);
224
						header[0] = 0;
225
					}
226
				} else if(c != '\r') {
227
					nl = 0;
228
					cbuf[0] = c;
229
					char* tmp = header;
230
					header = cm_strcat(tmp, cbuf);
231
					free(tmp);
232
				}
233
			}
234
		}
235
	}
236
getout:
237
	free(header);
238
	if(bad) {
239
		tw_free_request(req);
240
		return 1;
241
	}
242
	return 0;
243
}