Project

General

Profile

lighttpd_1.4.26_chunked_decoding2.patch

andreas_stoe, 2010-04-29 16:00

View differences:

lighttpd-1.4.26/src/base.h 2010-04-29 15:27:24.671174348 +0200
331 331
	CON_STATE_WRITE,
332 332
	CON_STATE_RESPONSE_END,
333 333
	CON_STATE_ERROR,
334
	CON_STATE_CLOSE
334
	CON_STATE_CLOSE,
335
	CON_STATE_READ_CHUNKED, /* Added by Andreas Stöckel for "transfer-encoding: chunked" support */
335 336
} connection_state_t;
336 337

  
337 338
typedef enum { COND_RESULT_UNSET, COND_RESULT_FALSE, COND_RESULT_TRUE } cond_result_t;
......
439 440
	etag_flags_t etag_flags;
440 441

  
441 442
	int conditional_is_valid[COMP_LAST_ELEMENT]; 
443

  
444
	/* Added by Andreas Stöckel for "transfer-encoding: chunked" support */
445
	void *chunk_decoder;
442 446
} connection;
443 447

  
444 448
typedef struct {
lighttpd-1.4.26/src/chunk_decoder.c 2010-04-29 15:27:41.127175837 +0200
1
/*
2
  chunk_decoder.c
3

  
4
  small library for decoding http requests with "transfer-encoding: chunked".
5

  
6
  (c) by Andreas Stöckel 2010
7

  
8
  This file is licensed under the revised BSD license, see ../COPYING for more info
9
*/
10

  
11
#include "chunk_decoder.h"
12
#include <stdlib.h>
13
#include <stdio.h>
14
#include <string.h>
15

  
16
#define INT_STATE_READ_NUMBER 100
17
#define INT_STATE_READ_NUMBER_WAIT_FOR_CR 101
18
#define INT_STATE_READ_NUMBER_WAIT_FOR_LF 102
19
#define INT_STATE_READ_CHUNK 103
20
#define INT_STATE_READ_CHUNK_WAIT_FOR_CR 104
21
#define INT_STATE_READ_CHUNK_WAIT_FOR_LF 105
22
#define IS_NUMBER(nb) ((nb >= 48) && (nb <= 57))
23
#define IS_HEX_NUMBER(nb) (((nb >= 48) && (nb <= 57)) || ((nb >= 'A') && (nb <= 'F')))
24
#define IS_CRLF(c) ((c == '\r') || (c == '\n'))
25
#define IS_CR(c) (c == '\r')
26
#define IS_LF(c) (c == '\n')
27
#define IS_SPACE(c) (c == ' ')
28
#define CHAR_TO_NB(nb) (nb - 48)
29
#define HEX_CHAR_TO_NB(nb) ((nb <= '9') ? (nb - 48) : (nb - 65 + 10))
30

  
31
typedef struct{
32
	chunk_decoder dec;
33
	int int_state;
34
	chk_int64 numberbuf;
35
	chk_int64 cur_chunk_size;
36
} chunk_decoder_int;
37
typedef chunk_decoder_int* lp_chunk_decoder_int;
38

  
39
lp_chunk_decoder chunk_decoder_create()
40
{
41
	/* Reserve some memory for the chunk_decoder structure and return it */
42
	lp_chunk_decoder_int dec = malloc(sizeof(*dec));
43
	memset(dec, 0, sizeof(*dec));
44
	dec->dec.state = CHUNK_DECODER_STATE_START;
45
	dec->dec.size = 0;
46

  
47
	return (lp_chunk_decoder)dec;
48
}
49

  
50
void chunk_decoder_free(lp_chunk_decoder decoder)
51
{
52
	/* Free the memory reserved for the chunk_decoder */
53
	if (decoder != NULL)
54
		free(decoder);
55
}
56

  
57
#define READCHAR() {mem_in++; data_remaining--;}
58
#define WRITECHAR(c) {mem_out[0] = c; mem_out++; dec->dec.size++; dec->cur_chunk_size--; (*write_size)++;}
59

  
60
/* Decode mem_in and write it to mem_out */
61
int chunk_decoder_work(lp_chunk_decoder decoder, char *mem_in, char *mem_out,
62
	size_t read_size, size_t *write_size)
63
{
64
	*write_size = 0;
65
	size_t data_remaining = read_size;
66
	lp_chunk_decoder_int dec = (lp_chunk_decoder_int)decoder;
67

  
68
	while (data_remaining > 0)
69
	{
70
		switch (dec->dec.state)
71
		{
72

  
73
			/* Handle the chunk decoder start state */
74
			case CHUNK_DECODER_STATE_START:
75
				if (IS_CRLF(mem_in[0]))
76
				{
77
					READCHAR();
78
				}
79
				else
80
				{
81
					dec->dec.state = CHUNK_DECODER_STATE_READING;
82
					dec->int_state = INT_STATE_READ_NUMBER;
83
					dec->numberbuf = -1;
84
				}
85
					
86
				break;
87
			
88
			/* Handle the chunk decoder read state. This is the state where the
89
			   acutal information is read */
90
			case CHUNK_DECODER_STATE_READING:
91
				switch (dec->int_state)
92
				{
93
					/* Reading the preceding number from every chunk */
94
					case INT_STATE_READ_NUMBER:
95
						/* Check whether the current char is a hex number char */
96
						if (IS_HEX_NUMBER(mem_in[0]))
97
						{
98
							/* Append the hex number char to the current hex number being read */
99
							if (dec->numberbuf == -1)
100
								dec->numberbuf = HEX_CHAR_TO_NB(mem_in[0]);
101
							else
102
								dec->numberbuf = (dec->numberbuf << 4) + HEX_CHAR_TO_NB(mem_in[0]);
103
							READCHAR();
104
						}
105
						else
106
						{
107
							/* As there had been no other hex number following, go on
108
							   reading the actual chunk data */
109
							if (dec->numberbuf == -1)
110
							{
111
								/* There was no chunk size read at all, exit */
112
								dec->dec.state = CHUNK_DECODER_STATE_ERROR;
113
								return -1;
114
							}
115
							else
116
							{
117
								/* If the number read was zero, we're at the end of
118
								   the chunked encoded data */
119
								dec->cur_chunk_size = dec->numberbuf;
120
								if (dec->cur_chunk_size == 0)
121
								{
122
									dec->dec.state = CHUNK_DECODER_STATE_END;
123
									return 1;
124
								}
125
								dec->numberbuf = -1;
126
								dec->int_state = INT_STATE_READ_NUMBER_WAIT_FOR_CR;
127
							}
128
						}
129

  
130
						break;
131

  
132
					/* Wait for an CR after reading the number */
133
					case INT_STATE_READ_NUMBER_WAIT_FOR_CR:
134
						if (IS_CR(mem_in[0]))
135
						{
136
							dec->int_state = INT_STATE_READ_NUMBER_WAIT_FOR_LF;
137
						}
138
						READCHAR();
139

  
140
						break;
141

  
142
					/* Wait for an LF after reading the CR. LF has to follow the
143
					   CR immediately */
144
					case INT_STATE_READ_NUMBER_WAIT_FOR_LF:
145
						if (IS_LF(mem_in[0]))
146
						{
147
							dec->int_state = INT_STATE_READ_CHUNK;
148
							READCHAR();
149
						}
150
						else
151
						{
152
							dec->dec.state = CHUNK_DECODER_STATE_ERROR;
153
							return -1;
154
						}
155
							
156
						break;
157

  
158
					/* Read the actual chunk data */
159
					case INT_STATE_READ_CHUNK:
160
						if (dec->cur_chunk_size > 0)
161
						{
162
							WRITECHAR(mem_in[0]);
163
							READCHAR();
164
						}
165
						else
166
						{
167
							dec->int_state = INT_STATE_READ_CHUNK_WAIT_FOR_CR;
168
						}
169

  
170
						break;
171

  
172
					/* Wait for the CR which should follow each chunk */
173
					case INT_STATE_READ_CHUNK_WAIT_FOR_CR:
174
						if (IS_CR(mem_in[0]))
175
						{
176
							dec->int_state = INT_STATE_READ_CHUNK_WAIT_FOR_LF;
177
						}
178
						READCHAR();
179

  
180
						break;
181

  
182
					/* Wait for the LF which should follow the CR (see above) */
183
					case INT_STATE_READ_CHUNK_WAIT_FOR_LF:
184
						if (IS_LF(mem_in[0]))
185
						{
186
							dec->int_state = INT_STATE_READ_NUMBER;
187
							READCHAR();
188
						}
189
						else
190
						{
191
							dec->dec.state = CHUNK_DECODER_STATE_ERROR;
192
							return -1;
193
						}
194

  
195
						break;
196

  
197
					/* Default must not happen! */
198
					default:
199
						dec->dec.state = CHUNK_DECODER_STATE_ERROR;
200
						return -1;
201
				}
202

  
203
				break;
204

  
205
			case CHUNK_DECODER_STATE_ERROR:
206
				return -1;
207

  
208
			case CHUNK_DECODER_STATE_END:
209
				/* This function must not be called if the chunk decoder is already finished! */
210
				dec->dec.state = CHUNK_DECODER_STATE_ERROR;
211
				return -1;
212

  
213
			/* Default must not happen! */
214
			default:
215
				dec->dec.state = CHUNK_DECODER_STATE_ERROR;
216
				return -1;
217
		}
218
	}
219
	return 0;
220
}
221

  
lighttpd-1.4.26/src/chunk_decoder.h 2010-04-29 15:10:32.711175470 +0200
1
/*
2
  chunk_decoder.h
3

  
4
  small library for decoding http requests with "transfer-encoding: chunked".
5

  
6
  (c) by Andreas Stöckel 2010
7

  
8
  This file is licensed under the revised BSD license, see ../COPYING for more info
9
*/
10

  
11
#ifndef _CHUNK_DECODER_H_
12
#define _CHUNK_DECODER_H_
13

  
14
#include <stddef.h>
15

  
16
#define CHUNK_DECODER_STATE_START 0
17
#define CHUNK_DECODER_STATE_READING 1
18
#define CHUNK_DECODER_STATE_END 2
19
#define CHUNK_DECODER_STATE_ERROR 3
20

  
21
//Int64 data type used for storing the size information for (hypotetically) very large files
22
typedef long long chk_int64;
23

  
24
/*Public data of the chunk decoder. You must not set any of these values, they
25
are for read access only.*/
26
typedef struct{
27
	/*The current state of the chunk decoder. May be CHUNK_DECODER_STATE_START if
28
	the chunk decoder has just been created, CHUNK_DECODER_STATE_READING if the
29
	chunk decoder is just working on the data, CHUNK_DECODER_STATE_END if the
30
	chunk decoder has come to a normal end and CHUNK_DEOCDER_STATE_ERROR if the
31
	chunk decoder came to an abnormal ending and has not been able to go on decoding
32
	the data. */
33
	int state;
34
	/*Contains the full size of the decoded data.*/
35
	chk_int64 size;
36
} chunk_decoder;
37

  
38
/*Pointer on chunk_decoder*/
39
typedef chunk_decoder* lp_chunk_decoder;
40

  
41
/*Create an instance of chunk_decoder*/
42
lp_chunk_decoder chunk_decoder_create();
43

  
44
/*Feed the deocder with data from mem_in of read_size size. The deocder should
45
write the decoded data to mem_out. The size of the data written is stored in
46
write_size. The memory reserved on mem_out should at least have the size of
47
read_size. chunk_decoder_work will return 0 if the operation had been executed
48
successfully, -1 if an error occurred and 1 if decoding has finished. Additionally
49
it set decoder->state accordingly.*/
50
int chunk_decoder_work(lp_chunk_decoder decoder, char *mem_in, char *mem_out,
51
	size_t read_size, size_t *write_size);
52

  
53
/*Free the instance of chunk_decoder*/
54
void chunk_decoder_free(lp_chunk_decoder decoder);
55

  
56
#endif /*_CHUNK_DECODER_H_*/
57

  
lighttpd-1.4.26/src/connections.c 2010-04-29 15:29:11.319175204 +0200
15 15

  
16 16
#include "inet_ntop_cache.h"
17 17

  
18
#include "chunk_decoder.h"
19

  
18 20
#include <sys/stat.h>
19 21

  
20 22
#include <stdlib.h>
......
882 884
	off_t last_offset;
883 885
	chunkqueue *cq = con->read_queue;
884 886
	chunkqueue *dst_cq = con->request_content_queue;
887
	lp_chunk_decoder dec = NULL;
885 888
	int is_closed = 0; /* the connection got closed, if we don't have a complete header, -> error */
886 889

  
887 890
	if (con->is_readable) {
......
1031 1034
			connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
1032 1035
		}
1033 1036
		break;
1037

  
1034 1038
	case CON_STATE_READ_POST:
1035 1039
		for (c = cq->first; c && (dst_cq->bytes_in != (off_t)con->request.content_length); c = c->next) {
1036 1040
			off_t weWant, weHave, toRead;
......
1155 1159
		}
1156 1160

  
1157 1161
		break;
1158
	default: break;
1162

  
1163
#define FREE_AND_QUIT {\
1164
chunk_decoder_free(dec);\
1165
con->chunk_decoder = NULL;\
1166
dec = NULL;\
1167
connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);\
1168
}
1169

  
1170
	/* Added by Andreas Stöckel for "transfer-encoding: chunked" support */
