Skip to content

Commit 718b141

Browse files
authored
Merge branch 'dev' into bugfix/1991-auth-fragment-is-not-opened-with-extras
2 parents 69425d7 + ebdac47 commit 718b141

File tree

4 files changed

+79
-52
lines changed

4 files changed

+79
-52
lines changed

changelog.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
vNext
22
----------
3-
- [MINOR] Add suberror for network errors (#2537)
3+
- [MAJOR] Add suberror for network errors (#2537)
44
- [PATCH] Translate MFA token error to UIRequiredException instead of ServiceException (#2538)
55
- [MINOR] Add Child Spans for Interactive Span (#2516)
66
- [MINOR] For MSAL CPP flows, match exact claims when deleting AT with intersecting scopes (#2548)

common4j/src/main/com/microsoft/identity/common/java/exception/ConnectionError.kt

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,19 @@
2222
// THE SOFTWARE.
2323
package com.microsoft.identity.common.java.exception
2424

25+
import java.io.EOFException
26+
import java.net.ConnectException
27+
import java.net.SocketException
28+
import java.net.SocketTimeoutException
29+
import java.net.UnknownHostException
30+
import javax.net.ssl.SSLException
31+
2532
enum class ConnectionError(val value: String) {
26-
FAILED_TO_OPEN_CONNECTION("ce_failed_to_open_connection"),
27-
FAILED_TO_SET_REQUEST_METHOD("ce_failed_to_set_request_method"),
28-
FAILED_TO_WRITE_TO_OUTPUT_STREAM("ce_failed_to_write_to_output_stream"),
29-
FAILED_TO_READ_FROM_INPUT_STREAM("ce_failed_to_read_from_input_stream"),
30-
FAILED_TO_GET_RESPONSE_CODE("ce_failed_to_get_response_code"),
33+
NO_NETWORK("ce_no_network"),
34+
NETWORK_TEMPORARILY_UNAVAILABLE("ce_network_temporarily_unavailable"),
35+
UNEXPECTED_EXCEPTION("ce_unexpected_exception"),
3136
CONNECTION_TIMEOUT("ce_connection_timeout");
3237

33-
/**
34-
* Converts this [ConnectionError] into a [ClientException]
35-
**/
36-
fun getClientException(cause: Throwable): ClientException {
37-
val e = ClientException(
38-
ClientException.IO_ERROR,
39-
"An IO error occurred in the network layer: " + cause.message,
40-
cause
41-
)
42-
e.subErrorCode = value
43-
return e
44-
}
45-
4638
/**
4739
* Returns true if the given [Throwable] is a connection error.
4840
**/
@@ -53,4 +45,46 @@ enum class ConnectionError(val value: String) {
5345

5446
return this.value == throwable.subErrorCode
5547
}
48+
49+
companion object {
50+
/**
51+
* Converts this [ConnectionError] into a [ClientException]
52+
**/
53+
@JvmStatic
54+
fun getClientException(cause: Throwable): ClientException {
55+
val e = ClientException(
56+
ClientException.IO_ERROR,
57+
"An IO error occurred in the network layer: " + cause.message,
58+
cause
59+
)
60+
61+
e.subErrorCode = getConnectionError(cause).value
62+
return e
63+
}
64+
65+
/**
66+
* Converts a [Throwable] into a suberrorCode
67+
**/
68+
private fun getConnectionError(cause: Throwable): ConnectionError {
69+
if (cause is SocketTimeoutException) {
70+
// Slow or unreliable network
71+
return CONNECTION_TIMEOUT
72+
}
73+
74+
if (cause is EOFException // Unexpected disconnect
75+
|| cause is SSLException // SSL Handshake failed
76+
|| cause is ConnectException // Remote socket connection failed
77+
) {
78+
return NETWORK_TEMPORARILY_UNAVAILABLE
79+
}
80+
81+
if (cause is UnknownHostException // Unable to query DNS for hostname
82+
|| cause is SocketException // No conn to remote socket, or Airplane mode, or no internet permission will hit this
83+
) {
84+
return NO_NETWORK
85+
}
86+
87+
return UNEXPECTED_EXCEPTION
88+
}
89+
}
5690
}

common4j/src/main/com/microsoft/identity/common/java/net/UrlConnectionHttpClient.java

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,19 @@
4848

4949
import java.io.BufferedReader;
5050
import java.io.Closeable;
51+
import java.io.EOFException;
5152
import java.io.IOException;
5253
import java.io.InputStream;
5354
import java.io.InputStreamReader;
5455
import java.io.OutputStream;
56+
import java.net.ConnectException;
5557
import java.net.HttpURLConnection;
58+
import java.net.NoRouteToHostException;
5659
import java.net.ProtocolException;
60+
import java.net.SocketException;
5761
import java.net.SocketTimeoutException;
5862
import java.net.URL;
63+
import java.net.UnknownHostException;
5964
import java.util.Date;
6065
import java.util.HashMap;
6166
import java.util.List;
@@ -66,6 +71,7 @@
6671
import javax.annotation.Nullable;
6772
import javax.net.ssl.HttpsURLConnection;
6873
import javax.net.ssl.SSLContext;
74+
import javax.net.ssl.SSLException;
6975
import javax.net.ssl.SSLSocketFactory;
7076

7177
import io.opentelemetry.api.trace.Span;
@@ -313,7 +319,7 @@ private static HttpRequest constructHttpRequest(@NonNull HttpClient.HttpMethod h
313319
* @return The converted string
314320
* @throws IOException Thrown when failing to access inputStream stream.
315321
*/
316-
private String convertStreamToString(final InputStream inputStream) throws ClientException {
322+
private String convertStreamToString(final InputStream inputStream) throws IOException {
317323
try {
318324
final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream,
319325
AuthenticationConstants.CHARSET_UTF8));
@@ -326,8 +332,6 @@ private String convertStreamToString(final InputStream inputStream) throws Clien
326332
}
327333

328334
return stringBuilder.toString();
329-
} catch (IOException e) {
330-
throw ConnectionError.FAILED_TO_READ_FROM_INPUT_STREAM.getClientException(e);
331335
} finally {
332336
safeCloseStream(inputStream);
333337
}
@@ -353,10 +357,17 @@ private static void safeCloseStream(final Closeable stream) {
353357
}
354358

355359
private HttpResponse executeHttpSend(HttpRequest request, Consumer<HttpResponse> completionCallback) throws ClientException {
356-
final HttpURLConnection urlConnection = setupConnection(request);
357-
358-
sendRequest(urlConnection, request.getRequestContent(), request.getRequestHeaders().get(HttpConstants.HeaderField.CONTENT_TYPE));
360+
try {
361+
final HttpURLConnection urlConnection = setupConnection(request);
362+
sendRequest(urlConnection, request.getRequestContent(), request.getRequestHeaders().get(HttpConstants.HeaderField.CONTENT_TYPE));
363+
return getHttpResponse(completionCallback, urlConnection);
364+
} catch (final IOException e) {
365+
throw ConnectionError.getClientException(e);
366+
}
367+
}
359368

369+
private HttpResponse getHttpResponse(Consumer<HttpResponse> completionCallback,
370+
HttpURLConnection urlConnection) throws IOException {
360371
InputStream responseStream = null;
361372
HttpResponse response = null;
362373
try {
@@ -366,18 +377,13 @@ private HttpResponse executeHttpSend(HttpRequest request, Consumer<HttpResponse>
366377
// SocketTimeoutExcetion is thrown when connection timeout happens. For connection
367378
// timeout, we want to retry once. Throw the exception to the upper layer, and the
368379
// upper layer will handle the retry.
369-
throw ConnectionError.CONNECTION_TIMEOUT.getClientException(e);
380+
throw e;
370381
} catch (final IOException ioException) {
371382
// 404, for example, will generate an exception. We should catch it.
372383
responseStream = urlConnection.getErrorStream();
373384
}
374385

375-
final int statusCode;
376-
try {
377-
statusCode = urlConnection.getResponseCode();
378-
} catch (IOException e) {
379-
throw ConnectionError.FAILED_TO_GET_RESPONSE_CODE.getClientException(e);
380-
}
386+
final int statusCode = urlConnection.getResponseCode();
381387

382388
final Date date = new Date(urlConnection.getDate());
383389

@@ -401,12 +407,12 @@ private HttpResponse executeHttpSend(HttpRequest request, Consumer<HttpResponse>
401407
);
402408

403409
span.setAttribute(
404-
com.microsoft.identity.common.java.opentelemetry.AttributeName.ccs_request_id.name(),
410+
AttributeName.ccs_request_id.name(),
405411
response.getHeaderValue(XMS_CCS_REQUEST_ID, 0)
406412
);
407413

408414
span.setAttribute(
409-
com.microsoft.identity.common.java.opentelemetry.AttributeName.ccs_request_sequence.name(),
415+
AttributeName.ccs_request_sequence.name(),
410416
response.getHeaderValue(XMS_CCS_REQUEST_SEQUENCE, 0)
411417
);
412418
}
@@ -419,7 +425,6 @@ private HttpResponse executeHttpSend(HttpRequest request, Consumer<HttpResponse>
419425
AttributeName.http_status_code.name(),
420426
response.getStatusCode()
421427
);
422-
423428
} finally {
424429
completionCallback.accept(response);
425430
safeCloseStream(responseStream);
@@ -428,14 +433,9 @@ private HttpResponse executeHttpSend(HttpRequest request, Consumer<HttpResponse>
428433
return response;
429434
}
430435

431-
private HttpURLConnection setupConnection(HttpRequest request) throws ClientException {
436+
private HttpURLConnection setupConnection(HttpRequest request) throws IOException {
432437
final String methodName = ":setupConnection";
433-
final HttpURLConnection urlConnection;
434-
try {
435-
urlConnection = HttpUrlConnectionFactory.createHttpURLConnection(request.getRequestUrl());
436-
} catch (IOException e) {
437-
throw ConnectionError.FAILED_TO_OPEN_CONNECTION.getClientException(e);
438-
}
438+
final HttpURLConnection urlConnection = HttpUrlConnectionFactory.createHttpURLConnection(request.getRequestUrl());
439439

440440
// Apply request headers and update the headers with default attributes first
441441
final Set<Map.Entry<String, String>> headerEntries = request.getRequestHeaders().entrySet();
@@ -454,12 +454,7 @@ private HttpURLConnection setupConnection(HttpRequest request) throws ClientExce
454454
Logger.warn(TAG + methodName, "gets a request from an unexpected protocol: " + request.getRequestUrl().getProtocol());
455455
}
456456

457-
try {
458-
urlConnection.setRequestMethod(request.getRequestMethod());
459-
} catch (ProtocolException e) {
460-
throw ConnectionError.FAILED_TO_SET_REQUEST_METHOD.getClientException(e);
461-
}
462-
457+
urlConnection.setRequestMethod(request.getRequestMethod());
463458
urlConnection.setConnectTimeout(getConnectTimeoutMs());
464459
urlConnection.setReadTimeout(getReadTimeoutMs());
465460
urlConnection.setInstanceFollowRedirects(true);
@@ -479,7 +474,7 @@ private int getConnectTimeoutMs() {
479474

480475
private static void sendRequest(@NonNull final HttpURLConnection connection,
481476
final byte[] contentRequest,
482-
final String requestContentType) throws ClientException {
477+
final String requestContentType) throws IOException {
483478
if (contentRequest == null) {
484479
return;
485480
}
@@ -497,8 +492,6 @@ private static void sendRequest(@NonNull final HttpURLConnection connection,
497492
try {
498493
out = connection.getOutputStream();
499494
out.write(contentRequest);
500-
} catch (IOException e) {
501-
throw ConnectionError.FAILED_TO_WRITE_TO_OUTPUT_STREAM.getClientException(e);
502495
} finally {
503496
safeCloseStream(out);
504497
}

common4j/src/test/com/microsoft/identity/common/java/net/UrlConnectionHttpClientTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1218,7 +1218,7 @@ public void testConnectingToTLS13ServerWhileEnforcing12OnClientSide() throws Cli
12181218
);
12191219
Assert.fail();
12201220
} catch (ClientException e){
1221-
Assert.assertTrue(ConnectionError.FAILED_TO_GET_RESPONSE_CODE.compare(e));
1221+
Assert.assertTrue(ConnectionError.NETWORK_TEMPORARILY_UNAVAILABLE.compare(e));
12221222
}
12231223
}
12241224

0 commit comments

Comments
 (0)