Jersey: Get binary data from post request - java

I am trying to build a Java server using Jersey, which accepts POST requests with binary data.
Here is a sample request it should accept:
curl -XPOST --data-binary #/path/to/some/file "localhost:<port>/myServer/processMyData"
I implemented something that currently doesn't work:
#Path("/myServer")
class MyServer {
#POST
#Path("/processMyData")
#Consumes(MediaType.APPLICATION_OCTET_STREAM)
String processMyData(byte[] payload) {
return "Sanity check string";
}
}
When I run the curl command (see above) at the first time, I get the error:
curl: (56) Recv failure: Connection reset by peer
Then, curl prints nothing when I call it again, and exits with status 0, while the server ignores everything.
What am I doing wrong?
Note: When I remove the --data-binary #/path/to/some/file clause from the curl command, the request is accepted on the server. How can I accept the binary data in Jersey?

Answering myself...
It is very simple, the removal of #Consumes(MediaType.APPLICATION_OCTET_STREAM) solved the problem

Related

HTTP connection to JIRA XRAY Rest API not working

Need help
I am trying to connect to XRAY JIRA using Rest API and want to execute a case but getting 400 error response at step inputStream=new InputStreamReader(con.getInputStream(),"UTF-8")
java.io.IOException: Server returned HTTP response code: 400 for URL:
My Code is below:
*HttpURLConnection con=null;
InputStreamReader inputStream=null;
URL jira_API_URL=new URL("https://jira.abc.com/rest/raven/latest/import/execution");
String encodeCredentials=Base64.getEncoder().encodeToString(
"kkris:testjira#234".getBytes("UTF-8"));
con=(HttpURLConnection)jira_API_URL.openConnection();
con.setRequestMethod("POST"); con.setDoOutput(true);
con.setRequestProperty("Autherization", "Basic "+encodeCredentials);
con.setRequestProperty("Content-Type", "application/json");
con.setRequestProperty("X-Atlassian-Token", "nocheck");
try(OutputStream os=con.getOutputStream()){
byte[] input=json.toString().getBytes("UTF-8");
os.write(input,0,input.length);
}
inputStream=new InputStreamReader(con.getInputStream(),"UTF-8");*
Note :Would like to add that I am able to hit this RestAPI using postman and Restassured & able to execute testcase in XRAYJIRA successfully
Well, first of all we need to clarify if you have Xray on Jira server/datacenter or Xray on Jira cloud, as they are different products and the APIs are also slightly different.
From your example, it seems that you're targetting Xray on Jira server/datacenter, and that you aim to import results using the Xray JSON format and respective endpoint as detailed here.
The endpoint URL in that case should either be
<jira_base_url>/rest/raven/1.0/import/execution or <jira_base_url>/rest/raven/2.0/import/execution
Also, please make sure the Xray JSON content you submit follows this syntax.
Note: you may want to have a look at this repo which contains some sample code for submiting results in Java and other languages, including a proof-of-concept client api.
The response body content may show you clues about what's wrong. You can start by using curl utility first as shown here and then implement the java code.
curl -H "Content-Type: application/json" -X POST -u admin:admin --data #data.json http://yourserver/rest/raven/1.0/import/execution

Retrofit POST java.io.IOException: unexpected end of stream on Connection caused by java.io.EOFException: \n not found:

