Getting incorrect encoding when reading JSON Sinatra - android

Using Sinatra I've created a web server and now I'm trying to pass some data there. I'm passing it using Retrofit (android) and Gson as a converter.
Here is a logcat log:
--> POST http://192.168.88.147:4567/payload HTTP/1.1
Content-Type: application/json; charset=UTF-8
Content-Length: 245
[{"composition":"","name":"Чебурек с мясом","price":42,"type":"выпечка","weight":"100","mId":158},{"composition":"","name":"Ватрушка с картошкой","price":32,"type":"выпечка","weight":"100","mId":159}]
--> END POST (245-byte body)
<-- 200 OK http://192.168.88.147:4567/payload (164ms)
Content-Type: text/html;charset=utf-8
Content-Length: 448
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Connection: keep-alive
D/OkHttp: Server: thin
D/OkHttp: OkHttp-Sent-Millis: 1472197883936
OkHttp-Received-Millis: 1472197884075
[{"composition"=>"", "name"=>"\u0427\u0435\u0431\u0443\u0440\u0435\u043A \u0441 \u043C\u044F\u0441\u043E\u043C", "price"=>42, "type"=>"\u0432\u044B\u043F\u0435\u0447\u043A\u0430", "weight"=>"100", "mId"=>158}, {"composition"=>"", "name"=>"\u0412\u0430\u0442\u0440\u0443\u0448\u043A\u0430 \u0441 \u043A\u0430\u0440\u0442\u043E\u0448\u043A\u043E\u0439", "price"=>32, "type"=>"\u0432\u044B\u043F\u0435\u0447\u043A\u0430", "weight"=>"100", "mId"=>159}]
<-- END HTTP (448-byte body)
Pay attention that this is cyrillic.
Here is the Ruby serverside code how I'm doing this:
post '/payload' do
push = JSON.parse(request.body.read)
push.inspect.force_encoding("UTF-8")
end
I used some third party online decoder and it said that this unreadable text is UTF16.
Please help me to find out what is going on here.
I'm new in Ruby.
Also consider to rename this question, because maybe I'm misuse some terms.

Windows 7 was the problem. As soon as I switched to Ubuntu(virtual box) I got the encoding correctly withoud any encode\decode manipulations.

Related

Retrofit v2.4.0 is not sending the If-Modified-Since header

