HttpBuilder accessing secured link (i.e. https link) - groovy

Can we request a https link using HttpBuilder ?
1 - if yes then is their any specific parameters or properties we need to set while request the data.
2 - if not then can we request same as the normal http link as in the below e.g.
def http = new HTTPBuilder('http://some_link');
def resp = http.get(path: '/abcd/efg', query:[login:'login_id', pass: 'password',ttype:'trans_type',prodid:'prod_id'], contentType : XML, headers : [Accept : 'application/xml'] )

Just tried it, and it does work as it says in the docs...
#Grab( 'org.ccil.cowan.tagsoup:tagsoup:1.2' )
#Grab( 'org.codehaus.groovy.modules.http-builder:http-builder:0.5.2' )
import org.ccil.cowan.tagsoup.Parser
import groovyx.net.http.HTTPBuilder
import static groovyx.net.http.ContentType.*
// Using an https url
new HTTPBuilder( 'https://twitter.com' ).with {
// Get from the given path as TEXT
get( path:'/tim_yates', contentType:TEXT ) { resp, reader ->
// Pass the html through tagsoup and generate a parser
new XmlParser( new Parser() ).parseText( reader.text ).with {
// print the title text from inside the head section
println head.title.text()
}
}
}

Related

HTTPBuilder - Cannot invoke method withInputStream() on null object

I have a groovy script using HTTPBuilder to make a RestAPI call to an SSL site. To connect I have imported the certs into a new keystore and use the recommended code in the HTTPBuilder support pages https://github.com/jgritman/httpbuilder/wiki/SSL
Here is the code:
def url = 'https://1.1.1.1'
def uri = new URIBuilder(url)
uri.path = '/rest/com/session'
def httpLOGIN = new HTTPBuilder(uri)
def keyStore = KeyStore.getInstance(KeyStore.defaultType)
getClass().getResource("\keystore.jks").withInputStream {
keyStore.load( it, "password".toCharArray())
}
String TempBasicAuth = "username:password".bytes.encodeBase64().toString()
httpLOGIN.client.connectionManager.schemeRegistry.register(
new Scheme("https", 443, new SSLSocketFactory(keyStore))
)
httpLOGIN.request(POST,JSON) { req ->
headers.'Authorization' = "Basic $TempBasicAuth"
response.failure = { resp, json ->
println "POST Failure. LOGIN: ${resp.statusLine}"
}
response.success = {// resp, json ->
println "POST Success. LOGIN: ${resp.statusLine}"*/
}
}
The response I get is
Caught: java.lang.NullPointerException: Cannot invoke method withInputStream() on null object
java.lang.NullPointerException: Cannot invoke method withInputStream() on null object
I've tried providing the full path to the keystore but get the same result. Is it that I have not supplied the path correctly (I've tried multiple iterations) or is there another issue?
The issue was explained in this question -
Loading resources using getClass().getResource()
getResource is looking for the path relative to the class / script path and not the file path.

Groovy HTTPBuilder get last modified datetime

I am trying to fetch some files from internet. I want to update my schedule service if the last modified time update. With HTTPBuilder I am unable to find the server response with last-modified parameter. Is there any way to get this parameter?
As in the docs Last-Modified is a header and should be searched among other headers. What's important is that it's server that decides if Last-Modified header will be included in the response. Hence, if the server you connect to doesn't return the header in the response it's impossible to get the value.
Headers can be obtained via response object, see below:
#Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7.1')
import groovyx.net.http.HTTPBuilder
import static groovyx.net.http.Method.GET
import static groovyx.net.http.ContentType.TEXT
def http = new HTTPBuilder( 'http://www.google.com/search' )
http.request(GET,TEXT) { req ->
response.success = { res ->
res.headers.each { h ->
println h
}
}
}

How to call REST from jenkins workflow

