1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.util;
24  
25  import com.liferay.portal.kernel.util.ByteArrayMaker;
26  import com.liferay.portal.kernel.util.GetterUtil;
27  import com.liferay.portal.kernel.util.StringMaker;
28  import com.liferay.portal.kernel.util.StringPool;
29  import com.liferay.portal.kernel.util.StringUtil;
30  import com.liferay.portal.kernel.util.Validator;
31  
32  import java.io.IOException;
33  import java.io.InputStream;
34  
35  import java.net.URL;
36  import java.net.URLConnection;
37  
38  import java.util.ArrayList;
39  import java.util.Iterator;
40  import java.util.List;
41  import java.util.Map;
42  import java.util.StringTokenizer;
43  import java.util.regex.Pattern;
44  
45  import javax.portlet.ActionRequest;
46  import javax.portlet.RenderRequest;
47  
48  import javax.servlet.http.HttpServletRequest;
49  
50  import org.apache.commons.httpclient.Cookie;
51  import org.apache.commons.httpclient.Credentials;
52  import org.apache.commons.httpclient.Header;
53  import org.apache.commons.httpclient.HostConfiguration;
54  import org.apache.commons.httpclient.HttpClient;
55  import org.apache.commons.httpclient.HttpMethod;
56  import org.apache.commons.httpclient.HttpState;
57  import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
58  import org.apache.commons.httpclient.NTCredentials;
59  import org.apache.commons.httpclient.NameValuePair;
60  import org.apache.commons.httpclient.URI;
61  import org.apache.commons.httpclient.UsernamePasswordCredentials;
62  import org.apache.commons.httpclient.auth.AuthPolicy;
63  import org.apache.commons.httpclient.auth.AuthScope;
64  import org.apache.commons.httpclient.cookie.CookiePolicy;
65  import org.apache.commons.httpclient.methods.GetMethod;
66  import org.apache.commons.httpclient.methods.PostMethod;
67  import org.apache.commons.httpclient.params.HttpConnectionParams;
68  import org.apache.commons.logging.Log;
69  import org.apache.commons.logging.LogFactory;
70  
71  /**
72   * <a href="Http.java.html"><b><i>View Source</i></b></a>
73   *
74   * @author Brian Wing Shun Chan
75   *
76   */
77  public class Http {
78  
79      public static final String HTTP = "http";
80  
81      public static final String HTTPS = "https";
82  
83      public static final String HTTP_WITH_SLASH = "http://";
84  
85      public static final String HTTPS_WITH_SLASH = "https://";
86  
87      public static final int HTTP_PORT = 80;
88  
89      public static final int HTTPS_PORT = 443;
90  
91      public static final String LIFERAY_PROXY_HOST = GetterUtil.getString(
92          SystemProperties.get(Http.class.getName() + ".proxy.host"));
93  
94      public static final int LIFERAY_PROXY_PORT = GetterUtil.getInteger(
95          SystemProperties.get(Http.class.getName() + ".proxy.port"));
96  
97      private static final int MAX_CONNECTIONS_PER_HOST = GetterUtil.getInteger(
98          SystemProperties.get(
99              Http.class.getName() + ".max.connections.per.host"),
100         2);
101 
102     private static final int MAX_TOTAL_CONNECTIONS = GetterUtil.getInteger(
103         SystemProperties.get(Http.class.getName() + ".max.total.connections"),
104         20);
105 
106     public static final String PROXY_HOST = GetterUtil.getString(
107         SystemProperties.get("http.proxyHost"), LIFERAY_PROXY_HOST);
108 
109     public static final int PROXY_PORT = GetterUtil.getInteger(
110         SystemProperties.get("http.proxyPort"), LIFERAY_PROXY_PORT);
111 
112     public static final String NON_PROXY_HOSTS =
113         SystemProperties.get("http.nonProxyHosts");
114 
115     public static final String PROXY_AUTH_TYPE = GetterUtil.getString(
116         SystemProperties.get(Http.class.getName() + ".proxy.auth.type"));
117 
118     public static final String PROXY_USERNAME = GetterUtil.getString(
119         SystemProperties.get(Http.class.getName() + ".proxy.username"));
120 
121     public static final String PROXY_PASSWORD = GetterUtil.getString(
122         SystemProperties.get(Http.class.getName() + ".proxy.password"));
123 
124     public static final String PROXY_NTLM_DOMAIN = GetterUtil.getString(
125         SystemProperties.get(Http.class.getName() + ".proxy.ntlm.domain"));
126 
127     public static final String PROXY_NTLM_HOST = GetterUtil.getString(
128         SystemProperties.get(Http.class.getName() + ".proxy.ntlm.host"));
129 
130     public static final int TIMEOUT = GetterUtil.getInteger(
131         SystemProperties.get(Http.class.getName() + ".timeout"), 5000);
132 
133     public static String addParameter(String url, String name, boolean value) {
134         return addParameter(url, name, String.valueOf(value));
135     }
136 
137     public static String addParameter(String url, String name, double value) {
138         return addParameter(url, name, String.valueOf(value));
139     }
140 
141     public static String addParameter(String url, String name, int value) {
142         return addParameter(url, name, String.valueOf(value));
143     }
144 
145     public static String addParameter(String url, String name, long value) {
146         return addParameter(url, name, String.valueOf(value));
147     }
148 
149     public static String addParameter(String url, String name, short value) {
150         return addParameter(url, name, String.valueOf(value));
151     }
152 
153     public static String addParameter(String url, String name, String value) {
154         if (url == null) {
155             return null;
156         }
157 
158         if (url.indexOf(StringPool.QUESTION) == -1) {
159             url += StringPool.QUESTION;
160         }
161 
162         if (!url.endsWith(StringPool.QUESTION) &&
163             !url.endsWith(StringPool.AMPERSAND)) {
164 
165             url += StringPool.AMPERSAND;
166         }
167 
168         return url + name + StringPool.EQUAL + HttpUtil.encodeURL(value);
169     }
170 
171     /**
172      * @deprecated This method has been moved to
173      * <code>com.liferay.util.HttpUtil</code>.
174      */
175     public static String decodeURL(String url) {
176         return HttpUtil.decodeURL(url);
177     }
178 
179     /**
180      * @deprecated This method has been moved to
181      * <code>com.liferay.util.HttpUtil</code>.
182      */
183     public static String encodeURL(String url) {
184         return HttpUtil.encodeURL(url);
185     }
186 
187     public static HttpClient getClient(HostConfiguration hostConfig)
188         throws IOException {
189 
190         return _instance._getClient(hostConfig);
191     }
192 
193     public static String getCompleteURL(HttpServletRequest req) {
194         StringBuffer completeURL = req.getRequestURL();
195 
196         if (completeURL == null) {
197             completeURL = new StringBuffer();
198         }
199 
200         if (req.getQueryString() != null) {
201             completeURL.append(StringPool.QUESTION);
202             completeURL.append(req.getQueryString());
203         }
204 
205         return completeURL.toString();
206     }
207 
208     public static HostConfiguration getHostConfig(String location)
209         throws IOException {
210 
211         if (_log.isDebugEnabled()) {
212             _log.debug("Location is " + location);
213         }
214 
215         HostConfiguration hostConfig = new HostConfiguration();
216 
217         hostConfig.setHost(new URI(location, false));
218 
219         if (isProxyHost(hostConfig.getHost())) {
220             hostConfig.setProxy(PROXY_HOST, PROXY_PORT);
221         }
222 
223         return hostConfig;
224     }
225 
226     public static String getParameter(String url, String name) {
227         return getParameter(url, name, true);
228     }
229 
230     public static String getParameter(
231         String url, String name, boolean escaped) {
232 
233         if (Validator.isNull(url) || Validator.isNull(name)) {
234             return StringPool.BLANK;
235         }
236 
237         String[] parts = StringUtil.split(url, StringPool.QUESTION);
238 
239         if (parts.length == 2) {
240             String[] params = null;
241 
242             if (escaped) {
243                 params = StringUtil.split(parts[1], "&amp;");
244             }
245             else {
246                 params = StringUtil.split(parts[1], StringPool.AMPERSAND);
247             }
248 
249             for (int i = 0; i < params.length; i++) {
250                 String[] kvp = StringUtil.split(params[i], StringPool.EQUAL);
251 
252                 if ((kvp.length == 2) && kvp[0].equals(name)) {
253                     return kvp[1];
254                 }
255             }
256         }
257 
258         return StringPool.BLANK;
259     }
260 
261     /**
262      * @deprecated This method has been moved to
263      * <code>com.liferay.util.HttpUtil</code>.
264      */
265     public static Map getParameterMap(String queryString) {
266         return HttpUtil.parameterMapFromString(queryString);
267     }
268 
269     public static String getProtocol(boolean secure) {
270         if (!secure) {
271             return HTTP;
272         }
273         else {
274             return HTTPS;
275         }
276     }
277 
278     public static String getProtocol(HttpServletRequest req) {
279         return getProtocol(req.isSecure());
280     }
281 
282     public static String getProtocol(ActionRequest req) {
283         return getProtocol(req.isSecure());
284     }
285 
286     public static String getProtocol(RenderRequest req) {
287         return getProtocol(req.isSecure());
288     }
289 
290     /**
291      * @deprecated This method has been moved to
292      * <code>com.liferay.util.HttpUtil</code>.
293      */
294     public static String getQueryString(String url) {
295         return HttpUtil.getQueryString(url);
296     }
297 
298     public static String getRequestURL(HttpServletRequest req) {
299         return req.getRequestURL().toString();
300     }
301 
302     public static boolean hasProxyConfig() {
303         if (Validator.isNotNull(PROXY_HOST) && (PROXY_PORT > 0)) {
304             return true;
305         }
306         else {
307             return false;
308         }
309     }
310 
311     public static boolean isNonProxyHost(String host) {
312         return _instance._isNonProxyHost(host);
313     }
314 
315     public static boolean isProxyHost(String host) {
316         if (hasProxyConfig() && !isNonProxyHost(host)) {
317             return true;
318         }
319         else {
320             return false;
321         }
322     }
323 
324     /**
325      * @deprecated This method has been moved to
326      * <code>com.liferay.util.HttpUtil</code>.
327      */
328     public static String parameterMapToString(Map parameterMap) {
329         return HttpUtil.parameterMapToString(parameterMap);
330     }
331 
332     /**
333      * @deprecated This method has been moved to
334      * <code>com.liferay.util.HttpUtil</code>.
335      */
336     public static String parameterMapToString(
337         Map parameterMap, boolean addQuestion) {
338 
339         return HttpUtil.parameterMapToString(parameterMap, addQuestion);
340     }
341 
342     public static String protocolize(String url, boolean secure) {
343         if (secure) {
344             if (url.startsWith(HTTP_WITH_SLASH)) {
345                 return StringUtil.replace(
346                     url, HTTP_WITH_SLASH, HTTPS_WITH_SLASH);
347             }
348         }
349         else {
350             if (url.startsWith(HTTPS_WITH_SLASH)) {
351                 return StringUtil.replace(
352                     url, HTTPS_WITH_SLASH, HTTP_WITH_SLASH);
353             }
354         }
355 
356         return url;
357     }
358 
359     public static String protocolize(String url, HttpServletRequest req) {
360         return protocolize(url, req.isSecure());
361     }
362 
363     public static String protocolize(String url, ActionRequest req) {
364         return protocolize(url, req.isSecure());
365     }
366 
367     public static String protocolize(String url, RenderRequest req) {
368         return protocolize(url, req.isSecure());
369     }
370 
371     public static void proxifyState(
372         HttpState state, HostConfiguration hostConfig) {
373 
374         Credentials proxyCredentials = _instance._proxyCredentials;
375 
376         String host = hostConfig.getHost();
377 
378         if (isProxyHost(host) && (proxyCredentials != null)) {
379             AuthScope scope = new AuthScope(PROXY_HOST, PROXY_PORT, null);
380 
381             state.setProxyCredentials(scope, proxyCredentials);
382         }
383     }
384 
385     public static String removeParameter(String url, String name) {
386         int pos = url.indexOf(StringPool.QUESTION);
387 
388         if (pos == -1) {
389             return url;
390         }
391 
392         StringMaker sm = new StringMaker();
393 
394         sm.append(url.substring(0, pos + 1));
395 
396         StringTokenizer st = new StringTokenizer(
397             url.substring(pos + 1, url.length()), StringPool.AMPERSAND);
398 
399         while (st.hasMoreTokens()) {
400             String token = st.nextToken();
401 
402             if (Validator.isNotNull(token)) {
403                 String[] kvp = StringUtil.split(token, StringPool.EQUAL);
404 
405                 String key = kvp[0];
406 
407                 String value = StringPool.BLANK;
408 
409                 if (kvp.length > 1) {
410                     value = kvp[1];
411                 }
412 
413                 if (!key.equals(name)) {
414                     sm.append(key);
415                     sm.append(StringPool.EQUAL);
416                     sm.append(value);
417                     sm.append(StringPool.AMPERSAND);
418                 }
419             }
420         }
421 
422         url = StringUtil.replace(
423             sm.toString(), StringPool.AMPERSAND + StringPool.AMPERSAND,
424             StringPool.AMPERSAND);
425 
426         return url;
427     }
428 
429     public static String removeProtocol(String url) {
430         if (url.startsWith(HTTP_WITH_SLASH)) {
431             return url.substring(HTTP_WITH_SLASH.length() , url.length());
432         }
433         else if (url.startsWith(HTTPS_WITH_SLASH)) {
434             return url.substring(HTTPS_WITH_SLASH.length() , url.length());
435         }
436         else {
437             return url;
438         }
439     }
440 
441     public static void submit(String location) throws IOException {
442         submit(location, null);
443     }
444 
445     public static void submit(String location, Cookie[] cookies)
446         throws IOException {
447 
448         submit(location, cookies, false);
449     }
450 
451     public static void submit(String location, boolean post)
452         throws IOException {
453 
454         submit(location, null, post);
455     }
456 
457     public static void submit(
458             String location, Cookie[] cookies, boolean post)
459         throws IOException {
460 
461         URLtoByteArray(location, cookies, post);
462     }
463 
464     public static void submit(
465             String location, Cookie[] cookies, Map parts, boolean post)
466         throws IOException {
467 
468         URLtoByteArray(location, cookies, parts, post);
469     }
470 
471     public static byte[] URLtoByteArray(String location)
472         throws IOException {
473 
474         return URLtoByteArray(location, null);
475     }
476 
477     public static byte[] URLtoByteArray(String location, Cookie[] cookies)
478         throws IOException {
479 
480         return URLtoByteArray(location, cookies, false);
481     }
482 
483     public static byte[] URLtoByteArray(String location, boolean post)
484         throws IOException {
485 
486         return URLtoByteArray(location, null, post);
487     }
488 
489     public static byte[] URLtoByteArray(
490             String location, Cookie[] cookies, boolean post)
491         throws IOException {
492 
493         return URLtoByteArray(location, cookies, null, post);
494     }
495 
496     public static byte[] URLtoByteArray(
497             String location, Cookie[] cookies, Map parts, boolean post)
498         throws IOException {
499 
500         byte[] byteArray = null;
501 
502         HttpMethod method = null;
503 
504         try {
505             if (location == null) {
506                 return byteArray;
507             }
508             else if (!location.startsWith(HTTP_WITH_SLASH) &&
509                      !location.startsWith(HTTPS_WITH_SLASH)) {
510 
511                 location = HTTP_WITH_SLASH + location;
512             }
513 
514             HostConfiguration hostConfig = getHostConfig(location);
515 
516             HttpClient client = getClient(hostConfig);
517 
518             if (post) {
519                 method = new PostMethod(location);
520 
521                 if ((parts != null) && (parts.size() > 0)) {
522                     List nvpList = new ArrayList();
523 
524                     Iterator itr = parts.entrySet().iterator();
525 
526                     while (itr.hasNext()) {
527                         Map.Entry entry = (Map.Entry)itr.next();
528 
529                         String key = (String)entry.getKey();
530                         String value = (String)entry.getValue();
531 
532                         if (value != null) {
533                             nvpList.add(new NameValuePair(key, value));
534                         }
535                     }
536 
537                     NameValuePair[] nvpArray = (NameValuePair[])nvpList.toArray(
538                         new NameValuePair[nvpList.size()]);
539 
540                     PostMethod postMethod = (PostMethod)method;
541 
542                     postMethod.setRequestBody(nvpArray);
543                 }
544             }
545             else {
546                 method = new GetMethod(location);
547             }
548 
549             method.addRequestHeader(
550                 "Content-Type", "application/x-www-form-urlencoded");
551 
552             method.addRequestHeader(
553                 "User-agent",
554                 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)");
555 
556             //method.setFollowRedirects(true);
557 
558             HttpState state = new HttpState();
559 
560             if ((cookies != null) && (cookies.length > 0)) {
561                 state.addCookies(cookies);
562 
563                 method.getParams().setCookiePolicy(
564                     CookiePolicy.BROWSER_COMPATIBILITY);
565             }
566 
567             proxifyState(state, hostConfig);
568 
569             client.executeMethod(hostConfig, method, state);
570 
571             Header locationHeader = method.getResponseHeader("location");
572 
573             if (locationHeader != null) {
574                 return URLtoByteArray(locationHeader.getValue(), cookies, post);
575             }
576 
577             InputStream is = method.getResponseBodyAsStream();
578 
579             if (is != null) {
580                 ByteArrayMaker bam = new ByteArrayMaker();
581                 byte[] bytes = new byte[512];
582 
583                 for (int i = is.read(bytes, 0, 512); i != -1;
584                         i = is.read(bytes, 0, 512)) {
585 
586                     bam.write(bytes, 0, i);
587                 }
588 
589                 byteArray = bam.toByteArray();
590 
591                 is.close();
592                 bam.close();
593             }
594 
595             return byteArray;
596         }
597         finally {
598             try {
599                 if (method != null) {
600                     method.releaseConnection();
601                 }
602             }
603             catch (Exception e) {
604                 _log.error(e, e);
605             }
606         }
607     }
608 
609     public static String URLtoString(String location)
610         throws IOException {
611 
612         return URLtoString(location, null);
613     }
614 
615     public static String URLtoString(String location, Cookie[] cookies)
616         throws IOException {
617 
618         return URLtoString(location, cookies, false);
619     }
620 
621     public static String URLtoString(String location, boolean post)
622         throws IOException {
623 
624         return URLtoString(location, null, post);
625     }
626 
627     public static String URLtoString(
628             String location, Cookie[] cookies, boolean post)
629         throws IOException {
630 
631         return new String(URLtoByteArray(location, cookies, post));
632     }
633 
634     public static String URLtoString(
635             String location, Cookie[] cookies, Map parts, boolean post)
636         throws IOException {
637 
638         return new String(URLtoByteArray(location, cookies, parts, post));
639     }
640 
641     /**
642      * This method only uses the default Commons HttpClient implementation when
643      * the URL object represents a HTTP resource. The URL object could also
644      * represent a file or some JNDI resource. In that case, the default Java
645      * implementation is used.
646      *
647      * @param       url URL object
648      * @return      A string representation of the resource referenced by the
649      *              URL object
650      * @throws      IOException
651      */
652     public static String URLtoString(URL url) throws IOException {
653         String xml = null;
654 
655         if (url != null) {
656             String protocol = url.getProtocol().toLowerCase();
657 
658             if (protocol.startsWith(HTTP) || protocol.startsWith(HTTPS)) {
659                 return URLtoString(url.toString());
660             }
661 
662             URLConnection con = url.openConnection();
663 
664             InputStream is = con.getInputStream();
665 
666             ByteArrayMaker bam = new ByteArrayMaker();
667             byte[] bytes = new byte[512];
668 
669             for (int i = is.read(bytes, 0, 512); i != -1;
670                     i = is.read(bytes, 0, 512)) {
671 
672                 bam.write(bytes, 0, i);
673             }
674 
675             xml = new String(bam.toByteArray());
676 
677             is.close();
678             bam.close();
679         }
680 
681         return xml;
682     }
683 
684     private Http() {
685 
686         // Mimic behavior found in
687         // http://java.sun.com/j2se/1.5.0/docs/guide/net/properties.html
688 
689         if (Validator.isNotNull(NON_PROXY_HOSTS)) {
690             String nonProxyHostsRegEx = NON_PROXY_HOSTS;
691 
692             nonProxyHostsRegEx = nonProxyHostsRegEx.replaceAll(
693                 "\\.", "\\\\.");
694             nonProxyHostsRegEx = nonProxyHostsRegEx.replaceAll(
695                 "\\*", ".*?");
696             nonProxyHostsRegEx = nonProxyHostsRegEx.replaceAll(
697                 "\\|", ")|(");
698 
699             nonProxyHostsRegEx = "(" + nonProxyHostsRegEx + ")";
700 
701             _nonProxyHostsPattern = Pattern.compile(nonProxyHostsRegEx);
702         }
703 
704         MultiThreadedHttpConnectionManager connectionManager =
705             new MultiThreadedHttpConnectionManager();
706 
707         HttpConnectionParams params = connectionManager.getParams();
708 
709         params.setParameter(
710             "maxConnectionsPerHost", new Integer(MAX_CONNECTIONS_PER_HOST));
711         params.setParameter(
712             "maxTotalConnections", new Integer(MAX_TOTAL_CONNECTIONS));
713         params.setConnectionTimeout(TIMEOUT);
714         params.setSoTimeout(TIMEOUT);
715 
716         _client.setHttpConnectionManager(connectionManager);
717         _proxyClient.setHttpConnectionManager(connectionManager);
718 
719         if (hasProxyConfig() && Validator.isNotNull(PROXY_USERNAME)) {
720             if (PROXY_AUTH_TYPE.equals("username-password")) {
721                 _proxyCredentials = new UsernamePasswordCredentials(
722                     PROXY_USERNAME, PROXY_PASSWORD);
723             }
724             else if (PROXY_AUTH_TYPE.equals("ntlm")) {
725                 _proxyCredentials = new NTCredentials(
726                     PROXY_USERNAME, PROXY_PASSWORD, PROXY_NTLM_HOST,
727                     PROXY_NTLM_DOMAIN);
728 
729                 List authPrefs = new ArrayList();
730 
731                 authPrefs.add(AuthPolicy.NTLM);
732                 authPrefs.add(AuthPolicy.BASIC);
733                 authPrefs.add(AuthPolicy.DIGEST);
734 
735                 _proxyClient.getParams().setParameter(
736                     AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);
737             }
738         }
739     }
740 
741     private HttpClient _getClient(HostConfiguration hostConfig)
742         throws IOException {
743 
744         if (isProxyHost(hostConfig.getHost())) {
745             return _proxyClient;
746         }
747         else {
748             return _client;
749         }
750     }
751 
752     private boolean _isNonProxyHost(String host) {
753         if (_nonProxyHostsPattern == null ||
754             _nonProxyHostsPattern.matcher(host).matches()) {
755 
756             return true;
757         }
758         else {
759             return false;
760         }
761     }
762 
763     private static Log _log = LogFactory.getLog(Http.class);
764 
765     private static Http _instance = new Http();
766 
767     private HttpClient _client = new HttpClient();
768     private HttpClient _proxyClient = new HttpClient();
769     private Credentials _proxyCredentials;
770     private Pattern _nonProxyHostsPattern;
771 
772 }