This may be a very basic question, but I've ran out of ideas.
Retrofit v2.4.0 is not sending the If-Modified-Since header, as a result caching is not working.
I'm polling the server several times a day to see if is there any updated data, hence the need for If-Modified-Since header. (push notifications may be implemented in a new release)
Based on this article, the setup is extremely easy: https://futurestud.io/tutorials/retrofit-2-activate-response-caching-etag-last-modified
I've read several related articles, but those were focused on the use-cases when the server's implementation was either inaccessible or it didn't send the headers. This is not my case. Those suggested the usage of networkInterceptors(). As the correct response headers are sent, I shouldn't need an interceptor (I guess).
Theoretically it should work.
Based on the response headers, it looks like that the server is correctly configured.
Here's the code:
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
Cache cache = new Cache(getApplication().getCacheDir(), 30 * 1024 * 1024);
httpClient = new OkHttpClient.Builder()
.cache(cache)
.addInterceptor(logging)
.build();
retrofit = new Retrofit.Builder()
.baseUrl("http://someserver:8080/")
.callbackExecutor(Executors.newSingleThreadExecutor())
.client(httpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
Logs:
D/OkHttp: --> GET http://someserver:8080/model/modelId http/1.1
D/OkHttp: --> END GET
<-- 200 OK http://someserver:8080/model/modelId (23ms)
D/OkHttp: Cache-Control: private D/OkHttp: Content-Length:
3240854 D/OkHttp: Content-Type: application/octet-stream
D/OkHttp: Last-Modified: Mon, 14 May 2018 07:22:25 GMT
D/OkHttp: Date: Mon, 14 May 2018 09:03:50 GMT D/OkHttp: <-- END
HTTP
Please let me know what am I doing wrong.
Your server's cache configuration is incorrect. If you look at this article's Troubleshooting section you'll notice that it needs to be Cache-Control: private, must-revalidate.

android - Volley response headers

I try to use volley for http request. I tried a request on postman and the response headers are below :
Content-Encoding →gzip
Content-Length →153
Content-Type →application/json; charset=utf-8
Date →Fri, 30 Jun 2017 13:36:10 GMT
ETag →W/"23-E742p6pP4kakmVh3lok1ww"
Server →Microsoft-IIS/8.0
Vary→X-HTTP-Method-Override, Accept-Encoding,Accept-Encoding
X-Powered-By →Express, ASP.NET
Volley adds some extra keys and removes Content-Length and Content-Encoding key. I write keys and values on parseNetworkResponse function. Response headers are below :
Content-Type : application/json; charset=utf-8
Date : Fri, 30 Jun 2017 09:05:23 GMT
ETag : W/"23-E742p6pP4kakmVh3lok1ww"
Server : Microsoft-IIS/8.0
Set-Cookie : ARRAffinity=16d81073e15abb17d2faba962adb6504734210ff2b9ff1ddfa770750ac7752e2;Path=/;xxx.net
Vary : X-HTTP-Method-Override, Accept-Encoding,Accept-Encoding
X-Android-Received-Millis : 1498813523832
X-Android-Response-Source : NETWORK 200
X-Android-Selected-Protocol : http/1.1
X-Android-Sent-Millis : 1498813523632
X-Powered-By : Express
Why does it change content? How can I get Content-Length and Content-Encoding?
if you are using the java.net.HttpURLConnection and the getHeaderFieldKey method, then do not forget to check the 0th element from the header according to the API reference:
Some implementations may treat the 0th header field as special, i.e. as the status line returned by the HTTP server. In this case, getHeaderField(0) returns the status line, but getHeaderFieldKey(0) returns null.

Webservice not working in Android Retrofit , but works in Postman and Swift / iOS, Getting 401 Unauthorized

BRIEFING BEFORE 'technical stuff'
Not new to working with Retrofit but came across this strange behaviour which I am having very hard time to understand and fix,
I have two web service, both work fine as expected in Postman and iOS but only one works in Retrofit and not the other,
In my defence I can say I am getting (Unauthorized) response,which means I was able to hit the server and get a result
In API developer's defence he says it works in Postman and other devices so not a service issue
If any Retrofit expert out there tell me what retrofit may be doing behind my back in order to get this error?
TECHNICAL STUFF
Talking about the type of service , It contains Authorization Bearer token as header which expires every 6 hours and contains no params at all (so it should be easy, right ?) and a simple url
http://hashchuna.nn-assets.com/api/locations
Unfortunately the header token cant be shared with valid key, cos it'l be expired before anyone can try it, but here it's anyway Authorization Bearer 3d44626a55dbb024725984e0d37868336fd7e48a
WHAT I'VE TRIED
I am using okhttp intercept to add Authorization Header to request using both addHeader/header method, no spaces in the url cos there r no params
Getting 401 unauthorized error in retrofit?
Java: Android: Retrofit - using Call but, Response{code = 401,message=unauthorized}
https://github.com/square/retrofit/issues/1290
But non of them helped
WARNING
Now the tricky part to keep in mind, the token when expired must give 401 error which is expected, but the problem is even for freshly created token I get 401 , which is my core problem
LOG
D/OkHttp: --> GET http://hashchuna.nn-assets.com/api/locations http/1.1
D/OkHttp: Authorization: Bearer 7c0d53de006b6de931f7d8747b22442354cecef9
D/OkHttp: --> END GET
D/OkHttp: <-- 401 Unauthorized http://hashchuna.nn-assets.com/api/locations (773ms)
D/OkHttp: Date: Mon, 20 Feb 2017 10:44:11 GMT
D/OkHttp: Server: Apache
D/OkHttp: X-Powered-By: PHP/7.0.15
D/OkHttp: Access-Control-Allow-Origin: *
D/OkHttp: Access-Control-Allow-Credentials: true
D/OkHttp: Access-Control-Max-Age: 1000
D/OkHttp: Access-Control-Allow-Headers: X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding
D/OkHttp: Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT
D/OkHttp: Expires: Thu, 19 Nov 1981 08:52:00 GMT
D/OkHttp: Cache-Control: no-store, no-cache, must-revalidate
D/OkHttp: Pragma: no-cache
D/OkHttp: Set-Cookie: PHPSESSID=u477o8g0q387t92hms4nhc14n1; path=/
D/OkHttp: Vary: Authorization
D/OkHttp: X-Powered-By: PleskLin
D/OkHttp: Keep-Alive: timeout=5
D/OkHttp: Connection: Keep-Alive
D/OkHttp: Transfer-Encoding: chunked
D/OkHttp: Content-Type: application/json;charset=utf-8
D/OkHttp: <-- END HTTP
CODE
Intercept
Request request = chain
.request()
.newBuilder()
//.header("Authorization","Bearer "+ SharedPrefsUtils.getSPinstance().getAccessToken(context))
.addHeader("Authorization","Bearer 1ed6b7c1839e02bbf7a1b4a8dbca84d23127c68e")
//.addHeader("cache-control", "no-cache")
//.cacheControl(CacheControl.FORCE_NETWORK)
.build();
Retrofit Instance
private Api getApiInstance(Context context) {
HttpLoggingInterceptor logInter = new HttpLoggingInterceptor();
logInter.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient mIntercepter = new OkHttpClient.Builder()
.addInterceptor(new RequestResponseInterseptor(context))
.addInterceptor(logInter)
.build();
Retrofit retrofitInstance = new Retrofit.Builder()
//.addConverterFactory(new NullOnEmptyConverterFactory())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.client(mIntercepter)
.build();
return retrofitInstance.create(Api.class);
}
SOLUTION (its the COOKIE)
Thanks to some of the tips, the actual reason for Incompatibility of service is , Supposedly POSTMAN and iOS client store and reuse COOKIE all by itself when requests are made without any need for explicit handling, Cookie in Postman can be tested using Postman Intercepter ,but cant be edited because chrome does not allow editing cookie by plugins
However Retrofit/OkHttp unless specified will consider it disabled(for security reason maybe) ,
Cookie is added either inside Interseptor as one of the header addHeader("Cookie","KEY-VALUE")
or
Use cookieJar to add into
OkHttpClient mIntercepter = new OkHttpClient.Builder()
.cookieJar(mCookieJar)
.addInterceptor(new RequestResponseInterseptor(context))
.addInterceptor(logInter)
.build();
based on your need and cookie type
I think you are overriding other headers Retrofit is adding for you, causing your API to not care about your Authorization header. Code below will add a header to your existing headers instead of overriding them.
OkHttpClient mIntercepter = new OkHttpClient.Builder()
...
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder().addHeader("Authorization", "Bearer " + "1ed6b7c1839e02bbf7a1b4a8dbca84d23127c68e").build();
return chain.proceed(request);
})
...
.build();
Format of those headers is correct here, key should be Authorization and value should be Bearer 1ed6b7c1839e02bbf7a1b4a8dbca84d23127c68e (in your case).
In my case, unfortunately, none of advices listed in #Ujju 's solution worked (i.e. neither "Cookie" header nor CookieJar applied). The only thing that helped me is just replacing addInterceptor with addNetworkInterceptor, and everything began to work.
401 Unauthorized http://www.stackoverflow.com/api/login?email=test#test.com&password=123456
Date: Fri, 07 Apr 2017 11:23:28 GMT
Server: Apache/2.4.25 (Amazon) PHP/5.6.29
X-Powered-By: PHP/5.6.29
Cache-Control: no-cache, private
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
Content-Length: 41
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: application/json
{"msg":"Invalid Credentials"}
I was facing issue like that not able fetch error message.
When server throwing error like 401 or other error we are getting null body from server.But you can get error message from server in errorBody
String response = response.errorBody().string()
I faced with this problem as well.
Requests work fine not only in POSTMAN, but also in CURl.
Having spent a lot of time I found a solution.
Service example for login:
#FormUrlEncoded
#POST("authentication/login")
fun login(
#Field("login") login: String,
#Field("password") password: String
): Single<Void>
Provide okhttp client:
private fun provideOkHttpClient(): OkHttpClient {
val cookieManager = CookieManager()
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL)
val client = OkHttpClient.Builder()
.cookieJar(JavaNetCookieJar(cookieManager))
.addNetworkInterceptor(provideRequestInterceptor())
.addNetworkInterceptor(provideLoggingInterceptor())
.protocols(Arrays.asList(Protocol.HTTP_1_1))
.build()
return client
}
Make enable JavaNetCookieJar:
implementation "com.squareup.okhttp3:okhttp-urlconnection:$okHttpVersion"
Provide authorization:
private fun provideRequestInterceptor() = Interceptor {
val builder = it.request().newBuilder().url(it.request().url())
val tokenStr = BuildConfig.SONAR_TOKEN
builder.addHeader("Authorization", "Basic "+ getBase64String(tokenStr+":"))
it.proceed(builder.build())
}
private fun getBase64String(value: String): String {
return Base64.encodeToString(value.toByteArray(charset("UTF-8")), Base64.NO_WRAP)
}

