View Javadoc

1   // Copyright (C) 2000 - 2012 Philip Aston
2   // All rights reserved.
3   //
4   // This file is part of The Grinder software distribution. Refer to
5   // the file LICENSE which is part of The Grinder distribution for
6   // licensing details. The Grinder distribution is available on the
7   // Internet at http://grinder.sourceforge.net/
8   //
9   // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
10  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
11  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12  // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
13  // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
14  // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
15  // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
16  // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
17  // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
18  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
19  // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
20  // OF THE POSSIBILITY OF SUCH DAMAGE.
21  
22  package net.grinder.plugin.http;
23  
24  import static net.grinder.testutility.AssertUtilities.assertArraysEqual;
25  import static net.grinder.testutility.AssertUtilities.assertContains;
26  import static org.junit.Assert.assertEquals;
27  import static org.junit.Assert.assertFalse;
28  import static org.junit.Assert.assertNotNull;
29  import static org.junit.Assert.assertNull;
30  import static org.junit.Assert.assertSame;
31  import static org.junit.Assert.assertTrue;
32  import static org.junit.Assert.fail;
33  import static org.mockito.Matchers.isA;
34  import static org.mockito.Mockito.atLeastOnce;
35  import static org.mockito.Mockito.doThrow;
36  import static org.mockito.Mockito.mock;
37  import static org.mockito.Mockito.reset;
38  import static org.mockito.Mockito.times;
39  import static org.mockito.Mockito.verify;
40  import static org.mockito.Mockito.verifyNoMoreInteractions;
41  import static org.mockito.Mockito.when;
42  
43  import java.io.ByteArrayInputStream;
44  import java.io.File;
45  import java.io.FileOutputStream;
46  import java.io.OutputStream;
47  import java.util.Random;
48  
49  import net.grinder.common.GrinderException;
50  import net.grinder.common.SSLContextFactory;
51  import net.grinder.engine.process.dcr.DCRContextImplementation;
52  import net.grinder.plugininterface.GrinderPlugin;
53  import net.grinder.plugininterface.PluginException;
54  import net.grinder.plugininterface.PluginProcessContext;
55  import net.grinder.plugininterface.PluginRegistry;
56  import net.grinder.plugininterface.PluginThreadContext;
57  import net.grinder.script.Grinder.ScriptContext;
58  import net.grinder.script.InvalidContextException;
59  import net.grinder.script.Statistics;
60  import net.grinder.script.Statistics.StatisticsForTest;
61  import net.grinder.scriptengine.Instrumenter;
62  import net.grinder.scriptengine.Recorder;
63  import net.grinder.scriptengine.java.JavaScriptEngineService;
64  import net.grinder.statistics.StatisticsIndexMap;
65  import net.grinder.statistics.StatisticsServicesImplementation;
66  import net.grinder.util.InsecureSSLContextFactory;
67  import net.grinder.util.StandardTimeAuthority;
68  import net.grinder.util.TimeAuthority;
69  
70  import org.junit.After;
71  import org.junit.Before;
72  import org.junit.Test;
73  import org.mockito.ArgumentCaptor;
74  import org.mockito.Captor;
75  import org.mockito.Mock;
76  import org.mockito.MockitoAnnotations;
77  import org.slf4j.Logger;
78  
79  import HTTPClient.HTTPResponse;
80  import HTTPClient.HttpURLConnection;
81  import HTTPClient.NVPair;
82  import HTTPClient.ParseException;
83  
84  
85  /**
86   * Unit tests for {@link HTTPRequest}.
87   *
88   * @author Philip Aston
89   */
90  public class TestHTTPRequest {
91    private static final Random s_random = new Random();
92  
93    private final SSLContextFactory m_sslContextFactory =
94        new InsecureSSLContextFactory();
95  
96    @Mock private ScriptContext m_scriptContext;
97    @Mock private Statistics m_statistics;
98    @Mock private PluginThreadContext m_threadContext;
99    @Mock private PluginProcessContext m_pluginProcessContext;
100   @Mock private StatisticsForTest m_statisticsForTest;
101 
102   @Mock private Logger m_logger;
103   @Captor private ArgumentCaptor<String> m_stringCaptor;
104 
105   private HTTPRequestHandler m_handler;
106 
107   @Before public void setUp() throws Exception {
108 
109     MockitoAnnotations.initMocks(this);
110 
111     final HTTPPluginThreadState threadState =
112       new HTTPPluginThreadState(m_threadContext,
113                                 m_sslContextFactory,
114                                 null,
115                                 new StandardTimeAuthority());
116 
117     when(m_statistics.isTestInProgress()).thenReturn(false);
118 
119     when(m_scriptContext.getStatistics()).thenReturn(m_statistics);
120     when(m_scriptContext.getLogger()).thenReturn(m_logger);
121 
122     when(m_pluginProcessContext.getPluginThreadListener())
123       .thenReturn(threadState);
124     when(m_pluginProcessContext.getScriptContext()).thenReturn(m_scriptContext);
125     when(m_pluginProcessContext.getStatisticsServices())
126       .thenReturn(StatisticsServicesImplementation.getInstance());
127 
128     new PluginRegistry() {
129       {
130         setInstance(this);
131       }
132 
133       @Override
134       public void register(final GrinderPlugin plugin) throws GrinderException {
135         plugin.initialize(m_pluginProcessContext);
136       }
137     };
138 
139     HTTPPlugin.getPlugin().initialize(m_pluginProcessContext);
140 
141     // Discard the registration of statistic views.
142     reset(m_statistics);
143 
144     m_handler = new HTTPRequestHandler();
145     m_handler.start();
146   }
147 
148   @After public void tearDown() throws Exception {
149     m_handler.shutdown();
150   }
151 
152   @Test public void testTimeout() throws Exception {
153     final HTTPPluginConnectionDefaults connectionDefaults =
154       HTTPPluginConnectionDefaults.getConnectionDefaults();
155 
156     final int originalTimeout = connectionDefaults.getTimeout();
157     final String originalProxyHost = connectionDefaults.getProxyHost();
158     final int originalProxyPort = connectionDefaults.getProxyPort();
159 
160     try {
161       connectionDefaults.setTimeout(1);
162 
163       try {
164         final HTTPRequest request = new HTTPRequest();
165         request.GET("http://idontexist.grinder.sf.net");
166         fail("Expected TimeoutException");
167       }
168       catch (final TimeoutException e) {
169       }
170 
171       try {
172         connectionDefaults.setProxyServer("idontexist.grinder.sf.net", 8080);
173         final HTTPRequest request2 = new HTTPRequest();
174         request2.GET("http://idontexist.grinder.sf.net");
175         fail("Expected TimeoutException");
176       }
177       catch (final TimeoutException e) {
178       }
179     }
180     finally {
181       connectionDefaults.setTimeout(originalTimeout);
182       connectionDefaults.setProxyServer(originalProxyHost, originalProxyPort);
183     }
184   }
185 
186   @Test public void testSetUrl() throws Exception {
187     final HTTPRequest httpRequest = new HTTPRequest();
188 
189     assertNull(httpRequest.getUrl());
190 
191     try {
192       httpRequest.setUrl("foo/bah");
193       fail("Expected URLException");
194     }
195     catch (final URLException e) {
196     }
197 
198     assertNull(httpRequest.getUrl());
199 
200     try {
201       httpRequest.setUrl("http://foo:bah/blah");
202       fail("Expected ParseException");
203     }
204     catch (final ParseException e) {
205     }
206 
207     assertNull(httpRequest.getUrl());
208 
209     httpRequest.setUrl("http://foo/bah");
210 
211     assertEquals("http://foo/bah", httpRequest.getUrl());
212   }
213 
214   @Test public void testSetHeaders() {
215     final HTTPRequest httpRequest = new HTTPRequest();
216 
217     assertEquals(0, httpRequest.getHeaders().length);
218 
219     final NVPair[] newHeaders = new NVPair[] {
220       new NVPair("name", "value"),
221       new NVPair("another name", "another value"),
222     };
223 
224     httpRequest.setHeaders(newHeaders);
225     assertArraysEqual(newHeaders, httpRequest.getHeaders());
226   }
227 
228   @Test public void testDELETE() throws Exception {
229     final HTTPRequest request = new HTTPRequest();
230 
231     try {
232       request.DELETE();
233       fail("Expected URLException");
234     }
235     catch (final URLException e) {
236     }
237 
238     try {
239       request.DELETE("/partial");
240       fail("Expected URLException");
241     }
242     catch (final URLException e) {
243     }
244 
245     final HTTPResponse response = request.DELETE(m_handler.getURL());
246     assertEquals(200, response.getStatusCode());
247     assertEquals("DELETE / HTTP/1.1", m_handler.getRequestFirstHeader());
248 
249     request.setUrl(m_handler.getURL());
250     final HTTPResponse response2 = request.DELETE("/foo");
251     assertEquals(200, response2.getStatusCode());
252     assertEquals("DELETE /foo HTTP/1.1", m_handler.getRequestFirstHeader());
253 
254     final HTTPResponse response3 = request.DELETE();
255     assertEquals(200, response3.getStatusCode());
256     assertEquals("DELETE / HTTP/1.1", m_handler.getRequestFirstHeader());
257 
258     final NVPair[] headers = {
259       new NVPair("x", "212"),
260       new NVPair("y", "321"),
261     };
262 
263     final HTTPResponse response4 = request.DELETE("/", headers);
264     assertEquals(200, response4.getStatusCode());
265     assertEquals("DELETE / HTTP/1.1", m_handler.getRequestFirstHeader());
266     m_handler.assertRequestContainsHeader("x: 212");
267     m_handler.assertRequestContainsHeader("y: 321");
268 
269     final NVPair[] headers2 = {
270       new NVPair("x", "1"),
271       new NVPair("y", "2"),
272       new NVPair("z", "3"),
273     };
274 
275     request.setHeaders(headers2);
276 
277     request.DELETE("/", headers);
278     m_handler.assertRequestContainsHeader("x: 212");
279     m_handler.assertRequestContainsHeader("y: 321");
280     m_handler.assertRequestContainsHeader("z: 3");
281     m_handler.assertRequestDoesNotContainHeader("y: 2");
282   }
283 
284   @Test public void testGET() throws Exception {
285     final HTTPRequest request = new HTTPRequest();
286 
287     try {
288       request.GET();
289       fail("Expected URLException");
290     }
291     catch (final URLException e) {
292     }
293 
294     try {
295       request.GET("#partial");
296       fail("Expected URLException");
297     }
298     catch (final URLException e) {
299     }
300 
301     final HTTPResponse response = request.GET(m_handler.getURL());
302     assertEquals(200, response.getStatusCode());
303     assertEquals("GET / HTTP/1.1", m_handler.getRequestFirstHeader());
304 
305     request.setUrl(m_handler.getURL());
306     final HTTPResponse response2 = request.GET("/foo");
307     assertEquals(200,  response2.getStatusCode());
308     assertEquals("GET /foo HTTP/1.1", m_handler.getRequestFirstHeader());
309 
310     final HTTPResponse response3 = request.GET();
311     assertEquals(200, response3.getStatusCode());
312     assertEquals("GET / HTTP/1.1", m_handler.getRequestFirstHeader());
313 
314     final NVPair[] parameters4 = {
315       new NVPair("some", "header"),
316       new NVPair("y", "321"),
317     };
318 
319     final HTTPResponse response4 = request.GET("/lah/de/dah", parameters4);
320     assertEquals(200, response4.getStatusCode());
321     assertEquals("GET /lah/de/dah?some=header&y=321 HTTP/1.1",
322                  m_handler.getRequestFirstHeader());
323 
324     final NVPair[] parameters5 = {
325       new NVPair("another", "header"),
326       new NVPair("y", "331"),
327     };
328 
329     request.setUrl(m_handler.getURL() + "/lah/");
330     final HTTPResponse response5 = request.GET(parameters5);
331     assertEquals(200, response5.getStatusCode());
332     assertEquals("GET /lah/?another=header&y=331 HTTP/1.1",
333                  m_handler.getRequestFirstHeader());
334 
335     final NVPair[] headers = {
336       new NVPair("key", "value"),
337     };
338 
339     request.setHeaders(headers);
340     final HTTPResponse response6 = request.GET();
341     assertEquals(200, response6.getStatusCode());
342     assertEquals("GET /lah/ HTTP/1.1", m_handler.getRequestFirstHeader());
343     m_handler.assertRequestContainsHeader("key: value");
344 
345     final NVPair[] headers2 = {
346       new NVPair("key", "anotherValue"),
347       new NVPair("x", "1"),
348       new NVPair("y", "2"),
349     };
350 
351     request.GET("/", null, headers2);
352     m_handler.assertRequestContainsHeader("x: 1");
353     m_handler.assertRequestContainsHeader("y: 2");
354     m_handler.assertRequestContainsHeader("key: anotherValue");
355     m_handler.assertRequestDoesNotContainHeader("key: value");
356 
357     final HTTPResponse response7 = request.GET("//multipleSlashes");
358     assertEquals(200, response7.getStatusCode());
359     assertEquals("GET //multipleSlashes HTTP/1.1",
360                  m_handler.getRequestFirstHeader());
361 
362     request.GET("/#a fragment", null, new NVPair[] {});
363     assertEquals("GET / HTTP/1.1", m_handler.getRequestFirstHeader());
364     m_handler.assertRequestDoesNotContainHeader("key: anotherValue");
365     m_handler.assertRequestContainsHeader("key: value");
366   }
367 
368   @Test public void testHEAD() throws Exception {
369     final HTTPRequest request = new HTTPRequest();
370 
371     try {
372       request.HEAD();
373       fail("Expected URLException");
374     }
375     catch (final URLException e) {
376     }
377 
378     try {
379       request.HEAD("?partial");
380       fail("Expected URLException");
381     }
382     catch (final URLException e) {
383     }
384 
385     final HTTPResponse response = request.HEAD(m_handler.getURL());
386     assertEquals(200, response.getStatusCode());
387     assertEquals("HEAD / HTTP/1.1", m_handler.getRequestFirstHeader());
388 
389     request.setUrl(m_handler.getURL());
390     final HTTPResponse response2 = request.HEAD("/foo");
391     assertEquals(200, response2.getStatusCode());
392     assertEquals("HEAD /foo HTTP/1.1", m_handler.getRequestFirstHeader());
393 
394     final HTTPResponse response3 = request.HEAD();
395     assertEquals(200, response3.getStatusCode());
396     assertEquals("HEAD / HTTP/1.1", m_handler.getRequestFirstHeader());
397 
398     final NVPair[] parameters4 = {
399       new NVPair("some", "header"),
400       new NVPair("y", "321"),
401     };
402 
403     final HTTPResponse response4 = request.HEAD("/lah/de/dah", parameters4);
404     assertEquals(200, response4.getStatusCode());
405     assertEquals("HEAD /lah/de/dah?some=header&y=321 HTTP/1.1",
406                  m_handler.getRequestFirstHeader());
407 
408     final NVPair[] parameters5 = {
409       new NVPair("another", "header"),
410       new NVPair("y", "331"),
411     };
412 
413     request.setUrl(m_handler.getURL() + "/lah/");
414     final HTTPResponse response5 = request.HEAD(parameters5);
415     assertEquals(200, response5.getStatusCode());
416     assertEquals("HEAD /lah/?another=header&y=331 HTTP/1.1",
417                  m_handler.getRequestFirstHeader());
418 
419     final NVPair[] headers6 = {
420       new NVPair("key", "value"),
421     };
422 
423     request.setHeaders(headers6);
424     final HTTPResponse response6 = request.HEAD();
425     assertEquals(200, response6.getStatusCode());
426     assertEquals("HEAD /lah/ HTTP/1.1", m_handler.getRequestFirstHeader());
427     m_handler.assertRequestContainsHeader("key: value");
428   }
429 
430   @Test public void testOPTIONS() throws Exception {
431     final HTTPRequest request = new HTTPRequest();
432 
433     try {
434       request.OPTIONS();
435       fail("Expected URLException");
436     }
437     catch (final URLException e) {
438     }
439 
440     try {
441       request.OPTIONS("///::partial");
442       fail("Expected URLException");
443     }
444     catch (final URLException e) {
445     }
446 
447     final HTTPResponse response = request.OPTIONS(m_handler.getURL());
448     assertEquals(200, response.getStatusCode());
449     assertEquals("OPTIONS / HTTP/1.1", m_handler.getRequestFirstHeader());
450 
451     request.setUrl(m_handler.getURL());
452     final HTTPResponse response2 = request.OPTIONS("/foo");
453     assertEquals(200, response2.getStatusCode());
454     assertEquals("OPTIONS /foo HTTP/1.1", m_handler.getRequestFirstHeader());
455 
456     final HTTPResponse response3 = request.OPTIONS();
457     assertEquals(200, response3.getStatusCode());
458     assertEquals("OPTIONS / HTTP/1.1", m_handler.getRequestFirstHeader());
459 
460     final byte[] data4 = randomBytes(10);
461 
462     final HTTPResponse response4 = request.OPTIONS("/blah", data4);
463     assertEquals(200, response4.getStatusCode());
464     assertEquals("OPTIONS /blah HTTP/1.1", m_handler.getRequestFirstHeader());
465     assertArraysEqual(data4, m_handler.getLastRequestBody());
466 
467     final byte[] data5 = randomBytes(100);
468 
469     request.setUrl(m_handler.getURL() + "/lah/");
470     request.setData(data5);
471     final HTTPResponse response5 = request.OPTIONS("/blah");
472     assertEquals(200, response5.getStatusCode());
473     assertEquals("OPTIONS /blah HTTP/1.1", m_handler.getRequestFirstHeader());
474     assertArraysEqual(data5, m_handler.getLastRequestBody());
475 
476     final NVPair[] headers6 = {
477       new NVPair("key", "value"),
478     };
479 
480     request.setHeaders(headers6);
481     final HTTPResponse response6 = request.OPTIONS();
482     assertEquals(200, response6.getStatusCode());
483     assertEquals("OPTIONS /lah/ HTTP/1.1", m_handler.getRequestFirstHeader());
484     m_handler.assertRequestContainsHeader("key: value");
485 
486     final byte[] data6 = randomBytes(10000);
487 
488     final HTTPResponse response7 =
489       request.OPTIONS("/blah", new ByteArrayInputStream(data6));
490     assertEquals(200, response7.getStatusCode());
491     assertEquals("OPTIONS /blah HTTP/1.1", m_handler.getRequestFirstHeader());
492     assertArraysEqual(data6, m_handler.getLastRequestBody());
493 
494     final byte[] data7 = randomBytes(10000);
495 
496     final HTTPResponse response8 =
497       request.OPTIONS("/blah", new ByteArrayInputStream(data7), headers6);
498     assertEquals(200, response8.getStatusCode());
499     assertEquals("OPTIONS /blah HTTP/1.1", m_handler.getRequestFirstHeader());
500     assertArraysEqual(data7, m_handler.getLastRequestBody());
501     m_handler.assertRequestContainsHeader("key: value");
502   }
503 
504   @Test public void testPOST() throws Exception {
505     final HTTPRequest request = new HTTPRequest();
506 
507     try {
508       request.POST();
509       fail("Expected URLException");
510     }
511     catch (final URLException e) {
512     }
513 
514     try {
515       request.POST("#:/partial");
516       fail("Expected URLException");
517     }
518     catch (final URLException e) {
519     }
520 
521     final HTTPResponse response = request.POST(m_handler.getURL());
522     assertEquals(200, response.getStatusCode());
523     assertEquals("POST / HTTP/1.1", m_handler.getRequestFirstHeader());
524 
525     request.setUrl(m_handler.getURL());
526     final HTTPResponse response2 = request.POST("/foo");
527     assertEquals(200, response2.getStatusCode());
528     assertEquals("POST /foo HTTP/1.1", m_handler.getRequestFirstHeader());
529 
530     final HTTPResponse response3 = request.POST();
531     assertEquals(200, response3.getStatusCode());
532     assertEquals("POST / HTTP/1.1", m_handler.getRequestFirstHeader());
533 
534     final byte[] data4 = randomBytes(10);
535 
536     final HTTPResponse response4 = request.POST("/blah", data4);
537     assertEquals(200, response4.getStatusCode());
538     assertEquals("POST /blah HTTP/1.1", m_handler.getRequestFirstHeader());
539     assertArraysEqual(data4, m_handler.getLastRequestBody());
540 
541     final byte[] data5 = randomBytes(100);
542 
543     request.setUrl(m_handler.getURL() + "/lah/");
544     request.setData(data5);
545     final HTTPResponse response5 = request.POST("/blah");
546     assertEquals(200, response5.getStatusCode());
547     assertEquals("POST /blah HTTP/1.1", m_handler.getRequestFirstHeader());
548     assertArraysEqual(data5, m_handler.getLastRequestBody());
549 
550     final NVPair[] headers6 = {
551       new NVPair("key", "value"),
552     };
553 
554     request.setHeaders(headers6);
555     final HTTPResponse response6 = request.POST();
556     assertEquals(200, response6.getStatusCode());
557     assertEquals("POST /lah/ HTTP/1.1", m_handler.getRequestFirstHeader());
558     m_handler.assertRequestContainsHeader("key: value");
559 
560     final NVPair[] formData7 = {
561       new NVPair("Vessel", "Grace of Lefkas"),
562     };
563 
564     final HTTPResponse response7 = request.POST("/foo?abc=def", formData7);
565     assertEquals(200, response7.getStatusCode());
566     assertEquals("POST /foo?abc=def HTTP/1.1",
567                  m_handler.getRequestFirstHeader());
568     m_handler.assertRequestContainsHeader("key: value");
569     final String bodyText7 = new String(m_handler.getLastRequestBody());
570     assertTrue(bodyText7.indexOf("Vessel=Grace+of+Lefkas") > -1);
571 
572     final NVPair[] formData8 = {
573       new NVPair("LOA", "12.3m"),
574       new NVPair("Draught", "1.7"),
575     };
576 
577     request.setFormData(formData8);
578 
579     final HTTPResponse response8 = request.POST();
580     assertEquals(200, response8.getStatusCode());
581     assertEquals("POST /lah/ HTTP/1.1", m_handler.getRequestFirstHeader());
582     m_handler.assertRequestContainsHeader("key: value");
583     assertArraysEqual(data5, m_handler.getLastRequestBody());
584 
585     request.setData(null);
586 
587     final HTTPResponse response9 = request.POST();
588     assertEquals(200, response9.getStatusCode());
589     assertEquals("POST /lah/ HTTP/1.1", m_handler.getRequestFirstHeader());
590     m_handler.assertRequestContainsHeader("key: value");
591     final String bodyText9 = new String(m_handler.getLastRequestBody());
592     assertTrue(bodyText9.indexOf("LOA=12.3m") > -1);
593 
594     final HTTPResponse response10 = request.POST(formData7);
595     assertEquals(200, response10.getStatusCode());
596     assertEquals("POST /lah/ HTTP/1.1", m_handler.getRequestFirstHeader());
597     final String bodyText10 = new String(m_handler.getLastRequestBody());
598     assertTrue(bodyText10.indexOf("Vessel=Grace+of+Lefkas") > -1);
599 
600     final byte[] data6 = randomBytes(10000);
601 
602     final HTTPResponse response11 = request.POST("/bhxhh",
603                                                  new ByteArrayInputStream(data6));
604     assertEquals(200, response11.getStatusCode());
605     assertEquals("POST /bhxhh HTTP/1.1", m_handler.getRequestFirstHeader());
606     assertArraysEqual(data6, m_handler.getLastRequestBody());
607 
608     final byte[] data7 = randomBytes(10000);
609 
610     final HTTPResponse response12 =
611       request.POST("/bhxhh", new ByteArrayInputStream(data7), headers6);
612     assertEquals(200, response12.getStatusCode());
613     assertEquals("POST /bhxhh HTTP/1.1", m_handler.getRequestFirstHeader());
614     assertArraysEqual(data7, m_handler.getLastRequestBody());
615     m_handler.assertRequestContainsHeader("key: value");
616 
617     headers6[0] = new NVPair("Content-Length", Integer.toString(data7.length));
618 
619     final HTTPResponse response13 =
620       request.POST("/bhxhh", new ByteArrayInputStream(data7), headers6);
621     assertEquals(200, response13.getStatusCode());
622     assertEquals("POST /bhxhh HTTP/1.1", m_handler.getRequestFirstHeader());
623     assertArraysEqual(data7, m_handler.getLastRequestBody());
624     m_handler.assertRequestContainsHeader("Content-length: " + data7.length);
625   }
626 
627   @Test public void testPOSTMultiPart() throws Exception {
628     final HTTPRequest request = new HTTPRequest();
629     request.setUrl(m_handler.getURL());
630 
631     final NVPair[] formData = {
632         new NVPair("Vessel", "Grace of Lefkas"),
633       };
634 
635     request.setHeaders(new NVPair[] { new NVPair("key", "value"), });
636 
637     final NVPair[] headers2 = {
638         new NVPair("Content-Type", "multipart/form-data; boundary=---------------------------7db2b32b130706"),
639       };
640 
641     final HTTPResponse response =
642       request.POST("/foo?abc=def", formData, headers2, true);
643     assertEquals(200, response.getStatusCode());
644     assertEquals("POST /foo?abc=def HTTP/1.1",
645                  m_handler.getRequestFirstHeader());
646 
647     m_handler.assertRequestContainsHeader("key: value");
648 
649     final String bodyText = new String(m_handler.getLastRequestBody());
650     assertTrue(bodyText.indexOf("Grace of Lefkas") > -1);
651   }
652 
653   @Test public void testPUT() throws Exception {
654     final HTTPRequest request = new HTTPRequest();
655 
656     try {
657       request.PUT();
658       fail("Expected URLException");
659     }
660     catch (final URLException e) {
661     }
662 
663     try {
664       request.PUT("?:/partial");
665       fail("Expected URLException");
666     }
667     catch (final URLException e) {
668     }
669 
670     final HTTPResponse response = request.PUT(m_handler.getURL());
671     assertEquals(200, response.getStatusCode());
672     assertEquals("PUT / HTTP/1.1", m_handler.getRequestFirstHeader());
673 
674     request.setUrl(m_handler.getURL());
675     final HTTPResponse response2 = request.PUT("/foo");
676     assertEquals(200, response2.getStatusCode());
677     assertEquals("PUT /foo HTTP/1.1", m_handler.getRequestFirstHeader());
678 
679     final HTTPResponse response3 = request.PUT();
680     assertEquals(200, response3.getStatusCode());
681     assertEquals("PUT / HTTP/1.1", m_handler.getRequestFirstHeader());
682 
683     final byte[] data4 = randomBytes(10);
684 
685     final HTTPResponse response4 = request.PUT("/blah", data4);
686     assertEquals(200, response4.getStatusCode());
687     assertEquals("PUT /blah HTTP/1.1", m_handler.getRequestFirstHeader());
688     assertArraysEqual(data4, m_handler.getLastRequestBody());
689 
690     final byte[] data5 = randomBytes(100);
691 
692     request.setUrl(m_handler.getURL() + "/lah/");
693     request.setData(data5);
694     final HTTPResponse response5 = request.PUT("/blah");
695     assertEquals(200, response5.getStatusCode());
696     assertEquals("PUT /blah HTTP/1.1", m_handler.getRequestFirstHeader());
697     assertArraysEqual(data5, m_handler.getLastRequestBody());
698 
699     final NVPair[] headers6 = {
700       new NVPair("key", "value"),
701     };
702 
703     request.setHeaders(headers6);
704     final HTTPResponse response6 = request.PUT();
705     assertEquals(200, response6.getStatusCode());
706     assertEquals("PUT /lah/ HTTP/1.1", m_handler.getRequestFirstHeader());
707     m_handler.assertRequestContainsHeader("key: value");
708 
709     final byte[] data7 = randomBytes(10000);
710 
711     final HTTPResponse response7 = request.PUT("/bhhh",
712                                                new ByteArrayInputStream(data7));
713     assertEquals(200, response7.getStatusCode());
714     assertEquals("PUT /bhhh HTTP/1.1", m_handler.getRequestFirstHeader());
715     assertArraysEqual(data7, m_handler.getLastRequestBody());
716   }
717 
718   @Test public void testTRACE() throws Exception {
719     final HTTPRequest request = new HTTPRequest();
720 
721     try {
722       request.TRACE();
723       fail("Expected URLException");
724     }
725     catch (final URLException e) {
726     }
727 
728     try {
729       request.TRACE("??partial");
730       fail("Expected URLException");
731     }
732     catch (final URLException e) {
733     }
734 
735     final HTTPResponse response = request.TRACE(m_handler.getURL());
736     assertEquals(200, response.getStatusCode());
737     assertEquals("TRACE / HTTP/1.1", m_handler.getRequestFirstHeader());
738 
739     request.setUrl(m_handler.getURL());
740     final HTTPResponse response2 = request.TRACE("/foo");
741     assertEquals(200, response2.getStatusCode());
742     assertEquals("TRACE /foo HTTP/1.1", m_handler.getRequestFirstHeader());
743 
744     final HTTPResponse response3 = request.TRACE();
745     assertEquals(200, response3.getStatusCode());
746     assertEquals("TRACE / HTTP/1.1", m_handler.getRequestFirstHeader());
747 
748     final NVPair[] headers4 = {
749       new NVPair("key", "value"),
750     };
751 
752     request.setUrl(m_handler.getURL() + "/lah/");
753     request.setHeaders(headers4);
754     final HTTPResponse response4 = request.TRACE();
755     assertEquals(200, response4.getStatusCode());
756     assertEquals("TRACE /lah/ HTTP/1.1", m_handler.getRequestFirstHeader());
757     m_handler.assertRequestContainsHeader("key: value");
758   }
759 
760   @Test public void testToString() throws Exception {
761     final HTTPRequest request = new HTTPRequest();
762 
763     assertEquals("<Undefined URL>\n", request.toString());
764 
765     request.setUrl("http://grinder.sf.net/");
766     assertEquals("http://grinder.sf.net/\n", request.toString());
767 
768     request.setHeaders(new NVPair[] {
769                          new NVPair("home", "end"),
770                          new NVPair("pause", "insert"),
771                        });
772 
773     assertEquals("http://grinder.sf.net/\nhome: end\npause: insert\n",
774                  request.toString());
775   }
776 
777   @Test public void testSetDataFromFile() throws Exception {
778 
779     final File file = File.createTempFile("testing", "123");
780     file.deleteOnExit();
781 
782     final OutputStream out = new FileOutputStream(file);
783 
784     final byte[] data5 = randomBytes(10);
785 
786     out.write(data5);
787     out.close();
788 
789     final HTTPRequest request = new HTTPRequest();
790     request.setDataFromFile(file.getPath());
791 
792     assertArraysEqual(data5, request.getData());
793 
794   }
795 
796   @Test public void testResponseProcessing() throws Exception {
797     final HTTPRequest request = new HTTPRequest();
798     request.GET(m_handler.getURL());
799 
800     verify(m_logger).info(m_stringCaptor.capture());
801 
802     final String message = m_stringCaptor.getValue();
803     assertTrue(message.indexOf("200") >= 0);
804     assertEquals(-1, message.indexOf("Redirect"));
805 
806     verify(m_statistics).isTestInProgress();
807     verifyNoMoreInteractions(m_statistics);
808   }
809 
810   @Test public void testRedirectResponseProcessing() throws Exception {
811 
812     final int[] redirectCodes = {
813         302,
814         HttpURLConnection.HTTP_MOVED_PERM,
815         HttpURLConnection.HTTP_MOVED_TEMP,
816         307,
817     };
818 
819     for (final int redirectCode : redirectCodes) {
820       final HTTPRequestHandler handler = new HTTPRequestHandler() {
821         @Override
822         protected void writeHeaders(final StringBuffer response) {
823           response.append("HTTP/1.0 ");
824           response.append(redirectCode);
825           response.append(" Moved Temporarily\r\n"); // whatever
826         }
827       };
828       handler.start();
829 
830       when(m_statistics.isTestInProgress()).thenReturn(true);
831       when(m_statistics.getForCurrentTest()).thenReturn(m_statisticsForTest);
832 
833       final HTTPRequest request = new HTTPRequest();
834       final HTTPResponse response = request.GET(handler.getURL());
835       assertNotNull(response);
836 
837       verify(m_logger, atLeastOnce()).info(m_stringCaptor.capture());
838       final String message = m_stringCaptor.getValue();
839 
840       assertTrue(message.indexOf(Integer.toString(redirectCode)) >= 0);
841       assertTrue(message.indexOf("Redirect") >= 0);
842 
843       handler.shutdown();
844     }
845   }
846 
847   @Test public void testBadRequestResponseProcessing() throws Exception {
848     final HTTPRequestHandler handler = new HTTPRequestHandler() {
849       @Override
850       protected void writeHeaders(final StringBuffer response) {
851         response.append("HTTP/1.0 400 Bad Request\r\n");
852       }
853     };
854 
855     handler.start();
856 
857     when(m_statistics.isTestInProgress()).thenReturn(true);
858     when(m_statistics.getForCurrentTest()).thenReturn(m_statisticsForTest);
859 
860     final HTTPRequest request = new HTTPRequest();
861     final HTTPResponse response = request.GET(handler.getURL());
862     assertNotNull(response);
863 
864     verify(m_logger).info(m_stringCaptor.capture());
865     final String message = m_stringCaptor.getValue();
866     assertTrue(message.indexOf("400") >= 0);
867 
868     handler.shutdown();
869   }
870 
871   @Test public void testSubclassProcessResponse() throws Exception {
872     final Object[] resultHolder = new Object[1];
873 
874     final HTTPRequest request = new HTTPRequest() {
875         @Override
876         public void processResponse(final HTTPResponse response) {
877           resultHolder[0] = response;
878         }
879       };
880 
881     final HTTPResponse response = request.GET(m_handler.getURL());
882 
883     assertSame(response, resultHolder[0]);
884   }
885 
886   @Test public void testConnectionTimingsAndStatistics() throws Exception {
887 
888     final ListTimeAuthority timeAuthority =
889       new ListTimeAuthority(new long[] {
890           100, // start time
891           101, // start time (internal to HTTPResponse - i.e. post redirect)
892           123, // DNS time
893           200, // connection time
894           219, // time to first byte
895       });
896 
897     final HTTPPluginThreadState threadState =
898       new HTTPPluginThreadState(m_threadContext,
899                                 m_sslContextFactory,
900                                 null,
901                                 timeAuthority);
902 
903     when(m_pluginProcessContext.getPluginThreadListener())
904       .thenReturn(threadState);
905 
906     when(m_statistics.isTestInProgress()).thenReturn(true);
907     when(m_statistics.getForCurrentTest()).thenReturn(m_statisticsForTest);
908 
909     final HTTPRequest request = new HTTPRequest();
910     final String bodyText = "Your heart's gone the colour of Coca Cola\n";
911     m_handler.setBody(bodyText);
912     final HTTPResponse response = request.GET(m_handler.getURL());
913     assertEquals(200, response.getStatusCode());
914     assertEquals("GET / HTTP/1.1", m_handler.getRequestFirstHeader());
915 
916     verify(m_statisticsForTest)
917       .addLong(StatisticsIndexMap.HTTP_PLUGIN_RESPONSE_LENGTH_KEY,
918                bodyText.length());
919     verify(m_statisticsForTest)
920       .setLong(StatisticsIndexMap.HTTP_PLUGIN_RESPONSE_STATUS_KEY, 200);
921     verify(m_statisticsForTest)
922       .addLong(StatisticsIndexMap.HTTP_PLUGIN_DNS_TIME_KEY, 22);
923     verify(m_statisticsForTest)
924       .addLong(StatisticsIndexMap.HTTP_PLUGIN_CONNECT_TIME_KEY, 99);
925     verify(m_statisticsForTest)
926       .addLong(StatisticsIndexMap.HTTP_PLUGIN_FIRST_BYTE_TIME_KEY, 119);
927     verify(m_statisticsForTest)
928       .addLong(StatisticsIndexMap.HTTP_PLUGIN_CONNECTIONS_ESTABLISHED, 1);
929     verifyNoMoreInteractions(m_statisticsForTest);
930 
931     try {
932       timeAuthority.getTimeInMilliseconds();
933       fail("Not all times used");
934     }
935     catch (final ArrayIndexOutOfBoundsException e) {
936     }
937 
938     assertTrue(response.getInputStream() instanceof ByteArrayInputStream);
939     assertEquals(bodyText, response.getText());
940   }
941 
942   @Test public void testConnectionTimingsAndStatisticsInvalidTimes()
943       throws Exception {
944 
945     final ListTimeAuthority timeAuthority =
946         new ListTimeAuthority(new long[] {
947             400, // start time
948             400, // start time (internal to HTTPResponse - i.e. post redirect)
949             0, // DNS time
950             0, // connection time
951             419, // time to first byte
952         });
953 
954     final HTTPPluginThreadState threadState =
955       new HTTPPluginThreadState(m_threadContext,
956                                 m_sslContextFactory,
957                                 null,
958                                 timeAuthority);
959 
960     when(m_pluginProcessContext.getPluginThreadListener())
961       .thenReturn(threadState);
962 
963     when(m_statistics.isTestInProgress()).thenReturn(true);
964     when(m_statistics.getForCurrentTest()).thenReturn(m_statisticsForTest);
965 
966     m_handler.setBody(null);
967 
968     final HTTPRequest request = new HTTPRequest();
969     final HTTPResponse response = request.GET(m_handler.getURL());
970     assertEquals(200, response.getStatusCode());
971     assertEquals("GET / HTTP/1.1", m_handler.getRequestFirstHeader());
972 
973     verify(m_statisticsForTest)
974       .addLong(StatisticsIndexMap.HTTP_PLUGIN_RESPONSE_LENGTH_KEY, 0);
975     verify(m_statisticsForTest)
976       .setLong(StatisticsIndexMap.HTTP_PLUGIN_RESPONSE_STATUS_KEY, 200);
977     verify(m_statisticsForTest)
978       .addLong(StatisticsIndexMap.HTTP_PLUGIN_DNS_TIME_KEY, 0);
979     verify(m_statisticsForTest)
980       .addLong(StatisticsIndexMap.HTTP_PLUGIN_CONNECT_TIME_KEY, 0);
981     verify(m_statisticsForTest)
982       .addLong(StatisticsIndexMap.HTTP_PLUGIN_FIRST_BYTE_TIME_KEY, 19);
983     verify(m_statisticsForTest)
984       .addLong(StatisticsIndexMap.HTTP_PLUGIN_CONNECTIONS_ESTABLISHED, 1);
985     verifyNoMoreInteractions(m_statisticsForTest);
986 
987     try {
988       timeAuthority.getTimeInMilliseconds();
989       fail("Not all times used");
990     }
991     catch (final ArrayIndexOutOfBoundsException e) {
992     }
993 
994     assertTrue(response.getInputStream() instanceof ByteArrayInputStream);
995     assertEquals("", response.getText());
996   }
997 
998   @Test public void testSetReadResponseBody() throws Exception {
999 
1000     final ListTimeAuthority timeAuthority =
1001       new ListTimeAuthority(new long[] {
1002           100, // start time
1003           110, // start time (internal to HTTPResponse - i.e. post redirect)
1004           123, // DNS time
1005           200, // connection time
1006           219, // time to first byte
1007       });
1008 
1009     final HTTPPluginThreadState threadState =
1010       new HTTPPluginThreadState(m_threadContext,
1011                                 m_sslContextFactory,
1012                                 null,
1013                                 timeAuthority);
1014 
1015     when(m_pluginProcessContext.getPluginThreadListener())
1016       .thenReturn(threadState);
1017 
1018     when(m_statistics.isTestInProgress()).thenReturn(true);
1019     when(m_statistics.getForCurrentTest()).thenReturn(m_statisticsForTest);
1020 
1021     final HTTPRequest request = new HTTPRequest();
1022 
1023     assertTrue(request.getReadResponseBody());
1024     request.setReadResponseBody(false);
1025     assertFalse(request.getReadResponseBody());
1026     request.setReadResponseBody(true);
1027     assertTrue(request.getReadResponseBody());
1028     request.setReadResponseBody(false);
1029     assertFalse(request.getReadResponseBody());
1030 
1031     final String bodyText =
1032       "Your heart's gone the colour of a dust\nbin\n liner";
1033     m_handler.setBody(bodyText);
1034 
1035     final HTTPResponse response = request.GET(m_handler.getURL());
1036     assertEquals(200, response.getStatusCode());
1037     assertEquals("GET / HTTP/1.1", m_handler.getRequestFirstHeader());
1038 
1039     verify(m_statisticsForTest)
1040       .addLong(StatisticsIndexMap.HTTP_PLUGIN_RESPONSE_LENGTH_KEY, 0);
1041     verify(m_statisticsForTest)
1042       .setLong(StatisticsIndexMap.HTTP_PLUGIN_RESPONSE_STATUS_KEY, 200);
1043     verify(m_statisticsForTest)
1044     .addLong(StatisticsIndexMap.HTTP_PLUGIN_DNS_TIME_KEY, 13);
1045     verify(m_statisticsForTest)
1046       .addLong(StatisticsIndexMap.HTTP_PLUGIN_CONNECT_TIME_KEY, 90);
1047     verify(m_statisticsForTest)
1048       .addLong(StatisticsIndexMap.HTTP_PLUGIN_FIRST_BYTE_TIME_KEY, 119);
1049     verify(m_statisticsForTest)
1050       .addLong(StatisticsIndexMap.HTTP_PLUGIN_CONNECTIONS_ESTABLISHED, 1);
1051     verifyNoMoreInteractions(m_statisticsForTest);
1052 
1053     try {
1054       timeAuthority.getTimeInMilliseconds();
1055       fail("Not all times used");
1056     }
1057     catch (final ArrayIndexOutOfBoundsException e) {
1058     }
1059   }
1060 
1061   @Test public void testWithBadStatistics() throws Exception {
1062 
1063     when(m_statistics.isTestInProgress()).thenReturn(true);
1064     when(m_statistics.getForCurrentTest()).thenReturn(m_statisticsForTest);
1065 
1066     final Exception exception = new InvalidContextException("bah");
1067 
1068     doThrow(exception)
1069     .when(m_statisticsForTest).addLong(isA(String.class), isA(Long.class));
1070 
1071     final HTTPRequest request = new HTTPRequest();
1072 
1073     try {
1074       request.GET(m_handler.getURL());
1075       fail("Expected PluginException");
1076     }
1077     catch (final PluginException e) {
1078       assertSame(exception, e.getCause());
1079     }
1080   }
1081 
1082   @Test public void testConnectionClose() throws Exception {
1083     final HTTPRequest request = new HTTPRequest();
1084 
1085     final HTTPResponse response = request.GET(m_handler.getURL());
1086     assertEquals(200, response.getStatusCode());
1087     assertEquals("GET / HTTP/1.1", m_handler.getRequestFirstHeader());
1088 
1089     HTTPPluginControl.getThreadConnection(m_handler.getURL()).close();
1090 
1091     final HTTPResponse response2 = request.GET(m_handler.getURL());
1092     assertEquals(200, response2.getStatusCode());
1093     assertEquals("GET / HTTP/1.1", m_handler.getRequestFirstHeader());
1094   }
1095 
1096   @Test public void testDCRInstrumentation() throws Exception {
1097     final HTTPRequest request = new HTTPRequest();
1098 
1099     final Instrumenter instrumenter =
1100       new JavaScriptEngineService(DCRContextImplementation.create(null))
1101       .createInstrumenters().get(0);
1102 
1103     final Recorder recorder = mock(Recorder.class);
1104 
1105     final net.grinder.common.Test test = mock(net.grinder.common.Test.class);
1106 
1107     instrumenter.instrument(test, recorder, request,
1108                             HTTPRequest.getHttpMethodFilter());
1109     verifyNoMoreInteractions(recorder);
1110 
1111     try {
1112       request.GET();
1113       fail("Expected URLException");
1114     }
1115     catch (final URLException e) {
1116     }
1117 
1118     // GET() delegates to two more general versions.
1119     verify(recorder, times(3)).start();
1120     verify(recorder, times(3)).end(false);
1121     verifyNoMoreInteractions(recorder);
1122 
1123     try {
1124       request.GET("#partial");
1125       fail("Expected URLException");
1126     }
1127     catch (final URLException e) {
1128     }
1129 
1130     // GET(String) delegates to one more general version.
1131     verify(recorder, times(5)).start();
1132     verify(recorder, times(5)).end(false);
1133     verifyNoMoreInteractions(recorder);
1134 
1135     final HTTPResponse response = request.GET(m_handler.getURL());
1136     assertEquals(200, response.getStatusCode());
1137     assertEquals("GET / HTTP/1.1", m_handler.getRequestFirstHeader());
1138 
1139     verify(recorder, times(7)).start();
1140     verify(recorder, times(2)).end(true);
1141     verifyNoMoreInteractions(recorder);
1142   }
1143 
1144   @Test public void testReadTimeout() throws Exception {
1145 
1146     final HTTPRequestHandler httpServer = new HTTPRequestHandler();
1147     httpServer.setResponseDelay(100);
1148     httpServer.start();
1149 
1150     final HTTPPluginConnectionDefaults connectionDefaults =
1151       HTTPPluginConnectionDefaults.getConnectionDefaults();
1152 
1153     final int originalTimeout = connectionDefaults.getTimeout();
1154 
1155     try {
1156       connectionDefaults.setTimeout(1);
1157 
1158       try {
1159         new HTTPRequest().GET(httpServer.getURL());
1160         fail("Expected TimeoutException");
1161       }
1162       catch (final TimeoutException e) {
1163       }
1164 
1165       // Need another HTTPRequestHandler - the first will have closed its
1166       // socket.
1167       final HTTPRequestHandler httpServer2 = new HTTPRequestHandler();
1168       httpServer2.setResponseDelay(100);
1169       httpServer2.start();
1170 
1171       final HTTPPluginConnection connection =
1172         HTTPPluginControl.getThreadConnection(httpServer2.getURL());
1173 
1174       connection.setTimeout(0);
1175       final HTTPResponse response = new HTTPRequest().GET(httpServer2.getURL());
1176       assertEquals("", response.getText());
1177 
1178       connection.setTimeout(1);
1179 
1180       try {
1181         new HTTPRequest().GET(httpServer2.getURL());
1182         fail("Expected TimeoutException");
1183       }
1184       catch (final TimeoutException e) {
1185       }
1186 
1187       connection.close();
1188     }
1189     finally {
1190       connectionDefaults.setTimeout(originalTimeout);
1191     }
1192   }
1193 
1194   @Test public void testNullHeaders() throws Exception {
1195     try {
1196       new HTTPRequest().GET(m_handler.getURL(), null, null);
1197     }
1198     catch (final NullPointerException e) {
1199       assertContains(e.getMessage(), "headers");
1200     }
1201   }
1202 
1203   @Test public void testNullHeader() throws Exception {
1204 
1205     final NVPair[] headers = { new NVPair("a", "b"),
1206                                null };
1207 
1208     try {
1209       new HTTPRequest().GET(m_handler.getURL(), null, headers);
1210     }
1211     catch (final NullPointerException e) {
1212       assertContains(e.getMessage(), "headers[1]");
1213     }
1214   }
1215 
1216   @Test public void testNullHeaderName() throws Exception {
1217 
1218     final NVPair[] headers = { new NVPair("a", "b"),
1219                                new NVPair(null, "123") };
1220 
1221     try {
1222       new HTTPRequest().GET(m_handler.getURL(), null, headers);
1223     }
1224     catch (final NullPointerException e) {
1225       assertContains(e.getMessage(), "headers[1].getName()");
1226     }
1227   }
1228 
1229   private static byte[] randomBytes(final int max) {
1230     final byte[] result = new byte[s_random.nextInt(max)];
1231     s_random.nextBytes(result);
1232     return result;
1233   }
1234 
1235   private static class ListTimeAuthority implements TimeAuthority {
1236 
1237     private long[] m_times;
1238     private int m_last;
1239 
1240     ListTimeAuthority(final long[] times) {
1241       setTimes(times);
1242     }
1243 
1244     public void setTimes(final long[] times) {
1245       m_times = times;
1246       m_last = -1;
1247     }
1248 
1249     @Override
1250     public long getTimeInMilliseconds() {
1251       return m_times[++m_last];
1252     }
1253   }
1254 }