开发者

Content-Length header already present

开发者 https://www.devze.com 2023-01-09 08:08 出处:网络
I am using the Apache HttpClient (4.1) included in Android to execute a HttpPut.I have verified that I only have 1 content-length header.However, every time I send the request, I get a protocol except

I am using the Apache HttpClient (4.1) included in Android to execute a HttpPut. I have verified that I only have 1 content-length header. However, every time I send the request, I get a protocol exception about the Content-Length header already specified.

HttpClient client = new DefaultHttpClient();
putMethod = new HttpPut(url + encodedFileName);
putMethod.addHeader(..)  //<-once for each header
putMethod.setEntity(new ByteArrayEntity(data));
client.execute(putMethod);  //throws Exception

Caused by: org.apache.http.ProtocolException: Content-Length header already present at org.apache.http.protocol.RequestContent.process(RequestContent.java:70) at org.apache.http.protocol.BasicHttpProcessor.pro开发者_Python百科cess(BasicHttpProcessor.java:290)

Any ideas?


As pointed out by igor.zh, this problem can occur if using Spring's HttpComponentsMessageSender class. To be more precise though, this is only a problem if you are passing your own instance of HttpClient into the HttpComponentsMessageSender constructor - the issue is handled automatically otherwise.

As of spring-ws 2.1.4, the HttpComponentsMessageSender.RemoveSoapHeadersInterceptor subclass that is used in the default constructor was made public to address this issue (see https://jira.spring.io/browse/SWS-835) and so can be used in your own HttpClient instances instead of writing your own class to do it. It also clears the HTTP.TRANSFER_ENCODING header.

Use the HttpClientBuilder.addInterceptorFirst method to inject this interceptor into your own HttpClient instance. Example below using XML bean wiring. If anybody knows a more concise way of constructing the HttpClient instance (aside from writing a factory bean class), I'm all ears!

<bean id="httpClientBuilder" class="org.apache.http.impl.client.HttpClientBuilder" factory-method="create"/>

<bean id="interceptedHttpClientBuilder" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetObject" ref="httpClientBuilder" />
    <property name="targetMethod" value="addInterceptorFirst"> </property>
    <property name="arguments">
        <list>
            <bean class="org.springframework.ws.transport.http.HttpComponentsMessageSender.RemoveSoapHeadersInterceptor"/>
        </list>
    </property>
</bean>

<bean id="httpClient" factory-bean="interceptedHttpClientBuilder" factory-method="build" />

<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
    <constructor-arg ref="messageFactory"/>
    <property name="messageSender">
        <bean class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
            <property name="httpClient" ref="httpClient"/>
        </bean>
    </property>
</bean>

Alternatively, if you can, just allow HttpComponentsMessageSender to construct its own HttpClient instance rather than passing one to it. Minor note on this: as of spring-ws 2.2.0-RELEASE, the default constructor for HttpComponentsMessageSender continues to use the DefaultHttpClient class, which is now deprecated. Hopefully this will be addressed in a future release.


I've not used HttpClient myself, but I suspect that the problem is that putMethod.setEntity(...) is implicitly supplying a content length and you are also setting it explicitly via one of the putMethod.addHeader(...) calls.


That happens to me when I used http://docs.spring.io/spring-ws/site/apidocs/org/springframework/ws/transport/http/HttpComponentsMessageSender.html as a Spring WebService Message Sender. In that case the stuff like HttpPut or HttpRequest are not easily accessible, so, building HttpClient with HttpClientBuilder, I ended up inserting a HttpRequestInterceptor in front of the culprit RequestContent :

private static class ContentLengthHeaderRemover implements HttpRequestInterceptor{
    @Override
    public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
        request.removeHeaders(HTTP.CONTENT_LEN);// fighting org.apache.http.protocol.RequestContent's ProtocolException("Content-Length header already present");
    }
}
...
    HttpClientBuilder httpClientBuilder = HttpClients.custom();
    httpClientBuilder.addInterceptorFirst(new CcontentLengthHeaderRemover());


John Rix's answer has the right idea. Here's how you do it with plain java:

HttpClient client = HttpClients.custom()
    .addInterceptorFirst(new RemoveSoapHeadersInterceptor())
    .build();


If you checkout http://docjar.org/docs/api/org/apache/http/protocol/RequestContent.html you'll notice that it throws that exception if you've set it yourself. Therefore, the internal working set the content length automatically. This also means, to set it to "0", you need to set the entity to null.


If you are adopting the XML configuration answer by John Rix, make sure to add a depends-on attribute to the httpClient bean so the RemoveSoapHeadersInterceptor interceptor is added to the ClientBuilder before the httpClient bean gets created. SOAP headers removal hasn't taken effect until I have done so.

<bean id="httpClient" factory-bean="httpClientBuilder" factory-method="build" depends-on="interceptedHttpClientBuilder"/>


@John Rix's answer This code helped solved the problem

    private static class ContentLengthHeaderRemover implements HttpRequestInterceptor{
            @Override
            public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
                request.removeHeaders(HTTP.CONTENT_LEN);// fighting org.apache.http.protocol.RequestContent's ProtocolException("Content-Length header already present");
            }
        }
HttpClient client = HttpClients.custom()
                .addInterceptorFirst(new ContentLengthHeaderRemover())
                .build();
0

精彩评论

暂无评论...
验证码 换一张
取 消