| /* Copyright 2013 Justin Erenkrantz and Greg Stein |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <apr_strings.h> |
| |
| #include "serf.h" |
| #include "serf_bucket_util.h" |
| #include "serf_private.h" |
| |
| /* Use a linked list to store the config values, as we'll only store a couple |
| of values per context. */ |
| struct serf__config_hdr_t { |
| apr_pool_t *pool; |
| struct config_entry_t *first; |
| }; |
| |
| typedef struct config_entry_t { |
| apr_uint32_t key; |
| void *value; |
| struct config_entry_t *next; |
| } config_entry_t; |
| |
| static serf__config_hdr_t *create_config_hdr(apr_pool_t *pool) |
| { |
| serf__config_hdr_t *hdr = apr_pcalloc(pool, sizeof(serf__config_hdr_t)); |
| hdr->pool = pool; |
| return hdr; |
| } |
| |
| static apr_status_t |
| add_or_replace_entry(serf__config_hdr_t *hdr, serf_config_key_t key, void *value) |
| { |
| config_entry_t *iter = hdr->first; |
| config_entry_t *last = iter; |
| int found = FALSE; |
| |
| /* Find the entry with the matching key. If it exists, replace its value. */ |
| while (iter != NULL) { |
| if (iter->key == key) { |
| found = TRUE; |
| break; |
| } |
| last = iter; |
| iter = iter->next; |
| } |
| |
| if (found) { |
| iter->key = key; |
| iter->value = value; |
| } else { |
| /* Not found, create a new entry and append it to the list. */ |
| config_entry_t *entry = apr_palloc(hdr->pool, sizeof(config_entry_t)); |
| entry->key = key; |
| entry->value = value; |
| entry->next = NULL; |
| |
| if (last) |
| last->next = entry; |
| else |
| hdr->first = entry; |
| } |
| |
| return APR_SUCCESS; |
| } |
| |
| /*** Config Store ***/ |
| apr_status_t serf__config_store_init(serf_context_t *ctx) |
| { |
| apr_pool_t *pool = ctx->pool; |
| |
| ctx->config_store.pool = pool; |
| ctx->config_store.global_per_context = create_config_hdr(pool); |
| ctx->config_store.global_per_host = apr_hash_make(pool); |
| ctx->config_store.global_per_conn = apr_hash_make(pool); |
| |
| return APR_ENOTIMPL; |
| } |
| |
| /* Defines the key to use for per host settings */ |
| static const char * host_key_for_conn(serf_connection_t *conn, |
| apr_pool_t *pool) |
| { |
| /* SCHEME://HOSTNAME:PORT, e.g. http://localhost:12345 */ |
| return conn->host_url; |
| } |
| |
| /* Defines the key to use for per connection settings */ |
| static const char * conn_key_for_conn(serf_connection_t *conn, |
| apr_pool_t *pool) |
| { |
| /* Key needs to be unique per connection, so stringify its pointer value */ |
| return apr_psprintf(pool, "%x", (unsigned int)conn); |
| } |
| |
| /* TODO: when will this be released? Related config to a specific lifecyle: |
| connection or context */ |
| apr_status_t serf__config_store_get_config(serf_context_t *ctx, |
| serf_connection_t *conn, |
| serf_config_t **config, |
| apr_pool_t *out_pool) |
| { |
| serf__config_store_t *config_store = &ctx->config_store; |
| |
| serf_config_t *cfg = apr_pcalloc(out_pool, sizeof(serf_config_t)); |
| cfg->ctx_pool = ctx->pool; |
| cfg->per_context = config_store->global_per_context; |
| |
| if (conn) { |
| const char *host_key, *conn_key; |
| serf__config_hdr_t *per_conn, *per_host; |
| apr_pool_t *tmp_pool; |
| apr_status_t status; |
| |
| cfg->conn_pool = conn->pool; |
| |
| if ((status = apr_pool_create(&tmp_pool, out_pool)) != APR_SUCCESS) |
| return status; |
| |
| /* Find the config values for this connection, create empty structure |
| if needed */ |
| conn_key = conn_key_for_conn(conn, tmp_pool); |
| per_conn = apr_hash_get(config_store->global_per_conn, conn_key, |
| APR_HASH_KEY_STRING); |
| if (!per_conn) { |
| per_conn = create_config_hdr(conn->pool); |
| apr_hash_set(config_store->global_per_conn, |
| apr_pstrdup(conn->pool, conn_key), |
| APR_HASH_KEY_STRING, per_conn); |
| } |
| cfg->per_conn = per_conn; |
| |
| /* Find the config values for this host, create empty structure |
| if needed */ |
| host_key = host_key_for_conn(conn, tmp_pool); |
| per_host = apr_hash_get(config_store->global_per_host, |
| host_key, |
| APR_HASH_KEY_STRING); |
| if (!per_host) { |
| per_host = create_config_hdr(config_store->pool); |
| apr_hash_set(config_store->global_per_host, |
| apr_pstrdup(config_store->pool, host_key), |
| APR_HASH_KEY_STRING, per_host); |
| } |
| cfg->per_host = per_host; |
| |
| apr_pool_destroy(tmp_pool); |
| } |
| |
| *config = cfg; |
| |
| return APR_SUCCESS; |
| } |
| |
| apr_status_t |
| serf__config_store_remove_connection(serf__config_store_t config_store, |
| serf_connection_t *conn) |
| { |
| return APR_ENOTIMPL; |
| } |
| |
| apr_status_t |
| serf__config_store_remove_host(serf__config_store_t config_store, |
| const char *hostname_port) |
| { |
| return APR_ENOTIMPL; |
| } |
| |
| /*** Config ***/ |
| apr_status_t serf_config_set_string(serf_config_t *config, |
| serf_config_key_t key, |
| const char *value) |
| { |
| /* Cast away const is ok here, the callers should always use |
| serf_config_get_string for this key. */ |
| return serf_config_set_object(config, key, (void *)value); |
| } |
| |
| apr_status_t serf_config_set_stringc(serf_config_t *config, |
| serf_config_key_t key, |
| const char *value) |
| { |
| const char *cvalue; |
| apr_pool_t *pool; |
| |
| if (key & SERF_CONFIG_PER_CONTEXT || |
| key & SERF_CONFIG_PER_HOST) { |
| pool = config->ctx_pool; |
| } else { |
| pool = config->conn_pool; |
| } |
| |
| cvalue = apr_pstrdup(pool, value); |
| |
| return serf_config_set_string(config, key, cvalue); |
| } |
| |
| apr_status_t serf_config_set_stringf(serf_config_t *config, |
| serf_config_key_t key, |
| const char *fmt, ...) |
| { |
| apr_pool_t *pool; |
| va_list argp; |
| char *cvalue; |
| |
| if (key & SERF_CONFIG_PER_CONTEXT) |
| pool = config->ctx_pool; |
| else if (key & SERF_CONFIG_PER_HOST) |
| pool = config->ctx_pool; |
| else |
| pool = config->conn_pool; |
| |
| va_start(argp, fmt); |
| cvalue = apr_pvsprintf(pool, fmt, argp); |
| va_end(argp); |
| |
| return serf_config_set_string(config, key, cvalue); |
| } |
| |
| apr_status_t serf_config_set_object(serf_config_t *config, |
| serf_config_key_t key, |
| void *value) |
| { |
| serf__config_hdr_t *target; |
| |
| /* Set the value in the hash table of the selected category */ |
| if (key & SERF_CONFIG_PER_CONTEXT) { |
| target = config->per_context; |
| } |
| else if (key & SERF_CONFIG_PER_HOST) { |
| target = config->per_host; |
| } |
| else { |
| target = config->per_conn; |
| } |
| |
| if (!target) { |
| /* Config object doesn't manage keys in this category */ |
| return APR_EINVAL; |
| } |
| |
| return add_or_replace_entry(target, key, value); |
| } |
| |
| apr_status_t serf_config_get_string(serf_config_t *config, |
| serf_config_key_t key, |
| const char **value) |
| { |
| return serf_config_get_object(config, key, (void**)value); |
| } |
| |
| apr_status_t serf_config_get_object(serf_config_t *config, |
| serf_config_key_t key, |
| void **value) |
| { |
| serf__config_hdr_t *target; |
| |
| if (key & SERF_CONFIG_PER_CONTEXT) |
| target = config->per_context; |
| else if (key & SERF_CONFIG_PER_HOST) |
| target = config->per_host; |
| else |
| target = config->per_conn; |
| |
| *value = NULL; |
| if (target) { |
| config_entry_t *iter = target->first; |
| /* Find the matching key and return its value */ |
| while (iter != NULL) { |
| if (iter->key == key) { |
| *value = iter->value; |
| return APR_SUCCESS; |
| } |
| iter = iter->next; |
| } |
| return APR_SUCCESS; |
| } else { |
| /* Config object doesn't manage keys in this category */ |
| return APR_EINVAL; |
| } |
| } |
| |
| apr_status_t serf_config_remove_value(serf_config_t *config, |
| serf_config_key_t key) |
| { |
| return serf_config_set_object(config, key, NULL); |
| } |