如何发送Host头与目标地址不同的Java HTTP请求?
发送自定义Host头的HTTP请求(Host与目标地址不一致)的实现方案
问题背景
需要发送HTTP请求,要求请求的Host头与实际连接的目标地址不同,已知变量如下:
String host = "https://example.com/something"; String destination_address = "https://8.8.8.8"; String http_version = "HTTP/2"; String http_method = "POST"; boolean follow_redirects = true;
当前使用Apache Http-Client-5的代码(存在逻辑问题):
H2AsyncClientBuilder builder_2 = HttpAsyncClients.customHttp2(); if (follow_redirects) { builder_2.setRedirectStrategy(new DefaultRedirectStrategy()); } // 错误:builder_1未定义,且用代理方式处理目标地址是错误逻辑 if (!(Objects.equals(destination_address, host))) { builder_1.setProxy(HttpHost.create(destination_address)); } CloseableHttpAsyncClient client = HttpAsyncClients.createDefault(); client = builder_2.build(); client.start(); // POST请求部分 http_request = SimpleRequestBuilder.post(host) .setBody(request_body, ContentType.TEXT_PLAIN) .setHeaders(request_headers) .build(); Future<SimpleHttpResponse> future = client.execute(http_request, null); SimpleHttpResponse http_response = future.get(); client.close(CloseMode.GRACEFUL);
可行实现方案
方案一:修正Apache HttpClient 5异步HTTP/2实现
原代码核心问题是错误使用代理逻辑,正确做法是直接连接到destination_address,同时手动设置Host头为host中的域名部分。
修正后的代码:
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.impl.nio.H2AsyncClientBuilder; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpHost; import java.net.URI; import java.util.concurrent.Future; // 解析目标地址和Host信息 HttpHost targetHost = HttpHost.create(destination_address); URI hostUri = URI.create(host); String targetPath = hostUri.getPath() + (hostUri.getQuery() != null ? "?" + hostUri.getQuery() : ""); String customHostHeader = hostUri.getHost() + (hostUri.getPort() != -1 ? ":" + hostUri.getPort() : ""); // 构建HTTP/2异步客户端 H2AsyncClientBuilder builder = HttpAsyncClients.customHttp2(); if (follow_redirects) { builder.setRedirectStrategy(new DefaultRedirectStrategy()); } CloseableHttpAsyncClient client = builder.build(); client.start(); // 构建POST请求 SimpleHttpRequest httpRequest = SimpleRequestBuilder.post() .setHttpHost(targetHost) .setPath(targetPath) // 手动设置自定义Host头 .setHeader("Host", customHostHeader) .setBody(request_body, ContentType.TEXT_PLAIN) .setHeaders(request_headers) .build(); // 执行请求 Future<SimpleHttpResponse> future = client.execute(httpRequest, HttpClientContext.create(), null); SimpleHttpResponse response = future.get(); // 关闭客户端 client.close(CloseMode.GRACEFUL);
关键说明:
- 从
host中提取实际请求路径和自定义Host头值 - 直接指定请求的目标
HttpHost为destination_address - 手动添加
Host头覆盖默认值
方案二:Apache HttpClient 5同步HTTP/2实现
如果不需要异步处理,可使用同步版本:
import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.nio.H2ClientBuilder; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.io.entity.StringEntity; import java.net.URI; // 解析目标和Host信息 HttpHost targetHost = HttpHost.create(destination_address); URI hostUri = URI.create(host); String targetPath = hostUri.getPath() + (hostUri.getQuery() != null ? "?" + hostUri.getQuery() : ""); String customHostHeader = hostUri.getHost() + (hostUri.getPort() != -1 ? ":" + hostUri.getPort() : ""); // 构建同步HTTP/2客户端 H2ClientBuilder builder = HttpClients.customHttp2(); if (follow_redirects) { builder.setRedirectStrategy(new DefaultRedirectStrategy()); } CloseableHttpClient client = builder.build(); // 构建POST请求 HttpPost httpPost = new HttpPost(targetPath); httpPost.setEntity(new StringEntity(request_body, ContentType.TEXT_PLAIN)); httpPost.setHeader("Host", customHostHeader); // 添加其他请求头 if (request_headers != null) { httpPost.setHeaders(request_headers); } // 执行请求 try (CloseableHttpClient c = client) { var response = c.execute(targetHost, httpPost); // 处理响应 }
方案三:使用OkHttp实现
OkHttp对HTTP/2支持良好,处理自定义Host头更简洁:
import okhttp3.*; import java.io.IOException; // 解析目标地址和Host信息 HttpUrl targetUrl = HttpUrl.parse(destination_address); HttpUrl hostUrl = HttpUrl.parse(host); assert targetUrl != null && hostUrl != null; // 构建请求路径:保留host中的路径和查询参数 String targetPath = hostUrl.encodedPath() + (hostUrl.encodedQuery() != null ? "?" + hostUrl.encodedQuery() : ""); HttpUrl finalTargetUrl = targetUrl.newBuilder() .encodedPath(targetPath) .build(); // 构建OkHttpClient OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); if (follow_redirects) { clientBuilder.followRedirects(true); } // 强制优先使用HTTP/2 clientBuilder.protocols(java.util.List.of(Protocol.HTTP_2, Protocol.HTTP_1_1)); OkHttpClient client = clientBuilder.build(); // 构建请求,手动设置Host头 Request request = new Request.Builder() .url(finalTargetUrl) .method(http_method, RequestBody.create(request_body, MediaType.get("text/plain"))) .header("Host", hostUrl.host() + (hostUrl.port() != -1 ? ":" + hostUrl.port() : "")) // 添加其他请求头 .headers(Headers.of(request_headers)) .build(); // 执行请求 try (Response response = client.newCall(request).execute()) { // 处理响应 } catch (IOException e) { e.printStackTrace(); }
内容的提问来源于stack exchange,提问作者netcat_dd
相关产品推荐
相关产品推荐

