java.net.SocketException: Unexpected end of file from server using Spring's RestTemplate
Asked Answered
H

3

8

I've already checked several questions / answers regarding similar subjects, but can't find the proper answer for my case.

I'm using Spring's RestTemplate but fails to get the response from a third party server with the following exception:

Exception in thread "main" org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://64.76.157.19:8283/ENAP/ProveedorExterno/v1.0/insertarUltimaPosicion":Unexpected end of file from server; nested exception is java.net.SocketException: Unexpected end of file from server
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:567)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:512)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:454)
    at cl.waypoint.integracion.GenericCallback.sendEnap(GenericCallback.java:187)
    at cl.waypoint.integracion.GenericCallback.main(GenericCallback.java:167)
Caused by: java.net.SocketException: Unexpected end of file from server
    at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:718)
    at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:579)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1322)
    at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:468)
    at org.springframework.http.client.SimpleClientHttpResponse.getRawStatusCode(SimpleClientHttpResponse.java:48)
    at cl.waypoint.integracion.GenericCallback$LoggingRequestInterceptor.log(GenericCallback.java:229)
    at cl.waypoint.integracion.GenericCallback$LoggingRequestInterceptor.intercept(GenericCallback.java:216)
    at org.springframework.http.client.InterceptingClientHttpRequest$RequestExecution.execute(InterceptingClientHttpRequest.java:84)
    at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:69)
    at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
    at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:551)
    ... 4 more

But when sending the same request/headers/payload using command line curl there seems to be no problem at all, this is the verbose output of it:

