OSDN Git Service

* updated copyright.
[modchxj/mod_chxj.git] / src / chxj_serf.c
1 /*
2  * Copyright (C) 2005-2011 Atsushi Konno All rights reserved.
3  * Copyright (C) 2005 QSDN,Inc. All rights reserved.
4  *
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
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17 #include "chxj_serf.h"
18 #include "mod_chxj.h"
19 #include "apr_pools.h"
20
21 typedef struct __app_ctx_t     app_ctx_t;
22 typedef struct __handler_ctx_t handler_ctx_t;
23
24 struct __app_ctx_t {
25   int                 ssl_flag;
26   serf_ssl_context_t  *ssl_ctx;
27   serf_bucket_alloc_t *bkt_alloc;
28 };
29
30 struct __handler_ctx_t {
31 #if APR_MAJOR_VERSION > 0
32   apr_uint32_t requests_outstanding;
33 #else
34   apr_atomic_t requests_outstanding;
35 #endif
36
37   serf_response_acceptor_t acceptor;
38   app_ctx_t                *acceptor_ctx;
39
40   serf_response_handler_t  handler;
41
42   const char *host;
43   const char *method;
44   const char *path;
45   const char *user_agent;
46
47   apr_status_t rv;
48   const char *reason;
49   int response_code;
50
51   char *response;
52   apr_size_t response_len;
53   char *post_data;
54   apr_size_t post_data_len;
55   apr_table_t *headers_out;
56   apr_pool_t *pool;
57   request_rec *r;
58 };
59
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;
66
67
68 void
69 s_init(apr_pool_t *ppool, apr_pool_t **pool)
70 {
71   apr_pool_create(pool, ppool);
72   apr_atomic_init(*pool);
73 }
74
75
76
77 static serf_bucket_t *
78 s_connection_setup(apr_socket_t *skt, void *setup_ctx, apr_pool_t *UNUSED(pool))
79 {
80   serf_bucket_t  *c;
81   app_ctx_t      *ctx = (app_ctx_t *)setup_ctx;
82
83   c = serf_bucket_socket_create(skt, ctx->bkt_alloc);
84   if (ctx->ssl_flag) {
85     c = serf_bucket_ssl_decrypt_create(c, ctx->ssl_ctx, ctx->bkt_alloc);
86     if (!ctx->ssl_ctx) {
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);
90     }
91     return c;
92   }
93   return c;
94 }
95
96
97 static void 
98 s_connection_closed(serf_connection_t *UNUSED(conn), void *UNUSED(closed_baton), apr_status_t UNUSED(why), apr_pool_t *UNUSED(pool))
99 {
100   /* nothing */
101 }
102
103
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))
106 {
107     serf_bucket_alloc_t *bkt_alloc;
108     serf_bucket_t       *c;
109
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);
113 }
114
115
116 static apr_status_t 
117 s_handle_response(serf_request_t *UNUSED(request), serf_bucket_t *response, void *handler_ctx, apr_pool_t *UNUSED(pool))
118 {
119   const char      *data;
120   apr_size_t      len;
121   serf_status_line sl;
122   apr_status_t     rv;
123   handler_ctx_t  *ctx = handler_ctx;
124   request_rec    *r;
125
126   r = ctx->r;
127
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__);
133       return rv;
134     }
135     ctx->rv = rv;
136     apr_atomic_dec32(&ctx->requests_outstanding); 
137     DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
138     return rv;
139   }
140   ctx->reason = sl.reason;
141   ctx->response_code = sl.code;
142
143   while (1) {
144     rv = serf_bucket_read(response, 2048, &data, &len);
145     if (SERF_BUCKET_READ_ERROR(rv)) {
146       ctx->rv = 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__);
150       return rv;
151     }
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__);
156       return rv;
157     }
158
159     if (len > 0) {
160       if (! ctx->response) {
161         ctx->response = apr_palloc(ctx->pool, len);
162         ctx->response[0] = 0;
163         ctx->response_len = 0;
164       }
165       else {
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);
170       }
171       memcpy(&ctx->response[ctx->response_len], data, len);
172       ctx->response_len += len;
173       ctx->response[ctx->response_len] = 0;
174     }
175     
176     if (APR_STATUS_IS_EOF(rv)) {
177       serf_bucket_t *hdrs;
178       char *tmp_headers = "";
179       hdrs = serf_bucket_response_get_headers(response);
180       while (1) {
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__);
185           return rv;
186         }
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)) {
189           break;
190         }
191       }
192       ctx->headers_out = apr_table_make(ctx->pool, 0);
193
194       char *pstat;
195       char *pair = NULL;
196       for (;;) {
197         pair = apr_strtok(tmp_headers, "\n", &pstat);
198         if (!pair) break;
199         tmp_headers = NULL;
200         char *key;
201         char *val;
202
203         char *tpair = apr_pstrdup(ctx->pool, pair);
204         key = tpair;
205         val = strchr(tpair, ':');
206         if (val) {
207           *val = 0;
208           val++;
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);
213         }
214       }
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__);
219       return APR_EOF;
220     }
221
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__);
225       return rv;
226     }
227   }
228 }
229
230 static apr_status_t 
231 s_setup_request(serf_request_t           *request,
232                 void                     *setup_ctx,
233                 serf_bucket_t            **req_bkt,
234                 serf_response_acceptor_t *acceptor,
235                 void                     **acceptor_ctx,
236                 serf_response_handler_t  *handler,
237                 void                     **handler_ctx,
238                 apr_pool_t               *UNUSED(pool))
239 {
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;
244   int ii;
245
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));
248   }
249
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);
252
253
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);
260   }
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));
268
269   }
270   DBG(ctx->r, "REQ[%X] REQUEST Content-Length:[%s]", TO_ADDR(ctx->r), serf_bucket_headers_get(hdrs_bkt, "Content-Length"));
271
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;
276
277     req_alloc = serf_request_get_alloc(request);
278
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);
282     }
283     else {
284       *req_bkt = serf_bucket_ssl_encrypt_create(*req_bkt, app_ctx->ssl_ctx, app_ctx->bkt_alloc);
285     }
286   }
287   *acceptor       = ctx->acceptor;
288   *acceptor_ctx   = ctx->acceptor_ctx;
289   *handler        = ctx->handler;
290   *handler_ctx    = ctx;
291
292   return APR_SUCCESS;
293 }
294
295 char *
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)
297 {
298   apr_pool_t *pool;
299   apr_uri_t url;
300   apr_status_t rv;
301   apr_sockaddr_t *address = NULL;
302
303   serf_context_t *context;
304   serf_connection_t *connection;
305
306   app_ctx_t app_ctx;
307   handler_ctx_t handler_ctx;
308   char *ret;
309
310
311   s_init(ppool, &pool);
312
313   apr_uri_parse(pool, url_path, &url);
314   if (!url.port) {
315     url.port = apr_uri_port_of_scheme(url.scheme);
316   }
317   if (!url.port) {
318     url.port = 80;
319   }
320   if (!url.path) {
321     url.path = "/";
322   }
323   if (!url.hostname) {
324     url.hostname = "localhost";
325   }
326   if (url.query) {
327     url.path = apr_psprintf(pool, "%s?%s", url.path, url.query);
328   }
329
330   rv = apr_sockaddr_info_get(&address, url.hostname, APR_UNSPEC, url.port, 0, pool);
331   if (rv != APR_SUCCESS) {
332     char buf[256];
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));
335     return NULL;
336   }
337   memset(&app_ctx, 0, sizeof(app_ctx_t));
338
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;
342   }
343
344   context = serf_context_create(pool);
345   connection = serf_connection_create(context, address, s_connection_setup, &app_ctx, s_connection_closed, &app_ctx, pool);
346
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);
355   }
356   handler_ctx.post_data = NULL;
357   handler_ctx.post_data_len = 0;
358
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;
363   handler_ctx.r            = r;
364   handler_ctx.response_len = 0;
365   handler_ctx.response     = NULL;
366
367   serf_connection_request_create(connection, s_setup_request, &handler_ctx);
368
369   while (1) {
370     rv = serf_context_run(context, SERF_DURATION_FOREVER, pool);
371     if (APR_STATUS_IS_TIMEUP(rv))
372       continue;
373     if (rv) {
374       char buf[200];
375       ERR(r, "Error running context: (%d) %s\n", rv, apr_strerror(rv, buf, sizeof(buf)));
376       break;
377     }
378     if (!apr_atomic_read32(&handler_ctx.requests_outstanding)) {
379       if (handler_ctx.rv != APR_SUCCESS) {
380         char buf[200];
381         ERR(r, "Error running context: (%d) %s\n", handler_ctx.rv, apr_strerror(handler_ctx.rv, buf, sizeof(buf)));
382       }
383       break;
384     }
385   }
386
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");
393     if (contentType) {
394       chxj_set_content_type(r, contentType);
395     }
396   }
397   return ret;
398 }
399
400
401 char *
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)
403 {
404   apr_pool_t *pool;
405   apr_uri_t url;
406   apr_status_t rv;
407   apr_sockaddr_t *address = NULL;
408
409   serf_context_t *context;
410   serf_connection_t *connection;
411
412   app_ctx_t app_ctx;
413   handler_ctx_t handler_ctx;
414   char *ret;
415
416   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
417
418
419   s_init(ppool, &pool);
420
421   apr_uri_parse(pool, url_path, &url);
422   if (!url.port) {
423     url.port = apr_uri_port_of_scheme(url.scheme);
424   }
425   if (!url.port) {
426     url.port = 80;
427   }
428   if (!url.path) {
429     url.path = "/";
430   }
431   if (!url.hostname) {
432     url.hostname = "localhost";
433   }
434   if (url.query) {
435     url.path = apr_psprintf(pool, "%s?%s", url.path, url.query);
436   }
437
438   rv = apr_sockaddr_info_get(&address, url.hostname, APR_UNSPEC, url.port, 0, pool);
439   if (rv != APR_SUCCESS) {
440     char buf[256];
441     ERR(r, "apr_sockaddr_info_get() failed: rv:[%d|%s]", rv, apr_strerror(rv, buf, 256));
442     return NULL;
443   }
444   memset(&app_ctx, 0, sizeof(app_ctx_t));
445
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;
449   }
450
451   context = serf_context_create(pool);
452   connection = serf_connection_create(context, address, s_connection_setup, &app_ctx, s_connection_closed, &app_ctx, pool);
453
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);
462   }
463   handler_ctx.post_data = post_data;
464   handler_ctx.post_data_len = post_data_len;
465
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;
470   handler_ctx.r            = r;
471   handler_ctx.response_len = 0;
472   handler_ctx.response     = NULL;
473
474   serf_connection_request_create(connection, s_setup_request, &handler_ctx);
475
476   while (1) {
477     rv = serf_context_run(context, SERF_DURATION_FOREVER, pool);
478     if (APR_STATUS_IS_TIMEUP(rv))
479       continue;
480     if (rv) {
481       char buf[200];
482       ERR(r, "Error running context: (%d) %s\n", rv, apr_strerror(rv, buf, sizeof(buf)));
483       break;
484     }
485     if (!apr_atomic_read32(&handler_ctx.requests_outstanding)) {
486       if (handler_ctx.rv != APR_SUCCESS) {
487         char buf[200];
488         ERR(r, "Error running context: (%d) %s\n", handler_ctx.rv, apr_strerror(handler_ctx.rv, buf, sizeof(buf)));
489       }
490       break;
491     }
492   }
493
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);
502   }
503   else {
504     ret = apr_pstrdup(ppool, "");
505   }
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");
510     if (contentType) {
511       DBG(r,"REQ[%X] response content type[%s]", TO_ADDR(r),contentType);
512       chxj_set_content_type(r, apr_pstrdup(r->pool, contentType));
513     }
514   }
515   if (rv) {
516     *response_len = 0;
517   }
518   *response_code = handler_ctx.response_code;
519   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
520   return ret;
521 }
522
523
524 apr_table_t *
525 default_chxj_serf_head(request_rec *r, apr_pool_t *ppool, const char *url_path, int *response_code)
526 {
527   apr_pool_t *pool;
528   apr_uri_t url;
529   apr_status_t rv;
530   apr_sockaddr_t *address = NULL;
531
532   serf_context_t *context;
533   serf_connection_t *connection;
534
535   app_ctx_t app_ctx;
536   handler_ctx_t handler_ctx;
537   char *ret;
538
539   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
540
541
542   s_init(ppool, &pool);
543
544   apr_uri_parse(pool, url_path, &url);
545   if (!url.port) {
546     url.port = apr_uri_port_of_scheme(url.scheme);
547   }
548   if (!url.port) {
549     url.port = 80;
550   }
551   if (!url.path) {
552     url.path = "/";
553   }
554   if (!url.hostname) {
555     url.hostname = "localhost";
556   }
557   if (url.query) {
558     url.path = apr_psprintf(pool, "%s?%s", url.path, url.query);
559   }
560
561   rv = apr_sockaddr_info_get(&address, url.hostname, APR_UNSPEC, url.port, 0, pool);
562   if (rv != APR_SUCCESS) {
563     char buf[256];
564     ERR(r, "apr_sockaddr_info_get() failed: rv:[%d|%s]", rv, apr_strerror(rv, buf, 256));
565     return NULL;
566   }
567   memset(&app_ctx, 0, sizeof(app_ctx_t));
568
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;
572   }
573
574   context = serf_context_create(pool);
575   connection = serf_connection_create(context, address, s_connection_setup, &app_ctx, s_connection_closed, &app_ctx, pool);
576
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);
588   }
589   handler_ctx.post_data     = NULL;
590   handler_ctx.post_data_len = 0;
591
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;
596   handler_ctx.r            = r;
597   handler_ctx.response_len = 0;
598   handler_ctx.response     = NULL;
599
600   serf_connection_request_create(connection, s_setup_request, &handler_ctx);
601
602   while (1) {
603     rv = serf_context_run(context, SERF_DURATION_FOREVER, pool);
604     if (APR_STATUS_IS_TIMEUP(rv))
605       continue;
606     if (rv) {
607       char buf[200];
608       ERR(r, "Error running context: (%d) %s\n", rv, apr_strerror(rv, buf, sizeof(buf)));
609       break;
610     }
611     if (!apr_atomic_read32(&handler_ctx.requests_outstanding)) {
612       if (handler_ctx.rv != APR_SUCCESS) {
613         char buf[200];
614         ERR(r, "Error running context: (%d) %s\n", handler_ctx.rv, apr_strerror(handler_ctx.rv, buf, sizeof(buf)));
615       }
616       break;
617     }
618   }
619
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);
626   }
627   else {
628     ret = apr_pstrdup(ppool, "");
629   }
630   *response_code = handler_ctx.response_code;
631   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
632   return handler_ctx.headers_out;
633 }
634 /*
635  * vim:ts=2 et
636  */