
leap.lang.http.client.JdkHttpRequest Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package leap.lang.http.client;
import leap.lang.Args;
import leap.lang.Strings;
import leap.lang.Try;
import leap.lang.http.ContentTypes;
import leap.lang.http.HTTP;
import leap.lang.http.Headers;
import leap.lang.http.QueryStringBuilder;
import leap.lang.http.exception.HttpException;
import leap.lang.http.exception.HttpIOException;
import leap.lang.io.IO;
import leap.lang.logging.Log;
import leap.lang.logging.LogFactory;
import leap.lang.net.Urls;
import leap.lang.time.StopWatch;
import leap.lang.value.ImmutableNamedValue;
import leap.lang.value.NamedValue;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
class JdkHttpRequest implements HttpRequest {
private static final Log log = LogFactory.get(JdkHttpRequest.class);
protected final JdkHttpClient client;
protected final String url;
protected final boolean ssl;
protected final Map cookies = new LinkedHashMap<>();
protected final SimpleHttpHeaders headers = new SimpleHttpHeaders();
protected final QueryStringBuilder queryParams = new QueryStringBuilder();
protected final List> formParams = new ArrayList<>();
protected int connectTimeout;
protected int readTimeout;
protected Charset charset;
protected InputStream content;
protected HTTP.Method method;
protected HttpURLConnection conn;
private boolean form;
private boolean aborted;
protected JdkHttpRequest(JdkHttpClient client, String url) {
Args.notEmpty(url, "url");
Args.assertTrue(Strings.startsWithIgnoreCase(url, HttpClient.PREFIX_HTTP) ||
Strings.startsWithIgnoreCase(url, HttpClient.PREFIX_HTTPS),
"The url must prefix with http:// or https://");
this.client = client;
this.url = url;
this.ssl = Strings.startsWithIgnoreCase(url, HttpClient.PREFIX_HTTPS);
this.connectTimeout = client.getDefaultConnectTimeout();
this.readTimeout = client.getDefaultReadTimeout();
this.charset = client.getDefaultCharset();
}
@Override
public boolean isAborted() {
return aborted;
}
@Override
public void abort() {
if(null != conn) {
aborted = true;
conn.disconnect();
}
}
@Override
public HttpRequest setCookie(String name, String value) {
Args.notEmpty(name, "name");
cookies.put(name, value);
return this;
}
@Override
public HttpRequest setHeader(String name, String value) {
Args.notEmpty(name, "name");
headers.set(name, value);
return this;
}
@Override
public HttpRequest addHeader(String name, String value) {
Args.notEmpty(name);
headers.add(name, value);
return this;
}
@Override
public HttpRequest addQueryParam(String name, String value) {
Args.notEmpty(name, "name");
queryParams.add(name, value);
return this;
}
@Override
public HttpRequest addFormParam(String name, String value) {
Args.notEmpty(name, "name");
formParams.add(new ImmutableNamedValue(name, value));
return this;
}
@Override
public HttpRequest setMethod(HTTP.Method method) {
this.method = method;
return this;
}
@Override
public HttpRequest setBody(byte[] data) {
Args.notNull(data, "data");
this.content = new ByteArrayInputStream(data);
return this;
}
@Override
public HttpRequest setBody(InputStream is) {
Args.notNull(is);
this.content = is;
return this;
}
@Override
public HttpResponse get() {
return send(HTTP.Method.GET);
}
@Override
public HttpResponse post() {
return send(HTTP.Method.POST);
}
@Override
public HttpResponse send() {
return doSend(initConnUrl(), true);
}
@Override
public void sendAsync(HttpHandler handler) {
String connUrl = initConnUrl();
handler.beforeRequest(this);
try {
handler.afterResponse(this, doSend(connUrl, false));
} catch (Exception e) {
if(aborted) {
handler.afterAborted(this);
}else if(e instanceof RuntimeException) {
throw (RuntimeException)e;
}else{
throw new HttpException(e);
}
}finally{
Try.catchAll(conn::disconnect);
}
}
protected String initConnUrl() {
String connUrl = url;
if(!queryParams.isEmpty()) {
connUrl = Urls.appendQueryString(connUrl, queryParams.build());
}
if(null == method) {
if(form || null != content) {
method = HTTP.Method.POST;
}else{
method = HTTP.Method.GET;
}
}
//JDK Http Connection does not supports PATCH method.
if(method == HTTP.Method.PATCH) {
method = HTTP.Method.POST;
addHeader(Headers.X_HTTP_METHOD_OVERRIDE, HTTP.Method.PATCH.name());
}
return connUrl;
}
protected boolean hasHeader(String name) {
return headers.exists(name);
}
protected JdkHttpResponse doSend(String connUrl, boolean immediately) {
try {
HTTP.Method m = method;
log.debug("Sending '{}' request to url : {}", m, connUrl);
StopWatch sw = StopWatch.startNew();
final URLConnection raw = new URL(connUrl).openConnection();
if(!(raw instanceof HttpURLConnection)) {
throw new IllegalStateException("Url opens '" + raw.getClass().getName() +
"' instead of expected HttpURLConnection");
}
conn = (HttpURLConnection)raw;
setConnParams(conn, m);
setHeaders(conn);
setCookies(conn);
if(m == HTTP.Method.POST || m == HTTP.Method.PUT || m == HTTP.Method.PATCH) {
conn.setDoOutput(true);
try(final InputStream body = getBody()) {
if(null != body) {
//Set form content type.
if(form && !hasHeader(Headers.CONTENT_TYPE)) {
conn.setRequestProperty(Headers.CONTENT_TYPE,
ContentTypes.create(ContentTypes.APPLICATION_FORM_URLENCODED, charset.name()));
}
log.debug("writing body content");
//Writes content.
try(final OutputStream out = conn.getOutputStream()){
IO.copy(body, out);
}
}
}
}
JdkHttpResponse response = new JdkHttpResponse(client, this, immediately);
log.debug("Response used {}ms", sw.getElapsedMilliseconds());
return response;
} catch (MalformedURLException e) {
throw new HttpException("Invalid url : " + e.getMessage(), e);
} catch (IOException e) {
throw new HttpIOException(e);
} finally {
if(immediately) {
Try.catchAll(conn::disconnect);
}
}
}
protected void setConnParams(HttpURLConnection conn, HTTP.Method m) throws IOException {
if(connectTimeout > 0) {
conn.setConnectTimeout(connectTimeout);
}
if(readTimeout > 0) {
conn.setReadTimeout(readTimeout);
}
conn.setRequestMethod(m.name());
conn.setUseCaches(false);
conn.setInstanceFollowRedirects(false);
}
protected void setHeaders(HttpURLConnection conn) throws IOException {
headers.forEach((name,value) -> conn.addRequestProperty(name, value));
}
protected void setCookies(HttpURLConnection conn) throws IOException {
if(!cookies.isEmpty()) {
StringBuilder header = new StringBuilder();
int i=0;
for(Entry cookie : cookies.entrySet()) {
if(i>0) {
header.append(';');
}
i++;
header.append(cookie.getKey()).append('=').append(cookie.getValue());
}
conn.setRequestProperty(Headers.COOKIE, header.toString());
}
}
protected InputStream getBody() throws IOException {
if(null != content) {
return content;
}
if(!formParams.isEmpty()) {
form = true;
return getFormInputStream();
}
return null;
}
protected InputStream getFormInputStream() throws IOException {
final StringBuilder content = new StringBuilder();
for (final NamedValue parameter : formParams) {
final String encodedName = Urls.encode(parameter.getName(), charset.name());
final String encodedValue = Urls.encode(parameter.getValue(), charset.name());
if (content.length() > 0) {
content.append('&');
}
content.append(encodedName);
if (encodedValue != null) {
content.append('=');
content.append(encodedValue);
}
}
return new ByteArrayInputStream(Strings.getBytesUtf8(content.toString()));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy