OSDN Git Service

* Changed DEBUG Log.
[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
125   rv = serf_bucket_response_status(response, &sl);
126   if (rv != APR_SUCCESS) {
127     if (APR_STATUS_IS_EAGAIN(rv)) {
128       return rv;
129     }
130     ctx->rv = rv;
131     apr_atomic_dec32(&ctx->requests_outstanding); 
132     return rv;
133   }
134   ctx->reason = sl.reason;
135   ctx->response_code = sl.code;
136
137   while (1) {
138     rv = serf_bucket_read(response, 2048, &data, &len);
139     if (SERF_BUCKET_READ_ERROR(rv)) {
140       ctx->rv = rv;
141       apr_atomic_dec32(&ctx->requests_outstanding);
142       DBG(ctx->r, "REQ[%X] (ERROR)", TO_ADDR(ctx->r));
143       DBG(ctx->r,"REQ[%X] end %s()",TO_ADDR(ctx->r),__func__);
144       return rv;
145     }
146     if (APR_STATUS_IS_EAGAIN(rv)) {
147       /* 0 byte return if EAGAIN returned. */
148       DBG(ctx->r,"REQ[%X] (EAGAIN) len:[%d]", TO_ADDR(ctx->r), (int)len);
149       DBG(ctx->r,"REQ[%X] end %s()",TO_ADDR(ctx->r),__func__);
150       return rv;
151     }
152
153     if (len > 0) {
154       if (! ctx->response) {
155         ctx->response = apr_palloc(ctx->pool, len);
156         ctx->response[0] = 0;
157         ctx->response_len = 0;
158       }
159       else {
160         char *tmp = apr_palloc(ctx->pool, ctx->response_len);
161         memcpy(tmp, ctx->response, ctx->response_len);
162         ctx->response = apr_palloc(ctx->pool, ctx->response_len + len);
163         memcpy(ctx->response, tmp, ctx->response_len);
164       }
165       memcpy(&ctx->response[ctx->response_len], data, len);
166       ctx->response_len += len;
167       ctx->response[ctx->response_len] = 0;
168     }
169     
170     if (APR_STATUS_IS_EOF(rv)) {
171       serf_bucket_t *hdrs;
172       char *tmp_headers = "";
173       hdrs = serf_bucket_response_get_headers(response);
174       while (1) {
175         rv = serf_bucket_read(hdrs, 2048, &data, &len);
176         if (SERF_BUCKET_READ_ERROR(rv))
177           return rv;
178         tmp_headers = apr_pstrcat(ctx->pool, tmp_headers, apr_psprintf(ctx->pool , "%.*s", (unsigned int)len, data), NULL);
179         if (APR_STATUS_IS_EOF(rv)) {
180           break;
181         }
182       }
183       ctx->headers_out = apr_table_make(ctx->pool, 0);
184
185       char *pstat;
186       char *pair = NULL;
187       for (;;) {
188         pair = apr_strtok(tmp_headers, "\n", &pstat);
189         if (!pair) break;
190         tmp_headers = NULL;
191         char *key;
192         char *val;
193
194         char *tpair = apr_pstrdup(ctx->pool, pair);
195         key = tpair;
196         val = strchr(tpair, ':');
197         if (val) {
198           *val = 0;
199           val++;
200           key = qs_trim_string(ctx->pool, key);
201           val = qs_trim_string(ctx->pool, val);
202           DBG(ctx->r,"REQ[%X] key:[%s], val:[%s]", TO_ADDR(ctx->r),key, val);
203           apr_table_add(ctx->headers_out, key, val);
204         }
205       }
206       ctx->rv = APR_SUCCESS;
207       apr_atomic_dec32(&ctx->requests_outstanding);
208       DBG(ctx->r,"REQ[%X] (NORMAL)", TO_ADDR(ctx->r));
209       DBG(ctx->r,"REQ[%X] end %s()",TO_ADDR(ctx->r),__func__);
210       return APR_EOF;
211     }
212
213     if (APR_STATUS_IS_EAGAIN(rv)) {
214       DBG(ctx->r,"REQ[%X] (EAGAIN)", TO_ADDR(ctx->r));
215       DBG(ctx->r,"REQ[%X] end %s()",TO_ADDR(ctx->r),__func__);
216       return rv;
217     }
218   }
219 }
220
221 static apr_status_t 
222 s_setup_request(serf_request_t           *request,
223                 void                     *setup_ctx,
224                 serf_bucket_t            **req_bkt,
225                 serf_response_acceptor_t *acceptor,
226                 void                     **acceptor_ctx,
227                 serf_response_handler_t  *handler,
228                 void                     **handler_ctx,
229                 apr_pool_t               *UNUSED(pool))
230 {
231   handler_ctx_t *ctx = setup_ctx;
232   serf_bucket_t *hdrs_bkt;
233   serf_bucket_t *body_bkt = NULL;
234   request_rec *r = ctx->r;
235   int ii;
236
237   if (ctx->post_data) {
238     body_bkt = serf_bucket_simple_create(ctx->post_data, ctx->post_data_len, NULL, NULL, serf_request_get_alloc(request));
239   }
240
241   *req_bkt = serf_bucket_request_create(ctx->method, ctx->path, body_bkt, serf_request_get_alloc(request));
242   hdrs_bkt = serf_bucket_request_get_headers(*req_bkt);
243
244
245   apr_array_header_t *headers = (apr_array_header_t*)apr_table_elts(r->headers_in);
246   apr_table_entry_t  *hentryp = (apr_table_entry_t*)headers->elts;
247   for (ii=headers->nelts-1; ii>=0; ii--) {
248     DBG(ctx->r, "REQ[%X] REQUEST PREV key:[%s], val:[%s]", TO_ADDR(ctx->r), hentryp[ii].key, hentryp[ii].val);
249     serf_bucket_headers_setc(hdrs_bkt, hentryp[ii].key, (hentryp[ii].val) ? hentryp[ii].val : "");
250     DBG(ctx->r, "REQ[%X] REQUEST AFTER key:[%s], val:[%s]", TO_ADDR(ctx->r), hentryp[ii].key, hentryp[ii].val);
251   }
252   if (ctx->post_data) {
253     DBG(ctx->r, "REQ[%X] REQUEST PREV key:[%s], val:[%s]", TO_ADDR(ctx->r), "X-Chxj-Forward", "Done");
254     serf_bucket_headers_setc(hdrs_bkt, "X-Chxj-Forward", "Done");
255     DBG(ctx->r, "REQ[%X] REQUEST AFTER key:[%s], val:[%s]", TO_ADDR(ctx->r), "X-Chxj-Forward", "Done");
256     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));
257     serf_bucket_headers_setc(hdrs_bkt, "X-Chxj-Content-Length", apr_psprintf(r->pool, "%" APR_SIZE_T_FMT , ctx->post_data_len));
258     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));
259   }
260   DBG(ctx->r, "REQ[%X] REQUEST Content-Length:[%s]", TO_ADDR(ctx->r), serf_bucket_headers_get(hdrs_bkt, "Content-Length"));
261
262   apr_atomic_inc32(&(ctx->requests_outstanding));
263   if (ctx->acceptor_ctx->ssl_flag) {
264     serf_bucket_alloc_t *req_alloc;
265     app_ctx_t *app_ctx = ctx->acceptor_ctx;
266
267     req_alloc = serf_request_get_alloc(request);
268
269     if (app_ctx->ssl_ctx == NULL) {
270       *req_bkt = serf_bucket_ssl_encrypt_create(*req_bkt, NULL, app_ctx->bkt_alloc);
271       app_ctx->ssl_ctx = serf_bucket_ssl_encrypt_context_get(*req_bkt);
272     }
273     else {
274       *req_bkt = serf_bucket_ssl_encrypt_create(*req_bkt, app_ctx->ssl_ctx, app_ctx->bkt_alloc);
275     }
276   }
277   *acceptor       = ctx->acceptor;
278   *acceptor_ctx   = ctx->acceptor_ctx;
279   *handler        = ctx->handler;
280   *handler_ctx    = ctx;
281
282   return APR_SUCCESS;
283 }
284
285 char *
286 default_chxj_serf_get(request_rec *r, apr_pool_t *ppool, const char *url_path, int set_headers_flag, apr_size_t *response_len)
287 {
288   apr_pool_t *pool;
289   apr_uri_t url;
290   apr_status_t rv;
291   apr_sockaddr_t *address = NULL;
292
293   serf_context_t *context;
294   serf_connection_t *connection;
295
296   app_ctx_t app_ctx;
297   handler_ctx_t handler_ctx;
298   char *ret;
299
300
301   s_init(ppool, &pool);
302
303   apr_uri_parse(pool, url_path, &url);
304   if (!url.port) {
305     url.port = apr_uri_port_of_scheme(url.scheme);
306   }
307   if (!url.port) {
308     url.port = 80;
309   }
310   if (!url.path) {
311     url.path = "/";
312   }
313   if (!url.hostname) {
314     url.hostname = "localhost";
315   }
316   if (url.query) {
317     url.path = apr_psprintf(pool, "%s?%s", url.path, url.query);
318   }
319
320   rv = apr_sockaddr_info_get(&address, url.hostname, APR_UNSPEC, url.port, 0, pool);
321   if (rv != APR_SUCCESS) {
322     char buf[256];
323     ERR(r, "REQ[%X] %s:%d apr_sockaddr_info_get() failed: rv:[%d|%s] - Please check DNS settings.", 
324            (unsigned int)(apr_size_t)r, __FILE__,__LINE__, rv, apr_strerror(rv, buf, 256));
325     return NULL;
326   }
327   memset(&app_ctx, 0, sizeof(app_ctx_t));
328
329   app_ctx.bkt_alloc = serf_bucket_allocator_create(pool, NULL, NULL);
330   if (strcasecmp(url.scheme, "https") == 0) {
331     app_ctx.ssl_flag = 1;
332   }
333
334   context = serf_context_create(pool);
335   connection = serf_connection_create(context, address, s_connection_setup, &app_ctx, s_connection_closed, &app_ctx, pool);
336
337   memset(&handler_ctx, 0, sizeof(handler_ctx_t));
338   handler_ctx.requests_outstanding = 0;
339   handler_ctx.host = url.hostinfo;
340   handler_ctx.method = "GET";
341   handler_ctx.path = url.path;
342   handler_ctx.user_agent = (char *)apr_table_get(r->headers_in, CHXJ_HTTP_USER_AGENT);
343   if (!handler_ctx.user_agent) {
344     handler_ctx.user_agent = (char *)apr_table_get(r->headers_in, HTTP_USER_AGENT);
345   }
346   handler_ctx.post_data = NULL;
347   handler_ctx.post_data_len = 0;
348
349   handler_ctx.acceptor     = s_accept_response;
350   handler_ctx.acceptor_ctx = &app_ctx;
351   handler_ctx.handler      = s_handle_response;
352   handler_ctx.pool         = pool;
353   handler_ctx.r            = r;
354   handler_ctx.response_len = 0;
355   handler_ctx.response     = NULL;
356
357   serf_connection_request_create(connection, s_setup_request, &handler_ctx);
358
359   while (1) {
360     rv = serf_context_run(context, SERF_DURATION_FOREVER, pool);
361     if (APR_STATUS_IS_TIMEUP(rv))
362       continue;
363     if (rv) {
364       char buf[200];
365       ERR(r, "Error running context: (%d) %s\n", rv, apr_strerror(rv, buf, sizeof(buf)));
366       break;
367     }
368     if (!apr_atomic_read32(&handler_ctx.requests_outstanding)) {
369       if (handler_ctx.rv != APR_SUCCESS) {
370         char buf[200];
371         ERR(r, "Error running context: (%d) %s\n", handler_ctx.rv, apr_strerror(handler_ctx.rv, buf, sizeof(buf)));
372       }
373       break;
374     }
375   }
376
377   serf_connection_close(connection);
378   ret = apr_pstrdup(ppool, handler_ctx.response);
379   if (set_headers_flag) {
380     r->headers_out = apr_table_copy(pool, handler_ctx.headers_out);
381     *response_len = handler_ctx.response_len;
382     char *contentType = (char *)apr_table_get(handler_ctx.headers_out, "Content-Type");
383     if (contentType) {
384       chxj_set_content_type(r, contentType);
385     }
386   }
387   return ret;
388 }
389
390
391 char *
392 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)
393 {
394   apr_pool_t *pool;
395   apr_uri_t url;
396   apr_status_t rv;
397   apr_sockaddr_t *address = NULL;
398
399   serf_context_t *context;
400   serf_connection_t *connection;
401
402   app_ctx_t app_ctx;
403   handler_ctx_t handler_ctx;
404   char *ret;
405
406   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
407
408
409   s_init(ppool, &pool);
410
411   apr_uri_parse(pool, url_path, &url);
412   if (!url.port) {
413     url.port = apr_uri_port_of_scheme(url.scheme);
414   }
415   if (!url.port) {
416     url.port = 80;
417   }
418   if (!url.path) {
419     url.path = "/";
420   }
421   if (!url.hostname) {
422     url.hostname = "localhost";
423   }
424   if (url.query) {
425     url.path = apr_psprintf(pool, "%s?%s", url.path, url.query);
426   }
427
428   rv = apr_sockaddr_info_get(&address, url.hostname, APR_UNSPEC, url.port, 0, pool);
429   if (rv != APR_SUCCESS) {
430     char buf[256];
431     ERR(r, "apr_sockaddr_info_get() failed: rv:[%d|%s]", rv, apr_strerror(rv, buf, 256));
432     return NULL;
433   }
434   memset(&app_ctx, 0, sizeof(app_ctx_t));
435
436   app_ctx.bkt_alloc = serf_bucket_allocator_create(pool, NULL, NULL);
437   if (strcasecmp(url.scheme, "https") == 0) {
438     app_ctx.ssl_flag = 1;
439   }
440
441   context = serf_context_create(pool);
442   connection = serf_connection_create(context, address, s_connection_setup, &app_ctx, s_connection_closed, &app_ctx, pool);
443
444   memset(&handler_ctx, 0, sizeof(handler_ctx_t));
445   handler_ctx.requests_outstanding = 0;
446   handler_ctx.host = url.hostinfo;
447   handler_ctx.method = "POST";
448   handler_ctx.path = url.path;
449   handler_ctx.user_agent = (char *)apr_table_get(r->headers_in, CHXJ_HTTP_USER_AGENT);
450   if (! handler_ctx.user_agent) {
451     handler_ctx.user_agent = (char *)apr_table_get(r->headers_in, HTTP_USER_AGENT);
452   }
453   handler_ctx.post_data = post_data;
454   handler_ctx.post_data_len = post_data_len;
455
456   handler_ctx.acceptor     = s_accept_response;
457   handler_ctx.acceptor_ctx = &app_ctx;
458   handler_ctx.handler      = s_handle_response;
459   handler_ctx.pool         = pool;
460   handler_ctx.r            = r;
461   handler_ctx.response_len = 0;
462   handler_ctx.response     = NULL;
463
464   serf_connection_request_create(connection, s_setup_request, &handler_ctx);
465
466   while (1) {
467     rv = serf_context_run(context, SERF_DURATION_FOREVER, pool);
468     if (APR_STATUS_IS_TIMEUP(rv))
469       continue;
470     if (rv) {
471       char buf[200];
472       ERR(r, "Error running context: (%d) %s\n", rv, apr_strerror(rv, buf, sizeof(buf)));
473       break;
474     }
475     if (!apr_atomic_read32(&handler_ctx.requests_outstanding)) {
476       if (handler_ctx.rv != APR_SUCCESS) {
477         char buf[200];
478         ERR(r, "Error running context: (%d) %s\n", handler_ctx.rv, apr_strerror(handler_ctx.rv, buf, sizeof(buf)));
479       }
480       break;
481     }
482   }
483
484   DBG(r,"REQ[%X] end of serf request",TO_ADDR(r));
485   DBG(r,"REQ[%X] response_code:[%d]", TO_ADDR(r),handler_ctx.response_code);
486   DBG(r,"REQ[%X] response:[%s][%" APR_SIZE_T_FMT "]", TO_ADDR(r),handler_ctx.response, handler_ctx.response_len);
487   serf_connection_close(connection);
488   if (handler_ctx.response) {
489     ret = apr_palloc(ppool, handler_ctx.response_len + 1);
490     memset(ret, 0, handler_ctx.response_len + 1);
491     memcpy(ret, handler_ctx.response, handler_ctx.response_len);
492   }
493   else {
494     ret = apr_pstrdup(ppool, "");
495   }
496   if (set_headers_flag && !rv) {
497     r->headers_out = apr_table_copy(pool, handler_ctx.headers_out);
498     *response_len = handler_ctx.response_len;
499     char *contentType = (char *)apr_table_get(handler_ctx.headers_out, "Content-Type");
500     if (contentType) {
501       DBG(r,"REQ[%X] response content type[%s]", TO_ADDR(r),contentType);
502       chxj_set_content_type(r, apr_pstrdup(r->pool, contentType));
503     }
504   }
505   if (rv) {
506     *response_len = 0;
507   }
508   *response_code = handler_ctx.response_code;
509   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
510   return ret;
511 }
512
513
514 apr_table_t *
515 default_chxj_serf_head(request_rec *r, apr_pool_t *ppool, const char *url_path, int *response_code)
516 {
517   apr_pool_t *pool;
518   apr_uri_t url;
519   apr_status_t rv;
520   apr_sockaddr_t *address = NULL;
521
522   serf_context_t *context;
523   serf_connection_t *connection;
524
525   app_ctx_t app_ctx;
526   handler_ctx_t handler_ctx;
527   char *ret;
528
529   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
530
531
532   s_init(ppool, &pool);
533
534   apr_uri_parse(pool, url_path, &url);
535   if (!url.port) {
536     url.port = apr_uri_port_of_scheme(url.scheme);
537   }
538   if (!url.port) {
539     url.port = 80;
540   }
541   if (!url.path) {
542     url.path = "/";
543   }
544   if (!url.hostname) {
545     url.hostname = "localhost";
546   }
547   if (url.query) {
548     url.path = apr_psprintf(pool, "%s?%s", url.path, url.query);
549   }
550
551   rv = apr_sockaddr_info_get(&address, url.hostname, APR_UNSPEC, url.port, 0, pool);
552   if (rv != APR_SUCCESS) {
553     char buf[256];
554     ERR(r, "apr_sockaddr_info_get() failed: rv:[%d|%s]", rv, apr_strerror(rv, buf, 256));
555     return NULL;
556   }
557   memset(&app_ctx, 0, sizeof(app_ctx_t));
558
559   app_ctx.bkt_alloc = serf_bucket_allocator_create(pool, NULL, NULL);
560   if (strcasecmp(url.scheme, "https") == 0) {
561     app_ctx.ssl_flag = 1;
562   }
563
564   context = serf_context_create(pool);
565   connection = serf_connection_create(context, address, s_connection_setup, &app_ctx, s_connection_closed, &app_ctx, pool);
566
567   memset(&handler_ctx, 0, sizeof(handler_ctx_t));
568   handler_ctx.requests_outstanding = 0;
569   handler_ctx.host = url.hostinfo;
570   /*========================================================================================================*/
571   /* XXX Maybe, libserf doesn't support the HEAD request. Because the part body is waited for with polling. */
572   /*========================================================================================================*/
573   handler_ctx.method = "GET";
574   handler_ctx.path = url.path;
575   handler_ctx.user_agent = (char *)apr_table_get(r->headers_in, CHXJ_HTTP_USER_AGENT);
576   if (! handler_ctx.user_agent) {
577     handler_ctx.user_agent = (char *)apr_table_get(r->headers_in, HTTP_USER_AGENT);
578   }
579   handler_ctx.post_data     = NULL;
580   handler_ctx.post_data_len = 0;
581
582   handler_ctx.acceptor     = s_accept_response;
583   handler_ctx.acceptor_ctx = &app_ctx;
584   handler_ctx.handler      = s_handle_response;
585   handler_ctx.pool         = pool;
586   handler_ctx.r            = r;
587   handler_ctx.response_len = 0;
588   handler_ctx.response     = NULL;
589
590   serf_connection_request_create(connection, s_setup_request, &handler_ctx);
591
592   while (1) {
593     rv = serf_context_run(context, SERF_DURATION_FOREVER, pool);
594     if (APR_STATUS_IS_TIMEUP(rv))
595       continue;
596     if (rv) {
597       char buf[200];
598       ERR(r, "Error running context: (%d) %s\n", rv, apr_strerror(rv, buf, sizeof(buf)));
599       break;
600     }
601     if (!apr_atomic_read32(&handler_ctx.requests_outstanding)) {
602       if (handler_ctx.rv != APR_SUCCESS) {
603         char buf[200];
604         ERR(r, "Error running context: (%d) %s\n", handler_ctx.rv, apr_strerror(handler_ctx.rv, buf, sizeof(buf)));
605       }
606       break;
607     }
608   }
609
610   DBG(r,"REQ[%X] end of serf request",TO_ADDR(r));
611   DBG(r,"REQ[%X] response_code:[%d]", TO_ADDR(r),handler_ctx.response_code);
612   DBG(r,"REQ[%X] response:[%s][%" APR_SIZE_T_FMT "]", TO_ADDR(r),handler_ctx.response, handler_ctx.response_len);
613   serf_connection_close(connection);
614   if (handler_ctx.response) {
615     ret = apr_pstrdup(ppool, handler_ctx.response);
616   }
617   else {
618     ret = apr_pstrdup(ppool, "");
619   }
620   *response_code = handler_ctx.response_code;
621   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
622   return handler_ctx.headers_out;
623 }
624 /*
625  * vim:ts=2 et
626  */