/**
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2013 Andreas Wohlén
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
*/
package httpwrapper;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import javax.xml.bind.DatatypeConverter;
/**
* Version 1.0
* <p>
* Wrapper class for {@link HttpURLConnection} for
* simple http requests. Simple means send/receive text data
* supporting the most common request parameters without streaming. Usage:
* <p>
* 1. Format the request: <code>Http conn = new Http().method("POST").url("http://localhost/").data("My post data!");</code>
* <p>
* 2. Execute request: <code>String respData = conn.exec();</code>
* <p>
* 3. Optional. Check response: <code>conn.respCode(), conn.respHeaders(), ...</code>
* <p>
* 4. Optional. Clear data: <code>conn.clear(), conn.clearReq(), conn.clearResp()</code>
* <p>
*/
/*
* TODO:
* - Post form data.
* - Cookies?
*/
public class Http {
/** Convenience method for simply getting an URL. */
public static String get(String url) throws IOException {
return new Http(url).exec();
}
//request params
private String url;
private String method;
private int timeout;
private List<String> reqHeaderFieldNames;
private List<String> reqHeaderFieldValues;
private String basicAuthUsername;
private String basicAuthPassword;
private String reqData;
//response data
private int respCode;
private String respData;
private Map<String, List<String>> respHeaders;
private IOException ioException;
/** Create an empty http object. */
public Http() {
clear();
}
/** Same as <code>new Http().url(myurl)</code>. */
public Http(String urlToExecute) {
clear();
url = urlToExecute;
}
/** Resets both req and resp to default values effectively recycling this instance. */
public Http clear() {
clearReq();
clearResp();
return this;
}
/** Reset req parameters to default values. */
public Http clearReq() {
url = null;
method = "GET";
timeout = -1;
reqHeaderFieldNames = null;
reqHeaderFieldValues = null;
basicAuthUsername = null;
basicAuthPassword = null;
reqData = null;
return this;
}
/** Removes latest response releasing potentially large resources like respData. */
public Http clearResp() {
respCode = -1;
respData = null;
respHeaders = null;
ioException = null;
return this;
}
/** Set the url. Wrapper for {@link URL#URL(String)}. */
public Http url(String urlToExecute) {
url = urlToExecute;
return this;
}
/** Wrapper for {@link HttpURLConnection#getURL()} */
public String url() {
return url;
}
/** Wrapper for {@link HttpURLConnection#setRequestMethod(String)}.
* Default is "GET". */
public Http method(String httpMethodToUse) {
method = httpMethodToUse;
return this;
}
/** Wrapper for {@link HttpURLConnection#getRequestMethod()} */
public String method() {
return method;
}
/** Wrapper for {@link HttpURLConnection#setConnectTimeout(int)}. */
public Http timeout(int newTimeoutMillis) {
timeout = newTimeoutMillis;
return this;
}
/** Wrapper for {@link HttpURLConnection#getConnectTimeout()} */
public int timeout() {
return timeout;
}
/** Set a request header. Wrapper for
* {@link HttpURLConnection#setRequestProperty(String, String)}. */
public Http header(String fieldName, String fieldValue) {
if(reqHeaderFieldNames == null) {
reqHeaderFieldNames = new ArrayList<String>();
reqHeaderFieldValues = new ArrayList<String>();
}
reqHeaderFieldNames.add(fieldName);
reqHeaderFieldValues.add(fieldValue);
return this;
}
/** Set basic authentication params. Null user will reset. */
public Http basicAuth(String username, String password) {
basicAuthUsername = username;
basicAuthPassword = password;
return this;
}
/** Set data to send with this request. */
public Http data(String dataToSend) {
reqData = dataToSend;
return this;
}
/** @return request data set by {@link #data(String)}. */
public String data() {
return reqData;
}
/**
* Execute request silently. Check exceptions
* by calling {@link #ioException()}.
*
* @return the response data including error data.
* (Same as {@link #respData()}.)
*/
public String execSilently() {
try {
httpExec();
return respData;
} catch (IOException e) {
ioException = e;
return respData;
}
}
/**
* Execute request.
*
* @return the response data on a successful request.
* (Retrieve response data for http codes above 400 by {@link #respData()}
* after caught exception).
* @throws IOException
*/
public String exec() throws IOException {
httpExec();
return respData;
}
/** Checks whether we got a response code from server.
* If false, check {@link #ioException}. */
public boolean gotResp() {
return respCode != -1;
}
/** Wrapper for {@link HttpURLConnection#getResponseCode()}. */
public int respCode() {
return respCode;
}
/** @return The latest response from server including error data
* (eg. http 404 response). */
public String respData() {
return respData;
}
/** Wrapper for {@link URLConnection#getHeaderFields()}. */
public Map<String, List<String>> respHeaders() {
return respHeaders;
}
/** @return saved exception from {@link #execSilently()}. */
public IOException ioException() {
return ioException;
}
private void httpExec() throws IOException {
clearResp();
final HttpURLConnection httpCon = (HttpURLConnection)
new URL(url).openConnection();
try {
//set connection parameters
httpCon.setRequestMethod(method);
if(timeout >= 0) httpCon.setConnectTimeout(timeout);
if(reqHeaderFieldNames != null) {
for(int i=0; i<reqHeaderFieldNames.size(); i++) {
httpCon.setRequestProperty(reqHeaderFieldNames.get(i), reqHeaderFieldValues.get(i));
}
}
if(basicAuthUsername != null) {
httpCon.setRequestProperty("Authorization", "Basic " +
DatatypeConverter.printBase64Binary(
(basicAuthUsername+":"+basicAuthPassword).getBytes()));
}
//send data
if(reqData != null) {
httpCon.setDoOutput(true);
writeStream(httpCon.getOutputStream(), reqData);
}
//receive data
respCode = httpCon.getResponseCode();
respHeaders = httpCon.getHeaderFields();
try {
respData = readStream(httpCon.getInputStream());
} catch (IOException e) {
respData = readStream(httpCon.getErrorStream());
throw e;
}
} finally {
httpCon.disconnect();
}
}
/**
* Write an output stream to a string.
* It will internally wrap the stream with a {@link BufferedOutputStream}
* and close it when done.
*/
public static void writeStream(OutputStream os, String data) throws IOException {
final OutputStreamWriter out = new OutputStreamWriter(
new BufferedOutputStream(os));
try {
out.write(data);
} finally {
out.close();
}
}
/**
* Read an input stream into a string assuming UTF-8.
* It will internally wrap the stream with a {@link BufferedInputStream}
* and close it when done.
*/
public static String readStream(InputStream is) throws IOException {
if(is == null) return null;
final Scanner scanner = new Scanner(new BufferedInputStream(is), "UTF-8");
scanner.useDelimiter("\\A");
final String content = scanner.hasNext() ? scanner.next() : "";
final IOException readException = scanner.ioException();
scanner.close();
if(readException != null) {
throw new IOException(readException);
}
return content;
}
}