Skip to content

Commit

Permalink
[fix] Fix handling for Streams in IConnection for raw content (#2791)
Browse files Browse the repository at this point in the history
* Fix handling for Streams

Fix `NullReferenceException` if raw content was handled as a string rather than a stream by `HttpClientAdapter.BuildResponse()`.
Resolves #2789.

* Add Connection.GetRaw tests

Add tests for `Connection.GetRaw()` and for #2789.

---------

Co-authored-by: Keegan Campbell <me@kfcampbell.com>
  • Loading branch information
martincostello and kfcampbell authored Nov 7, 2023
1 parent c015d8c commit 4f826bc
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 2 deletions.
110 changes: 110 additions & 0 deletions Octokit.Tests/Http/ConnectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,116 @@ public async Task SendsProperlyFormattedRequestWithProperAcceptHeader()
}
}

public class TheGetRawMethod
{
[Fact]
public async Task SendsProperlyFormattedRequestWithProperAcceptHeader()
{
var httpClient = Substitute.For<IHttpClient>();
var response = CreateResponse(HttpStatusCode.OK);
httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
var connection = new Connection(new ProductHeaderValue("OctokitTests"),
_exampleUri,
Substitute.For<ICredentialStore>(),
httpClient,
Substitute.For<IJsonSerializer>());

await connection.GetRaw(new Uri("endpoint", UriKind.Relative), new Dictionary<string, string>());

httpClient.Received(1).Send(Arg.Is<IRequest>(req =>
req.BaseAddress == _exampleUri &&
req.ContentType == null &&
req.Body == null &&
req.Method == HttpMethod.Get &&
req.Headers["Accept"] == "application/vnd.github.v3.raw" &&
req.Endpoint == new Uri("endpoint", UriKind.Relative)), Args.CancellationToken);
}

[Fact]
public async Task SendsProperlyFormattedRequestWithProperAcceptHeaderAndTimeout()
{
var httpClient = Substitute.For<IHttpClient>();
var response = CreateResponse(HttpStatusCode.OK);
httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
var connection = new Connection(new ProductHeaderValue("OctokitTests"),
_exampleUri,
Substitute.For<ICredentialStore>(),
httpClient,
Substitute.For<IJsonSerializer>());

await connection.GetRaw(new Uri("endpoint", UriKind.Relative), new Dictionary<string, string>(), TimeSpan.FromSeconds(1));

httpClient.Received(1).Send(Arg.Is<IRequest>(req =>
req.BaseAddress == _exampleUri &&
req.Timeout == TimeSpan.FromSeconds(1) &&
req.ContentType == null &&
req.Body == null &&
req.Method == HttpMethod.Get &&
req.Headers["Accept"] == "application/vnd.github.v3.raw" &&
req.Endpoint == new Uri("endpoint", UriKind.Relative)), Args.CancellationToken);
}

[Fact]
public async Task ReturnsCorrectContentForNull()
{
object body = null;
var httpClient = Substitute.For<IHttpClient>();
var response = CreateResponse(HttpStatusCode.OK, body);
httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
var connection = new Connection(new ProductHeaderValue("OctokitTests"),
_exampleUri,
Substitute.For<ICredentialStore>(),
httpClient,
Substitute.For<IJsonSerializer>());

var actual = await connection.GetRaw(new Uri("endpoint", UriKind.Relative), new Dictionary<string, string>());

Assert.NotNull(actual);
Assert.Null(actual.Body);
}

[Fact]
public async Task ReturnsCorrectContentForByteArray()
{
var body = new byte[] { 1, 2, 3 };

var httpClient = Substitute.For<IHttpClient>();
var response = CreateResponse(HttpStatusCode.OK, body);
httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
var connection = new Connection(new ProductHeaderValue("OctokitTests"),
_exampleUri,
Substitute.For<ICredentialStore>(),
httpClient,
Substitute.For<IJsonSerializer>());

var actual = await connection.GetRaw(new Uri("endpoint", UriKind.Relative), new Dictionary<string, string>());

Assert.NotNull(actual);
Assert.Equal(body, actual.Body);
}

[Fact]
public async Task ReturnsCorrectContentForStream()
{
var bytes = new byte[] { 1, 2, 3 };
var body = new MemoryStream(bytes);

var httpClient = Substitute.For<IHttpClient>();
var response = CreateResponse(HttpStatusCode.OK, body);
httpClient.Send(Args.Request, Args.CancellationToken).Returns(Task.FromResult(response));
var connection = new Connection(new ProductHeaderValue("OctokitTests"),
_exampleUri,
Substitute.For<ICredentialStore>(),
httpClient,
Substitute.For<IJsonSerializer>());

var actual = await connection.GetRaw(new Uri("endpoint", UriKind.Relative), new Dictionary<string, string>());

Assert.NotNull(actual);
Assert.Equal(bytes, actual.Body);
}
}

public class ThePatchMethod
{
[Fact]
Expand Down
9 changes: 7 additions & 2 deletions Octokit/Http/Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -714,8 +714,13 @@ async Task<IApiResponse<byte[]>> GetRaw(IRequest request)
{
request.Headers.Add("Accept", AcceptHeaders.RawContentMediaType);
var response = await RunRequest(request, CancellationToken.None).ConfigureAwait(false);

return new ApiResponse<byte[]>(response, await StreamToByteArray(response.Body as Stream));

if (response.Body is Stream stream)
{
return new ApiResponse<byte[]>(response, await StreamToByteArray(stream));
}

return new ApiResponse<byte[]>(response, response.Body as byte[]);
}

async Task<IApiResponse<Stream>> GetRawStream(IRequest request)
Expand Down

0 comments on commit 4f826bc

Please sign in to comment.