package be.cytomine.client;
/*
* Copyright (c) 2009-2016. Authors: see NOTICE file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.log4j.Logger;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* HTTP Client abstraction
*/
public class HttpClient {
private static final Logger log = Logger.getLogger(HttpClient.class);
DefaultHttpClient client;
HttpHost targetHost;
BasicHttpContext localcontext;
URL URL;
HttpResponse response;
Header[] headersArray;
boolean isAuthByPrivateKey;
String publicKey;
String privateKey;
String host;
public HttpClient() {
}
public HttpClient(String publicKey, String privateKey, String host) {
this.publicKey = publicKey;
this.privateKey = privateKey;
this.host = host;
}
public void addHeader(String name, String value) {
Header[] headers;
if (headersArray != null) {
headers = new Header[headersArray.length + 1];
}
else {
headers = new Header[1];
}
for (int i = 0; i < headers.length - 1; i++) {
headers[i] = headersArray[i];
}
headers[headers.length - 1] = new BasicHeader(name, value);
headersArray = headers;
}
public void connect(String url, String username, String password) throws IOException {
isAuthByPrivateKey = false;
log.info("Connection to " + url + " with login=" + username + " and pass=" + password);
URL = new URL(url);
targetHost = new HttpHost(URL.getHost(), URL.getPort());
client = new DefaultHttpClient();
// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate BASIC scheme object and add it to the local
// auth cache
BasicScheme basicAuth = new BasicScheme();
authCache.put(targetHost, basicAuth);
// Add AuthCache to the execution context
localcontext = new BasicHttpContext();
localcontext.setAttribute(ClientContext.AUTH_CACHE, authCache);
// Set credentials
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, password);
client.getCredentialsProvider().setCredentials(AuthScope.ANY, creds);
}
public void connect(String url) throws IOException {
log.info("Connection to " + url);
isAuthByPrivateKey = true;
URL = new URL(url);
targetHost = new HttpHost(URL.getHost(), URL.getPort());
client = new DefaultHttpClient();
localcontext = new BasicHttpContext();
}
public int get() throws IOException {
HttpGet httpGet = new HttpGet(URL.toString());
if (isAuthByPrivateKey) httpGet.setHeaders(headersArray);
response = client.execute(targetHost, httpGet, localcontext);
return response.getStatusLine().getStatusCode();
}
public int get(String url, String dest) throws IOException {
log.debug("get:" + url);
URL URL = new URL(url);
HttpHost targetHost = new HttpHost(URL.getHost(), URL.getPort());
log.debug("targetHost:" + targetHost);
DefaultHttpClient client = new DefaultHttpClient();
log.debug("client:" + client);
// Add AuthCache to the execution context
BasicHttpContext localcontext = new BasicHttpContext();
log.debug("localcontext:" + localcontext);
headersArray = null;
authorize("GET", URL.toString(), "", "application/json,*/*");
HttpGet httpGet = new HttpGet(URL.getPath());
httpGet.setHeaders(headersArray);
HttpResponse response = client.execute(targetHost, httpGet, localcontext);
int code = response.getStatusLine().getStatusCode();
log.info("url=" + url + " is " + code + "(OK=" + HttpURLConnection.HTTP_OK + ",MOVED=" + HttpURLConnection.HTTP_MOVED_TEMP + ")");
boolean isOK = (code == HttpURLConnection.HTTP_OK);
boolean isFound = (code == HttpURLConnection.HTTP_MOVED_TEMP);
boolean isErrorServer = (code == HttpURLConnection.HTTP_INTERNAL_ERROR);
if (!isOK && !isFound & !isErrorServer) {
throw new IOException(url + " cannot be read: " + code);
}
HttpEntity entity = response.getEntity();
if (entity != null) {
entity.writeTo(new FileOutputStream(dest));
}
return code;
}
public int delete() throws IOException {
log.debug("Delete " + URL.getPath());
HttpDelete httpDelete = new HttpDelete(URL.toString());
if (isAuthByPrivateKey) {
httpDelete.setHeaders(headersArray);
}
response = client.execute(targetHost, httpDelete, localcontext);
return response.getStatusLine().getStatusCode();
}
public int post(String data) throws IOException {
log.debug("Post " + URL.getPath());
HttpPost httpPost = new HttpPost(URL.toString());
if (isAuthByPrivateKey) {
httpPost.setHeaders(headersArray);
}
log.debug("Post send :" + data.replace("\n", ""));
//write data
BasicHttpEntity entity = new BasicHttpEntity();
entity.setContent(new ByteArrayInputStream(data.getBytes()));
entity.setContentLength((long) data.getBytes().length);
httpPost.setEntity(entity);
response = client.execute(targetHost, httpPost, localcontext);
return response.getStatusLine().getStatusCode();
}
public int put(String data) throws IOException {
log.debug("Put " + URL.getPath());
HttpPut httpPut = new HttpPut(URL.toString());
if (isAuthByPrivateKey) {
httpPut.setHeaders(headersArray);
}
log.debug("Put send :" + data.replace("\n", ""));
//write data
BasicHttpEntity entity = new BasicHttpEntity();
entity.setContent(new ByteArrayInputStream(data.getBytes()));
entity.setContentLength((long) data.getBytes().length);
httpPut.setEntity(entity);
response = client.execute(targetHost, httpPut, localcontext);
return response.getStatusLine().getStatusCode();
}
public int post(byte[] data) throws IOException {
log.debug("POST " + URL.toString());
HttpPost httpPost = new HttpPost(URL.toString());
if (isAuthByPrivateKey) {
httpPost.setHeaders(headersArray);
}
log.debug("Post send :" + data.length);
InputStreamEntity reqEntity = new InputStreamEntity(new ByteArrayInputStream(data), data.length);
reqEntity.setContentType("binary/octet-stream");
reqEntity.setChunked(false);
BufferedHttpEntity myEntity = null;
try {
myEntity = new BufferedHttpEntity(reqEntity);
} catch (IOException e) {
// TODO Auto-generated catch block
log.error(e);
}
httpPost.setEntity(myEntity);
response = client.execute(targetHost, httpPost, localcontext);
return response.getStatusLine().getStatusCode();
}
public int post(MultipartEntity entity) throws IOException {
log.debug("POST " + URL.toString());
HttpPost httpPost = new HttpPost(URL.toString());
if (isAuthByPrivateKey) {
httpPost.setHeaders(headersArray);
}
log.debug("Post send :" + entity);
httpPost.setEntity(entity);
response = client.execute(targetHost, httpPost, localcontext);
return response.getStatusLine().getStatusCode();
}
public int put(byte[] data) throws IOException {
log.debug("Put " + URL.getPath());
HttpPut httpPut = new HttpPut(URL.getPath());
if (isAuthByPrivateKey) {
httpPut.setHeaders(headersArray);
}
log.debug("Put send :" + data.length);
InputStreamEntity reqEntity = new InputStreamEntity(new ByteArrayInputStream(data), data.length);
reqEntity.setContentType("binary/octet-stream");
reqEntity.setChunked(false);
BufferedHttpEntity myEntity = null;
try {
myEntity = new BufferedHttpEntity(reqEntity);
} catch (IOException e) {
// TODO Auto-generated catch block
log.error(e);
}
httpPut.setEntity(myEntity);
response = client.execute(targetHost, httpPut, localcontext);
return response.getStatusLine().getStatusCode();
}
public int download(String dest) throws IOException {
log.debug("Download " + URL.getPath());
HttpGet httpGet = new HttpGet(URL.getPath());
if (isAuthByPrivateKey) {
httpGet.setHeaders(headersArray);
}
response = client.execute(targetHost, httpGet, localcontext);
HttpEntity entity = response.getEntity();
if (entity != null) {
FileOutputStream fos = new FileOutputStream(dest);
entity.writeTo(fos);
fos.close();
}
return response.getStatusLine().getStatusCode();
}
public BufferedImage readBufferedImageFromURL(String url) throws IOException {
log.debug("readBufferedImageFromURL:" + url);
URL URL = new URL(url);
HttpHost targetHost = new HttpHost(URL.getHost(), URL.getPort());
log.debug("targetHost:" + targetHost);
DefaultHttpClient client = new DefaultHttpClient();
log.debug("client:" + client);
// Add AuthCache to the execution context
BasicHttpContext localcontext = new BasicHttpContext();
log.debug("localcontext:" + localcontext);
headersArray = null;
authorize("GET", URL.toString(), "", "application/json,*/*");
BufferedImage img = null;
HttpGet httpGet = new HttpGet(URL.toString());
httpGet.setHeaders(headersArray);
HttpResponse response = client.execute(targetHost, httpGet, localcontext);
int code = response.getStatusLine().getStatusCode();
log.info("url=" + url + " is " + code + "(OK=" + HttpURLConnection.HTTP_OK + ",MOVED=" + HttpURLConnection.HTTP_MOVED_TEMP + ")");
boolean isOK = (code == HttpURLConnection.HTTP_OK);
boolean isFound = (code == HttpURLConnection.HTTP_MOVED_TEMP);
boolean isErrorServer = (code == HttpURLConnection.HTTP_INTERNAL_ERROR);
if (!isOK && !isFound & !isErrorServer) throw new IOException(url + " cannot be read: " + code);
HttpEntity entity = response.getEntity();
if (entity != null) {
img = ImageIO.read(entity.getContent());
}
return img;
}
public static BufferedImage readBufferedImageFromPOST(String url, String post) throws IOException{
log.debug("readBufferedImageFromURL:" + url);
URL URL = new URL(url);
HttpHost targetHost = new HttpHost(URL.getHost(), URL.getPort());
log.debug("targetHost:" + targetHost);
DefaultHttpClient client = new DefaultHttpClient();
log.debug("client:" + client);
// Add AuthCache to the execution context
BasicHttpContext localcontext = new BasicHttpContext();
log.debug("localcontext:" + localcontext);
BufferedImage img = null;
HttpPost httpPost = new HttpPost(URL.toString());
httpPost.setEntity(new StringEntity(post, "UTF-8"));
HttpResponse response = client.execute(targetHost, httpPost, localcontext);
int code = response.getStatusLine().getStatusCode();
log.info("url=" + url + " is " + code + "(OK=" + HttpURLConnection.HTTP_OK + ",MOVED=" + HttpURLConnection.HTTP_MOVED_TEMP + ")");
boolean isOK = (code == HttpURLConnection.HTTP_OK);
boolean isFound = (code == HttpURLConnection.HTTP_MOVED_TEMP);
boolean isErrorServer = (code == HttpURLConnection.HTTP_INTERNAL_ERROR);
if (!isOK && !isFound & !isErrorServer) throw new IOException(url + " cannot be read: " + code);
HttpEntity entity = response.getEntity();
if (entity != null) {
img = ImageIO.read(entity.getContent());
}
return img;
}
public static BufferedImage readBufferedImageFromRETRIEVAL(String url, String publicKey, String privateKey, String host) throws IOException {
log.debug("readBufferedImageFromURL:" + url);
URL URL = new URL(url);
HttpHost targetHost = new HttpHost(URL.getHost(), URL.getPort());
log.debug("targetHost:" + targetHost);
DefaultHttpClient client = new DefaultHttpClient();
log.debug("client:" + client);
// Add AuthCache to the execution context
BasicHttpContext localcontext = new BasicHttpContext();
log.debug("localcontext:" + localcontext);
Header[] headers = authorizeFromRETRIEVAL("GET", URL.toString(), "", "", publicKey, privateKey, host);
log.debug("headers:" + headers.length);
BufferedImage img = null;
HttpGet httpGet = new HttpGet(URL.toString());
httpGet.setHeaders(headers);
HttpResponse response = client.execute(targetHost, httpGet, localcontext);
int code = response.getStatusLine().getStatusCode();
log.info("url=" + url + " is " + code + "(OK=" + HttpURLConnection.HTTP_OK + ",MOVED=" + HttpURLConnection.HTTP_MOVED_TEMP + ")");
boolean isOK = (code == HttpURLConnection.HTTP_OK);
boolean isFound = (code == HttpURLConnection.HTTP_MOVED_TEMP);
boolean isErrorServer = (code == HttpURLConnection.HTTP_INTERNAL_ERROR);
if (!isOK && !isFound & !isErrorServer) {
throw new IOException(url + " cannot be read: " + code);
}
HttpEntity entity = response.getEntity();
if (entity != null) {
log.debug("img=" + entity.getContent());
img = ImageIO.read(entity.getContent());
}
return img;
}
public static Header[] authorizeFromRETRIEVAL(String action, String urlFullStr, String contentType, String accept, String publicKey, String privateKey, String host) throws IOException {
log.debug("authorize: action=" + action + ", url=" + urlFullStr + ", contentType=" + contentType + ",accept=" + accept);
String url = urlFullStr.replace(host, "");
log.debug("authorize: url short=" + url);
Header[] headers = new Header[3];
headers[0] = new BasicHeader("accept", accept);
headers[1] = new BasicHeader("date", getActualDateStr());
String canonicalHeaders = action + "\n\n" + contentType + "\n" + headers[1].getValue() + "\n";
String messageToSign = canonicalHeaders + url;
SecretKeySpec privateKeySign = new SecretKeySpec(privateKey.getBytes(), "HmacSHA1");
try {
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(privateKeySign);
byte[] rawHmac = mac.doFinal(new String(messageToSign.getBytes(), "UTF-8").getBytes());
//byte[] signatureBytes = Base64.encodeToByte(rawHmac,false);
byte[] signatureBytes = org.apache.commons.codec.binary.Base64.encodeBase64(rawHmac, false);
String authorization = "CYTOMINE " + publicKey + ":" + new String(signatureBytes);
headers[2] = new BasicHeader("authorization", authorization);
} catch (GeneralSecurityException e) {
throw new IOException(e);
}
return headers;
}
public void authorize(String action, String url, String contentType, String accept) throws IOException {
url = url.replace(host, "");
url = url.replace("http://" + host, "");
url = url.replace("https://" + host, "");
TreeMap<String, String> headers = new TreeMap<String, String>();
headers.put("accept", accept);
headers.put("date", getActualDateStr());
log.debug("AUTHORIZE: " + action + "\\n\\n" + contentType + "\\n" + headers.get("date") + "\n");
String canonicalHeaders = action + "\n\n" + contentType + "\n" + headers.get("date") + "\n";
String messageToSign = canonicalHeaders + url;
log.debug("publicKey=" + publicKey);
log.debug("privateKey=" + privateKey);
log.debug("messageToSign=" + messageToSign);
SecretKeySpec privateKeySign = new SecretKeySpec(privateKey.getBytes(), "HmacSHA1");
try {
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(privateKeySign);
byte[] rawHmac = mac.doFinal(new String(messageToSign.getBytes(), "UTF-8").getBytes());
byte[] signatureBytes = Base64.encodeBase64(rawHmac);
String signature = new String(signatureBytes);
String authorization = "CYTOMINE " + publicKey + ":" + signature;
log.debug("signature=" + signature);
log.debug("authorization=" + authorization);
headers.put("authorization", authorization);
for (String key : headers.keySet()) {
addHeader(key, headers.get(key));
}
} catch (GeneralSecurityException e) {
throw new IOException(e);
}
}
public static String getActualDateStr() {
Date today = Calendar.getInstance().getTime();
return new SimpleDateFormat("%E, %d %M %Y %H:%M:%S +0000").format(today);
}
public String getResponseData() throws IOException {
HttpEntity entityResponse = response.getEntity();
return IOUtils.toString(entityResponse.getContent());
}
public int getResponseCode() {
return response.getStatusLine().getStatusCode();
}
public void disconnect() {
log.debug("Disconnect");
try {
client.getConnectionManager().shutdown();
} catch (Exception e) {
log.error(e);
}
}
}