1 /* Copyright 2004 Justin Erenkrantz and Greg Stein
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
18 #include <apr_general.h> /* for strcasecmp() */
21 #include "serf_bucket_util.h"
24 typedef struct header_list {
28 apr_size_t header_size;
29 apr_size_t value_size;
32 #define ALLOC_HEADER 0x0001 /* header lives in our allocator */
33 #define ALLOC_VALUE 0x0002 /* value lives in our allocator */
35 struct header_list *next;
41 header_list_t *cur_read;
43 READ_START, /* haven't started reading yet */
44 READ_HEADER, /* reading cur_read->header */
45 READ_SEP, /* reading ": " */
46 READ_VALUE, /* reading cur_read->value */
47 READ_CRLF, /* reading "\r\n" */
48 READ_TERM, /* reading the final "\r\n" */
49 READ_DONE /* no more data to read */
51 apr_size_t amt_read; /* how much of the current state we've read */
56 serf_bucket_t *serf_bucket_headers_create(
57 serf_bucket_alloc_t *allocator)
59 headers_context_t *ctx;
61 ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
63 ctx->state = READ_START;
65 return serf_bucket_create(&serf_bucket_type_headers, allocator, ctx);
68 void serf_bucket_headers_setx(
70 const char *header, apr_size_t header_size, int header_copy,
71 const char *value, apr_size_t value_size, int value_copy)
73 headers_context_t *ctx = bkt->data;
74 header_list_t *iter = ctx->list;
78 /* ### include this? */
80 /* we started reading. can't change now. */
85 hdr = serf_bucket_mem_alloc(bkt->allocator, sizeof(*hdr));
86 hdr->header_size = header_size;
87 hdr->value_size = value_size;
92 hdr->header = serf_bstrmemdup(bkt->allocator, header, header_size);
93 hdr->alloc_flags |= ALLOC_HEADER;
100 hdr->value = serf_bstrmemdup(bkt->allocator, value, value_size);
101 hdr->alloc_flags |= ALLOC_VALUE;
107 /* Add the new header at the end of the list. */
108 while (iter && iter->next) {
117 void serf_bucket_headers_set(
118 serf_bucket_t *headers_bucket,
122 serf_bucket_headers_setx(headers_bucket,
123 header, strlen(header), 0,
124 value, strlen(value), 1);
127 void serf_bucket_headers_setc(
128 serf_bucket_t *headers_bucket,
132 serf_bucket_headers_setx(headers_bucket,
133 header, strlen(header), 1,
134 value, strlen(value), 1);
137 void serf_bucket_headers_setn(
138 serf_bucket_t *headers_bucket,
142 serf_bucket_headers_setx(headers_bucket,
143 header, strlen(header), 0,
144 value, strlen(value), 0);
147 const char *serf_bucket_headers_get(
148 serf_bucket_t *headers_bucket,
151 headers_context_t *ctx = headers_bucket->data;
152 header_list_t *found = ctx->list;
153 const char *val = NULL;
158 if (strcasecmp(found->header, header) == 0) {
160 /* The header is already present. RFC 2616, section 4.2
161 indicates that we should append the new value, separated by
162 a comma. Reasoning: for headers whose values are known to
163 be comma-separated, that is clearly the correct behavior;
164 for others, the correct behavior is undefined anyway. */
166 /* The "+1" is for the comma; the +1 in the alloc
167 call is for the terminating '\0' */
168 apr_size_t new_size = found->value_size + value_size + 1;
169 char *new_val = serf_bucket_mem_alloc(headers_bucket->allocator,
171 memcpy(new_val, val, value_size);
172 new_val[value_size] = ',';
173 memcpy(new_val + value_size + 1, found->value,
175 new_val[new_size] = '\0';
176 /* Copy the new value over the already existing value. */
178 serf_bucket_mem_free(headers_bucket->allocator, (void*)val);
179 val_alloc |= ALLOC_VALUE;
181 value_size = new_size;
185 value_size = found->value_size;
194 void serf_bucket_headers_do(
195 serf_bucket_t *headers_bucket,
196 serf_bucket_headers_do_callback_fn_t func,
199 headers_context_t *ctx = headers_bucket->data;
200 header_list_t *scan = ctx->list;
203 if (func(baton, scan->header, scan->value) != 0) {
210 static void serf_headers_destroy_and_data(serf_bucket_t *bucket)
212 headers_context_t *ctx = bucket->data;
213 header_list_t *scan = ctx->list;
216 header_list_t *next_hdr = scan->next;
218 if (scan->alloc_flags & ALLOC_HEADER)
219 serf_bucket_mem_free(bucket->allocator, (void *)scan->header);
220 if (scan->alloc_flags & ALLOC_VALUE)
221 serf_bucket_mem_free(bucket->allocator, (void *)scan->value);
222 serf_bucket_mem_free(bucket->allocator, scan);
227 serf_default_destroy_and_data(bucket);
230 static void select_value(
231 headers_context_t *ctx,
238 if (ctx->state == READ_START) {
239 if (ctx->list == NULL) {
240 /* No headers. Move straight to the TERM state. */
241 ctx->state = READ_TERM;
244 ctx->state = READ_HEADER;
245 ctx->cur_read = ctx->list;
250 switch (ctx->state) {
252 v = ctx->cur_read->header;
253 l = ctx->cur_read->header_size;
260 v = ctx->cur_read->value;
261 l = ctx->cur_read->value_size;
276 *value = v + ctx->amt_read;
277 *len = l - ctx->amt_read;
280 /* the current data chunk has been read/consumed. move our internal state. */
281 static apr_status_t consume_chunk(headers_context_t *ctx)
283 /* move to the next state, resetting the amount read. */
287 /* just sent the terminator and moved to DONE. signal completion. */
288 if (ctx->state == READ_DONE)
291 /* end of this header. move to the next one. */
292 if (ctx->state == READ_TERM) {
293 ctx->cur_read = ctx->cur_read->next;
294 if (ctx->cur_read != NULL) {
295 /* We've got another head to send. Reset the read state. */
296 ctx->state = READ_HEADER;
298 /* else leave in READ_TERM */
301 /* there is more data which can be read immediately. */
305 static apr_status_t serf_headers_peek(serf_bucket_t *bucket,
309 headers_context_t *ctx = bucket->data;
311 select_value(ctx, data, len);
313 /* already done or returning the CRLF terminator? return EOF */
314 if (ctx->state == READ_DONE || ctx->state == READ_TERM)
320 static apr_status_t serf_headers_read(serf_bucket_t *bucket,
321 apr_size_t requested,
322 const char **data, apr_size_t *len)
324 headers_context_t *ctx = bucket->data;
327 select_value(ctx, data, &avail);
328 if (ctx->state == READ_DONE) {
333 if (requested >= avail) {
334 /* return everything from this chunk */
337 /* we consumed this chunk. advance the state. */
338 return consume_chunk(ctx);
341 /* return just the amount requested, and advance our pointer */
343 ctx->amt_read += requested;
345 /* there is more that can be read immediately */
349 static apr_status_t serf_headers_readline(serf_bucket_t *bucket,
350 int acceptable, int *found,
351 const char **data, apr_size_t *len)
353 headers_context_t *ctx = bucket->data;
356 /* ### what behavior should we use here? APR_EGENERAL for now */
357 if ((acceptable & SERF_NEWLINE_CRLF) == 0)
360 /* get whatever is in this chunk */
361 select_value(ctx, data, len);
362 if (ctx->state == READ_DONE)
365 /* we consumed this chunk. advance the state. */
366 status = consume_chunk(ctx);
368 /* the type of newline found is easy... */
369 *found = (ctx->state == READ_CRLF || ctx->state == READ_TERM)
370 ? SERF_NEWLINE_CRLF : SERF_NEWLINE_NONE;
375 static apr_status_t serf_headers_read_iovec(serf_bucket_t *bucket,
376 apr_size_t requested,
381 apr_size_t avail = requested;
386 for (i = 0; i < vecs_size; i++) {
391 /* Calling read() would not be a safe opt in the general case, but it
392 * is here for the header bucket as it only frees all of the header
393 * keys and values when the entire bucket goes away - not on a
394 * per-read() basis as is normally the case.
396 status = serf_headers_read(bucket, avail, &data, &len);
399 vecs[*vecs_used].iov_base = (char*)data;
400 vecs[*vecs_used].iov_len = len;
404 if (avail != SERF_READ_ALL_AVAIL) {
407 /* If we reach 0, then read()'s status will suffice. */
422 const serf_bucket_type_t serf_bucket_type_headers = {
425 serf_headers_readline,
426 serf_headers_read_iovec,
427 serf_default_read_for_sendfile,
428 serf_default_read_bucket,
430 serf_headers_destroy_and_data,