Subversion Repositories Tewi

Rev

Rev 16 | Rev 20 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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