OSDN Git Service

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