LWP::UserAgent SendHub API Issue - perl

SendHub's documentation state to send a post with data and json data.
This is the curl example they gave
Trying to do this with perl and LWP::User Agent but getting either bad request or unauthorized
Do I have the request coded properly?
curl -H "Content-Type: application/json" -X POST --data '{"contacts" : [1111],"text" : "Testing"}' https://api.sendhub.com/v1/messages/?username
require LWP::UserAgent;
my $uri = 'https://api.sendhub.com/v1/messages/?username=MY_USERNAME\&api_key=MY_KEY_NUMBER';
my $json = '{"contacts":[18005551212],"text":"testing"}';
my $req = HTTP::Request->new('POST',$uri);
$req->header('Content-Type' => 'application/json');
$req->content($json);
my $lwp = LWP::UserAgent->new;
my $response=$lwp->request($req);
if ($response->is_success) {
print $response->decoded_content;
}
else {
die $response->status_line;
}

It looks basically OK.
What is the backslash before the ampersand in the URL?
'https://api.sendhub.com/v1/messages/?username=MY_USERNAME\&api_key=MY_KEY_NUMBER'
I think it should be
'https://api.sendhub.com/v1/messages/?username=MY_USERNAME&api_key=MY_KEY_NUMBER'
but if you're getting 401 Unauthorized then it's most likely the request is correct but the user name and key are wrong.

Related

CURL example to Invoke-RestMethod

I am trying to convert curl command to powershell Invoke-RestMethod
Command from MobileIron (in case someone will google that topic)
curl --user <username>:<password> --header 'Content-Type: application/json' --request PUT 'https://
<mobileiron_core>/api/v2/devices/wakeup?adminDeviceSpaceId=1' --data '{"deviceUuids": ["af0cea8bf56c-
49f5-b615-76cc34936b2f"], "note": "something"}'
Ok, my code (based on https://gist.github.com/rayterrill/e83a1bd877547eccfd59b656e7f91b48#file-mobileiron-addremovelabels-ps1 )
$url = $mi_server + "/api/v2/devices/wakeup?adminDeviceSpaceId=1"
$contentType = "application/json"
$headers = #{
Authorization=("Basic {0}" -f $regase64AuthInfo)
}
$data = #{
note = "test";
deviceUuids = $guid
};
$json = $data | ConvertTo-Json;
Invoke-RestMethod -Method PUT -Uri $url -ContentType $contentType -Headers $headers -Body $data;
But i am getting 400 error - so,as i understand - something wrong with --data , but what ?
MobileIron 400 is a general error: 400 Bad request, Meaning the request was invalid.
The accompanying error message (That follows the the error code) explains the reason.
Can you post the rest of the error message?
You cannot use --data or -Body with a PUT request.
You must make a query string for the data.
The query string must be url encoded.
Because it is HTTPS transfer you may need -k (--insecure)
The PUT request may work, I believe MobileIron prefers GET, There is no real difference between the two. I tested both.
I tried (I did my testing with PHP)
A PUT Request
Request Headers
Content-Type: application/json
Accept: application/json
POST DATA was the same as the Query String
I checked with and without the POST DATA and no change.
I also sent the POST DATA with no query string and it was if not data was sent.
URL and Query
$query = urlencode('{"deviceUuids": ["af0cea8bf56c-49f5-b615-76cc34936b2f"], "note": "something"}');
$url = https://<domain>/api/v2/devices/wakeup? . $query
URL was changed to my server to an app that returns the request header, and parameters.
Which returned this on the server:
Request Headers:
Content-Type: application/json
Accept: application/json
Accept-Encoding: deflate, gzip, br
Expect: 100-continue
Host: mydomain.com
Transfer-Encoding: chunked
The server GET and server REQUEST values
$_GET
array (
'{"deviceUuids":_' =>
array (
'"af0cea8bf56c-49f5-b615-76cc34936b2f"' => '',
),
)
$_REQUEST
array (
'{"deviceUuids":_' =>
array (
'"af0cea8bf56c-49f5-b615-76cc34936b2f"' => '',
),
)
The Request Query
$_SERVER['QUERY_STRING']) (url decoded)
{"deviceUuids": ["af0cea8bf56c-49f5-b615-76cc34936b2f"], "note": "something"}
Notice above the JSON query string was cut off.
But was in the arguments
argv array (url decoded)
0 = {"deviceUuids":
1 = ["af0cea8bf56c-49f5-b615-76cc34936b2f"],
2 = "note":
3 = "something"}
I removed the Content-Type: application/json from the request header and there was no change.
I changed the PUT to GET, no change
When a I started sending the Content=Type: application/json
With a POST request, and no Query String, the POS Data was ONLY in the BODY of the request.
And here are some example requesst from some MobileIron documentation:
The first request was a default GET not a PUT.
curl -u admin_username:admin_password -kv "https://URL_to_your_mobileiron_cloud_environment/api/v1/account?emailAddress=johnnydoe#acme.com&uid=johnnydoe#acme.com&accountSource=Local" -d ''
curl GET -kv -u user:password "https://[mobileiron_cloud]/api/v1/device/57d9903c-aadf-4b40-aef3-e1f24302f180/customattributes”
curl -X POST -kv -u user:password -H 'Content-Type: application/json' -d '{ "attrs" : { "tlark" : [ "1" ] } } ' "https://[mobileiron_cloud]/api/v1/device/22000/customattributes"
curl -X POST -kv -u user:password -H 'Content-Type: application/json' -d '{ "attrs" : { "tlark" : [ "1" ] } } ' "https://[mobileiron_cloud]/api/v1/device/8bcc4cee-dca9-476d-8710-9bb1e738ade9/customattributes"
$param_pam_pam = #{
Method = 'PUT'
Uri = "$url"
ContentType = "application/json"
Headers = #{authorization = ("Basic {0}" -f $regase64AuthInfo) }
Body = #{
deviceUuids = #($guid)
note = "force check-in from $ma"
} | ConvertTo-Json
}
$reg = Invoke-RestMethod #param_pam_pam