Call parse.com job from javascript using REST API

As the title says, I'm trying to run a job on parse.com from javascript (specifically an Appcelerator Android app) using the REST API. I'm using REST because this is just for diagnostics and I don't want to deal with trying to get the parse.com javascript API working in Appcelerator. The problem is I cannot get authenticated. If I don't pass in the authentication headers, I get the appropriate 401 authentication error, but if I do set them, I get "BAD REQUEST". I have gotten it working via cURL, so the URL is right, and parse responds to the call as expected. Here's my code:
var url = "https://api.parse.com/1/jobs/sendMail";
var client = Ti.Network.createHTTPClient({
// function called when the response data is available
onload : function(e) {
Ti.API.info("Received text: " + this.responseText);
alert("Received text: " + this.responseText);
},
// function called when an error occurs, including a timeout
onerror : function(e) {
Ti.API.debug(e.error);
alert(e.error);
},
timeout : 5000 // in milliseconds
});
var param = {"text":msg};
// Prepare the connection.
var auth = {"app":"sTnsthoeunotreallymyappIDbutabunchofcharactersESnecu","key":"8ll5thisisntreallymykeyeitherhMKqkYG"};
client.open("POST", url);
client.setRequestHeader("X-Parse-Application-Id",auth.app);
client.setRequestHeader("X-Parse-REST-API-Key",auth.key);
client.setRequestHeader("Content-Type","application/json");
// Send the request.
client.send(param);
Here is the request and response:
POST https://api.parse.com/1/jobs/sendMail HTTP/1.1
X-Parse-Application-Id: myappid
User-Agent:
X-Parse-REST-API-Key: myrestapikey
Content-Type: application/json
X-Requested-With: XMLHttpRequest
Content-Length: 0
Host: api.parse.com
Connection: Keep-Alive
HTTP/1.1 401 Unauthorized
Access-Control-Allow-Methods: *
Access-Control-Allow-Origin: *
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Date: Sun, 22 Feb 2015 04:36:11 GMT
Server: nginx/1.6.0
Set-Cookie: _parse_session=BAh7BkkiD3Nlc3Npb25faWQGOgZFRiIlMTY4MzY0NTZlOWQ3ZGRjZDJkOWQwMjA4MWZjNWViMTY%3D--ffc760efbe32aa80a5e6d369606361413433fa72; domain=.parse.com; path=/; expires=Tue, 24-Mar-2015 04:36:11 GMT; secure; HttpOnly
Status: 401 Unauthorized
WWW-Authenticate: Basic realm="Parse"
X-Content-Type-Options: nosniff
X-Runtime: 0.018320
X-UA-Compatible: IE=Edge,chrome=1
Content-Length: 24
Connection: keep-alive
{"error":"unauthorized"}
Triggering background jobs via the REST-API requires using the Master-Key, not the REST-API key.
https://parse.com/docs/rest#backgroundjobs

