rest assured java.net.SocketException - java

I am executing a simple post test in rest-assured,
While exeuting post() method I am getting "java.net.SocketException: Operation timed out"
#Test
public void validateGetProductInfo_Prod() throws IOException {
String jsonBody = new String(Files.readAllBytes(Paths.get("src/test/resources/product.json")));
Headers basicHeaders = new Headers(asList(
new Header("X-ApplicationAuthorizationToken", "AAABB"),
new Header("Authorization", "Basic ABC"),
new Header("Content-Type", "application/json")));
String produPath = "http://api.abcd.com/product/v1/product/info";
given().headers(basicHeaders)
.body(jsonBody)
.when()
.post(produPath)
.then().using().defaultParser(Parser.JSON)
.statusCode(200);
}
I had validated that there is no error till the when is executed,
On executign the post() method facing the exception?

The original issue is the service end-point is expecting a user-agent header, the header was not sent in the request so the end-point was not responding.
Once the header was added the issues resolved.
Note:
Postman adds the default chrom user-agent if no header was provided

Related

MockServer unexpectedly responds with empty body

Let's see a test, which is using MockServer (org.mock-server:mockserver-netty:5.10.0) for mocking responses.
It is expected that the response body will be equal to string "something".
Nevertheless, this test fails, because the response body is an empty string.
#Test
void test1() throws Exception {
var server = ClientAndServer.startClientAndServer(9001);
server
.when(
request().withMethod("POST").withPath("/checks/"),
exactly(1)
)
.respond(
response()
.withBody("\"something\"")
.withStatusCode(205)
.withHeader("Content-Type", "application/json")
);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://localhost:9001/checks/"))
.POST(BodyPublishers.noBody())
.build();
HttpResponse<String> response =
HttpClient.newHttpClient().send(request, BodyHandlers.ofString());
assertEquals(205, response.statusCode());
assertEquals("something", response.body()); // fails
}
How to make the response body be equal to the string provided in response().withBody(...)?
The problem is on the client side. It drops content.
Why!?
Because, HTTP 205 is RESET_CONTENT.
This status was chosen accidentally for test as "somenthing different from HTTP 200", and unfortunately caused this behaviour.
Looks like it is very popular "accidental" mistake (i.e. here), although it is strictly in accordance with the HTTP spec.

Using SOAPAction in HTTP header to consume SOAP Service in JAVA

I'm trying to implement a soap service consumer in Java, using spring WebServiceGatewaySupport.
When I'm using curl to consume the service as below, it is giving proper response.
curl -d #request.xml -H 'SOAPAction:abc:mnEvent#DoAction' https://myhost.org/cd/doAction.jsp
I'm trying to implement the same using JAVA, by adding following HttpHeaders in a template class inheriting from WebServiceGatewaySupport
public O callWebService(String url, I request) {
return (O) getWebServiceTemplate().marshalSendAndReceive(url, request, new WebServiceMessageCallback() {
#Override
public void doWithMessage(WebServiceMessage message) {
TransportContext transportContext = TransportContextHolder.getTransportContext();
HttpComponentsConnection connection = (HttpComponentsConnection) transportContext.getConnection();
connection.getHttpPost().addHeader("SOAPAction", "abc:mnEvent#DoAction");
}
});
}
With this code, I'm getting an error message like below.
SOP-330006 The method 'DoAction, ""' is not defined in SOAP service 'abc:mnEvent'.
What do I miss here when moving curl command to JAVA?
The error message SOP-330006 The method 'DoAction, ""' is not defined in SOAP service 'abc:mnEvent'. indicates, there are two soap actions in the request.
Explicit SoapAction added in HttpHeader
Implicit SoapAction in SoapMessage
To avoid this issue, we need to remove the soapAction from header and set it in SoapMessage.
SaajSoapMessage soapMessage = (SaajSoapMessage) message;
soapMessage.setSoapAction("abc:mnEvent#DoAction");

Jersey 1x does not send Content-Length header in request