1171
	case CON_STATE_READ_CHUNKED:
1172
		/* Check whether the chunk decoder has been created, if not, create it */
1173
		if (con->chunk_decoder == NULL)	{
1174
			dec = chunk_decoder_create();
1175
			con->chunk_decoder = dec;
1176
		} else {
1177
			dec = (lp_chunk_decoder)con->chunk_decoder;
1178
		}		
1179

  
1180
		for (c = cq->first; c != NULL; c = c->next) {
1181
			/* Reserve some memory which should contain the decoded data */
1182
			int toRead = c->mem->used - c->offset - 1;
1183
			char *target = malloc(toRead);
1184
			memset(target, 0, toRead);
1185

  
1186
			size_t decoded_size = 0;
1187

  
1188
			int res = chunk_decoder_work(dec, c->mem->ptr + c->offset, target, toRead,
1189
				&decoded_size);
1190
			if (res >= 0)
1191
			{
1192
				/* the new way, copy everything into a chunkqueue whcih might use tempfiles */
1193
				chunk *dst_c = NULL;
1194

  
1195
				/* copy everything to max 1Mb sized tempfiles */
1196

  
1197
				/*
1198
				 * if the last chunk is
1199
				 * - smaller than 1Mb (size < 1Mb)
1200
				 * - not read yet (offset == 0)
1201
				 * -> append to it
1202
				 * otherwise
1203
				 * -> create a new chunk
1204
				 *
1205
				 * */
1206

  
1207
				if (dst_cq->last &&
1208
					dst_cq->last->type == FILE_CHUNK &&
1209
					dst_cq->last->file.is_temp &&
1210
					dst_cq->last->offset == 0) {
1211
					/* ok, take the last chunk for our job */
1212

  
1213
			 		if (dst_cq->last->file.length < 1 * 1024 * 1024) {
1214
						dst_c = dst_cq->last;
1215

  
1216
						if (dst_c->file.fd == -1) {
1217
							/* this should not happen as we cache the fd, but you never know */
1218
							dst_c->file.fd = open(dst_c->file.name->ptr, O_WRONLY | O_APPEND);
1219
#ifdef FD_CLOEXEC
1220
							fcntl(dst_c->file.fd, F_SETFD, FD_CLOEXEC);
1221
#endif
1222
						}
1223
					} else {
1224
						/* the chunk is too large now, close it */
1225
						dst_c = dst_cq->last;
1226

  
1227
						if (dst_c->file.fd != -1) {
1228
							close(dst_c->file.fd);
1229
							dst_c->file.fd = -1;
1230
						}
1231
						dst_c = chunkqueue_get_append_tempfile(dst_cq);
1232
					}
1233
				} else {
1234
					dst_c = chunkqueue_get_append_tempfile(dst_cq);
1235
				}
1236

  
1237
				/* we have a chunk, let's write to it */
1238

  
1239
				if (dst_c->file.fd == -1) {
1240
					/* we don't have file to write to,
1241
					 * EACCES might be one reason.
1242
					 *
1243
					 * Instead of sending 500 we send 413 and say the request is too large
1244
					 *  */
1245

  
1246
					log_error_write(srv, __FILE__, __LINE__, "sbs",
1247
							"denying upload as opening to temp-file for upload failed:",
1248
							dst_c->file.name, strerror(errno));
1249

  
1250
					con->http_status = 413; /* Request-Entity too large */
1251
					con->keep_alive = 0;
1252
					
1253
					FREE_AND_QUIT;
1254
					free(target);			
1255

  
1256
					break;
1257
				}
1258

  
1259
				if ((int)decoded_size != write(dst_c->file.fd, target, decoded_size)) {
1260
					/* write failed for some reason ... disk full ? */
1261
					log_error_write(srv, __FILE__, __LINE__, "sbs",
1262
							"denying upload as writing to file failed:",
1263
							dst_c->file.name, strerror(errno));
1264

  
1265
					con->http_status = 413; /* Request-Entity too large */
1266
					con->keep_alive = 0;
1267

  
1268
					close(dst_c->file.fd);
1269
					dst_c->file.fd = -1;
1270

  
1271
					FREE_AND_QUIT;
1272
					free(target);
1273

  
1274
					break;
1275
				}
1276

  
1277
				if (res == 1) {
1278
					/* we read everything, close the chunk */
1279
					close(dst_c->file.fd);
1280
					dst_c->file.fd = -1;
1281
				}
1282

  
1283
				dst_c->file.length += decoded_size;
1284
				dst_cq->bytes_in += decoded_size;
1285
				c->offset += toRead;
1286
			}
1287
			free(target);
1288
		}
