IoT module GET request in Java - java

I'm trying to read a JSON response from a RESTful webserver running on an IoT module (Advantech WISE-4012). According to the documentation, any GET request should be made in this form
GET /ai_value/slot_0/ch_0
Any Java implementation of GET requests (Java libraries, Apache etc.), anyway, append to the end of the request the protocol signature HTTP/1.1. E.g:
GET http://192.168.0.14/ai_value/slot_0/ch_0 HTTP/1.1
Because of this (probably) i'm getting Error 400 (Bad request) on every client i tried so far. The only working method i've discovered was sending a simple request through the address bar on Google Chrome browser (sometimes i get a response, sometimes a get a bad request error either). How can i write a java implementation of a GET request plain and simple as described by the documentation? How can i test a custom GET request without HTTP/1.1 at the end? Every chrome extension i tried (Advanced REST Client, Postman) add the protocol version at the end, so i haven't had the chance to verify if that's why i'm getting a bad request error.
EDIT:
This is the response header from Advanced REST client
Connection: close
Content-Type: application/json
Server: WISE-4000/8.1.0020
While the source message is the following one:
GET /ai_value/slot_0/ch_0 HTTP/1.1
HOST: 192.168.0.14
The only mismatch between the documentation is the HTTP/1.1 signature as mentioned before. Adding the "accept: application/json" makes no difference either

After a bit of digging into the documentation, it looks like the default timeout (i.e. 720 seconds) is the one causing an issue. There doesn't seem to be any way to work it around (ideally, the system should reset the time after a successful request and we should only get 400 - or 403 ideally after 720 seconds of inactivity).
A couple of points I would like to recommend to the API developers for WISE-4012 (if they are in touch with you):
Add brief documentation for authentication and timeout (probably, more response codes and error messages with each error response)
Enable OAuth for API Access
As far as current implentation is conerned, I guess you need to do a basic auth and pass username/password with every request, Or add Authentication header with every API request to get successful response without any 400s.

Check if this helps.
HttpClient httpClient = HttpClients.createDefault();
URI reqUri = new URI(<uri>);
RequestBuilder requestBuilder = RequestBuilder.create("GET");
requestBuilder.setUri(reqUri);
requestBuilder.setHeader(<headerKey>, <headerValue>);
requestBuilder.setEntity(<entity_data>);
HttpUriRequest httpRequest = requestBuilder.build();
httpResponse = httpClient.execute(httpRequest);

Related

How do I modify the HTTP response code and message in java

My goal is to modify the http response status code and the content that comes with it (not using spring by the way). The current response I'm getting is an HTTP 501 and I would like to modify that response to return an HTTP 200 instead with a custom message.
I've tried using javax.servlet.Filter and found out that that won't work (as far as I know) since I can't modify the response after the filterChain.doFilter() call. I tried redirecting a response using httpResponse.sendError() but didn't work since I'm getting an IllegalStateException telling me about the reponse was already committed or something like that.
I've also tried adding an <error-page>...</error-page> block in my web.xml using a web servlet as the location but apparently that does not work for unsupported methods and invalid headers (tried using a random string as request method; also tried Transfer-Encoding: random as an invalid header); works for invalid uri paths though.
Is there a way to create a general "filter" mechanism where I can modify the response code and message when there's a HTTP error code response?

407 Proxy Authentication error - Unirest Java 8

Hi everyone i'm so hopeless so ask you guys.
I'm trying to do a simple HTTP request but with my proxies I get 407 error code.
I use Unirest and Java 8.
Unirest.config().proxy(host, port, usernameProxy, passwordProxy);
HttpResponse<JsonNode> response = Unirest.post(url).asJson();
String body = response.getBody().toString();
That's it, my url is private but i wrote it like this: "https://myurl.com/?param1=param&param2....
It works proxyless but i'm stuck with proxies.
Thanks a lot
Seems like the proxy server expects for the proxy credentials within the Headers, which Unirest doesn't seem to propagate.
The header must specifically contain the "Proxy-Authorization" key in order to the handshake be even started.
String proxyCred= "user:password";
String baseCred= Base64.getEncoder().encodeToString(proxyCred.getBytes());
HttpHeaders headers = new HttpHeaders();
headers.add("Proxy-Authorization", "Basic " + baseCred); // the proxy server needs this
This solution uses the Basic mechanism; It may not work, as the proxy may expect another type of authentication. You'll know which one is by reading the Proxy-Authenticate header within the server's response.
If the communication is not secured (HTTP and not HTTPS), you could read the response by sniffing the packet with some tool such as WireShark. Once you locate the 407 packet, you could read inside the Proxy-Authenticate value and modify your authorization method according do it.

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.

Java Server Side: send Http POST response - status only

Some third party is sending an Http Post request whenever something changes in their DB (e.g. when a contact has been updated, they send the contactID and 'contact_updated'). I have build a socket listener that catches those requests and is able to parse the information. However, I just can't get it to work to send back a response with the status '200 - OK'. Thus, the server on the client side keeps on trying (four times or so) to re-send the request.
Is there any, simple way to just send the response status without the need of adding external libs etc.?
It should be enough to send the string HTTP/1.1 200 OK back in your socket-listener.
If you have troubles, you can check out this answer, it shows how to use a HttpServer in Java just via plain JavaSE features.
Use
response.setStatus(HttpServletResponse.SC_OK);
to set the status code in your response header.
You may also set the content type.
response.setContentType("text/html;charset=UTF-8");

FormData getting lost in HTTP 302 to 200

We getting a SSO request using Ping Identity Federation server which opening target URL with a post of opentoken as form data. But at my application I am getting two request one is 302 Found where I can see form data in headers but after that it issue a 200 request in which data is not there.
Is this normal if yes how to get formdata?
Just curious for this behaviour....I can still access that data from referrer header information.
I am using java/jsp.
It was actually coming in request body which need to be read using getReader. that's why it redirecting again to 200 as in first request I was consuming parameter instead of body.

Categories