I have went through all the question related to this and yet, I haven't found a solution that works for me.
Im using retrofit 2.8.1 and OkHttp 4.5.0.
My service interface looks like the following
public interface MlApiService
{
#POST
#Multipart
Call<List<PreprocessedDocument>> postDocument( #Url String apiUrl, #Part MultipartBody.Part document,
#Part ( "document_id") RequestBody documentId );
}
And I build the client like the following with requestTimeoutInSeconds set to 90 seconds.
public void init()
{
GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter( new TypeToken<List<PreprocessedDocument>>() {}.getType(), new CustomResponseDeserializer() );
HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor();
logInterceptor.setLevel( HttpLoggingInterceptor.Level.HEADERS );
OkHttpClient client = new OkHttpClient.Builder().retryOnConnectionFailure( true ).addInterceptor( logInterceptor )
.readTimeout( requestTimeoutInSeconds, TimeUnit.SECONDS ).build();
//Dummy Base URL must be provided. otherwise client won't get initialized
Retrofit retrofit = new Retrofit.Builder().baseUrl( "http://thisIsJustDummyUrlForTheSakeOfAddingBaseUrl.com/" )
.client( client ).addConverterFactory( GsonConverterFactory.create( gson.setLenient().create() ) ).build();
mlApiService = retrofit.create( MlApiService.class );
}
The request reaches the server and just when the server responds I get the following error:
Caused by: java.io.IOException: unexpected end of stream on Connection{34.XXX.XXX.9:8085, proxy=DIRECT hostAddress=/34.XXX.XXX.9:8085 cipherSuite=none protocol=http/1.1}
at okhttp3.internal.http1.Http1Codec.readResponseHeaders(Http1Codec.java:203)
at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:88)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
Caused by: java.io.EOFException: \n not found: limit=0 content=…
at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:227)
at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:211)
at okhttp3.internal.http1.Http1Codec.readResponseHeaders(Http1Codec.java:187)
Few things which I have tried so far
retryOnConnectionFailure(true)
.addHeader("Connection","close")
.header("Accept-Encoding", "identity")
The API works fine from postman but it fails when I try from code. So I tried the same headers sent by postman. Still no luck.
Few observations:
It sometimes works. Doesn't fail always.(The same file works always with postman)
It always works for other files(never an issue).
The request reaches the server and process the request without any errors and responds too. I get the error immediately after server completes processing and responds back to client.
EDIT 1:
The server I hit is backed by gunicorn/20.0.4 and uses Flask. I don't have access to the server code. And I doubt that the response sent received has some garbage characters causing the error. I don't know how to log the raw response before being read by retrofit/okhttp.
EDIT 2:
I ran the Curl command with verbose and this is what I got.
< HTTP/1.1 100 Continue
Empty reply from server
Connection #0 to host xx.xxx.xxx.9 left intact
curl: (52) Empty reply from server
Short story
The problem was with the server I was hitting. It was not sending any response (literally nothing. No headers, no body, nothing).
Long story
So after going through all the related answers on stackoverflow, other good websites and trying out so many solution which I have mentioned in the question itself, it did not solve my issue.
After carefully reading the stack trace, I came across the following line.
okhttp3.internal.http1.Http1Codec.readResponseHeaders(Http1Codec.java:203)
The client (my code) is trying to read the Response header and that's when the error java.io.EOFException: \n not found: limit=0 content= is thrown.
This gave me a hint that the problem could be with the server and not with the client. So I thought I should try with a different client and see if I can see the raw response.
The first tool that came to my mind was Curl (Postman used to give the generic Could not get any response and this did not happen consistently). I hit the server using curl with verbose option and boom! I got the following response:
curl -v --location --request POST 'http://XX.XXX.XXX.9:8085/psc/document_upload' --form 'document=#/home/user376/Downloads/test-1.pdf' --form 'document_id=22004494_ae7f_4998_a1d8_73249bda9905'
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying XX.XXX.XXX.9...
* Connected to XX.XXX.XXX.9 (XX.XXX.XXX.9) port 8085 (#0)
> POST /psc/document_upload HTTP/1.1
> Host: XX.XXX.XXX.9:8085
> User-Agent: curl/7.49.0
> Accept: */*
> Content-Length: 4684053
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=------------------------a8446c7eedb10689
>
< HTTP/1.1 100 Continue
* Empty reply from server
* Connection #0 to host XX.XXX.XXX.9 left intact
curl: (52) Empty reply from server
And that confirmed the problem was with the server and not with the client(Retrofit / http).
Moral of the story: Sometimes you have to read the stacktrace word by word even if it doesn't seem worth looking in to :)
You get a empty reply without a status-line. This is the problem. HTTP-requests normally return a status line (e.g. HTTP/1.1 200 OK\r\n), which contains the status code see https://www.ietf.org/rfc/rfc2616.txt chapter 6.1.
This is normally a server error.
Most probably there are 2 things, happening at the same time. First, the url contains a port which is not commonly used AND secondly, you are using a VPN or proxy that does not support that port. Personally, I had the same problem. My server port was 45860 and I was using pSiphon anti-filter VPN. In that condition my Postman reported "connection hang-up" only when server's relpy was an error with status codes bigger than 0. (it was fine when some text was returning from server with no error code) Then I changed my web service port to 8080 on my server and, WOW, it worked! although psiphon vpn was connected. Therefore, my suggestion is that if you can change the server port, so try it, or check if there is a proxy problem. Perhaps your Postman and acual app are not on the same machin.

Query parameter not being extracted - JAX-RS and Jersey

I'm using Jersey 2.19 to implement a REST API but I'm having difficulty using #QueryParam to extract the query parameters from a POST request even though my resource method is being called.
This is my resource method:
#POST
#Produces(MediaType.TEXT_PLAIN)
public Response test(#QueryParam("test-param") String testParam)
{
String response = "testParam is: " + testParam + "\n";
return Response.status(Response.Status.OK).entity(response).build();
}
I'm using cURL to submit the HTTP POST request as follows:
curl -X POST http://192.168.0.2:8080/myApp/test --data test-param=Hello
The value returned is always null.
What am I doing wrong?
The --data in curl will provide the whole text test-param=Hello. The correct way to request it is:
curl -X POST http://192.168.0.2:8080/myApp/test?test-param=Hello
try to use curl -X POST '192.168.0.2:8080/myApp/test?test-param=Hello';
-d, --data
(HTTP) Sends the specified data in a POST request to the HTTP server, in the same way that a browser does when a user has filled in an HTML form and presses the submit button. This will cause curl to pass the data to the server using the content-type application/x-www-form-urlencoded. Compare to -F, --form.

Jersey Test Equivalent to curl request

First trip into the Java web services world. Trying to write a test for a post request. The post looks like this in curl:
url -v -X POST -H 'Content-Type: application/json' -d '{"transaction_zips": ["78732"],"condition": "good"}' http://localhost:8080/PricingService/vin/1GTEC19J37E152026/usedValues
I've attempted:
#Test
public void testVinPricing200() {
int status = target.path("PricingService/vin/1GTEC19J37E152026/usedValues").request(MediaType.APPLICATION_JSON_TYPE).
post(Entity.entity("{\"transaction_zips\": [\"78732\"],\"condition\": \"good\"}", MediaType.APPLICATION_JSON)).getStatus();
assertEquals(200, status);
}
This results in:
Failed tests: testVinPricing200(com.comp.platform.PricingServiceTest): expected:<200> but was:<406>
So the question is simple, I'm obviously not posting correctly, what am I doing wrong?
The difference between your curl request and your junit test in your junit test you are requesting a response of type MediaType.APPLICATION_JSON_TYPE, but your webservice is not capable of responding via JSON.
Http Status code: 406
The resource identified by the request is only capable of generating response entities
which have content characteristics not acceptable according to the accept headers sent in
the request.

Sending JSON in GET Rest resource - curl and code have separate behavior

I will start with: I am doing something terribly wrong. And here is what I am doing wrong.
I created a REST resource for searching something and I am expecting a JSON data in request parameters:
#GET
#Path("/device")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response searchContent(String searchJSONString) {
String message = new SearchServices().search(searchJSONString);
return getResponse(message); //Checks the message for any error and sends back the response.
}//end of searchContent()
I should not have written:
#Consumes
since it is a GET resource and it does not consumes anything. But my problem is how to send JSON data in a java code for this (GET resource). I tried curl command which is able to send JSON data to this resource but not a java code by any means.
I tried following curl command to send JSON data to it:
curl -X GET -H "Content-Type: application/json" -d '{"keyword":"hello"}' http://localhost:8080/search-test/rest/search
And its working fine and giving me back a proper JSON response.
But if I am using a curl command without specifying any method (which should be a default http get), I am getting a 405 (Method not allowed) response from tomcat:
curl -d '{"keyword":"hello"}' http://localhost:8080/search-test/rest/search
or through Java code:
HttpURLConnection urlConnection = (HttpURLConnection) new URL(urlString).openConnection();
urlConnection.setRequestProperty("Content-Type", "application/json");
urlConnection.setRequestMethod("GET"); //This is not working.
getting the same 405 (Method not allowed) response from tomcat.
If I am sending a GET request using java code, I am not able to send the JSON data as in a post method, and I am forced to use a name=value thing and for that I need to change my REST resource to accept it as a name/value pair.
It means something like this:
http://localhost:8080/search-test/rest/search?param={"keyword":"permission"}
If I am doing something similar in POST:
#POST
#Path("/device")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response searchContent(String searchJSONString) {
String message = new SearchServices().search(searchJSONString);
return getResponse(message); //Checks the message for any error and sends back the response.
}//end of searchContent()
I am able to send the JSON data both from Java code and curl command as well:
curl -X POST -H "Content-Type: application/json" -d '{"keyword":"hello"}' http://localhost:8080/search-test/rest/search
or through Java code:
HttpURLConnection urlConnection = (HttpURLConnection) new URL(urlString).openConnection();
urlConnection.setRequestMethod("POST"); //Works fine.
urlConnection.setRequestProperty("Content-Type", "application/json");
urlConnection.setDoOutput(true);
Where is the problem? Why am I not able to send it from code but from curl? Is there any other way to send JSON data to the GET resource other than a name=value pair?
HttpURLConnection does not allow GET requests with entity and will strip the entity from the request before it is sent to the server. So, I'd strongly recommend avoiding it as even if you use a different Java HTTP client library that allows you to do it, your users will likely run into similar issues (plus web caches and proxies may add more problems).

Categories