/* 
 * E-XML Library:  For XML, XML-RPC, HTTP, and related.
 * Copyright (C) 2002-2008  Elias Ross
 * 
 * genman@noderunner.net
 * http://noderunner.net/~genman
 * 
 * 1025 NE 73RD ST
 * SEATTLE WA 98115
 * USA
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * $Id$
 */

package net.noderunner.http;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * This class is for unit testing TokenUtil and HttpUtil.
 *
 * @author Elias Ross
 * @version 1.0
 */
public class HttpUtilTest
	extends junit.framework.TestCase
{
	public HttpUtilTest(String name) {
		super(name);
	}

	public void testHttpUtil()
		throws IOException
	{
		byte stuff[] = new byte[2048];
		java.util.Arrays.fill(stuff, (byte)'a');
		InputStream is;

		is = new ByteArrayInputStream(stuff);
		try {
			HttpUtil.readHttpLine(is);
			fail("Long line");
		} catch (HttpException e) { }

		is = new ByteArrayInputStream(new byte[] { });
		try {
			HttpUtil.readHttpLine(is);
			fail("EOF line");
		} catch (EOFException e) { }

		is = new ByteArrayInputStream(new byte[] { 'a', 'b', '\r', '\n' });
		String line = HttpUtil.readHttpLine(is);
		assertEquals("AB", "ab", line);

		is = new ByteArrayInputStream(new byte[] { 'a', 'b', '\r', 'a' });
		try {
			HttpUtil.readHttpLine(is);
			fail("Expected LN");
		} catch (HttpException e) { }
		
		is = new ByteArrayInputStream(new byte[] { 'a', 'b', '\n', 'a' });
		assertEquals("ab", HttpUtil.readHttpLine(is));
}

	public void testReadFullyClose()
		throws IOException
	{
		final MessageHeaders mh = new MessageHeaders();
		mh.add(MessageHeader.MH_CONNECTION_CLOSE);
		ClientResponse cr;
		cr = new ClientResponse() {
			public StatusLine getStatusLine()   { return null; }
			public MessageHeaders getHeaders() { return mh; }
			public InputStream getInputStream() { 
        		return new ByteArrayInputStream(new byte[1]);
			}
		};
		assertEquals(0, cr.readFully());
		mh.add(new MessageHeader( MessageHeader.FN_CONTENT_LENGTH, "1"));
		// TODO fix this
		// assertEquals(1, cr.readFully());
	}

	public void testReadFully()
		throws IOException
	{
		InputStream s;
		s = new ByteArrayInputStream(new byte[4]);
		assertEquals(4, HttpUtil.readFully(s));
		s = new ByteArrayInputStream(new byte[4000]);
		assertEquals(4000, HttpUtil.readFully(s));
		s = new LimitedInputStream(new ByteArrayInputStream(new byte[4000]), 2000);
		assertEquals(2000, HttpUtil.readFully(s));
		try {
			HttpUtil.readFully((InputStream)null);
			fail("cannot read");
		} catch (IllegalArgumentException e) { }
	}

	public void testWrapInputStream()
		throws IOException
	{
		MessageHeaders headers = new MessageHeaders();
		headers.add(MessageHeader.MH_TRANSFER_ENCODING_CHUNKED);
		headers.add(new MessageHeader(MessageHeader.FN_CONTENT_LENGTH, "10"));
		InputStream s = new ByteArrayInputStream(new byte[] { } );
		assertTrue("chunked input", HttpUtil.wrapInputStream(s, headers) instanceof ChunkedInputStream);
		
		headers = new MessageHeaders();
		headers.add(new MessageHeader(MessageHeader.FN_CONTENT_LENGTH, "10"));
		assertTrue("chunked input", HttpUtil.wrapInputStream(s, headers) instanceof LimitedInputStream);
		
		headers = new MessageHeaders();
		InputStream is = HttpUtil.wrapInputStream(s, headers);
		assertEquals(s, is);
		try {
    		headers = new MessageHeaders();
    		headers.add(new MessageHeader(MessageHeader.FN_CONTENT_LENGTH, "abacaba"));
			HttpUtil.wrapInputStream(s, headers);
			fail("bad content-length");
		} catch (HttpException e) { }
		try {
			HttpUtil.wrapInputStream(null, headers);
			fail("null");
		} catch (IllegalArgumentException e) { }
		try {
			HttpUtil.wrapInputStream(s, null);
			fail("null");
		} catch (IllegalArgumentException e) { }
	}

	public void testTokenUtil()
		throws IOException
	{
		assertTrue("(", !TokenUtil.isTokenChar('('));
		assertTrue("\t", !TokenUtil.isTokenChar('\t'));
		assertTrue("a", TokenUtil.isTokenChar('a'));
		assertTrue("bob", TokenUtil.isValidToken("bob"));
		assertTrue("bob ", !TokenUtil.isValidToken("bob "));
		assertTrue("bo:b", !TokenUtil.isValidToken("bo:b"));
	}

	public void testEncodeDecode()
		throws IOException
	{
		Map<String, String> m = new HashMap<String, String>();
		m.put("ab ", "cd");
		m.put("ef$", "&&&");
		m.put("xy&", "=zy");
		byte[] urlEncodedData = HttpUtil.urlEncode(m);
		Map<String, String[]> m2 = HttpUtil.urlDecode(new String(urlEncodedData));
		assertEquals("same map", m.keySet(), m2.keySet());
		assertEquals("cd", m2.get("ab ")[0]);
		assertEquals("&&&", m2.get("ef$")[0]);
	}

	public void testEncodeDecode2()
		throws IOException
	{
		String s[] = new String[] { "$&", "cd", "k y", "*?" };
		byte[] urlEncodedData = HttpUtil.urlEncode(s);
		String s2[] = HttpUtil.urlDecodeToArray(new String(urlEncodedData));
		assertEquals("same length", s.length, s2.length);
		for (int i = 0; i < s.length; i++)
			assertEquals("same entry", s[i], s2[i]);
	}

	public void testEncodeDecodeStringEncoding()
		throws IOException
	{
		String s[] = new String[] { "foo", "bar" };
		byte[] urlEncodedData;
		try {
			urlEncodedData = HttpUtil.urlEncode(s, "fuddy-duddy");
			fail("fuddy-duddy not valid");
		} catch (UnsupportedEncodingException e) {
		}
		urlEncodedData = HttpUtil.urlEncode(s, "UTF-8");
		String s2[] = HttpUtil.urlDecodeToArray(new String(urlEncodedData));
		for (int i = 0; i < s.length; i++)
			assertEquals("same entry", s[i], s2[i]);
	}

	public void testDecode()
		throws IOException
	{
		String urlEncodedData = "apple&banana=&cherry";
		Map<String, String[]> m = HttpUtil.urlDecode(urlEncodedData);
		assertTrue(m.containsKey("apple"));
		assertTrue(m.containsKey("banana"));
		assertTrue(m.containsKey("cherry"));

		String urlEncodedData2 = "apple=a&banana=b&cherry=c";
		Map<String, String[]> m2 = HttpUtil.urlDecode(urlEncodedData2);
		assertEquals("a", m2.get("apple")[0]);
		assertEquals("b", m2.get("banana")[0]);
		assertEquals("c", m2.get("cherry")[0]);
	}

	@SuppressWarnings("deprecation")
	public void testEncodeSpace()
		throws IOException
	{
		String a = " apple ";
		String s;

		s = new String(HttpUtil.urlEncode(new String[]{a, a}));
		assertEquals(URLEncoder.encode(a) + "=" + URLEncoder.encode(a), s);
		String a2 = "?apple?";
		s = new String(HttpUtil.urlEncode(new String[]{a2, a2}));
		assertEquals(URLEncoder.encode(a2) + "=" + URLEncoder.encode(a2), s);
	}

	public void testDecode2()
		throws IOException
	{
		String urlEncodedData = "+apple&ban+ana=+++&cherry";
		String s[] = HttpUtil.urlDecodeToArray(urlEncodedData);
		assertEquals(6, s.length);
		assertEquals(" apple", s[0]);
		assertEquals(null, s[1]);
		assertEquals("ban ana", s[2]);
		assertEquals("   ", s[3]);
		assertEquals("cherry", s[4]);
		assertEquals(null, s[5]);
	}
	
	public void testAdd() {
		String s[] = new String[] { "ab", "cd" };
		String[] strings = HttpUtil.add(s, "ef");
		assertEquals("ab", strings[0]);
		assertEquals("ef", strings[2]);
	}

	public void testDiscard()
		throws IOException
	{
		HttpUtil.discard((BufferedReader)null);
	}
	
	public void testDecodeInputStream()
		throws IOException
	{
		String s[] = new String[] { "ab", "cd", "ab", "de" };
		byte[] urlEncodedData = HttpUtil.urlEncode(s);
		Map<String, String[]> m = HttpUtil.urlDecode(new ByteArrayInputStream(urlEncodedData));
		String sa[] = m.get("ab");
		assertEquals("MAP " + m, 2, sa.length);
		assertEquals("cd", sa[0]);
		assertEquals("de", sa[1]);
		
		String[] s2 = HttpUtil.urlDecodeToArray(new ByteArrayInputStream(urlEncodedData));
		assertTrue(Arrays.equals(s, s2));
		
		try {
    		HttpUtil.urlDecodeToArray((String)null);
    		fail();
		} catch (RuntimeException e) {}
		try {
    		HttpUtil.urlDecodeToArray((InputStream)null);
    		fail();
		} catch (RuntimeException e) {}
	}

}