I wonder how to call REST API from a (groovy) Jenkins workflow script. I can execute "sh 'curl -X POST ...'" - it works, but building the request as a curl command is cumbersome and processing the response gets complicated. I'd prefer a native Groovy HTTP Client to program in groovy - which one should I start with? As the script is run in Jenkins, there is the step of copying all needed dependency jars to the groovy installation on Jenkins, so something light-weight would be appreciated.
Native Groovy Code without importing any packages:
// GET
def get = new URL("https://httpbin.org/get").openConnection();
def getRC = get.getResponseCode();
println(getRC);
if(getRC.equals(200)) {
println(get.getInputStream().getText());
}
// POST
def post = new URL("https://httpbin.org/post").openConnection();
def message = '{"message":"this is a message"}'
post.setRequestMethod("POST")
post.setDoOutput(true)
post.setRequestProperty("Content-Type", "application/json")
post.getOutputStream().write(message.getBytes("UTF-8"));
def postRC = post.getResponseCode();
println(postRC);
if(postRC.equals(200)) {
println(post.getInputStream().getText());
}
There is a built in step available that is using Jenkins HTTP Request Plugin to make http requests.
Plugin: https://wiki.jenkins-ci.org/display/JENKINS/HTTP+Request+Plugin
Step documentation: https://jenkins.io/doc/pipeline/steps/http_request/#httprequest-perform-an-http-request-and-return-a-response-object
Example from the plugin github page:
def response = httpRequest "http://httpbin.org/response-headers?param1=${param1}"
println('Status: '+response.status)
println('Response: '+response.content)
I had trouble installing the HTTPBuilder library, so I ended up using the more basic URL class to create an HttpUrlConnection.
HttpResponse doGetHttpRequest(String requestUrl){
URL url = new URL(requestUrl);
HttpURLConnection connection = url.openConnection();
connection.setRequestMethod("GET");
//get the request
connection.connect();
//parse the response
HttpResponse resp = new HttpResponse(connection);
if(resp.isFailure()){
error("\nGET from URL: $requestUrl\n HTTP Status: $resp.statusCode\n Message: $resp.message\n Response Body: $resp.body");
}
this.printDebug("Request (GET):\n URL: $requestUrl");
this.printDebug("Response:\n HTTP Status: $resp.statusCode\n Message: $resp.message\n Response Body: $resp.body");
return resp;
}
/**
* Posts the json content to the given url and ensures a 200 or 201 status on the response.
* If a negative status is returned, an error will be raised and the pipeline will fail.
*/
HttpResponse doPostHttpRequestWithJson(String json, String requestUrl){
return doHttpRequestWithJson(json, requestUrl, "POST");
}
/**
* Posts the json content to the given url and ensures a 200 or 201 status on the response.
* If a negative status is returned, an error will be raised and the pipeline will fail.
*/
HttpResponse doPutHttpRequestWithJson(String json, String requestUrl){
return doHttpRequestWithJson(json, requestUrl, "PUT");
}
/**
* Post/Put the json content to the given url and ensures a 200 or 201 status on the response.
* If a negative status is returned, an error will be raised and the pipeline will fail.
* verb - PUT or POST
*/
HttpResponse doHttpRequestWithJson(String json, String requestUrl, String verb){
URL url = new URL(requestUrl);
HttpURLConnection connection = url.openConnection();
connection.setRequestMethod(verb);
connection.setRequestProperty("Content-Type", "application/json");
connection.doOutput = true;
//write the payload to the body of the request
def writer = new OutputStreamWriter(connection.outputStream);
writer.write(json);
writer.flush();
writer.close();
//post the request
connection.connect();
//parse the response
HttpResponse resp = new HttpResponse(connection);
if(resp.isFailure()){
error("\n$verb to URL: $requestUrl\n JSON: $json\n HTTP Status: $resp.statusCode\n Message: $resp.message\n Response Body: $resp.body");
}
this.printDebug("Request ($verb):\n URL: $requestUrl\n JSON: $json");
this.printDebug("Response:\n HTTP Status: $resp.statusCode\n Message: $resp.message\n Response Body: $resp.body");
return resp;
}
class HttpResponse {
String body;
String message;
Integer statusCode;
boolean failure = false;
public HttpResponse(HttpURLConnection connection){
this.statusCode = connection.responseCode;
this.message = connection.responseMessage;
if(statusCode == 200 || statusCode == 201){
this.body = connection.content.text;//this would fail the pipeline if there was a 400
}else{
this.failure = true;
this.body = connection.getErrorStream().text;
}
connection = null; //set connection to null for good measure, since we are done with it
}
}
And then I can do a GET with something like:
HttpResponse resp = doGetHttpRequest("http://some.url");
And a PUT with JSON data using something like:
HttpResponse resp = this.doPutHttpRequestWithJson("{\"propA\":\"foo\"}", "http://some.url");
Have you tried Groovy's HTTPBuilder Class?
For example:
#Grapes(
#Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7.1')
)
import groovyx.net.http.HTTPBuilder
import static groovyx.net.http.ContentType.*
import static groovyx.net.http.Method.*
def http = new HTTPBuilder("http://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demo")
http.request(POST, JSON ) { req ->
body = []
response.success = { resp, reader ->
println "$resp.statusLine Respond rec"
}
}
Blocking the main thread on I/O calls is not a good idea.
Delegating the I/O operation to a shell step is the recommended way currently.
The other way, which requires development, is to add a new step. By the way, there is an initiative to add a common set of steps to be used securely inside the pipeline script, although a full REST client owes its own plugin.
Do a GET with the Basic Auth header.
def accessToken = "ACCESS_TOKEN".bytes.encodeBase64().toString()
def req = new URL("https://raw.githubusercontent.com/xxxx/something/hosts").openConnection();
req.setRequestProperty("Authorization", "Basic " + accessToken)
def content = req.getInputStream().getText()

