android-blogposter

Unnamed repository; edit this file 'description' to name the repository.
git clone http://git.code.weiherhei.de/android-blogposter.git
Log | Files | Refs

HTTPRequest.java (15047B)


      1 /*
      2 OpenRat Java-Client
      3 Copyright (C) 2009 Jan Dankert
      4  
      5 This library is free software; you can redistribute it and/or
      6 modify it under the terms of the GNU Library General Public
      7 License as published by the Free Software Foundation; either
      8 version 2 of the License, or (at your option) any later version.
      9 
     10 This library is distributed in the hope that it will be useful,
     11 but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13 Library General Public License for more details.
     14 
     15 You should have received a copy of the GNU Library General Public
     16 License along with this library; if not, write to the
     17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
     18 Boston, MA  02110-1301, USA.
     19 
     20  */
     21 package de.openrat.android.blog;
     22 
     23 import java.io.BufferedReader;
     24 import java.io.ByteArrayOutputStream;
     25 import java.io.IOException;
     26 import java.io.InputStream;
     27 import java.io.OutputStream;
     28 import java.io.Serializable;
     29 import java.io.UnsupportedEncodingException;
     30 import java.net.InetSocketAddress;
     31 import java.net.Socket;
     32 import java.net.SocketAddress;
     33 import java.net.URLEncoder;
     34 import java.util.ArrayList;
     35 import java.util.HashMap;
     36 import java.util.List;
     37 import java.util.Locale;
     38 import java.util.Map;
     39 import java.util.Map.Entry;
     40 
     41 /**
     42  * API-Request to the OpenRat Content Management System. <br>
     43  * <br>
     44  * The call to the CMS server is done via a (non-SSL) HTTP connection.<br>
     45  * <br>
     46  * Before a call you are able to set some key/value-pairs as parameters. After
     47  * calling the CMS a DOM-document is returned, which contains the server
     48  * response.<br>
     49  * Example <br>
     50  * 
     51  * <pre>
     52  * CMSRequest request = new CMSRequest(&quot;your.openrat.example.com&quot;);
     53  * //prints tracing information to stdout.
     54  * request.trace = true;
     55  * try
     56  * {
     57  * 	request.parameter.put(&quot;action&quot;, &quot;index&quot;);
     58  * 	request.parameter.put(&quot;subaction&quot;, &quot;showlogin&quot;); // login page
     59  * 	request.parameter.put(&quot;...&quot;, &quot;...&quot;);
     60  * 	Document response = request.call();
     61  * 	// now traverse through the dom tree and get your information.
     62  * } catch (IOException e)
     63  * {
     64  * 	// your error handling.
     65  * }
     66  * </pre>
     67  * 
     68  * @author Jan Dankert
     69  */
     70 public class HTTPRequest implements Serializable
     71 {
     72 
     73 	private static final String CRLF = "\r\n";
     74 
     75 	private Multipart multipart = new Multipart();
     76 
     77 	// some constants...
     78 	private static final String CHARSET_UTF8 = "UTF-8";
     79 	private static final String HTTP_GET = "GET";
     80 	private static final String HTTP_POST = "POST";
     81 
     82 	/**
     83 	 * if <code>true</code>, Tracing-Output will be logged to stdout. Default:
     84 	 * <code>false</code>.
     85 	 */
     86 	// this is public, for easier use.
     87 	public boolean trace = false;
     88 
     89 	/**
     90 	 * HTTP-method, must be "GET" or "POST", default: "GET".
     91 	 */
     92 	private String method = HTTP_GET;
     93 
     94 	/**
     95 	 * Parameter map.
     96 	 */
     97 	private Map<String, String> parameter = new HashMap<String, String>();
     98 	private Map<String, String> requestHeader = new HashMap<String, String>();
     99 
    100 	private String serverPath;
    101 	private String serverHost;
    102 	private int serverPort;
    103 
    104 	private String proxyHostname;
    105 	private int proxyPort;
    106 	private SocketAddress socketAddress;
    107 
    108 	private String cookieName;
    109 	private String cookieValue;
    110 	private String language;
    111 	private int timeout = 30000;
    112 
    113 	/**
    114      * 
    115      */
    116 	public HTTPRequest()
    117 	{
    118 		super();
    119 		this.language = Locale.getDefault().getLanguage();
    120 	}
    121 
    122 	public String getAcceptLanguage()
    123 	{
    124 		return language;
    125 	}
    126 
    127 	public void setAcceptLanguage(String language)
    128 	{
    129 		this.language = language;
    130 	}
    131 
    132 	/**
    133 	 * Setting a HTTP-Cookie.
    134 	 * 
    135 	 * @param name
    136 	 *            name
    137 	 * @param value
    138 	 *            value
    139 	 */
    140 	public void setCookie(String name, String value)
    141 	{
    142 
    143 		this.cookieName = this.urlEncode(name);
    144 		this.cookieValue = this.urlEncode(value);
    145 	}
    146 
    147 	/**
    148 	 * URL-Encoder.
    149 	 * 
    150 	 * @param value
    151 	 * @return url-encoded value
    152 	 */
    153 	private String urlEncode(String value)
    154 	{
    155 
    156 		try
    157 		{
    158 			return URLEncoder.encode(value, CHARSET_UTF8);
    159 		} catch (UnsupportedEncodingException e)
    160 		{
    161 			// maybe... this would be strange
    162 			throw new IllegalStateException(CHARSET_UTF8
    163 					+ " ist not supported by this VM");
    164 		}
    165 	}
    166 
    167 	/**
    168 	 * Setting a HTTP-Proxy.
    169 	 * 
    170 	 * @param host
    171 	 *            hostname
    172 	 * @param port
    173 	 *            port
    174 	 */
    175 	public void setProxy(String host, int port)
    176 	{
    177 
    178 		this.proxyHostname = host;
    179 		this.proxyPort = port;
    180 	}
    181 
    182 	
    183 	/**
    184 	 * Timeout for Socket.
    185 	 * @param timeout Timeout in milliseconds.
    186 	 * @return old timeout 
    187 	 */
    188 	public int setTimeout(int timeout)
    189 	{
    190 		int oldTimeout = this.timeout;
    191 		this.timeout = timeout;
    192 		
    193 		return oldTimeout;
    194 	}
    195 
    196 	/**
    197 	 * Set the HTTP Method. Default is "GET".
    198 	 * 
    199 	 * @param method
    200 	 *            HTTP-method
    201 	 */
    202 	public void setMethod(String method)
    203 	{
    204 
    205 		if (!HTTP_GET.equalsIgnoreCase(method)
    206 				&& !HTTP_POST.equalsIgnoreCase(method))
    207 			throw new IllegalArgumentException("Method must be '" + HTTP_POST
    208 					+ "' or '" + HTTP_GET + "'.");
    209 
    210 		this.method = method.toUpperCase();
    211 	}
    212 
    213 	/**
    214 	 * Clear parameter values.
    215 	 */
    216 	public void clearParameters()
    217 	{
    218 
    219 		parameter.clear();
    220 		requestHeader.clear();
    221 		multipart.parts.clear();
    222 	}
    223 
    224 	/**
    225 	 * Setting a parameter value. <strong>DO NOT url-encode your values</strong>
    226 	 * as this is done automatically inside this method!
    227 	 * 
    228 	 * @param paramName
    229 	 *            name
    230 	 * @param paramValue
    231 	 *            value
    232 	 */
    233 	public void setParameter(String paramName, String paramValue)
    234 	{
    235 
    236 		if (paramName == null || paramValue == null || "" == paramName)
    237 			throw new IllegalArgumentException(
    238 					"parameter name and value must have values");
    239 
    240 		parameter.put(paramName, paramValue);
    241 	}
    242 
    243 	/**
    244 	 * 
    245 	 * Setting a parameter value. <strong>DO NOT url-encode your values</strong>
    246 	 * as this is done automatically inside this method!
    247 	 * 
    248 	 * @param paramName
    249 	 *            name
    250 	 * @param paramValue
    251 	 *            value
    252 	 */
    253 	public void setHeader(String paramName, String paramValue)
    254 	{
    255 
    256 		if (paramName == null || paramValue == null || "" == paramName)
    257 			throw new IllegalArgumentException(
    258 					"parameter name and value must have values");
    259 
    260 		requestHeader.put(paramName, paramValue);
    261 	}
    262 
    263 	/**
    264 	 * Constructs a CMS-Request to the specified server.<br>
    265 	 * Server-Path is "/", Server-Port is 80.
    266 	 * 
    267 	 * @param host
    268 	 *            hostname
    269 	 */
    270 	public HTTPRequest(String host)
    271 	{
    272 
    273 		super();
    274 		this.serverHost = host;
    275 		this.serverPath = "/";
    276 		this.serverPort = 80;
    277 	}
    278 
    279 	/**
    280 	 * Constructs a CMS-Request to the specified server/path.<br>
    281 	 * Server-Port is 80.
    282 	 * 
    283 	 * @param host
    284 	 *            hostname
    285 	 * @param path
    286 	 *            path
    287 	 */
    288 	public HTTPRequest(String host, String path)
    289 	{
    290 
    291 		super();
    292 		this.serverHost = host;
    293 		this.serverPath = path;
    294 		this.serverPort = 80;
    295 	}
    296 
    297 	/**
    298 	 * Constructs a CMS-Request to the specified server/path/port.
    299 	 * 
    300 	 * @param host
    301 	 *            hostname
    302 	 * @param path
    303 	 *            path
    304 	 * @param port
    305 	 *            port-number
    306 	 */
    307 	public HTTPRequest(String host, String path, int port)
    308 	{
    309 
    310 		super();
    311 		this.serverHost = host;
    312 		this.serverPath = path;
    313 		this.serverPort = port;
    314 	}
    315 
    316 	/**
    317 	 * Sends a request to the openrat-server and parses the response into a DOM
    318 	 * tree document.
    319 	 * 
    320 	 * @return server response as a DOM tree
    321 	 * @throws IOException
    322 	 *             if server is unrechable or responds non-wellformed XML
    323 	 */
    324 	public byte[] performRequest() throws IOException
    325 	{
    326 		return performRequest(null);
    327 	}
    328 
    329 	/**
    330 	 * Sends a request to the openrat-server and parses the response into a DOM
    331 	 * tree document.
    332 	 * 
    333 	 * @return server response as a DOM tree
    334 	 * @throws IOException
    335 	 *             if server is unrechable or responds non-wellformed XML
    336 	 */
    337 	public byte[] performRequest(String body) throws IOException
    338 	{
    339 
    340 		final Socket socket = new Socket();
    341 
    342 		try
    343 		{
    344 
    345 			final boolean useProxy = this.proxyHostname != null;
    346 			final boolean useCookie = this.cookieName != null;
    347 
    348 			if (serverPath == null)
    349 				this.serverPath = "/";
    350 			if (!serverPath.startsWith("/"))
    351 				this.serverPath = "/" + this.serverPath;
    352 
    353 			// When a client uses a proxy, it typically sends all requests to
    354 			// that proxy, instead
    355 			// of to the servers in the URLs. Requests to a proxy differ from
    356 			// normal requests in one
    357 			// way: in the first line, they use the complete URL of the resource
    358 			// being requested,
    359 			// instead of just the path.
    360 			if (useProxy)
    361 			{
    362 				socketAddress = new InetSocketAddress(this.proxyHostname,
    363 						this.proxyPort);
    364 			} else
    365 			{
    366 				socketAddress = new InetSocketAddress(this.serverHost,
    367 						serverPort);
    368 			}
    369 
    370 			socket.setKeepAlive(false);
    371 			socket.setReuseAddress(false);
    372 			socket.setSoTimeout(timeout);
    373 			socket.connect(socketAddress, timeout);
    374 
    375 			final StringBuffer header = new StringBuffer();
    376 
    377 			final StringBuffer parameterList = new StringBuffer();
    378 
    379 			for (Entry<String, String> entry : this.parameter.entrySet())
    380 			{
    381 				if (parameterList.length() > 0)
    382 					parameterList.append("&");
    383 				parameterList.append(this.urlEncode(entry.getKey()));
    384 				parameterList.append("=");
    385 				parameterList.append(this.urlEncode(entry.getValue()));
    386 			}
    387 
    388 			String httpUrl = this.serverPath;
    389 
    390 			if (useProxy)
    391 				// See RFC 2616 Section 5.1.2 "Request-URI"
    392 				// "The absolute URI form is REQUIRED when the request is being made to a proxy"
    393 				httpUrl = "http://" + this.serverHost + httpUrl;
    394 
    395 			if (HTTP_GET.equals(this.method)
    396 					|| (body != null || multipart.parts.size() > 0))
    397 				httpUrl = httpUrl + "?" + parameterList;
    398 
    399 			// using HTTP/1.0 as this is supported by all HTTP-servers and
    400 			// proxys.
    401 			// We have no need for HTTP/1.1 at the moment.
    402 			header.append(this.method + " " + httpUrl + " HTTP/1.0" + CRLF);
    403 
    404 			// Setting the HTTP Header
    405 			Map<String, String> headers = new HashMap<String, String>();
    406 			headers.put("Host", this.serverHost);
    407 			headers.put("User-Agent",
    408 					"Mozilla/5.0; compatible (OpenRat android-client)");
    409 			headers.put("Accept", "application/json");
    410 			headers.put("Accept-Language", language);
    411 			headers.put("Accept-Charset", "utf-8");
    412 			headers.put("Connection", "close");
    413 			if (useCookie)
    414 				headers.put("Cookie", cookieName + "=" + cookieValue);
    415 
    416 			if (HTTP_POST.equals(this.method))
    417 			{
    418 				if (body == null && multipart.parts.size() == 0)
    419 				{
    420 					headers.put("Content-Type",
    421 							"application/x-www-form-urlencoded");
    422 					headers.put("Content-Length", "" + parameterList.length());
    423 				} else if (multipart.parts.size() > 0)
    424 				{
    425 
    426 					headers.put("Content-Type", multipart.getContentType());
    427 					headers.put("Content-Length", ""
    428 							+ multipart.getPayload().length);
    429 
    430 				} else
    431 				{
    432 					headers.put("Content-Type", "text/plain");
    433 
    434 				}
    435 			}
    436 
    437 			headers.putAll(requestHeader);
    438 			for (String headerName : headers.keySet())
    439 			{
    440 				header.append(headerName + ": " + headers.get(headerName)
    441 						+ CRLF);
    442 
    443 			}
    444 
    445 			header.append(CRLF);
    446 
    447 			final OutputStream outputStream = socket
    448 					.getOutputStream();
    449 			outputStream.write(header.toString().getBytes());
    450 			
    451 			if (HTTP_POST.equals(this.method))
    452 			{
    453 				if (body == null && multipart.parts.size() == 0)
    454 					outputStream.write(parameterList.toString().getBytes());
    455 				else if (multipart.parts.size() > 0)
    456 					outputStream.write(multipart.getPayload());
    457 				else
    458 					outputStream.write(body.getBytes());
    459 			}
    460 
    461 			if (this.trace)
    462 				System.out.println("--- request ---");
    463 			if (this.trace)
    464 				System.out.println(header.toString());
    465 
    466 
    467 			outputStream.flush();
    468 
    469 			final InputStream inputStream = socket.getInputStream();
    470 //			final int available = inputStream.available();
    471 
    472 			final BufferedReader bufferedReader = new BufferedReader(
    473 					new MyStreamReader(inputStream),1);
    474 
    475 			final String httpResponse = bufferedReader.readLine().trim();
    476 			final String httpRetCode = httpResponse.substring(9, 12);
    477 
    478 			if (this.trace)
    479 				System.out.println("--- response ---");
    480 			if (this.trace)
    481 				System.out.println(httpResponse);
    482 
    483 			// Check if we got the status 200=OK.
    484 			if (!httpRetCode.equals("200"))
    485 			{
    486 
    487 				// non-200-status seems to be an error.
    488 				throw new IOException("No HTTP 200: Status=" + httpRetCode
    489 						+ " (" + httpResponse + ")");
    490 			}
    491 
    492 			while (true)
    493 			{
    494 
    495 				String responseHeader = bufferedReader.readLine().trim();
    496 
    497 				if (responseHeader.equals(""))
    498 					break;
    499 
    500 				if (this.trace)
    501 					System.out.println(responseHeader);
    502 			}
    503 			//inputStreamReader.reset();
    504 			//inputStream.reset();
    505 
    506 			ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    507 			
    508 			int nRead;
    509 			byte[] data = new byte[1024];
    510 
    511 			while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
    512 			  buffer.write(data, 0, nRead);
    513 			}
    514 			buffer.flush();
    515 
    516 			byte[] response = buffer.toByteArray();
    517 			
    518 			if (this.trace)
    519 				System.out.println("--- response body ---");
    520 			if (this.trace)
    521 				System.out.println(response + "\n\n\n");
    522 
    523 			return response;
    524 		} finally
    525 		{
    526 			try
    527 			{
    528 				socket.close(); // Clean up the socket.
    529 			} catch (IOException e)
    530 			{
    531 				// We have done our very best.
    532 			}
    533 		}
    534 
    535 	}
    536 
    537 	public void setFile(String name, byte[] value, String filename,
    538 			String type, String encoding)
    539 	{
    540 
    541 		Part part = new Part();
    542 		part.file = value;
    543 		part.filename = filename;
    544 		part.encoding = encoding;
    545 		part.name = name;
    546 		part.contentType = type;
    547 		this.multipart.parts.add(part);
    548 	}
    549 
    550 	public void setText(String name, String value)
    551 	{
    552 
    553 		Part part = new Part();
    554 		part.name = name;
    555 		part.text = value;
    556 		part.contentType = "text/plain";
    557 		this.multipart.parts.add(part);
    558 	}
    559 
    560 	private class Multipart implements Serializable
    561 	{
    562 
    563 		private static final String CRLF = "\r\n";
    564 		private static final String BOUNDARY = "614BA262123F3B29656A745C5DD26";
    565 		List<Part> parts = new ArrayList<Part>();
    566 
    567 		public byte[] getPayload() throws IOException
    568 		{
    569 			HttpOutputStream body = new HttpOutputStream();
    570 
    571 			for (Part part : parts)
    572 			{
    573 				body.append("--" + BOUNDARY + CRLF);
    574 				body.append("Content-Type: " + part.contentType + CRLF);
    575 
    576 				if (part.encoding != null)
    577 					body.append("Content-Transfer-Encoding: " + part.encoding
    578 							+ CRLF);
    579 
    580 				body.append("Content-Disposition: form-data; name=\""
    581 						+ part.name
    582 						+ "\""
    583 						+ (part.filename != null ? ("; filename=\""
    584 								+ part.filename + "\"") : "") + CRLF);
    585 				body.append(CRLF);
    586 				if (part.file.length > 0)
    587 					body.write(part.file);
    588 				else
    589 					body.append(part.text);
    590 				body.append(CRLF);
    591 			}
    592 			body.append("--" + BOUNDARY + "--");
    593 			return body.toByteArray();
    594 		}
    595 
    596 		public String getContentType()
    597 		{
    598 			return "multipart/form-data; boundary=" + Multipart.BOUNDARY;
    599 		}
    600 	}
    601 
    602 	private class Part implements Serializable
    603 	{
    604 		public byte[] file;
    605 		public String filename;
    606 		public String text;
    607 		public String name;
    608 		public String contentType;
    609 		public String encoding;
    610 	}
    611 
    612 	private class HttpOutputStream extends ByteArrayOutputStream
    613 	{
    614 
    615 		public void write(String s) throws IOException
    616 		{
    617 			super.write(s.getBytes());
    618 		}
    619 		public void append(String s) throws IOException
    620 		{
    621 			super.write(s.getBytes());
    622 		}
    623 	}
    624 }