Sending Post Data via LWP (Request built by HTTP::Request) for Spotify API

See: https://developer.spotify.com/web-api/authorization-guide/
I'm using the "client credentials flow" method.
sub get_token {
my $req = HTTP::Request->new(POST => $SPOTIFY_TOKEN);
$req->header('Authorization' => 'Basic MYBASE64HERE');
my $post_data = 'grant_type=client_credentials';
$req->content($post_data);
my $resp = $ua->request($req); #this is LWP
if ($resp->is_success) {
my $token = $resp->decoded_content;
print "$token\n";
return \$token;
}
else {
print "HTTP POST error code: ", $resp->code, "\n";
print "HTTP POST error message: ", $resp->message, "\n";
}
}
I get back HTTP POST error code: 400 / bad request
I know that it doesn't have to do with the header information or the URL. I tested via Curl and used Data::Dumper to verify it was formatted properly.
I'm not sure on the format I need to send the POST body data. I've tried the example above my $post_data = 'grant_type=client_credentials'; as well as every variation I could think of. Is there a proper way to do this in Perl using HTPP::Request to build the POST request?
I think following should work, Please try:
$req->content(grant_type => 'client_credentials');
my $post_data = "grant_type=client_credentials";
Turns out this is the answer. I'm not sure how I missed this previously.

Implementing custom Curl authentication in Perl script

