android-openrat

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

HTTPRequest.java (15332B)


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