001: /*
002: * Copyright 2001-2007 Geert Bevin <gbevin[remove] at uwyn dot com>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: * $Id: HttpUtils.java 3653 2007-02-05 15:09:25Z gbevin $
007: */
008: package com.uwyn.rife.tools;
009:
010: import java.io.*;
011: import java.util.*;
012: import javax.net.ssl.*;
013:
014: import java.net.HttpURLConnection;
015: import java.net.URL;
016: import java.security.KeyManagementException;
017: import java.security.NoSuchAlgorithmException;
018: import java.security.cert.CertificateException;
019: import java.security.cert.X509Certificate;
020: import java.util.regex.Matcher;
021: import java.util.regex.Pattern;
022: import java.util.zip.GZIPInputStream;
023: import javax.servlet.http.Cookie;
024:
025: public abstract class HttpUtils {
026: private static final String METHOD_GET = "GET";
027: private static final String METHOD_POST = "POST";
028:
029: private static final String HEADER_COOKIE = "Cookie";
030: private static final String HEADER_CONTENTTYPE = "Content-Type";
031: private static final String HEADER_CONTENTLENGTH = "Content-Length";
032: private static final String HEADER_SETCOOKIE = "Set-Cookie";
033:
034: private static final String CONTENTTYPE_FORM_URLENCODED = "application/x-www-form-urlencoded";
035:
036: public static final String CHARSET = "charset=";
037: public static final Pattern CHARSET_PATTERN = Pattern
038: .compile(";\\s*" + CHARSET + "(.*)$");
039:
040: /**
041: * Extracts only the mime-type from a Content-Type HTTP header. Thus a header
042: * like this: <code>text/html;charset=UTF-8</code> will return: <code>text/html</code>
043: *
044: * @param contentType the Content-Type header
045: *
046: * @return the content type header without the charset
047: * @since 1.6
048: */
049: public static String extractMimeTypeFromContentType(
050: String contentType) {
051: int charset_index = contentType.indexOf(CHARSET);
052: if (charset_index != -1) {
053: char indexed_char;
054: do {
055: indexed_char = contentType.charAt(--charset_index);
056: } while (' ' == indexed_char);
057:
058: if (';' == indexed_char) {
059: return contentType.substring(0, charset_index);
060: }
061: }
062:
063: return contentType;
064: }
065:
066: public static String getConnectionContent(
067: HttpURLConnection connection) throws IOException {
068: // content character set
069: String charset = StringUtils.ENCODING_ISO_8859_1;
070: String content_type = connection.getContentType();
071: if (content_type != null) {
072: Matcher content_type_matcher = CHARSET_PATTERN
073: .matcher(content_type);
074: if (content_type_matcher.find()) {
075: charset = content_type_matcher.group(1);
076: }
077: }
078:
079: ByteArrayOutputStream byte_output = new ByteArrayOutputStream();
080: if (!getConnectionContent(connection, byte_output)) {
081: return null;
082: }
083:
084: byte[] bytes = byte_output.toByteArray();
085: return new String(bytes, charset);
086: }
087:
088: public static boolean getConnectionContent(
089: HttpURLConnection connection, OutputStream output)
090: throws IOException {
091: InputStream input = null;
092: try {
093: input = connection.getInputStream();
094: } catch (FileNotFoundException e) {
095: return false;
096: }
097:
098: // headers
099: Map header_fields = connection.getHeaderFields();
100:
101: // content encoding
102: List content_encoding = (List) header_fields
103: .get("Content-Encoding");
104: if (content_encoding != null
105: && content_encoding.contains("gzip")) {
106: input = new GZIPInputStream(input);
107: }
108:
109: BufferedInputStream buffered_input = new BufferedInputStream(
110: input);
111:
112: byte[] buffer = new byte[8 * 1024];
113: int count = 0;
114: do {
115: output.write(buffer, 0, count);
116: count = buffered_input.read(buffer, 0, buffer.length);
117: } while (-1 != count);
118:
119: return true;
120: }
121:
122: public static Page retrievePage(String absoluteUrl)
123: throws IOException {
124: return retrievePage(absoluteUrl, true);
125: }
126:
127: public static Page retrievePage(String absoluteUrl,
128: boolean downloadContent) throws IOException {
129: return new Request(absoluteUrl).retrieve(downloadContent);
130: }
131:
132: public static class Request {
133: private String mUrl = null;
134: private String mMethod = null;
135: private Map<String, String> mHeaders = null;
136: private StringBuilder mQueryParams = null;
137: private StringBuilder mPostParams = null;
138:
139: public Request(String absoluteUrl) {
140: if (null == absoluteUrl) {
141: absoluteUrl = "";
142: }
143:
144: mUrl = absoluteUrl;
145: }
146:
147: public Request method(String method) {
148: mMethod = method;
149:
150: return this ;
151: }
152:
153: public Request queryParams(String[][] queryParams) {
154: if (null != queryParams) {
155: if (null == mQueryParams) {
156: mQueryParams = new StringBuilder();
157: }
158:
159: params(mQueryParams, queryParams);
160: }
161:
162: return this ;
163: }
164:
165: public Request queryParams(Map<String, String[]> queryParams) {
166: if (null != queryParams) {
167: if (null == mQueryParams) {
168: mQueryParams = new StringBuilder();
169: }
170:
171: params(mQueryParams, queryParams);
172: }
173:
174: return this ;
175: }
176:
177: public Request queryParam(String key, String value) {
178: if (null == mQueryParams) {
179: mQueryParams = new StringBuilder();
180: }
181:
182: param(mQueryParams, key, value);
183:
184: return this ;
185: }
186:
187: public Request postParams(String[][] postParams) {
188: if (null != postParams) {
189: if (null == mPostParams) {
190: mPostParams = new StringBuilder();
191: }
192:
193: params(mPostParams, postParams);
194: }
195:
196: return this ;
197: }
198:
199: public Request postParams(Map<String, String[]> postParams) {
200: if (null != postParams) {
201: if (null == mPostParams) {
202: mPostParams = new StringBuilder();
203: }
204:
205: params(mPostParams, postParams);
206: }
207:
208: return this ;
209: }
210:
211: public Request postParam(String key, String value) {
212: if (null == mPostParams) {
213: mPostParams = new StringBuilder();
214: }
215:
216: param(mPostParams, key, value);
217:
218: return this ;
219: }
220:
221: private void param(StringBuilder store, String key, String value) {
222: if (null != key && null != value) {
223: if (store.length() > 0) {
224: store.append("&");
225: }
226: store.append(StringUtils.encodeUrl(key));
227: store.append("=");
228: store.append(StringUtils.encodeUrl(value));
229: }
230: }
231:
232: private void params(StringBuilder store, String[][] params) {
233: for (String[] param : params) {
234: for (int i = 1; i < param.length; i++) {
235: param(store, param[0], param[i]);
236: }
237: }
238: }
239:
240: private void params(StringBuilder store,
241: Map<String, String[]> params) {
242: for (Map.Entry<String, String[]> param_entry : params
243: .entrySet()) {
244: for (String value : param_entry.getValue()) {
245: param(store, param_entry.getKey(), value);
246: }
247: }
248: }
249:
250: public Request headers(String[][] headers) {
251: if (null != headers) {
252: if (null == mHeaders) {
253: mHeaders = new LinkedHashMap<String, String>();
254: }
255:
256: LinkedHashMap<String, List<String>> header_list_map = new LinkedHashMap<String, List<String>>();
257: for (String[] header : headers) {
258: if (null != header[0] && null != header[1]) {
259: List<String> header_values = header_list_map
260: .get(header[0]);
261: if (null == header_values) {
262: header_values = new ArrayList<String>();
263: header_list_map.put(header[0],
264: header_values);
265: }
266:
267: boolean first = true;
268: for (String header_value : header) {
269: if (first) {
270: first = false;
271: continue;
272: }
273:
274: header_values.add(header_value);
275: }
276: }
277: }
278:
279: List<String> headers_values = null;
280: for (String headers_name : header_list_map.keySet()) {
281: headers_values = header_list_map.get(headers_name);
282:
283: mHeaders.put(headers_name, StringUtils.join(
284: headers_values, ","));
285: }
286: }
287:
288: return this ;
289: }
290:
291: public Request headers(Map<String, String> headers) {
292: if (null != headers) {
293: if (null == mHeaders) {
294: mHeaders = new LinkedHashMap<String, String>();
295: }
296:
297: mHeaders.putAll(headers);
298: }
299:
300: return this ;
301: }
302:
303: public Request header(String name, String content) {
304: if (null != name && null != content) {
305: if (null == mHeaders) {
306: mHeaders = new LinkedHashMap<String, String>();
307: }
308:
309: mHeaders.put(name, content);
310: }
311:
312: return this ;
313: }
314:
315: public Request cookie(String name, String value) {
316: String current_cookie_header = null;
317: if (mHeaders != null) {
318: current_cookie_header = mHeaders.get(HEADER_COOKIE);
319: }
320:
321: if (null == current_cookie_header) {
322: current_cookie_header = "$Version=\"1\"";
323: }
324:
325: StringBuilder cookie_header = new StringBuilder(
326: current_cookie_header);
327: cookie_header.append("; ");
328: cookie_header.append(name);
329: cookie_header.append("=\"");
330: cookie_header.append(value);
331: cookie_header.append("\"");
332:
333: header(HEADER_COOKIE, cookie_header.toString());
334:
335: return this ;
336: }
337:
338: public Page retrieve() throws IOException {
339: return retrieve(true);
340: }
341:
342: public Page retrieve(boolean downloadContent)
343: throws IOException {
344: return retrieve(downloadContent, null);
345: }
346:
347: public Page retrieve(boolean downloadContent,
348: OutputStream output) throws IOException {
349: Page page = null;
350:
351: URL url = null;
352: HttpURLConnection connection = null;
353: try {
354: // add the query parameters to the URL if thzy're present
355: if (mQueryParams != null && mQueryParams.length() > 0) {
356: StringBuilder buffer = new StringBuilder(mUrl);
357: if (-1 == mUrl.indexOf("?")) {
358: buffer.append("?");
359: } else {
360: buffer.append("&");
361: }
362: buffer.append(mQueryParams.toString());
363: mUrl = buffer.toString();
364: }
365:
366: // create a new URL
367: url = new URL(mUrl);
368:
369: // setup the request method
370: if (null == mMethod) {
371: if (mPostParams != null) {
372: mMethod = METHOD_POST;
373:
374: // put the correct content type for a post request
375: if (null == mHeaders
376: || !mHeaders
377: .containsKey(HEADER_CONTENTTYPE)) {
378: header(HEADER_CONTENTTYPE,
379: CONTENTTYPE_FORM_URLENCODED);
380: }
381: } else {
382: mMethod = METHOD_GET;
383: }
384: }
385:
386: if (mPostParams != null) {
387: // put the correct content length for post requests
388: header(HEADER_CONTENTLENGTH, String
389: .valueOf(mPostParams.length()));
390: }
391:
392: // make untrusted SSL certificates work
393: if (url.getProtocol().equals("https")) {
394: try {
395: java.security.Security
396: .addProvider(new com.sun.net.ssl.internal.ssl.Provider());
397: System
398: .setProperty(
399: "java.protocol.handler.pkgs",
400: "com.sun.net.ssl.internal.www.protocol");
401: X509TrustManager tm = new MyX509TrustManager();
402: HostnameVerifier hm = new MyHostnameVerifier();
403: KeyManager[] km = null;
404: TrustManager[] tma = { tm };
405: SSLContext sc = SSLContext.getInstance("SSL");
406: sc.init(km, tma,
407: new java.security.SecureRandom());
408: SSLSocketFactory sf1 = sc.getSocketFactory();
409:
410: HttpsURLConnection
411: .setDefaultSSLSocketFactory(sf1);
412: HttpsURLConnection
413: .setDefaultHostnameVerifier(hm);
414: } catch (KeyManagementException e) {
415: IOException e2 = new IOException();
416: e2.initCause(e);
417: throw e2;
418: } catch (NoSuchAlgorithmException e) {
419: IOException e2 = new IOException();
420: e2.initCause(e);
421: throw e2;
422: }
423: }
424:
425: // setup the connection
426: connection = (HttpURLConnection) url.openConnection();
427: connection.setRequestMethod(mMethod);
428: connection.setDoInput(true);
429: connection.setDoOutput(true);
430: connection.setUseCaches(true);
431:
432: // put the headers as properties to the connection
433: if (mHeaders != null) {
434: for (String headers_name : mHeaders.keySet()) {
435: connection.setRequestProperty(headers_name,
436: mHeaders.get(headers_name));
437: }
438: }
439:
440: // send the form parameters
441: if (mPostParams != null) {
442: DataOutputStream out = new DataOutputStream(
443: connection.getOutputStream());
444: out.writeBytes(mPostParams.toString());
445: out.flush();
446: out.close();
447: }
448:
449: // download the connection content if that's needed
450: String connection_content = null;
451: if (downloadContent) {
452: if (output != null) {
453: getConnectionContent(connection, output);
454: } else {
455: connection_content = getConnectionContent(connection);
456: }
457: }
458:
459: // construct a new result page from the connection result
460: page = new Page(connection_content, connection
461: .getHeaderFields(), connection
462: .getResponseCode(), connection
463: .getResponseMessage());
464:
465: connection.disconnect();
466: } finally {
467: if (null != connection) {
468: connection.disconnect();
469: }
470: }
471:
472: return page;
473: }
474: }
475:
476: static class MyHostnameVerifier implements HostnameVerifier {
477: public boolean verify(String urlHostname, SSLSession session) {
478: return true;
479: }
480: }
481:
482: static class MyX509TrustManager implements X509TrustManager {
483: public void checkClientTrusted(X509Certificate[] chain,
484: String authType) throws CertificateException {
485: }
486:
487: public void checkServerTrusted(X509Certificate[] chain,
488: String authType) throws CertificateException {
489: }
490:
491: public java.security.cert.X509Certificate[] getAcceptedIssuers() {
492: return new X509Certificate[0];
493: }
494: }
495:
496: public static class Page {
497: private String mContent = null;
498: private int mResponseCode = -1;
499: private String mResponseMessage = null;
500: private Map<String, List<String>> mHeaders = null;
501:
502: Page(String content, Map<String, List<String>> headers,
503: int responseCode, String responseMessage) {
504: mContent = content;
505: mHeaders = new HashMap<String, List<String>>(
506: (Map<String, List<String>>) headers);
507: mResponseCode = responseCode;
508: mResponseMessage = responseMessage;
509: }
510:
511: public String getContent() {
512: return mContent;
513: }
514:
515: public Map<String, List<String>> getHeaders() {
516: return mHeaders;
517: }
518:
519: public List<String> getHeader(String name) {
520: return mHeaders.get(name);
521: }
522:
523: public String getHeaderField(String name) {
524: List<String> header = getHeader(name);
525:
526: if (null == header) {
527: return null;
528: }
529:
530: return header.get(0);
531: }
532:
533: public int getResponseCode() {
534: return mResponseCode;
535: }
536:
537: public String getResponseMessage() {
538: return mResponseMessage;
539: }
540:
541: public String getContentType() {
542: return getHeaderField(HEADER_CONTENTTYPE);
543: }
544:
545: public boolean checkReceivedCookies(Cookie[] cookies) {
546: if (!getHeaders().containsKey(HEADER_SETCOOKIE)) {
547: return false;
548: }
549:
550: HashSet<Cookie> matched_cookies = new HashSet<Cookie>();
551: List<String> received_cookies = null;
552:
553: received_cookies = getHeaders().get(HEADER_SETCOOKIE);
554:
555: for (Cookie cookie : cookies) {
556: if (!received_cookies.contains(cookie.getName() + "="
557: + cookie.getValue())) {
558: return false;
559: }
560:
561: if (matched_cookies.contains(cookie)) {
562: return false;
563: }
564:
565: matched_cookies.add(cookie);
566: }
567:
568: return true;
569: }
570: }
571: }
|