2 * Copyright (C) 2005-2011 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;
52 apr_size_t response_len;
54 apr_size_t post_data_len;
55 apr_table_t *headers_out;
60 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);
61 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;
62 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, int *response_code);
63 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, int *response_code) = default_chxj_serf_post;
64 apr_table_t *default_chxj_serf_head(request_rec *r, apr_pool_t *ppool, const char *url_path, int *response_code);
65 apr_table_t *(*chxj_serf_head)(request_rec *r, apr_pool_t *ppool, const char *url_path, int *response_code) = default_chxj_serf_head;
69 s_init(apr_pool_t *ppool, apr_pool_t **pool)
71 apr_pool_create(pool, ppool);
72 apr_atomic_init(*pool);
77 static serf_bucket_t *
78 s_connection_setup(apr_socket_t *skt, void *setup_ctx, apr_pool_t *UNUSED(pool))
81 app_ctx_t *ctx = (app_ctx_t *)setup_ctx;
83 c = serf_bucket_socket_create(skt, ctx->bkt_alloc);
85 c = serf_bucket_ssl_decrypt_create(c, ctx->ssl_ctx, ctx->bkt_alloc);
87 ctx->ssl_ctx = serf_bucket_ssl_decrypt_context_get(c);
88 serf_ssl_use_default_certificates(ctx->ssl_ctx);
89 serf_ssl_server_cert_callback_set(ctx->ssl_ctx, NULL, NULL);
98 s_connection_closed(serf_connection_t *UNUSED(conn), void *UNUSED(closed_baton), apr_status_t UNUSED(why), apr_pool_t *UNUSED(pool))
104 static serf_bucket_t *
105 s_accept_response(serf_request_t *request, serf_bucket_t *stream, void *UNUSED(acceptor_baton), apr_pool_t *UNUSED(pool))
107 serf_bucket_alloc_t *bkt_alloc;
110 bkt_alloc = serf_request_get_alloc(request);
111 c = serf_bucket_barrier_create(stream, bkt_alloc);
112 return serf_bucket_response_create(c, bkt_alloc);
117 s_handle_response(serf_request_t *UNUSED(request), serf_bucket_t *response, void *handler_ctx, apr_pool_t *UNUSED(pool))
123 handler_ctx_t *ctx = handler_ctx;
128 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
129 rv = serf_bucket_response_status(response, &sl);
130 if (rv != APR_SUCCESS) {
131 if (APR_STATUS_IS_EAGAIN(rv)) {
132 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
136 apr_atomic_dec32(&ctx->requests_outstanding);
137 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
140 ctx->reason = sl.reason;
141 ctx->response_code = sl.code;
144 rv = serf_bucket_read(response, 2048, &data, &len);
145 if (SERF_BUCKET_READ_ERROR(rv)) {
147 apr_atomic_dec32(&ctx->requests_outstanding);
148 DBG(r,"REQ[%X] BACKET READ ERROR", TO_ADDR(r));
149 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
152 if (APR_STATUS_IS_EAGAIN(rv)) {
153 /* 0 byte return if EAGAIN returned. */
154 DBG(r,"REQ[%X] EAGAIN len:[%d]", TO_ADDR(r), (int)len);
155 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
160 if (! ctx->response) {
161 ctx->response = apr_palloc(ctx->pool, len);
162 ctx->response[0] = 0;
163 ctx->response_len = 0;
166 char *tmp = apr_palloc(ctx->pool, ctx->response_len);
167 memcpy(tmp, ctx->response, ctx->response_len);
168 ctx->response = apr_palloc(ctx->pool, ctx->response_len + len);
169 memcpy(ctx->response, tmp, ctx->response_len);
171 memcpy(&ctx->response[ctx->response_len], data, len);
172 ctx->response_len += len;
173 ctx->response[ctx->response_len] = 0;
176 if (APR_STATUS_IS_EOF(rv)) {
178 char *tmp_headers = "";
179 hdrs = serf_bucket_response_get_headers(response);
181 rv = serf_bucket_read(hdrs, 2048, &data, &len);
182 if (SERF_BUCKET_READ_ERROR(rv)) {
183 DBG(r,"REQ[%X] bucket read error",TO_ADDR(r));
184 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
187 tmp_headers = apr_pstrcat(ctx->pool, tmp_headers, apr_psprintf(ctx->pool , "%.*s", (unsigned int)len, data), NULL);
188 if (APR_STATUS_IS_EOF(rv)) {
192 ctx->headers_out = apr_table_make(ctx->pool, 0);
197 pair = apr_strtok(tmp_headers, "\n", &pstat);
203 char *tpair = apr_pstrdup(ctx->pool, pair);
205 val = strchr(tpair, ':');
209 key = qs_trim_string(ctx->pool, key);
210 val = qs_trim_string(ctx->pool, val);
211 DBG(r,"REQ[%X] key:[%s], val:[%s]", TO_ADDR(r), key, val);
212 apr_table_add(ctx->headers_out, key, val);
215 ctx->rv = APR_SUCCESS;
216 apr_atomic_dec32(&ctx->requests_outstanding);
217 DBG(r,"REQ[%X] NORMAL", TO_ADDR(r));
218 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
222 if (APR_STATUS_IS_EAGAIN(rv)) {
223 DBG(r,"REQ[%X] EAGAIN", TO_ADDR(r));
224 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
231 s_setup_request(serf_request_t *request,
233 serf_bucket_t **req_bkt,
234 serf_response_acceptor_t *acceptor,
236 serf_response_handler_t *handler,
238 apr_pool_t *UNUSED(pool))
240 handler_ctx_t *ctx = setup_ctx;
241 serf_bucket_t *hdrs_bkt;
242 serf_bucket_t *body_bkt = NULL;
243 request_rec *r = ctx->r;
246 if (ctx->post_data) {
247 body_bkt = serf_bucket_simple_create(ctx->post_data, ctx->post_data_len, NULL, NULL, serf_request_get_alloc(request));
250 *req_bkt = serf_bucket_request_create(ctx->method, ctx->path, body_bkt, serf_request_get_alloc(request));
251 hdrs_bkt = serf_bucket_request_get_headers(*req_bkt);
254 apr_array_header_t *headers = (apr_array_header_t*)apr_table_elts(r->headers_in);
255 apr_table_entry_t *hentryp = (apr_table_entry_t*)headers->elts;
256 for (ii=headers->nelts-1; ii>=0; ii--) {
257 DBG(ctx->r, "REQ[%X] REQUEST PREV key:[%s], val:[%s]", TO_ADDR(ctx->r), hentryp[ii].key, hentryp[ii].val);
258 serf_bucket_headers_setc(hdrs_bkt, hentryp[ii].key, (hentryp[ii].val) ? hentryp[ii].val : "");
259 DBG(ctx->r, "REQ[%X] REQUEST AFTER key:[%s], val:[%s]", TO_ADDR(ctx->r), hentryp[ii].key, hentryp[ii].val);
261 if (ctx->post_data) {
262 DBG(ctx->r, "REQ[%X] REQUEST PREV key:[%s], val:[%s]", TO_ADDR(ctx->r), "X-Chxj-Forward", "Done");
263 serf_bucket_headers_setc(hdrs_bkt, "X-Chxj-Forward", "Done");
264 DBG(ctx->r, "REQ[%X] REQUEST AFTER key:[%s], val:[%s]", TO_ADDR(ctx->r), "X-Chxj-Forward", "Done");
265 DBG(ctx->r, "REQ[%X] REQUEST PREV key:[%s], val:[%s]", TO_ADDR(ctx->r), "X-Chxj-Content-Length", apr_psprintf(r->pool, "%" APR_SIZE_T_FMT, ctx->post_data_len));
266 serf_bucket_headers_setc(hdrs_bkt, "X-Chxj-Content-Length", apr_psprintf(r->pool, "%" APR_SIZE_T_FMT , ctx->post_data_len));
267 DBG(ctx->r, "REQ[%X] REQUEST AFTER key:[%s], val:[%s]", TO_ADDR(ctx->r), "X-Chxj-Content-Length", apr_psprintf(r->pool, "%" APR_SIZE_T_FMT, ctx->post_data_len));
270 DBG(ctx->r, "REQ[%X] REQUEST Content-Length:[%s]", TO_ADDR(ctx->r), serf_bucket_headers_get(hdrs_bkt, "Content-Length"));
272 apr_atomic_inc32(&(ctx->requests_outstanding));
273 if (ctx->acceptor_ctx->ssl_flag) {
274 serf_bucket_alloc_t *req_alloc;
275 app_ctx_t *app_ctx = ctx->acceptor_ctx;
277 req_alloc = serf_request_get_alloc(request);
279 if (app_ctx->ssl_ctx == NULL) {
280 *req_bkt = serf_bucket_ssl_encrypt_create(*req_bkt, NULL, app_ctx->bkt_alloc);
281 app_ctx->ssl_ctx = serf_bucket_ssl_encrypt_context_get(*req_bkt);
284 *req_bkt = serf_bucket_ssl_encrypt_create(*req_bkt, app_ctx->ssl_ctx, app_ctx->bkt_alloc);
287 *acceptor = ctx->acceptor;
288 *acceptor_ctx = ctx->acceptor_ctx;
289 *handler = ctx->handler;
296 default_chxj_serf_get(request_rec *r, apr_pool_t *ppool, const char *url_path, int set_headers_flag, apr_size_t *response_len)
301 apr_sockaddr_t *address = NULL;
303 serf_context_t *context;
304 serf_connection_t *connection;
307 handler_ctx_t handler_ctx;
311 s_init(ppool, &pool);
313 apr_uri_parse(pool, url_path, &url);
315 url.port = apr_uri_port_of_scheme(url.scheme);
324 url.hostname = "localhost";
327 url.path = apr_psprintf(pool, "%s?%s", url.path, url.query);
330 rv = apr_sockaddr_info_get(&address, url.hostname, APR_UNSPEC, url.port, 0, pool);
331 if (rv != APR_SUCCESS) {
333 ERR(r, "REQ[%X] %s:%d apr_sockaddr_info_get() failed: rv:[%d|%s] - Please check DNS settings.",
334 (unsigned int)(apr_size_t)r, __FILE__,__LINE__, rv, apr_strerror(rv, buf, 256));
337 memset(&app_ctx, 0, sizeof(app_ctx_t));
339 app_ctx.bkt_alloc = serf_bucket_allocator_create(pool, NULL, NULL);
340 if (strcasecmp(url.scheme, "https") == 0) {
341 app_ctx.ssl_flag = 1;
344 context = serf_context_create(pool);
345 connection = serf_connection_create(context, address, s_connection_setup, &app_ctx, s_connection_closed, &app_ctx, pool);
347 memset(&handler_ctx, 0, sizeof(handler_ctx_t));
348 handler_ctx.requests_outstanding = 0;
349 handler_ctx.host = url.hostinfo;
350 handler_ctx.method = "GET";
351 handler_ctx.path = url.path;
352 handler_ctx.user_agent = (char *)apr_table_get(r->headers_in, CHXJ_HTTP_USER_AGENT);
353 if (!handler_ctx.user_agent) {
354 handler_ctx.user_agent = (char *)apr_table_get(r->headers_in, HTTP_USER_AGENT);
356 handler_ctx.post_data = NULL;
357 handler_ctx.post_data_len = 0;
359 handler_ctx.acceptor = s_accept_response;
360 handler_ctx.acceptor_ctx = &app_ctx;
361 handler_ctx.handler = s_handle_response;
362 handler_ctx.pool = pool;
364 handler_ctx.response_len = 0;
365 handler_ctx.response = NULL;
367 serf_connection_request_create(connection, s_setup_request, &handler_ctx);
370 rv = serf_context_run(context, SERF_DURATION_FOREVER, pool);
371 if (APR_STATUS_IS_TIMEUP(rv))
375 ERR(r, "Error running context: (%d) %s\n", rv, apr_strerror(rv, buf, sizeof(buf)));
378 if (!apr_atomic_read32(&handler_ctx.requests_outstanding)) {
379 if (handler_ctx.rv != APR_SUCCESS) {
381 ERR(r, "Error running context: (%d) %s\n", handler_ctx.rv, apr_strerror(handler_ctx.rv, buf, sizeof(buf)));
387 serf_connection_close(connection);
388 ret = apr_pstrdup(ppool, handler_ctx.response);
389 if (set_headers_flag) {
390 r->headers_out = apr_table_copy(pool, handler_ctx.headers_out);
391 *response_len = handler_ctx.response_len;
392 char *contentType = (char *)apr_table_get(handler_ctx.headers_out, "Content-Type");
394 chxj_set_content_type(r, contentType);
402 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, int *response_code)
407 apr_sockaddr_t *address = NULL;
409 serf_context_t *context;
410 serf_connection_t *connection;
413 handler_ctx_t handler_ctx;
416 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
419 s_init(ppool, &pool);
421 apr_uri_parse(pool, url_path, &url);
423 url.port = apr_uri_port_of_scheme(url.scheme);
432 url.hostname = "localhost";
435 url.path = apr_psprintf(pool, "%s?%s", url.path, url.query);
438 rv = apr_sockaddr_info_get(&address, url.hostname, APR_UNSPEC, url.port, 0, pool);
439 if (rv != APR_SUCCESS) {
441 ERR(r, "apr_sockaddr_info_get() failed: rv:[%d|%s]", rv, apr_strerror(rv, buf, 256));
444 memset(&app_ctx, 0, sizeof(app_ctx_t));
446 app_ctx.bkt_alloc = serf_bucket_allocator_create(pool, NULL, NULL);
447 if (strcasecmp(url.scheme, "https") == 0) {
448 app_ctx.ssl_flag = 1;
451 context = serf_context_create(pool);
452 connection = serf_connection_create(context, address, s_connection_setup, &app_ctx, s_connection_closed, &app_ctx, pool);
454 memset(&handler_ctx, 0, sizeof(handler_ctx_t));
455 handler_ctx.requests_outstanding = 0;
456 handler_ctx.host = url.hostinfo;
457 handler_ctx.method = "POST";
458 handler_ctx.path = url.path;
459 handler_ctx.user_agent = (char *)apr_table_get(r->headers_in, CHXJ_HTTP_USER_AGENT);
460 if (! handler_ctx.user_agent) {
461 handler_ctx.user_agent = (char *)apr_table_get(r->headers_in, HTTP_USER_AGENT);
463 handler_ctx.post_data = post_data;
464 handler_ctx.post_data_len = post_data_len;
466 handler_ctx.acceptor = s_accept_response;
467 handler_ctx.acceptor_ctx = &app_ctx;
468 handler_ctx.handler = s_handle_response;
469 handler_ctx.pool = pool;
471 handler_ctx.response_len = 0;
472 handler_ctx.response = NULL;
474 serf_connection_request_create(connection, s_setup_request, &handler_ctx);
477 rv = serf_context_run(context, SERF_DURATION_FOREVER, pool);
478 if (APR_STATUS_IS_TIMEUP(rv))
482 ERR(r, "Error running context: (%d) %s\n", rv, apr_strerror(rv, buf, sizeof(buf)));
485 if (!apr_atomic_read32(&handler_ctx.requests_outstanding)) {
486 if (handler_ctx.rv != APR_SUCCESS) {
488 ERR(r, "Error running context: (%d) %s\n", handler_ctx.rv, apr_strerror(handler_ctx.rv, buf, sizeof(buf)));
494 DBG(r,"REQ[%X] end of serf request", TO_ADDR(r));
495 DBG(r,"REQ[%X] response_code:[%d]", TO_ADDR(r),handler_ctx.response_code);
496 DBG(r,"REQ[%X] response:[%s][%" APR_SIZE_T_FMT "]", TO_ADDR(r),handler_ctx.response, handler_ctx.response_len);
497 serf_connection_close(connection);
498 if (handler_ctx.response) {
499 ret = apr_palloc(ppool, handler_ctx.response_len + 1);
500 memset(ret, 0, handler_ctx.response_len + 1);
501 memcpy(ret, handler_ctx.response, handler_ctx.response_len);
504 ret = apr_pstrdup(ppool, "");
506 if (set_headers_flag && !rv) {
507 r->headers_out = apr_table_copy(pool, handler_ctx.headers_out);
508 *response_len = handler_ctx.response_len;
509 char *contentType = (char *)apr_table_get(handler_ctx.headers_out, "Content-Type");
511 DBG(r,"REQ[%X] response content type[%s]", TO_ADDR(r),contentType);
512 chxj_set_content_type(r, apr_pstrdup(r->pool, contentType));
518 *response_code = handler_ctx.response_code;
519 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
525 default_chxj_serf_head(request_rec *r, apr_pool_t *ppool, const char *url_path, int *response_code)
530 apr_sockaddr_t *address = NULL;
532 serf_context_t *context;
533 serf_connection_t *connection;
536 handler_ctx_t handler_ctx;
539 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
542 s_init(ppool, &pool);
544 apr_uri_parse(pool, url_path, &url);
546 url.port = apr_uri_port_of_scheme(url.scheme);
555 url.hostname = "localhost";
558 url.path = apr_psprintf(pool, "%s?%s", url.path, url.query);
561 rv = apr_sockaddr_info_get(&address, url.hostname, APR_UNSPEC, url.port, 0, pool);
562 if (rv != APR_SUCCESS) {
564 ERR(r, "apr_sockaddr_info_get() failed: rv:[%d|%s]", rv, apr_strerror(rv, buf, 256));
567 memset(&app_ctx, 0, sizeof(app_ctx_t));
569 app_ctx.bkt_alloc = serf_bucket_allocator_create(pool, NULL, NULL);
570 if (strcasecmp(url.scheme, "https") == 0) {
571 app_ctx.ssl_flag = 1;
574 context = serf_context_create(pool);
575 connection = serf_connection_create(context, address, s_connection_setup, &app_ctx, s_connection_closed, &app_ctx, pool);
577 memset(&handler_ctx, 0, sizeof(handler_ctx_t));
578 handler_ctx.requests_outstanding = 0;
579 handler_ctx.host = url.hostinfo;
580 /*========================================================================================================*/
581 /* XXX Maybe, libserf doesn't support the HEAD request. Because the part body is waited for with polling. */
582 /*========================================================================================================*/
583 handler_ctx.method = "GET";
584 handler_ctx.path = url.path;
585 handler_ctx.user_agent = (char *)apr_table_get(r->headers_in, CHXJ_HTTP_USER_AGENT);
586 if (! handler_ctx.user_agent) {
587 handler_ctx.user_agent = (char *)apr_table_get(r->headers_in, HTTP_USER_AGENT);
589 handler_ctx.post_data = NULL;
590 handler_ctx.post_data_len = 0;
592 handler_ctx.acceptor = s_accept_response;
593 handler_ctx.acceptor_ctx = &app_ctx;
594 handler_ctx.handler = s_handle_response;
595 handler_ctx.pool = pool;
597 handler_ctx.response_len = 0;
598 handler_ctx.response = NULL;
600 serf_connection_request_create(connection, s_setup_request, &handler_ctx);
603 rv = serf_context_run(context, SERF_DURATION_FOREVER, pool);
604 if (APR_STATUS_IS_TIMEUP(rv))
608 ERR(r, "Error running context: (%d) %s\n", rv, apr_strerror(rv, buf, sizeof(buf)));
611 if (!apr_atomic_read32(&handler_ctx.requests_outstanding)) {
612 if (handler_ctx.rv != APR_SUCCESS) {
614 ERR(r, "Error running context: (%d) %s\n", handler_ctx.rv, apr_strerror(handler_ctx.rv, buf, sizeof(buf)));
620 DBG(r,"REQ[%X] end of serf request", TO_ADDR(r));
621 DBG(r,"REQ[%X] response_code:[%d]", TO_ADDR(r),handler_ctx.response_code);
622 DBG(r,"REQ[%X] response:[%s][%" APR_SIZE_T_FMT "]", TO_ADDR(r),handler_ctx.response, handler_ctx.response_len);
623 serf_connection_close(connection);
624 if (handler_ctx.response) {
625 ret = apr_pstrdup(ppool, handler_ctx.response);
628 ret = apr_pstrdup(ppool, "");
630 *response_code = handler_ctx.response_code;
631 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
632 return handler_ctx.headers_out;