| /******************************************************************************* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.ofbiz.base.util; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.OutputStreamWriter; |
| import java.net.HttpURLConnection; |
| import java.net.URL; |
| import java.net.URLConnection; |
| import java.security.cert.CertificateException; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Send HTTP GET/POST requests. |
| * The main problem with current implementation is that it does not handle connections release. You must rely on the SO to release them (timeout). |
| * |
| */ |
| public class HttpClient { |
| |
| public static final String module = HttpClient.class.getName(); |
| |
| private int hostVerification = SSLUtil.getHostCertNormalCheck(); |
| private int timeout = 30000; |
| private boolean debug = false; |
| private boolean lineFeed = true; |
| private boolean trustAny = false; |
| private boolean followRedirects = true; |
| private boolean keepAlive = false; |
| |
| private String contentType = null; |
| private String streamCharset = null; |
| private String url = null; |
| private String rawStream = null; |
| private String clientCertAlias = null; |
| private String basicAuthUsername = null; |
| private String basicAuthPassword = null; |
| |
| private Map<String, Object> parameters = null; |
| private Map<String, String> headers = null; |
| |
| private URL requestUrl = null; |
| private URLConnection con = null; |
| |
| /** Creates an empty HttpClient object. */ |
| public HttpClient() {} |
| |
| /** Creates a new HttpClient object. */ |
| public HttpClient(URL url) { |
| this.url = url.toExternalForm(); |
| } |
| |
| /** Creates a new HttpClient object. */ |
| public HttpClient(String url) { |
| this.url = url; |
| } |
| |
| /** Creates a new HttpClient object. */ |
| public HttpClient(String url, Map<String, Object> parameters) { |
| this.url = url; |
| this.parameters = parameters; |
| } |
| |
| /** Creates a new HttpClient object. */ |
| public HttpClient(URL url, Map<String, Object> parameters) { |
| this.url = url.toExternalForm(); |
| this.parameters = parameters; |
| } |
| |
| /** Creates a new HttpClient object. */ |
| public HttpClient(String url, Map<String, Object> parameters, Map<String, String> headers) { |
| this.url = url; |
| this.parameters = parameters; |
| this.headers = headers; |
| } |
| |
| /** Creates a new HttpClient object. */ |
| public HttpClient(URL url, Map<String, Object> parameters, Map<String, String> headers) { |
| this.url = url.toExternalForm(); |
| this.parameters = parameters; |
| this.headers = headers; |
| } |
| |
| /** When true overrides Debug.verboseOn() and forces debugging for this instance */ |
| public void setDebug(boolean debug) { |
| this.debug = debug; |
| } |
| |
| /** Sets the timeout for waiting for the connection (default 30sec) */ |
| public void setTimeout(int timeout) { |
| this.timeout = timeout; |
| } |
| |
| /** Enables this request to follow redirect 3xx codes (default true) */ |
| public void followRedirects(boolean followRedirects) { |
| this.followRedirects = followRedirects; |
| } |
| |
| /** Turns on or off line feeds in the request. (default is on) */ |
| public void setLineFeed(boolean lineFeed) { |
| this.lineFeed = lineFeed; |
| } |
| |
| /** Set the raw stream for posts. */ |
| public void setRawStream(String stream) { |
| this.rawStream = stream; |
| } |
| |
| /** Set the URL for this request. */ |
| public void setUrl(URL url) { |
| this.url = url.toExternalForm(); |
| } |
| |
| /** Set the URL for this request. */ |
| public void setUrl(String url) { |
| this.url = url; |
| } |
| |
| /** Set the parameters for this request. */ |
| public void setParameters(Map<String, Object> parameters) { |
| this.parameters = parameters; |
| } |
| |
| /** Set an individual parameter for this request. */ |
| public void setParameter(String name, String value) { |
| if (parameters == null) |
| parameters = new HashMap<String, Object>(); |
| parameters.put(name, value); |
| } |
| |
| /** Set the headers for this request. */ |
| public void setHeaders(Map<String, String> headers) { |
| this.headers = headers; |
| } |
| |
| /** Set an individual header for this request. */ |
| public void setHeader(String name, String value) { |
| if (headers == null) |
| headers = new HashMap<String, String>(); |
| headers.put(name, value); |
| } |
| |
| /** Return a Map of headers. */ |
| public Map<String, String> getHeaders() { |
| return headers; |
| } |
| |
| /** Return a Map of parameters. */ |
| public Map<String, Object> getParameters() { |
| return parameters; |
| } |
| |
| /** Return a string representing the requested URL. */ |
| public String getUrl() { |
| return url; |
| } |
| |
| /** Sets the content-type */ |
| public void setContentType(String contentType) { |
| this.contentType = contentType; |
| } |
| |
| /** Returns the content type */ |
| public String getContentType() { |
| return this.contentType; |
| } |
| |
| /** Sets the scream charset */ |
| public void setStreamCharset(String streamCharset) { |
| this.streamCharset = streamCharset; |
| } |
| |
| /** Returns the stream charset */ |
| public String getStreamCharset() { |
| return this.streamCharset; |
| } |
| |
| /** Toggle keep-alive setting */ |
| public void setKeepAlive(boolean keepAlive) { |
| this.keepAlive = keepAlive; |
| } |
| |
| /** Return keep-alive setting */ |
| public boolean getKeepAlive() { |
| return this.keepAlive; |
| } |
| |
| /** Sets the client certificate alias (from the keystore) to use for this SSL connection. */ |
| public void setClientCertificateAlias(String alias) { |
| this.clientCertAlias = alias; |
| } |
| |
| /** Returns the alias of the client certificate to be used for this SSL connection. */ |
| public String getClientCertificateAlias() { |
| return this.clientCertAlias; |
| } |
| |
| /** Sets the server hostname verification level */ |
| public void setHostVerificationLevel(int level) { |
| this.hostVerification = level; |
| } |
| |
| /** Returns the current server hostname verification level */ |
| public int getHostVerificationLevel() { |
| return this.hostVerification; |
| } |
| |
| /** Allow untrusted server certificates */ |
| public void setAllowUntrusted(boolean trustAny) { |
| this.trustAny = trustAny; |
| } |
| |
| /** Do we trust any certificate */ |
| public boolean getAllowUntrusted() { |
| return this.trustAny; |
| } |
| |
| public void setBasicAuthInfo(String basicAuthUsername, String basicAuthPassword) { |
| this.basicAuthUsername = basicAuthUsername; |
| this.basicAuthPassword = basicAuthPassword; |
| } |
| |
| /** Invoke HTTP request GET. */ |
| public String get() throws HttpClientException { |
| return sendHttpRequest("get"); |
| } |
| |
| /** Invoke HTTP request GET. */ |
| public InputStream getStream() throws HttpClientException { |
| return sendHttpRequestStream("get"); |
| } |
| |
| /** Invoke HTTP request POST. */ |
| public String post() throws HttpClientException { |
| return sendHttpRequest("post"); |
| } |
| |
| /** Invoke HTTP request POST and pass raw stream. */ |
| public String post(String stream) throws HttpClientException { |
| this.rawStream = stream; |
| return sendHttpRequest("post"); |
| } |
| |
| /** Invoke HTTP request POST. */ |
| public InputStream postStream() throws HttpClientException { |
| return sendHttpRequestStream("post"); |
| } |
| |
| /** Returns the value of the specified named response header field. */ |
| public String getResponseHeader(String header) throws HttpClientException { |
| if (con == null) { |
| throw new HttpClientException("Connection not yet established"); |
| } |
| return con.getHeaderField(header); |
| } |
| |
| /** Returns the key for the nth response header field. */ |
| public String getResponseHeaderFieldKey(int n) throws HttpClientException { |
| if (con == null) { |
| throw new HttpClientException("Connection not yet established"); |
| } |
| return con.getHeaderFieldKey(n); |
| } |
| |
| /** Returns the value for the nth response header field. It returns null of there are fewer then n fields. */ |
| public String getResponseHeaderField(int n) throws HttpClientException { |
| if (con == null) { |
| throw new HttpClientException("Connection not yet established"); |
| } |
| return con.getHeaderField(n); |
| } |
| |
| /** Returns the content of the response. */ |
| public Object getResponseContent() throws java.io.IOException, HttpClientException { |
| if (con == null) { |
| throw new HttpClientException("Connection not yet established"); |
| } |
| return con.getContent(); |
| } |
| |
| /** Returns the content-type of the response. */ |
| public String getResponseContentType() throws HttpClientException { |
| if (con == null) { |
| throw new HttpClientException("Connection not yet established"); |
| } |
| return con.getContentType(); |
| } |
| |
| /** Returns the content length of the response */ |
| public int getResponseContentLength() throws HttpClientException { |
| if (con == null) { |
| throw new HttpClientException("Connection not yet established"); |
| } |
| return con.getContentLength(); |
| } |
| |
| /** Returns the content encoding of the response. */ |
| public String getResponseContentEncoding() throws HttpClientException { |
| if (con == null) { |
| throw new HttpClientException("Connection not yet established"); |
| } |
| return con.getContentEncoding(); |
| } |
| |
| public int getResponseCode() throws HttpClientException { |
| if (con == null) { |
| throw new HttpClientException("Connection not yet established"); |
| } |
| if (!(con instanceof HttpURLConnection)) { |
| throw new HttpClientException("Connection is not HTTP; no response code"); |
| } |
| |
| try { |
| return ((HttpURLConnection) con).getResponseCode(); |
| } catch (IOException e) { |
| throw new HttpClientException(e.getMessage(), e); |
| } |
| } |
| |
| public String sendHttpRequest(String method) throws HttpClientException { |
| InputStream in = sendHttpRequestStream(method); |
| if (in == null) return null; |
| |
| StringBuilder buf = new StringBuilder(); |
| try { |
| if (Debug.verboseOn() || debug) { |
| try { |
| Debug.logInfo("ContentEncoding: " + con.getContentEncoding() + "; ContentType: " + |
| con.getContentType() + " or: " + URLConnection.guessContentTypeFromStream(in), module); |
| } catch (IOException ioe) { |
| Debug.logWarning(ioe, "Caught exception printing content debugging information", module); |
| } |
| } |
| |
| String charset = null; |
| String contentType = con.getContentType(); |
| if (contentType == null) { |
| try { |
| contentType = URLConnection.guessContentTypeFromStream(in); |
| } catch (IOException ioe) { |
| Debug.logWarning(ioe, "Problems guessing content type from steam", module); |
| } |
| } |
| |
| if (Debug.verboseOn() || debug) Debug.logVerbose("Content-Type: " + contentType, module); |
| |
| if (contentType != null) { |
| contentType = contentType.toUpperCase(); |
| int charsetEqualsLoc = contentType.indexOf("=", contentType.indexOf("CHARSET")); |
| int afterSemiColon = contentType.indexOf(";", charsetEqualsLoc); |
| if (charsetEqualsLoc >= 0 && afterSemiColon >= 0) { |
| charset = contentType.substring(charsetEqualsLoc + 1, afterSemiColon); |
| } else if (charsetEqualsLoc >= 0) { |
| charset = contentType.substring(charsetEqualsLoc + 1); |
| } |
| |
| if (charset != null) charset = charset.trim().replaceAll("\"", ""); |
| if (Debug.verboseOn() || debug) Debug.logVerbose("Getting text from HttpClient with charset: " + charset, module); |
| } |
| |
| BufferedReader post = new BufferedReader(charset == null ? new InputStreamReader(in) : new InputStreamReader(in, charset)); |
| String line = ""; |
| |
| if (Debug.verboseOn() || debug) Debug.logVerbose("---- HttpClient Response Content ----", module); |
| while ((line = post.readLine()) != null) { |
| if (Debug.verboseOn() || debug) Debug.logVerbose("[HttpClient] : " + line, module); |
| buf.append(line); |
| if (lineFeed) { |
| buf.append("\n"); |
| } |
| } |
| } catch (Exception e) { |
| throw new HttpClientException("Error processing input stream", e); |
| } |
| return buf.toString(); |
| } |
| |
| private InputStream sendHttpRequestStream(String method) throws HttpClientException { |
| return sendHttpRequestStream(method, false); |
| } |
| |
| private InputStream sendHttpRequestStream(String method, boolean overrideTrust) throws HttpClientException { |
| // setup some SSL variables |
| SSLUtil.loadJsseProperties(this.debug); |
| |
| String arguments = null; |
| InputStream in = null; |
| |
| if (url == null) { |
| throw new HttpClientException("Cannot process a null URL."); |
| } |
| |
| if (rawStream != null) { |
| arguments = rawStream; |
| } else if (UtilValidate.isNotEmpty(parameters)) { |
| arguments = UtilHttp.urlEncodeArgs(parameters, false); |
| } |
| |
| // Append the arguments to the query string if GET. |
| if (method.equalsIgnoreCase("get") && arguments != null) { |
| if (url.contains("?")) { |
| url = url + "&" + arguments; |
| } else { |
| url = url + "?" + arguments; |
| } |
| } |
| |
| // Create the URL and open the connection. |
| try { |
| requestUrl = new URL(url); |
| if (overrideTrust) { |
| con = URLConnector.openUntrustedConnection(requestUrl, timeout, clientCertAlias, hostVerification); |
| } else { |
| con = URLConnector.openConnection(requestUrl, timeout, clientCertAlias, hostVerification); |
| } |
| if (Debug.verboseOn() || debug) Debug.logVerbose("Connection opened to : " + requestUrl.toExternalForm(), module); |
| |
| if ((con instanceof HttpURLConnection)) { |
| ((HttpURLConnection) con).setInstanceFollowRedirects(followRedirects); |
| if (Debug.verboseOn() || debug) Debug.logVerbose("Connection is of type HttpURLConnection, more specifically: " + con.getClass().getName(), module); |
| } |
| |
| // set the content type |
| if (contentType != null) { |
| con.setRequestProperty("Content-type", contentType); |
| } |
| |
| // connection settings |
| con.setDoOutput(true); |
| con.setUseCaches(false); |
| if (keepAlive) { |
| con.setRequestProperty("Connection", "Keep-Alive"); |
| } |
| |
| if (method.equalsIgnoreCase("post")) { |
| if (contentType == null) { |
| con.setRequestProperty("Content-type", "application/x-www-form-urlencoded"); |
| } |
| con.setDoInput(true); |
| } |
| |
| // if there is basicAuth info set the request property for it |
| if (basicAuthUsername != null) { |
| String basicAuthString = "Basic " + Base64.base64Encode(basicAuthUsername + ":" + (basicAuthPassword == null ? "" : basicAuthPassword)); |
| con.setRequestProperty("Authorization", basicAuthString); |
| if (Debug.verboseOn() || debug) Debug.logVerbose("Header - Authorization: " + basicAuthString, module); |
| } |
| |
| if (UtilValidate.isNotEmpty(headers)) { |
| for (Map.Entry<String, String> entry: headers.entrySet()) { |
| String headerName = entry.getKey(); |
| String headerValue = entry.getValue(); |
| con.setRequestProperty(headerName, headerValue); |
| if (Debug.verboseOn() || debug) Debug.logVerbose("Header - " + headerName + ": " + headerValue, module); |
| } |
| } |
| |
| if (method.equalsIgnoreCase("post")) { |
| OutputStreamWriter out = new OutputStreamWriter(con.getOutputStream(), this.streamCharset != null ? this.streamCharset : "UTF-8"); |
| if (Debug.verboseOn() || debug) Debug.logVerbose("Opened output stream", module); |
| |
| if (arguments != null) { |
| out.write(arguments); |
| if (Debug.verboseOn() || debug) Debug.logVerbose("Wrote arguements (parameters) : " + arguments, module); |
| } |
| |
| out.flush(); |
| out.close(); |
| if (Debug.verboseOn() || debug) Debug.logVerbose("Flushed and closed buffer", module); |
| } |
| |
| if (Debug.verboseOn() || debug) { |
| Map<String, List<String>> headerFields = con.getHeaderFields(); |
| Debug.logInfo("Header Fields : " + headerFields, module); |
| } |
| |
| in = con.getInputStream(); |
| } catch (IOException ioe) { |
| if ((trustAny && !overrideTrust) && (ioe.getCause() instanceof CertificateException)) { |
| Debug.logWarning(ioe.getCause(), module); |
| return sendHttpRequestStream(method, true); |
| } |
| throw new HttpClientException("IO Error processing request", ioe); |
| } catch (Exception e) { |
| throw new HttpClientException("Error processing request", e); |
| } |
| |
| return in; |
| } |
| |
| |
| public static String getUrlContent(String url) throws HttpClientException { |
| HttpClient client = new HttpClient(url); |
| return client.get(); |
| } |
| |
| public static int checkHttpRequest(String url) throws HttpClientException { |
| HttpClient client = new HttpClient(url); |
| client.get(); |
| return client.getResponseCode(); |
| } |
| } |