Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 2014 Kevin Sawicki
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
package com.github.kevinsawicki.http;
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
import static java.net.HttpURLConnection.HTTP_CREATED;
import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
import static java.net.HttpURLConnection.HTTP_NOT_MODIFIED;
import static java.net.HttpURLConnection.HTTP_OK;
import static java.net.Proxy.Type.HTTP;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.Flushable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.security.AccessController;
import java.security.GeneralSecurityException;
import java.security.PrivilegedAction;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.GZIPInputStream;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
/**
* A fluid interface for making HTTP requests using an underlying
* {@link HttpURLConnection} (or sub-class).
*
* 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 TRUSTED_FACTORY;
private static HostnameVerifier TRUSTED_VERIFIER;
private static String getValidCharset(final String charset) {
if (charset != null && charset.length() > 0)
return charset;
else
return CHARSET_UTF8;
}
private static SSLSocketFactory getTrustedFactory()
throws HttpRequestException {
if (TRUSTED_FACTORY == null) {
final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(X509Certificate[] chain, String authType) {
// Intentionally left blank
}
public void checkServerTrusted(X509Certificate[] chain, String authType) {
// Intentionally left blank
}
} };
try {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, trustAllCerts, new SecureRandom());
TRUSTED_FACTORY = context.getSocketFactory();
} catch (GeneralSecurityException e) {
IOException ioException = new IOException(
"Security exception configuring SSL context");
ioException.initCause(e);
throw new HttpRequestException(ioException);
}
}
return TRUSTED_FACTORY;
}
private static HostnameVerifier getTrustedVerifier() {
if (TRUSTED_VERIFIER == null)
TRUSTED_VERIFIER = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
return TRUSTED_VERIFIER;
}
private static StringBuilder addPathSeparator(final String baseUrl,
final StringBuilder result) {
// Add trailing slash if the base URL doesn't have any path segments.
//
// The following test is checking for the last slash not being part of
// the protocol to host separator: '://'.
if (baseUrl.indexOf(':') + 2 == baseUrl.lastIndexOf('/'))
result.append('/');
return result;
}
private static StringBuilder addParamPrefix(final String baseUrl,
final StringBuilder result) {
// Add '?' if missing and add '&' if params already exist in base url
final int queryStart = baseUrl.indexOf('?');
final int lastChar = result.length() - 1;
if (queryStart == -1)
result.append('?');
else if (queryStart < lastChar && baseUrl.charAt(lastChar) != '&')
result.append('&');
return result;
}
private static StringBuilder addParam(final Object key, Object value,
final StringBuilder result) {
if (value != null && value.getClass().isArray())
value = arrayToList(value);
if (value instanceof Iterable>) {
Iterator> iterator = ((Iterable>) value).iterator();
while (iterator.hasNext()) {
result.append(key);
result.append("[]=");
Object element = iterator.next();
if (element != null)
result.append(element);
if (iterator.hasNext())
result.append("&");
}
} else {
result.append(key);
result.append("=");
if (value != null)
result.append(value);
}
return result;
}
/**
* Creates {@link HttpURLConnection HTTP connections} for
* {@link URL urls}.
*/
public interface ConnectionFactory {
/**
* Open an {@link HttpURLConnection} for the specified {@link URL}.
*
* @throws IOException
*/
HttpURLConnection create(URL url) throws IOException;
/**
* Open an {@link HttpURLConnection} for the specified {@link URL}
* and {@link Proxy}.
*
* @throws IOException
*/
HttpURLConnection create(URL url, Proxy proxy) throws IOException;
/**
* A {@link ConnectionFactory} which uses the built-in
* {@link URL#openConnection()}
*/
ConnectionFactory DEFAULT = new ConnectionFactory() {
public HttpURLConnection create(URL url) throws IOException {
return (HttpURLConnection) url.openConnection();
}
public HttpURLConnection create(URL url, Proxy proxy) throws IOException {
return (HttpURLConnection) url.openConnection(proxy);
}
};
}
private static ConnectionFactory CONNECTION_FACTORY = ConnectionFactory.DEFAULT;
/**
* Specify the {@link ConnectionFactory} used to create new requests.
*/
public static void setConnectionFactory(final ConnectionFactory connectionFactory) {
if (connectionFactory == null)
CONNECTION_FACTORY = ConnectionFactory.DEFAULT;
else
CONNECTION_FACTORY = connectionFactory;
}
/**
* Callback interface for reporting upload progress for a request.
*/
public interface UploadProgress {
/**
* Callback invoked as data is uploaded by the request.
*
* @param uploaded The number of bytes already uploaded
* @param total The total number of bytes that will be uploaded or -1 if
* the length is unknown.
*/
void onUpload(long uploaded, long total);
UploadProgress DEFAULT = new UploadProgress() {
public void onUpload(long uploaded, long total) {
}
};
}
/**
*
* 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.
*
*
* @param source
* the array to convert
* @param srcOffset
* the index where conversion begins
* @param numSigBytes
* the number of significant bytes in your array
* @param destination
* the array to hold the conversion
* @param destOffset
* the index where output will be put
* @return the destination array
* @since 1.3
*/
private static byte[] encode3to4(byte[] source, int srcOffset,
int numSigBytes, byte[] destination, int destOffset) {
byte[] ALPHABET = _STANDARD_ALPHABET;
int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
| (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
| (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
switch (numSigBytes) {
case 3:
destination[destOffset] = ALPHABET[(inBuff >>> 18)];
destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
return destination;
case 2:
destination[destOffset] = ALPHABET[(inBuff >>> 18)];
destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
destination[destOffset + 3] = EQUALS_SIGN;
return destination;
case 1:
destination[destOffset] = ALPHABET[(inBuff >>> 18)];
destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = EQUALS_SIGN;
destination[destOffset + 3] = EQUALS_SIGN;
return destination;
default:
return destination;
}
}
/**
* Encode string as a byte array in Base64 annotation.
*
* @param string
* @return The Base64-encoded data as a string
*/
public static String encode(String string) {
byte[] bytes;
try {
bytes = string.getBytes(PREFERRED_ENCODING);
} catch (UnsupportedEncodingException e) {
bytes = string.getBytes();
}
return encodeBytes(bytes);
}
/**
* Encodes a byte array into Base64 notation.
*
* @param source
* The data to convert
* @return The Base64-encoded data as a String
* @throws NullPointerException
* if source array is null
* @throws IllegalArgumentException
* if source array, offset, or length are invalid
* @since 2.0
*/
public static String encodeBytes(byte[] source) {
return encodeBytes(source, 0, source.length);
}
/**
* Encodes a byte array into Base64 notation.
*
* @param source
* The data to convert
* @param off
* Offset in array where conversion should begin
* @param len
* Length of data to convert
* @return The Base64-encoded data as a String
* @throws NullPointerException
* if source array is null
* @throws IllegalArgumentException
* if source array, offset, or length are invalid
* @since 2.0
*/
public static String encodeBytes(byte[] source, int off, int len) {
byte[] encoded = encodeBytesToBytes(source, off, len);
try {
return new String(encoded, PREFERRED_ENCODING);
} catch (UnsupportedEncodingException uue) {
return new String(encoded);
}
}
/**
* Similar to {@link #encodeBytes(byte[], int, int)} but returns a byte
* array instead of instantiating a String. This is more efficient if you're
* working with I/O streams and have large data sets to encode.
*
*
* @param source
* The data to convert
* @param off
* Offset in array where conversion should begin
* @param len
* Length of data to convert
* @return The Base64-encoded data as a String if there is an error
* @throws NullPointerException
* if source array is null
* @throws IllegalArgumentException
* if source array, offset, or length are invalid
* @since 2.3.1
*/
public static byte[] encodeBytesToBytes(byte[] source, int off, int len) {
if (source == null)
throw new NullPointerException("Cannot serialize a null array.");
if (off < 0)
throw new IllegalArgumentException("Cannot have negative offset: "
+ off);
if (len < 0)
throw new IllegalArgumentException("Cannot have length offset: " + len);
if (off + len > source.length)
throw new IllegalArgumentException(
String
.format(
"Cannot have offset of %d and length of %d with array of length %d",
off, len, source.length));
// Bytes needed for actual encoding
int encLen = (len / 3) * 4 + (len % 3 > 0 ? 4 : 0);
byte[] outBuff = new byte[encLen];
int d = 0;
int e = 0;
int len2 = len - 2;
for (; d < len2; d += 3, e += 4)
encode3to4(source, d + off, 3, outBuff, e);
if (d < len) {
encode3to4(source, d + off, len - d, outBuff, e);
e += 4;
}
if (e <= outBuff.length - 1) {
byte[] finalOut = new byte[e];
System.arraycopy(outBuff, 0, finalOut, 0, e);
return finalOut;
} else
return outBuff;
}
}
/**
* HTTP request exception whose cause is always an {@link IOException}
*/
public static class HttpRequestException extends RuntimeException {
private static final long serialVersionUID = -1170466989781746231L;
/**
* Create a new HttpRequestException with the given cause
*
* @param cause
*/
public HttpRequestException(final IOException cause) {
super(cause);
}
/**
* Get {@link IOException} that triggered this request exception
*
* @return {@link IOException} cause
*/
@Override
public IOException getCause() {
return (IOException) super.getCause();
}
}
/**
* Operation that handles executing a callback once complete and handling
* nested exceptions
*
* @param
*/
protected static abstract class Operation implements Callable {
/**
* Run operation
*
* @return result
* @throws HttpRequestException
* @throws IOException
*/
protected abstract V run() throws HttpRequestException, IOException;
/**
* Operation complete callback
*
* @throws IOException
*/
protected abstract void done() throws IOException;
public V call() throws HttpRequestException {
boolean thrown = false;
try {
return run();
} catch (HttpRequestException e) {
thrown = true;
throw e;
} catch (IOException e) {
thrown = true;
throw new HttpRequestException(e);
} finally {
try {
done();
} catch (IOException e) {
if (!thrown)
throw new HttpRequestException(e);
}
}
}
}
/**
* Class that ensures a {@link Closeable} gets closed with proper exception
* handling.
*
* @param
*/
protected static abstract class CloseOperation extends Operation {
private final Closeable closeable;
private final boolean ignoreCloseExceptions;
/**
* Create closer for operation
*
* @param closeable
* @param ignoreCloseExceptions
*/
protected CloseOperation(final Closeable closeable,
final boolean ignoreCloseExceptions) {
this.closeable = closeable;
this.ignoreCloseExceptions = ignoreCloseExceptions;
}
@Override
protected void done() throws IOException {
if (closeable instanceof Flushable)
((Flushable) closeable).flush();
if (ignoreCloseExceptions)
try {
closeable.close();
} catch (IOException e) {
// Ignored
}
else
closeable.close();
}
}
/**
* Class that and ensures a {@link Flushable} gets flushed with proper
* exception handling.
*
* @param
*/
protected static abstract class FlushOperation extends Operation {
private final Flushable flushable;
/**
* Create flush operation
*
* @param flushable
*/
protected FlushOperation(final Flushable flushable) {
this.flushable = flushable;
}
@Override
protected void done() throws IOException {
flushable.flush();
}
}
/**
* Request output stream
*/
public static class RequestOutputStream extends BufferedOutputStream {
private final CharsetEncoder encoder;
/**
* Create request output stream
*
* @param stream
* @param charset
* @param bufferSize
*/
public RequestOutputStream(final OutputStream stream, final String charset,
final int bufferSize) {
super(stream, bufferSize);
encoder = Charset.forName(getValidCharset(charset)).newEncoder();
}
/**
* Write string to stream
*
* @param value
* @return this stream
* @throws IOException
*/
public RequestOutputStream write(final String value) throws IOException {
final ByteBuffer bytes = encoder.encode(CharBuffer.wrap(value));
super.write(bytes.array(), 0, bytes.limit());
return this;
}
}
/**
* Represents array of any type as list of objects so we can easily iterate over it
* @param array of elements
* @return list with the same elements
*/
private static List