2 * Copyright (C) 2005-2008 Atsushi Konno All rights reserved.
3 * Copyright (C) 2005 QSDN,Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 #include "chxj_serf.h"
19 #include "apr_pools.h"
21 typedef struct __app_ctx_t app_ctx_t;
22 typedef struct __handler_ctx_t handler_ctx_t;
26 serf_ssl_context_t *ssl_ctx;
27 serf_bucket_alloc_t *bkt_alloc;
30 struct __handler_ctx_t {
31 #if APR_MAJOR_VERSION > 0
32 apr_uint32_t requests_outstanding;
34 apr_atomic_t requests_outstanding;
37 serf_response_acceptor_t acceptor;
38 app_ctx_t *acceptor_ctx;
40 serf_response_handler_t handler;
45 const char *user_agent;
51 apr_size_t response_len;
53 apr_size_t post_data_len;
54 apr_table_t *headers_out;
59 char *default_chxj_serf_get(request_rec *r, apr_pool_t *ppool, const char *url_path, int set_headers_flag, apr_size_t *response_len);
60 char *(*chxj_serf_get)(request_rec *r, apr_pool_t *ppool, const char *url_path, int set_headers_flag, apr_size_t *response_len) = default_chxj_serf_get;
61 char *default_chxj_serf_post(request_rec *r, apr_pool_t *ppool, const char *url_path, char *post_data, apr_size_t post_data_len, int set_headers_flag, apr_size_t *response_len);
62 char *(*chxj_serf_post)(request_rec *r, apr_pool_t *ppool, const char *url_path, char *post_data, apr_size_t post_data_len, int set_headers_flag, apr_size_t *response_len) = default_chxj_serf_post;
66 s_init(apr_pool_t *ppool, apr_pool_t **pool)
68 apr_pool_create(pool, ppool);
69 apr_atomic_init(*pool);
74 static serf_bucket_t *
75 s_connection_setup(apr_socket_t *skt, void *setup_ctx, apr_pool_t *UNUSED(pool))
78 app_ctx_t *ctx = (app_ctx_t *)setup_ctx;
80 c = serf_bucket_socket_create(skt, ctx->bkt_alloc);
82 c = serf_bucket_ssl_decrypt_create(c, ctx->ssl_ctx, ctx->bkt_alloc);
84 ctx->ssl_ctx = serf_bucket_ssl_decrypt_context_get(c);
85 serf_ssl_use_default_certificates(ctx->ssl_ctx);
86 serf_ssl_server_cert_callback_set(ctx->ssl_ctx, NULL, NULL);
95 s_connection_closed(serf_connection_t *UNUSED(conn), void *UNUSED(closed_baton), apr_status_t UNUSED(why), apr_pool_t *UNUSED(pool))
101 static serf_bucket_t *
102 s_accept_response(serf_request_t *request, serf_bucket_t *stream, void *UNUSED(acceptor_baton), apr_pool_t *UNUSED(pool))
104 serf_bucket_alloc_t *bkt_alloc;
107 bkt_alloc = serf_request_get_alloc(request);
108 c = serf_bucket_barrier_create(stream, bkt_alloc);
109 return serf_bucket_response_create(c, bkt_alloc);
114 s_handle_response(serf_request_t *UNUSED(request), serf_bucket_t *response, void *handler_ctx, apr_pool_t *UNUSED(pool))
120 handler_ctx_t *ctx = handler_ctx;
122 rv = serf_bucket_response_status(response, &sl);
123 if (rv != APR_SUCCESS) {
124 if (APR_STATUS_IS_EAGAIN(rv)) {
128 apr_atomic_dec32(&ctx->requests_outstanding);
131 ctx->reason = sl.reason;
134 rv = serf_bucket_read(response, 2048, &data, &len);
135 if (SERF_BUCKET_READ_ERROR(rv)) {
137 apr_atomic_dec32(&ctx->requests_outstanding);
138 DBG(ctx->r, "REQ[%X] end of s_handle_response() (ERROR)", (unsigned int)(apr_size_t)ctx->r);
141 if (APR_STATUS_IS_EAGAIN(rv)) {
142 /* 0 byte return if EAGAIN returned. */
143 DBG(ctx->r, "REQ[%X] end of s_handle_response() (EAGAIN) len:[%d]", (unsigned int)(apr_size_t)ctx->r, (int)len);
148 if (! ctx->response) {
149 ctx->response = apr_palloc(ctx->pool, len);
150 ctx->response[0] = 0;
151 ctx->response_len = 0;
154 char *tmp = apr_palloc(ctx->pool, ctx->response_len);
155 memcpy(tmp, ctx->response, ctx->response_len);
156 ctx->response = apr_palloc(ctx->pool, ctx->response_len + len);
157 memcpy(ctx->response, tmp, ctx->response_len);
159 memcpy(&ctx->response[ctx->response_len], data, len);
160 ctx->response_len += len;
161 ctx->response[ctx->response_len] = 0;
164 if (APR_STATUS_IS_EOF(rv)) {
166 char *tmp_headers = "";
167 hdrs = serf_bucket_response_get_headers(response);
169 rv = serf_bucket_read(hdrs, 2048, &data, &len);
170 if (SERF_BUCKET_READ_ERROR(rv))
172 tmp_headers = apr_pstrcat(ctx->pool, tmp_headers, apr_psprintf(ctx->pool , "%.*s", (unsigned int)len, data), NULL);
173 if (APR_STATUS_IS_EOF(rv)) {
177 ctx->headers_out = apr_table_make(ctx->pool, 0);
182 pair = apr_strtok(tmp_headers, "\n", &pstat);
188 char *tpair = apr_pstrdup(ctx->pool, pair);
190 val = strchr(tpair, ':');
194 key = qs_trim_string(ctx->pool, key);
195 val = qs_trim_string(ctx->pool, val);
196 DBG(ctx->r, "key:[%s], val:[%s]", key, val);
197 apr_table_add(ctx->headers_out, key, val);
200 ctx->rv = APR_SUCCESS;
201 apr_atomic_dec32(&ctx->requests_outstanding);
202 DBG(ctx->r, "REQ[%X] end of s_handle_response()(NORMAL)", (unsigned int)(apr_size_t)ctx->r);
206 if (APR_STATUS_IS_EAGAIN(rv)) {
207 DBG(ctx->r, "REQ[%X] end of s_handle_response() (EAGAIN)", (unsigned int)(apr_size_t)ctx->r);
214 s_setup_request(serf_request_t *request,
216 serf_bucket_t **req_bkt,
217 serf_response_acceptor_t *acceptor,
219 serf_response_handler_t *handler,
221 apr_pool_t *UNUSED(pool))
223 handler_ctx_t *ctx = setup_ctx;
224 serf_bucket_t *hdrs_bkt;
225 serf_bucket_t *body_bkt = NULL;
226 request_rec *r = ctx->r;
229 if (ctx->post_data) {
230 body_bkt = serf_bucket_simple_create(ctx->post_data, ctx->post_data_len, NULL, NULL, serf_request_get_alloc(request));
233 *req_bkt = serf_bucket_request_create(ctx->method, ctx->path, body_bkt, serf_request_get_alloc(request));
234 hdrs_bkt = serf_bucket_request_get_headers(*req_bkt);
237 apr_array_header_t *headers = (apr_array_header_t*)apr_table_elts(r->headers_in);
238 apr_table_entry_t *hentryp = (apr_table_entry_t*)headers->elts;
239 for (ii=headers->nelts-1; ii>=0; ii--) {
240 serf_bucket_headers_setc(hdrs_bkt, hentryp[ii].key, hentryp[ii].val);
241 DBG(ctx->r, "REQ[%X] REQUEST key:[%s], val:[%s]", (unsigned int)(apr_size_t)ctx->r, hentryp[ii].key, hentryp[ii].val);
243 if (ctx->post_data) {
244 serf_bucket_headers_setc(hdrs_bkt, "X-Chxj-Forward", "Done");
245 serf_bucket_headers_setc(hdrs_bkt, "X-Chxj-Content-Length", apr_psprintf(r->pool, "%" APR_SIZE_T_FMT , ctx->post_data_len));
246 DBG(ctx->r, "REQ[%X] REQUEST key:[%s], val:[%s]", (unsigned int)(apr_size_t)ctx->r, "X-Chxj-Forward", "Done");
247 DBG(ctx->r, "REQ[%X] REQUEST key:[%s], val:[%s]", (unsigned int)(apr_size_t)ctx->r, "X-Chxj-Content-Length", apr_psprintf(r->pool, "%" APR_SIZE_T_FMT, ctx->post_data_len));
250 DBG(ctx->r, "REQ[%X] REQUEST Content-Length:[%s]", (unsigned int)(apr_size_t)r, serf_bucket_headers_get(hdrs_bkt, "Content-Length"));
252 apr_atomic_inc32(&(ctx->requests_outstanding));
253 if (ctx->acceptor_ctx->ssl_flag) {
254 serf_bucket_alloc_t *req_alloc;
255 app_ctx_t *app_ctx = ctx->acceptor_ctx;
257 req_alloc = serf_request_get_alloc(request);
259 if (app_ctx->ssl_ctx == NULL) {
260 *req_bkt = serf_bucket_ssl_encrypt_create(*req_bkt, NULL, app_ctx->bkt_alloc);
261 app_ctx->ssl_ctx = serf_bucket_ssl_encrypt_context_get(*req_bkt);
264 *req_bkt = serf_bucket_ssl_encrypt_create(*req_bkt, app_ctx->ssl_ctx, app_ctx->bkt_alloc);
267 *acceptor = ctx->acceptor;
268 *acceptor_ctx = ctx->acceptor_ctx;
269 *handler = ctx->handler;
276 default_chxj_serf_get(request_rec *r, apr_pool_t *ppool, const char *url_path, int set_headers_flag, apr_size_t *response_len)
281 apr_sockaddr_t *address = NULL;
283 serf_context_t *context;
284 serf_connection_t *connection;
287 handler_ctx_t handler_ctx;
291 s_init(ppool, &pool);
293 apr_uri_parse(pool, url_path, &url);
295 url.port = apr_uri_port_of_scheme(url.scheme);
304 url.hostname = "localhost";
307 url.path = apr_psprintf(pool, "%s?%s", url.path, url.query);
310 rv = apr_sockaddr_info_get(&address, url.hostname, APR_UNSPEC, url.port, 0, pool);
311 if (rv != APR_SUCCESS) {
313 ERR(r, "apr_sockaddr_info_get() failed: rv:[%d|%s]", rv, apr_strerror(rv, buf, 256));
316 memset(&app_ctx, 0, sizeof(app_ctx_t));
318 app_ctx.bkt_alloc = serf_bucket_allocator_create(pool, NULL, NULL);
319 if (strcasecmp(url.scheme, "https") == 0) {
320 app_ctx.ssl_flag = 1;
323 context = serf_context_create(pool);
324 connection = serf_connection_create(context, address, s_connection_setup, &app_ctx, s_connection_closed, &app_ctx, pool);
326 memset(&handler_ctx, 0, sizeof(handler_ctx_t));
327 handler_ctx.requests_outstanding = 0;
328 handler_ctx.host = url.hostinfo;
329 handler_ctx.method = "GET";
330 handler_ctx.path = url.path;
331 handler_ctx.user_agent = (char *)apr_table_get(r->headers_in, "User-Agent");
332 handler_ctx.post_data = NULL;
333 handler_ctx.post_data_len = 0;
335 handler_ctx.acceptor = s_accept_response;
336 handler_ctx.acceptor_ctx = &app_ctx;
337 handler_ctx.handler = s_handle_response;
338 handler_ctx.pool = pool;
340 handler_ctx.response_len = 0;
341 handler_ctx.response = NULL;
343 serf_connection_request_create(connection, s_setup_request, &handler_ctx);
346 rv = serf_context_run(context, SERF_DURATION_FOREVER, pool);
347 if (APR_STATUS_IS_TIMEUP(rv))
351 ERR(r, "Error running context: (%d) %s\n", rv, apr_strerror(rv, buf, sizeof(buf)));
354 if (!apr_atomic_read32(&handler_ctx.requests_outstanding)) {
355 if (handler_ctx.rv != APR_SUCCESS) {
357 ERR(r, "Error running context: (%d) %s\n", handler_ctx.rv, apr_strerror(handler_ctx.rv, buf, sizeof(buf)));
363 serf_connection_close(connection);
364 ret = apr_pstrdup(ppool, handler_ctx.response);
365 if (set_headers_flag) {
366 r->headers_out = apr_table_copy(pool, handler_ctx.headers_out);
367 *response_len = handler_ctx.response_len;
368 char *contentType = (char *)apr_table_get(handler_ctx.headers_out, "Content-Type");
370 chxj_set_content_type(r, contentType);
378 default_chxj_serf_post(request_rec *r, apr_pool_t *ppool, const char *url_path, char *post_data, apr_size_t post_data_len, int set_headers_flag, apr_size_t *response_len)
383 apr_sockaddr_t *address = NULL;
385 serf_context_t *context;
386 serf_connection_t *connection;
389 handler_ctx_t handler_ctx;
392 DBG(r, "REQ:[%X] start chxj_serf_post()", (unsigned int)(apr_size_t)r);
395 s_init(ppool, &pool);
397 apr_uri_parse(pool, url_path, &url);
399 url.port = apr_uri_port_of_scheme(url.scheme);
408 url.hostname = "localhost";
411 url.path = apr_psprintf(pool, "%s?%s", url.path, url.query);
414 rv = apr_sockaddr_info_get(&address, url.hostname, APR_UNSPEC, url.port, 0, pool);
415 if (rv != APR_SUCCESS) {
417 ERR(r, "apr_sockaddr_info_get() failed: rv:[%d|%s]", rv, apr_strerror(rv, buf, 256));
420 memset(&app_ctx, 0, sizeof(app_ctx_t));
422 app_ctx.bkt_alloc = serf_bucket_allocator_create(pool, NULL, NULL);
423 if (strcasecmp(url.scheme, "https") == 0) {
424 app_ctx.ssl_flag = 1;
427 context = serf_context_create(pool);
428 connection = serf_connection_create(context, address, s_connection_setup, &app_ctx, s_connection_closed, &app_ctx, pool);
430 memset(&handler_ctx, 0, sizeof(handler_ctx_t));
431 handler_ctx.requests_outstanding = 0;
432 handler_ctx.host = url.hostinfo;
433 handler_ctx.method = "POST";
434 handler_ctx.path = url.path;
435 handler_ctx.user_agent = (char *)apr_table_get(r->headers_in, "User-Agent");
436 handler_ctx.post_data = post_data;
437 handler_ctx.post_data_len = post_data_len;
439 handler_ctx.acceptor = s_accept_response;
440 handler_ctx.acceptor_ctx = &app_ctx;
441 handler_ctx.handler = s_handle_response;
442 handler_ctx.pool = pool;
444 handler_ctx.response_len = 0;
445 handler_ctx.response = NULL;
447 serf_connection_request_create(connection, s_setup_request, &handler_ctx);
450 rv = serf_context_run(context, SERF_DURATION_FOREVER, pool);
451 if (APR_STATUS_IS_TIMEUP(rv))
455 ERR(r, "Error running context: (%d) %s\n", rv, apr_strerror(rv, buf, sizeof(buf)));
458 if (!apr_atomic_read32(&handler_ctx.requests_outstanding)) {
459 if (handler_ctx.rv != APR_SUCCESS) {
461 ERR(r, "Error running context: (%d) %s\n", handler_ctx.rv, apr_strerror(handler_ctx.rv, buf, sizeof(buf)));
467 DBG(r, "end of serf request");
468 DBG(r, "response:[%s][%" APR_SIZE_T_FMT "]", handler_ctx.response, handler_ctx.response_len);
469 serf_connection_close(connection);
470 ret = apr_pstrdup(ppool, handler_ctx.response);
471 if (set_headers_flag) {
472 r->headers_out = apr_table_copy(pool, handler_ctx.headers_out);
473 *response_len = handler_ctx.response_len;
474 char *contentType = (char *)apr_table_get(handler_ctx.headers_out, "Content-Type");
476 DBG(r, "response content type[%s]", contentType);
477 chxj_set_content_type(r, apr_pstrdup(r->pool, contentType));
480 DBG(r, "REQ:[%X] end chxj_serf_post()", (unsigned int)(apr_size_t)r);