+ * Each instance supports making a single request and cannot be reused for
+ * further requests.
+ */
+public class HttpRequest {
+
+ /**
+ * 'UTF-8' charset name
+ */
+ public static final String CHARSET_UTF8 = "UTF-8";
+
+ /**
+ * 'application/x-www-form-urlencoded' content type header value
+ */
+ public static final String CONTENT_TYPE_FORM = "application/x-www-form-urlencoded";
+
+ /**
+ * 'application/json' content type header value
+ */
+ public static final String CONTENT_TYPE_JSON = "application/json";
+
+ /**
+ * 'gzip' encoding header value
+ */
+ public static final String ENCODING_GZIP = "gzip";
+
+ /**
+ * 'Accept' header name
+ */
+ public static final String HEADER_ACCEPT = "Accept";
+
+ /**
+ * 'Accept-Charset' header name
+ */
+ public static final String HEADER_ACCEPT_CHARSET = "Accept-Charset";
+
+ /**
+ * 'Accept-Encoding' header name
+ */
+ public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
+
+ /**
+ * 'Authorization' header name
+ */
+ public static final String HEADER_AUTHORIZATION = "Authorization";
+
+ /**
+ * 'Cache-Control' header name
+ */
+ public static final String HEADER_CACHE_CONTROL = "Cache-Control";
+
+ /**
+ * 'Content-Encoding' header name
+ */
+ public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";
+
+ /**
+ * 'Content-Length' header name
+ */
+ public static final String HEADER_CONTENT_LENGTH = "Content-Length";
+
+ /**
+ * 'Content-Type' header name
+ */
+ public static final String HEADER_CONTENT_TYPE = "Content-Type";
+
+ /**
+ * 'Date' header name
+ */
+ public static final String HEADER_DATE = "Date";
+
+ /**
+ * 'ETag' header name
+ */
+ public static final String HEADER_ETAG = "ETag";
+
+ /**
+ * 'Expires' header name
+ */
+ public static final String HEADER_EXPIRES = "Expires";
+
+ /**
+ * 'If-None-Match' header name
+ */
+ public static final String HEADER_IF_NONE_MATCH = "If-None-Match";
+
+ /**
+ * 'Last-Modified' header name
+ */
+ public static final String HEADER_LAST_MODIFIED = "Last-Modified";
+
+ /**
+ * 'Location' header name
+ */
+ public static final String HEADER_LOCATION = "Location";
+
+ /**
+ * 'Proxy-Authorization' header name
+ */
+ public static final String HEADER_PROXY_AUTHORIZATION = "Proxy-Authorization";
+
+ /**
+ * 'Referer' header name
+ */
+ public static final String HEADER_REFERER = "Referer";
+
+ /**
+ * 'Server' header name
+ */
+ public static final String HEADER_SERVER = "Server";
+
+ /**
+ * 'User-Agent' header name
+ */
+ public static final String HEADER_USER_AGENT = "User-Agent";
+
+ /**
+ * 'DELETE' request method
+ */
+ public static final String METHOD_DELETE = "DELETE";
+
+ /**
+ * 'GET' request method
+ */
+ public static final String METHOD_GET = "GET";
+
+ /**
+ * 'HEAD' request method
+ */
+ public static final String METHOD_HEAD = "HEAD";
+
+ /**
+ * 'OPTIONS' options method
+ */
+ public static final String METHOD_OPTIONS = "OPTIONS";
+
+ /**
+ * 'POST' request method
+ */
+ public static final String METHOD_POST = "POST";
+
+ /**
+ * 'PUT' request method
+ */
+ public static final String METHOD_PUT = "PUT";
+
+ /**
+ * 'TRACE' request method
+ */
+ public static final String METHOD_TRACE = "TRACE";
+
+ /**
+ * 'charset' header value parameter
+ */
+ public static final String PARAM_CHARSET = "charset";
+
+ private static final String BOUNDARY = "00content0boundary00";
+
+ private static final String CONTENT_TYPE_MULTIPART = "multipart/form-data; boundary="
+ + BOUNDARY;
+
+ private static final String CRLF = "\r\n";
+
+ private static final String[] EMPTY_STRINGS = new String[0];
+
+ private static SSLSocketFactory PINNED_FACTORY;
+
+ private static SSLSocketFactory TRUSTED_FACTORY;
+
+ private static ArrayList
+ * Encodes and decodes to and from Base64 notation.
+ *
+ * I am placing this code in the Public Domain. Do with it as you will. This
+ * software comes with no guarantees or warranties but with plenty of
+ * well-wishing instead! Please visit http://iharder.net/base64 periodically
+ * to check for updates or to contribute improvements.
+ *
+ * Encodes up to three bytes of the array source and writes the
+ * resulting four Base64 bytes to destination. The source and
+ * destination arrays can be manipulated anywhere along their length by
+ * specifying srcOffset and destOffset. This method
+ * does not check to make sure your arrays are large enough to accomodate
+ * srcOffset + 3 for the source array or
+ * destOffset + 4 for the destination array. The
+ * actual number of significant bytes in your array is given by
+ * numSigBytes.
+ *
+ * This is the lowest level of the encoding methods with all possible
+ * parameters.
+ *
+ * This method ensures the path and query segments of the URL are properly
+ * encoded such as ' ' characters being encoded to '%20' or any UTF-8
+ * characters that are non-ASCII. No encoding of URLs is done by default by
+ * the {@link HttpRequest} constructors and so if URL encoding is needed this
+ * method should be called before calling the {@link HttpRequest} constructor.
+ *
+ * @param url
+ * @return encoded URL
+ * @throws HttpRequestException
+ */
+ public static String encode(final CharSequence url)
+ throws HttpRequestException {
+ URL parsed;
+ try {
+ parsed = new URL(url.toString());
+ } catch (IOException e) {
+ throw new HttpRequestException(e);
+ }
+
+ String host = parsed.getHost();
+ int port = parsed.getPort();
+ if (port != -1)
+ host = host + ':' + Integer.toString(port);
+
+ try {
+ String encoded = new URI(parsed.getProtocol(), host, parsed.getPath(),
+ parsed.getQuery(), null).toASCIIString();
+ int paramsStart = encoded.indexOf('?');
+ if (paramsStart > 0 && paramsStart + 1 < encoded.length())
+ encoded = encoded.substring(0, paramsStart + 1)
+ + encoded.substring(paramsStart + 1).replace("+", "%2B");
+ return encoded;
+ } catch (URISyntaxException e) {
+ IOException io = new IOException("Parsing URI failed");
+ io.initCause(e);
+ throw new HttpRequestException(io);
+ }
+ }
+
+ /**
+ * Append given map as query parameters to the base URL
+ *
+ * Each map entry's key will be a parameter name and the value's
+ * {@link Object#toString()} will be the parameter value.
+ *
+ * @param url
+ * @param params
+ * @return URL with appended query params
+ */
+ public static String append(final CharSequence url, final Map, ?> params) {
+ final String baseUrl = url.toString();
+ if (params == null || params.isEmpty())
+ return baseUrl;
+
+ final StringBuilder result = new StringBuilder(baseUrl);
+
+ addPathSeparator(baseUrl, result);
+ addParamPrefix(baseUrl, result);
+
+ Entry, ?> entry;
+ Object value;
+ Iterator> iterator = params.entrySet().iterator();
+ entry = (Entry, ?>) iterator.next();
+ result.append(entry.getKey().toString());
+ result.append('=');
+ value = entry.getValue();
+ if (value != null)
+ result.append(value);
+
+ while (iterator.hasNext()) {
+ result.append('&');
+ entry = (Entry, ?>) iterator.next();
+ result.append(entry.getKey().toString());
+ result.append('=');
+ value = entry.getValue();
+ if (value != null)
+ result.append(value);
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * Append given name/value pairs as query parameters to the base URL
+ *
+ * The params argument is interpreted as a sequence of name/value pairs so the
+ * given number of params must be divisible by 2.
+ *
+ * @param url
+ * @param params
+ * name/value pairs
+ * @return URL with appended query params
+ */
+ public static String append(final CharSequence url, final Object... params) {
+ final String baseUrl = url.toString();
+ if (params == null || params.length == 0)
+ return baseUrl;
+
+ if (params.length % 2 != 0)
+ throw new IllegalArgumentException(
+ "Must specify an even number of parameter names/values");
+
+ final StringBuilder result = new StringBuilder(baseUrl);
+
+ addPathSeparator(baseUrl, result);
+ addParamPrefix(baseUrl, result);
+
+ Object value;
+ result.append(params[0]);
+ result.append('=');
+ value = params[1];
+ if (value != null)
+ result.append(value);
+
+ for (int i = 2; i < params.length; i += 2) {
+ result.append('&');
+ result.append(params[i]);
+ result.append('=');
+ value = params[i + 1];
+ if (value != null)
+ result.append(value);
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * Start a 'GET' request to the given URL
+ *
+ * @param url
+ * @return request
+ * @throws HttpRequestException
+ */
+ public static HttpRequest get(final CharSequence url)
+ throws HttpRequestException {
+ return new HttpRequest(url, METHOD_GET);
+ }
+
+ /**
+ * Start a 'GET' request to the given URL
+ *
+ * @param url
+ * @return request
+ * @throws HttpRequestException
+ */
+ public static HttpRequest get(final URL url) throws HttpRequestException {
+ return new HttpRequest(url, METHOD_GET);
+ }
+
+ /**
+ * Start a 'GET' request to the given URL along with the query params
+ *
+ * @param baseUrl
+ * @param params
+ * The query parameters to include as part of the baseUrl
+ * @param encode
+ * true to encode the full URL
+ *
+ * @see #append(CharSequence, Map)
+ * @see #encode(CharSequence)
+ *
+ * @return request
+ */
+ public static HttpRequest get(final CharSequence baseUrl,
+ final Map, ?> params, final boolean encode) {
+ String url = append(baseUrl, params);
+ return get(encode ? encode(url) : url);
+ }
+
+ /**
+ * Start a 'GET' request to the given URL along with the query params
+ *
+ * @param baseUrl
+ * @param encode
+ * true to encode the full URL
+ * @param params
+ * the name/value query parameter pairs to include as part of the
+ * baseUrl
+ *
+ * @see #append(CharSequence, String...)
+ * @see #encode(CharSequence)
+ *
+ * @return request
+ */
+ public static HttpRequest get(final CharSequence baseUrl,
+ final boolean encode, final Object... params) {
+ String url = append(baseUrl, params);
+ return get(encode ? encode(url) : url);
+ }
+
+ /**
+ * Start a 'POST' request to the given URL
+ *
+ * @param url
+ * @return request
+ * @throws HttpRequestException
+ */
+ public static HttpRequest post(final CharSequence url)
+ throws HttpRequestException {
+ return new HttpRequest(url, METHOD_POST);
+ }
+
+ /**
+ * Start a 'POST' request to the given URL
+ *
+ * @param url
+ * @return request
+ * @throws HttpRequestException
+ */
+ public static HttpRequest post(final URL url) throws HttpRequestException {
+ return new HttpRequest(url, METHOD_POST);
+ }
+
+ /**
+ * Start a 'POST' request to the given URL along with the query params
+ *
+ * @param baseUrl
+ * @param params
+ * the query parameters to include as part of the baseUrl
+ * @param encode
+ * true to encode the full URL
+ *
+ * @see #append(CharSequence, Map)
+ * @see #encode(CharSequence)
+ *
+ * @return request
+ */
+ public static HttpRequest post(final CharSequence baseUrl,
+ final Map, ?> params, final boolean encode) {
+ String url = append(baseUrl, params);
+ return post(encode ? encode(url) : url);
+ }
+
+ /**
+ * Start a 'POST' request to the given URL along with the query params
+ *
+ * @param baseUrl
+ * @param encode
+ * true to encode the full URL
+ * @param params
+ * the name/value query parameter pairs to include as part of the
+ * baseUrl
+ *
+ * @see #append(CharSequence, String...)
+ * @see #encode(CharSequence)
+ *
+ * @return request
+ */
+ public static HttpRequest post(final CharSequence baseUrl,
+ final boolean encode, final Object... params) {
+ String url = append(baseUrl, params);
+ return post(encode ? encode(url) : url);
+ }
+
+ /**
+ * Start a 'PUT' request to the given URL
+ *
+ * @param url
+ * @return request
+ * @throws HttpRequestException
+ */
+ public static HttpRequest put(final CharSequence url)
+ throws HttpRequestException {
+ return new HttpRequest(url, METHOD_PUT);
+ }
+
+ /**
+ * Start a 'PUT' request to the given URL
+ *
+ * @param url
+ * @return request
+ * @throws HttpRequestException
+ */
+ public static HttpRequest put(final URL url) throws HttpRequestException {
+ return new HttpRequest(url, METHOD_PUT);
+ }
+
+ /**
+ * Start a 'PUT' request to the given URL along with the query params
+ *
+ * @param baseUrl
+ * @param params
+ * the query parameters to include as part of the baseUrl
+ * @param encode
+ * true to encode the full URL
+ *
+ * @see #append(CharSequence, Map)
+ * @see #encode(CharSequence)
+ *
+ * @return request
+ */
+ public static HttpRequest put(final CharSequence baseUrl,
+ final Map, ?> params, final boolean encode) {
+ String url = append(baseUrl, params);
+ return put(encode ? encode(url) : url);
+ }
+
+ /**
+ * Start a 'PUT' request to the given URL along with the query params
+ *
+ * @param baseUrl
+ * @param encode
+ * true to encode the full URL
+ * @param params
+ * the name/value query parameter pairs to include as part of the
+ * baseUrl
+ *
+ * @see #append(CharSequence, String...)
+ * @see #encode(CharSequence)
+ *
+ * @return request
+ */
+ public static HttpRequest put(final CharSequence baseUrl,
+ final boolean encode, final Object... params) {
+ String url = append(baseUrl, params);
+ return put(encode ? encode(url) : url);
+ }
+
+ /**
+ * Start a 'DELETE' request to the given URL
+ *
+ * @param url
+ * @return request
+ * @throws HttpRequestException
+ */
+ public static HttpRequest delete(final CharSequence url)
+ throws HttpRequestException {
+ return new HttpRequest(url, METHOD_DELETE);
+ }
+
+ /**
+ * Start a 'DELETE' request to the given URL
+ *
+ * @param url
+ * @return request
+ * @throws HttpRequestException
+ */
+ public static HttpRequest delete(final URL url) throws HttpRequestException {
+ return new HttpRequest(url, METHOD_DELETE);
+ }
+
+ /**
+ * Start a 'DELETE' request to the given URL along with the query params
+ *
+ * @param baseUrl
+ * @param params
+ * The query parameters to include as part of the baseUrl
+ * @param encode
+ * true to encode the full URL
+ *
+ * @see #append(CharSequence, Map)
+ * @see #encode(CharSequence)
+ *
+ * @return request
+ */
+ public static HttpRequest delete(final CharSequence baseUrl,
+ final Map, ?> params, final boolean encode) {
+ String url = append(baseUrl, params);
+ return delete(encode ? encode(url) : url);
+ }
+
+ /**
+ * Start a 'DELETE' request to the given URL along with the query params
+ *
+ * @param baseUrl
+ * @param encode
+ * true to encode the full URL
+ * @param params
+ * the name/value query parameter pairs to include as part of the
+ * baseUrl
+ *
+ * @see #append(CharSequence, String...)
+ * @see #encode(CharSequence)
+ *
+ * @return request
+ */
+ public static HttpRequest delete(final CharSequence baseUrl,
+ final boolean encode, final Object... params) {
+ String url = append(baseUrl, params);
+ return delete(encode ? encode(url) : url);
+ }
+
+ /**
+ * Start a 'HEAD' request to the given URL
+ *
+ * @param url
+ * @return request
+ * @throws HttpRequestException
+ */
+ public static HttpRequest head(final CharSequence url)
+ throws HttpRequestException {
+ return new HttpRequest(url, METHOD_HEAD);
+ }
+
+ /**
+ * Start a 'HEAD' request to the given URL
+ *
+ * @param url
+ * @return request
+ * @throws HttpRequestException
+ */
+ public static HttpRequest head(final URL url) throws HttpRequestException {
+ return new HttpRequest(url, METHOD_HEAD);
+ }
+
+ /**
+ * Start a 'HEAD' request to the given URL along with the query params
+ *
+ * @param baseUrl
+ * @param params
+ * The query parameters to include as part of the baseUrl
+ * @param encode
+ * true to encode the full URL
+ *
+ * @see #append(CharSequence, Map)
+ * @see #encode(CharSequence)
+ *
+ * @return request
+ */
+ public static HttpRequest head(final CharSequence baseUrl,
+ final Map, ?> params, final boolean encode) {
+ String url = append(baseUrl, params);
+ return head(encode ? encode(url) : url);
+ }
+
+ /**
+ * Start a 'GET' request to the given URL along with the query params
+ *
+ * @param baseUrl
+ * @param encode
+ * true to encode the full URL
+ * @param params
+ * the name/value query parameter pairs to include as part of the
+ * baseUrl
+ *
+ * @see #append(CharSequence, String...)
+ * @see #encode(CharSequence)
+ *
+ * @return request
+ */
+ public static HttpRequest head(final CharSequence baseUrl,
+ final boolean encode, final Object... params) {
+ String url = append(baseUrl, params);
+ return head(encode ? encode(url) : url);
+ }
+
+ /**
+ * Start an 'OPTIONS' request to the given URL
+ *
+ * @param url
+ * @return request
+ * @throws HttpRequestException
+ */
+ public static HttpRequest options(final CharSequence url)
+ throws HttpRequestException {
+ return new HttpRequest(url, METHOD_OPTIONS);
+ }
+
+ /**
+ * Start an 'OPTIONS' request to the given URL
+ *
+ * @param url
+ * @return request
+ * @throws HttpRequestException
+ */
+ public static HttpRequest options(final URL url) throws HttpRequestException {
+ return new HttpRequest(url, METHOD_OPTIONS);
+ }
+
+ /**
+ * Start a 'TRACE' request to the given URL
+ *
+ * @param url
+ * @return request
+ * @throws HttpRequestException
+ */
+ public static HttpRequest trace(final CharSequence url)
+ throws HttpRequestException {
+ return new HttpRequest(url, METHOD_TRACE);
+ }
+
+ /**
+ * Start a 'TRACE' request to the given URL
+ *
+ * @param url
+ * @return request
+ * @throws HttpRequestException
+ */
+ public static HttpRequest trace(final URL url) throws HttpRequestException {
+ return new HttpRequest(url, METHOD_TRACE);
+ }
+
+ /**
+ * Set the 'http.keepAlive' property to the given value.
+ *
+ * This setting will apply to all requests.
+ *
+ * @param keepAlive
+ */
+ public static void keepAlive(final boolean keepAlive) {
+ setProperty("http.keepAlive", Boolean.toString(keepAlive));
+ }
+
+ /**
+ * Set the 'http.maxConnections' property to the given value.
+ *
+ * This setting will apply to all requests.
+ *
+ * @param maxConnections
+ */
+ public static void maxConnections(final int maxConnections) {
+ setProperty("http.maxConnections", Integer.toString(maxConnections));
+ }
+
+ /**
+ * Set the 'http.proxyHost' & 'https.proxyHost' properties to the given host
+ * value.
+ *
+ * This setting will apply to all requests.
+ *
+ * @param host
+ */
+ public static void proxyHost(final String host) {
+ setProperty("http.proxyHost", host);
+ setProperty("https.proxyHost", host);
+ }
+
+ /**
+ * Set the 'http.proxyPort' & 'https.proxyPort' properties to the given port
+ * number.
+ *
+ * This setting will apply to all requests.
+ *
+ * @param port
+ */
+ public static void proxyPort(final int port) {
+ final String portValue = Integer.toString(port);
+ setProperty("http.proxyPort", portValue);
+ setProperty("https.proxyPort", portValue);
+ }
+
+ /**
+ * Set the 'http.nonProxyHosts' property to the given host values.
+ *
+ * Hosts will be separated by a '|' character.
+ *
+ * This setting will apply to all requests.
+ *
+ * @param hosts
+ */
+ public static void nonProxyHosts(final String... hosts) {
+ if (hosts != null && hosts.length > 0) {
+ StringBuilder separated = new StringBuilder();
+ int last = hosts.length - 1;
+ for (int i = 0; i < last; i++)
+ separated.append(hosts[i]).append('|');
+ separated.append(hosts[last]);
+ setProperty("http.nonProxyHosts", separated.toString());
+ } else
+ setProperty("http.nonProxyHosts", null);
+ }
+
+ /**
+ * Set property to given value.
+ *
+ * Specifying a null value will cause the property to be cleared
+ *
+ * @param name
+ * @param value
+ * @return previous value
+ */
+ private static String setProperty(final String name, final String value) {
+ final PrivilegedAction
+ * The default value of this setting is
+ * This size is also used for send and receive buffers created for both char
+ * and byte arrays
+ *
+ * The default buffer size is 8,192 bytes
+ *
+ * @param size
+ * @return this request
+ */
+ public HttpRequest bufferSize(final int size) {
+ if (size < 1)
+ throw new IllegalArgumentException("Size must be greater than zero");
+ bufferSize = size;
+ return this;
+ }
+
+ /**
+ * Get the configured buffer size
+ *
+ * The default buffer size is 8,192 bytes
+ *
+ * @return buffer size
+ */
+ public int bufferSize() {
+ return bufferSize;
+ }
+
+ /**
+ * Set whether or not the response body should be automatically uncompressed
+ * when read from.
+ *
+ * This will only affect requests that have the 'Content-Encoding' response
+ * header set to 'gzip'.
+ *
+ * This causes all receive methods to use a {@link GZIPInputStream} when
+ * applicable so that higher level streams and readers can read the data
+ * uncompressed.
+ *
+ * Setting this option does not cause any request headers to be set
+ * automatically so {@link #acceptGzipEncoding()} should be used in
+ * conjunction with this setting to tell the server to gzip the response.
+ *
+ * @param uncompress
+ * @return this request
+ */
+ public HttpRequest uncompress(final boolean uncompress) {
+ this.uncompress = uncompress;
+ return this;
+ }
+
+ /**
+ * Create byte array output stream
+ *
+ * @return stream
+ */
+ protected ByteArrayOutputStream byteStream() {
+ final int size = contentLength();
+ if (size > 0)
+ return new ByteArrayOutputStream(size);
+ else
+ return new ByteArrayOutputStream();
+ }
+
+ /**
+ * Get response as {@link String} in given character set
+ *
+ * This will fall back to using the UTF-8 character set if the given charset
+ * is null
+ *
+ * @param charset
+ * @return string
+ * @throws HttpRequestException
+ */
+ public String body(final String charset) throws HttpRequestException {
+ final ByteArrayOutputStream output = byteStream();
+ try {
+ copy(buffer(), output);
+ return output.toString(getValidCharset(charset));
+ } catch (IOException e) {
+ throw new HttpRequestException(e);
+ }
+ }
+
+ /**
+ * Get response as {@link String} using character set returned from
+ * {@link #charset()}
+ *
+ * @return string
+ * @throws HttpRequestException
+ */
+ public String body() throws HttpRequestException {
+ return body(charset());
+ }
+
+ /**
+ * Get the response body as a {@link String} and set it as the value of the
+ * given reference.
+ *
+ * @param output
+ * @return this request
+ * @throws HttpRequestException
+ */
+ public HttpRequest body(final AtomicReference
+ * This will fall back to using the UTF-8 character set if the given charset
+ * is null
+ *
+ * @param charset
+ * @return reader
+ * @throws HttpRequestException
+ */
+ public InputStreamReader reader(final String charset)
+ throws HttpRequestException {
+ try {
+ return new InputStreamReader(stream(), getValidCharset(charset));
+ } catch (UnsupportedEncodingException e) {
+ throw new HttpRequestException(e);
+ }
+ }
+
+ /**
+ * Get reader to response body using the character set returned from
+ * {@link #charset()}
+ *
+ * @return reader
+ * @throws HttpRequestException
+ */
+ public InputStreamReader reader() throws HttpRequestException {
+ return reader(charset());
+ }
+
+ /**
+ * Get buffered reader to response body using the given character set r and
+ * the configured buffer size
+ *
+ *
+ * @see #bufferSize(int)
+ * @param charset
+ * @return reader
+ * @throws HttpRequestException
+ */
+ public BufferedReader bufferedReader(final String charset)
+ throws HttpRequestException {
+ return new BufferedReader(reader(charset), bufferSize);
+ }
+
+ /**
+ * Get buffered reader to response body using the character set returned from
+ * {@link #charset()} and the configured buffer size
+ *
+ * @see #bufferSize(int)
+ * @return reader
+ * @throws HttpRequestException
+ */
+ public BufferedReader bufferedReader() throws HttpRequestException {
+ return bufferedReader(charset());
+ }
+
+ /**
+ * Stream response body to file
+ *
+ * @param file
+ * @return this request
+ * @throws HttpRequestException
+ */
+ public HttpRequest receive(final File file) throws HttpRequestException {
+ final OutputStream output;
+ try {
+ output = new BufferedOutputStream(new FileOutputStream(file), bufferSize);
+ } catch (FileNotFoundException e) {
+ throw new HttpRequestException(e);
+ }
+ return new CloseOperation
+ * This will be all key=value pairs after the first ';' that are separated by
+ * a ';'
+ *
+ * @param headerName
+ * @return non-null but possibly empty map of parameter headers
+ */
+ public Map
+ * The given stream will be closed once sending completes
+ *
+ * @param input
+ * @return this request
+ * @throws HttpRequestException
+ */
+ public HttpRequest send(final InputStream input) throws HttpRequestException {
+ try {
+ openOutput();
+ copy(input, output);
+ } catch (IOException e) {
+ throw new HttpRequestException(e);
+ }
+ return this;
+ }
+
+ /**
+ * Write reader to request body
+ *
+ * The given reader will be closed once sending completes
+ *
+ * @param input
+ * @return this request
+ * @throws HttpRequestException
+ */
+ public HttpRequest send(final Reader input) throws HttpRequestException {
+ try {
+ openOutput();
+ } catch (IOException e) {
+ throw new HttpRequestException(e);
+ }
+ final Writer writer = new OutputStreamWriter(output,
+ output.encoder.charset());
+ return new FlushOperation
+ * The charset configured via {@link #contentType(String)} will be used and
+ * UTF-8 will be used if it is unset.
+ *
+ * @param value
+ * @return this request
+ * @throws HttpRequestException
+ */
+ public HttpRequest send(final CharSequence value) throws HttpRequestException {
+ try {
+ openOutput();
+ output.write(value.toString());
+ } catch (IOException e) {
+ throw new HttpRequestException(e);
+ }
+ return this;
+ }
+
+ /**
+ * Create writer to request output stream
+ *
+ * @return writer
+ * @throws HttpRequestException
+ */
+ public OutputStreamWriter writer() throws HttpRequestException {
+ try {
+ openOutput();
+ return new OutputStreamWriter(output, output.encoder.charset());
+ } catch (IOException e) {
+ throw new HttpRequestException(e);
+ }
+ }
+
+ /**
+ * Write the values in the map as form data to the request body
+ *
+ * The pairs specified will be URL-encoded in UTF-8 and sent with the
+ * 'application/x-www-form-urlencoded' content-type
+ *
+ * @param values
+ * @return this request
+ * @throws HttpRequestException
+ */
+ public HttpRequest form(final Map, ?> values) throws HttpRequestException {
+ return form(values, CHARSET_UTF8);
+ }
+
+ /**
+ * Write the key and value in the entry as form data to the request body
+ *
+ * The pair specified will be URL-encoded in UTF-8 and sent with the
+ * 'application/x-www-form-urlencoded' content-type
+ *
+ * @param entry
+ * @return this request
+ * @throws HttpRequestException
+ */
+ public HttpRequest form(final Entry, ?> entry) throws HttpRequestException {
+ return form(entry, CHARSET_UTF8);
+ }
+
+ /**
+ * Write the key and value in the entry as form data to the request body
+ *
+ * The pair specified will be URL-encoded and sent with the
+ * 'application/x-www-form-urlencoded' content-type
+ *
+ * @param entry
+ * @param charset
+ * @return this request
+ * @throws HttpRequestException
+ */
+ public HttpRequest form(final Entry, ?> entry, final String charset)
+ throws HttpRequestException {
+ return form(entry.getKey(), entry.getValue(), charset);
+ }
+
+ /**
+ * Write the name/value pair as form data to the request body
+ *
+ * The pair specified will be URL-encoded in UTF-8 and sent with the
+ * 'application/x-www-form-urlencoded' content-type
+ *
+ * @param name
+ * @param value
+ * @return this request
+ * @throws HttpRequestException
+ */
+ public HttpRequest form(final Object name, final Object value)
+ throws HttpRequestException {
+ return form(name, value, CHARSET_UTF8);
+ }
+
+ /**
+ * Write the name/value pair as form data to the request body
+ *
+ * The values specified will be URL-encoded and sent with the
+ * 'application/x-www-form-urlencoded' content-type
+ *
+ * @param name
+ * @param value
+ * @param charset
+ * @return this request
+ * @throws HttpRequestException
+ */
+ public HttpRequest form(final Object name, final Object value, String charset)
+ throws HttpRequestException {
+ final boolean first = !form;
+ if (first) {
+ contentType(CONTENT_TYPE_FORM, charset);
+ form = true;
+ }
+ charset = getValidCharset(charset);
+ try {
+ openOutput();
+ if (!first)
+ output.write('&');
+ output.write(URLEncoder.encode(name.toString(), charset));
+ output.write('=');
+ if (value != null)
+ output.write(URLEncoder.encode(value.toString(), charset));
+ } catch (IOException e) {
+ throw new HttpRequestException(e);
+ }
+ return this;
+ }
+
+ /**
+ * Write the values in the map as encoded form data to the request body
+ *
+ * @param values
+ * @param charset
+ * @return this request
+ * @throws HttpRequestException
+ */
+ public HttpRequest form(final Map, ?> values, final String charset)
+ throws HttpRequestException {
+ if (!values.isEmpty())
+ for (Entry, ?> entry : values.entrySet())
+ form(entry, charset);
+ return this;
+ }
+
+ /**
+ * Configure HTTPS connection to trust only certain certificates
+ *
+ * This method throws an exception if the current request is not a HTTPS request
+ *
+ * @return this request
+ * @throws HttpRequestException
+ */
+ public HttpRequest pinToCerts() throws HttpRequestException {
+ final HttpURLConnection connection = getConnection();
+ if (connection instanceof HttpsURLConnection) {
+ ((HttpsURLConnection) connection).setSSLSocketFactory(getPinnedFactory());
+ } else {
+ IOException e = new IOException("You must use a https url to use ssl pinning");
+ throw new HttpRequestException(e);
+ }
+ return this;
+ }
+
+ /**
+ * Configure HTTPS connection to trust all certificates
+ *
+ * This method does nothing if the current request is not a HTTPS request
+ *
+ * @return this request
+ * @throws HttpRequestException
+ */
+ public HttpRequest trustAllCerts() throws HttpRequestException {
+ final HttpURLConnection connection = getConnection();
+ if (connection instanceof HttpsURLConnection)
+ ((HttpsURLConnection) connection)
+ .setSSLSocketFactory(getTrustedFactory());
+ return this;
+ }
+
+ /**
+ * Configure HTTPS connection to trust all hosts using a custom
+ * {@link HostnameVerifier} that always returns
+ * This method does nothing if the current request is not a HTTPS request
+ *
+ * @return this request
+ */
+ public HttpRequest trustAllHosts() {
+ final HttpURLConnection connection = getConnection();
+ if (connection instanceof HttpsURLConnection)
+ ((HttpsURLConnection) connection)
+ .setHostnameVerifier(getTrustedVerifier());
+ return this;
+ }
+
+ /**
+ * Get the {@link URL} of this request's connection
+ *
+ * @return request URL
+ */
+ public URL url() {
+ return getConnection().getURL();
+ }
+
+ /**
+ * Get the HTTP method of this request
+ *
+ * @return method
+ */
+ public String method() {
+ return getConnection().getRequestMethod();
+ }
+
+ /**
+ * Configure an HTTP proxy on this connection. Use {{@link #proxyBasic(String, String)} if
+ * this proxy requires basic authentication.
+ *
+ * @param proxyHost
+ * @param proxyPort
+ * @return this request
+ */
+ public HttpRequest useProxy(final String proxyHost, final int proxyPort) {
+ if (connection != null)
+ throw new IllegalStateException("The connection has already been created. This method must be called before reading or writing to the request.");
+
+ this.httpProxyHost = proxyHost;
+ this.httpProxyPort = proxyPort;
+ return this;
+ }
+
+ /**
+ * Set whether or not the underlying connection should follow redirects in
+ * the response.
+ *
+ * @param followRedirects - true fo follow redirects, false to not.
+ * @return this request
+ */
+ public HttpRequest followRedirects(final boolean followRedirects) {
+ getConnection().setInstanceFollowRedirects(followRedirects);
+ return this;
+ }
+}
diff --git a/src/android/com/synconset/CordovaHTTP/VeryTrustingHostnameVerifier.java b/src/android/com/synconset/CordovaHTTP/VeryTrustingHostnameVerifier.java
deleted file mode 100644
index 9368ea2..0000000
--- a/src/android/com/synconset/CordovaHTTP/VeryTrustingHostnameVerifier.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.synconset;
-
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.SSLSession;
-
-public class VeryTrustingHostnameVerifier implements HostnameVerifier {
- @Override
- public boolean verify(String hostname, SSLSession session) {
- return true;
- }
-}
\ No newline at end of file
diff --git a/src/android/com/synconset/CordovaHTTP/VeryTrustingTrustManager.java b/src/android/com/synconset/CordovaHTTP/VeryTrustingTrustManager.java
deleted file mode 100644
index f711946..0000000
--- a/src/android/com/synconset/CordovaHTTP/VeryTrustingTrustManager.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.synconset;
-
-import java.security.cert.X509Certificate;
-import java.security.cert.CertificateException;
-import javax.net.ssl.X509TrustManager;
-
-public class VeryTrustingTrustManager implements X509TrustManager {
- @Override
- public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException, IllegalArgumentException { }
-
- @Override
- public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException, IllegalArgumentException { }
-
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return new X509Certificate[0];
- }
-}
\ No newline at end of file
diff --git a/www/cordovaHTTP.js b/www/cordovaHTTP.js
index 50faf37..634b2e9 100644
--- a/www/cordovaHTTP.js
+++ b/www/cordovaHTTP.js
@@ -7,40 +7,28 @@
var exec = require('cordova/exec');
var http = {
- setAuthorizationHeaderWithUsernameAndPassword: function(username, password, success, failure) {
- return exec(success, failure, "CordovaHttpPlugin", "setAuthorizationHeaderWithUsernameAndPassword", [username, password]);
+ useBasicAuth: function(username, password, success, failure) {
+ return exec(success, failure, "CordovaHttpPlugin", "useBasicAuth", [username, password]);
},
setHeader: function(header, value, success, failure) {
return exec(success, failure, "CordovaHttpPlugin", "setHeader", [header, value]);
},
- enableSSLPinning: function(success, failure) {
- return exec(success, failure, "CordovaHttpPlugin", "enableSSLPinning", []);
+ enableSSLPinning: function(enable, success, failure) {
+ return exec(success, failure, "CordovaHttpPlugin", "enableSSLPinning", [enable]);
},
- validateEntireCertificateChain: function(validateChain, success, failure) {
- return exec(success, failure, "CordovaHttpPlugin", "validateEntireCertificateChain", [validateChain]);
- },
- allowInvalidCertificates: function(allow, success, failure) {
- return exec(success, failure, "CordovaHttpPlugin", "allowInvalidCertificates", [allow]);
- },
- acceptText: function(success, failure) {
- return exec(success, failure, "CordovaHttpPlugin", "acceptText", []);
- },
- acceptData: function(success, failure) {
- return exec(success, failure, "CordovaHttpPlugin", "acceptData", []);
- },
- setAcceptableContentTypes: function(contentTypes, success, failure) {
- return exec(success, failure, "CordovaHttpPlugin", "setAcceptableContentTypes", contentTypes);
+ acceptAllCerts: function(allow, success, failure) {
+ return exec(success, failure, "CordovaHttpPlugin", "acceptAllCerts", [allow]);
},
post: function(url, params, headers, success, failure) {
return exec(success, failure, "CordovaHttpPlugin", "post", [url, params, headers]);
},
- get: function(url, params, success, failure) {
- return exec(success, failure, "CordovaHttpPlugin", "get", [url, params]);
+ get: function(url, params, headers, success, failure) {
+ return exec(success, failure, "CordovaHttpPlugin", "get", [url, params, headers]);
},
- uploadFile: function(url, params, filePath, name, success, failure) {
- return exec(success, failure, "CordovaHttpPlugin", "uploadFile", [url, params, filePath, name]);
+ uploadFile: function(url, params, headers, filePath, name, success, failure) {
+ return exec(success, failure, "CordovaHttpPlugin", "uploadFile", [url, params, headers, filePath, name]);
},
- downloadFile: function(url, params, filePath, success, failure) {
+ downloadFile: function(url, params, headers, filePath, success, failure) {
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -71,7 +59,7 @@ var http = {
entry.fullPath = result.file.fullPath;
success(entry);
};
- return exec(win, failure, "CordovaHttpPlugin", "downloadFile", [url, params, filePath]);
+ return exec(win, failure, "CordovaHttpPlugin", "downloadFile", [url, params, headers, filePath]);
}
};
@@ -109,41 +97,29 @@ if (angular) {
}
var cordovaHTTP = {
- setAuthorizationHeaderWithUsernameAndPassword: function(username, password) {
- return makePromise(http.setAuthorizationHeaderWithUsernameAndPassword, [username, password]);
+ useBasicAuth: function(username, password) {
+ return makePromise(http.useBasicAuth, [username, password]);
},
setHeader: function(header, value) {
return makePromise(http.setHeader, [header, value]);
},
- enableSSLPinning: function() {
- return makePromise(http.enableSSLPinning, []);
+ enableSSLPinning: function(enable) {
+ return makePromise(http.enableSSLPinning, [enable]);
},
- validateEntireCertificateChain: function(validateChain) {
- return makePromise(http.validateEntireCertificateChain, [validateChain]);
- },
- allowInvalidCertificates: function(allow) {
- return makePromise(http.allowInvalidCertificates, [allow]);
- },
- acceptText: function() {
- return makePromise(http.acceptText, []);
- },
- acceptData: function() {
- return makePromise(http.acceptData, []);
- },
- setAcceptableContentTypes: function(contentTypes) {
- return makePromise(http.setAcceptableContentTypes, [contentTypes]);
+ acceptAllCerts: function(allow) {
+ return makePromise(http.acceptAllCerts, [allow]);
},
post: function(url, params, headers) {
return makePromise(http.post, [url, params, headers], true);
},
- get: function(url, params) {
- return makePromise(http.get, [url, params], true);
+ get: function(url, params, headers) {
+ return makePromise(http.get, [url, params, headers], true);
},
- uploadFile: function(url, params, filePath, name) {
- return makePromise(http.uploadFile, [url, params, filePath, name], true);
+ uploadFile: function(url, params, headers, filePath, name) {
+ return makePromise(http.uploadFile, [url, params, headers, filePath, name], true);
},
- downloadFile: function(url, params, filePath) {
- return makePromise(http.downloadFile, [url, params, filePath], true);
+ downloadFile: function(url, params, headers, filePath) {
+ return makePromise(http.downloadFile, [url, params, headers, filePath], true);
}
};
return cordovaHTTP;
true
+ *
+ * @param ignore
+ * @return this request
+ */
+ public HttpRequest ignoreCloseExceptions(final boolean ignore) {
+ ignoreCloseExceptions = ignore;
+ return this;
+ }
+
+ /**
+ * Get whether or not exceptions thrown by {@link Closeable#close()} are
+ * ignored
+ *
+ * @return true if ignoring, false if throwing
+ */
+ public boolean ignoreCloseExceptions() {
+ return ignoreCloseExceptions;
+ }
+
+ /**
+ * Get the status code of the response
+ *
+ * @return the response code
+ * @throws HttpRequestException
+ */
+ public int code() throws HttpRequestException {
+ try {
+ closeOutput();
+ return getConnection().getResponseCode();
+ } catch (IOException e) {
+ throw new HttpRequestException(e);
+ }
+ }
+
+ /**
+ * Set the value of the given {@link AtomicInteger} to the status code of the
+ * response
+ *
+ * @param output
+ * @return this request
+ * @throws HttpRequestException
+ */
+ public HttpRequest code(final AtomicInteger output)
+ throws HttpRequestException {
+ output.set(code());
+ return this;
+ }
+
+ /**
+ * Is the response code a 200 OK?
+ *
+ * @return true if 200, false otherwise
+ * @throws HttpRequestException
+ */
+ public boolean ok() throws HttpRequestException {
+ return HTTP_OK == code();
+ }
+
+ /**
+ * Is the response code a 201 Created?
+ *
+ * @return true if 201, false otherwise
+ * @throws HttpRequestException
+ */
+ public boolean created() throws HttpRequestException {
+ return HTTP_CREATED == code();
+ }
+
+ /**
+ * Is the response code a 204 No Content?
+ *
+ * @return true if 204, false otherwise
+ * @throws HttpRequestException
+ */
+ public boolean noContent() throws HttpRequestException {
+ return HTTP_NO_CONTENT == code();
+ }
+
+ /**
+ * Is the response code a 500 Internal Server Error?
+ *
+ * @return true if 500, false otherwise
+ * @throws HttpRequestException
+ */
+ public boolean serverError() throws HttpRequestException {
+ return HTTP_INTERNAL_ERROR == code();
+ }
+
+ /**
+ * Is the response code a 400 Bad Request?
+ *
+ * @return true if 400, false otherwise
+ * @throws HttpRequestException
+ */
+ public boolean badRequest() throws HttpRequestException {
+ return HTTP_BAD_REQUEST == code();
+ }
+
+ /**
+ * Is the response code a 404 Not Found?
+ *
+ * @return true if 404, false otherwise
+ * @throws HttpRequestException
+ */
+ public boolean notFound() throws HttpRequestException {
+ return HTTP_NOT_FOUND == code();
+ }
+
+ /**
+ * Is the response code a 304 Not Modified?
+ *
+ * @return true if 304, false otherwise
+ * @throws HttpRequestException
+ */
+ public boolean notModified() throws HttpRequestException {
+ return HTTP_NOT_MODIFIED == code();
+ }
+
+ /**
+ * Get status message of the response
+ *
+ * @return message
+ * @throws HttpRequestException
+ */
+ public String message() throws HttpRequestException {
+ try {
+ closeOutput();
+ return getConnection().getResponseMessage();
+ } catch (IOException e) {
+ throw new HttpRequestException(e);
+ }
+ }
+
+ /**
+ * Disconnect the connection
+ *
+ * @return this request
+ */
+ public HttpRequest disconnect() {
+ getConnection().disconnect();
+ return this;
+ }
+
+ /**
+ * Set chunked streaming mode to the given size
+ *
+ * @param size
+ * @return this request
+ */
+ public HttpRequest chunk(final int size) {
+ getConnection().setChunkedStreamingMode(size);
+ return this;
+ }
+
+ /**
+ * Set the size used when buffering and copying between streams
+ * true for each
+ * host verified
+ *