View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.http.integration;
29  
30  import java.io.BufferedReader;
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.io.InputStreamReader;
34  import java.io.OutputStream;
35  import java.nio.charset.Charset;
36  import java.util.ArrayList;
37  import java.util.List;
38  import java.util.Random;
39  
40  import org.apache.http.Header;
41  import org.apache.http.HttpConnectionMetrics;
42  import org.apache.http.HttpEntity;
43  import org.apache.http.HttpEntityEnclosingRequest;
44  import org.apache.http.HttpException;
45  import org.apache.http.HttpHost;
46  import org.apache.http.HttpRequest;
47  import org.apache.http.HttpRequestInterceptor;
48  import org.apache.http.HttpResponse;
49  import org.apache.http.HttpStatus;
50  import org.apache.http.HttpVersion;
51  import org.apache.http.entity.AbstractHttpEntity;
52  import org.apache.http.entity.ByteArrayEntity;
53  import org.apache.http.entity.ContentType;
54  import org.apache.http.entity.StringEntity;
55  import org.apache.http.impl.DefaultBHttpClientConnection;
56  import org.apache.http.message.BasicHttpEntityEnclosingRequest;
57  import org.apache.http.message.BasicHttpRequest;
58  import org.apache.http.protocol.HTTP;
59  import org.apache.http.protocol.HttpContext;
60  import org.apache.http.protocol.HttpExpectationVerifier;
61  import org.apache.http.protocol.HttpRequestHandler;
62  import org.apache.http.protocol.ImmutableHttpProcessor;
63  import org.apache.http.protocol.RequestConnControl;
64  import org.apache.http.protocol.RequestExpectContinue;
65  import org.apache.http.protocol.RequestTargetHost;
66  import org.apache.http.protocol.RequestUserAgent;
67  import org.apache.http.testserver.HttpClient;
68  import org.apache.http.testserver.HttpServer;
69  import org.apache.http.util.EncodingUtils;
70  import org.apache.http.util.EntityUtils;
71  import org.junit.After;
72  import org.junit.Assert;
73  import org.junit.Before;
74  import org.junit.Test;
75  
76  public class TestSyncHttp {
77  
78      private HttpServer server;
79      private HttpClient client;
80  
81      @Before
82      public void initServer() throws Exception {
83          this.server = new HttpServer();
84          this.server.setTimeout(5000);
85      }
86  
87      @Before
88      public void initClient() throws Exception {
89          this.client = new HttpClient();
90          this.client.setTimeout(5000);
91      }
92  
93      @After
94      public void shutDownServer() throws Exception {
95          if (this.server != null) {
96              this.server.shutdown();
97          }
98      }
99  
100     /**
101      * This test case executes a series of simple GET requests
102      */
103     @Test
104     public void testSimpleBasicHttpRequests() throws Exception {
105 
106         final int reqNo = 20;
107 
108         final Random rnd = new Random();
109 
110         // Prepare some random data
111         final List<byte[]> testData = new ArrayList<byte[]>(reqNo);
112         for (int i = 0; i < reqNo; i++) {
113             final int size = rnd.nextInt(5000);
114             final byte[] data = new byte[size];
115             rnd.nextBytes(data);
116             testData.add(data);
117         }
118 
119         // Initialize the server-side request handler
120         this.server.registerHandler("*", new HttpRequestHandler() {
121 
122             @Override
123             public void handle(
124                     final HttpRequest request,
125                     final HttpResponse response,
126                     final HttpContext context) throws HttpException, IOException {
127 
128                 String s = request.getRequestLine().getUri();
129                 if (s.startsWith("/?")) {
130                     s = s.substring(2);
131                 }
132                 final int index = Integer.parseInt(s);
133                 final byte[] data = testData.get(index);
134                 final ByteArrayEntity entity = new ByteArrayEntity(data);
135                 response.setEntity(entity);
136             }
137 
138         });
139 
140         this.server.start();
141 
142         final DefaultBHttpClientConnection conn = client.createConnection();
143         final HttpHost host = new HttpHost("localhost", this.server.getPort());
144 
145         try {
146             for (int r = 0; r < reqNo; r++) {
147                 if (!conn.isOpen()) {
148                     client.connect(host, conn);
149                 }
150 
151                 final BasicHttpRequest get = new BasicHttpRequest("GET", "/?" + r);
152                 final HttpResponse response = this.client.execute(get, host, conn);
153                 final byte[] received = EntityUtils.toByteArray(response.getEntity());
154                 final byte[] expected = testData.get(r);
155 
156                 Assert.assertEquals(expected.length, received.length);
157                 for (int i = 0; i < expected.length; i++) {
158                     Assert.assertEquals(expected[i], received[i]);
159                 }
160                 if (!this.client.keepAlive(response)) {
161                     conn.close();
162                 }
163             }
164 
165             //Verify the connection metrics
166             final HttpConnectionMetrics cm = conn.getMetrics();
167             Assert.assertEquals(reqNo, cm.getRequestCount());
168             Assert.assertEquals(reqNo, cm.getResponseCount());
169 
170         } finally {
171             conn.close();
172             this.server.shutdown();
173         }
174     }
175 
176     /**
177      * This test case executes a series of simple POST requests with content length
178      * delimited content.
179      */
180     @Test
181     public void testSimpleHttpPostsWithContentLength() throws Exception {
182 
183         final int reqNo = 20;
184 
185         final Random rnd = new Random();
186 
187         // Prepare some random data
188         final List<byte[]> testData = new ArrayList<byte[]>(reqNo);
189         for (int i = 0; i < reqNo; i++) {
190             final int size = rnd.nextInt(5000);
191             final byte[] data = new byte[size];
192             rnd.nextBytes(data);
193             testData.add(data);
194         }
195 
196         // Initialize the server-side request handler
197         this.server.registerHandler("*", new HttpRequestHandler() {
198 
199             @Override
200             public void handle(
201                     final HttpRequest request,
202                     final HttpResponse response,
203                     final HttpContext context) throws HttpException, IOException {
204 
205                 if (request instanceof HttpEntityEnclosingRequest) {
206                     final HttpEntity incoming = ((HttpEntityEnclosingRequest) request).getEntity();
207                     final byte[] data = EntityUtils.toByteArray(incoming);
208 
209                     final ByteArrayEntity outgoing = new ByteArrayEntity(data);
210                     outgoing.setChunked(false);
211                     response.setEntity(outgoing);
212                 } else {
213                     final StringEntity outgoing = new StringEntity("No content");
214                     response.setEntity(outgoing);
215                 }
216             }
217 
218         });
219 
220         this.server.start();
221 
222         final DefaultBHttpClientConnection conn = client.createConnection();
223         final HttpHost host = new HttpHost("localhost", this.server.getPort());
224 
225         try {
226             for (int r = 0; r < reqNo; r++) {
227                 if (!conn.isOpen()) {
228                     client.connect(host, conn);
229                 }
230 
231                 final BasicHttpEntityEnclosingRequest post = new BasicHttpEntityEnclosingRequest("POST", "/");
232                 final byte[] data = testData.get(r);
233                 final ByteArrayEntity outgoing = new ByteArrayEntity(data);
234                 post.setEntity(outgoing);
235 
236                 final HttpResponse response = this.client.execute(post, host, conn);
237                 final byte[] received = EntityUtils.toByteArray(response.getEntity());
238                 final byte[] expected = testData.get(r);
239 
240                 Assert.assertEquals(expected.length, received.length);
241                 for (int i = 0; i < expected.length; i++) {
242                     Assert.assertEquals(expected[i], received[i]);
243                 }
244                 if (!this.client.keepAlive(response)) {
245                     conn.close();
246                 }
247             }
248             //Verify the connection metrics
249             final HttpConnectionMetrics cm = conn.getMetrics();
250             Assert.assertEquals(reqNo, cm.getRequestCount());
251             Assert.assertEquals(reqNo, cm.getResponseCount());
252 
253         } finally {
254             conn.close();
255             this.server.shutdown();
256         }
257     }
258 
259     /**
260      * This test case executes a series of simple POST requests with chunk
261      * coded content content.
262      */
263     @Test
264     public void testSimpleHttpPostsChunked() throws Exception {
265 
266         final int reqNo = 20;
267 
268         final Random rnd = new Random();
269 
270         // Prepare some random data
271         final List<byte[]> testData = new ArrayList<byte[]>(reqNo);
272         for (int i = 0; i < reqNo; i++) {
273             final int size = rnd.nextInt(20000);
274             final byte[] data = new byte[size];
275             rnd.nextBytes(data);
276             testData.add(data);
277         }
278 
279         // Initialize the server-side request handler
280         this.server.registerHandler("*", new HttpRequestHandler() {
281 
282             @Override
283             public void handle(
284                     final HttpRequest request,
285                     final HttpResponse response,
286                     final HttpContext context) throws HttpException, IOException {
287 
288                 if (request instanceof HttpEntityEnclosingRequest) {
289                     final HttpEntity incoming = ((HttpEntityEnclosingRequest) request).getEntity();
290                     final byte[] data = EntityUtils.toByteArray(incoming);
291 
292                     final ByteArrayEntity outgoing = new ByteArrayEntity(data);
293                     outgoing.setChunked(true);
294                     response.setEntity(outgoing);
295                 } else {
296                     final StringEntity outgoing = new StringEntity("No content");
297                     response.setEntity(outgoing);
298                 }
299             }
300 
301         });
302 
303         this.server.start();
304 
305         final DefaultBHttpClientConnection conn = client.createConnection();
306         final HttpHost host = new HttpHost("localhost", this.server.getPort());
307 
308         try {
309             for (int r = 0; r < reqNo; r++) {
310                 if (!conn.isOpen()) {
311                     client.connect(host, conn);
312                 }
313 
314                 final BasicHttpEntityEnclosingRequest post = new BasicHttpEntityEnclosingRequest("POST", "/");
315                 final byte[] data = testData.get(r);
316                 final ByteArrayEntity outgoing = new ByteArrayEntity(data);
317                 outgoing.setChunked(true);
318                 post.setEntity(outgoing);
319 
320                 final HttpResponse response = this.client.execute(post, host, conn);
321                 final byte[] received = EntityUtils.toByteArray(response.getEntity());
322                 final byte[] expected = testData.get(r);
323 
324                 Assert.assertEquals(expected.length, received.length);
325                 for (int i = 0; i < expected.length; i++) {
326                     Assert.assertEquals(expected[i], received[i]);
327                 }
328                 if (!this.client.keepAlive(response)) {
329                     conn.close();
330                 }
331             }
332             //Verify the connection metrics
333             final HttpConnectionMetrics cm = conn.getMetrics();
334             Assert.assertEquals(reqNo, cm.getRequestCount());
335             Assert.assertEquals(reqNo, cm.getResponseCount());
336         } finally {
337             conn.close();
338             this.server.shutdown();
339         }
340     }
341 
342     /**
343      * This test case executes a series of simple HTTP/1.0 POST requests.
344      */
345     @Test
346     public void testSimpleHttpPostsHTTP10() throws Exception {
347 
348         final int reqNo = 20;
349 
350         final Random rnd = new Random();
351 
352         // Prepare some random data
353         final List<byte[]> testData = new ArrayList<byte[]>(reqNo);
354         for (int i = 0; i < reqNo; i++) {
355             final int size = rnd.nextInt(5000);
356             final byte[] data = new byte[size];
357             rnd.nextBytes(data);
358             testData.add(data);
359         }
360 
361         // Initialize the server-side request handler
362         this.server.registerHandler("*", new HttpRequestHandler() {
363 
364             @Override
365             public void handle(
366                     final HttpRequest request,
367                     final HttpResponse response,
368                     final HttpContext context) throws HttpException, IOException {
369 
370                 if (request instanceof HttpEntityEnclosingRequest) {
371                     final HttpEntity incoming = ((HttpEntityEnclosingRequest) request).getEntity();
372                     final byte[] data = EntityUtils.toByteArray(incoming);
373 
374                     final ByteArrayEntity outgoing = new ByteArrayEntity(data);
375                     outgoing.setChunked(false);
376                     response.setEntity(outgoing);
377                 } else {
378                     final StringEntity outgoing = new StringEntity("No content");
379                     response.setEntity(outgoing);
380                 }
381             }
382 
383         });
384 
385         this.server.start();
386 
387         final DefaultBHttpClientConnection conn = client.createConnection();
388         final HttpHost host = new HttpHost("localhost", this.server.getPort());
389 
390         try {
391             for (int r = 0; r < reqNo; r++) {
392                 if (!conn.isOpen()) {
393                     client.connect(host, conn);
394                 }
395 
396                 // Set protocol level to HTTP/1.0
397                 final BasicHttpEntityEnclosingRequest post = new BasicHttpEntityEnclosingRequest(
398                         "POST", "/", HttpVersion.HTTP_1_0);
399                 final byte[] data = testData.get(r);
400                 final ByteArrayEntity outgoing = new ByteArrayEntity(data);
401                 post.setEntity(outgoing);
402 
403                 final HttpResponse response = this.client.execute(post, host, conn);
404                 Assert.assertEquals(HttpVersion.HTTP_1_1, response.getStatusLine().getProtocolVersion());
405                 final byte[] received = EntityUtils.toByteArray(response.getEntity());
406                 final byte[] expected = testData.get(r);
407 
408                 Assert.assertEquals(expected.length, received.length);
409                 for (int i = 0; i < expected.length; i++) {
410                     Assert.assertEquals(expected[i], received[i]);
411                 }
412                 if (!this.client.keepAlive(response)) {
413                     conn.close();
414                 }
415             }
416 
417             //Verify the connection metrics
418             final HttpConnectionMetrics cm = conn.getMetrics();
419             Assert.assertEquals(reqNo, cm.getRequestCount());
420             Assert.assertEquals(reqNo, cm.getResponseCount());
421         } finally {
422             conn.close();
423             this.server.shutdown();
424         }
425     }
426 
427     /**
428      * This test case executes a series of simple POST requests using
429      * the 'expect: continue' handshake.
430      */
431     @Test
432     public void testHttpPostsWithExpectContinue() throws Exception {
433 
434         final int reqNo = 20;
435 
436         final Random rnd = new Random();
437 
438         // Prepare some random data
439         final List<byte[]> testData = new ArrayList<byte[]>(reqNo);
440         for (int i = 0; i < reqNo; i++) {
441             final int size = rnd.nextInt(5000);
442             final byte[] data = new byte[size];
443             rnd.nextBytes(data);
444             testData.add(data);
445         }
446 
447         // Initialize the server-side request handler
448         this.server.registerHandler("*", new HttpRequestHandler() {
449 
450             @Override
451             public void handle(
452                     final HttpRequest request,
453                     final HttpResponse response,
454                     final HttpContext context) throws HttpException, IOException {
455 
456                 if (request instanceof HttpEntityEnclosingRequest) {
457                     final HttpEntity incoming = ((HttpEntityEnclosingRequest) request).getEntity();
458                     final byte[] data = EntityUtils.toByteArray(incoming);
459 
460                     final ByteArrayEntity outgoing = new ByteArrayEntity(data);
461                     outgoing.setChunked(true);
462                     response.setEntity(outgoing);
463                 } else {
464                     final StringEntity outgoing = new StringEntity("No content");
465                     response.setEntity(outgoing);
466                 }
467             }
468 
469         });
470 
471         this.server.start();
472 
473         // Activate 'expect: continue' handshake
474         final DefaultBHttpClientConnection conn = client.createConnection();
475         final HttpHost host = new HttpHost("localhost", this.server.getPort());
476 
477         try {
478             for (int r = 0; r < reqNo; r++) {
479                 if (!conn.isOpen()) {
480                     client.connect(host, conn);
481                 }
482 
483                 final BasicHttpEntityEnclosingRequest post = new BasicHttpEntityEnclosingRequest("POST", "/");
484                 final byte[] data = testData.get(r);
485                 final ByteArrayEntity outgoing = new ByteArrayEntity(data);
486                 outgoing.setChunked(true);
487                 post.setEntity(outgoing);
488 
489                 final HttpResponse response = this.client.execute(post, host, conn);
490                 final byte[] received = EntityUtils.toByteArray(response.getEntity());
491                 final byte[] expected = testData.get(r);
492 
493                 Assert.assertEquals(expected.length, received.length);
494                 for (int i = 0; i < expected.length; i++) {
495                     Assert.assertEquals(expected[i], received[i]);
496                 }
497                 if (!this.client.keepAlive(response)) {
498                     conn.close();
499                 }
500             }
501 
502             //Verify the connection metrics
503             final HttpConnectionMetrics cm = conn.getMetrics();
504             Assert.assertEquals(reqNo, cm.getRequestCount());
505             Assert.assertEquals(reqNo, cm.getResponseCount());
506         } finally {
507             conn.close();
508             this.server.shutdown();
509         }
510     }
511 
512 
513     /**
514      * This test case executes a series of simple POST requests that do not
515      * meet the target server expectations.
516      */
517     @Test
518     public void testHttpPostsWithExpectationVerification() throws Exception {
519 
520         final int reqNo = 3;
521 
522         // Initialize the server-side request handler
523         this.server.registerHandler("*", new HttpRequestHandler() {
524 
525             @Override
526             public void handle(
527                     final HttpRequest request,
528                     final HttpResponse response,
529                     final HttpContext context) throws HttpException, IOException {
530 
531                 final StringEntity outgoing = new StringEntity("No content");
532                 response.setEntity(outgoing);
533             }
534 
535         });
536 
537         this.server.setExpectationVerifier(new HttpExpectationVerifier() {
538 
539             @Override
540             public void verify(
541                     final HttpRequest request,
542                     final HttpResponse response,
543                     final HttpContext context) throws HttpException {
544                 final Header someheader = request.getFirstHeader("Secret");
545                 if (someheader != null) {
546                     final int secretNumber;
547                     try {
548                         secretNumber = Integer.parseInt(someheader.getValue());
549                     } catch (final NumberFormatException ex) {
550                         response.setStatusCode(HttpStatus.SC_BAD_REQUEST);
551                         return;
552                     }
553                     if (secretNumber < 2) {
554                         response.setStatusCode(HttpStatus.SC_EXPECTATION_FAILED);
555                         final ByteArrayEntity outgoing = new ByteArrayEntity(
556                                 EncodingUtils.getAsciiBytes("Wrong secret number"));
557                         response.setEntity(outgoing);
558                     }
559                 }
560             }
561 
562         });
563 
564         this.server.start();
565 
566         final DefaultBHttpClientConnection conn = client.createConnection();
567         final HttpHost host = new HttpHost("localhost", this.server.getPort());
568 
569         try {
570             for (int r = 0; r < reqNo; r++) {
571                 if (!conn.isOpen()) {
572                     client.connect(host, conn);
573                 }
574 
575                 final BasicHttpEntityEnclosingRequest post = new BasicHttpEntityEnclosingRequest("POST", "/");
576                 post.addHeader("Secret", Integer.toString(r));
577                 final ByteArrayEntity outgoing = new ByteArrayEntity(
578                         EncodingUtils.getAsciiBytes("No content " + r));
579                 post.setEntity(outgoing);
580 
581                 final HttpResponse response = this.client.execute(post, host, conn);
582 
583                 final HttpEntity entity = response.getEntity();
584                 Assert.assertNotNull(entity);
585                 EntityUtils.consume(entity);
586 
587                 if (r < 2) {
588                     Assert.assertEquals(HttpStatus.SC_EXPECTATION_FAILED, response.getStatusLine().getStatusCode());
589                 } else {
590                     Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
591                 }
592 
593                 if (!this.client.keepAlive(response)) {
594                     conn.close();
595                 }
596             }
597             //Verify the connection metrics
598             final HttpConnectionMetrics cm = conn.getMetrics();
599             Assert.assertEquals(reqNo, cm.getRequestCount());
600             Assert.assertEquals(reqNo, cm.getResponseCount());
601         } finally {
602             conn.close();
603             this.server.shutdown();
604         }
605     }
606 
607     static class RepeatingEntity extends AbstractHttpEntity {
608 
609         private final byte[] raw;
610         private final int n;
611 
612         public RepeatingEntity(final String content, final Charset charset, final int n) {
613             super();
614             final Charset cs = charset != null ? charset : Charset.forName("US-ASCII");
615             final byte[] b = content.getBytes(cs);
616             this.raw = b;
617             this.n = n;
618         }
619 
620         @Override
621         public InputStream getContent() throws IOException, IllegalStateException {
622             throw new IllegalStateException("This method is not implemented");
623         }
624 
625         @Override
626         public long getContentLength() {
627             return (this.raw.length + 2) * this.n;
628         }
629 
630         @Override
631         public boolean isRepeatable() {
632             return true;
633         }
634 
635         @Override
636         public boolean isStreaming() {
637             return false;
638         }
639 
640         @Override
641         public void writeTo(final OutputStream outStream) throws IOException {
642             for (int i = 0; i < this.n; i++) {
643                 outStream.write(this.raw);
644                 outStream.write('\r');
645                 outStream.write('\n');
646             }
647             outStream.flush();
648         }
649 
650     }
651 
652     @Test
653     public void testHttpContent() throws Exception {
654 
655         final String[] patterns = {
656 
657             "0123456789ABCDEF",
658             "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
659             "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
660             "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
661             "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
662             "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
663             "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
664             "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
665             "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
666             "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
667             "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
668             "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
669             "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
670             "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
671             "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
672             "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that"
673         };
674 
675         // Initialize the server-side request handler
676         this.server.registerHandler("*", new HttpRequestHandler() {
677 
678             @Override
679             public void handle(
680                     final HttpRequest request,
681                     final HttpResponse response,
682                     final HttpContext context) throws HttpException, IOException {
683 
684                 if (request instanceof HttpEntityEnclosingRequest) {
685                     int n = 1;
686                     String s = request.getRequestLine().getUri();
687                     if (s.startsWith("/?n=")) {
688                         s = s.substring(4);
689                         try {
690                             n = Integer.parseInt(s);
691                             if (n <= 0) {
692                                 throw new HttpException("Invalid request: " +
693                                         "number of repetitions cannot be negative or zero");
694                             }
695                         } catch (final NumberFormatException ex) {
696                             throw new HttpException("Invalid request: " +
697                                     "number of repetitions is invalid");
698                         }
699                     }
700 
701                     final HttpEntity incoming = ((HttpEntityEnclosingRequest) request).getEntity();
702                     final String line = EntityUtils.toString(incoming);
703                     final ContentType contentType = ContentType.getOrDefault(incoming);
704                     Charset charset = contentType.getCharset();
705                     if (charset == null) {
706                         charset = HTTP.DEF_CONTENT_CHARSET;
707                     }
708                     final RepeatingEntity outgoing = new RepeatingEntity(line, charset, n);
709                     outgoing.setChunked(n % 2 == 0);
710                     response.setEntity(outgoing);
711                 } else {
712                     throw new HttpException("Invalid request: POST request expected");
713                 }
714             }
715 
716         });
717 
718         this.server.start();
719         final DefaultBHttpClientConnection conn = client.createConnection();
720         final HttpHost host = new HttpHost("localhost", this.server.getPort());
721 
722         try {
723             for (final String pattern : patterns) {
724                 for (int n = 1000; n < 1020; n++) {
725                     if (!conn.isOpen()) {
726                         client.connect(host, conn);
727                     }
728 
729                     final BasicHttpEntityEnclosingRequest post = new BasicHttpEntityEnclosingRequest(
730                             "POST", "/?n=" + n);
731                     final StringEntity outgoing = new StringEntity(pattern);
732                     outgoing.setChunked(n % 2 == 0);
733                     post.setEntity(outgoing);
734 
735                     final HttpResponse response = this.client.execute(post, host, conn);
736                     final HttpEntity incoming = response.getEntity();
737                     Assert.assertNotNull(incoming);
738                     final InputStream inStream = incoming.getContent();
739                     final ContentType contentType = ContentType.getOrDefault(incoming);
740                     Charset charset = contentType.getCharset();
741                     if (charset == null) {
742                         charset = HTTP.DEF_CONTENT_CHARSET;
743                     }
744                     Assert.assertNotNull(inStream);
745                     final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream, charset));
746 
747                     String line;
748                     int count = 0;
749                     while ((line = reader.readLine()) != null) {
750                         Assert.assertEquals(pattern, line);
751                         count++;
752                     }
753                     Assert.assertEquals(n, count);
754                     if (!this.client.keepAlive(response)) {
755                         conn.close();
756                     }
757                 }
758             }
759         } finally {
760             conn.close();
761             this.server.shutdown();
762         }
763     }
764 
765     @Test
766     public void testHttpPostNoEntity() throws Exception {
767         this.server.registerHandler("*", new HttpRequestHandler() {
768 
769             @Override
770             public void handle(
771                     final HttpRequest request,
772                     final HttpResponse response,
773                     final HttpContext context) throws HttpException, IOException {
774 
775                 if (request instanceof HttpEntityEnclosingRequest) {
776                     final HttpEntity incoming = ((HttpEntityEnclosingRequest) request).getEntity();
777                     final byte[] data = EntityUtils.toByteArray(incoming);
778                     final ByteArrayEntity outgoing = new ByteArrayEntity(data);
779                     response.setEntity(outgoing);
780                 } else {
781                     final StringEntity outgoing = new StringEntity("No content");
782                     response.setEntity(outgoing);
783                 }
784             }
785 
786         });
787 
788         this.server.start();
789 
790         final DefaultBHttpClientConnection conn = client.createConnection();
791         final HttpHost host = new HttpHost("localhost", this.server.getPort());
792 
793         try {
794             if (!conn.isOpen()) {
795                 client.connect(host, conn);
796             }
797 
798             final BasicHttpEntityEnclosingRequest post = new BasicHttpEntityEnclosingRequest("POST", "/");
799             post.setEntity(null);
800 
801             final HttpResponse response = this.client.execute(post, host, conn);
802             Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
803             final byte[] received = EntityUtils.toByteArray(response.getEntity());
804             Assert.assertEquals(0, received.length);
805         } finally {
806             conn.close();
807             this.server.shutdown();
808         }
809     }
810 
811     @Test
812     public void testHttpPostNoContentLength() throws Exception {
813         this.server.registerHandler("*", new HttpRequestHandler() {
814 
815             @Override
816             public void handle(
817                     final HttpRequest request,
818                     final HttpResponse response,
819                     final HttpContext context) throws HttpException, IOException {
820 
821                 if (request instanceof HttpEntityEnclosingRequest) {
822                     final HttpEntity incoming = ((HttpEntityEnclosingRequest) request).getEntity();
823                     final byte[] data = EntityUtils.toByteArray(incoming);
824                     final ByteArrayEntity outgoing = new ByteArrayEntity(data);
825                     response.setEntity(outgoing);
826                 } else {
827                     final StringEntity outgoing = new StringEntity("No content");
828                     response.setEntity(outgoing);
829                 }
830             }
831 
832         });
833 
834         this.server.start();
835 
836         final DefaultBHttpClientConnection conn = client.createConnection();
837         final HttpHost host = new HttpHost("localhost", this.server.getPort());
838 
839         try {
840             if (!conn.isOpen()) {
841                 client.connect(host, conn);
842             }
843 
844             final BasicHttpEntityEnclosingRequest post = new BasicHttpEntityEnclosingRequest("POST", "/");
845             post.setEntity(null);
846 
847             this.client = new HttpClient(new ImmutableHttpProcessor(
848                     new RequestTargetHost(),
849                     new RequestConnControl(),
850                     new RequestUserAgent(),
851                     new RequestExpectContinue(true)));
852 
853             final HttpResponse response = this.client.execute(post, host, conn);
854             Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
855             final byte[] received = EntityUtils.toByteArray(response.getEntity());
856             Assert.assertEquals(0, received.length);
857         } finally {
858             conn.close();
859             this.server.shutdown();
860         }
861     }
862 
863     @Test
864     public void testHttpPostIdentity() throws Exception {
865         this.server.registerHandler("*", new HttpRequestHandler() {
866 
867             @Override
868             public void handle(
869                     final HttpRequest request,
870                     final HttpResponse response,
871                     final HttpContext context) throws HttpException, IOException {
872 
873                 if (request instanceof HttpEntityEnclosingRequest) {
874                     final HttpEntity incoming = ((HttpEntityEnclosingRequest) request).getEntity();
875                     final byte[] data = EntityUtils.toByteArray(incoming);
876                     final ByteArrayEntity outgoing = new ByteArrayEntity(data);
877                     response.setEntity(outgoing);
878                 } else {
879                     final StringEntity outgoing = new StringEntity("No content");
880                     response.setEntity(outgoing);
881                 }
882             }
883 
884         });
885 
886         this.server.start();
887 
888         final DefaultBHttpClientConnection conn = client.createConnection();
889         final HttpHost host = new HttpHost("localhost", this.server.getPort());
890 
891         try {
892             if (!conn.isOpen()) {
893                 client.connect(host, conn);
894             }
895 
896             final BasicHttpEntityEnclosingRequest post = new BasicHttpEntityEnclosingRequest("POST", "/");
897             post.setEntity(null);
898 
899             this.client = new HttpClient(new ImmutableHttpProcessor(
900                     new HttpRequestInterceptor() {
901 
902                         @Override
903                         public void process(
904                                 final HttpRequest request,
905                                 final HttpContext context) throws HttpException, IOException {
906                             request.addHeader(HTTP.TRANSFER_ENCODING, "identity");
907                         }
908 
909                     }, new RequestTargetHost(), new RequestConnControl(), new RequestUserAgent(), new RequestExpectContinue(true)));
910 
911             final HttpResponse response = this.client.execute(post, host, conn);
912             Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusLine().getStatusCode());
913         } finally {
914             conn.close();
915             this.server.shutdown();
916         }
917     }
918 
919     @Test
920     public void testNoContentResponse() throws Exception {
921 
922         final int reqNo = 20;
923 
924         // Initialize the server-side request handler
925         this.server.registerHandler("*", new HttpRequestHandler() {
926 
927             @Override
928             public void handle(
929                     final HttpRequest request,
930                     final HttpResponse response,
931                     final HttpContext context) throws HttpException, IOException {
932                 response.setStatusCode(HttpStatus.SC_NO_CONTENT);
933             }
934 
935         });
936 
937         this.server.start();
938 
939         final DefaultBHttpClientConnection conn = client.createConnection();
940         final HttpHost host = new HttpHost("localhost", this.server.getPort());
941 
942         try {
943             for (int r = 0; r < reqNo; r++) {
944                 if (!conn.isOpen()) {
945                     client.connect(host, conn);
946                 }
947 
948                 final BasicHttpRequest get = new BasicHttpRequest("GET", "/?" + r);
949                 final HttpResponse response = this.client.execute(get, host, conn);
950                 Assert.assertNull(response.getEntity());
951                 if (!this.client.keepAlive(response)) {
952                     conn.close();
953                     Assert.fail("Connection expected to be re-usable");
954                 }
955             }
956 
957             //Verify the connection metrics
958             final HttpConnectionMetrics cm = conn.getMetrics();
959             Assert.assertEquals(reqNo, cm.getRequestCount());
960             Assert.assertEquals(reqNo, cm.getResponseCount());
961 
962         } finally {
963             conn.close();
964             this.server.shutdown();
965         }
966     }
967 
968 }