REST::Client | unable to parse GET response JSON object - rest

This is the first time i am writing a Perl client to consume a REST service. I am using REST::Client and JSON perl module. The web service returns data in JSON format. The problem is when I try to use from_json or decode_json method on client->responseContent() method, I am getting an error saying
"malformed JSON string, neither array, object, number, string or atom, at character offset 0 (before "HTTP/1.1 200 \r\nCon..."
The web service is ofcourse a stable one and works fine with other languages REST clients.
After debugging the issue I found that client->responseContent() not only contains the JSON data but also the header information hence from_json is unable to parse it. Below is the snippet of the code:
my $url = "/data";
my $client = REST::Client->new();
$client->setHost($host);
my $headers = {Accept => 'application/json'};
$client->GET($url, $headers);
my $response = from_json($client->responseContent());
Not able to figure out this thing from two days now :-(
Here is the dump of "$client->{_res}->dump"
Fri Feb 23 09:38:35 2018: HTTP/0.9 200 EOF
Client-Date: Fri, 23 Feb 2018 09:38:35 GMT
Client-Peer: 45.32.84.105:8282
Client-Response-Num: 1
HTTP/1.1 200 \r
Content-Type: application/json;charset=UTF-8\r
Transfer-Encoding: chunked\r
Date: Fri, 23 Feb 2018 09:38:33 GMT\r
Connection: close\r
\r
2000\r
[{"REGION":"AP","REMARK":null,"STATUS":"PROD","UPDATED_TIME":null,"UPDATED_BY":null,"ROUTE_ID":1,"ROUTE_ID_VER":20150310,"USER_ROUTE_LOGIC":"|CAPTIVE|","USER_DEST":null,"USER_ORDSIZE_TYPE":null,"MIN_USER_ORDSIZE_VAL":0,"MAX_USER_ORDSIZE_VAL":100,"TAG_775":"|1|","CROSS_CURRENCY":"|Y|N|","TAG_12703":"|PB-CS|","COUNTRY":"|AU|HK|ID|IN|JP|KR|MY|SG|","TAG_12207":...
(+ 423449 more bytes not shown)
Even when the transfer encoding is not chunked, I am getting the same issue;
Fri Feb 23 10:40:20 2018: HTTP/1.1 200 ^M
Content-Type: application/json;charset=utf-8^M
Content-Length: 1618^M
Date: Fri, 23 Feb 2018 10:40:20 GMT^M
Connection: close^M
^M
{ "data":[ {
"REGION" : "AP",
"REMARK" : "",
"STATUS" : "PROD",
"UPDATED_TIME" : "",
"UPDATED_BY" : "",
Ultimately, solved using curl command for now (Actual code snippet below):
my $command = "curl '$url'";
my $rules = qx/$command/;

I have a similar issue that I am working through. I suspect (Have not verified yet) that the root cause is that the server is not passing the Encoding-Content Response Header, therefore LWP::UserAgent and HTTP::Response are not attempting to return the decoded string. I am going to try to isolate where this decision is being made and see what options are available and request a patch.
Perl REST::Client - Garbage data in response

Related

Failed to parse SDKResponse from http_response

I implemented a webhook to conform to the Actions on Google Conversation Protocol. However, when I simulate an interaction through the Web Simulator, I get the following error:
{
"response": “action name isn’t responding right now. Try again soon.\n",
"audioResponse": "...",
"debugInfo": {
"sharedDebugInfo": [
{
"name": "ExecutionResponse",
"debugInfo": "Failed to parse SDKResponse from http_response: 'HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nTransfer-Encoding: chunked\r\nConnection: keep-alive\r\nDate: Sun, 11 Dec 2016 22:54:50 GMT\r\nServer: Server-Software/1.0\r\nGoogle-Assistant-API-Version: v1\r\nVary: Accept-Encoding\r\nContent-Encoding: gzip\r\nX-Cache: Miss from CDN\r\nVia: 1.1 cdn.example.net (CDN)\r\n\r\n{\"conversation_token\":null,\"expect_user_response\":false,\"expected_inputs\":[],\"final_response\":{\"speech_response\":{\"ssml\":null,\"text_to_speech\":\"Hello!\"}}}'"
}
]
}
}
The block inside the debugInfo field is:
Failed to parse SDKResponse from http_response: 'HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
Date: Sun, 11 Dec 2016 22:54:50 GMT
Server: Server-Software/1.0
Google-Assistant-API-Version: v1
Vary: Accept-Encoding
Content-Encoding: gzip
X-Cache: Miss from CDN
Via: 1.1 cdn.example.net (CDN)
{\"conversation_token\":null,\"expect_user_response\":false,\"expected_inputs\":[],\"final_response\":{\"speech_response\":{\"ssml\":null,\"text_to_speech\":\"Hello!\"}}}'
Fields that have null values or empty arrays should be excluded. For example, since expect_user_response is false, the expected_inputs field should not be present in the output. Use the following instead:
{
"expect_user_response": false,
"final_response": {
"speech_response": {
"text_to_speech": "Hello!"
}
}
}

Google oAuth2 unauthorized_client by refresh_token

i'm trying to use an refresh token from the oAuth2 web redirect auth in my console perl script. The client id is the same and correct client id i used in my javascript an i checked it 5 times that it ist the same as i have in my google API's Console.
The client secret is checked two and it is correct.
The refresh token was created with approval_prompt=force&access_type=offline
Here is my perl sample code i use:
# -----------------------------------------------------------------------------------
my $CLIENT_ID = 'XXXXX.apps.googleusercontent.com';
my $CLIENT_SECRET = 'YYYYYYYYYYY';
# -----------------------------------------------------------------------------------
# TESTING
my $refresh_token = '1/is_5_minutes_old';
# -----------------------------------------------------------------------------------
my $string = '';
$string .= 'grant_type=refresh_token';
$string .= '&client_id=' . $CLIENT_ID;
$string .= '&client_secret=' . $CLIENT_SECRET;
$string .= '&refresh_token=' . $refresh_token;
$ua = LWP::UserAgent->new;
my $req =
HTTP::Request->new( POST => 'https://accounts.google.com/o/oauth2/token' );
$req->content_type('application/x-www-form-urlencoded');
$req->content($string);
print $string . "\n";
my $res = $ua->request($req);
print $res->as_string;
The response of it:
HTTP/1.1 400 Bad Request
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Connection: close
Date: Mon, 02 Sep 2013 10:50:26 GMT
Pragma: no-cache
Server: GSE
Content-Type: application/json
Expires: Fri, 01 Jan 1990 00:00:00 GMT
Alternate-Protocol: 443:quic
Client-Date: Mon, 02 Sep 2013 10:50:26 GMT
Client-Peer: 74.125.136.84:443
Client-Response-Num: 1
Client-SSL-Cert-Issuer: /C=US/O=Google Inc/CN=Google Internet Authority G2
Client-SSL-Cert-Subject: /C=US/ST=California/L=Mountain View/O=Google Inc/CN=accounts.google.com
Client-SSL-Cipher: RC4-SHA
Client-SSL-Warning: Peer certificate not verified
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
{
"error" : "unauthorized_client"
}
i hope you have an idea to help.
greatings
Invalid client usually means that the client ID and client secret don't match, or there is a typo in one of them (though you mention you've double checked this!). Nothing in your code looks wrong.
When you retrieve the refresh token, could you try putting the access token that comes along with it into the tokeinfo endpoint and making sure the values for client ID there match the ones you've configured with: https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=
It might be worth dumping out the request to make sure there isn't a mistake in that (e.g. too short a content-length header or similar).
Dumping the request is key. To help , here is one I baked earlier..
==POST==
https://accounts.google.com/o/oauth2/token
refresh_token=1/_PEzU2m71wertwertwerJUtrtrytrytryf3trytryoCo
&client_id=612222222225
&client_secret=Q7334534543534yKLu
&grant_type=refresh_token
Are you using the short form of the client id, ie. just the number?

Luasocket custom headers, 404 turns to 301

My previous question was about fetching page title in lua using the socket.http module. The question lies here. Previously, youtube pages led me to a 404 error page. Based on MattJ's help, I put up custom HOST header for the request. This is what I did and what was the result:
Code
header = { host= "youtube.com" }
local result,b,c,h = http.request{ url = "http://www.youtube.com/watch?v=_eT40eV7OiI", headers = header }
print ( result, b, c, h )
for k,v in pairs(c) do print(k,v) end
Result
1 301 table: 0047D430 HTTP/1.1 301 Moved Permanently
x-content-type-options nosniff
content-length 0
expires Tue, 27 Apr 1971 19:44:06 EST
cache-control no-cache
connection close
location http://www.youtube.com/watch?v=_eT40eV7OiI
content-type text/html; charset=utf-8
date Sat, 28 Apr 2012 04:26:21 GMT
server wiseguy/0.6.11
As far as I was able to understand from this, the error is basically because of X-Content-Type-Options valued nosniff. Reading its documentation, I got to know that the only defined value, "nosniff", prevents Internet Explorer from MIME-sniffing a response away from the declared content-type.
Please help me so that I can use custom proxy and fetch the youtube(and some other sites, as mentioned in the previous question) title from their body. Here is the complete LUA file I currently have:
local http = require "socket.http"
http.PROXY="http://<proxy address here>:8080"
header = { host= "youtube.com" }
local result,b,c,h = http.request{ url = "http://www.youtube.com/watch?v=_eT40eV7OiI", headers = header }
print ( result, b, c, h )
for k,v in pairs(c) do print(k,v) end
I believe this line should be changed:
header = { host= "youtube.com" }
To:
header = { host= "www.youtube.com" }
After that, works for me.
The solution is to install luasec and to use ssl.https module to do the request.
Answered here by Paul Kulchenko!
Example:
-- luasec version 0.4.2
require("ssl")
require("https")
-- ssl.https.request(...)

adding a response header to 302 response using perl

i am trying to write a perl page that returns an http 302 response to a different location and adds a custom header to that response.
so my desired http response should be something like this:
HTTP/1.1 302 Moved
Date: Sun, 15 Apr 2012 10:59:02 GMT
Server: Apache
Location: http://www.google.com
Content-Length: 396
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=iso-8859-1
CUSTOM_HEADER: CUSTOM_VALUE
i tried using CGI:
#!/bin/perl
use strict;
use APR::Request::Apache2;
my $r = shift;
$r->content_type('text/html; charset=utf-8');
$r->headers_out()->add("CUSTOM_HEADER", "CUSTOM_VALUE");
$r->headers_out()->add("Location", "http://www.google.com");
$r->status(302);
and i do get 302 response to google but no CUSTOM_HEADER. once i change the status to 200 by $r->status(200); i do get the CUSTOM_HEADER.
so whats up with this behavior ? how can i add my header to the 302 response ?
Use $r->err_headers_out->set or $r->err_headers_out->add
my $r = shift;
$r->content_type('text/html; charset=utf-8');
$r->err_headers_out->set(Location => "http://www.google.com");
$r->status(302);
You should use err_headers_out(). These will be printed even on errors and redirects.

How can I get both the GET and POST request params, on a POST request?

I'm creating a facebook app with a Perl backend. The problem is that since Facebook sends the request to my web app as a POST request I'm having a problem getting the GET parameters that were also part of the base URL for the application -- in effect I'm only getting the POST params from $CGI->Vars.
See CGI/MIXING POST AND URL PARAMETERS.
Short version: use $CGI->param() for post paramenters and $CGI->url_param() for query string parameters.
Dump CGI in favour of a better interface. Plack's param method returns GET and POST parameters mixed.
plackup -MPlack::Request -e 'sub {
my ($env) = #_;
my $r = Plack::Request->new($env);
return [200, ["Content-Type" => "text/plain"], [join "\n", $r->param("foo")]];
}'
> lwp-request -m POST -USe 'http://localhost:5000/fnord?foo=bar;baz=quux'
Please enter content (application/x-www-form-urlencoded) to be POSTed:
foo=123;baz=456
␄
POST http://localhost:5000/fnord?foo=bar;baz=quux
User-Agent: lwp-request/6.03 libwww-perl/6.03
Content-Length: 16
Content-Type: application/x-www-form-urlencoded
200 OK
Date: Thu, 27 Oct 2011 21:27:46 GMT
Server: HTTP::Server::PSGI
Content-Length: 7
Content-Type: text/plain
Client-Date: Thu, 27 Oct 2011 21:27:46 GMT
Client-Peer: 127.0.0.1:5000
Client-Response-Num: 1
bar
123
Just set $CGI::APPEND_QUERY_STRING = 1;