1
2
3
4 package org.archive.wayback.resourceindex.cdxserver;
5
6 import java.io.IOException;
7 import java.io.PrintWriter;
8 import java.io.StringWriter;
9 import java.util.ArrayList;
10 import java.util.Arrays;
11 import java.util.Collections;
12 import java.util.List;
13 import java.util.logging.Level;
14 import java.util.logging.Logger;
15
16 import javax.servlet.http.Cookie;
17 import javax.servlet.http.HttpServletRequest;
18 import javax.servlet.http.HttpServletResponse;
19
20 import junit.framework.TestCase;
21
22 import org.archive.cdxserver.CDXQuery;
23 import org.archive.cdxserver.CDXServer;
24 import org.archive.cdxserver.auth.AuthToken;
25 import org.archive.cdxserver.auth.PrivTokenAuthChecker;
26 import org.archive.cdxserver.writer.CDXWriter;
27 import org.archive.cdxserver.writer.HttpCDXWriter;
28 import org.archive.format.cdx.CDXFieldConstants;
29 import org.archive.format.cdx.CDXLine;
30 import org.archive.format.cdx.FieldSplitFormat;
31 import org.archive.format.gzip.zipnum.ZipNumCluster;
32 import org.archive.format.gzip.zipnum.ZipNumParams;
33 import org.archive.util.iterator.CloseableIterator;
34 import org.archive.wayback.accesscontrol.robotstxt.redis.RedisRobotExclusionFilterFactory;
35 import org.archive.wayback.core.CaptureSearchResult;
36 import org.archive.wayback.core.CaptureSearchResults;
37 import org.archive.wayback.core.SearchResults;
38 import org.archive.wayback.core.WaybackRequest;
39 import org.archive.wayback.exception.ResourceNotInArchiveException;
40 import org.archive.wayback.exception.RobotAccessControlException;
41 import org.archive.wayback.resourceindex.filters.ExclusionFilter;
42 import org.archive.wayback.util.ObjectFilter;
43 import org.archive.wayback.util.WrappedCloseableIterator;
44 import org.archive.wayback.util.url.KeyMakerUrlCanonicalizer;
45 import org.archive.wayback.webapp.PerfStats;
46 import org.easymock.EasyMock;
47 import org.easymock.IAnswer;
48
49
50
51
52
53
54 public class EmbeddedCDXServerIndexTest extends TestCase {
55
56
57
58
59
60
61
62
63
64 public static class TestCDXServer extends CDXServer {
65 public List<Object[]> capturedArgs = new ArrayList<Object[]>();
66 public CDXLine[] cdxLines;
67
68 @Override
69 public void getCdx(CDXQuery query, AuthToken authToken,
70 CDXWriter responseWriter) throws IOException {
71 capturedArgs.add(new Object[] { query, authToken, responseWriter });
72
73 responseWriter.begin();
74 for (CDXLine cdxLine : cdxLines) {
75 responseWriter.writeLine(cdxLine);
76 }
77 responseWriter.end();
78 }
79
80 public void clearCapturedArgs() {
81 capturedArgs.clear();
82 }
83 }
84
85 EmbeddedCDXServerIndex cut;
86 TestCDXServer testCDXServer;
87
88
89
90
91 protected void setUp() throws Exception {
92 cut = new EmbeddedCDXServerIndex();
93 cut.setCanonicalizer(new KeyMakerUrlCanonicalizer());
94 cut.setCdxServer(testCDXServer = new TestCDXServer());
95
96 Logger.getLogger(PerfStats.class.getName()).setLevel(Level.WARNING);
97 }
98
99
100
101
102
103
104
105
106 protected void setCdxLines(String... lines) {
107
108
109 final FieldSplitFormat fmt = CDXFieldConstants.CDX_ALL_NAMES;
110 testCDXServer.cdxLines = new CDXLine[lines.length];
111 int i = 0;
112 for (String line : lines) {
113 testCDXServer.cdxLines[i++] = new CDXLine(line, fmt);
114 }
115 }
116
117
118
119 final String CDXLINE1 = "com,example)/ 20101124000000 http://example.com/ text/html 200" +
120 " ABCDEFGHIJKLMNOPQRSTUVWXYZ012345 - - 2000 0 /a/a.warc.gz";
121
122 final String CDXLINE2 = "com,norobots)/ 20101124000000 http://example.com/ text/html 200" +
123 " ABCDEFGHIJKLMNOPQRSTUVWXYZ012345 - - 2000 0 /a/a.warc.gz";
124
125
126
127
128 public void testQuery() throws Exception {
129 WaybackRequest wbr = new WaybackRequest();
130 wbr.setRequestUrl("http://example.com/");
131 wbr.setCaptureQueryRequest();
132
133
134
135 FieldSplitFormat fmt = CDXFieldConstants.CDX_ALL_NAMES;
136 testCDXServer.cdxLines = new CDXLine[] {
137 new CDXLine(CDXLINE1, fmt)
138 };
139
140 SearchResults sr = cut.query(wbr);
141
142 assertEquals(1, sr.getReturnedCount());
143
144 assertEquals(1, testCDXServer.capturedArgs.size());
145
146 Object[] args = testCDXServer.capturedArgs.get(0);
147 CDXQuery query = (CDXQuery)args[0];
148 String[] filter = query.getFilter();
149 assertEquals(1, filter.length);
150 assertEquals("!statuscode:(500|502|504)", filter[0]);
151
152 AuthToken authToken = (AuthToken)args[1];
153 assertFalse(authToken.isIgnoreRobots());
154 }
155
156
157
158
159
160 public void testRevisitResolution() throws Exception {
161 WaybackRequest wbr = WaybackRequest.createReplayRequest(
162 "http://example.com/", "20101125000000", null, null);
163 setCdxLines(
164 "com,example)/ 20101124000000 http://example.com/ text/html 200" +
165 " XXXX - - 2000 0 /a/a.warc.gz",
166 "com,example)/ 20101125000000 http://example.com/ warc/revisit 200" +
167 " XXXX - - 2000 0 /a/b.warc.gz",
168 "com,example)/ 20101126000000 http://example.com/ text/html 200" +
169 " XXXX - - 2000 0 /a/c.warc.gz"
170 );
171 SearchResults sr = cut.query(wbr);
172
173 assertEquals(3, sr.getReturnedCount());
174
175 CaptureSearchResults results = (CaptureSearchResults)sr;
176 List<CaptureSearchResult> list = results.getResults();
177 CaptureSearchResult capture2 = list.get(1);
178 assertEquals("20101125000000", capture2.getCaptureTimestamp());
179 assertEquals("20101124000000", capture2.getDuplicateDigestStoredTimestamp());
180 assertEquals("/a/a.warc.gz", capture2.getDuplicatePayloadFile());
181 assertEquals(0, (long)capture2.getDuplicatePayloadOffset());
182 assertEquals(2000, capture2.getDuplicatePayloadCompressedLength());
183
184 assertSame(list.get(0), capture2.getDuplicatePayload());
185 }
186
187
188
189
190
191
192
193
194
195 public void testRevisitResolutionReverse() throws Exception {
196 WaybackRequest wbr = WaybackRequest.createReplayRequest(
197 "http://example.com/", "20101125000000", null, null);
198 final String[] CDXLINES = {
199 "com,example)/ 20101124000000 http://example.com/ text/html 200" +
200 " XXXX - - 2000 0 /a/a.warc.gz",
201 "com,example)/ 20101125000000 http://example.com/ warc/revisit 200" +
202 " XXXX - - 2000 0 /a/b.warc.gz",
203 "com,example)/ 20101126000000 http://example.com/ text/html 200" +
204 " XXXX - - 2000 0 /a/c.warc.gz"
205 };
206 CDXQuery query = new CDXQuery(wbr.getRequestUrl());
207 query.setSort(CDXQuery.SortType.reverse);
208 assertTrue(query.isReverse());
209 CDXToCaptureSearchResultsWriter cdxw = new CDXToCaptureSearchResultsWriter(query, true, false, null);
210
211 final FieldSplitFormat fmt = CDXFieldConstants.CDX_ALL_NAMES;
212 cdxw.begin();
213
214 for (int i = CDXLINES.length; i > 0; i--) {
215 CDXLine line = new CDXLine(CDXLINES[i - 1], fmt);
216 cdxw.trackLine(line);
217 cdxw.writeLine(line);
218 }
219 cdxw.end();
220
221 CaptureSearchResults results = cdxw.getSearchResults();
222
223 assertEquals(3, results.getReturnedCount());
224
225 List<CaptureSearchResult> list = results.getResults();
226
227 CaptureSearchResult capture1 = list.get(0);
228
229
230 assertEquals("20101124000000", capture1.getCaptureTimestamp());
231
232 CaptureSearchResult capture2 = list.get(1);
233 assertEquals("20101125000000", capture2.getCaptureTimestamp());
234 assertEquals("20101124000000", capture2.getDuplicateDigestStoredTimestamp());
235 assertEquals("/a/a.warc.gz", capture2.getDuplicatePayloadFile());
236 assertEquals(0, (long)capture2.getDuplicatePayloadOffset());
237 assertEquals(2000, capture2.getDuplicatePayloadCompressedLength());
238
239 assertSame(capture1, capture2.getDuplicatePayload());
240 }
241
242
243
244
245
246
247
248
249 public void testSoftBlock() throws Exception {
250 WaybackRequest wbr = WaybackRequest.createReplayRequest(
251 "http://example.com/", "20101125000000", null, null);
252 setCdxLines(
253 "com,example)/ 20101124000000 http://example.com/ text/html 200" +
254 " XXXX - X 2000 0 /a/a.warc.gz",
255 "com,example)/ 20101125000000 http://example.com/ warc/revisit 200" +
256 " XXXX - - 2000 0 /a/b.warc.gz",
257 "com,example)/ 20101126000000 http://example.com/ text/html 200" +
258 " XXXX - - 2000 0 /a/c.warc.gz"
259 );
260 CaptureSearchResults results = (CaptureSearchResults)cut.query(wbr);
261
262 assertEquals(2, results.getReturnedCount());
263
264
265 List<CaptureSearchResult> list = results.getResults();
266 assertEquals(2, list.size());
267
268 CaptureSearchResult capture1 = list.get(0);
269 assertEquals("20101125000000", capture1.getCaptureTimestamp());
270
271 CaptureSearchResult capture2 = list.get(1);
272 assertEquals("20101126000000", capture2.getCaptureTimestamp());
273
274
275 assertEquals("20101124000000", capture1.getDuplicateDigestStoredTimestamp());
276 assertEquals("/a/a.warc.gz", capture1.getDuplicatePayloadFile());
277 assertEquals(0, (long)capture1.getDuplicatePayloadOffset());
278 assertEquals(2000, capture1.getDuplicatePayloadCompressedLength());
279
280
281 CaptureSearchResult captureX = capture1.getDuplicatePayload();
282 assertNotNull(captureX);
283 assertEquals("20101124000000", captureX.getCaptureTimestamp());
284
285
286
287
288 assertFalse(capture1.isDuplicateDigest());
289 }
290
291
292
293
294
295
296
297
298
299 public void testSoftBlock_fieldModificationRecognized() throws Exception {
300 WaybackRequest wbr = WaybackRequest.createReplayRequest(
301 "http://example.com/", "20101125000000", null, null);
302 final String[] CDXLINES = {
303
304 "com,example)/ 20101124000000 http://example.com/ text/html 200" +
305 " XXXX - - 2000 0 /a/a.warc.gz",
306 "com,example)/ 20101125000000 http://example.com/ warc/revisit 200" +
307 " XXXX - - 2000 0 /a/b.warc.gz",
308 "com,example)/ 20101126000000 http://example.com/ text/html 200" +
309 " XXXX - - 2000 0 /a/c.warc.gz"
310 };
311 CDXQuery query = new CDXQuery(wbr.getRequestUrl());
312 ExclusionFilter exclusionFilter = new ExclusionFilter() {
313 @Override
314 public int filterObject(CaptureSearchResult o) {
315 if (o.getCaptureTimestamp().startsWith("20101124")) {
316 o.setRobotFlag(CaptureSearchResult.CAPTURE_ROBOT_BLOCKED);
317 }
318 return FILTER_INCLUDE;
319 }
320 };
321 CDXToCaptureSearchResultsWriter cdxw = new CDXToCaptureSearchResultsWriter(query, true, false, null);
322 cdxw.setExclusionFilter(exclusionFilter);
323
324 final FieldSplitFormat fmt = CDXFieldConstants.CDX_ALL_NAMES;
325 cdxw.begin();
326 for (String l : CDXLINES) {
327 CDXLine line = new CDXLine(l, fmt);
328 cdxw.trackLine(line);
329 cdxw.writeLine(line);
330 }
331 cdxw.end();
332
333 CaptureSearchResults results = cdxw.getSearchResults();
334
335
336 assertEquals(2, results.getReturnedCount());
337
338 List<CaptureSearchResult> list = results.getResults();
339
340 CaptureSearchResult capture1 = list.get(0);
341
342 assertEquals("20101125000000", capture1.getCaptureTimestamp());
343
344 CaptureSearchResult captureX = capture1.getDuplicatePayload();
345 assertNotNull(captureX);
346 assertEquals("20101124000000", captureX.getCaptureTimestamp());
347
348
349 assertEquals("X", captureX.getRobotFlags());
350 }
351
352
353
354
355
356
357 public void testSoftBlock_revisitPayloadLookup() throws Exception {
358 WaybackRequest wbr = WaybackRequest.createReplayRequest(
359 "http://example.com/", "20101124000000", null, null);
360 wbr.put(EmbeddedCDXServerIndex.REQUEST_REVISIT_LOOKUP, "true");
361 setCdxLines(
362 "com,example)/ 20101124000000 http://example.com/ text/html 200" +
363 " XXXX - X 2000 0 /a/a.warc.gz",
364 "com,example)/ 20101125000000 http://example.com/ warc/revisit 200" +
365 " XXXX - - 2000 0 /a/b.warc.gz",
366 "com,example)/ 20101126000000 http://example.com/ text/html 200" +
367 " XXXX - - 2000 0 /a/c.warc.gz"
368 );
369 CaptureSearchResults results = (CaptureSearchResults)cut.query(wbr);
370
371 CaptureSearchResult capture1 = results.getResults().get(0);
372 assertEquals("20101124000000", capture1.getCaptureTimestamp());
373 assertSame(capture1, results.getClosest());
374 }
375
376
377
378
379 public void testBuildStatusFilter() {
380 final String[][] CASES = new String[][] {
381 { "!500", "!statuscode:500" },
382 { "! 400|500|502 ", "!statuscode:400|500|502" },
383 { "[23]..", "statuscode:[23].." },
384 { "! ", "" },
385 { "", "" },
386 { null, "" }
387 };
388 for (String[] c : CASES) {
389 assertEquals(c[1], EmbeddedCDXServerIndex.buildStatusFilter(c[0]));
390 }
391 }
392
393
394
395
396
397 public void testQueryWithCustomStatusFilter() throws Exception {
398 WaybackRequest wbr = new WaybackRequest();
399 wbr.setRequestUrl("http://example.com/");
400 wbr.setCaptureQueryRequest();
401
402
403
404 setCdxLines(CDXLINE1);
405
406 cut.setBaseStatusRegexp("");
407 {
408 @SuppressWarnings("unused")
409 SearchResults sr = cut.query(wbr);
410
411 assertEquals(1, testCDXServer.capturedArgs.size());
412
413 Object[] args = testCDXServer.capturedArgs.get(0);
414 CDXQuery query = (CDXQuery)args[0];
415 String[] filter = query.getFilter();
416 assertNull("there should be no filter", filter);
417 }
418
419 testCDXServer.clearCapturedArgs();
420 cut.setBaseStatusRegexp("!500");
421 {
422 @SuppressWarnings("unused")
423 SearchResults sr = cut.query(wbr);
424
425 assertEquals(1, testCDXServer.capturedArgs.size());
426
427 Object[] args = testCDXServer.capturedArgs.get(0);
428 CDXQuery query = (CDXQuery)args[0];
429 String[] filter = query.getFilter();
430 assertEquals(1, filter.length);
431 assertEquals("!statuscode:500", filter[0]);
432 }
433 }
434
435
436
437
438
439
440 public void testIgnoreRobotPaths() throws Exception {
441 cut.setIgnoreRobotPaths(Arrays.asList(new String[]{ "com,norobots" }));
442 WaybackRequest wbr = new WaybackRequest();
443 wbr.setRequestUrl("http://norobots.com/");
444 wbr.setCaptureQueryRequest();
445
446
447
448 setCdxLines(CDXLINE2);
449
450 @SuppressWarnings("unused")
451 SearchResults sr = cut.query(wbr);
452
453 assertEquals(1, testCDXServer.capturedArgs.size());
454
455 Object[] args = testCDXServer.capturedArgs.get(0);
456
457 AuthToken authToken = (AuthToken)args[1];
458 assertTrue(authToken.isIgnoreRobots());
459 }
460
461
462
463
464
465
466
467
468
469 public void testCollapseTime() throws Exception {
470 WaybackRequest wbr = WaybackRequest.createCaptureQueryRequet(
471 "http://example.com/", null, null, null);
472 setCdxLines(CDXLINE1);
473
474 {
475 cut.setTimestampDedupLength(10);
476 @SuppressWarnings("unused")
477 SearchResults sr = cut.query(wbr);
478
479 Object[] args = testCDXServer.capturedArgs.get(0);
480 assertEquals(10, ((CDXQuery)args[0]).getCollapseTime());
481 }
482 testCDXServer.clearCapturedArgs();
483 {
484 wbr.setCollapseTime(8);
485 @SuppressWarnings("unused")
486 SearchResults sr = cut.query(wbr);
487
488 Object[] args = testCDXServer.capturedArgs.get(0);
489 assertEquals(8, ((CDXQuery)args[0]).getCollapseTime());
490 }
491 }
492
493
494
495
496
497
498
499 public void testHandleRequest() throws Exception {
500 HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
501 EasyMock.expect(request.getParameter("url")).andStubReturn("http://example.com/");
502
503 HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
504 StringWriter sw = new StringWriter();
505 EasyMock.expect(response.getWriter()).andReturn(new PrintWriter(sw));
506
507 FieldSplitFormat fmt = CDXFieldConstants.CDX_ALL_NAMES;
508 testCDXServer.cdxLines = new CDXLine[] {
509 new CDXLine(CDXLINE1, fmt)
510 };
511
512 EasyMock.replay(request, response);
513 cut.handleRequest(request, response);
514
515 assertEquals(1, testCDXServer.capturedArgs.size());
516 Object[] args = testCDXServer.capturedArgs.get(0);
517
518 CDXQuery query = (CDXQuery)args[0];
519 assertEquals("API query should not have filter by default", 0, query.getFilter().length);
520
521 assertEquals(CDXLINE1+"\n", sw.toString());
522 }
523
524
525
526
527
528
529 public void testRenderMementoTimemap() throws Exception {
530 HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
531
532 EasyMock.expect(request.getRequestURL()).andAnswer(new IAnswer<StringBuffer>() {
533 @Override
534 public StringBuffer answer() throws Throwable {
535 return new StringBuffer("/timemap/memento/http://example.com/");
536 }
537 });
538 HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
539 StringWriter sw = new StringWriter();
540 EasyMock.expect(response.getWriter()).andReturn(new PrintWriter(sw));
541
542
543
544
545
546
547
548 WaybackRequest wbr = new WaybackRequest();
549 wbr.setRequestUrl("http://example.com/");
550 wbr.setMementoTimemapFormat("memento");
551
552 FieldSplitFormat fmt = CDXFieldConstants.CDX_ALL_NAMES;
553 testCDXServer.cdxLines = new CDXLine[] {
554 new CDXLine(CDXLINE1, fmt)
555 };
556
557 EasyMock.replay(request, response);
558 boolean r = cut.renderMementoTimemap(wbr, request, response);
559
560 assertTrue("renderMementoTimemap returns true", r);
561
562 assertEquals(1, testCDXServer.capturedArgs.size());
563 Object[] args = testCDXServer.capturedArgs.get(0);
564
565 CDXQuery query = (CDXQuery)args[0];
566 assertEquals("API query should not have filter by default", 0, query.getFilter().length);
567
568
569
570
571 assertTrue(sw.toString().startsWith("<http://example.com/>;"));
572 }
573
574
575
576 public static class ExcludeAllFilterFactory extends RedisRobotExclusionFilterFactory {
577 @Override
578 public ExclusionFilter get() {
579 return new ExclusionFilter() {
580 @Override
581 public int filterObject(CaptureSearchResult o) {
582 return ObjectFilter.FILTER_EXCLUDE;
583 }
584 };
585 }
586 }
587
588
589 public static class StubZipNumCluster extends ZipNumCluster {
590 List<String> cdxlines;
591 public StubZipNumCluster(String... cdxlines) {
592 this.cdxlines = Arrays.asList(cdxlines);
593 }
594
595
596 @Override
597 public CloseableIterator<String> getCDXIterator(String key,
598 String start, String end, ZipNumParams params)
599 throws IOException {
600 return new WrappedCloseableIterator<String>(cdxlines.iterator());
601 }
602 }
603
604
605
606
607
608
609
610
611
612 public void testIgnoreRobotsForEmbeds() throws Exception {
613 CDXServer cdxServer = new CDXServer();
614 ZipNumCluster cdxSource = new StubZipNumCluster(
615 "com,example)/style.css 20101124000000 http://example.com/style.css text/css 200"
616 + " ABCDEFGHIJKLMNOPQRSTUVWXYZ012345 - - 2000 0 /a/a.warc.gz");
617 cdxServer.setZipnumSource(cdxSource);
618
619
620
621
622 WaybackAuthChecker authChecker = new WaybackAuthChecker();
623 authChecker.setRobotsExclusions(new ExcludeAllFilterFactory());
624 cdxServer.setAuthChecker(authChecker);
625 cdxServer.afterPropertiesSet();
626 cut.setCdxServer(cdxServer);
627
628 {
629 WaybackRequest wbRequest = WaybackRequest.createReplayRequest(
630 "http://example.com/style.css", "20140101000000", null, null);
631 wbRequest.setCSSContext(true);
632
633 try {
634 cut.query(wbRequest);
635 } catch (RobotAccessControlException ex) {
636 fail("robots.txt exclusion is not disabled for embeds");
637 }
638 }
639
640
641
642
643 {
644 WaybackRequest wbRequest = WaybackRequest.createReplayRequest(
645 "http://example.com/style.css", "20140101000000", null, null);
646
647 try {
648 cut.query(wbRequest);
649 fail("RobotAccessControlException was not thrown");
650 } catch (RobotAccessControlException ex) {
651
652 }
653 }
654
655
656 {
657 HttpServletRequest httpRequest = EasyMock.createNiceMock(HttpServletRequest.class);
658 EasyMock.expect(httpRequest.getParameter("url")).andStubReturn("http://exmaple.com/style.css");
659
660 HttpServletResponse httpResponse = EasyMock.createMock(HttpServletResponse.class);
661
662 final StringWriter output = new StringWriter();
663 EasyMock.expect(httpResponse.getWriter()).andReturn(new PrintWriter(output));
664 httpResponse.setContentType(EasyMock.<String>notNull());
665 EasyMock.expectLastCall().once();
666 httpResponse.setStatus(403);
667 EasyMock.expectLastCall().once();
668 httpResponse.setHeader(EasyMock.eq(HttpCDXWriter.RUNTIME_ERROR_HEADER), EasyMock.matches("(?i).*Robot.*"));
669
670 EasyMock.replay(httpRequest, httpResponse);
671
672 cut.handleRequest(httpRequest, httpResponse);
673
674 EasyMock.verify(httpResponse);
675 }
676
677
678 {
679 final String IGNORE_ROBOTS_TOKEN = "DISABLE-ROBOTS-EXCLUSION";
680 authChecker.setIgnoreRobotsAccessTokens(Collections.singletonList(IGNORE_ROBOTS_TOKEN));
681
682 HttpServletRequest httpRequest = EasyMock.createNiceMock(HttpServletRequest.class);
683 EasyMock.expect(httpRequest.getParameter("url")).andStubReturn("http://exmaple.com/style.css");
684 EasyMock.expect(httpRequest.getCookies()).andStubReturn(
685 new Cookie[] { new Cookie(cdxServer.getCookieAuthToken(),
686 IGNORE_ROBOTS_TOKEN) });
687
688 HttpServletResponse httpResponse = EasyMock.createMock(HttpServletResponse.class);
689
690 final StringWriter output = new StringWriter();
691 EasyMock.expect(httpResponse.getWriter()).andReturn(new PrintWriter(output));
692 httpResponse.setContentType(EasyMock.<String>notNull());
693 EasyMock.expectLastCall().once();
694
695
696
697 EasyMock.replay(httpRequest, httpResponse);
698
699 cut.handleRequest(httpRequest, httpResponse);
700
701
702 EasyMock.verify(httpResponse);
703
704 System.out.println(output.toString());
705 }
706 }
707 }