4040import java .io .IOException ;
4141import java .net .ServerSocket ;
4242import java .net .Socket ;
43+ import java .nio .charset .StandardCharsets ;
4344import java .util .concurrent .atomic .AtomicBoolean ;
4445import org .apache .hc .client5 .http .impl .async .CloseableHttpAsyncClient ;
4546import org .apache .hc .client5 .http .impl .async .HttpAsyncClients ;
47+ import org .apache .hc .core5 .http .ClassicHttpRequest ;
48+ import org .apache .hc .core5 .http .ClassicHttpResponse ;
49+ import org .apache .hc .core5 .http .ContentType ;
4650import org .apache .hc .core5 .http .EntityDetails ;
4751import org .apache .hc .core5 .http .Header ;
4852import org .apache .hc .core5 .http .HttpException ;
53+ import org .apache .hc .core5 .http .HttpHeaders ;
4954import org .apache .hc .core5 .http .HttpRequest ;
5055import org .apache .hc .core5 .http .HttpRequestInterceptor ;
56+ import org .apache .hc .core5 .http .HttpRequestMapper ;
57+ import org .apache .hc .core5 .http .HttpResponse ;
58+ import org .apache .hc .core5 .http .HttpStatus ;
59+ import org .apache .hc .core5 .http .impl .bootstrap .HttpServer ;
60+ import org .apache .hc .core5 .http .impl .io .HttpService ;
61+ import org .apache .hc .core5 .http .io .HttpRequestHandler ;
62+ import org .apache .hc .core5 .http .io .entity .ByteArrayEntity ;
63+ import org .apache .hc .core5 .http .io .support .BasicHttpServerRequestHandler ;
5164import org .apache .hc .core5 .http .protocol .HttpContext ;
65+ import org .apache .hc .core5 .http .protocol .HttpProcessor ;
5266
5367import org .junit .After ;
5468import org .junit .AfterClass ;
@@ -61,11 +75,6 @@ public class ApacheHttp2TransportIT {
6175 private static final ImmutableMap <String , Object > payload =
6276 ImmutableMap .<String , Object >of ("foo" , "bar" );
6377
64- // Sets a 5 second delay before server response to simulate a slow network that
65- // results in a read timeout.
66- private static final String DELAY_URL = "https://httpbin.org/delay/5" ;
67- private static final String GET_URL = "https://httpbin.org/get" ;
68- private static final String POST_URL = "https://httpbin.org/post" ;
6978
7079 private static ServerSocket serverSocket ;
7180 private static Socket fillerSocket ;
@@ -105,19 +114,45 @@ public void cleanup() {
105114 }
106115
107116 @ Test (timeout = 10_000L )
108- public void testUnauthorizedGetRequest () throws FirebaseException {
109- ErrorHandlingHttpClient <FirebaseException > httpClient = getHttpClient (false );
110- HttpRequestInfo request = HttpRequestInfo .buildGetRequest (GET_URL );
111- IncomingHttpResponse response = httpClient .send (request );
112- assertEquals (200 , response .getStatusCode ());
117+ public void testUnauthorizedGetRequest () throws Exception {
118+ final HttpRequestHandler handler = new HttpRequestHandler () {
119+ @ Override
120+ public void handle (
121+ ClassicHttpRequest request , ClassicHttpResponse response , HttpContext context )
122+ throws HttpException , IOException {
123+ response .setCode (HttpStatus .SC_OK );
124+ response .setHeader (HttpHeaders .CONTENT_LENGTH , "0" );
125+ }
126+ };
127+ try (FakeServer server = new FakeServer (handler )) {
128+ ErrorHandlingHttpClient <FirebaseException > httpClient = getHttpClient (false );
129+ HttpRequestInfo request = HttpRequestInfo .buildGetRequest ("http://localhost:" + server .getPort ());
130+ IncomingHttpResponse response = httpClient .send (request );
131+ assertEquals (200 , response .getStatusCode ());
132+ }
113133 }
114134
115135 @ Test (timeout = 10_000L )
116- public void testUnauthorizedPostRequest () throws FirebaseException {
117- ErrorHandlingHttpClient <FirebaseException > httpClient = getHttpClient (false );
118- HttpRequestInfo request = HttpRequestInfo .buildJsonPostRequest (POST_URL , payload );
119- GenericData body = httpClient .sendAndParse (request , GenericData .class );
120- assertEquals ("{\" foo\" :\" bar\" }" , body .get ("data" ));
136+ public void testUnauthorizedPostRequest () throws Exception {
137+ final HttpRequestHandler handler = new HttpRequestHandler () {
138+ @ Override
139+ public void handle (
140+ ClassicHttpRequest request , ClassicHttpResponse response , HttpContext context )
141+ throws HttpException , IOException {
142+ String responseJson = "{\" data\" :\" {\\ \" foo\\ \" :\\ \" bar\\ \" }\" }" ;
143+ byte [] responseData = responseJson .getBytes (StandardCharsets .UTF_8 );
144+ response .setCode (HttpStatus .SC_OK );
145+ response .setHeader (HttpHeaders .CONTENT_LENGTH , String .valueOf (responseData .length ));
146+ response .setHeader (HttpHeaders .CONTENT_TYPE , "application/json" );
147+ response .setEntity (new ByteArrayEntity (responseData , ContentType .APPLICATION_JSON ));
148+ }
149+ };
150+ try (FakeServer server = new FakeServer (handler )) {
151+ ErrorHandlingHttpClient <FirebaseException > httpClient = getHttpClient (false );
152+ HttpRequestInfo request = HttpRequestInfo .buildJsonPostRequest ("http://localhost:" + server .getPort (), payload );
153+ GenericData body = httpClient .sendAndParse (request , GenericData .class );
154+ assertEquals ("{\" foo\" :\" bar\" }" , body .get ("data" ));
155+ }
121156 }
122157
123158 @ Test (timeout = 10_000L )
@@ -159,84 +194,72 @@ public void testConnectTimeoutAuthorizedPost() throws FirebaseException {
159194 }
160195
161196 @ Test (timeout = 10_000L )
162- public void testReadTimeoutAuthorizedGet () throws FirebaseException {
163- app = FirebaseApp .initializeApp (FirebaseOptions .builder ()
164- .setCredentials (MOCK_CREDENTIALS )
165- .setReadTimeout (100 )
166- .build (), "test-app" );
167- ErrorHandlingHttpClient <FirebaseException > httpClient = getHttpClient (true , app );
168- HttpRequestInfo request = HttpRequestInfo .buildGetRequest (DELAY_URL );
169-
170- try {
171- httpClient .send (request );
172- fail ("No exception thrown for HTTP error response" );
173- } catch (FirebaseException e ) {
174- assertEquals (ErrorCode .UNKNOWN , e .getErrorCode ());
175- assertEquals ("IO error: Stream exception in request" , e .getMessage ());
176- assertNull (e .getHttpResponse ());
197+ public void testReadTimeoutAuthorizedGet () throws Exception {
198+ final HttpRequestHandler handler = new HttpRequestHandler () {
199+ @ Override
200+ public void handle (
201+ ClassicHttpRequest request , ClassicHttpResponse response , HttpContext context )
202+ throws HttpException , IOException {
203+ try {
204+ Thread .sleep (1000 );
205+ } catch (InterruptedException e ) {
206+ // Ignore
207+ }
208+ response .setCode (HttpStatus .SC_OK );
209+ }
210+ };
211+ try (FakeServer server = new FakeServer (handler )) {
212+ app = FirebaseApp .initializeApp (FirebaseOptions .builder ()
213+ .setCredentials (MOCK_CREDENTIALS )
214+ .setConnectTimeout (5000 )
215+ .setReadTimeout (100 )
216+ .build (), "test-app" );
217+ ErrorHandlingHttpClient <FirebaseException > httpClient = getHttpClient (true , app );
218+ HttpRequestInfo request = HttpRequestInfo .buildGetRequest ("http://localhost:" + server .getPort ());
219+
220+ try {
221+ httpClient .send (request );
222+ fail ("No exception thrown for HTTP error response" );
223+ } catch (FirebaseException e ) {
224+ assertEquals (ErrorCode .UNKNOWN , e .getErrorCode ());
225+ assertEquals ("IO error: Connection Timeout" , e .getMessage ());
226+ assertNull (e .getHttpResponse ());
227+ }
177228 }
178229 }
179230
180231 @ Test (timeout = 10_000L )
181- public void testReadTimeoutAuthorizedPost () throws FirebaseException {
182- app = FirebaseApp .initializeApp (FirebaseOptions .builder ()
183- .setCredentials (MOCK_CREDENTIALS )
184- .setReadTimeout (100 )
185- .build (), "test-app" );
186- ErrorHandlingHttpClient <FirebaseException > httpClient = getHttpClient (true , app );
187- HttpRequestInfo request = HttpRequestInfo .buildJsonPostRequest (DELAY_URL , payload );
188-
189- try {
190- httpClient .send (request );
191- fail ("No exception thrown for HTTP error response" );
192- } catch (FirebaseException e ) {
193- assertEquals (ErrorCode .UNKNOWN , e .getErrorCode ());
194- assertEquals ("IO error: Stream exception in request" , e .getMessage ());
195- assertNull (e .getHttpResponse ());
196- }
197- }
198-
199- @ Test (timeout = 10_000L )
200- public void testWriteTimeoutAuthorizedGet () throws FirebaseException {
201- // Use a fresh transport so that writeTimeout triggers while waiting for the transport to
202- // be ready to receive data.
203- app = FirebaseApp .initializeApp (FirebaseOptions .builder ()
204- .setCredentials (MOCK_CREDENTIALS )
205- .setWriteTimeout (100 )
206- .setHttpTransport (new ApacheHttp2Transport ())
207- .build (), "test-app" );
208- ErrorHandlingHttpClient <FirebaseException > httpClient = getHttpClient (true , app );
209- HttpRequestInfo request = HttpRequestInfo .buildGetRequest (GET_URL );
210-
211- try {
212- httpClient .send (request );
213- fail ("No exception thrown for HTTP error response" );
214- } catch (FirebaseException e ) {
215- assertEquals (ErrorCode .UNKNOWN , e .getErrorCode ());
216- assertEquals ("IO error: Write Timeout" , e .getMessage ());
217- assertNull (e .getHttpResponse ());
218- }
219- }
220-
221- @ Test (timeout = 10_000L )
222- public void testWriteTimeoutAuthorizedPost () throws FirebaseException {
223- // Use a fresh transport so that writeTimeout triggers while waiting for the transport to
224- // be ready to receive data.
225- app = FirebaseApp .initializeApp (FirebaseOptions .builder ()
226- .setCredentials (MOCK_CREDENTIALS )
227- .setWriteTimeout (100 )
228- .setHttpTransport (new ApacheHttp2Transport ())
229- .build (), "test-app" );
230- ErrorHandlingHttpClient <FirebaseException > httpClient = getHttpClient (true , app );
231- HttpRequestInfo request = HttpRequestInfo .buildJsonPostRequest (POST_URL , payload );
232-
233- try {
234- httpClient .send (request );
235- fail ("No exception thrown for HTTP error response" );
236- } catch (FirebaseException e ) {
237- assertEquals (ErrorCode .UNKNOWN , e .getErrorCode ());
238- assertEquals ("IO error: Write Timeout" , e .getMessage ());
239- assertNull (e .getHttpResponse ());
232+ public void testReadTimeoutAuthorizedPost () throws Exception {
233+ final HttpRequestHandler handler = new HttpRequestHandler () {
234+ @ Override
235+ public void handle (
236+ ClassicHttpRequest request , ClassicHttpResponse response , HttpContext context )
237+ throws HttpException , IOException {
238+ try {
239+ Thread .sleep (1000 );
240+ } catch (InterruptedException e ) {
241+ // Ignore
242+ }
243+ response .setCode (HttpStatus .SC_OK );
244+ }
245+ };
246+ try (FakeServer server = new FakeServer (handler )) {
247+ app = FirebaseApp .initializeApp (FirebaseOptions .builder ()
248+ .setCredentials (MOCK_CREDENTIALS )
249+ .setConnectTimeout (5000 )
250+ .setReadTimeout (100 )
251+ .build (), "test-app-2" );
252+ ErrorHandlingHttpClient <FirebaseException > httpClient = getHttpClient (true , app );
253+ HttpRequestInfo request = HttpRequestInfo .buildJsonPostRequest ("http://localhost:" + server .getPort (), payload );
254+
255+ try {
256+ httpClient .send (request );
257+ fail ("No exception thrown for HTTP error response" );
258+ } catch (FirebaseException e ) {
259+ assertEquals (ErrorCode .UNKNOWN , e .getErrorCode ());
260+ assertEquals ("IO error: Connection Timeout" , e .getMessage ());
261+ assertNull (e .getHttpResponse ());
262+ }
240263 }
241264 }
242265
@@ -290,7 +313,7 @@ public void testVerifyProxyIsRespected() {
290313 System .setProperty ("https.proxyPort" , "8080" );
291314
292315 HttpTransport transport = new ApacheHttp2Transport ();
293- transport .createRequestFactory ().buildGetRequest (new GenericUrl (GET_URL )).execute ();
316+ transport .createRequestFactory ().buildGetRequest (new GenericUrl ("https://dummy.nonexistent/get" )).execute ();
294317 fail ("No exception thrown for HTTP error response" );
295318 } catch (IOException e ) {
296319 assertEquals ("Connection exception in request" , e .getMessage ());
@@ -352,4 +375,54 @@ public FirebaseException handleParseException(IOException e, IncomingHttpRespons
352375 return new FirebaseException (ErrorCode .UNKNOWN , "Parse error" , e , response );
353376 }
354377 }
378+
379+ private static class FakeServer implements AutoCloseable {
380+ private final HttpServer server ;
381+
382+ FakeServer (final HttpRequestHandler httpHandler ) throws IOException {
383+ HttpRequestMapper <HttpRequestHandler > mapper = new HttpRequestMapper <HttpRequestHandler >() {
384+ @ Override
385+ public HttpRequestHandler resolve (HttpRequest request , HttpContext context )
386+ throws HttpException {
387+ return httpHandler ;
388+ }
389+ };
390+ server = new HttpServer (
391+ 0 ,
392+ HttpService .builder ()
393+ .withHttpProcessor (
394+ new HttpProcessor () {
395+ @ Override
396+ public void process (
397+ HttpRequest request , EntityDetails entity , HttpContext context )
398+ throws HttpException , IOException {
399+ }
400+
401+ @ Override
402+ public void process (
403+ HttpResponse response , EntityDetails entity , HttpContext context )
404+ throws HttpException , IOException {
405+ }
406+ })
407+ .withHttpServerRequestHandler (new BasicHttpServerRequestHandler (mapper ))
408+ .build (),
409+ null ,
410+ null ,
411+ null ,
412+ null ,
413+ null ,
414+ null );
415+ server .start ();
416+ }
417+
418+ public int getPort () {
419+ return server .getLocalPort ();
420+ }
421+
422+ @ Override
423+ public void close () {
424+ server .initiateShutdown ();
425+ }
426+ }
355427}
428+
0 commit comments