Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Oracle limit #800

Merged
merged 16 commits into from
Jul 12, 2023
Merged

Add Oracle limit #800

merged 16 commits into from
Jul 12, 2023

Conversation

shargon
Copy link
Member

@shargon shargon commented Apr 27, 2023

No description provided.

@shargon
Copy link
Member Author

shargon commented Apr 27, 2023

@dotnet-policy-service agree

@shargon please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.

@dotnet-policy-service agree [company="{your company}"]

Options:

  • (default - no company specified) I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.
@dotnet-policy-service agree
  • (when company given) I am making Submissions in the course of work for my employer (or my employer has intellectual property rights in my Submissions by contract or applicable law). I have permission from my employer to make Submissions and enter into this Agreement on behalf of my employer. By signing below, the defined term “You” includes me and my employer.
@dotnet-policy-service agree company="Microsoft"

Contributor License Agreement

@shargon
Copy link
Member Author

shargon commented Apr 27, 2023

@roman-khimov Please, could you take a look?

@erikzhang
Copy link
Member

Is the response size of NeoFS also limited?

@AnnaShaleva
Copy link
Member

AnnaShaleva commented May 2, 2023

Is the response size of NeoFS also limited?

It should be limited by the client, but currently it's not. So the problem exists for OracleNeoFSProtocol as far. The size of a single physical NeoFS object is limited by 64MB in mainnet\testnet, but there are large ("virtual") objects constructed from a set of small physical ones and these are almost unlimited.

The neofs-api-csharp Client is capable to handle such large "virtual" objects and doesn't restrict their maximum size:
https://github.com/neo-ngd/neofs-api-csharp/blob/82ca3de1d87c96a04bdf094c60c7c6039bbacf81/src/Neo.FileStorage.API/client/Client.Object.cs#L106-L123
OracleNeoFSProtocol uses both client.GetObject and client.GetObjectPayloadRangeData, reads object data from the stream and converts the potentially large result directly to string:

private static async Task<string> GetPayloadAsync(Client client, Address addr, CancellationToken cancellation)
{
Object obj = await client.GetObject(addr, options: new CallOptions { Ttl = 2 }, context: cancellation);
return obj.Payload.ToString(Utility.StrictUTF8);
}

