static apr_byte_t oidc_util_http_call(request_rec *r, const char *url,
		const char *data, const char *content_type, const char *basic_auth,
		const char *bearer_token, int ssl_validate_server, char **response,
		int timeout, const char *outgoing_proxy,
		apr_array_header_t *pass_cookies, const char *ssl_cert,
		const char *ssl_key, const char *ssl_key_pwd) {
	char curlError[CURL_ERROR_SIZE];
	oidc_curl_buffer curlBuffer;
	CURL *curl;
	struct curl_slist *h_list = NULL;
	int i;
	oidc_cfg *c = ap_get_module_config(r->server->module_config,
			&auth_openidc_module);

	/* do some logging about the inputs */
	oidc_debug(r,
			"url=%s, data=%s, content_type=%s, basic_auth=%s, bearer_token=%s, ssl_validate_server=%d, timeout=%d, outgoing_proxy=%s, pass_cookies=%pp, ssl_cert=%s, ssl_key=%s, ssl_key_pwd=%s",
			url, data, content_type, basic_auth ? "****" : "null", bearer_token,
					ssl_validate_server, timeout, outgoing_proxy, pass_cookies,
					ssl_cert, ssl_key, ssl_key_pwd ? "****" : "(null)");

	curl = curl_easy_init();
	if (curl == NULL) {
		oidc_error(r, "curl_easy_init() error");
		return FALSE;
	}

	/* set the error buffer as empty before performing a request */
	curlError[0] = 0;

	/* some of these are not really required */
	curl_easy_setopt(curl, CURLOPT_HEADER, 0L);
	curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
	curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curlError);
	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
	curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5L);

	/* set the timeout */
	curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);

	/* setup the buffer where the response will be written to */
	curlBuffer.r = r;
	curlBuffer.memory = NULL;
	curlBuffer.size = 0;
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, oidc_curl_write);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void* )&curlBuffer);

#ifndef LIBCURL_NO_CURLPROTO
	curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS,
			CURLPROTO_HTTP|CURLPROTO_HTTPS);
	curl_easy_setopt(curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
#endif

	/* set the options for validating the SSL server certificate that the remote site presents */
	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER,
			(ssl_validate_server != FALSE ? 1L : 0L));
	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST,
			(ssl_validate_server != FALSE ? 2L : 0L));

#if LIBCURL_VERSION_NUM >= 0x071900
	if (r->subprocess_env != NULL) {
		const char *env_var_value = apr_table_get(r->subprocess_env,
				"CURLOPT_SSL_OPTIONS");
		if (env_var_value != NULL) {
			oidc_debug(r, "SSL options environment variable %s=%s found",
					"CURLOPT_SSL_OPTIONS", env_var_value);
			if (strstr(env_var_value, "CURLSSLOPT_ALLOW_BEAST")) {
				oidc_debug(r,
						"curl_easy_setopt CURLOPT_SSL_OPTIONS CURLSSLOPT_ALLOW_BEAST");
				curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS,
						CURLSSLOPT_ALLOW_BEAST);
			}
#if LIBCURL_VERSION_NUM >= 0x072c00
			if (strstr(env_var_value, "CURLSSLOPT_NO_REVOKE")) {
				oidc_debug(r,
						"curl_easy_setopt CURLOPT_SSL_OPTIONS CURLSSLOPT_NO_REVOKE");
				curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS,
						CURLSSLOPT_NO_REVOKE);
			}
#endif
#if LIBCURL_VERSION_NUM >= 0x074400
			if (strstr(env_var_value, "CURLSSLOPT_NO_PARTIALCHAIN")) {
				oidc_debug(r,
						"curl_easy_setopt CURLOPT_SSL_OPTIONS CURLSSLOPT_NO_PARTIALCHAIN");
				curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS,
						CURLSSLOPT_NO_PARTIALCHAIN);
			}
#endif
#if LIBCURL_VERSION_NUM >= 0x074600
			if (strstr(env_var_value, "CURLSSLOPT_REVOKE_BEST_EFFORT")) {
				oidc_debug(r,
						"curl_easy_setopt CURLOPT_SSL_OPTIONS CURLSSLOPT_REVOKE_BEST_EFFORT");
				curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS,
						CURLSSLOPT_REVOKE_BEST_EFFORT);
			}
#endif
#if LIBCURL_VERSION_NUM >= 0x074700
			if (strstr(env_var_value, "CURLSSLOPT_NATIVE_CA")) {
				oidc_debug(r,
						"curl_easy_setopt CURLOPT_SSL_OPTIONS CURLSSLOPT_NATIVE_CA");
				curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS,
						CURLSSLOPT_NATIVE_CA);
			}
#endif
		}
	}
#endif

	if (c->ca_bundle_path != NULL)
		curl_easy_setopt(curl, CURLOPT_CAINFO, c->ca_bundle_path);

#ifdef WIN32
	else {
		DWORD buflen;
		char *ptr = NULL;
		char *retval = (char *) malloc(sizeof (TCHAR) * (MAX_PATH + 1));
		retval[0] = '\0';
		buflen = SearchPath(NULL, "curl-ca-bundle.crt", NULL, MAX_PATH+1, retval, &ptr);
		if (buflen > 0)
			curl_easy_setopt(curl, CURLOPT_CAINFO, retval);
		else
			oidc_warn(r, "no curl-ca-bundle.crt file found in path");
		free(retval);
	}
#endif

	/* identify this HTTP client */
	curl_easy_setopt(curl, CURLOPT_USERAGENT, "mod_auth_openidc");

	/* set optional outgoing proxy for the local network */
	if (outgoing_proxy) {
		curl_easy_setopt(curl, CURLOPT_PROXY, outgoing_proxy);
	}

	/* see if we need to add token in the Bearer Authorization header */
	if (bearer_token != NULL) {
		h_list = curl_slist_append(h_list,
				apr_psprintf(r->pool, "Authorization: Bearer %s",
						bearer_token));
	}

	/* see if we need to perform HTTP basic authentication to the remote site */
	if (basic_auth != NULL) {
		curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
		curl_easy_setopt(curl, CURLOPT_USERPWD, basic_auth);
	}

	if (ssl_cert != NULL)
		curl_easy_setopt(curl, CURLOPT_SSLCERT, ssl_cert);
	if (ssl_key != NULL)
		curl_easy_setopt(curl, CURLOPT_SSLKEY, ssl_key);
	if (ssl_key_pwd != NULL)
		curl_easy_setopt(curl, CURLOPT_KEYPASSWD, ssl_key_pwd);

	if (data != NULL) {
		/* set POST data */
		curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
		/* set HTTP method to POST */
		curl_easy_setopt(curl, CURLOPT_POST, 1);
	}

	if (content_type != NULL) {
		/* set content type */
		h_list = curl_slist_append(h_list,
				apr_psprintf(r->pool, "%s: %s", OIDC_HTTP_HDR_CONTENT_TYPE,
						content_type));
	}

	/* see if we need to add any custom headers */
	if (h_list != NULL)
		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, h_list);

	if (pass_cookies != NULL) {
		/* gather cookies that we need to pass on from the incoming request */
		char *cookie_string = NULL;
		for (i = 0; i < pass_cookies->nelts; i++) {
			const char *cookie_name = ((const char**) pass_cookies->elts)[i];
			char *cookie_value = oidc_util_get_cookie(r, cookie_name);
			if (cookie_value != NULL) {
				cookie_string =
						(cookie_string == NULL) ?
								apr_psprintf(r->pool, "%s=%s", cookie_name,
										cookie_value) :
										apr_psprintf(r->pool, "%s; %s=%s",
												cookie_string, cookie_name,
												cookie_value);
			}
		}

		/* see if we need to pass any cookies */
		if (cookie_string != NULL) {
			oidc_debug(r, "passing browser cookies on backend call: %s",
					cookie_string);
			curl_easy_setopt(curl, CURLOPT_COOKIE, cookie_string);
		}
	}

	/* set the target URL */
	curl_easy_setopt(curl, CURLOPT_URL, url);

	/* call it and record the result */
	int rv = TRUE;
	if (curl_easy_perform(curl) != CURLE_OK) {
		oidc_error(r, "curl_easy_perform() failed on: %s (%s)", url,
				curlError[0] ? curlError : "");
		rv = FALSE;
		goto out;
	}

	long response_code;
	curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
	oidc_debug(r, "HTTP response code=%ld", response_code);

	*response = apr_pstrmemdup(r->pool, curlBuffer.memory, curlBuffer.size);

	/* set and log the response */
	oidc_debug(r, "response=%s", *response ? *response : "");

out:

	/* cleanup and return the result */
	if (h_list != NULL)
		curl_slist_free_all(h_list);
	curl_easy_cleanup(curl);

	return rv;
}