Response from a POST request in Groovy RESTClient is missing data

I am using groovy RESTClient 0.6 to make a POST request. I expect an XML payload in the response. I have the following code:
def restclient = new RESTClient('<some URL>')
def headers= ["Content-Type": "application/xml"]
def body= getClass().getResource("/new_resource.xml").text
/*
If I omit the closure from the following line of code
RESTClient blows up with an NPE..BUG?
*/
def response = restclient.post(
path:'/myresource', headers:headers, body:body){it}
println response.status //prints correct response code
println response.headers['Content-Length']//prints 225
println response.data //Always null?!
The response.data is always null, even though when I try the same request using Google chrome's postman client, I get back the expected response body. Is this a known issue with RESTClient?
The HTTP Builder documentation says that data is supposed to contain the parsed response content but, as you've discovered, it just doesn't. You can, however, get the parsed response content from the reader object. The easiest, most consistent way I've found of doing this is to set the default success and failure closures on your RESTClient object like so:
def restClient = new RESTClient()
restClient.handler.failure = { resp, reader ->
[response:resp, reader:reader]
}
restClient.handler.success = { resp, reader ->
[response:resp, reader:reader]
}
You'll get the same thing on success and failure: a Map containing the response (which is an instance of HttpResponseDecorator) and the reader (the type of which will be determined by the content of the response body).
You can then access the response and reader thusly:
def map = restClient.get([:]) // or POST, OPTIONS, etc.
def response = map['response']
def reader = map['reader']
assert response.status == 200
I faced a similar issue and I took the cue from Sams solution but used closures to address it (similar solution but coded using closures instead of the returned object).
resp.data is always null when using the RESTClient, however the reader contains the data, so it would look something like this:
def restclient = new RESTClient('<some URL>')
def headers= ["Content-Type": "application/xml"]
def body= getClass().getResource("/new_resource.xml").text
try {
restclient.post(path:'/myresource', headers:headers, body:body) { resp, reader ->
resp.status // Status Integer
resp.contentType // Content type String
resp.headers // Map of headers
resp.data // <-- ALWAYS null (the bug you faced)
reader // <-- Data you're looking for
}
} catch (Exception e) {
e.response.status // Get HTTP error status Integer
}

XmlSlurper.parse(uri) with HTTP basic authentication

I need to grab a data from XML-RPC web-service.
new XmlSlurper().parse("http://host/service") works fine, but now I have a particular service that requires basic HTTP authentication.
How can I set username and password for parse() method, or modify HTTP headers of the request?
Using http://username:password#host/service doesn't help - I still get java.io.IOException: Server returned HTTP response code: 401 for URL exception.
Thanks
I found this code over here which might help?
Editing this code to your situation, we get:
def addr = "http://host/service"
def authString = "username:password".getBytes().encodeBase64().toString()
def conn = addr.toURL().openConnection()
conn.setRequestProperty( "Authorization", "Basic ${authString}" )
if( conn.responseCode == 200 ) {
def feed = new XmlSlurper().parseText( conn.content.text )
// Work with the xml document
} else {
println "Something bad happened."
println "${conn.responseCode}: ${conn.responseMessage}"
}
This will work for you
Please remember to use this instead of the 'def authString' mentioned above:
def authString = "${usr}:${pwd}".getBytes().encodeBase64().toString()

Resources