1289

  
1290
		/* Check whether the file upload limit has been exhausted */
1291

  
1292
		/* divide by 1024 as srvconf.max_request_size is in kBytes */
1293
		if (dec != NULL) {
1294
			if (srv->srvconf.max_request_size != 0 &&
1295
				(dec->size >> 10) > srv->srvconf.max_request_size) {
1296
				con->http_status = 413;
1297
				con->keep_alive = 0;
1298

  
1299
				log_error_write(srv, __FILE__, __LINE__, "sos",
1300
						"request-size too long:", (off_t) dec->size, "-> 413");
1301

  
1302
				FREE_AND_QUIT;
1303
			}
1304
		}
1305

  
1306
		if (dec != NULL) {
1307
			if (dec->state >= CHUNK_DECODER_STATE_END) {
1308
				switch (dec->state) {
1309
					case CHUNK_DECODER_STATE_END:
1310
						/* Set the content length to allow the plugins to treat this request as
1311
						   if it had been send with content_lenght */
1312
						con->request.content_length = dec->size;
1313
					
1314
						break;
1315
					case CHUNK_DECODER_STATE_ERROR:
1316
						/* Chunk decoding failed. Return an error 400, bad request */
1317
						log_error_write(srv, __FILE__, __LINE__, "s",
1318
							"chunked transfer-encoding decoding failed.");
1319

  
1320
						con->http_status = 400;
1321
						con->keep_alive = 0;
1322
						break;
1323
				}
1324
				FREE_AND_QUIT;
1325
			}
1326
		}