I'd like to send a Content-Length header in an HTTP request using Jersey client 1.18 over Dropwizard.
LoggingFilter shows that the request does not contain Content-Length and the OP says it doesn't receive it.
However, when I try to add it manually, I get an org.apache.http.ProtocolException: Content-Length header already present error.
Here's my code:
byte[] entity = objectMapper.writeValueAsBytes(msg);
response = client.resource(uri)
.type(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.header(TransactionIdUtils.TRANSACTION_ID_HEADER, transactionId)
.header(CREDENTIALS_HEADER, this.credentials)
.header(HttpHeaders.CONTENT_LENGTH, entity.length)
.post(ClientResponse.class, entity);
Any advice is much appreciated.
Edit: Here's my LoggingFilter code:
client.addFilter(new LoggingFilter(new JulFacade()));
//a class for redirecting Jersey logging to sl4j
private static class JulFacade extends java.util.logging.Logger {
JulFacade() {
super("Jersey", null);
}
#Override
public void info(String msg) {
LOGGER.info(msg);
}
}

OkHttp POST error "connection reset by peer" for unauthorized call and large payload

I've been struggling with the following issue:
I have a spring boot application which allows a user to post JSON content to an API endpoint. To use this endpoint, the user has to authenticate himself via basic authentication. Moreover, I use OkHttp (3.6.0) as an HTTP client.
Now if I post a large payload (> 4 MB) while being unauthorized, the OkHttp client fails with the following error:
java.net.SocketException: Connection reset by peer: socket write error
To reproduce the issue, I created a minimal client and server:
Server (Spring Boot Web App)
#SpringBootApplication
#RestController
public class App {
#PostMapping
public String create(#RequestBody Object obj) {
System.out.println(obj);
return "success";
}
public static void main(String[] args) {
SpringApplication.run(App.class);
}
}
Client (OkHttp 3.6.0)
public class Main {
public static void main(String[] args) {
OkHttpClient client = new OkHttpClient
.Builder()
.build();
Request request = new Request.Builder()
.url("http://localhost:8080")
.header("Content-Type", "application/json")
.post(RequestBody.create(MediaType.parse("application/json"), new File("src/main/java/content.json")))
// .post(RequestBody.create(MediaType.parse("application/json"), new File("src/main/java/content-small.json")))
.build();
try {
Response response = client.newCall(request).execute();
System.out.println(response);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Instead of the previously mentioned exception ("java.net.SocketException: Connection reset by peer: socket write error"), I would expect the response to be a default error message with HTTP status code 401, e.g. {"timestamp":1508767498068,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/"}. This is the result I get when using cURL and Postman as clients.
When I'm using less payload (content-small.json; approx. 1KB) instead of the large payload (content.json; approx. 4881KB), I receive the expected response, i.e. Response{protocol=http/1.1, code=401, message=, url=http://localhost:8080/}.
The issue is actually embedded in a larger project with Eureka and Feign clients. Threfore, I would like to continue using OkHttp client and I need the expected behavior.
My problem analysis
Of course, I tried to solve this problem myself for quite some time now. The IOException occurs when the request body is written to the HTTP stream:
if (permitsRequestBody(request) && request.body() != null) {
Sink requestBodyOut = httpStream.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}
My assumption is that the server closes the connection as soon as it receives the headers (as the request is unauthorized), but the client continues trying to write to the stream although it is already closed.
Update
I've also implemented a simple client with Unirest which shows the same behavior. Implementation:
public class UnirestMain {
public static void main(String[] args)
throws IOException, UnirestException {
HttpResponse response = Unirest
.post("http://localhost:8080")
.header("Content-Type", "aplication/json")
.body(Files.readAllBytes(new File("src/main/java/content.json").toPath()))
// .body(Files.readAllBytes(new File("src/main/java/content-small.json").toPath()))
.asJson();
System.out.println(response.getStatus());
System.out.println(response.getStatusText());
System.out.println(response.getBody());
}
}
Expected output: {"path":"/","error":"Unauthorized","message":"Full authentication is required to access this resource","timestamp":1508769862951,"status":401}
Actual output: java.net.SocketException: Connection reset by peer: socket write error

Jersey 2.0 Content-Length not set

I'm trying to post to a web service that requires the Content-Length header to be set using the following code:
// EDIT: added apache connector code
ClientConfig clientConfig = new ClientConfig();
ApacheConnector apache = new ApacheConnector(clientConfig);
// setup client to log requests and responses and their entities
client.register(new LoggingFilter(Logger.getLogger("com.example.app"), true));
Part part = new Part("123");
WebTarget target = client.target("https://api.thing.com/v1.0/thing/{thingId}");
Response jsonResponse = target.resolveTemplate("thingId", "abcdefg")
.request(MediaType.APPLICATION_JSON)
.header(HttpHeaders.AUTHORIZATION, "anauthcodehere")
.post(Entity.json(part));
From the release notes https://java.net/jira/browse/JERSEY-1617 and the Jersey 2.0 documentation https://jersey.java.net/documentation/latest/message-body-workers.html it implies that Content-Length is automatically set. However, I get a 411 response code back from the server indicating that Content-Length is not present in the request.
Does anyone know the best way to get the Content-Length header set?
I've verified through setting up a logger that the Content-Length header is not generated in the request.
Thanks.
I ran a quick test with Jersey Client 2.2 and Netcat, and it is showing me that Jersey is sending the Content-Length header, even though the LoggingFilter is not reporting it.
To do this test, I first ran netcat in one shell.
nc -l 8090
Then I executed the following Jersey code in another shell.
Response response = ClientBuilder.newClient()
.register(new LoggingFilter(Logger.getLogger("com.example.app"), true))
.target("http://localhost:8090/test")
.request()
.post(Entity.json(IOUtils.toInputStream("{key:\"value\"}")));
After running this code, the following lines get logged.
INFO: 1 * LoggingFilter - Request received on thread main
1 > POST http://localhost:8090/test
1 > Content-Type: application/json
{key:"value"}
However, netcat reports several more headers in the message.
POST /test HTTP/1.1
Content-Type: application/json
User-Agent: Jersey/2.0 (HttpUrlConnection 1.7.0_17)
Host: localhost:8090
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 13
{key:"value"}
I ran this test on OSX with Java6 and Java7, with the same results. I also ran the test in Jersey 2.0, with similar results.
After looking at the source code for the ApacheConnector class, I see the problem. When a ClientRequest is converted to a HttpUriRequest a private method getHttpEntity() is called that returns a HttpEntity. Unfortunately, this returns a HttpEntity whose getContentLength() always returns a -1.
When the Apache http client creates the request it will consult the HttpEntity object for a length and since it returns -1 no Content-Length header will be set.
I solved my problem by creating a new connector that is a copy of the source code for the ApacheConnector but has a different implementation of the getHttpEntity(). I read the entity from the original ClientRequest into a byte array and then wrap that byte array with a ByteArrayEntity. When the Apache Http client creates the request it will consult the entity and the ByteArrayEntity will respond with the correct content length which in turns allows the Content-Length header to be set.
Here's the relevant code:
private HttpEntity getHttpEntity(final ClientRequest clientRequest) {
final Object entity = clientRequest.getEntity();
if (entity == null) {
return null;
}
byte[] content = getEntityContent(clientRequest);
return new ByteArrayEntity(content);
}
private byte[] getEntityContent(final ClientRequest clientRequest) {
// buffer into which entity will be serialized
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
// set up a mock output stream to capture the output
clientRequest.setStreamProvider(new OutboundMessageContext.StreamProvider() {
#Override
public OutputStream getOutputStream(int contentLength) throws IOException {
return baos;
}
});
try {
clientRequest.writeEntity();
}
catch (IOException e) {
LOGGER.log(Level.SEVERE, null, e);
// re-throw new exception
throw new ProcessingException(e);
}
return baos.toByteArray();
}
WARNING: My problem space was constrained and only contained small entity bodies as part of requests. This method proposed above may be problematic with large entity bodies such as images so I don't think this is a general solution for all.
I've tested with Jersey 2.25.1 a simpler solution that consists in setting setChunkedEncodingEnabled(false) in the Jersey Client configuration. Instead of using a chunked encoding, the whole entity is serialised in memory and the Content-Length is set on the request.
For reference, here is an example of a configuration I've used:
private Client createJerseyClient(Environment environment) {
Logger logger = Logger.getLogger(getClass().getName());
JerseyClientConfiguration clientConfig = new JerseyClientConfiguration();
clientConfig.setProxyConfiguration(new ProxyConfiguration("localhost", 3333));
clientConfig.setGzipEnabled(false);
clientConfig.setGzipEnabledForRequests(false);
clientConfig.setChunkedEncodingEnabled(false);
return new JerseyClientBuilder(environment)
.using(clientConfig)
.build("RestClient")
.register(new LoggingFeature(logger, Level.INFO, null, null));
}
I've used mitmproxy to verify the request headers and the Content-Length header was set correctly.
This is supported in Jersey 2.5 (https://java.net/jira/browse/JERSEY-2224). You could use https://jersey.java.net/apidocs/latest/jersey/org/glassfish/jersey/client/RequestEntityProcessing.html#BUFFERED to stream your content. I put together a simple example that shows both chunked and buffering content using ApacheConnector. Checkout this project: https://github.com/aruld/sof-18157218
public class EntityStreamingTest extends JerseyTest {
private static final Logger LOGGER = Logger.getLogger(EntityStreamingTest.class.getName());
#Path("/test")
public static class HttpMethodResource {
#POST
#Path("chunked")
public String postChunked(#HeaderParam("Transfer-Encoding") String transferEncoding, String entity) {
assertEquals("POST", entity);
assertEquals("chunked", transferEncoding);
return entity;
}
#POST
public String postBuffering(#HeaderParam("Content-Length") String contentLength, String entity) {
assertEquals("POST", entity);
assertEquals(entity.length(), Integer.parseInt(contentLength));
return entity;
}
}
#Override
protected Application configure() {
ResourceConfig config = new ResourceConfig(HttpMethodResource.class);
config.register(new LoggingFilter(LOGGER, true));
return config;
}
#Override
protected void configureClient(ClientConfig config) {
config.connectorProvider(new ApacheConnectorProvider());
}
#Test
public void testPostChunked() {
Response response = target().path("test/chunked").request().post(Entity.text("POST"));
assertEquals(200, response.getStatus());
assertTrue(response.hasEntity());
}
#Test
public void testPostBuffering() {
ClientConfig cc = new ClientConfig();
cc.property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.BUFFERED);
cc.connectorProvider(new ApacheConnectorProvider());
JerseyClient client = JerseyClientBuilder.createClient(cc);
WebTarget target = client.target(getBaseUri());
Response response = target.path("test").request().post(Entity.text("POST"));
assertEquals(200, response.getStatus());
assertTrue(response.hasEntity());
}
}
#Test
public void testForbiddenHeadersAllowed() {
Client client = ClientBuilder.newClient();
System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
Response response = testHeaders(client);
System.out.println(response.readEntity(String.class));
Assert.assertEquals(200, response.getStatus());

Categories