首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用google-http-client和google-http-client-apache-v2在代理后面生成NonRepeatableRequestException。

使用google-http-client和google-http-client-apache-v2在代理后面生成NonRepeatableRequestException。
EN

Stack Overflow用户
提问于 2021-06-16 12:09:32
回答 1查看 615关注 0票数 0

我使用google-http-clientgoogle-http-client-apache-v2库在代理后面创建POST request

代码语言:javascript
复制
// 1.- Setting ssl and proxy
HttpClientBuilder builder = HttpClientBuilder.create();
            
SSLContext sslContext = SslUtils.getTlsSslContext();
SslUtils.initSslContext(sslContext, GoogleUtils.getCertificateTrustStore(), SslUtils.getPkixTrustManagerFactory());
builder.setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext));
            
builder.setProxy(new HttpHost(host, port));
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(new AuthScope(host, port), new UsernamePasswordCredentials(user, pass));
builder.setDefaultCredentialsProvider(credentialsProvider);

// 2.- Build request
HttpTransport httpTransport = new ApacheHttpTransport(builder.build());
HttpRequestFactory factory = httpTransport.createRequestFactory(credential);

HttpContent httpContent = new ByteArrayContent("application/json", "{}")
HttpRequest request = factory.buildRequest("POST", new GenericUrl(url), httpContent);

// 3.- Execute request
HttpResponse httpResponse = request.execute();

该请求产生一个NonRepeatableRequestException

代码语言:javascript
复制
org.apache.http.client.ClientProtocolException
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:187) ~[httpclient-4.5.13.jar!/:4.5.13]
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83) ~[httpclient-4.5.13.jar!/:4.5.13]
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108) ~[httpclient-4.5.13.jar!/:4.5.13]
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56) ~[httpclient-4.5.13.jar!/:4.5.13]
    at com.google.api.client.http.apache.v2.ApacheHttpRequest.execute(ApacheHttpRequest.java:73) ~[google-http-client-apache-v2-1.39.2.jar!/:?]
    at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1012) ~[google-http-client-1.39.2.jar!/:1.39.2]
    at 
    ...
Caused by: org.apache.http.client.NonRepeatableRequestException: Cannot retry request with a non-repeatable request entity.
    at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:225) ~[httpclient-4.5.13.jar!/:4.5.13]
    at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186) ~[httpclient-4.5.13.jar!/:4.5.13]
    at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89) ~[httpclient-4.5.13.jar!/:4.5.13]
    at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110) ~[httpclient-4.5.13.jar!/:4.5.13]
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185) ~[httpclient-4.5.13.jar!/:4.5.13]
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83) ~[httpclient-4.5.13.jar!/:4.5.13]
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108) ~[httpclient-4.5.13.jar!/:4.5.13]
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56) ~[httpclient-4.5.13.jar!/:4.5.13]
    at com.google.api.client.http.apache.v2.ApacheHttpRequest.execute(ApacheHttpRequest.java:73) ~[google-http-client-apache-v2-1.39.2.jar!/:?]
    at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1012) ~[google-http-client-1.39.2.jar!/:1.39.2]

似乎ApacheHttpRequest将ByteArrayContent封装为可重复的(参见JavaDoc)在ContentEntity (即non-repeatable. )中。

在google库中调试执行时,代理返回"407代理身份验证所需“,然后尝试重复请求(猜测包括凭据),而出现此异常是因为google库使用的ContentEntity是不可重复的。

是否有任何方法可以避免与代理握手,包括在第一个请求中包含凭据,以避免实体的重用?

有没有办法告诉使用可重复实体的google库?

尝试使用折叠库版本:

  • google-api-client-1.31.5
  • google-http-client-jackson2-1.39.2
  • google-oauth-client-1.31.5
  • google-http-client-apache-v2-1.39.2
  • google-http-client-1.39.2
  • httpclient-4.5.13
  • httpcore-4.4.14
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-07-07 07:05:42

我在github上发布了解决办法,以防它对某人有帮助:

作为解决办法,我尝试的是将ApacheHttpTransport包装在CustomApacheHttpTransport,中,它将方法的结果委托给ApacheHttpTransport,但buildRequest方法除外。

CustomApacheHttpTransport中的这个buildRequest方法生成一个类型为CustomApacheHttpRequest.的自定义请求。

代码语言:javascript
复制
public final class CustomApacheHttpTransport extends HttpTransport {
    
    private ApacheHttpTransport apacheHttpTransport;
    
    public CustomApacheHttpTransport (HttpClient httpClient) {
        this.apacheHttpTransport = new ApacheHttpTransport(httpClient);
    }
    
    @Override
    protected LowLevelHttpRequest buildRequest (String method, String url) {
        HttpRequestBase requestBase;
        if (method.equals("DELETE")) {
            requestBase = new HttpDelete(url);
        } else if (method.equals("GET")) {
            requestBase = new HttpGet(url);
        } else if (method.equals("HEAD")) {
            requestBase = new HttpHead(url);
        } else if (method.equals("PATCH")) {
            requestBase = new HttpPatch(url);
        } else if (method.equals("POST")) {
            ..
        }
        return new CustomApacheHttpRequest(apacheHttpTransport.getHttpClient(), requestBase);
    }
}

这个自定义请求与ApacheHttpRequest类似,除非它在执行时创建一个自定义实体,CustomContentEntity,将根据请求内容是否支持重试而可重复。

代码语言:javascript
复制
final class CustomApacheHttpRequest extends LowLevelHttpRequest {
    
    private final HttpClient httpClient;
    private final HttpRequestBase request;
    private RequestConfig.Builder requestConfig;
    
    CustomApacheHttpRequest (HttpClient httpClient, HttpRequestBase request) {
        this.httpClient = httpClient;
        this.request = request;
        this.requestConfig = RequestConfig.custom().setRedirectsEnabled(false).setNormalizeUri(false).setStaleConnectionCheckEnabled(false);
    }
    
    ...
        
    @Override
    public LowLevelHttpResponse execute () throws IOException {
        if (this.getStreamingContent() != null) {
            Preconditions.checkState(request instanceof HttpEntityEnclosingRequest, "Apache HTTP client does not support %s requests with content.", request.getRequestLine().getMethod());
            
            CustomContentEntity entity = new CustomContentEntity(this.getContentLength(), this.getStreamingContent());
            entity.setContentEncoding(this.getContentEncoding());
            entity.setContentType(this.getContentType());
            if (this.getContentLength() == -1L) {
                entity.setChunked(true);
            }
            ((HttpEntityEnclosingRequest) request).setEntity(entity);
        }
        
        request.setConfig(requestConfig.build());
        return new CustomApacheHttpResponse(request, httpClient.execute(request));
    }
}

CustomContentEntity中的关键是isRepeatable方法,它不像ContentEntity那样总是返回false

代码语言:javascript
复制
final class CustomContentEntity extends AbstractHttpEntity {
    
    private final long contentLength;
    private final StreamingContent streamingContent;
    
    CustomContentEntity (long contentLength, StreamingContent streamingContent) {
        this.contentLength = contentLength;
        this.streamingContent = streamingContent;
    }
    
    @Override
    public boolean isRepeatable () {
        return ((HttpContent) streamingContent).retrySupported();
    }
    ...
}

此外,我还必须创建CustomApacheHttpResponse作为CustomApacheHttpRequest的响应,因为ApacheHttpResponse是包私有的(CustomApacheHttpResponseApacheHttpResponse).完全一样。

票数 -2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68002387

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档