1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package tests.support;
19
20import java.io.ByteArrayOutputStream;
21import java.io.IOException;
22import java.io.InputStream;
23import java.io.OutputStream;
24import junit.framework.Assert;
25
26/**
27 * Performs some basic testing of either HttpConnection or HttpURLConnection
28 * depending on the Support_ServerSocket and Support_HttpConnector passed to the
29 * constructor.
30 *
31 */
32public class Support_HttpTests {
33
34	private Support_ServerSocket serversocket;
35
36	private Support_HttpConnector connector;
37
38	public Support_HttpTests(Support_ServerSocket serversocket,
39			Support_HttpConnector client) {
40		this.serversocket = serversocket;
41		this.connector = client;
42	}
43
44	public void runTests(junit.framework.TestCase test) {
45
46		// get a port to use for the test
47		int portNumber = Support_PortManager.getNextPort();
48
49		// url's for the various tests
50		final String chunkedTestUrl = "http://localhost:" + portNumber
51				+ Support_HttpServer.CHUNKEDTEST;
52		final String contentTestUrl = "http://localhost:" + portNumber
53				+ Support_HttpServer.CONTENTTEST;
54		final String redirectTestUrl = "http://localhost:" + portNumber
55				+ Support_HttpServer.REDIRECTTEST;
56		final String postTestUrl = "http://localhost:" + portNumber
57				+ Support_HttpServer.POSTTEST;
58		final String headersTestUrl = "http://localhost:" + portNumber
59				+ Support_HttpServer.HEADERSTEST;
60
61		// start the test server. It will timeout and shut down after
62		// 5 seconds of inactivity
63		Support_HttpServer server = new Support_HttpServer(serversocket, test);
64		server.startServer(portNumber);
65
66		ByteArrayOutputStream bout = new ByteArrayOutputStream();
67		InputStream is;
68		int c;
69
70		// Chunked HTTP Transfer Coding Test
71		try {
72			// access the url and open a stream
73			connector.open(chunkedTestUrl);
74			is = connector.getInputStream();
75
76			// receive the data, and then read again after EOF
77			c = is.read();
78			while (c > 0) {
79                c = is.read();
80            }
81			c = is.read();
82			is.close();
83			connector.close();
84			Assert.assertEquals("Error receiving chunked transfer coded data",
85					-1, c);
86		} catch (Exception e) {
87			e.printStackTrace();
88			Assert.fail("Exception during test a: " + e);
89		}
90
91		// Content-Length Test
92		try {
93			connector.open(contentTestUrl);
94			is = connector.getInputStream();
95			bout.reset();
96			do {
97				c = is.read();
98				if (c != -1) {
99                    bout.write(c);
100                }
101			} while (c != -1);
102			is.close();
103			connector.close();
104			String result = new String(bout.toByteArray(), "ISO8859_1");
105			Assert.assertTrue("Error receiving content coded data: " + result,
106					"ABCDE".equals(result));
107		} catch (Exception e) {
108			e.printStackTrace();
109			Assert.fail("Exception during test b: " + e);
110		}
111
112		// Headers Test
113		try {
114			connector.open(headersTestUrl);
115			connector.setRequestProperty("header1", "value1");
116			connector.setRequestProperty("header1", "value2");
117			int i = 0, found = 0;
118			String[] expected = new String[] { "no-cache=\"set-cookie\"",
119					"private", "no-transform" };
120			while (true) {
121				String key = connector.getHeaderFieldKey(i);
122				if (key == null && i > 0) {
123                    break;
124                }
125				if ("Cache-Control".equals(key)) {
126					Assert.assertTrue("Too many headers at: " + i, found <= 2);
127					String value = connector.getHeaderField(i);
128					Assert.assertTrue("Invalid header value: " + found + ": "
129							+ value, expected[found].equals(value));
130					found++;
131				}
132				i++;
133			}
134			Assert.assertTrue("Invalid headers: " + found, found == 3);
135			connector.close();
136		} catch (Exception e) {
137			e.printStackTrace();
138			Assert.fail("Exception during test c: " + e);
139		}
140
141		// Post Test
142		// Same as "Basic post" test below, but uses read() instead
143		// of read(buf, offset, length) to read the results
144		try {
145			String toWrite = "abcdef";
146			connector.open(postTestUrl);
147			OutputStream out = connector.getOutputStream();
148			out.write(toWrite.getBytes("ISO8859_1"));
149			out.close();
150			is = connector.getInputStream();
151			bout.reset();
152			do {
153				c = is.read();
154				if (c != -1) {
155                    bout.write(c);
156                }
157			} while (c != -1);
158			is.close();
159			connector.close();
160			String result = new String(bout.toByteArray(), "ISO8859_1");
161			Assert.assertTrue("Error sending data 1: " + result, toWrite
162					.equals(result));
163		} catch (Exception e) {
164			e.printStackTrace();
165			Assert.fail("Exception during test d: " + e);
166		}
167
168		// Post Test chunked
169		try {
170			String toWrite = "zyxwvuts";
171			connector.open(postTestUrl);
172			connector.setRequestProperty("Transfer-encoding", "chunked");
173			OutputStream out = connector.getOutputStream();
174			out.write(toWrite.getBytes("ISO8859_1"));
175			out.close();
176			is = connector.getInputStream();
177			bout.reset();
178			do {
179				c = is.read();
180				if (c != -1) {
181                    bout.write(c);
182                }
183			} while (c != -1);
184			is.close();
185			connector.close();
186			String result = new String(bout.toByteArray(), "ISO8859_1");
187            Assert.assertEquals(toWrite, result);
188		} catch (Exception e) {
189			e.printStackTrace();
190			Assert.fail("Exception during test e: " + e);
191		}
192
193		OutputStream os = null;
194
195		byte[] data = new byte[1024];
196		int len = 0;
197
198		// Basic post
199		try {
200			String message = "test";
201			connector.open(postTestUrl);
202			os = connector.getOutputStream();
203			os.write(message.getBytes("ISO8859_1"));
204			os.close();
205			is = connector.getInputStream();
206			len = 0;
207			do {
208				int r = is.read(data, len, data.length - len);
209				if (r == -1) {
210                    break;
211                }
212				len += r;
213			} while (true);
214			is.close();
215			connector.close();
216			String result = new String(data, 0, len, "ISO8859_1");
217			Assert.assertTrue("Basic port error: " + result, message
218					.equals(result));
219		} catch (IOException e) {
220			e.printStackTrace();
221			Assert.fail("Exception during basic post test: " + e);
222		}
223
224		String chunkChar = connector.isChunkedOnFlush() ? "C" : "";
225
226		// Flushing with post
227		try {
228			String message1 = "test2", message2 = "test3";
229			connector.open(postTestUrl);
230			os = connector.getOutputStream();
231			os.write(message1.getBytes("ISO8859_1"));
232			os.flush();
233			os.write(message2.getBytes("ISO8859_1"));
234			os.close();
235			is = connector.getInputStream();
236			len = 0;
237			do {
238				int r = is.read(data, len, data.length - len);
239				if (r == -1) {
240                    break;
241                }
242				len += r;
243			} while (true);
244			is.close();
245			connector.close();
246			String result = new String(data, 0, len, "ISO8859_1");
247			Assert.assertTrue("Flushing with post: " + result, (chunkChar
248					+ message1 + chunkChar + message2).equals(result));
249		} catch (IOException e) {
250			e.printStackTrace();
251			Assert.fail("Exception during flushing post test: " + e);
252		}
253
254		// Flushing with post and setting content-length
255		try {
256			String message1 = "test4", message2 = "test5";
257			connector.open(postTestUrl);
258			connector.setRequestProperty("Content-Length", "10");
259			os = connector.getOutputStream();
260			os.write(message1.getBytes("ISO8859_1"));
261			os.flush();
262			os.write(message2.getBytes("ISO8859_1"));
263			os.close();
264			is = connector.getInputStream();
265			len = 0;
266			do {
267				int r = is.read(data, len, data.length - len);
268				if (r == -1) {
269                    break;
270                }
271				len += r;
272			} while (true);
273			is.close();
274			connector.close();
275			String result = new String(data, 0, len, "ISO8859_1");
276			Assert.assertTrue("Flushing with post and setting content-length: "
277					+ result, (chunkChar + message1 + chunkChar + message2)
278					.equals(result));
279		} catch (IOException e) {
280			e.printStackTrace();
281			Assert.fail("Exception during flushing with content-length post test: "
282							+ e);
283		}
284
285		// Flushing followed immediately by a close()
286		try {
287			String message = "test6";
288			connector.open(postTestUrl);
289			os = connector.getOutputStream();
290			os.write(message.getBytes("ISO8859_1"));
291			os.flush();
292			os.close();
293			is = connector.getInputStream();
294			len = 0;
295			do {
296				int r = is.read(data, len, data.length - len);
297				if (r == -1) {
298                    break;
299                }
300				len += r;
301			} while (true);
302			is.close();
303			connector.close();
304			String result = new String(data, 0, len, "ISO8859_1");
305			Assert.assertTrue("Flushing followed immediately by a close(): "
306					+ result, (chunkChar + message).equals(result));
307		} catch (IOException e) {
308			e.printStackTrace();
309			Assert.fail("Exception during flush followed by close post test: "
310					+ e);
311		}
312
313		// Redirection Tests
314		final int[] testCodes = { Support_HttpServer.MULT_CHOICE,
315				Support_HttpServer.MOVED_PERM, Support_HttpServer.FOUND,
316				Support_HttpServer.SEE_OTHER, Support_HttpServer.NOT_MODIFIED,
317				Support_HttpServer.UNUSED, Support_HttpServer.TEMP_REDIRECT, };
318
319		final int[] results = { 'A', 'A', 'A', 'A', 'P', 'P', 'P' };
320		// see Support_HTTPServer for the source of this data
321
322		final String fullLocalLocation = contentTestUrl;
323		final String partLocalLocation = Support_HttpServer.CONTENTTEST;
324
325		for (int i = 0; i < testCodes.length; i++) {
326
327			// test each of the redirection response codes
328			try {
329				// append the response code for the server to return
330				// and the location to redirect to
331				connector.open(redirectTestUrl + "/" + testCodes[i] + "-"
332						+ fullLocalLocation);
333				is = connector.getInputStream();
334				connector.close();
335
336				c = is.read();
337
338				if (testCodes[i] == Support_HttpServer.NOT_MODIFIED) {
339					// accept either the message-body or nothing, since the spec
340					// says there MUST NOT be a message body on 304 responses.
341					// But Java returns the message-body
342					if (!(c == results[i] || c == -1)) {
343						Assert.fail("Incorrect data returned on test of HTTP response "
344										+ testCodes[i]);
345					}
346				} else if (c != results[i]) {
347					Assert.fail("Incorrect data returned on test of HTTP response "
348									+ testCodes[i]);
349				}
350				while (c > 0) {
351                    c = is.read();
352                }
353				c = is.read();
354				is.close();
355			} catch (Exception e) {
356				e.printStackTrace();
357				Assert.fail("Error during redirection test " + i + ": " + e);
358			}
359		}
360
361		// Test redirecting to a location on a different port
362		Class<?> serversocketclass = serversocket.getClass();
363		try {
364			Support_ServerSocket serversocket2 = (Support_ServerSocket) serversocketclass
365					.newInstance();
366
367			Support_HttpServer server2 = new Support_HttpServer(serversocket2,
368					test);
369			int newport = Support_PortManager.getNextPort();
370			server2.startServer(newport);
371			server2.setPortRedirectTestEnable(true);
372
373			// Test if redirection to a different port works
374			final String otherPortLocation = "http://localhost:" + newport
375					+ Support_HttpServer.PORTREDIRTEST;
376
377			try {
378				// append the response code for the server to return
379				// and the location to redirect to
380
381				connector.open(redirectTestUrl + "/"
382						+ Support_HttpServer.MOVED_PERM + "-"
383						+ otherPortLocation);
384				is = connector.getInputStream();
385				connector.close();
386
387				c = is.read();
388				Assert.assertEquals("Incorrect data returned on redirection to a different port.",
389								'A', c);
390				while (c > 0) {
391                    c = is.read();
392                }
393				c = is.read();
394				is.close();
395			} catch (Exception e) {
396				e.printStackTrace();
397				Assert.fail("Exception during test f: " + e);
398			}
399			server2.stopServer();
400		} catch (IllegalAccessException e) {
401			Assert.fail("Exception during redirection to a different port:" + e);
402		} catch (InstantiationException e) {
403			Assert.fail("Exception during redirection to a different port:" + e);
404		}
405
406		// test redirecting to a relative URL on the same host
407		try {
408			// append the response code for the server to return
409			connector.open(redirectTestUrl + "/"
410					+ Support_HttpServer.MOVED_PERM + "-" + partLocalLocation);
411			is = connector.getInputStream();
412			connector.close();
413
414			c = is.read();
415			Assert.assertEquals("Incorrect data returned on redirect to relative URI.",
416					'A', c);
417			while (c > 0) {
418                c = is.read();
419            }
420			c = is.read();
421			is.close();
422		} catch (Exception e) {
423			e.printStackTrace();
424			Assert.fail("Exception during redirection test to a relative URL: " + e);
425		}
426		server.stopServer();
427	}
428
429}
430