1 /* ====================================================================
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 * ====================================================================
19 */
20
21 #include <apr_lib.h>
22 #include <apr_strings.h>
23 #include <apr_date.h>
24
25 #include "serf.h"
26 #include "serf_bucket_util.h"
27 #include "serf_private.h"
28
29 typedef struct {
30 serf_bucket_t *stream;
31 serf_bucket_t *body; /* Pointer to the stream wrapping the body. */
32 serf_bucket_t *headers; /* holds parsed headers */
33
34 enum {
35 STATE_STATUS_LINE, /* reading status line */
36 STATE_HEADERS, /* reading headers */
37 STATE_BODY, /* reading body */
38 STATE_TRAILERS, /* reading trailers */
39 STATE_DONE /* we've sent EOF */
40 } state;
41
42 /* Buffer for accumulating a line from the response. */
43 serf_linebuf_t linebuf;
44
45 serf_status_line sl;
46
47 int chunked; /* Do we need to read trailers? */
48 int head_req; /* Was this a HEAD request? */
49 } response_context_t;
50
51 /* Returns 1 if according to RFC2626 this response can have a body, 0 if it
52 must not have a body. */
expect_body(response_context_t * ctx)53 static int expect_body(response_context_t *ctx)
54 {
55 if (ctx->head_req)
56 return 0;
57
58 /* 100 Continue and 101 Switching Protocols */
59 if (ctx->sl.code >= 100 && ctx->sl.code < 200)
60 return 0;
61
62 /* 204 No Content */
63 if (ctx->sl.code == 204)
64 return 0;
65
66 /* 205? */
67
68 /* 304 Not Modified */
69 if (ctx->sl.code == 304)
70 return 0;
71
72 return 1;
73 }
74
serf_bucket_response_create(serf_bucket_t * stream,serf_bucket_alloc_t * allocator)75 serf_bucket_t *serf_bucket_response_create(
76 serf_bucket_t *stream,
77 serf_bucket_alloc_t *allocator)
78 {
79 response_context_t *ctx;
80
81 ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
82 ctx->stream = stream;
83 ctx->body = NULL;
84 ctx->headers = serf_bucket_headers_create(allocator);
85 ctx->state = STATE_STATUS_LINE;
86 ctx->chunked = 0;
87 ctx->head_req = 0;
88
89 serf_linebuf_init(&ctx->linebuf);
90
91 return serf_bucket_create(&serf_bucket_type_response, allocator, ctx);
92 }
93
serf_bucket_response_set_head(serf_bucket_t * bucket)94 void serf_bucket_response_set_head(
95 serf_bucket_t *bucket)
96 {
97 response_context_t *ctx = bucket->data;
98
99 ctx->head_req = 1;
100 }
101
serf_bucket_response_get_headers(serf_bucket_t * bucket)102 serf_bucket_t *serf_bucket_response_get_headers(
103 serf_bucket_t *bucket)
104 {
105 return ((response_context_t *)bucket->data)->headers;
106 }
107
108
serf_response_destroy_and_data(serf_bucket_t * bucket)109 static void serf_response_destroy_and_data(serf_bucket_t *bucket)
110 {
111 response_context_t *ctx = bucket->data;
112
113 if (ctx->state != STATE_STATUS_LINE) {
114 serf_bucket_mem_free(bucket->allocator, (void*)ctx->sl.reason);
115 }
116
117 serf_bucket_destroy(ctx->stream);
118 if (ctx->body != NULL)
119 serf_bucket_destroy(ctx->body);
120 serf_bucket_destroy(ctx->headers);
121
122 serf_default_destroy_and_data(bucket);
123 }
124
fetch_line(response_context_t * ctx,int acceptable)125 static apr_status_t fetch_line(response_context_t *ctx, int acceptable)
126 {
127 return serf_linebuf_fetch(&ctx->linebuf, ctx->stream, acceptable);
128 }
129
parse_status_line(response_context_t * ctx,serf_bucket_alloc_t * allocator)130 static apr_status_t parse_status_line(response_context_t *ctx,
131 serf_bucket_alloc_t *allocator)
132 {
133 int res;
134 char *reason; /* ### stupid APR interface makes this non-const */
135
136 /* Ensure a valid length, to avoid overflow on the final '\0' */
137 if (ctx->linebuf.used >= SERF_LINEBUF_LIMIT) {
138 return SERF_ERROR_BAD_HTTP_RESPONSE;
139 }
140
141 /* apr_date_checkmask assumes its arguments are valid C strings */
142 ctx->linebuf.line[ctx->linebuf.used] = '\0';
143
144 /* ctx->linebuf.line should be of form: 'HTTP/1.1 200 OK',
145 but we also explicitly allow the forms 'HTTP/1.1 200' (no reason)
146 and 'HTTP/1.1 401.1 Logon failed' (iis extended error codes) */
147 res = apr_date_checkmask(ctx->linebuf.line, "HTTP/#.# ###*");
148 if (!res) {
149 /* Not an HTTP response? Well, at least we won't understand it. */
150 return SERF_ERROR_BAD_HTTP_RESPONSE;
151 }
152
153 ctx->sl.version = SERF_HTTP_VERSION(ctx->linebuf.line[5] - '0',
154 ctx->linebuf.line[7] - '0');
155 ctx->sl.code = apr_strtoi64(ctx->linebuf.line + 8, &reason, 10);
156
157 /* Skip leading spaces for the reason string. */
158 if (apr_isspace(*reason)) {
159 reason++;
160 }
161
162 /* Copy the reason value out of the line buffer. */
163 ctx->sl.reason = serf_bstrmemdup(allocator, reason,
164 ctx->linebuf.used
165 - (reason - ctx->linebuf.line));
166
167 return APR_SUCCESS;
168 }
169
170 /* This code should be replaced with header buckets. */
fetch_headers(serf_bucket_t * bkt,response_context_t * ctx)171 static apr_status_t fetch_headers(serf_bucket_t *bkt, response_context_t *ctx)
172 {
173 apr_status_t status;
174
175 /* RFC 2616 says that CRLF is the only line ending, but we can easily
176 * accept any kind of line ending.
177 */
178 status = fetch_line(ctx, SERF_NEWLINE_ANY);
179 if (SERF_BUCKET_READ_ERROR(status)) {
180 return status;
181 }
182 /* Something was read. Process it. */
183
184 if (ctx->linebuf.state == SERF_LINEBUF_READY && ctx->linebuf.used) {
185 const char *end_key;
186 const char *c;
187
188 end_key = c = memchr(ctx->linebuf.line, ':', ctx->linebuf.used);
189 if (!c) {
190 /* Bad headers? */
191 return SERF_ERROR_BAD_HTTP_RESPONSE;
192 }
193
194 /* Skip over initial ':' */
195 c++;
196
197 /* And skip all whitespaces. */
198 for(; c < ctx->linebuf.line + ctx->linebuf.used; c++)
199 {
200 if (!apr_isspace(*c))
201 {
202 break;
203 }
204 }
205
206 /* Always copy the headers (from the linebuf into new mem). */
207 /* ### we should be able to optimize some mem copies */
208 serf_bucket_headers_setx(
209 ctx->headers,
210 ctx->linebuf.line, end_key - ctx->linebuf.line, 1,
211 c, ctx->linebuf.line + ctx->linebuf.used - c, 1);
212 }
213
214 return status;
215 }
216
217 /* Perform one iteration of the state machine.
218 *
219 * Will return when one the following conditions occurred:
220 * 1) a state change
221 * 2) an error
222 * 3) the stream is not ready or at EOF
223 * 4) APR_SUCCESS, meaning the machine can be run again immediately
224 */
run_machine(serf_bucket_t * bkt,response_context_t * ctx)225 static apr_status_t run_machine(serf_bucket_t *bkt, response_context_t *ctx)
226 {
227 apr_status_t status = APR_SUCCESS; /* initialize to avoid gcc warnings */
228
229 switch (ctx->state) {
230 case STATE_STATUS_LINE:
231 /* RFC 2616 says that CRLF is the only line ending, but we can easily
232 * accept any kind of line ending.
233 */
234 status = fetch_line(ctx, SERF_NEWLINE_ANY);
235 if (SERF_BUCKET_READ_ERROR(status))
236 return status;
237
238 if (ctx->linebuf.state == SERF_LINEBUF_READY) {
239 /* The Status-Line is in the line buffer. Process it. */
240 status = parse_status_line(ctx, bkt->allocator);
241 if (status)
242 return status;
243
244 /* Good times ahead: we're switching protocols! */
245 if (ctx->sl.code == 101) {
246 ctx->body =
247 serf_bucket_barrier_create(ctx->stream, bkt->allocator);
248 ctx->state = STATE_DONE;
249 break;
250 }
251
252 /* Okay... move on to reading the headers. */
253 ctx->state = STATE_HEADERS;
254 }
255 else {
256 /* The connection closed before we could get the next
257 * response. Treat the request as lost so that our upper
258 * end knows the server never tried to give us a response.
259 */
260 if (APR_STATUS_IS_EOF(status)) {
261 return SERF_ERROR_REQUEST_LOST;
262 }
263 }
264 break;
265 case STATE_HEADERS:
266 status = fetch_headers(bkt, ctx);
267 if (SERF_BUCKET_READ_ERROR(status))
268 return status;
269
270 /* If an empty line was read, then we hit the end of the headers.
271 * Move on to the body.
272 */
273 if (ctx->linebuf.state == SERF_LINEBUF_READY && !ctx->linebuf.used) {
274 const void *v;
275
276 /* Advance the state. */
277 ctx->state = STATE_BODY;
278
279 /* If this is a response to a HEAD request, or code == 1xx,204 or304
280 then we don't receive a real body. */
281 if (!expect_body(ctx)) {
282 ctx->body = serf_bucket_simple_create(NULL, 0, NULL, NULL,
283 bkt->allocator);
284 ctx->state = STATE_BODY;
285 break;
286 }
287
288 ctx->body =
289 serf_bucket_barrier_create(ctx->stream, bkt->allocator);
290
291 /* Are we C-L, chunked, or conn close? */
292 v = serf_bucket_headers_get(ctx->headers, "Content-Length");
293 if (v) {
294 apr_uint64_t length;
295 length = apr_strtoi64(v, NULL, 10);
296 if (errno == ERANGE) {
297 return APR_FROM_OS_ERROR(ERANGE);
298 }
299 ctx->body = serf_bucket_response_body_create(
300 ctx->body, length, bkt->allocator);
301 }
302 else {
303 v = serf_bucket_headers_get(ctx->headers, "Transfer-Encoding");
304
305 /* Need to handle multiple transfer-encoding. */
306 if (v && strcasecmp("chunked", v) == 0) {
307 ctx->chunked = 1;
308 ctx->body = serf_bucket_dechunk_create(ctx->body,
309 bkt->allocator);
310 }
311 }
312 v = serf_bucket_headers_get(ctx->headers, "Content-Encoding");
313 if (v) {
314 /* Need to handle multiple content-encoding. */
315 if (v && strcasecmp("gzip", v) == 0) {
316 ctx->body =
317 serf_bucket_deflate_create(ctx->body, bkt->allocator,
318 SERF_DEFLATE_GZIP);
319 }
320 else if (v && strcasecmp("deflate", v) == 0) {
321 ctx->body =
322 serf_bucket_deflate_create(ctx->body, bkt->allocator,
323 SERF_DEFLATE_DEFLATE);
324 }
325 }
326 }
327 break;
328 case STATE_BODY:
329 /* Don't do anything. */
330 break;
331 case STATE_TRAILERS:
332 status = fetch_headers(bkt, ctx);
333 if (SERF_BUCKET_READ_ERROR(status))
334 return status;
335
336 /* If an empty line was read, then we're done. */
337 if (ctx->linebuf.state == SERF_LINEBUF_READY && !ctx->linebuf.used) {
338 ctx->state = STATE_DONE;
339 return APR_EOF;
340 }
341 break;
342 case STATE_DONE:
343 return APR_EOF;
344 default:
345 /* Not reachable */
346 return APR_EGENERAL;
347 }
348
349 return status;
350 }
351
wait_for_body(serf_bucket_t * bkt,response_context_t * ctx)352 static apr_status_t wait_for_body(serf_bucket_t *bkt, response_context_t *ctx)
353 {
354 apr_status_t status;
355
356 /* Keep reading and moving through states if we aren't at the BODY */
357 while (ctx->state != STATE_BODY) {
358 status = run_machine(bkt, ctx);
359
360 /* Anything other than APR_SUCCESS means that we cannot immediately
361 * read again (for now).
362 */
363 if (status)
364 return status;
365 }
366 /* in STATE_BODY */
367
368 return APR_SUCCESS;
369 }
370
serf_bucket_response_wait_for_headers(serf_bucket_t * bucket)371 apr_status_t serf_bucket_response_wait_for_headers(
372 serf_bucket_t *bucket)
373 {
374 response_context_t *ctx = bucket->data;
375
376 return wait_for_body(bucket, ctx);
377 }
378
serf_bucket_response_status(serf_bucket_t * bkt,serf_status_line * sline)379 apr_status_t serf_bucket_response_status(
380 serf_bucket_t *bkt,
381 serf_status_line *sline)
382 {
383 response_context_t *ctx = bkt->data;
384 apr_status_t status;
385
386 if (ctx->state != STATE_STATUS_LINE) {
387 /* We already read it and moved on. Just return it. */
388 *sline = ctx->sl;
389 return APR_SUCCESS;
390 }
391
392 /* Running the state machine once will advance the machine, or state
393 * that the stream isn't ready with enough data. There isn't ever a
394 * need to run the machine more than once to try and satisfy this. We
395 * have to look at the state to tell whether it advanced, though, as
396 * it is quite possible to advance *and* to return APR_EAGAIN.
397 */
398 status = run_machine(bkt, ctx);
399 if (ctx->state == STATE_HEADERS) {
400 *sline = ctx->sl;
401 }
402 else {
403 /* Indicate that we don't have the information yet. */
404 sline->version = 0;
405 }
406
407 return status;
408 }
409
serf_response_read(serf_bucket_t * bucket,apr_size_t requested,const char ** data,apr_size_t * len)410 static apr_status_t serf_response_read(serf_bucket_t *bucket,
411 apr_size_t requested,
412 const char **data, apr_size_t *len)
413 {
414 response_context_t *ctx = bucket->data;
415 apr_status_t rv;
416
417 rv = wait_for_body(bucket, ctx);
418 if (rv) {
419 /* It's not possible to have read anything yet! */
420 if (APR_STATUS_IS_EOF(rv) || APR_STATUS_IS_EAGAIN(rv)) {
421 *len = 0;
422 }
423 return rv;
424 }
425
426 rv = serf_bucket_read(ctx->body, requested, data, len);
427 if (SERF_BUCKET_READ_ERROR(rv))
428 return rv;
429
430 if (APR_STATUS_IS_EOF(rv)) {
431 if (ctx->chunked) {
432 ctx->state = STATE_TRAILERS;
433 /* Mask the result. */
434 rv = APR_SUCCESS;
435 } else {
436 ctx->state = STATE_DONE;
437 }
438 }
439 return rv;
440 }
441
serf_response_readline(serf_bucket_t * bucket,int acceptable,int * found,const char ** data,apr_size_t * len)442 static apr_status_t serf_response_readline(serf_bucket_t *bucket,
443 int acceptable, int *found,
444 const char **data, apr_size_t *len)
445 {
446 response_context_t *ctx = bucket->data;
447 apr_status_t rv;
448
449 rv = wait_for_body(bucket, ctx);
450 if (rv) {
451 return rv;
452 }
453
454 /* Delegate to the stream bucket to do the readline. */
455 return serf_bucket_readline(ctx->body, acceptable, found, data, len);
456 }
457
serf_response_full_become_aggregate(serf_bucket_t * bucket)458 apr_status_t serf_response_full_become_aggregate(serf_bucket_t *bucket)
459 {
460 response_context_t *ctx = bucket->data;
461 serf_bucket_t *bkt;
462 char buf[256];
463 int size;
464
465 serf_bucket_aggregate_become(bucket);
466
467 /* Add reconstructed status line. */
468 size = apr_snprintf(buf, 256, "HTTP/%d.%d %d ",
469 SERF_HTTP_VERSION_MAJOR(ctx->sl.version),
470 SERF_HTTP_VERSION_MINOR(ctx->sl.version),
471 ctx->sl.code);
472 bkt = serf_bucket_simple_copy_create(buf, size,
473 bucket->allocator);
474 serf_bucket_aggregate_append(bucket, bkt);
475 bkt = serf_bucket_simple_copy_create(ctx->sl.reason, strlen(ctx->sl.reason),
476 bucket->allocator);
477 serf_bucket_aggregate_append(bucket, bkt);
478 bkt = SERF_BUCKET_SIMPLE_STRING_LEN("\r\n", 2,
479 bucket->allocator);
480 serf_bucket_aggregate_append(bucket, bkt);
481
482 /* Add headers and stream buckets in order. */
483 serf_bucket_aggregate_append(bucket, ctx->headers);
484 serf_bucket_aggregate_append(bucket, ctx->stream);
485
486 serf_bucket_mem_free(bucket->allocator, ctx);
487
488 return APR_SUCCESS;
489 }
490
491 /* ### need to implement */
492 #define serf_response_peek NULL
493
494 const serf_bucket_type_t serf_bucket_type_response = {
495 "RESPONSE",
496 serf_response_read,
497 serf_response_readline,
498 serf_default_read_iovec,
499 serf_default_read_for_sendfile,
500 serf_default_read_bucket,
501 serf_response_peek,
502 serf_response_destroy_and_data,
503 };
504