*   Trying A.B.C.D...
* Connected to A.B.C.D (A.B.C.D) port 8283 (#0)
> POST /ENAP/ProveedorExterno/v1.0/insertarUltimaPosicion HTTP/1.1
> Host: A.B.C.D:8283
> User-Agent: curl/7.43.0
> Accept: */*
> Content-Type: application/json
> Authorization: Bearer dsgfsdgf786dsfg7dsgf
> Content-Length: 567
> 
* upload completely sent off: 567 out of 567 bytes
< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: POST
< Access-Control-Allow-Headers: authorization,Access-Control-Allow-Origin,Content-Type
< Content-Type: application/json
< Date: Wed, 27 Jul 2016 13:35:26 GMT
< Transfer-Encoding: chunked
< 
* Connection #0 to host 64.76.157.19 left intact

PS: Authorization token and server's IP address have been changed for security reasons.

Spring seems to hang for a while and then then throw the exception, perhaps it's waiting for something by default...Content-Length header on the response? If so, can that be overriden?

The Exception comes from the following interceptor:

class LoggingRequestInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
            throws IOException {

        ClientHttpResponse response = execution.execute(request, body);

        log(request, body, response);

        return response;
    }

    private void log(HttpRequest request, byte[] body, ClientHttpResponse response) throws IOException {
        HttpHeaders headers = request.getHeaders();
        System.out.println("=============================");
        for (Entry<String, List<String>> header : headers.entrySet()) {
            System.out.println(header.getKey() + ": " + header.getValue());
        }
        System.out.println("=============================");
        System.out.println(new String(body));
        System.out.println(response.getRawStatusCode());
        System.out.println(response.getStatusText());

    }
}

Which is used from the following code snippet:

private void sendEnap(String patente, String fecha, String latitud, String longitud, BigInteger sentido,
        BigInteger velocidad, int ignicion) {
    RestTemplate restTemplate = new RestTemplate();
    // set interceptors/requestFactory
    ClientHttpRequestInterceptor ri = new LoggingRequestInterceptor();
    List<ClientHttpRequestInterceptor> ris = new ArrayList<ClientHttpRequestInterceptor>();
    ris.add(ri);
    restTemplate.setInterceptors(ris);

    MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>();
    headers.add("Authorization", "Bearer " + ENANGAB_TOKEN);
    headers.add("Content-Type", MediaType.APPLICATION_JSON.toString());
    headers.add("User-Agent", "Waypoint");
    EnapRequest enapRequest = new EnapRequest(patente, fecha, latitud, longitud, sentido, velocidad, ignicion);
    HttpEntity<EnapRequest> request = new HttpEntity<EnapRequest>(enapRequest, headers);

    ResponseEntity<EnapResponse> response = restTemplate.exchange(ENAP_ENDPOINT, HttpMethod.POST, request,
            EnapResponse.class);
    System.out.println(response.getBody());

}

If the interceptor is disabled, same exception arises but now with this stacktrace:

Exception in thread "main" org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://64.76.157.19:8283/ENAP/ProveedorExterno/v1.0/insertarUltimaPosicion":Unexpected end of file from server; nested exception is java.net.SocketException: Unexpected end of file from server
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:567)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:512)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:454)
    at cl.waypoint.integracion.GenericCallback.sendEnap(GenericCallback.java:187)
    at cl.waypoint.integracion.GenericCallback.main(GenericCallback.java:167)
Caused by: java.net.SocketException: Unexpected end of file from server
    at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:718)
    at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:579)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1322)
    at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:468)
    at org.springframework.http.client.SimpleClientHttpResponse.getRawStatusCode(SimpleClientHttpResponse.java:48)
    at org.springframework.http.client.AbstractClientHttpResponse.getStatusCode(AbstractClientHttpResponse.java:33)
    at org.springframework.web.client.DefaultResponseErrorHandler.getHttpStatusCode(DefaultResponseErrorHandler.java:56)
    at org.springframework.web.client.DefaultResponseErrorHandler.hasError(DefaultResponseErrorHandler.java:50)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:552)
    ... 4 more

The above is another hint for a missing header on the response, don't know which one, and how to avoid such wait too.

Any hints would be appreciated

EDIT:

Headers sent:

Accept: [application/json, application/*+json]
Authorization: [Bearer dsgfsdgf786dsfg7dsgf]
Content-Type: [application/json]
User-Agent: [Waypoint]
Content-Length: [567]

EnapRequest class: package cl.waypoint.integracion;

import java.math.BigInteger;

import com.fasterxml.jackson.annotation.JsonProperty;

public class EnapRequest {

    @JsonProperty("token_proveedor")
    private String tokenProveedor = GenericCallback.ENANGAB_TOKEN;
    private Posicion[] posicion;

    public EnapRequest(String patente, String fecha, String latitud, String longitud, BigInteger sentido,
            BigInteger velocidad, int  ignicion) {
        posicion = new Posicion[1];
        posicion[0] = new Posicion(patente, fecha, latitud, longitud, sentido, velocidad, ignicion);
    }

    public String getTokenProveedor() {
        return tokenProveedor;
    }

    public void setTokenProveedor(String tokenProveedor) {
        this.tokenProveedor = tokenProveedor;
    }

    public Posicion[] getPosicion() {
        return posicion;
    }

    public void setPosicion(Posicion[] posicion) {
        this.posicion = posicion;
    }
}

The request body is in fact being sent as JSON (exactly the same as with curl, pretty print here for improved reading):

{
  "posicion": [
    {
      "patente": "AB1234",
      "latitud": "-36.752752",
      "longitud": "-73.0804947",
      "direccion": "120",
      "velocidad": "65",
      "transportista": "ENANGAB",
      "sensora1": null,
      "sensora2": null,
      "sensora3": null,
      "mopo_sensord1": null,
      "mopo_sensord2": null,
      "mopo_sensord3": null,
      "mopo_sensord4": null,
      "mopo_sensord5": null,
      "mopo_sensord6": null,
      "opcional1": null,
      "opcional2": null,
      "opcional3": null,
      "opcional4": null,
      "codigo_interno": null,
      "fecha_hora": "2016-07-15T14:24:00",
      "mopo_estado": "1",
      "mopo_estado_ignicion": "1",
      "moev_numero_evento": "45"
    }
  ],
  "token_proveedor": "dsgfsdgf786dsfg7dsgf"
}

The RestTemplate object has already configured support for the following converters:

class org.springframework.http.converter.ByteArrayHttpMessageConverter
class org.springframework.http.converter.StringHttpMessageConverter
class org.springframework.http.converter.ResourceHttpMessageConverter
class org.springframework.http.converter.xml.SourceHttpMessageConverter
class org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter
class org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter
class org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
Hotien answered 27/7, 2016 at 13:44 Comment(2)
It seems there is something wrong with headers or body of your POST request. Could you add your EnapRequest class?Theaterintheround
@Theaterintheround added headers, body and request class..thanks!Hotien
T
5

I think the problem here is that your request has a wrong data type which server can not parse and thus can not reply.

Since you are sending a POST request with JSON Content-Type header, your EnapRequest must be JSON-encoded.

To do that, you need to make sure EnapRequest is a POJO class, then modify your code inside sendEnap()

RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());

and include Jackson libraries in the classpath

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.8.1</version>
</dependency>
Theaterintheround answered 27/7, 2016 at 15:10 Comment(4)
Please see question edit, including request headers, body and request class. I'm already sending JSONHotien
What happen if you try curl with headers User-Agent: Waypoint and Accept: application/json, application/*+json? Currently curl has User-Agent: curl/7.43.0 and Accept: */*Theaterintheround
mmmm....nice hint, no response changing the accept header! Switch the RestTemplate to Accept: / and now it works, a bit odd though as Curl does report that the response is "application/json; charset=UTF-8", via Spring no such header is found.Hotien
Glad to know it's working now. I think you may need to read the server API document for detailed instruction.Theaterintheround
O
0

I had the same error, check the packet size configured at the network level for the server(if there is a mismatch - the packet size expected is lesser than the packet size recieved, this error would arise)
Try running the following command on the server you are hitting:
ip addr | grep mtu

Outthink answered 15/11, 2018 at 7:21 Comment(0)
T
0

I also had the same error, but with another cause. The Java application using restTemplate was forcibly applying a different value to the Host header.

Whenever I tried to make the same request using cURL, I was successful.

I then decided to use the tcpdump tool to obtain a snapshot of the network, open it in Wireshark and see in full what was being passed, I saw that my Host header value had been automatically replaced.

For my problem, this issue saved me.

If you are having the same problem, check that your request headers are correct and that the client server is not closing the request prematurely due to a wrong header.

Tempestuous answered 20/8 at 18:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.