Implemented as a Groovy script, meaning there is no top-level class per se. Could probably be more concise.
//imports removed for brevity
// Housekeeping - see what the User wants
// Groovy note: not declaring a var puts it into "the binding", which makes it globally accessible
// * this is only valid for Groovy scripts
// * declaring a type (or def) of a var automatically scopes it locally
config = new Config()
parseInput(args)
baseUrl = "https://" + config.host + ":" + config.port + "/myapp/"
debug "\tBase URL: " + baseUrl
println "Loading keys..."
// Create client keystore - this needs to contain:
// Client's certificate (i.e., Subject CN == clientname)
// Client's private key
// Needs to be a JKS keystore
clientKeys = createKeystore(config.clientKeyFile, config.clientKeyPass)
// Truststore for server verification - this needs to contain:
// Server certificate: Subject CN == hostname
// Server private key
// Needs to be a JKS keystore
serverTrust = createKeystore(config.serverKeyFile, config.serverKeyPass)
debug "\tclient key: " + clientKeys
debug "\tserver key: " + serverTrust
try {
// ** Create Connection **
socketFactory = new SSLSocketFactory(
SSLSocketFactory.TLS,
clientKeys,
config.clientKeyPass,
serverTrust,
null,
new TrustSelfSignedStrategy(), // not a good choice for production
SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); // not a good choice for production
// Connect socket to enable client PKI authentication
HttpParams params = new BasicHttpParams();
params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 1000);
SSLSocket socket = (SSLSocket) socketFactory.createSocket(params);
socket.setEnabledCipherSuites("SSL_RSA_WITH_RC4_128_MD5");
InetSocketAddress address = new InetSocketAddress(config.host, Integer.parseInt(config.port));
socketFactory.connectSocket(socket, address, null, params);
sch = new Scheme("https", new Integer(config.port), socketFactory);
httpclient = new DefaultHttpClient();
httpclient.getConnectionManager().getSchemeRegistry().register(sch);
// Create a local instance of cookie store
cookieStore = new BasicCookieStore();
// Create local HTTP context
localContext = new BasicHttpContext();
// Bind custom cookie store to the local context
localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
// ****** Example usage *******
// This is the map-based impl - produces same output as the object-based
// .... put your own data in here
def data = [
"_method" : "POST" ,
"displayName" : row[DISPLAY_NAME],
"imageUrlSmall" : row[IMAGE_URL_SMALL] ,
"imageUrlLarge" : row[IMAGE_URL_LARGE],
"width" : row[WIDTH],
"height" : row[HEIGHT]
]
String dataStr = JSONSerializer.toJSON(data)
try {
int retries = 5
while (!created && retries-- > 0) {
rsp = post (baseUrl + "widget", ["data":dataStr])
matcher = rsp =~ /\"success\":([\w]+)/
if (matcher[0]) {
val = matcher[0][1]
debug "\tCREATE RESPONSE: ${val}"
created = Boolean.parseBoolean(val)
}
// This is just for logging
if (!created) {
println "ERROR: Creation failed with response: ${rsp}"
// Detect message failure and retry
// Detect failure by GUID invalid format
matcher = rsp =~ /Property [name].*does not match the required pattern/
if (matcher[0]) {
debug "\tERROR RECEIVED:: name is not correct format: ${guid}"
}
}
} // end while
}
catch (Exception e) {
debug "Error creating listing: " + e
e.printStackTrace()
guid = null
}
}
/**
* Return a Keystore from a given filename and password
*/
def KeyStore createKeystore (filename, password) {
debug "Default KeyAlgo: " + KeyManagerFactory.getDefaultAlgorithm()
debug "Default KeyStore: " + KeyStore.getDefaultType() // jks
// Pull off file extension
keyType = filename[-3..-1]
debug "Requested KeyType: " + keyType
// Client keystore, for client authentication
KeyStore keys
switch (keyType) {
case "p12":
keys = KeyStore.getInstance("pkcs12");
break
default:
keys = KeyStore.getInstance(KeyStore.getDefaultType());
}
FileInputStream instream = new FileInputStream(new File(filename));
try {
keys.load(instream, password.toCharArray());
} finally {
try { instream.close(); } catch (Exception ignore) {}
}
return keys
}
def post(url, paramMap) {
HttpPost httpPost = new HttpPost(url)
String result
// Add specific params
List nvps = new ArrayList ();
// These params always present
nvps.add(new BasicNameValuePair("sample1", "3.6.0-GA"))
nvps.add(new BasicNameValuePair("dojo.preventCache", "1302893700594"))
paramMap.each() { key, value ->
debug "${key} ==> ${value}"
// Expect the caller to JSON-ize each param, as required
if (value instanceof List) {
// Add value multiple times with same key
value.each {
nvps.add(new BasicNameValuePair(key, it.toString()))
}
}
else {
nvps.add(new BasicNameValuePair(key, value))
}
}
httpPost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8))
// Debug
debug(dump(httpPost))
debug "Posting request: " + httpPost
HttpResponse response = httpclient.execute(httpPost, localContext);
//String response = httpclient.execute(httpPost, new BasicResponseHandler());
debug "#### RESPONSE TYPE: " + response.class.name
HttpEntity entity = response.getEntity();
debug "#### RESPONSE BODY RECEIVED: " + entity.toString()
// ** Dump results **
debug "\t" + response.getStatusLine()
StringBuilder sb = new StringBuilder(255)
if (entity != null) {
InputStream ris = entity.getContent()
OutputStream os = new ByteArrayOutputStream(BUFFER_SIZE)
byte[] buffer = new byte[BUFFER_SIZE];
try {
while ((l = ris.read(buffer)) != -1) {
os.write(buffer, 0, l)
sb.append(os.toString())
os.reset()
}
} finally {
ris.close()
}
debug "\tEntity contentLength: " + sb.length()
result = sb?.toString()
debug "\t-------------------------------------"
List cookies = cookieStore.getCookies();
for (int i = 0; i < cookies.size(); i++) {
println("\tCookie: " + cookies.get(i));
}
debug "\t-------------------------------------"
}
else {
println "Error connecting...check connection details"
//!! EXIT HERE
return
}
debug "-------------------------------------"
if (result && result.size() > 256) {
debug "RESPONSE: " + result.substring(0, 256)
}
else {
debug "RESPONSE: " + result
}
debug "-------------------------------------"
return result
}
def debug(stmt) {
if (config.verbose) {
println stmt
}
}
def dump(HttpPost post) {
StringBuilder sb = new StringBuilder(128)
sb.append("\n\t------------------------------")
sb.append("\n\tRequestURL: ").append(post.getURI())
sb.append("\n\tType: ").append(post.getMethod())
sb.append("\n\tParams: ").append(post.getParams())
org.apache.http.params.BasicHttpParams
sb.append("\n\t------------------------------")
return sb.toString()
}
class Config {
String host = "localhost"
String port = "8443"
boolean verbose = false
String inputFile
String clientKeyFile
String clientKeyPass
String serverKeyFile
String serverKeyPass
}
def parseInput(args) {
if (!args || args.length < 1) {
usage()
}
for(int i=0; i < args.length; i++) {
it = args[i]
println "Processing arg: " + it
switch (it) {
case "-v":
debug "Verbose output enabled "
config.verbose = true
break
case "-h":
it = args[++i]
debug "Setting Host/Port from: " + it
// Parse out host and port
def serverAddr = it.split(":")
try {
if (serverAddr.length > 0 && serverAddr[0] != null) {
config.host = serverAddr[0]
}
if (serverAddr.length > 1 && serverAddr[1] != null) {
config.port = serverAddr[1]
}
} catch(Exception e) {
usage()
}
break
case "-clientKeys":
it = args[++i]
debug "Setting client keyfile to: " + it
config.clientKeyFile = it
break
case "-clientKeyPass":
it = args[++i]
debug "Setting client keyfile pwd to: " + it
config.clientKeyPass = it
break
case "-serverKeys":
it = args[++i]
debug "Setting server keyfile to: " + it
config.serverKeyFile = it
break
case "-serverKeyPass":
it = args[++i]
debug "Setting server keyfile pwd to: " + it
config.serverKeyPass = it
break
default:
debug "Setting inputFile to: " + it
config.inputFile = it;
}
}
}