1
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) NGINX, Inc.
5 */
6
7 #include <nxt_main.h>
8
9
10 #define NXT_FASTCGI_DATA_MIDDLE 0
11 #define NXT_FASTCGI_DATA_END_ON_BORDER 1
12 #define NXT_FASTCGI_DATA_END 2
13
14
15 static nxt_int_t nxt_fastcgi_buffer(nxt_fastcgi_parse_t *fp, nxt_buf_t ***tail,
16 nxt_buf_t *in);
17
18
19 void
nxt_fastcgi_record_parse(nxt_task_t * task,nxt_fastcgi_parse_t * fp,nxt_buf_t * in)20 nxt_fastcgi_record_parse(nxt_task_t *task, nxt_fastcgi_parse_t *fp,
21 nxt_buf_t *in)
22 {
23 u_char ch;
24 nxt_int_t ret, stream;
25 nxt_buf_t *b, *nb, **tail[2];
26 const char *msg;
27 enum {
28 sw_fastcgi_version = 0,
29 sw_fastcgi_type,
30 sw_fastcgi_request_id_high,
31 sw_fastcgi_request_id_low,
32 sw_fastcgi_content_length_high,
33 sw_fastcgi_content_length_low,
34 sw_fastcgi_padding_length,
35 sw_fastcgi_reserved,
36 sw_fastcgi_data,
37 sw_fastcgi_padding,
38 sw_fastcgi_end_request,
39 } state;
40
41 fp->out[0] = NULL;
42 fp->out[1] = NULL;
43
44 tail[0] = &fp->out[0];
45 tail[1] = &fp->out[1];
46
47 state = fp->state;
48
49 for (b = in; b != NULL; b = b->next) {
50
51 if (nxt_buf_is_sync(b)) {
52 **tail = b;
53 *tail = &b->next;
54 continue;
55 }
56
57 fp->pos = b->mem.pos;
58
59 while (fp->pos < b->mem.free) {
60 /*
61 * The sw_fastcgi_data state is tested outside the
62 * switch to preserve fp->pos and to not touch memory.
63 */
64 if (state == sw_fastcgi_data) {
65
66 /*
67 * fp->type here can be only NXT_FASTCGI_STDOUT
68 * or NXT_FASTCGI_STDERR. NXT_FASTCGI_END_REQUEST
69 * is tested in sw_fastcgi_reserved.
70 */
71 stream = fp->type - NXT_FASTCGI_STDOUT;
72
73 ret = nxt_fastcgi_buffer(fp, &tail[stream], b);
74
75 if (ret == NXT_FASTCGI_DATA_MIDDLE) {
76 goto next;
77 }
78
79 if (nxt_slow_path(ret == NXT_ERROR)) {
80 fp->error = 1;
81 goto done;
82 }
83
84 if (fp->padding == 0) {
85 state = sw_fastcgi_version;
86
87 } else {
88 state = sw_fastcgi_padding;
89 }
90
91 if (ret == NXT_FASTCGI_DATA_END_ON_BORDER) {
92 goto next;
93 }
94
95 /* ret == NXT_FASTCGI_DATA_END */
96 }
97
98 ch = *fp->pos++;
99
100 nxt_thread_log_debug("fastcgi record byte: %02Xd", ch);
101
102 switch (state) {
103
104 case sw_fastcgi_version:
105 if (nxt_fast_path(ch == 1)) {
106 state = sw_fastcgi_type;
107 continue;
108 }
109
110 msg = "unsupported FastCGI protocol version";
111 goto fastcgi_error;
112
113 case sw_fastcgi_type:
114 switch (ch) {
115 case NXT_FASTCGI_STDOUT:
116 case NXT_FASTCGI_STDERR:
117 case NXT_FASTCGI_END_REQUEST:
118 fp->type = ch;
119 state = sw_fastcgi_request_id_high;
120 continue;
121 default:
122 msg = "invalid FastCGI record type";
123 goto fastcgi_error;
124 }
125
126 case sw_fastcgi_request_id_high:
127 /* FastCGI multiplexing is not supported. */
128 if (nxt_fast_path(ch == 0)) {
129 state = sw_fastcgi_request_id_low;
130 continue;
131 }
132
133 msg = "unexpected FastCGI request ID high byte";
134 goto fastcgi_error;
135
136 case sw_fastcgi_request_id_low:
137 if (nxt_fast_path(ch == 1)) {
138 state = sw_fastcgi_content_length_high;
139 continue;
140 }
141
142 msg = "unexpected FastCGI request ID low byte";
143 goto fastcgi_error;
144
145 case sw_fastcgi_content_length_high:
146 fp->length = ch << 8;
147 state = sw_fastcgi_content_length_low;
148 continue;
149
150 case sw_fastcgi_content_length_low:
151 fp->length |= ch;
152 state = sw_fastcgi_padding_length;
153 continue;
154
155 case sw_fastcgi_padding_length:
156 fp->padding = ch;
157 state = sw_fastcgi_reserved;
158 continue;
159
160 case sw_fastcgi_reserved:
161 nxt_thread_log_debug("fastcgi record type:%d "
162 "length:%uz padding:%d",
163 fp->type, fp->length, fp->padding);
164
165 if (nxt_fast_path(fp->type != NXT_FASTCGI_END_REQUEST)) {
166 state = sw_fastcgi_data;
167 continue;
168 }
169
170 state = sw_fastcgi_end_request;
171 continue;
172
173 case sw_fastcgi_data:
174 /*
175 * This state is processed before the switch.
176 * It added here just to suppress a warning.
177 */
178 continue;
179
180 case sw_fastcgi_padding:
181 /*
182 * No special fast processing of padding
183 * because it usually takes just 1-7 bytes.
184 */
185 fp->padding--;
186
187 if (fp->padding == 0) {
188 nxt_thread_log_debug("fastcgi record end");
189 state = sw_fastcgi_version;
190 }
191 continue;
192
193 case sw_fastcgi_end_request:
194 /* Just skip 8 bytes of END_REQUEST. */
195 fp->length--;
196
197 if (fp->length != 0) {
198 continue;
199 }
200
201 fp->done = 1;
202
203 nxt_thread_log_debug("fastcgi end request");
204
205 goto done;
206 }
207 }
208
209 if (b->retain == 0) {
210 /* No record data was found in a buffer. */
211 nxt_thread_current_work_queue_add(task->thread,
212 b->completion_handler,
213 task, b, b->parent);
214 }
215
216 next:
217
218 continue;
219 }
220
221 fp->state = state;
222
223 return;
224
225 fastcgi_error:
226
227 nxt_thread_log_error(NXT_LOG_ERR, "upstream sent %s: %d", msg, ch);
228
229 fp->fastcgi_error = 1;
230
231 done:
232
233 nb = fp->last_buf(fp);
234
235 if (nxt_fast_path(nb != NULL)) {
236 *tail[0] = nb;
237
238 } else {
239 fp->error = 1;
240 }
241
242 // STUB: fp->fastcgi_error = 1;
243 // STUB: fp->error = 1;
244
245 return;
246 }
247
248
249 static nxt_int_t
nxt_fastcgi_buffer(nxt_fastcgi_parse_t * fp,nxt_buf_t *** tail,nxt_buf_t * in)250 nxt_fastcgi_buffer(nxt_fastcgi_parse_t *fp, nxt_buf_t ***tail, nxt_buf_t *in)
251 {
252 u_char *p;
253 size_t size;
254 nxt_buf_t *b;
255
256 if (fp->length == 0) {
257 return NXT_FASTCGI_DATA_END;
258 }
259
260 p = fp->pos;
261 size = in->mem.free - p;
262
263 if (fp->length >= size && in->retain == 0) {
264 /*
265 * Use original buffer if the buffer is lesser than or equal to
266 * FastCGI record size and this is the first record in the buffer.
267 */
268 in->mem.pos = p;
269 **tail = in;
270 *tail = &in->next;
271
272 } else {
273 b = nxt_buf_mem_alloc(fp->mem_pool, 0, 0);
274 if (nxt_slow_path(b == NULL)) {
275 return NXT_ERROR;
276 }
277
278 **tail = b;
279 *tail = &b->next;
280
281 b->parent = in;
282 in->retain++;
283 b->mem.pos = p;
284 b->mem.start = p;
285
286 if (fp->length < size) {
287 p += fp->length;
288 fp->pos = p;
289
290 b->mem.free = p;
291 b->mem.end = p;
292
293 return NXT_FASTCGI_DATA_END;
294 }
295
296 b->mem.free = in->mem.free;
297 b->mem.end = in->mem.free;
298 }
299
300 fp->length -= size;
301
302 if (fp->length == 0) {
303 return NXT_FASTCGI_DATA_END_ON_BORDER;
304 }
305
306 return NXT_FASTCGI_DATA_MIDDLE;
307 }
308