1327
	
1328
		break;
1329

  
1330
	default:
1331
		break;
1159 1332
	}
1160 1333

  
1161 1334
	/* the connection got closed and we didn't got enough data to leave one of the READ states
......
1406 1579
			buffer_reset(con->uri.query);
1407 1580
			buffer_reset(con->request.orig_uri);
1408 1581

  
1409
			if (http_request_parse(srv, con)) {
1410
				/* we have to read some data from the POST request */
1411

  
1412
				connection_set_state(srv, con, CON_STATE_READ_POST);
1413

  
1414
				break;
1582
			/* As we've now got three possible results, a "switch" is needed (Andreas Stöckel) */
1583
			int res = http_request_parse(srv, con);
1584
			switch (res)
1585
			{
1586
				case 1:
1587
					/* we have to read some data from the POST request */
1588
					connection_set_state(srv, con, CON_STATE_READ_POST);
1589
					break;
1590
				case 2:
1591
					/* we have to read chunk encoded data */
1592
					connection_set_state(srv, con, CON_STATE_READ_CHUNKED);
1593
					break;
1594
				default:
1595
					connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
1415 1596
			}
1416 1597

  
1417
			connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
1418

  
1419 1598
			break;
1420 1599
		case CON_STATE_HANDLE_REQUEST:
