Monday, 27 May 2013

Lua: Using split method from the string_ext module

Due to the minimalist nature of Lua, the standard library for string does not contain commonly used methods like split. However, the module string_ext contains them. Below is a snippet on how to use the split method. The point to consider here is the module we have to import in order for the method to work correctly. Importing only the string_ext module is not enough. We have to import list and strbuf modules as well.
require("string_ext")
require("list")
require("strbuf")
print(string.split("Hello World", " ")) -- outputs {1=Hello,2=World}

Tuesday, 14 May 2013

HTTPBuilder Requests with Local SSL Certificate in Grails

HTTPBuilder APIs for Groovy which is a wrapper for Apache's HTTPClient allows to make HTTP/HTTPS requests from JVM. Making HTTP requests are pretty straight forward. However, HTTPS requests to local server running a self-signed SSL certificate involves a bit of work.

HTTPBuilder APIs are available to grails as a plugin, called rest. To install the plugin go to the grails project directory and run
grails install-plugin rest
1. Run the grails application with HTTPS.
2. Access the home page over HTTPS which will display a security warning. If you are using Firefox, choose 'View' in the 'Add Security Exception' dialog to view the SSL certificate. From the details tab, choose 'Export' and save as X.509 Certificate (PEM) with file name, say 'localhost' which will be saved as 'localhost.crt'.
3. Now we have to import the certificate to the Java keystore. From terminal (cmd), navigate to the location where the certificate is saved and type the following command:
keytool -importcert -alias "subdomain.your-website-domain.com" -file localhost.crt -keystore truststore.jks -storepass password1234
4. Copy the truststore.jks file created in step 3 to grails-app/conf folder.
5. RestConnector is a simple example which can be used for HTTP request. We will modify the class to support HTTPS requests.
//RestConnector.groovy
import grails.converters.JSON

import groovyx.net.http.HTTPBuilder
import groovyx.net.http.ContentType
import groovyx.net.http.Method
import org.apache.http.impl.client.DefaultRedirectStrategy
import org.apache.http.impl.cookie.BasicClientCookie

import java.security.KeyStore
import org.apache.http.conn.scheme.Scheme
import org.apache.http.conn.ssl.SSLSocketFactory
import org.apache.http.HttpRequest
import org.apache.http.HttpResponse
import org.apache.http.protocol.HttpContext

import javax.servlet.http.HttpServletResponse
import javax.servlet.http.Cookie

import groovyx.net.http.Method
import groovyx.net.http.ContentType
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.HttpResponseDecorator

class RestConnector {
    private String baseUrl
    private HTTPBuilder httpBuilder
    private List<string> cookies

    RestConnector(String url) {
        this.baseUrl = url
        this.httpBuilder = initializeHttpBuilder()
        this.cookies = []
    }

    public def request(Method method, ContentType contentType, String url, Map<String, Serializable> params) {
        debug("Send $method request to ${this.baseUrl}$url: $params")

        // import the key from the keystore
        def keyStore = KeyStore.getInstance(KeyStore.defaultType)
        getClass().getResource( "/truststore.jks").withInputStream {
            keyStore.load(it, "password1234".toCharArray())
        }
        SSLSocketFactory sf = new SSLSocketFactory(keyStore)
        sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
        httpBuilder.client.connectionManager.schemeRegistry.register(new Scheme("https", sf, 443))

        httpBuilder.request(method, contentType) { request ->
            uri.path = url
            //uri.query = params
            headers['Cookie'] = cookies.join(';')
            requestContentType = ContentType.URLENC
            // sample params
            body = ['username': "${params.username}", 'password': "${params.password}", 'otherParams': "${params.otherExampleParams}"]
            headers.'Connection' = params['Connection']
            headers.'Cookie' = params['Cookie']
            println("::Cookie: -> " + params['Cookie'])
            headers.'Host' = params['Host']
            headers.'X-Requested-With' = params['X-Requested-With']
            headers.'Origin' = params['Origin']
        }
    }

    private HTTPBuilder initializeHttpBuilder() {
        def httpBuilder = new HTTPBuilder(baseUrl)

        httpBuilder.handler.success = { HttpResponseDecorator resp, reader ->
            resp.getHeaders('Set-Cookie').each {
                //[Set-Cookie: JSESSIONID=E68D4799D4D6282F0348FDB7E8B88AE9; Path=/frontoffice/; HttpOnly]
                String cookie = it.value.split(';')[0]
                debug("Adding cookie to collection: $cookie")
                cookies.add(cookie)
            }
            if (resp.status == 302) {
                println('302 detected')
                return ['status': 'redirect', 'response': resp]
            }
            debug("Resp: ${resp}")
            debug("Reader: ${reader}")
            return ['status': 'ok', 'result': reader]
        }

        // Enable to follow redirect requests
        /*httpBuilder.client.setRedirectStrategy(new DefaultRedirectStrategy() {
            @Override
            boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context) {
                def redirected = super.isRedirected(request, response, context)
                return redirected || response.getStatusLine().getStatusCode() == 302
            }
        })*/
        return httpBuilder
    }

    private debug(String message) {
        System.out.println(message) //for Gradle
    }
}
6. Using the above class in a grails controller:
//ExampleController.groovy
// ..imports
class ExampleController {
    def index() {
        String serverName = request.getServerName()
        String serverUrl = "https://${serverName}"
        RestConnector restConnector = new RestConnector(serverUrl)  //some https url here
        def result = restConnector.request(Method.POST, ContentType.JSON, "${serverUrl}", ['username': "${username}", 'password': "${password}", 'otherParam': "${otherParam}"])
        if (result.status == "redirect") {
            def resp = result.response
            def heads = resp.getHeaders()
            redirect(uri: 'redirect-uri-here')
        } else if (result.status == "ok") {
            render (result.result as JSON)
        }
    }
}