1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include "http_core.h"
19 #include "http_config.h"
20 #include "http_protocol.h"
26 module AP_MODULE_DECLARE_DATA serf_module
;
39 serf_config_rec
*conf
;
40 serf_ssl_context_t
*ssl_ctx
;
41 serf_bucket_alloc_t
*bkt_alloc
;
45 static void closed_connection(serf_connection_t
*conn
,
50 s_baton_t
*ctx
= closed_baton
;
53 /* justin says that error handling isn't done yet. hah. */
55 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, why
, ctx
->r
, "Closed Connection Error");
56 ctx
->rstatus
= HTTP_INTERNAL_SERVER_ERROR
;
61 static serf_bucket_t
* conn_setup(apr_socket_t
*sock
,
66 s_baton_t
*ctx
= setup_baton
;
68 c
= serf_bucket_socket_create(sock
, ctx
->bkt_alloc
);
70 c
= serf_bucket_ssl_decrypt_create(c
, ctx
->ssl_ctx
, ctx
->bkt_alloc
);
76 int copy_headers_in(void *vbaton
, const char *key
, const char *value
)
78 serf_bucket_t
*hdrs_bkt
= (serf_bucket_t
*)vbaton
;
80 /* XXXXX: List of headers not to copy to serf. serf's serf_bucket_headers_setn,
81 * doesn't actually overwrite a header if we set it once, so we need to ignore anything
82 * we might want to toggle or combine.
87 if (strcasecmp("Accept-Encoding", key
) == 0) {
93 if (strcasecmp("Connection", key
) == 0) {
99 if (strcasecmp("Host", key
) == 0) {
105 if (strcasecmp("Keep-Alive", key
) == 0) {
111 if (strcasecmp("TE", key
) == 0) {
114 if (strcasecmp("Trailer", key
) == 0) {
120 if (strcasecmp("Upgrade", key
) == 0) {
128 serf_bucket_headers_setn(hdrs_bkt
, key
, value
);
132 int copy_headers_out(void *vbaton
, const char *key
, const char *value
)
134 s_baton_t
*ctx
= vbaton
;
137 /* XXXXX: Special Treatment required for MANY other headers. fixme.*/
141 if (strcasecmp("Content-Type", key
) == 0) {
142 ap_set_content_type(ctx
->r
, value
);
146 else if (strcasecmp("Connection", key
) == 0) {
150 else if (strcasecmp("Content-Encoding", key
) == 0) {
154 else if (strcasecmp("Content-Length", key
) == 0) {
161 if (strcasecmp("Transfer-Encoding", key
) == 0) {
171 apr_table_addn(ctx
->r
->headers_out
, key
, value
);
177 static serf_bucket_t
* accept_response(serf_request_t
*request
,
178 serf_bucket_t
*stream
,
179 void *acceptor_baton
,
183 serf_bucket_alloc_t
*bkt_alloc
;
185 /* get the per-request bucket allocator */
186 bkt_alloc
= serf_request_get_alloc(request
);
188 /* Create a barrier so the response doesn't eat us! */
189 c
= serf_bucket_barrier_create(stream
, bkt_alloc
);
191 return serf_bucket_response_create(c
, bkt_alloc
);
194 static apr_status_t
handle_response(serf_request_t
*request
,
195 serf_bucket_t
*response
,
200 s_baton_t
*ctx
= vbaton
;
205 /* XXXXXXX: Create better error message. */
206 rv
= serf_bucket_response_status(response
, &sl
);
208 if (APR_STATUS_IS_EAGAIN(rv
)) {
212 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, rv
, ctx
->r
, "serf_bucket_response_status...");
214 ctx
->rstatus
= HTTP_INTERNAL_SERVER_ERROR
;
220 * XXXXX: If I understood serf buckets better, it might be possible to not
221 * copy all of the data here, and better stream it to the client.
225 rv
= serf_bucket_read(response
, AP_IOBUFSIZE
, &data
, &len
);
227 if (SERF_BUCKET_READ_ERROR(rv
)) {
228 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, rv
, ctx
->r
, "serf_bucket_read(response)");
232 if (!ctx
->done_headers
) {
234 hdrs
= serf_bucket_response_get_headers(response
);
235 serf_bucket_headers_do(hdrs
, copy_headers_out
, ctx
);
236 ctx
->done_headers
= 1;
239 /* XXXX: write to brigades and stuff. meh */
240 ap_rwrite(data
, len
, ctx
->r
);
242 if (APR_STATUS_IS_EOF(rv
)) {
243 ctx
->keep_reading
= 0;
247 /* XXXX: Should we send a flush now? */
248 if (APR_STATUS_IS_EAGAIN(rv
)) {
256 static apr_status_t
setup_request(serf_request_t
*request
,
258 serf_bucket_t
**req_bkt
,
259 serf_response_acceptor_t
*acceptor
,
260 void **acceptor_baton
,
261 serf_response_handler_t
*handler
,
262 void **handler_baton
,
265 s_baton_t
*ctx
= vbaton
;
266 serf_bucket_t
*hdrs_bkt
;
267 serf_bucket_t
*body_bkt
= NULL
;
270 /* XXXXX: handle incoming request bodies */
271 *req_bkt
= serf_bucket_request_create(ctx
->r
->method
, ctx
->r
->unparsed_uri
, body_bkt
,
272 serf_request_get_alloc(request
));
274 hdrs_bkt
= serf_bucket_request_get_headers(*req_bkt
);
276 apr_table_do(copy_headers_in
, hdrs_bkt
, ctx
->r
->headers_in
, NULL
);
278 /* XXXXXX: SerfPreserveHost on */
279 serf_bucket_headers_setn(hdrs_bkt
, "Host", ctx
->conf
->url
.hostname
);
281 serf_bucket_headers_setn(hdrs_bkt
, "Accept-Encoding", "gzip");
284 serf_bucket_alloc_t
*req_alloc
;
286 req_alloc
= serf_request_get_alloc(request
);
288 if (ctx
->ssl_ctx
== NULL
) {
289 *req_bkt
= serf_bucket_ssl_encrypt_create(*req_bkt
, NULL
,
291 ctx
->ssl_ctx
= serf_bucket_ssl_encrypt_context_get(*req_bkt
);
294 *req_bkt
= serf_bucket_ssl_encrypt_create(*req_bkt
, ctx
->ssl_ctx
,
299 *acceptor
= accept_response
;
300 *acceptor_baton
= ctx
;
301 *handler
= handle_response
;
302 *handler_baton
= ctx
;
307 static int drive_serf(request_rec
*r
, serf_config_rec
*conf
)
310 apr_pool_t
*pool
= r
->pool
;
311 apr_sockaddr_t
*address
;
313 /* XXXXX: make persistent/per-process or something.*/
314 serf_context_t
*serfme
;
315 serf_connection_t
*conn
;
316 serf_request_t
*srequest
;
318 /* XXXXX: cache dns? */
319 rv
= apr_sockaddr_info_get(&address
, conf
->url
.hostname
,
320 APR_UNSPEC
, conf
->url
.port
, 0,
323 if (rv
!= APR_SUCCESS
) {
324 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, rv
, r
, "Unable to resolve: %s", conf
->url
.hostname
);
325 return HTTP_INTERNAL_SERVER_ERROR
;
328 serfme
= serf_context_create(pool
);
332 baton
.bkt_alloc
= serf_bucket_allocator_create(pool
, NULL
, NULL
);
333 baton
.ssl_ctx
= NULL
;
336 baton
.done_headers
= 0;
337 baton
.keep_reading
= 1;
339 if (strcasecmp(conf
->url
.scheme
, "https") == 0) {
346 conn
= serf_connection_create(serfme
, address
,
348 closed_connection
, &baton
,
351 srequest
= serf_connection_request_create(conn
, setup_request
,
355 rv
= serf_context_run(serfme
, SERF_DURATION_FOREVER
, pool
);
357 /* XXXX: Handle timeouts */
358 if (APR_STATUS_IS_TIMEUP(rv
)) {
362 if (rv
!= APR_SUCCESS
) {
363 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, rv
, r
, "serf_context_run()");
364 return HTTP_INTERNAL_SERVER_ERROR
;
367 serf_debug__closed_conn(baton
.bkt_alloc
);
368 } while (baton
.keep_reading
);
370 return baton
.rstatus
;
373 static int serf_handler(request_rec
*r
)
375 serf_config_rec
*conf
= ap_get_module_config(r
->per_dir_config
,
382 return drive_serf(r
, conf
);
385 static const char *add_pass(cmd_parms
*cmd
, void *vconf
,
389 serf_config_rec
*conf
= (serf_config_rec
*) vconf
;
391 rv
= apr_uri_parse(cmd
->pool
, vdest
, &conf
->url
);
393 if (rv
!= APR_SUCCESS
) {
394 return "mod_serf: Unable to parse SerfPass url.";
397 /* XXXX: These are bugs in apr_uri_parse. Fixme. */
398 if (!conf
->url
.port
) {
399 conf
->url
.port
= apr_uri_port_of_scheme(conf
->url
.scheme
);
402 if (!conf
->url
.path
) {
403 conf
->url
.path
= "/";
411 static void *create_config(apr_pool_t
*p
, char *dummy
)
413 serf_config_rec
*new = (serf_config_rec
*) apr_pcalloc(p
, sizeof(serf_config_rec
));
418 static const command_rec serf_cmds
[] =
420 AP_INIT_TAKE1("SerfPass", add_pass
, NULL
, OR_INDEXES
/*making shit up*/,
421 "A prefix and destination"),
425 static void register_hooks(apr_pool_t
*p
)
427 ap_hook_handler(serf_handler
, NULL
, NULL
, APR_HOOK_FIRST
);
430 module AP_MODULE_DECLARE_DATA serf_module
=
432 STANDARD20_MODULE_STUFF
,