1421 1600
			/*
......
1624 1803
			}
1625 1804

  
1626 1805
			break;
1806
		case CON_STATE_READ_CHUNKED: //Added by Andreas Stöckel for "transfer-encoding: chunked" support
1627 1807
		case CON_STATE_READ_POST:
1628 1808
		case CON_STATE_READ:
1629 1809
			if (srv->srvconf.log_state_handling) {
......
1777 1957
	}
1778 1958

  
1779 1959
	switch(con->state) {
1780
	case CON_STATE_READ_POST:
1960
	case CON_STATE_READ_CHUNKED:
1961
	case CON_STATE_READ_POST: //Added by Andreas Stöckel for "transfer-encoding: chunked" support
1781 1962
	case CON_STATE_READ:
1782 1963
	case CON_STATE_CLOSE:
1783 1964
		fdevent_event_add(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_IN);
lighttpd-1.4.26/src/request.c 2010-04-29 15:29:38.103175268 +0200
1010 1010
									return 0;
1011 1011
								}
1012 1012
							}
1013

  
1014 1013
							if (ds) array_insert_unique(con->request.headers, (data_unset *)ds);
1015 1014
						} else {
1016 1015
							/* empty header-fields are not allowed by HTTP-RFC, we just ignore them */
......
1120 1119
		return 0;
