From 6c78e6a0474118ebd42db56b98198a080638527e Mon Sep 17 00:00:00 2001 From: doujinlong Date: Fri, 10 Aug 2018 17:53:21 +0800 Subject: [PATCH 1/2] make reponse to HttpResponse --- .../github/kevinsawicki/http/HttpRequest.java | 2473 +++++++++-------- 1 file changed, 1245 insertions(+), 1228 deletions(-) diff --git a/lib/src/main/java/com/github/kevinsawicki/http/HttpRequest.java b/lib/src/main/java/com/github/kevinsawicki/http/HttpRequest.java index d8876ba2..7b76dfa2 100644 --- a/lib/src/main/java/com/github/kevinsawicki/http/HttpRequest.java +++ b/lib/src/main/java/com/github/kevinsawicki/http/HttpRequest.java @@ -260,6 +260,14 @@ public class HttpRequest { private static HostnameVerifier TRUSTED_VERIFIER; + private static final int DEFAULT_READ_TIMEOUT = 5000; + + private static final int DEFAULT_CONNECT_TIMEOUT = 5000; + + private boolean defaultTimeOutFlag = false; + + private HttpResponse httpResponse; + private static String getValidCharset(final String charset) { if (charset != null && charset.length() > 0) return charset; @@ -1548,120 +1556,6 @@ 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 * @@ -1735,1525 +1629,1648 @@ public HttpRequest uncompress(final boolean uncompress) { return this; } + /** - * Create byte array output stream + * Set read timeout on connection to given value * - * @return stream + * @param timeout + * @return this request */ - protected ByteArrayOutputStream byteStream() { - final int size = contentLength(); - if (size > 0) - return new ByteArrayOutputStream(size); - else - return new ByteArrayOutputStream(); + public HttpRequest readTimeout(final int timeout) { + getConnection().setReadTimeout(timeout); + return this; } /** - * 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 + * Set connect timeout on connection to given value * - * @param charset - * @return string - * @throws HttpRequestException + * @param timeout + * @return this request */ - 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); - } + public HttpRequest connectTimeout(final int timeout) { + getConnection().setConnectTimeout(timeout); + return this; } /** - * Get response as {@link String} using character set returned from - * {@link #charset()} + * Set header name to given value * - * @return string - * @throws HttpRequestException + * @param name + * @param value + * @return this request */ - public String body() throws HttpRequestException { - return body(charset()); + public HttpRequest header(final String name, final String value) { + getConnection().setRequestProperty(name, value); + return this; } /** - * Get the response body as a {@link String} and set it as the value of the - * given reference. + * Set header name to given value * - * @param output + * @param name + * @param value * @return this request - * @throws HttpRequestException */ - public HttpRequest body(final AtomicReference output) throws HttpRequestException { - output.set(body()); - return this; + public HttpRequest header(final String name, final Number value) { + return header(name, value != null ? value.toString() : null); } /** - * Get the response body as a {@link String} and set it as the value of the - * given reference. + * Set all headers found in given map where the keys are the header names and + * the values are the header values * - * @param output - * @param charset + * @param headers * @return this request - * @throws HttpRequestException */ - public HttpRequest body(final AtomicReference output, final String charset) throws HttpRequestException { - output.set(body(charset)); + public HttpRequest headers(final Map headers) { + if (!headers.isEmpty()) + for (Entry header : headers.entrySet()) + header(header); return this; } - /** - * Is the response body empty? + * Set header to have given entry's key as the name and value as the value * - * @return true if the Content-Length response header is 0, false otherwise - * @throws HttpRequestException + * @param header + * @return this request */ - public boolean isBodyEmpty() throws HttpRequestException { - return contentLength() == 0; + public HttpRequest header(final Entry header) { + return header(header.getKey(), header.getValue()); } /** - * Get response as byte array + * Get parameter values from header value * - * @return byte array - * @throws HttpRequestException + * @param header + * @return parameter value or null if none */ - public byte[] bytes() throws HttpRequestException { - final ByteArrayOutputStream output = byteStream(); - try { - copy(buffer(), output); - } catch (IOException e) { - throw new HttpRequestException(e); + protected Map getParams(final String header) { + if (header == null || header.length() == 0) + return Collections.emptyMap(); + + final int headerLength = header.length(); + int start = header.indexOf(';') + 1; + if (start == 0 || start == headerLength) + return Collections.emptyMap(); + + int end = header.indexOf(';', start); + if (end == -1) + end = headerLength; + + Map params = new LinkedHashMap(); + while (start < end) { + int nameEnd = header.indexOf('=', start); + if (nameEnd != -1 && nameEnd < end) { + String name = header.substring(start, nameEnd).trim(); + if (name.length() > 0) { + String value = header.substring(nameEnd + 1, end).trim(); + int length = value.length(); + if (length != 0) + if (length > 2 && '"' == value.charAt(0) + && '"' == value.charAt(length - 1)) + params.put(name, value.substring(1, length - 1)); + else + params.put(name, value); + } + } + + start = end + 1; + end = header.indexOf(';', start); + if (end == -1) + end = headerLength; } - return output.toByteArray(); - } - /** - * Get response in a buffered stream - * - * @see #bufferSize(int) - * @return stream - * @throws HttpRequestException - */ - public BufferedInputStream buffer() throws HttpRequestException { - return new BufferedInputStream(stream(), bufferSize); + return params; } /** - * Get stream to response body + * Get parameter value from header value * - * @return stream - * @throws HttpRequestException + * @param value + * @param paramName + * @return parameter value or null if none */ - public InputStream stream() throws HttpRequestException { - InputStream stream; - if (code() < HTTP_BAD_REQUEST) - try { - stream = getConnection().getInputStream(); - } catch (IOException e) { - throw new HttpRequestException(e); - } - else { - stream = getConnection().getErrorStream(); - if (stream == null) - try { - stream = getConnection().getInputStream(); - } catch (IOException e) { - if (contentLength() > 0) - throw new HttpRequestException(e); + protected String getParam(final String value, final String paramName) { + if (value == null || value.length() == 0) + return null; + + final int length = value.length(); + int start = value.indexOf(';') + 1; + if (start == 0 || start == length) + return null; + + int end = value.indexOf(';', start); + if (end == -1) + end = length; + + while (start < end) { + int nameEnd = value.indexOf('=', start); + if (nameEnd != -1 && nameEnd < end + && paramName.equals(value.substring(start, nameEnd).trim())) { + String paramValue = value.substring(nameEnd + 1, end).trim(); + int valueLength = paramValue.length(); + if (valueLength != 0) + if (valueLength > 2 && '"' == paramValue.charAt(0) + && '"' == paramValue.charAt(valueLength - 1)) + return paramValue.substring(1, valueLength - 1); else - stream = new ByteArrayInputStream(new byte[0]); - } + return paramValue; + } + + start = end + 1; + end = value.indexOf(';', start); + if (end == -1) + end = length; } - if (!uncompress || !ENCODING_GZIP.equals(contentEncoding())) - return stream; - else - try { - return new GZIPInputStream(stream); - } catch (IOException e) { - throw new HttpRequestException(e); - } + return null; } + /** - * Get reader to response body using given character set. - *

- * This will fall back to using the UTF-8 character set if the given charset - * is null + * Set the 'User-Agent' header to given value * - * @param charset - * @return reader - * @throws HttpRequestException + * @param userAgent + * @return this request */ - public InputStreamReader reader(final String charset) - throws HttpRequestException { - try { - return new InputStreamReader(stream(), getValidCharset(charset)); - } catch (UnsupportedEncodingException e) { - throw new HttpRequestException(e); - } + public HttpRequest userAgent(final String userAgent) { + return header(HEADER_USER_AGENT, userAgent); } /** - * Get reader to response body using the character set returned from - * {@link #charset()} + * Set the 'Referer' header to given value * - * @return reader - * @throws HttpRequestException + * @param referer + * @return this request */ - public InputStreamReader reader() throws HttpRequestException { - return reader(charset()); + public HttpRequest referer(final String referer) { + return header(HEADER_REFERER, referer); } /** - * Get buffered reader to response body using the given character set r and - * the configured buffer size - * + * Set value of {@link HttpURLConnection#setUseCaches(boolean)} * - * @see #bufferSize(int) - * @param charset - * @return reader - * @throws HttpRequestException + * @param useCaches + * @return this request */ - public BufferedReader bufferedReader(final String charset) - throws HttpRequestException { - return new BufferedReader(reader(charset), bufferSize); + public HttpRequest useCaches(final boolean useCaches) { + getConnection().setUseCaches(useCaches); + return this; } /** - * Get buffered reader to response body using the character set returned from - * {@link #charset()} and the configured buffer size + * Set the 'Accept-Encoding' header to given value * - * @see #bufferSize(int) - * @return reader - * @throws HttpRequestException + * @param acceptEncoding + * @return this request */ - public BufferedReader bufferedReader() throws HttpRequestException { - return bufferedReader(charset()); + public HttpRequest acceptEncoding(final String acceptEncoding) { + return header(HEADER_ACCEPT_ENCODING, acceptEncoding); } /** - * Stream response body to file + * Set the 'Accept-Encoding' header to 'gzip' * - * @param file + * @see #uncompress(boolean) * @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(output, ignoreCloseExceptions) { - - @Override - protected HttpRequest run() throws HttpRequestException, IOException { - return receive(output); - } - }.call(); + public HttpRequest acceptGzipEncoding() { + return acceptEncoding(ENCODING_GZIP); } /** - * Stream response to given output stream + * Set the 'Accept-Charset' header to given value * - * @param output + * @param acceptCharset * @return this request - * @throws HttpRequestException */ - public HttpRequest receive(final OutputStream output) - throws HttpRequestException { - try { - return copy(buffer(), output); - } catch (IOException e) { - throw new HttpRequestException(e); - } + public HttpRequest acceptCharset(final String acceptCharset) { + return header(HEADER_ACCEPT_CHARSET, acceptCharset); } /** - * Stream response to given print stream + * Set the 'Authorization' header to given value * - * @param output + * @param authorization * @return this request - * @throws HttpRequestException */ - public HttpRequest receive(final PrintStream output) - throws HttpRequestException { - return receive((OutputStream) output); + public HttpRequest authorization(final String authorization) { + return header(HEADER_AUTHORIZATION, authorization); } /** - * Receive response into the given appendable + * Set the 'Proxy-Authorization' header to given value * - * @param appendable + * @param proxyAuthorization * @return this request - * @throws HttpRequestException */ - public HttpRequest receive(final Appendable appendable) - throws HttpRequestException { - final BufferedReader reader = bufferedReader(); - return new CloseOperation(reader, ignoreCloseExceptions) { - - @Override - public HttpRequest run() throws IOException { - final CharBuffer buffer = CharBuffer.allocate(bufferSize); - int read; - while ((read = reader.read(buffer)) != -1) { - buffer.rewind(); - appendable.append(buffer, 0, read); - buffer.rewind(); - } - return HttpRequest.this; - } - }.call(); + public HttpRequest proxyAuthorization(final String proxyAuthorization) { + return header(HEADER_PROXY_AUTHORIZATION, proxyAuthorization); } /** - * Receive response into the given writer + * Set the 'Authorization' header to given values in Basic authentication + * format * - * @param writer + * @param name + * @param password * @return this request - * @throws HttpRequestException */ - public HttpRequest receive(final Writer writer) throws HttpRequestException { - final BufferedReader reader = bufferedReader(); - return new CloseOperation(reader, ignoreCloseExceptions) { - - @Override - public HttpRequest run() throws IOException { - return copy(reader, writer); - } - }.call(); + public HttpRequest basic(final String name, final String password) { + return authorization("Basic " + Base64.encode(name + ':' + password)); } /** - * Set read timeout on connection to given value + * Set the 'Proxy-Authorization' header to given values in Basic authentication + * format * - * @param timeout + * @param name + * @param password * @return this request */ - public HttpRequest readTimeout(final int timeout) { - getConnection().setReadTimeout(timeout); - return this; + public HttpRequest proxyBasic(final String name, final String password) { + return proxyAuthorization("Basic " + Base64.encode(name + ':' + password)); } /** - * Set connect timeout on connection to given value + * Set the 'If-Modified-Since' request header to the given value * - * @param timeout + * @param ifModifiedSince * @return this request */ - public HttpRequest connectTimeout(final int timeout) { - getConnection().setConnectTimeout(timeout); + public HttpRequest ifModifiedSince(final long ifModifiedSince) { + getConnection().setIfModifiedSince(ifModifiedSince); return this; } /** - * Set header name to given value + * Set the 'If-None-Match' request header to the given value * - * @param name - * @param value + * @param ifNoneMatch * @return this request */ - public HttpRequest header(final String name, final String value) { - getConnection().setRequestProperty(name, value); - return this; + public HttpRequest ifNoneMatch(final String ifNoneMatch) { + return header(HEADER_IF_NONE_MATCH, ifNoneMatch); } /** - * Set header name to given value + * Set the 'Content-Type' request header to the given value * - * @param name - * @param value + * @param contentType * @return this request */ - public HttpRequest header(final String name, final Number value) { - return header(name, value != null ? value.toString() : null); + public HttpRequest contentType(final String contentType) { + return contentType(contentType, null); } /** - * Set all headers found in given map where the keys are the header names and - * the values are the header values + * Set the 'Content-Type' request header to the given value and charset * - * @param headers + * @param contentType + * @param charset * @return this request */ - public HttpRequest headers(final Map headers) { - if (!headers.isEmpty()) - for (Entry header : headers.entrySet()) - header(header); - return this; + public HttpRequest contentType(final String contentType, final String charset) { + if (charset != null && charset.length() > 0) { + final String separator = "; " + PARAM_CHARSET + '='; + return header(HEADER_CONTENT_TYPE, contentType + separator + charset); + } else + return header(HEADER_CONTENT_TYPE, contentType); } + /** - * Set header to have given entry's key as the name and value as the value + * Set the 'Content-Length' request header to the given value * - * @param header + * @param contentLength * @return this request */ - public HttpRequest header(final Entry header) { - return header(header.getKey(), header.getValue()); + public HttpRequest contentLength(final String contentLength) { + return contentLength(Integer.parseInt(contentLength)); } /** - * Get a response header + * Set the 'Content-Length' request header to the given value * - * @param name - * @return response header - * @throws HttpRequestException + * @param contentLength + * @return this request */ - public String header(final String name) throws HttpRequestException { - closeOutputQuietly(); - return getConnection().getHeaderField(name); + public HttpRequest contentLength(final int contentLength) { + getConnection().setFixedLengthStreamingMode(contentLength); + return this; } /** - * Get all the response headers + * Set the 'Accept' header to given value * - * @return map of response header names to their value(s) - * @throws HttpRequestException + * @param accept + * @return this request */ - public Map> headers() throws HttpRequestException { - closeOutputQuietly(); - return getConnection().getHeaderFields(); + public HttpRequest accept(final String accept) { + return header(HEADER_ACCEPT, accept); } /** - * Get a date header from the response falling back to returning -1 if the - * header is missing or parsing fails + * Set the 'Accept' header to 'application/json' * - * @param name - * @return date, -1 on failures - * @throws HttpRequestException + * @return this request */ - public long dateHeader(final String name) throws HttpRequestException { - return dateHeader(name, -1L); + public HttpRequest acceptJson() { + return accept(CONTENT_TYPE_JSON); } /** - * Get a date header from the response falling back to returning the given - * default value if the header is missing or parsing fails + * Copy from input stream to output stream * - * @param name - * @param defaultValue - * @return date, default value on failures - * @throws HttpRequestException + * @param input + * @param output + * @return this request + * @throws IOException */ - public long dateHeader(final String name, final long defaultValue) - throws HttpRequestException { - closeOutputQuietly(); - return getConnection().getHeaderFieldDate(name, defaultValue); + protected HttpResponse copy(final InputStream input, final OutputStream output) + throws IOException { + return new CloseOperation(input, ignoreCloseExceptions) { + + @Override + public HttpResponse run() throws IOException { + final byte[] buffer = new byte[bufferSize]; + int read; + while ((read = input.read(buffer)) != -1) { + output.write(buffer, 0, read); + totalWritten += read; + progress.onUpload(totalWritten, totalSize); + } + return HttpRequest.this.httpResponse; + } + }.call(); } /** - * Get an integer header from the response falling back to returning -1 if the - * header is missing or parsing fails + * Copy from reader to writer * - * @param name - * @return header value as an integer, -1 when missing or parsing fails - * @throws HttpRequestException + * @param input + * @param output + * @return this request + * @throws IOException */ - public int intHeader(final String name) throws HttpRequestException { - return intHeader(name, -1); + protected HttpRequest copy(final Reader input, final Writer output) + throws IOException { + return new CloseOperation(input, ignoreCloseExceptions) { + + @Override + public HttpRequest run() throws IOException { + final char[] buffer = new char[bufferSize]; + int read; + while ((read = input.read(buffer)) != -1) { + output.write(buffer, 0, read); + totalWritten += read; + progress.onUpload(totalWritten, -1); + } + return HttpRequest.this; + } + }.call(); } /** - * Get an integer header value from the response falling back to the given - * default value if the header is missing or if parsing fails + * Set the UploadProgress callback for this request * - * @param name - * @param defaultValue - * @return header value as an integer, default value when missing or parsing - * fails - * @throws HttpRequestException + * @param callback + * @return this request */ - public int intHeader(final String name, final int defaultValue) - throws HttpRequestException { - closeOutputQuietly(); - return getConnection().getHeaderFieldInt(name, defaultValue); + public HttpRequest progress(final UploadProgress callback) { + if (callback == null) + progress = UploadProgress.DEFAULT; + else + progress = callback; + return this; + } + + private HttpRequest incrementTotalSize(final long size) { + if (totalSize == -1) + totalSize = 0; + totalSize += size; + return this; } + + + /** - * Get all values of the given header from the response + * Open output stream * - * @param name - * @return non-null but possibly empty array of {@link String} header values + * @return this request + * @throws IOException */ - public String[] headers(final String name) { - final Map> headers = headers(); - if (headers == null || headers.isEmpty()) - return EMPTY_STRINGS; - - final List values = headers.get(name); - if (values != null && !values.isEmpty()) - return values.toArray(new String[values.size()]); - else - return EMPTY_STRINGS; + protected HttpRequest openOutput() throws IOException { + if (output != null) + return this; + getConnection().setDoOutput(true); + final String charset = getParam( + getConnection().getRequestProperty(HEADER_CONTENT_TYPE), PARAM_CHARSET); + output = new RequestOutputStream(getConnection().getOutputStream(), charset, + bufferSize); + return this; } /** - * Get parameter with given name from header value in response + * Start part of a multipart * - * @param headerName - * @param paramName - * @return parameter value or null if missing + * @return this request + * @throws IOException */ - public String parameter(final String headerName, final String paramName) { - return getParam(header(headerName), paramName); + protected HttpRequest startPart() throws IOException { + if (!multipart) { + multipart = true; + contentType(CONTENT_TYPE_MULTIPART).openOutput(); + output.write("--" + BOUNDARY + CRLF); + } else + output.write(CRLF + "--" + BOUNDARY + CRLF); + return this; } /** - * Get all parameters from header value in response - *

- * This will be all key=value pairs after the first ';' that are separated by - * a ';' + * Write part header * - * @param headerName - * @return non-null but possibly empty map of parameter headers + * @param name + * @param filename + * @return this request + * @throws IOException */ - public Map parameters(final String headerName) { - return getParams(header(headerName)); + protected HttpRequest writePartHeader(final String name, final String filename) + throws IOException { + return writePartHeader(name, filename, null); } /** - * Get parameter values from header value + * Write part header * - * @param header - * @return parameter value or null if none - */ - protected Map getParams(final String header) { - if (header == null || header.length() == 0) - return Collections.emptyMap(); - - final int headerLength = header.length(); - int start = header.indexOf(';') + 1; - if (start == 0 || start == headerLength) - return Collections.emptyMap(); - - int end = header.indexOf(';', start); - if (end == -1) - end = headerLength; - - Map params = new LinkedHashMap(); - while (start < end) { - int nameEnd = header.indexOf('=', start); - if (nameEnd != -1 && nameEnd < end) { - String name = header.substring(start, nameEnd).trim(); - if (name.length() > 0) { - String value = header.substring(nameEnd + 1, end).trim(); - int length = value.length(); - if (length != 0) - if (length > 2 && '"' == value.charAt(0) - && '"' == value.charAt(length - 1)) - params.put(name, value.substring(1, length - 1)); - else - params.put(name, value); - } - } - - start = end + 1; - end = header.indexOf(';', start); - if (end == -1) - end = headerLength; - } - - return params; - } - - /** - * Get parameter value from header value - * - * @param value - * @param paramName - * @return parameter value or null if none - */ - protected String getParam(final String value, final String paramName) { - if (value == null || value.length() == 0) - return null; - - final int length = value.length(); - int start = value.indexOf(';') + 1; - if (start == 0 || start == length) - return null; - - int end = value.indexOf(';', start); - if (end == -1) - end = length; - - while (start < end) { - int nameEnd = value.indexOf('=', start); - if (nameEnd != -1 && nameEnd < end - && paramName.equals(value.substring(start, nameEnd).trim())) { - String paramValue = value.substring(nameEnd + 1, end).trim(); - int valueLength = paramValue.length(); - if (valueLength != 0) - if (valueLength > 2 && '"' == paramValue.charAt(0) - && '"' == paramValue.charAt(valueLength - 1)) - return paramValue.substring(1, valueLength - 1); - else - return paramValue; - } - - start = end + 1; - end = value.indexOf(';', start); - if (end == -1) - end = length; - } - - return null; - } - - /** - * Get 'charset' parameter from 'Content-Type' response header - * - * @return charset or null if none + * @param name + * @param filename + * @param contentType + * @return this request + * @throws IOException */ - public String charset() { - return parameter(HEADER_CONTENT_TYPE, PARAM_CHARSET); + protected HttpRequest writePartHeader(final String name, + final String filename, final String contentType) throws IOException { + final StringBuilder partBuffer = new StringBuilder(); + partBuffer.append("form-data; name=\"").append(name); + if (filename != null) + partBuffer.append("\"; filename=\"").append(filename); + partBuffer.append('"'); + partHeader("Content-Disposition", partBuffer.toString()); + if (contentType != null) + partHeader(HEADER_CONTENT_TYPE, contentType); + return send(CRLF); } /** - * Set the 'User-Agent' header to given value + * Write part of a multipart request to the request body * - * @param userAgent + * @param name + * @param part * @return this request */ - public HttpRequest userAgent(final String userAgent) { - return header(HEADER_USER_AGENT, userAgent); + public HttpRequest part(final String name, final String part) { + return part(name, null, part); } /** - * Set the 'Referer' header to given value + * Write part of a multipart request to the request body * - * @param referer + * @param name + * @param filename + * @param part * @return this request + * @throws HttpRequestException */ - public HttpRequest referer(final String referer) { - return header(HEADER_REFERER, referer); + public HttpRequest part(final String name, final String filename, + final String part) throws HttpRequestException { + return part(name, filename, null, part); } /** - * Set value of {@link HttpURLConnection#setUseCaches(boolean)} + * Write part of a multipart request to the request body * - * @param useCaches + * @param name + * @param filename + * @param contentType + * value of the Content-Type part header + * @param part * @return this request + * @throws HttpRequestException */ - public HttpRequest useCaches(final boolean useCaches) { - getConnection().setUseCaches(useCaches); + public HttpRequest part(final String name, final String filename, + final String contentType, final String part) throws HttpRequestException { + try { + startPart(); + writePartHeader(name, filename, contentType); + output.write(part); + } catch (IOException e) { + throw new HttpRequestException(e); + } return this; } /** - * Set the 'Accept-Encoding' header to given value + * Write part of a multipart request to the request body * - * @param acceptEncoding + * @param name + * @param part * @return this request + * @throws HttpRequestException */ - public HttpRequest acceptEncoding(final String acceptEncoding) { - return header(HEADER_ACCEPT_ENCODING, acceptEncoding); + public HttpRequest part(final String name, final Number part) + throws HttpRequestException { + return part(name, null, part); } /** - * Set the 'Accept-Encoding' header to 'gzip' + * Write part of a multipart request to the request body * - * @see #uncompress(boolean) + * @param name + * @param filename + * @param part * @return this request + * @throws HttpRequestException */ - public HttpRequest acceptGzipEncoding() { - return acceptEncoding(ENCODING_GZIP); + public HttpRequest part(final String name, final String filename, + final Number part) throws HttpRequestException { + return part(name, filename, part != null ? part.toString() : null); } /** - * Set the 'Accept-Charset' header to given value + * Write part of a multipart request to the request body * - * @param acceptCharset + * @param name + * @param part * @return this request + * @throws HttpRequestException */ - public HttpRequest acceptCharset(final String acceptCharset) { - return header(HEADER_ACCEPT_CHARSET, acceptCharset); + public HttpRequest part(final String name, final File part) + throws HttpRequestException { + return part(name, null, part); } /** - * Get the 'Content-Encoding' header from the response + * Write part of a multipart request to the request body * + * @param name + * @param filename + * @param part * @return this request + * @throws HttpRequestException */ - public String contentEncoding() { - return header(HEADER_CONTENT_ENCODING); + public HttpRequest part(final String name, final String filename, + final File part) throws HttpRequestException { + return part(name, filename, null, part); } /** - * Get the 'Server' header from the response + * Write part of a multipart request to the request body * - * @return server + * @param name + * @param filename + * @param contentType + * value of the Content-Type part header + * @param part + * @return this request + * @throws HttpRequestException */ - public String server() { - return header(HEADER_SERVER); + public HttpRequest part(final String name, final String filename, + final String contentType, final File part) throws HttpRequestException { + final InputStream stream; + try { + stream = new BufferedInputStream(new FileInputStream(part)); + incrementTotalSize(part.length()); + } catch (IOException e) { + throw new HttpRequestException(e); + } + return part(name, filename, contentType, stream); } /** - * Get the 'Date' header from the response + * Write part of a multipart request to the request body * - * @return date value, -1 on failures + * @param name + * @param part + * @return this request + * @throws HttpRequestException */ - public long date() { - return dateHeader(HEADER_DATE); + public HttpRequest part(final String name, final InputStream part) + throws HttpRequestException { + return part(name, null, null, part); } /** - * Get the 'Cache-Control' header from the response + * Write part of a multipart request to the request body * - * @return cache control + * @param name + * @param filename + * @param contentType + * value of the Content-Type part header + * @param part + * @return this request + * @throws HttpRequestException */ - public String cacheControl() { - return header(HEADER_CACHE_CONTROL); + public HttpRequest part(final String name, final String filename, + final String contentType, final InputStream part) + throws HttpRequestException { + try { + startPart(); + writePartHeader(name, filename, contentType); + copy(part, output); + } catch (IOException e) { + throw new HttpRequestException(e); + } + return this; } /** - * Get the 'ETag' header from the response + * Write a multipart header to the response body * - * @return entity tag + * @param name + * @param value + * @return this request + * @throws HttpRequestException */ - public String eTag() { - return header(HEADER_ETAG); + public HttpRequest partHeader(final String name, final String value) + throws HttpRequestException { + return send(name).send(": ").send(value).send(CRLF); } /** - * Get the 'Expires' header from the response + * Write contents of file to request body * - * @return expires value, -1 on failures + * @param input + * @return this request + * @throws HttpRequestException */ - public long expires() { - return dateHeader(HEADER_EXPIRES); + public HttpRequest send(final File input) throws HttpRequestException { + final InputStream stream; + try { + stream = new BufferedInputStream(new FileInputStream(input)); + incrementTotalSize(input.length()); + } catch (FileNotFoundException e) { + throw new HttpRequestException(e); + } + return send(stream); } /** - * Get the 'Last-Modified' header from the response + * Write byte array to request body * - * @return last modified value, -1 on failures + * @param input + * @return this request + * @throws HttpRequestException */ - public long lastModified() { - return dateHeader(HEADER_LAST_MODIFIED); + public HttpRequest send(final byte[] input) throws HttpRequestException { + if (input != null) + incrementTotalSize(input.length); + return send(new ByteArrayInputStream(input)); } /** - * Get the 'Location' header from the response - * - * @return location - */ - public String location() { - return header(HEADER_LOCATION); - } - - /** - * Set the 'Authorization' header to given value + * Write stream to request body + *

+ * The given stream will be closed once sending completes * - * @param authorization + * @param input * @return this request + * @throws HttpRequestException */ - public HttpRequest authorization(final String authorization) { - return header(HEADER_AUTHORIZATION, authorization); + public HttpRequest send(final InputStream input) throws HttpRequestException { + try { + openOutput(); + copy(input, output); + } catch (IOException e) { + throw new HttpRequestException(e); + } + return this; } /** - * Set the 'Proxy-Authorization' header to given value + * Write reader to request body + *

+ * The given reader will be closed once sending completes * - * @param proxyAuthorization + * @param input * @return this request + * @throws HttpRequestException */ - public HttpRequest proxyAuthorization(final String proxyAuthorization) { - return header(HEADER_PROXY_AUTHORIZATION, proxyAuthorization); + 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(writer) { + + @Override + protected HttpRequest run() throws IOException { + return copy(input, writer); + } + }.call(); } /** - * Set the 'Authorization' header to given values in Basic authentication - * format + * Write char sequence to request body + *

+ * The charset configured via {@link #contentType(String)} will be used and + * UTF-8 will be used if it is unset. * - * @param name - * @param password + * @param value * @return this request + * @throws HttpRequestException */ - public HttpRequest basic(final String name, final String password) { - return authorization("Basic " + Base64.encode(name + ':' + password)); + public HttpRequest send(final CharSequence value) throws HttpRequestException { + try { + openOutput(); + output.write(value.toString()); + } catch (IOException e) { + throw new HttpRequestException(e); + } + return this; } /** - * Set the 'Proxy-Authorization' header to given values in Basic authentication - * format + * Create writer to request output stream * - * @param name - * @param password - * @return this request + * @return writer + * @throws HttpRequestException */ - public HttpRequest proxyBasic(final String name, final String password) { - return proxyAuthorization("Basic " + Base64.encode(name + ':' + password)); + public OutputStreamWriter writer() throws HttpRequestException { + try { + openOutput(); + return new OutputStreamWriter(output, output.encoder.charset()); + } catch (IOException e) { + throw new HttpRequestException(e); + } } /** - * Set the 'If-Modified-Since' request header to the given value + * 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 ifModifiedSince + * @param values * @return this request + * @throws HttpRequestException */ - public HttpRequest ifModifiedSince(final long ifModifiedSince) { - getConnection().setIfModifiedSince(ifModifiedSince); - return this; + public HttpRequest form(final Map values) throws HttpRequestException { + return form(values, CHARSET_UTF8); } /** - * Set the 'If-None-Match' request header to the given value + * 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 ifNoneMatch + * @param entry * @return this request + * @throws HttpRequestException */ - public HttpRequest ifNoneMatch(final String ifNoneMatch) { - return header(HEADER_IF_NONE_MATCH, ifNoneMatch); + public HttpRequest form(final Entry entry) throws HttpRequestException { + return form(entry, CHARSET_UTF8); } /** - * Set the 'Content-Type' request header to the given value + * 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 contentType + * @param entry + * @param charset * @return this request + * @throws HttpRequestException */ - public HttpRequest contentType(final String contentType) { - return contentType(contentType, null); + public HttpRequest form(final Entry entry, final String charset) + throws HttpRequestException { + return form(entry.getKey(), entry.getValue(), charset); } /** - * Set the 'Content-Type' request header to the given value and 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 contentType - * @param charset + * @param name + * @param value * @return this request + * @throws HttpRequestException */ - public HttpRequest contentType(final String contentType, final String charset) { - if (charset != null && charset.length() > 0) { - final String separator = "; " + PARAM_CHARSET + '='; - return header(HEADER_CONTENT_TYPE, contentType + separator + charset); - } else - return header(HEADER_CONTENT_TYPE, contentType); + public HttpRequest form(final Object name, final Object value) + throws HttpRequestException { + return form(name, value, CHARSET_UTF8); } /** - * Get the 'Content-Type' header from the response + * 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 * - * @return response header value + * @param name + * @param value + * @param charset + * @return this request + * @throws HttpRequestException */ - public String contentType() { - return header(HEADER_CONTENT_TYPE); + 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; } /** - * Get the 'Content-Length' header from the response + * Write the values in the map as encoded form data to the request body * - * @return response header value + * @param values + * @param charset + * @return this request + * @throws HttpRequestException */ - public int contentLength() { - return intHeader(HEADER_CONTENT_LENGTH); + public HttpRequest form(final Map values, final String charset) + throws HttpRequestException { + if (!values.isEmpty()) + for (Entry entry : values.entrySet()) + form(entry, charset); + return this; } /** - * Set the 'Content-Length' request header to the given value + * Configure HTTPS connection to trust all certificates + *

+ * This method does nothing if the current request is not a HTTPS request * - * @param contentLength * @return this request + * @throws HttpRequestException */ - public HttpRequest contentLength(final String contentLength) { - return contentLength(Integer.parseInt(contentLength)); + public HttpRequest trustAllCerts() throws HttpRequestException { + final HttpURLConnection connection = getConnection(); + if (connection instanceof HttpsURLConnection) + ((HttpsURLConnection) connection) + .setSSLSocketFactory(getTrustedFactory()); + return this; } /** - * Set the 'Content-Length' request header to the given value + * Configure HTTPS connection to trust all hosts using a custom + * {@link HostnameVerifier} that always returns true for each + * host verified + *

+ * This method does nothing if the current request is not a HTTPS request * - * @param contentLength * @return this request */ - public HttpRequest contentLength(final int contentLength) { - getConnection().setFixedLengthStreamingMode(contentLength); + public HttpRequest trustAllHosts() { + final HttpURLConnection connection = getConnection(); + if (connection instanceof HttpsURLConnection) + ((HttpsURLConnection) connection) + .setHostnameVerifier(getTrustedVerifier()); return this; } /** - * Set the 'Accept' header to given value + * Get the {@link URL} of this request's connection * - * @param accept - * @return this request + * @return request URL */ - public HttpRequest accept(final String accept) { - return header(HEADER_ACCEPT, accept); + public URL url() { + return getConnection().getURL(); } /** - * Set the 'Accept' header to 'application/json' + * Get the HTTP method of this request * - * @return this request + * @return method */ - public HttpRequest acceptJson() { - return accept(CONTENT_TYPE_JSON); + public String method() { + return getConnection().getRequestMethod(); } /** - * Copy from input stream to output stream + * Configure an HTTP proxy on this connection. Use {{@link #proxyBasic(String, String)} if + * this proxy requires basic authentication. * - * @param input - * @param output + * @param proxyHost + * @param proxyPort * @return this request - * @throws IOException */ - protected HttpRequest copy(final InputStream input, final OutputStream output) - throws IOException { - return new CloseOperation(input, ignoreCloseExceptions) { + 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."); - @Override - public HttpRequest run() throws IOException { - final byte[] buffer = new byte[bufferSize]; - int read; - while ((read = input.read(buffer)) != -1) { - output.write(buffer, 0, read); - totalWritten += read; - progress.onUpload(totalWritten, totalSize); - } - return HttpRequest.this; - } - }.call(); + this.httpProxyHost = proxyHost; + this.httpProxyPort = proxyPort; + return this; } /** - * Copy from reader to writer + * Set whether or not the underlying connection should follow redirects in + * the response. * - * @param input - * @param output + * @param followRedirects - true fo follow redirects, false to not. * @return this request - * @throws IOException */ - protected HttpRequest copy(final Reader input, final Writer output) - throws IOException { - return new CloseOperation(input, ignoreCloseExceptions) { - - @Override - public HttpRequest run() throws IOException { - final char[] buffer = new char[bufferSize]; - int read; - while ((read = input.read(buffer)) != -1) { - output.write(buffer, 0, read); - totalWritten += read; - progress.onUpload(totalWritten, -1); - } - return HttpRequest.this; - } - }.call(); + public HttpRequest followRedirects(final boolean followRedirects) { + getConnection().setInstanceFollowRedirects(followRedirects); + return this; } - /** - * Set the UploadProgress callback for this request - * - * @param callback - * @return this request - */ - public HttpRequest progress(final UploadProgress callback) { - if (callback == null) - progress = UploadProgress.DEFAULT; - else - progress = callback; - return this; + private void setDefaultTimeOut() { + getConnection().setConnectTimeout(DEFAULT_CONNECT_TIMEOUT); + getConnection().setReadTimeout(DEFAULT_READ_TIMEOUT); } - private HttpRequest incrementTotalSize(final long size) { - if (totalSize == -1) - totalSize = 0; - totalSize += size; + public HttpRequest defaultTimeOut() { + this.defaultTimeOutFlag = true; return this; } - /** - * Close output stream - * - * @return this request - * @throws HttpRequestException - * @throws IOException - */ - protected HttpRequest closeOutput() throws IOException { - progress(null); - if (output == null) - return this; - if (multipart) - output.write(CRLF + "--" + BOUNDARY + "--" + CRLF); - if (ignoreCloseExceptions) + public class HttpResponse{ + + /** + * Get the status code of the response + * + * @return the response code + * @throws HttpRequestException + */ + public int code() throws HttpRequestException { try { - output.close(); - } catch (IOException ignored) { - // Ignored + closeOutput(); + return getConnection().getResponseCode(); + } catch (IOException e) { + throw new HttpRequestException(e); } - else - output.close(); - output = null; - return this; - } + } - /** - * Call {@link #closeOutput()} and re-throw a caught {@link IOException}s as - * an {@link HttpRequestException} - * - * @return this request - * @throws HttpRequestException - */ - protected HttpRequest closeOutputQuietly() throws HttpRequestException { - try { - return closeOutput(); - } catch (IOException e) { - throw new HttpRequestException(e); + /** + * Close output stream + * + * @return this request + * @throws HttpRequestException + * @throws IOException + */ + protected HttpResponse closeOutput() throws IOException { + progress(null); + if (output == null) + return this; + if (multipart) + output.write(CRLF + "--" + BOUNDARY + "--" + CRLF); + if (ignoreCloseExceptions) + try { + output.close(); + } catch (IOException ignored) { + // Ignored + } + else + output.close(); + output = null; + return this; } - } - /** - * Open output stream - * - * @return this request - * @throws IOException - */ - protected HttpRequest openOutput() throws IOException { - if (output != null) + /** + * Set the value of the given {@link AtomicInteger} to the status code of the + * response + * + * @param output + * @return this request + * @throws HttpRequestException + */ + public HttpResponse code(final AtomicInteger output) + throws HttpRequestException { + output.set(code()); return this; - getConnection().setDoOutput(true); - final String charset = getParam( - getConnection().getRequestProperty(HEADER_CONTENT_TYPE), PARAM_CHARSET); - output = new RequestOutputStream(getConnection().getOutputStream(), charset, - bufferSize); - return this; - } + } - /** - * Start part of a multipart - * - * @return this request - * @throws IOException - */ - protected HttpRequest startPart() throws IOException { - if (!multipart) { - multipart = true; - contentType(CONTENT_TYPE_MULTIPART).openOutput(); - output.write("--" + BOUNDARY + CRLF); - } else - output.write(CRLF + "--" + BOUNDARY + CRLF); - return this; - } + /** + * Call {@link #closeOutput()} and re-throw a caught {@link IOException}s as + * an {@link HttpRequestException} + * + * @return this request + * @throws HttpRequestException + */ + protected HttpResponse closeOutputQuietly() throws HttpRequestException { + try { + return closeOutput(); + } catch (IOException e) { + throw new HttpRequestException(e); + } + } - /** - * Write part header - * - * @param name - * @param filename - * @return this request - * @throws IOException - */ - protected HttpRequest writePartHeader(final String name, final String filename) - throws IOException { - return writePartHeader(name, filename, null); - } + /** + * Is the response code a 200 OK? + * + * @return true if 200, false otherwise + * @throws HttpRequestException + */ + public boolean ok() throws HttpRequestException { + return HTTP_OK == code(); + } - /** - * Write part header - * - * @param name - * @param filename - * @param contentType - * @return this request - * @throws IOException - */ - protected HttpRequest writePartHeader(final String name, - final String filename, final String contentType) throws IOException { - final StringBuilder partBuffer = new StringBuilder(); - partBuffer.append("form-data; name=\"").append(name); - if (filename != null) - partBuffer.append("\"; filename=\"").append(filename); - partBuffer.append('"'); - partHeader("Content-Disposition", partBuffer.toString()); - if (contentType != null) - partHeader(HEADER_CONTENT_TYPE, contentType); - return send(CRLF); - } - /** - * Write part of a multipart request to the request body - * - * @param name - * @param part - * @return this request - */ - public HttpRequest part(final String name, final String part) { - return part(name, null, part); - } + /** + * Is the response code a 201 Created? + * + * @return true if 201, false otherwise + * @throws HttpRequestException + */ + public boolean created() throws HttpRequestException { + return HTTP_CREATED == code(); + } - /** - * Write part of a multipart request to the request body - * - * @param name - * @param filename - * @param part - * @return this request - * @throws HttpRequestException - */ - public HttpRequest part(final String name, final String filename, - final String part) throws HttpRequestException { - return part(name, filename, null, part); - } + /** + * 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(); + } - /** - * Write part of a multipart request to the request body - * - * @param name - * @param filename - * @param contentType - * value of the Content-Type part header - * @param part - * @return this request - * @throws HttpRequestException - */ - public HttpRequest part(final String name, final String filename, - final String contentType, final String part) throws HttpRequestException { - try { - startPart(); - writePartHeader(name, filename, contentType); - output.write(part); - } catch (IOException e) { - throw new HttpRequestException(e); + /** + * 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(); } - return this; - } - /** - * Write part of a multipart request to the request body - * - * @param name - * @param part - * @return this request - * @throws HttpRequestException - */ - public HttpRequest part(final String name, final Number part) - throws HttpRequestException { - return part(name, null, part); - } + /** + * 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(); + } - /** - * Write part of a multipart request to the request body - * - * @param name - * @param filename - * @param part - * @return this request - * @throws HttpRequestException - */ - public HttpRequest part(final String name, final String filename, - final Number part) throws HttpRequestException { - return part(name, filename, part != null ? part.toString() : null); - } + /** + * 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(); + } - /** - * Write part of a multipart request to the request body - * - * @param name - * @param part - * @return this request - * @throws HttpRequestException - */ - public HttpRequest part(final String name, final File part) - throws HttpRequestException { - return part(name, null, part); - } - - /** - * Write part of a multipart request to the request body - * - * @param name - * @param filename - * @param part - * @return this request - * @throws HttpRequestException - */ - public HttpRequest part(final String name, final String filename, - final File part) throws HttpRequestException { - return part(name, filename, null, part); - } + /** + * 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(); + } - /** - * Write part of a multipart request to the request body - * - * @param name - * @param filename - * @param contentType - * value of the Content-Type part header - * @param part - * @return this request - * @throws HttpRequestException - */ - public HttpRequest part(final String name, final String filename, - final String contentType, final File part) throws HttpRequestException { - final InputStream stream; - try { - stream = new BufferedInputStream(new FileInputStream(part)); - incrementTotalSize(part.length()); - } catch (IOException e) { - throw new HttpRequestException(e); + /** + * 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); + } } - return part(name, filename, contentType, stream); - } - /** - * Write part of a multipart request to the request body - * - * @param name - * @param part - * @return this request - * @throws HttpRequestException - */ - public HttpRequest part(final String name, final InputStream part) - throws HttpRequestException { - return part(name, null, null, part); - } - /** - * Write part of a multipart request to the request body - * - * @param name - * @param filename - * @param contentType - * value of the Content-Type part header - * @param part - * @return this request - * @throws HttpRequestException - */ - public HttpRequest part(final String name, final String filename, - final String contentType, final InputStream part) - throws HttpRequestException { - try { - startPart(); - writePartHeader(name, filename, contentType); - copy(part, output); - } catch (IOException e) { - throw new HttpRequestException(e); + /** + * 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); + } } - return this; - } - - /** - * Write a multipart header to the response body - * - * @param name - * @param value - * @return this request - * @throws HttpRequestException - */ - public HttpRequest partHeader(final String name, final String value) - throws HttpRequestException { - return send(name).send(": ").send(value).send(CRLF); - } - /** - * Write contents of file to request body - * - * @param input - * @return this request - * @throws HttpRequestException - */ - public HttpRequest send(final File input) throws HttpRequestException { - final InputStream stream; - try { - stream = new BufferedInputStream(new FileInputStream(input)); - incrementTotalSize(input.length()); - } catch (FileNotFoundException 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()); } - return send(stream); - } - /** - * Write byte array to request body - * - * @param input - * @return this request - * @throws HttpRequestException - */ - public HttpRequest send(final byte[] input) throws HttpRequestException { - if (input != null) - incrementTotalSize(input.length); - return send(new ByteArrayInputStream(input)); - } + /** + * 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 HttpResponse body(final AtomicReference output) throws HttpRequestException { + output.set(body()); + return this; + } - /** - * Write stream to request body - *

- * 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); + /** + * Get the response body as a {@link String} and set it as the value of the + * given reference. + * + * @param output + * @param charset + * @return this request + * @throws HttpRequestException + */ + public HttpResponse body(final AtomicReference output, final String charset) throws HttpRequestException { + output.set(body(charset)); + return this; } - 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); + + /** + * Is the response body empty? + * + * @return true if the Content-Length response header is 0, false otherwise + * @throws HttpRequestException + */ + public boolean isBodyEmpty() throws HttpRequestException { + return contentLength() == 0; } - final Writer writer = new OutputStreamWriter(output, - output.encoder.charset()); - return new FlushOperation(writer) { - @Override - protected HttpRequest run() throws IOException { - return copy(input, writer); + /** + * Get response as byte array + * + * @return byte array + * @throws HttpRequestException + */ + public byte[] bytes() throws HttpRequestException { + final ByteArrayOutputStream output = byteStream(); + try { + copy(buffer(), output); + } catch (IOException e) { + throw new HttpRequestException(e); } - }.call(); - } + return output.toByteArray(); + } - /** - * Write char sequence to request body - *

- * 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); + /** + * Get response in a buffered stream + * + * @see #bufferSize(int) + * @return stream + * @throws HttpRequestException + */ + public BufferedInputStream buffer() throws HttpRequestException { + return new BufferedInputStream(stream(), bufferSize); + } + /** + * Get a response header + * + * @param name + * @return response header + * @throws HttpRequestException + */ + public String header(final String name) throws HttpRequestException { + closeOutputQuietly(); + return getConnection().getHeaderField(name); } - 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); + /** + * Get all the response headers + * + * @return map of response header names to their value(s) + * @throws HttpRequestException + */ + public Map> headers() throws HttpRequestException { + closeOutputQuietly(); + return getConnection().getHeaderFields(); } - } - /** - * 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); - } + /** + * Get a date header from the response falling back to returning -1 if the + * header is missing or parsing fails + * + * @param name + * @return date, -1 on failures + * @throws HttpRequestException + */ + public long dateHeader(final String name) throws HttpRequestException { + return dateHeader(name, -1L); + } - /** - * 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); - } + /** + * Get a date header from the response falling back to returning the given + * default value if the header is missing or parsing fails + * + * @param name + * @param defaultValue + * @return date, default value on failures + * @throws HttpRequestException + */ + public long dateHeader(final String name, final long defaultValue) + throws HttpRequestException { + closeOutputQuietly(); + return getConnection().getHeaderFieldDate(name, defaultValue); + } - /** - * 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); - } + /** + * Get the 'Content-Encoding' header from the response + * + * @return this request + */ + public String contentEncoding() { + return header(HEADER_CONTENT_ENCODING); + } - /** - * 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; + /** + * Get the 'Server' header from the response + * + * @return server + */ + public String server() { + return header(HEADER_SERVER); } - 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); + + /** + * Get the 'Date' header from the response + * + * @return date value, -1 on failures + */ + public long date() { + return dateHeader(HEADER_DATE); } - 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; - } + /** + * Get the 'Cache-Control' header from the response + * + * @return cache control + */ + public String cacheControl() { + return header(HEADER_CACHE_CONTROL); + } - /** - * 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; - } + /** + * Get the 'ETag' header from the response + * + * @return entity tag + */ + public String eTag() { + return header(HEADER_ETAG); + } - /** - * Configure HTTPS connection to trust all hosts using a custom - * {@link HostnameVerifier} that always returns true for each - * host verified - *

- * 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 'Expires' header from the response + * + * @return expires value, -1 on failures + */ + public long expires() { + return dateHeader(HEADER_EXPIRES); + } - /** - * Get the {@link URL} of this request's connection - * - * @return request URL - */ - public URL url() { - return getConnection().getURL(); - } + /** + * Get the 'Last-Modified' header from the response + * + * @return last modified value, -1 on failures + */ + public long lastModified() { + return dateHeader(HEADER_LAST_MODIFIED); + } - /** - * Get the HTTP method of this request - * - * @return method - */ - public String method() { - return getConnection().getRequestMethod(); - } + /** + * Get the 'Location' header from the response + * + * @return location + */ + public String location() { + return header(HEADER_LOCATION); + } - /** - * 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."); + /** + * Get parameter with given name from header value in response + * + * @param headerName + * @param paramName + * @return parameter value or null if missing + */ + public String parameter(final String headerName, final String paramName) { + return getParam(header(headerName), paramName); + } - this.httpProxyHost = proxyHost; - this.httpProxyPort = proxyPort; - return this; + /** + * Get all parameters from header value in response + *

+ * 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 parameters(final String headerName) { + return getParams(header(headerName)); + } + + /** + * Get 'charset' parameter from 'Content-Type' response header + * + * @return charset or null if none + */ + public String charset() { + return parameter(HEADER_CONTENT_TYPE, PARAM_CHARSET); + } + + /** + * Get all values of the given header from the response + * + * @param name + * @return non-null but possibly empty array of {@link String} header values + */ + public String[] headers(final String name) { + final Map> headers = headers(); + if (headers == null || headers.isEmpty()) + return EMPTY_STRINGS; + + final List values = headers.get(name); + if (values != null && !values.isEmpty()) + return values.toArray(new String[values.size()]); + else + return EMPTY_STRINGS; + } + + + /** + * 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 the 'Content-Length' header from the response + * + * @return response header value + */ + public int contentLength() { + return intHeader(HEADER_CONTENT_LENGTH); + } + + /** + * Get an integer header from the response falling back to returning -1 if the + * header is missing or parsing fails + * + * @param name + * @return header value as an integer, -1 when missing or parsing fails + * @throws HttpRequestException + */ + public int intHeader(final String name) throws HttpRequestException { + return intHeader(name, -1); + } + + /** + * Get an integer header value from the response falling back to the given + * default value if the header is missing or if parsing fails + * + * @param name + * @param defaultValue + * @return header value as an integer, default value when missing or parsing + * fails + * @throws HttpRequestException + */ + public int intHeader(final String name, final int defaultValue) + throws HttpRequestException { + closeOutputQuietly(); + return getConnection().getHeaderFieldInt(name, defaultValue); + } + + /** + * Get stream to response body + * + * @return stream + * @throws HttpRequestException + */ + public InputStream stream() throws HttpRequestException { + if (defaultTimeOutFlag) { + setDefaultTimeOut(); + } + InputStream stream; + if (code() < HTTP_BAD_REQUEST) + try { + stream = getConnection().getInputStream(); + } catch (IOException e) { + throw new HttpRequestException(e); + } + else { + stream = getConnection().getErrorStream(); + if (stream == null) + try { + stream = getConnection().getInputStream(); + } catch (IOException e) { + if (contentLength() > 0) + throw new HttpRequestException(e); + else + stream = new ByteArrayInputStream(new byte[0]); + } + } + + if (!uncompress || !ENCODING_GZIP.equals(contentEncoding())) + return stream; + else + try { + return new GZIPInputStream(stream); + } catch (IOException e) { + throw new HttpRequestException(e); + } + } + + + /** + * Get reader to response body using given character set. + *

+ * 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 HttpResponse 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(output, ignoreCloseExceptions) { + + @Override + protected HttpResponse run() throws HttpRequestException, IOException { + return receive(output); + } + }.call(); + } + + /** + * Stream response to given output stream + * + * @param output + * @return this request + * @throws HttpRequestException + */ + public HttpResponse receive(final OutputStream output) + throws HttpRequestException { + try { + return copy(buffer(), output); + } catch (IOException e) { + throw new HttpRequestException(e); + } + } + + /** + * Stream response to given print stream + * + * @param output + * @return this request + * @throws HttpRequestException + */ + public HttpResponse receive(final PrintStream output) + throws HttpRequestException { + return receive((OutputStream) output); + } + + /** + * Receive response into the given appendable + * + * @param appendable + * @return this request + * @throws HttpRequestException + */ + public HttpResponse receive(final Appendable appendable) + throws HttpRequestException { + final BufferedReader reader = bufferedReader(); + return new CloseOperation(reader, ignoreCloseExceptions) { + + @Override + public HttpResponse run() throws IOException { + final CharBuffer buffer = CharBuffer.allocate(bufferSize); + int read; + while ((read = reader.read(buffer)) != -1) { + buffer.rewind(); + appendable.append(buffer, 0, read); + buffer.rewind(); + } + return HttpRequest.this.httpResponse; + } + }.call(); + } } - /** - * 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; + public HttpResponse executeHttpRequest(){ + if (httpResponse ==null) { + httpResponse = new HttpResponse(); + } + return httpResponse; } + + } From 1ece1cb67247c67fda15b022568e75c7f6463835 Mon Sep 17 00:00:00 2001 From: doujinlong Date: Mon, 13 Aug 2018 15:14:46 +0800 Subject: [PATCH 2/2] make HttpRequest easy to use --- .../github/kevinsawicki/http/HttpRequest.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/src/main/java/com/github/kevinsawicki/http/HttpRequest.java b/lib/src/main/java/com/github/kevinsawicki/http/HttpRequest.java index 7b76dfa2..8dba9be2 100644 --- a/lib/src/main/java/com/github/kevinsawicki/http/HttpRequest.java +++ b/lib/src/main/java/com/github/kevinsawicki/http/HttpRequest.java @@ -2609,7 +2609,11 @@ public HttpRequest defaultTimeOut() { this.defaultTimeOutFlag = true; return this; } - + /** + * + * HttpResponse collects the http response info and make the responsibility clearly(HttpRequest for request,HttpResponse for response) + * + */ public class HttpResponse{ /** @@ -2630,7 +2634,7 @@ public int code() throws HttpRequestException { /** * Close output stream * - * @return this request + * @return this response * @throws HttpRequestException * @throws IOException */ @@ -2657,7 +2661,7 @@ protected HttpResponse closeOutput() throws IOException { * response * * @param output - * @return this request + * @return this response * @throws HttpRequestException */ public HttpResponse code(final AtomicInteger output) @@ -2670,7 +2674,7 @@ public HttpResponse code(final AtomicInteger output) * Call {@link #closeOutput()} and re-throw a caught {@link IOException}s as * an {@link HttpRequestException} * - * @return this request + * @return this response * @throws HttpRequestException */ protected HttpResponse closeOutputQuietly() throws HttpRequestException { @@ -2804,7 +2808,7 @@ public String body() throws HttpRequestException { * given reference. * * @param output - * @return this request + * @return this response * @throws HttpRequestException */ public HttpResponse body(final AtomicReference output) throws HttpRequestException { @@ -2818,7 +2822,7 @@ public HttpResponse body(final AtomicReference output) throws HttpReques * * @param output * @param charset - * @return this request + * @return this response * @throws HttpRequestException */ public HttpResponse body(final AtomicReference output, final String charset) throws HttpRequestException { @@ -3264,7 +3268,10 @@ public HttpResponse run() throws IOException { }.call(); } } - + /** + * this method is used to execute a http request and get a HttpResponse + * @return + */ public HttpResponse executeHttpRequest(){ if (httpResponse ==null) { httpResponse = new HttpResponse(); @@ -3272,5 +3279,4 @@ public HttpResponse executeHttpRequest(){ return httpResponse; } - }