int main(int argc, const char **argv) { apr_status_t status; apr_pool_t *pool; serf_bucket_alloc_t *bkt_alloc; serf_context_t *context; serf_connection_t **connections; app_baton_t app_ctx; handler_baton_t handler_ctx; serf_bucket_t *req_hdrs = NULL; apr_uri_t url; const char *proxy = NULL; const char *raw_url, *method, *req_body_path = NULL; int count, inflight, conn_count; int i; int print_headers, debug; const char *username = NULL; const char *password = ""; const char *pem_path = NULL, *pem_pwd = NULL; apr_getopt_t *opt; int opt_c; const char *opt_arg; apr_initialize(); atexit(apr_terminate); apr_pool_create(&pool, NULL); /* serf_initialize(); */ bkt_alloc = serf_bucket_allocator_create(pool, NULL, NULL); /* Default to one round of fetching with no limit to max inflight reqs. */ count = 1; inflight = 0; conn_count = 1; /* Default to GET. */ method = "GET"; /* Do not print headers by default. */ print_headers = 0; /* Do not debug by default. */ debug = 0; apr_getopt_init(&opt, pool, argc, argv); while ((status = apr_getopt_long(opt, options, &opt_c, &opt_arg)) == APR_SUCCESS) { switch (opt_c) { case 'U': username = opt_arg; break; case 'P': password = opt_arg; break; case 'd': debug = 1; break; case 'f': req_body_path = opt_arg; break; case 'h': print_usage(pool); exit(0); break; case 'H': print_headers = 1; break; case 'm': method = opt_arg; break; case 'n': errno = 0; count = apr_strtoi64(opt_arg, NULL, 10); if (errno) { printf("Problem converting number of times to fetch URL (%d)\n", errno); return errno; } break; case 'c': errno = 0; conn_count = apr_strtoi64(opt_arg, NULL, 10); if (errno) { printf("Problem converting number of concurrent connections to use (%d)\n", errno); return errno; } if (conn_count <= 0) { printf("Invalid number of concurrent connections to use (%d)\n", conn_count); return 1; } break; case 'x': errno = 0; inflight = apr_strtoi64(opt_arg, NULL, 10); if (errno) { printf("Problem converting number of requests to have outstanding (%d)\n", errno); return errno; } break; case 'p': proxy = opt_arg; break; case 'r': { char *sep; char *hdr_val; if (req_hdrs == NULL) { /* first request header, allocate bucket */ req_hdrs = serf_bucket_headers_create(bkt_alloc); } sep = strchr(opt_arg, ':'); if ((sep == NULL) || (sep == opt_arg) || (strlen(sep) <= 1)) { printf("Invalid request header string (%s)\n", opt_arg); return EINVAL; } hdr_val = sep + 1; while (*hdr_val == ' ') { hdr_val++; } serf_bucket_headers_setx(req_hdrs, opt_arg, (sep - opt_arg), 1, hdr_val, strlen(hdr_val), 1); } break; case CERTFILE: pem_path = opt_arg; break; case CERTPWD: pem_pwd = opt_arg; break; case 'v': puts("Serf version: " SERF_VERSION_STRING); exit(0); default: break; } } if (opt->ind != opt->argc - 1) { print_usage(pool); exit(-1); } raw_url = argv[opt->ind]; apr_uri_parse(pool, raw_url, &url); if (!url.port) { url.port = apr_uri_port_of_scheme(url.scheme); } if (!url.path) { url.path = "/"; } if (strcasecmp(url.scheme, "https") == 0) { app_ctx.using_ssl = 1; } else { app_ctx.using_ssl = 0; } if (strcasecmp(method, "HEAD") == 0) { app_ctx.head_request = 1; } else { app_ctx.head_request = 0; } app_ctx.hostinfo = url.hostinfo; app_ctx.pem_path = pem_path; app_ctx.pem_pwd = pem_pwd; context = serf_context_create(pool); app_ctx.serf_ctx = context; if (proxy) { apr_sockaddr_t *proxy_address = NULL; apr_port_t proxy_port; char *proxy_host; char *proxy_scope; status = apr_parse_addr_port(&proxy_host, &proxy_scope, &proxy_port, proxy, pool); if (status) { printf("Cannot parse proxy hostname/port: %d\n", status); apr_pool_destroy(pool); exit(1); } if (!proxy_host) { printf("Proxy hostname must be specified\n"); apr_pool_destroy(pool); exit(1); } if (!proxy_port) { printf("Proxy port must be specified\n"); apr_pool_destroy(pool); exit(1); } status = apr_sockaddr_info_get(&proxy_address, proxy_host, APR_UNSPEC, proxy_port, 0, pool); if (status) { printf("Cannot resolve proxy address '%s': %d\n", proxy_host, status); apr_pool_destroy(pool); exit(1); } serf_config_proxy(context, proxy_address); } if (username) { serf_config_authn_types(context, SERF_AUTHN_ALL); } else { serf_config_authn_types(context, SERF_AUTHN_NTLM | SERF_AUTHN_NEGOTIATE); } serf_config_credentials_callback(context, credentials_callback); /* Setup debug logging */ if (debug) { serf_log_output_t *output; apr_status_t status; status = serf_logging_create_stream_output(&output, context, SERF_LOG_DEBUG, SERF_LOGCOMP_ALL_MSG, SERF_LOG_DEFAULT_LAYOUT, stderr, pool); if (!status) serf_logging_add_output(context, output); } /* ### Connection or Context should have an allocator? */ app_ctx.bkt_alloc = bkt_alloc; connections = apr_pcalloc(pool, conn_count * sizeof(serf_connection_t*)); for (i = 0; i < conn_count; i++) { conn_baton_t *conn_ctx = apr_pcalloc(pool, sizeof(*conn_ctx)); conn_ctx->app = &app_ctx; conn_ctx->ssl_ctx = NULL; status = serf_connection_create2(&connections[i], context, url, conn_setup, conn_ctx, closed_connection, conn_ctx, pool); if (status) { printf("Error creating connection: %d\n", status); apr_pool_destroy(pool); exit(1); } serf_connection_set_max_outstanding_requests(connections[i], inflight); } handler_ctx.completed_requests = 0; handler_ctx.print_headers = print_headers; #if APR_VERSION_AT_LEAST(1, 3, 0) apr_file_open_flags_stdout(&handler_ctx.output_file, APR_BUFFERED, pool); #else apr_file_open_stdout(&handler_ctx.output_file, pool); #endif handler_ctx.host = url.hostinfo; handler_ctx.method = method; handler_ctx.path = apr_pstrcat(pool, url.path, url.query ? "?" : "", url.query ? url.query : "", NULL); handler_ctx.username = username; handler_ctx.password = password; handler_ctx.auth_attempts = 0; handler_ctx.req_body_path = req_body_path; handler_ctx.acceptor = accept_response; handler_ctx.acceptor_baton = &app_ctx; handler_ctx.handler = handle_response; handler_ctx.req_hdrs = req_hdrs; for (i = 0; i < count; i++) { /* We don't need the returned request here. */ serf_connection_request_create(connections[i % conn_count], setup_request, &handler_ctx); } while (1) { status = serf_context_run(context, SERF_DURATION_FOREVER, pool); if (APR_STATUS_IS_TIMEUP(status)) continue; if (status) { char buf[200]; const char *err_string; err_string = serf_error_string(status); if (!err_string) { err_string = apr_strerror(status, buf, sizeof(buf)); } printf("Error running context: (%d) %s\n", status, err_string); apr_pool_destroy(pool); exit(1); } if (apr_atomic_read32(&handler_ctx.completed_requests) >= count) { break; } /* Debugging purposes only! */ serf_debug__closed_conn(app_ctx.bkt_alloc); } apr_file_close(handler_ctx.output_file); for (i = 0; i < conn_count; i++) { serf_connection_close(connections[i]); } apr_pool_destroy(pool); return 0; }
static svn_error_t * load_config(svn_ra_serf__session_t *session, apr_hash_t *config_hash, apr_pool_t *pool) { svn_config_t *config, *config_client; const char *server_group; const char *proxy_host = NULL; const char *port_str = NULL; const char *timeout_str = NULL; const char *exceptions; apr_port_t proxy_port; svn_tristate_t chunked_requests; #if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING) apr_int64_t log_components; apr_int64_t log_level; #endif if (config_hash) { config = svn_hash_gets(config_hash, SVN_CONFIG_CATEGORY_SERVERS); config_client = svn_hash_gets(config_hash, SVN_CONFIG_CATEGORY_CONFIG); } else { config = NULL; config_client = NULL; } SVN_ERR(svn_config_get_bool(config, &session->using_compression, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_COMPRESSION, TRUE)); svn_config_get(config, &timeout_str, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_TIMEOUT, NULL); if (session->auth_baton) { if (config_client) { svn_auth_set_parameter(session->auth_baton, SVN_AUTH_PARAM_CONFIG_CATEGORY_CONFIG, config_client); } if (config) { svn_auth_set_parameter(session->auth_baton, SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS, config); } } /* Use the default proxy-specific settings if and only if "http-proxy-exceptions" is not set to exclude this host. */ svn_config_get(config, &exceptions, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_PROXY_EXCEPTIONS, ""); if (! svn_cstring_match_glob_list(session->session_url.hostname, svn_cstring_split(exceptions, ",", TRUE, pool))) { svn_config_get(config, &proxy_host, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_PROXY_HOST, NULL); svn_config_get(config, &port_str, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_PROXY_PORT, NULL); svn_config_get(config, &session->proxy_username, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_PROXY_USERNAME, NULL); svn_config_get(config, &session->proxy_password, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_PROXY_PASSWORD, NULL); } /* Load the global ssl settings, if set. */ SVN_ERR(svn_config_get_bool(config, &session->trust_default_ca, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_SSL_TRUST_DEFAULT_CA, TRUE)); svn_config_get(config, &session->ssl_authorities, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES, NULL); /* If set, read the flag that tells us to do bulk updates or not. Defaults to skelta updates. */ SVN_ERR(svn_config_get_tristate(config, &session->bulk_updates, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_BULK_UPDATES, "auto", svn_tristate_unknown)); /* Load the maximum number of parallel session connections. */ SVN_ERR(svn_config_get_int64(config, &session->max_connections, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, SVN_CONFIG_DEFAULT_OPTION_HTTP_MAX_CONNECTIONS)); /* Should we use chunked transfer encoding. */ SVN_ERR(svn_config_get_tristate(config, &chunked_requests, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS, "auto", svn_tristate_unknown)); #if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING) SVN_ERR(svn_config_get_int64(config, &log_components, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_SERF_LOG_COMPONENTS, SERF_LOGCOMP_NONE)); SVN_ERR(svn_config_get_int64(config, &log_level, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_SERF_LOG_LEVEL, SERF_LOG_INFO)); #endif server_group = svn_auth_get_parameter(session->auth_baton, SVN_AUTH_PARAM_SERVER_GROUP); if (server_group) { SVN_ERR(svn_config_get_bool(config, &session->using_compression, server_group, SVN_CONFIG_OPTION_HTTP_COMPRESSION, session->using_compression)); svn_config_get(config, &timeout_str, server_group, SVN_CONFIG_OPTION_HTTP_TIMEOUT, timeout_str); /* Load the group proxy server settings, overriding global settings. We intentionally ignore 'http-proxy-exceptions' here because, well, if this site was an exception, why is there a per-server proxy configuration for it? */ svn_config_get(config, &proxy_host, server_group, SVN_CONFIG_OPTION_HTTP_PROXY_HOST, proxy_host); svn_config_get(config, &port_str, server_group, SVN_CONFIG_OPTION_HTTP_PROXY_PORT, port_str); svn_config_get(config, &session->proxy_username, server_group, SVN_CONFIG_OPTION_HTTP_PROXY_USERNAME, session->proxy_username); svn_config_get(config, &session->proxy_password, server_group, SVN_CONFIG_OPTION_HTTP_PROXY_PASSWORD, session->proxy_password); /* Load the group ssl settings. */ SVN_ERR(svn_config_get_bool(config, &session->trust_default_ca, server_group, SVN_CONFIG_OPTION_SSL_TRUST_DEFAULT_CA, session->trust_default_ca)); svn_config_get(config, &session->ssl_authorities, server_group, SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES, session->ssl_authorities); /* Load the group bulk updates flag. */ SVN_ERR(svn_config_get_tristate(config, &session->bulk_updates, server_group, SVN_CONFIG_OPTION_HTTP_BULK_UPDATES, "auto", session->bulk_updates)); /* Load the maximum number of parallel session connections, overriding global values. */ SVN_ERR(svn_config_get_int64(config, &session->max_connections, server_group, SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, session->max_connections)); /* Should we use chunked transfer encoding. */ SVN_ERR(svn_config_get_tristate(config, &chunked_requests, server_group, SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS, "auto", chunked_requests)); #if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING) SVN_ERR(svn_config_get_int64(config, &log_components, server_group, SVN_CONFIG_OPTION_SERF_LOG_COMPONENTS, log_components)); SVN_ERR(svn_config_get_int64(config, &log_level, server_group, SVN_CONFIG_OPTION_SERF_LOG_LEVEL, log_level)); #endif } #if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING) if (log_components != SERF_LOGCOMP_NONE) { serf_log_output_t *output; apr_status_t status; status = serf_logging_create_stream_output(&output, session->context, (apr_uint32_t)log_level, (apr_uint32_t)log_components, SERF_LOG_DEFAULT_LAYOUT, stderr, pool); if (!status) serf_logging_add_output(session->context, output); } #endif /* Don't allow the http-max-connections value to be larger than our compiled-in limit, or to be too small to operate. Broken functionality and angry administrators are equally undesirable. */ if (session->max_connections > SVN_RA_SERF__MAX_CONNECTIONS_LIMIT) session->max_connections = SVN_RA_SERF__MAX_CONNECTIONS_LIMIT; if (session->max_connections < 2) session->max_connections = 2; /* Parse the connection timeout value, if any. */ session->timeout = apr_time_from_sec(DEFAULT_HTTP_TIMEOUT); if (timeout_str) { char *endstr; const long int timeout = strtol(timeout_str, &endstr, 10); if (*endstr) return svn_error_create(SVN_ERR_BAD_CONFIG_VALUE, NULL, _("Invalid config: illegal character in " "timeout value")); if (timeout < 0) return svn_error_create(SVN_ERR_BAD_CONFIG_VALUE, NULL, _("Invalid config: negative timeout value")); session->timeout = apr_time_from_sec(timeout); } SVN_ERR_ASSERT(session->timeout >= 0); /* Convert the proxy port value, if any. */ if (port_str) { char *endstr; const long int port = strtol(port_str, &endstr, 10); if (*endstr) return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL, _("Invalid URL: illegal character in proxy " "port number")); if (port < 0) return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL, _("Invalid URL: negative proxy port number")); if (port > 65535) return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL, _("Invalid URL: proxy port number greater " "than maximum TCP port number 65535")); proxy_port = (apr_port_t) port; } else { proxy_port = 80; } if (proxy_host) { apr_sockaddr_t *proxy_addr; apr_status_t status; status = apr_sockaddr_info_get(&proxy_addr, proxy_host, APR_UNSPEC, proxy_port, 0, session->pool); if (status) { return svn_ra_serf__wrap_err( status, _("Could not resolve proxy server '%s'"), proxy_host); } session->using_proxy = TRUE; serf_config_proxy(session->context, proxy_addr); } else { session->using_proxy = FALSE; } /* Setup detect_chunking and using_chunked_requests based on * the chunked_requests tristate */ if (chunked_requests == svn_tristate_unknown) { session->detect_chunking = TRUE; session->using_chunked_requests = TRUE; } else if (chunked_requests == svn_tristate_true) { session->detect_chunking = FALSE; session->using_chunked_requests = TRUE; } else /* chunked_requests == svn_tristate_false */ { session->detect_chunking = FALSE; session->using_chunked_requests = FALSE; } /* Setup authentication. */ SVN_ERR(load_http_auth_types(pool, config, server_group, &session->authn_types)); serf_config_authn_types(session->context, session->authn_types); serf_config_credentials_callback(session->context, svn_ra_serf__credentials_callback); return SVN_NO_ERROR; }