1121 1120
	}
1122 1121

  
1122
	data_string *teval = (data_string*)array_get_element(con->request.headers, "Transfer-Encoding");
1123
	int chunked_transfer_encoding = 
1124
		((teval != NULL) && (strcasecmp(teval->value->ptr, "chunked") == 0)) ? 1 : 0;
1125
	
1126

  
1123 1127
	switch(con->request.http_method) {
1124 1128
	case HTTP_METHOD_GET:
1125 1129
	case HTTP_METHOD_HEAD:
......
1133 1137
			con->http_status = 400;
1134 1138
			return 0;
1135 1139
		}
1140
		/* transfer-encoding is forbidden for those */ /* Changed to support "transfer-encoding: chunked" (Andreas Stöckel) */
1141
		if (con_length_set && (chunked_transfer_encoding != 0)) {
1142
			/* content-length is missing */
1143
			log_error_write(srv, __FILE__, __LINE__, "s",
1144
					"GET/HEAD with transfer-encoding -> 400");
1145

  
1146
			con->keep_alive = 0;
1147
			con->http_status = 400;
1148
			return 0;
1149
		}
1136 1150
		break;
1137 1151
	case HTTP_METHOD_POST:
1138 1152
		/* content-length is required for them */
1139
		if (!con_length_set) {
1153
		if (!con_length_set && (chunked_transfer_encoding != 1)) { /* Changed to support "transfer-encoding: chunked" (Andreas Stöckel) */
1140 1154
			/* content-length is missing */
1141 1155
			log_error_write(srv, __FILE__, __LINE__, "s",
1142 1156
					"POST-request, but content-length missing -> 411");
......
1187 1201
		}
1188 1202
	}
1189 1203

  
1204
	/* Added to support "transfer-encoding: chunked" (Andreas Stöckel) */
1205
	if (chunked_transfer_encoding == 1)
1206
		return 2;
1207

  
1190 1208
	return 0;
1191 1209
}