Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
],
"java.configuration.updateBuildConfiguration": "interactive",
"java.compile.nullAnalysis.mode": "automatic"
}
}
28 changes: 19 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

### Changed

## [1.1.5]

### Changed

- Fixed exception thrown when setting content length on stream request bodies.

## [1.1.4] - 2024-04-04

### Added
Expand All @@ -33,7 +43,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Fixed a bug where not providing scopes to `AzureIdentityAccessTokenProvider` failed with `UnsupportedOperationException` when attempting to fetch the token. [microsoftgraph/msgraph-sdk-java#1882](https://github.com/microsoftgraph/msgraph-sdk-java/issues/1882)
- Fixed a bug where not providing scopes to `AzureIdentityAccessTokenProvider` failed with `UnsupportedOperationException` when attempting to fetch the token. [microsoftgraph/msgraph-sdk-java#1882](https://github.com/microsoftgraph/msgraph-sdk-java/issues/1882)

## [1.1.0] - 2024-02-14

Expand All @@ -43,13 +53,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [1.0.6] - 2023-03-04

### Changed
### Changed

- Fixed a regression with the content length request header from 1.0.5.

## [1.0.5] - 2023-02-28

### Changed
### Changed

- Added contentLength property to RequestInformation to facilitate in setting the content length of the Okhttp3 RequestBody object within the OkhttpRequestAdapter.

Expand Down Expand Up @@ -210,7 +220,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Add PeriodAndDuration constructor to create new object from a PeriodAndDuration object.
- Add PeriodAndDuration constructor to create new object from a PeriodAndDuration object.

## [0.7.0] - 2023-08-18

Expand All @@ -222,7 +232,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Javax annotations replaced in favor of Jakarta annotations.
- Javax annotations replaced in favor of Jakarta annotations.

## [0.5.0] - 2023-07-26

Expand Down Expand Up @@ -269,8 +279,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Adds a NativeResponseHandler to abstractions.
- Adds setResponseHandler method to RequestInformation class in abstractions.
- Adds a NativeResponseHandler to abstractions.
- Adds setResponseHandler method to RequestInformation class in abstractions.

## [0.4.1] - 2023-03-29

Expand Down Expand Up @@ -373,11 +383,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Added ResponseHandlerOption class.
- Added ResponseHandlerOption class.

### Changed

- Removed responseHandler parameter from RequestAdapter sendAsync methods.
- Removed responseHandler parameter from RequestAdapter sendAsync methods.
- Compatibility for Android level 26.

## [0.0.5] - 2022-09-15
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.time.LocalDate;
Expand Down Expand Up @@ -846,7 +845,7 @@ private void setBaseUrlForRequestInformation(@Nonnull final RequestInformation r
try (final Scope scope = span.makeCurrent()) {
this.authProvider.authenticateRequest(requestInfo, null);
return (T) getRequestFromRequestInformation(requestInfo, span, span);
} catch (URISyntaxException | MalformedURLException ex) {
} catch (URISyntaxException | IOException ex) {
span.recordException(ex);
throw new RuntimeException(ex);
} finally {
Expand All @@ -862,13 +861,13 @@ private void setBaseUrlForRequestInformation(@Nonnull final RequestInformation r
* @param spanForAttributes the span for the attributes.
* @return the created request instance.
* @throws URISyntaxException if the URI is invalid.
* @throws MalformedURLException if the URL is invalid.
* @throws IOException if the URL is invalid.
*/
protected @Nonnull Request getRequestFromRequestInformation(
@Nonnull final RequestInformation requestInfo,
@Nonnull final Span parentSpan,
@Nonnull final Span spanForAttributes)
throws URISyntaxException, MalformedURLException {
throws URISyntaxException, IOException {
final Span span =
GlobalOpenTelemetry.getTracer(obsOptions.getTracerInstrumentationName())
.spanBuilder("getRequestFromRequestInformation")
Expand Down Expand Up @@ -907,30 +906,23 @@ public MediaType contentType() {

@Override
public long contentLength() throws IOException {
long length;
final Set<String> contentLength =
requestInfo.headers.getOrDefault(
contentLengthHeaderKey, new HashSet<>());
if (contentLength.isEmpty()
&& requestInfo.content
instanceof ByteArrayInputStream) {
if (!contentLength.isEmpty()) {
return Long.parseLong(
contentLength.toArray(new String[] {})[0]);
}
// super.contentLength() is not relied on since it defaults to
// -1L, causing wrong telemetry added to the attributes.
if (requestInfo.content instanceof ByteArrayInputStream) {
final ByteArrayInputStream contentStream =
(ByteArrayInputStream) requestInfo.content;
length = contentStream.available();
} else {
length =
Long.parseLong(
contentLength.toArray(new String[] {})[0]);
}
if (length <= 0) {
length = super.contentLength();
}
if (length > 0) {
spanForAttributes.setAttribute(
HttpIncubatingAttributes.HTTP_REQUEST_BODY_SIZE,
length);
// using available() on a byte-array backed input stream is
// reliable because array size is defined.
return contentStream.available();
}
return length;
return super.contentLength();
}

@Override
Expand Down Expand Up @@ -967,7 +959,18 @@ public void writeTo(@Nonnull BufferedSink sink) throws IOException {
requestBuilder.tag(obsOptions.getType(), obsOptions);
}
requestBuilder.tag(Span.class, parentSpan);
return requestBuilder.build();
final Request request = requestBuilder.build();
if (request != null) {
RequestBody requestBody = request.body();
if (requestBody != null) {
long contentLength = requestBody.contentLength();
if (contentLength >= 0) {
spanForAttributes.setAttribute(
HttpIncubatingAttributes.HTTP_REQUEST_BODY_SIZE, contentLength);
}
}
}
return request;
} finally {
span.end();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,22 +301,21 @@ void getRequestFromRequestInformationHasCorrectContentLength_JsonPayload() throw
new ByteArrayInputStream(
"{\"name\":\"value\",\"array\":[\"1\",\"2\",\"3\"]}"
.getBytes(StandardCharsets.UTF_8));
requestInformation.setStreamContent(content, "application/octet-stream");
requestInformation.setStreamContent(content, "application/json");
requestInformation.httpMethod = HttpMethod.PUT;
requestInformation.headers.tryAdd("Content-Length", String.valueOf(content.available()));
final var contentLength = content.available();
requestInformation.headers.tryAdd("Content-Length", String.valueOf(contentLength));

final var adapter = new OkHttpRequestAdapter(authenticationProviderMock);
final var request =
adapter.getRequestFromRequestInformation(
requestInformation, mock(Span.class), mock(Span.class));

assertEquals(
String.valueOf(requestInformation.content.available()),
request.headers().get("Content-Length"));
assertEquals("application/octet-stream", request.headers().get("Content-Type"));
assertEquals(String.valueOf(contentLength), request.headers().get("Content-Length"));
assertEquals("application/json", request.headers().get("Content-Type"));
assertNotNull(request.body());
assertEquals(request.body().contentLength(), requestInformation.content.available());
assertEquals(request.body().contentType(), MediaType.parse("application/octet-stream"));
assertEquals(request.body().contentLength(), contentLength);
assertEquals(request.body().contentType(), MediaType.parse("application/json"));
}

@Test
Expand All @@ -327,7 +326,8 @@ void getRequestFromRequestInformationIncludesContentLength_FilePayload() throws

requestInformation.setUri(new URI("https://localhost"));
requestInformation.httpMethod = HttpMethod.PUT;
requestInformation.headers.add("Content-Length", String.valueOf(testFile.length()));
final var contentLength = testFile.length();
requestInformation.headers.add("Content-Length", String.valueOf(contentLength));
try (FileInputStream content = new FileInputStream(testFile)) {
requestInformation.setStreamContent(content, "application/octet-stream");

Expand All @@ -336,16 +336,82 @@ void getRequestFromRequestInformationIncludesContentLength_FilePayload() throws
adapter.getRequestFromRequestInformation(
requestInformation, mock(Span.class), mock(Span.class));

assertEquals(
String.valueOf(requestInformation.content.available()),
request.headers().get("Content-Length"));
assertEquals(String.valueOf(contentLength), request.headers().get("Content-Length"));
assertEquals("application/octet-stream", request.headers().get("Content-Type"));
assertNotNull(request.body());
assertEquals(request.body().contentLength(), requestInformation.content.available());
assertEquals(request.body().contentLength(), contentLength);
assertEquals(request.body().contentType(), MediaType.parse("application/octet-stream"));
}
}

@Test
void getRequestFromRequestInformationWithoutContentLengthOverrideForStreamBody()
throws Exception {
final var authenticationProviderMock = mock(AuthenticationProvider.class);
final var testFile = new File("./src/test/resources/helloWorld.txt");
final var requestInformation = new RequestInformation();

requestInformation.setUri(new URI("https://localhost"));
requestInformation.httpMethod = HttpMethod.PUT;
try (FileInputStream content = new FileInputStream(testFile)) {
requestInformation.setStreamContent(content, "application/octet-stream");

final var adapter = new OkHttpRequestAdapter(authenticationProviderMock);
final var request =
adapter.getRequestFromRequestInformation(
requestInformation, mock(Span.class), mock(Span.class));

assertEquals("application/octet-stream", request.headers().get("Content-Type"));
assertNotNull(request.body());
assertEquals(-1L, request.body().contentLength());
assertEquals(request.body().contentType(), MediaType.parse("application/octet-stream"));
}
}

@Test
void getRequestFromRequestInformationWithoutContentLengthOverrideForJsonPayload()
throws Exception {
final var authenticationProviderMock = mock(AuthenticationProvider.class);
final var requestInformation = new RequestInformation();
requestInformation.setUri(new URI("https://localhost"));
ByteArrayInputStream content =
new ByteArrayInputStream(
"{\"name\":\"value\",\"array\":[\"1\",\"2\",\"3\"]}"
.getBytes(StandardCharsets.UTF_8));
requestInformation.setStreamContent(content, "application/json");
requestInformation.httpMethod = HttpMethod.PUT;
final var contentLength = content.available();

final var adapter = new OkHttpRequestAdapter(authenticationProviderMock);
final var request =
adapter.getRequestFromRequestInformation(
requestInformation, mock(Span.class), mock(Span.class));

assertEquals("application/json", request.headers().get("Content-Type"));
assertNotNull(request.body());
assertEquals(contentLength, request.body().contentLength());
assertEquals(MediaType.parse("application/json"), request.body().contentType());
}

@Test
void getRequestFromRequestInformationWithoutContentLengthOverrideWithEmptyPayload()
throws Exception {
final var authenticationProviderMock = mock(AuthenticationProvider.class);
final var requestInformation = new RequestInformation();
requestInformation.setUri(new URI("https://localhost"));
ByteArrayInputStream content = new ByteArrayInputStream(new byte[0]);
requestInformation.httpMethod = HttpMethod.PUT;
requestInformation.content = content;

final var adapter = new OkHttpRequestAdapter(authenticationProviderMock);
final var request =
adapter.getRequestFromRequestInformation(
requestInformation, mock(Span.class), mock(Span.class));

assertNull(request.headers().get("Content-Type"));
assertEquals(0, request.body().contentLength());
}

public static OkHttpClient getMockClient(final Response response) throws IOException {
final OkHttpClient mockClient = mock(OkHttpClient.class);
final Call remoteCall = mock(Call.class);
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ org.gradle.caching=true
mavenGroupId = com.microsoft.kiota
mavenMajorVersion = 1
mavenMinorVersion = 1
mavenPatchVersion = 4
mavenArtifactSuffix =
mavenPatchVersion = 5
mavenArtifactSuffix =

#These values are used to run functional tests
#If you wish to run the functional tests, edit the gradle.properties
Expand Down