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+ package org .apache .hc .client5 .testing .async ;
28+
29+ import java .io .IOException ;
30+ import java .util .Queue ;
31+ import java .util .concurrent .ConcurrentLinkedQueue ;
32+ import java .util .concurrent .CountDownLatch ;
33+
34+ import org .apache .hc .client5 .http .async .methods .SimpleHttpRequest ;
35+ import org .apache .hc .client5 .http .async .methods .SimpleHttpResponse ;
36+ import org .apache .hc .client5 .http .async .methods .SimpleRequestBuilder ;
37+ import org .apache .hc .client5 .http .async .methods .SimpleRequestProducer ;
38+ import org .apache .hc .client5 .http .async .methods .SimpleResponseConsumer ;
39+ import org .apache .hc .client5 .http .protocol .HttpClientContext ;
40+ import org .apache .hc .client5 .testing .Result ;
41+ import org .apache .hc .client5 .testing .extension .async .ClientProtocolLevel ;
42+ import org .apache .hc .client5 .testing .extension .async .ServerProtocolLevel ;
43+ import org .apache .hc .client5 .testing .extension .async .TestAsyncClient ;
44+ import org .apache .hc .core5 .concurrent .FutureCallback ;
45+ import org .apache .hc .core5 .http .ContentType ;
46+ import org .apache .hc .core5 .http .EntityDetails ;
47+ import org .apache .hc .core5 .http .HttpException ;
48+ import org .apache .hc .core5 .http .HttpHost ;
49+ import org .apache .hc .core5 .http .HttpRequest ;
50+ import org .apache .hc .core5 .http .HttpStatus ;
51+ import org .apache .hc .core5 .http .RequestNotExecutedException ;
52+ import org .apache .hc .core5 .http .URIScheme ;
53+ import org .apache .hc .core5 .http .nio .AsyncRequestConsumer ;
54+ import org .apache .hc .core5 .http .nio .AsyncServerRequestHandler ;
55+ import org .apache .hc .core5 .http .nio .entity .DiscardingEntityConsumer ;
56+ import org .apache .hc .core5 .http .nio .entity .StringAsyncEntityProducer ;
57+ import org .apache .hc .core5 .http .nio .support .AbstractAsyncPushHandler ;
58+ import org .apache .hc .core5 .http .nio .support .AbstractAsyncRequesterConsumer ;
59+ import org .apache .hc .core5 .http .nio .support .AbstractServerExchangeHandler ;
60+ import org .apache .hc .core5 .http .nio .support .BasicPushProducer ;
61+ import org .apache .hc .core5 .http .nio .support .BasicResponseProducer ;
62+ import org .apache .hc .core5 .http .protocol .HttpContext ;
63+ import org .apache .hc .core5 .http .support .BasicRequestBuilder ;
64+ import org .apache .hc .core5 .http .support .BasicResponseBuilder ;
65+ import org .apache .hc .core5 .http2 .config .H2Config ;
66+ import org .junit .jupiter .api .Assertions ;
67+ import org .junit .jupiter .api .Test ;
68+
69+ abstract class AbstractH2AsyncFundamentalsTest extends AbstractHttpAsyncFundamentalsTest {
70+
71+ public AbstractH2AsyncFundamentalsTest (final URIScheme scheme , final ClientProtocolLevel clientProtocolLevel , final ServerProtocolLevel serverProtocolLevel ) {
72+ super (scheme , clientProtocolLevel , serverProtocolLevel );
73+ }
74+
75+ @ Test
76+ void testPush () throws Exception {
77+ configureServer (bootstrap -> bootstrap
78+ .register ("/pushy" , () -> new AbstractServerExchangeHandler <HttpRequest >() {
79+
80+ @ Override
81+ protected AsyncRequestConsumer <HttpRequest > supplyConsumer (
82+ final HttpRequest request ,
83+ final EntityDetails entityDetails ,
84+ final HttpContext context ) throws HttpException {
85+
86+ return new AbstractAsyncRequesterConsumer <HttpRequest , Void >(new DiscardingEntityConsumer <>()) {
87+
88+ @ Override
89+ protected HttpRequest buildResult (final HttpRequest request , final Void entity , final ContentType contentType ) {
90+ return request ;
91+ }
92+
93+ };
94+ }
95+
96+ @ Override
97+ protected void handle (
98+ final HttpRequest request ,
99+ final AsyncServerRequestHandler .ResponseTrigger responseTrigger ,
100+ final HttpContext context ) throws HttpException , IOException {
101+ responseTrigger .pushPromise (
102+ BasicRequestBuilder .copy (request )
103+ .setPath ("/aaa" )
104+ .build (),
105+ context ,
106+ new BasicPushProducer (BasicResponseBuilder .create (HttpStatus .SC_OK )
107+ .build (),
108+ new StringAsyncEntityProducer ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" , ContentType .TEXT_PLAIN )));
109+ responseTrigger .pushPromise (
110+ BasicRequestBuilder .copy (request )
111+ .setPath ("/bbb" )
112+ .build (),
113+ context ,
114+ new BasicPushProducer (
115+ BasicResponseBuilder .create (HttpStatus .SC_OK ).build (),
116+ new StringAsyncEntityProducer ("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" , ContentType .TEXT_PLAIN )));
117+ responseTrigger .submitResponse (
118+ new BasicResponseProducer (
119+ BasicResponseBuilder .create (HttpStatus .SC_OK ).build (),
120+ new StringAsyncEntityProducer ("I am being very pushy" )
121+ ),
122+ context );
123+ }
124+
125+ }));
126+
127+ configureClient (builder -> builder
128+ .setH2Config (H2Config .custom ()
129+ .setPushEnabled (true )
130+ .build ()));
131+
132+ final HttpHost target = startServer ();
133+
134+ final TestAsyncClient client = startClient ();
135+
136+ client .start ();
137+
138+ final Queue <Result <String >> pushMessageQueue = new ConcurrentLinkedQueue <>();
139+ final CountDownLatch latch = new CountDownLatch (3 );
140+ final HttpClientContext context = HttpClientContext .create ();
141+ final SimpleHttpRequest request = SimpleRequestBuilder .get ()
142+ .setHttpHost (target )
143+ .setPath ("/pushy" )
144+ .build ();
145+ client .execute (
146+ SimpleRequestProducer .create (request ),
147+ SimpleResponseConsumer .create (),
148+ (r , c ) -> new AbstractAsyncPushHandler <SimpleHttpResponse >(SimpleResponseConsumer .create ()) {
149+
150+ @ Override
151+ protected void handleResponse (final HttpRequest promise ,
152+ final SimpleHttpResponse response ) throws IOException , HttpException {
153+ pushMessageQueue .add (new Result <>(promise , response , response .getBodyText ()));
154+ latch .countDown ();
155+ }
156+
157+ @ Override
158+ protected void handleError (final HttpRequest promise , final Exception cause ) {
159+ pushMessageQueue .add (new Result <>(promise , cause ));
160+ latch .countDown ();
161+ }
162+
163+ },
164+ context ,
165+ new FutureCallback <SimpleHttpResponse >() {
166+
167+ @ Override
168+ public void completed (final SimpleHttpResponse response ) {
169+ pushMessageQueue .add (new Result <>(request , response , response .getBodyText ()));
170+ latch .countDown ();
171+ }
172+
173+ @ Override
174+ public void failed (final Exception ex ) {
175+ pushMessageQueue .add (new Result <>(request , ex ));
176+ latch .countDown ();
177+ }
178+
179+ @ Override
180+ public void cancelled () {
181+ pushMessageQueue .add (new Result <>(request , new RequestNotExecutedException ()));
182+ latch .countDown ();
183+ }
184+
185+ }
186+ );
187+ Assertions .assertTrue (latch .await (TIMEOUT .getDuration (), TIMEOUT .getTimeUnit ()));
188+ Assertions .assertEquals (3 , pushMessageQueue .size ());
189+ for (final Result <String > result : pushMessageQueue ) {
190+ if (result .isOK ()) {
191+ Assertions .assertEquals (HttpStatus .SC_OK , result .response .getCode ());
192+ final String path = result .request .getPath ();
193+ if (path .equals ("/pushy" )) {
194+ Assertions .assertEquals ("I am being very pushy" , result .content );
195+ } else if (path .equals ("/aaa" )) {
196+ Assertions .assertEquals ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" , result .content );
197+ } else if (path .equals ("/bbb" )) {
198+ Assertions .assertEquals ("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" , result .content );
199+ } else {
200+ Assertions .fail ("Unxpected request path: " + path );
201+ }
202+ }
203+ }
204+ }
205+
206+ }
0 commit comments