I am building bot application trying to use perl script to implement the curl request, the issue I have is with Authorization.
The simple curl command is something like.
curl \
-H 'Authorization: Bearer VM2CKBMXI3AVX2GMYPLBMYFRW3RCHYXS' \
'https://api.wit.ai/message?v=20160722&q='
I preferred not to use system() calls from the Perl script as there will be plenty of back and forth between the user and the bot.
I found this library
http://search.cpan.org/~szbalint/WWW-Curl-4.17/lib/WWW/Curl.pm
I was searching for setopt function in order to find out which params does it accept as my issue is where to put the Authorization param inside the command.
I found this link
http://web.mit.edu/darwin/src/modules/curl/curl/perl/Curl_easy/easy.pm
My Script code for now it is like the following:
use strict;
use warnings;
use WWW::Curl::Easy;
my $curl = WWW::Curl::Easy->new;
my $Authorization="Authorization: Bearer VM2CKBMXI3AVX2GMYPLBMYFRW3RCHYXS";
$curl->setopt(CURLOPT_HEADER,1);
$curl->setopt(CURLOPT_URL, 'https://api.wit.ai/message?v=20160721&q=hello');
# A filehandle, reference to a scalar or reference to a typeglob can be used here.
my $response_body;
$curl->setopt(CURLOPT_WRITEDATA,\$response_body);
# Starts the actual request
my $retcode = $curl->perform;
# Looking at the results...
if ($retcode == 0) {
print("Transfer went ok\n");
my $response_code = $curl->getinfo(CURLINFO_HTTP_CODE);
# judge result and next action based on $response_code
print("Received response: $response_body\n");
} else {
# Error code, type of error, error message
print("An error happened: $retcode ".$curl->strerror($retcode)." ".$curl->errbuf."\n");
}
I just need to know which CURLOPT should I need to use in order to implement authorization.
If you have any idea that will be great.
Thanks
Eran Gross
If you just want to set the header "Authorization" the option is CURLOPT_HTTPHEADER:
#!/usr/bin/perl
use strict;
use warnings;
use WWW::Curl::Easy;
my $curl = WWW::Curl::Easy->new;
$curl->setopt(CURLOPT_URL, 'http://127.0.0.1/dumprequest.php');
my #headers = ("Authorization: keygoeshere", "X-Foo: Bah");
$curl->setopt(CURLOPT_HTTPHEADER, \#headers);
$curl->setopt(CURLOPT_HEADER, 1);
my $retcode = $curl->perform;
Gives:
GET dumprequest.php HTTP/1.1
Host: 127.0.0.1
Accept: */*
Authorization: keygoeshere
X-Foo: Bah
But if you actually want to do HTTP authentication then you would use CURLOPT_USERPWD and CURLOPT_HTTPAUTH. See https://curl.haxx.se/libcurl/c/curl_easy_setopt.html for more info on options.

Perl HTTP POST request with Content type and Authorization token

I need to send an HTTP POST request with the following HTTP headers:
Content-type: 'application/atom+xml'
Authorization: MyLogin auth=$token
The token is coming from an authorization subroutine. Here is the Perl making the actual request after the subroutine is successful:
my $ua = LWP::UserAgent->new;
my $req = $ua->post ( $url );
$req = header('Content-type' => 'application/atom+xml');
$req = header('Authorization' => "MyLogin auth=$token");
However, I receive the following error when run:
Undefined subroutine &main::header called ...
How can I accomplish this?
According to the LWP::UserAgent documentation, you can set additional headers by passing them as arguments to post:
my $ua = LWP::UserAgent->new;
my $response = $ua->post($url,
'Content-type' => 'application/atom+xml',
'Authorization' => "MyLogin auth=$token"
);
Note that $ua->post actually sends the request, so trying to set the headers after calling it, as you do in your example code, is useless. (Not to mention the fact that there is no header function in the main namespace unless you import it from somewhere or write it yourself.)

Dancer : how to share data and waiting response between 2 POST without blocking

I have a rest server written in perl dancer on a VM, this is the workflow :
customers POST some json stuff in /post_wait route : the POST should waiting the business processing to return the result
the request is processed by another VMs (communication is processed by POE + IKC)
the report should be returned by /report route to the customer identified with the session id by the REST server
DANCER CODE : (that's what I tried so far)
package var;
use Data::Dumper;
use Dancer ':syntax';
use JSON::XS;
my $session = {};
# curl -H 'Content-Type: application/json' -X POST -d '{"id":22}' http://127.0.0.1:3000/post_wait
post '/post_wait' => sub {
my $json = request->body;
my $h = decode_json $json;
my $id = $h->{id};
until (exists $session->{$id}->{report}) {
sleep 1;
print STDERR ".";
}
return Dumper $session;
};
# curl -H 'Content-Type: application/json' -X POST -d '{"foobar":"xxxx"}' http://127.0.0.1:3000/report
post '/report' => sub {
my $json = request->body;
my $h = decode_json $json;
my $id = $h->{id};
$session->{$id}->{report} = $h;
return "OK\n";
};
true;
The problem is that this code hangs, because I sleep the parent process.
Maybe I should use :
rewrite that with POE-Component-Server-REST ?
Dancer::Plugin::Async module ? but can I avoid using this ? (there's some technical restrictions)...
another script as a subprocess ?
a database like redis queue using BLPOP to dequeue ? (that needs some subprocess code around)
Dancer::Session ?
fork() ?
Threads ? (I guess no)
My information system should be scalable and in high-availability mode.
What is the best practice to do what I want ?
You need some kind of seesion tracking. Use Dancer::Session for that.
And you need to do some 'asynchronus', may be fork().
Then you can start the business process by calling
# curl -H 'Content-Type: application/json' -X POST -d '{"id":22}' http://127.0.0.1:3000/post_wait
where the process is forked and the pid is stored in the session.
Then you can call
# curl -H 'Content-Type: application/json' -X POST -d '{"foobar":"xxxx"}' http://127.0.0.1:3000/report
get the pid from your session (if you have one) and check if the process is still running or not.