HttpPost with Header, Cookies, Parameters - 417 - Expectation Failed

Ok this will be a long one, so thx for all who are reading this to the end.
Precondition: I can't access the server, I'm just trying to post data as a news comment.
I'm desperately testing this out for a couple of hours now but I still don't have any success. What I basically need is this kind of request:
POST http://www.example.com/gen/processtalkback.php
Cookie: userid=XXXX; password=XXXX
Content-Type: application/x-www-form-urlencoded
reference_id=XXXX&talkback_command=newentry&talkback_content=comment&talkback_viewpage=1&reference_area=11
So nothing special, just two headers with two cookies and a content discriptor and five parameters.
The two cookies are mandatory, so I set them like this:
CookieStore cookieStore = httpClient.getCookieStore();
BasicClientCookie cookie = new BasicClientCookie("userid", "XXXX");
cookie.setDomain("http://www.example.com");
cookieStore.addCookie(cookie);
BasicClientCookie cookie2 = new BasicClientCookie("password", "XXXX");
cookie2.setDomain("http://www.example.com");
cookieStore.addCookie(cookie2);
After that I set the header and content to the HttpPost object and execute it:
HttpPost httpost = new HttpPost("http://www.example.com/gen/processtalkback.php");
httpost.setHeader("Content-Type", "application/x-www-form-urlencoded");
List<NameValuePair> nvps = new ArrayList<NameValuePair>(5);
nvps.add(new BasicNameValuePair("reference_id", "XXXX"));
...
httpost.setEntity(new UrlEncodedFormEntity(nvps));
HttpResponse httpResponse = httpClient.execute(httpost);
I look into the response and it shows me:
Log.i("RESPONSE", httpResponseActivity.getStatusLine() + EntityUtils.toString(httpResponse .getEntity()));
HTTP/1.1 417 Expectation Failed
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>417 - Expectation Failed</title>
</head>
<body>
<h1>417 - Expectation Failed</h1>
</body>
</html>
I really don't know what the problem is. With Firefox Extensions like "poster" or "HttpRequester" I succeed with the posting:
POST http://www.example.com/gen/processtalkback.php
Cookie: userid=XXXX; password=XXXX
Content-Type: application/x-www-form-urlencoded
reference_id=XXXX&talkback_command=newentry&talkback_content=comment&talkback_viewpage=1&reference_area=11
200 OK
X-Powered-By: PHP/5.3.6
Set-Cookie: ...
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Date: Sun, 07 Aug 2011 16:24:01 GMT
Server: lighttpd/1.4.22
<a name="commentanchor"></a>
...
I also tried it with a Parameter object but still no success:
HttpParams params = new BasicHttpParams();
params.setParameter("reference_id", "XXXX");
...
httpost.setParams(params);
What could be the cause of the problem? Am I missing something? Any Apache HttpClient specifics I'm unaware of? I'm aware of the fact that the server is telling me something by this failure, so I searched in the web and tried one of the solutions for this:
params.setBooleanParameter("http.protocol.expect-continue", false);
Still no success.
What I got from wiretapping by the app "shark for root" without this parameter:
Data:
POST /gen/processtalkback.php
HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 119
Host: www.example.com
Connection: Keep-Alive
User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)
Expect: 100-Continue
HEX: .....
And now with the parameter:
Data:
POST /gen/processtalkback.php
HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 119
Host: www.example.com
Connection: Keep-Alive
User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)
reference_id=XXXX...............
HEX: .....
What I got by firebug from posting by browser:
Host www.example.de
User-Agent Mozilla/5.0
Accept text/javascript, text/html, application/xml, text/xml, */*
Accept-Language en-us,de-de;q=0.8,en;q=0.5,de;q=0.3
Accept-Encoding gzip, deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
DNT 1
Connection keep-alive
X-Requested-With XMLHttpRequest
X-Prototype-Version 1.7
Content-Type application/x-www-form-urlencoded; charset=UTF-8
Referer ...
Content-Length 134
Cookie ...
Pragma no-cache
Cache-Control no-cache
Why won't the cookies be displayed in my programmatic httppost attempts? It would be awesome if someone would have an idea. :-)
Got it working with another approach. I used this code of a German forum posting: http://www.android-hilfe.de/android-app-entwicklung/6398-http-post-request-mit-cookie.html
So I basically had to write
URL url = new URL("http://www.example.com/gen/processtalkback.php");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Cookie", "userid=XXXX; password=XXXX");
connection.connect();
Shark dump:
Data:
POST /gen/processtalkback.php
HTTP/1.1
cookie: userid=XXXX; password=XXXX
User-Agent: Dalvik/1.2.0 (Linux; U; Android 2.2.2; ...
Host: www.example.com
Connection: Keep-Alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 119
HEX: .....
Post content was send seperately:
Data:
reference_id=XXXX&talkback_command=newentry&talkback_content=comment&talkback_viewpage=1&reference_area=11
HEX: .....
And it worked perfectly!
I'm really confused why this approach was successful at the very first time and trying it with the Apache HttpClient for hours resulted in literally tearing my hair out.
Does anyone know more details? I would very much appreciate any explanation.
I had the same problem on Android 2.2 (worked without them for Android > 2.2). Worked fom me only when using both params, like this:
HttpParams params = new BasicHttpParams();
params.setParameter("reference_id", "XXXX");
params.setBooleanParameter("http.protocol.expect-continue", false);
httpost.setParams(params);

Categories

Resources