and
private static async Task<string> GetRangeAsync(Client client, Address addr, string[] ps, CancellationToken cancellation)
{
if (ps.Length == 0) throw new FormatException("missing object range (expected 'Offset|Length')");
Range range = ParseRange(ps[0]);
var res = await client.GetObjectPayloadRangeData(addr, range, options: new CallOptions { Ttl = 2 }, context: cancellation);
return Utility.StrictUTF8.GetString(res);

Preferred solution

Ideally, to solve this problem we need a change in the neofs-api-csharp Client's API to limit the amount of data that can be read from the stream. In neofs-sdk-go it is done by returning the ObjectReader structure so that it's the reader responsibility to restrict the amount of data that can be read from the underlying transmission channel: https://pkg.go.dev/github.com/nspcc-dev/neofs-sdk-go@v1.0.0-rc.8/client#Client.ObjectGetInit.

Another possible solution

Another way to solve this problem without neofs-api-csharp Client's API changes is to use GetObjectPayloadRangeData combined with manually pre-checked range lenght on the OracleNeoFSProtocol side for both GetObjectRange and GetObject operations. However, this way requires to access and check object headers firstly and is a "kludgy" way in general.

Co-authored-by: Erik Zhang <erik@neo.org>
Copy link
Member

@AnnaShaleva AnnaShaleva left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also consider fixing OracleNeoFSProtocol wrt #800 (comment).

private readonly HttpClient client = new(new HttpClientHandler() { AllowAutoRedirect = false });
private readonly HttpClient client = new(new HttpClientHandler() { AllowAutoRedirect = false })
{
MaxResponseContentBufferSize = OracleResponse.MaxResultSize
Copy link
Member

@AnnaShaleva AnnaShaleva May 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably adjust the exception handler of OracleService wrt this change. Currently if the responce content exceeds OracleResponse.MaxResultSize length, the exception will be thrown by message.Content.ReadAsStringAsync(...) method:

return (OracleResponseCode.Success, await message.Content.ReadAsStringAsync(cancellation));

It will be catched by the following catch block:
try
{
return await protocol.ProcessAsync(uri, ctsLinked.Token);
}
catch (Exception ex)
{
return (OracleResponseCode.Error, $"Request <{url}> Error:{ex.Message}");
}

As a result, we'll have OracleResponseCode.Error code instead of OracleResponseCode.ResponseTooLarge which is more appropriate in this case. In neo-go we return OracleResponseCode.ResponseTooLarge in this case.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which exception is throw?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@superboyiii could you test it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@superboyiii could you test it?

Sure

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@superboyiii could you test it?

@shargon Due to my test result, the same as @AnnaShaleva said. We need solve it.

@ZhangTao1596
Copy link
Collaborator

ZhangTao1596 commented May 12, 2023

We should also consider fixing OracleNeoFSProtocol wrt #800 (comment).

@AnnaShaleva @shargon There is a stream version GetObject here https://github.com/neo-ngd/neofs-api-csharp/blob/82ca3de1d87c96a04bdf094c60c7c6039bbacf81/src/Neo.FileStorage.API/client/Client.Object.cs#LL39C9-L39C9
We can limit read payload size in stream.

Co-authored-by: Owen Zhang <38493437+superboyiii@users.noreply.github.com>
superboyiii
superboyiii previously approved these changes May 17, 2023
@@ -82,6 +82,8 @@ public async Task<(OracleResponseCode, string)> ProcessAsync(Uri uri, Cancellati
return (OracleResponseCode.Error, message.StatusCode.ToString());
if (!Settings.Default.AllowedContentTypes.Contains(message.Content.Headers.ContentType.MediaType))
return (OracleResponseCode.ContentTypeNotSupported, null);
if (!message.Content.Headers.ContentLength.HasValue || message.Content.Headers.ContentLength > OracleResponse.MaxResultSize)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The !message.Content.Headers.ContentLength.HasValue part of it may affect legitimate responses that fit the limit, but don't have the header for whatever reason (dynamically generated content, for example).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can require this header

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That'd be an artificial limitation to me. It shouldn't be hard to handle !message.Content.Headers.ContentLength.HasValue case as well, NeoGo handles both cases easily.

Comment on lines 89 to 91
byte[] buffer = new byte[OracleResponse.MaxResultSize];
var stream = message.Content.ReadAsStream(cancellation);
var read = await stream.ReadAsync(buffer, 0, buffer.Length, cancellation);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If response stream contains more than buffer.Length bytes, then it's an error and we need to return OracleResponseCode.ResponseTooLarge code. And if I'm not mistaken, the current code will just stop reading and return a part of the response with OracleResponseCode.Success code (which isn't the desired behaviour).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, check it again

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part LGTM, resolved.

Copy link
Member

@AnnaShaleva AnnaShaleva left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shargon, could you also consider fixing the same problem for OracleNeoFSProtocol as @ZhangTao1596 suggested? #800 (comment)

Co-authored-by: Anna Shaleva <shaleva.ann@gmail.com>
Copy link
Member

@AnnaShaleva AnnaShaleva left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@shargon
Copy link
Member Author

shargon commented Jun 30, 2023

@superboyiii could you test it?

@shargon shargon requested a review from superboyiii July 5, 2023 17:23
@superboyiii
Copy link
Member

@superboyiii could you test it?

I'm ing...

Copy link
Member

@superboyiii superboyiii left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@superboyiii superboyiii merged commit 99ffc84 into master Jul 12, 2023
2 checks passed
@superboyiii superboyiii deleted the add-oracle-limit branch July 12, 2023 03:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants