1/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein 2 * 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 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 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. 14 */ 15 16#include <apr_pools.h> 17#include <apr_strings.h> 18 19#include "serf.h" 20#include "serf_bucket_util.h" 21 22 23typedef struct { 24 const char *method; 25 const char *uri; 26 serf_bucket_t *headers; 27 serf_bucket_t *body; 28 apr_int64_t len; 29} request_context_t; 30 31#define LENGTH_UNKNOWN ((apr_int64_t)-1) 32 33 34serf_bucket_t *serf_bucket_request_create( 35 const char *method, 36 const char *URI, 37 serf_bucket_t *body, 38 serf_bucket_alloc_t *allocator) 39{ 40 request_context_t *ctx; 41 42 ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); 43 ctx->method = method; 44 ctx->uri = URI; 45 ctx->headers = serf_bucket_headers_create(allocator); 46 ctx->body = body; 47 ctx->len = LENGTH_UNKNOWN; 48 49 return serf_bucket_create(&serf_bucket_type_request, allocator, ctx); 50} 51 52void serf_bucket_request_set_CL( 53 serf_bucket_t *bucket, 54 apr_int64_t len) 55{ 56 request_context_t *ctx = (request_context_t *)bucket->data; 57 58 ctx->len = len; 59} 60 61serf_bucket_t *serf_bucket_request_get_headers( 62 serf_bucket_t *bucket) 63{ 64 return ((request_context_t *)bucket->data)->headers; 65} 66 67void serf_bucket_request_set_root( 68 serf_bucket_t *bucket, 69 const char *root_url) 70{ 71 request_context_t *ctx = (request_context_t *)bucket->data; 72 73 /* If uri is already absolute, don't change it. */ 74 if (ctx->uri[0] != '/') 75 return; 76 77 /* If uri is '/' replace it with root_url. */ 78 if (ctx->uri[1] == '\0') 79 ctx->uri = root_url; 80 else 81 ctx->uri = 82 apr_pstrcat(serf_bucket_allocator_get_pool(bucket->allocator), 83 root_url, 84 ctx->uri, 85 NULL); 86} 87 88static void serialize_data(serf_bucket_t *bucket) 89{ 90 request_context_t *ctx = bucket->data; 91 serf_bucket_t *new_bucket; 92 const char *new_data; 93 struct iovec iov[4]; 94 apr_size_t nbytes; 95 96 /* Serialize the request-line and headers into one mother string, 97 * and wrap a bucket around it. 98 */ 99 iov[0].iov_base = (char*)ctx->method; 100 iov[0].iov_len = strlen(ctx->method); 101 iov[1].iov_base = " "; 102 iov[1].iov_len = sizeof(" ") - 1; 103 iov[2].iov_base = (char*)ctx->uri; 104 iov[2].iov_len = strlen(ctx->uri); 105 iov[3].iov_base = " HTTP/1.1\r\n"; 106 iov[3].iov_len = sizeof(" HTTP/1.1\r\n") - 1; 107 108 /* Create a new bucket for this string with a flat string. */ 109 new_data = serf_bstrcatv(bucket->allocator, iov, 4, &nbytes); 110 new_bucket = serf_bucket_simple_own_create(new_data, nbytes, 111 bucket->allocator); 112 113 /* Build up the new bucket structure. 114 * 115 * Note that self needs to become an aggregate bucket so that a 116 * pointer to self still represents the "right" data. 117 */ 118 serf_bucket_aggregate_become(bucket); 119 120 /* Insert the two buckets. */ 121 serf_bucket_aggregate_append(bucket, new_bucket); 122 serf_bucket_aggregate_append(bucket, ctx->headers); 123 124 /* If we know the length, then use C-L and the raw body. Otherwise, 125 use chunked encoding for the request. */ 126 if (ctx->len != LENGTH_UNKNOWN) { 127 char buf[30]; 128 sprintf(buf, "%" APR_INT64_T_FMT, ctx->len); 129 serf_bucket_headers_set(ctx->headers, "Content-Length", buf); 130 if (ctx->body != NULL) 131 serf_bucket_aggregate_append(bucket, ctx->body); 132 } 133 else if (ctx->body != NULL) { 134 /* Morph the body bucket to a chunked encoding bucket for now. */ 135 serf_bucket_headers_setn(ctx->headers, "Transfer-Encoding", "chunked"); 136 ctx->body = serf_bucket_chunk_create(ctx->body, bucket->allocator); 137 serf_bucket_aggregate_append(bucket, ctx->body); 138 } 139 140 /* Our private context is no longer needed, and is not referred to by 141 * any existing bucket. Toss it. 142 */ 143 serf_bucket_mem_free(bucket->allocator, ctx); 144} 145 146static apr_status_t serf_request_read(serf_bucket_t *bucket, 147 apr_size_t requested, 148 const char **data, apr_size_t *len) 149{ 150 /* Seralize our private data into a new aggregate bucket. */ 151 serialize_data(bucket); 152 153 /* Delegate to the "new" aggregate bucket to do the read. */ 154 return serf_bucket_read(bucket, requested, data, len); 155} 156 157static apr_status_t serf_request_readline(serf_bucket_t *bucket, 158 int acceptable, int *found, 159 const char **data, apr_size_t *len) 160{ 161 /* Seralize our private data into a new aggregate bucket. */ 162 serialize_data(bucket); 163 164 /* Delegate to the "new" aggregate bucket to do the readline. */ 165 return serf_bucket_readline(bucket, acceptable, found, data, len); 166} 167 168static apr_status_t serf_request_read_iovec(serf_bucket_t *bucket, 169 apr_size_t requested, 170 int vecs_size, 171 struct iovec *vecs, 172 int *vecs_used) 173{ 174 /* Seralize our private data into a new aggregate bucket. */ 175 serialize_data(bucket); 176 177 /* Delegate to the "new" aggregate bucket to do the read. */ 178 return serf_bucket_read_iovec(bucket, requested, 179 vecs_size, vecs, vecs_used); 180} 181 182static apr_status_t serf_request_peek(serf_bucket_t *bucket, 183 const char **data, 184 apr_size_t *len) 185{ 186 /* Seralize our private data into a new aggregate bucket. */ 187 serialize_data(bucket); 188 189 /* Delegate to the "new" aggregate bucket to do the peek. */ 190 return serf_bucket_peek(bucket, data, len); 191} 192 193void serf_bucket_request_become( 194 serf_bucket_t *bucket, 195 const char *method, 196 const char *uri, 197 serf_bucket_t *body) 198{ 199 request_context_t *ctx; 200 201 ctx = serf_bucket_mem_alloc(bucket->allocator, sizeof(*ctx)); 202 ctx->method = method; 203 ctx->uri = uri; 204 ctx->headers = serf_bucket_headers_create(bucket->allocator); 205 ctx->body = body; 206 207 bucket->type = &serf_bucket_type_request; 208 bucket->data = ctx; 209 210 /* The allocator remains the same. */ 211} 212 213const serf_bucket_type_t serf_bucket_type_request = { 214 "REQUEST", 215 serf_request_read, 216 serf_request_readline, 217 serf_request_read_iovec, 218 serf_default_read_for_sendfile, 219 serf_default_read_bucket, 220 serf_request_peek, 221 serf_default_destroy_and_data, 222}; 223 224