On Aug 22, 2014 9:06 AM, "'Matt Menke' via net-dev" <net...@chromium.org> wrote:
>
> For anyone unfamiliar with Cronet, it's a project to give the Chrome network stack an officially supported API for external embedders. The project is currently focused on Android and iOS wrappers.
>
> Currently, the network stack supports two basic types of uploads: Those where the size is known in advance, which consist of one or more files and/or memory buffers, and chunked uploads, where the size is not know. Because of lack of server support, and existing web APIs don't need it, chunked uploads are only used for internal Chrome requests, to the extent of my knowledge.
Nope. We use these on the Web in Chromium. Voice search, for example.
>
> Currently, the way the network stack supports chunked uploads (Both within Chrome and outside of Chrome) is for the embedder to just keep on pushing data to the UploadDataStream object, which buffers all the data pushed to it until the request is complete. The buffering is needed for retries and redirects. There's no way for an embedder to know when the stream actually needs more data, so they just push data as soon as they get it.
Are you sure about this? I worked with the voice team on refactoring UploadDataStream so that the IO completion of the previous write is the signal to write more data.
> This may be convenient when streaming video or audio as it's being recorded, but it relinquishes all control over the size of the buffer, which grows without bound.
Agreed.
>
> Currently, Cronet on Android has a Java interface for this push-based chunked behavior, which ends up copying all data twice on its way to the network stack.
That's odd, as we explicitly moved away from that sort of interface after it was repeatedly showing perf and correctness issues in Chrome's implementation.
> It also has a interface to allow uploads when data size is known, that does not support retries or redirects. The latter API uses a blocking API to read data, but does manage to use two fewer copies.
Blocking?
:(
>
> Neither of these APIs is great, and we're looking to replace them with a single non-blocking API. I've been toying with two different designs: One works just like the chunked design, except it allows buffering to be disabled, and has a notification when more data is needed and when the data is to be rewound. Advantage is it's completely compatible completely with the current chunked behavior, and works quite easily with the just want to push streaming input case. It also gives the embedder control over how much data is sent to the network stack at once. Downside is it has the same two extra copies.
Where are the copies?
> Simplified API looks something like this:
>
> class UploadStream {
> UploadStream(DataPusher pusher, Boolean bufferData);
Why should the creator specify buffering? Isn't that something internal to the net stack? Isn't the assumption always buffer? Or is cronet exposing some API to explicitly disable redirect handling?
> // Can be called at any point. Data is copied into our private buffer when called.
> void AppendData(ByteBuffer buffer);
> // Just fails the request.
> void OnReadError();
> };
>
> interface DataPusher {
> // Called when all appended data has been sent. Can be ignored,
> // replied to synchronously, or replied to asynchronously.
> void OnNeedsData();
This is effectively an edge-triggered notification, but the majority of Chromium has preferred level triggered (or at least ways to query the state).
This seems harder to reason about if the caller is going to try to handle this asynchronously.
> // Returns false if rewind isn't supported.
> Boolean RewindData();
> void OnCancel();
> };
>
> The other option is to do something pull-based instead. The network stack would provide the buffer, and then wait for a callback. When buffering is not being used, this would require two fewer copies. We could even just not support buffering, and leave it up to the client, if we so desired. The network stack has complete control over its buffer size. Downsides are that it doesn't work with the old chunked upload model, and there are some buffer ownership issues on cancellation that need to be worked through. API would look something like:
>
> class UploadStream {
> UploadStream(DataProvider provider, Boolean bufferData);
> // result > 0 means data was read, result == 0 means we're done, result < 0 means error.
> void OnReadComplete(int result);
> void OnRewindComplete(Boolean success);
> };
>
> interface DataProvider {
> // The provider writes to the buffer, and calls OnReadComplete when done.
> // It may be possible to even reuse the same buffer for all calls to this, but that may be too complicated,
> // without adding an extra copy.
> void GetData(ByteBuffer buffer);
> // Embedder must call OnRewindComplete once done.
> void RewindData();
> // We'll probably need to require the embedder call into UploadStream to acknowledge this,
> // so it can safely free the native buffer - definitely need to think about this case a bit more.
> void OnCancel();
> };
>
I'll have to think about this more as well. We have had something similar in our other APIs (like SSL), and its been really tricky to use.
A meaningful experiment would be to work from some sample code backwards, and let that gauge the complexity of the interfaces. I feel one of these (won't say which) will be substantially easier and more natural to use.
> So... Anyone have any thoughts? Certainly open to other ideas.
>
> Note: In both cases, I'm ignoring the threading model, but I suspect in the first case we'd just take an Executor, and require the UploadStream be called on that thread. In the second case, I believe we can pretty easily allow the UploadStream to be called from any thread, though we'd still want an executor to know what thread to call into the DataProvider on. We could also take an optional length, to allow the same interface to be used for non-chunked uploads as well.
>
> --
> You received this message because you are subscribed to the Google Groups "net-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to net-dev+u...@chromium.org.
> To post to this group, send email to net...@chromium.org.
> To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/net-dev/CAEK7mvpRAys-NPiwrjLRurQdA9iiuG15XTGXK_NUv1iNth9u9A%40mail.gmail.com.
On Aug 22, 2014 9:06 AM, "'Matt Menke' via net-dev" <net...@chromium.org> wrote:
>
> For anyone unfamiliar with Cronet, it's a project to give the Chrome network stack an officially supported API for external embedders. The project is currently focused on Android and iOS wrappers.
>
> Currently, the network stack supports two basic types of uploads: Those where the size is known in advance, which consist of one or more files and/or memory buffers, and chunked uploads, where the size is not know. Because of lack of server support, and existing web APIs don't need it, chunked uploads are only used for internal Chrome requests, to the extent of my knowledge.Nope. We use these on the Web in Chromium. Voice search, for example.
>
> Currently, the way the network stack supports chunked uploads (Both within Chrome and outside of Chrome) is for the embedder to just keep on pushing data to the UploadDataStream object, which buffers all the data pushed to it until the request is complete. The buffering is needed for retries and redirects. There's no way for an embedder to know when the stream actually needs more data, so they just push data as soon as they get it.Are you sure about this? I worked with the voice team on refactoring UploadDataStream so that the IO completion of the previous write is the signal to write more data.
> This may be convenient when streaming video or audio as it's being recorded, but it relinquishes all control over the size of the buffer, which grows without bound.
Agreed.
>
> Currently, Cronet on Android has a Java interface for this push-based chunked behavior, which ends up copying all data twice on its way to the network stack.That's odd, as we explicitly moved away from that sort of interface after it was repeatedly showing perf and correctness issues in Chrome's implementation.
> It also has a interface to allow uploads when data size is known, that does not support retries or redirects. The latter API uses a blocking API to read data, but does manage to use two fewer copies.
Blocking?
:(
>
> Neither of these APIs is great, and we're looking to replace them with a single non-blocking API. I've been toying with two different designs: One works just like the chunked design, except it allows buffering to be disabled, and has a notification when more data is needed and when the data is to be rewound. Advantage is it's completely compatible completely with the current chunked behavior, and works quite easily with the just want to push streaming input case. It also gives the embedder control over how much data is sent to the network stack at once. Downside is it has the same two extra copies.Where are the copies?
> Simplified API looks something like this:
>
> class UploadStream {
> UploadStream(DataPusher pusher, Boolean bufferData);Why should the creator specify buffering? Isn't that something internal to the net stack? Isn't the assumption always buffer? Or is cronet exposing some API to explicitly disable redirect handling?
> // Can be called at any point. Data is copied into our private buffer when called.
> void AppendData(ByteBuffer buffer);
> // Just fails the request.
> void OnReadError();
> };
>
> interface DataPusher {
> // Called when all appended data has been sent. Can be ignored,
> // replied to synchronously, or replied to asynchronously.
> void OnNeedsData();This is effectively an edge-triggered notification, but the majority of Chromium has preferred level triggered (or at least ways to query the state).
This seems harder to reason about if the caller is going to try to handle this asynchronously.
> // Returns false if rewind isn't supported.
> Boolean RewindData();
> void OnCancel();
> };
>
> The other option is to do something pull-based instead. The network stack would provide the buffer, and then wait for a callback. When buffering is not being used, this would require two fewer copies. We could even just not support buffering, and leave it up to the client, if we so desired. The network stack has complete control over its buffer size. Downsides are that it doesn't work with the old chunked upload model, and there are some buffer ownership issues on cancellation that need to be worked through. API would look something like:
>
> class UploadStream {
> UploadStream(DataProvider provider, Boolean bufferData);
> // result > 0 means data was read, result == 0 means we're done, result < 0 means error.
> void OnReadComplete(int result);
> void OnRewindComplete(Boolean success);
> };
>
> interface DataProvider {
> // The provider writes to the buffer, and calls OnReadComplete when done.
> // It may be possible to even reuse the same buffer for all calls to this, but that may be too complicated,
> // without adding an extra copy.
> void GetData(ByteBuffer buffer);
> // Embedder must call OnRewindComplete once done.
> void RewindData();
> // We'll probably need to require the embedder call into UploadStream to acknowledge this,
> // so it can safely free the native buffer - definitely need to think about this case a bit more.
> void OnCancel();
> };
>I'll have to think about this more as well. We have had something similar in our other APIs (like SSL), and its been really tricky to use.
A meaningful experiment would be to work from some sample code backwards, and let that gauge the complexity of the interfaces. I feel one of these (won't say which) will be substantially easier and more natural to use.
On Aug 22, 2014 10:28 AM, "Matt Menke" <mme...@google.com> wrote:
>
> On Fri, Aug 22, 2014 at 12:17 PM, Ryan Sleevi <rsl...@chromium.org> wrote:
>>
>>
>> On Aug 22, 2014 9:06 AM, "'Matt Menke' via net-dev" <net...@chromium.org> wrote:
>> >
>> > For anyone unfamiliar with Cronet, it's a project to give the Chrome network stack an officially supported API for external embedders. The project is currently focused on Android and iOS wrappers.
>> >
>> > Currently, the network stack supports two basic types of uploads: Those where the size is known in advance, which consist of one or more files and/or memory buffers, and chunked uploads, where the size is not know. Because of lack of server support, and existing web APIs don't need it, chunked uploads are only used for internal Chrome requests, to the extent of my knowledge.
>>
>> Nope. We use these on the Web in Chromium. Voice search, for example.
>
> XHRs don't support chunked uploads, do they?
Voice search does it as a form post with chunked upload.
I think you may be confusing the two ways we have to voice search - one within web content, the other within Chromium itself (via URLRequest)
>>
>> >
>> > Currently, the way the network stack supports chunked uploads (Both within Chrome and outside of Chrome) is for the embedder to just keep on pushing data to the UploadDataStream object, which buffers all the data pushed to it until the request is complete. The buffering is needed for retries and redirects. There's no way for an embedder to know when the stream actually needs more data, so they just push data as soon as they get it.
>>
>> Are you sure about this? I worked with the voice team on refactoring UploadDataStream so that the IO completion of the previous write is the signal to write more data.
>
> I'm pretty sure. If we know size in advance, ElementReaders can provide data on demand. But if we're doing a chunked upload, this is our only interface. The HttpStream doesn't have support for another method of chunking, and UploadElementReaders are expected to always know their size, after initialization.
Um, UploadDataStream explicitly doesn't use UploadElementReaders when chunking. Its initialized as chunk and uses AppendChunk directly. The only requirement is that each chunk have a discrete size, which is unsurprising.
Perhaps I misunderstood your response?
>
> Also, if you don't buffer, reusing a network connection is always dangerous - we have to retry on certain errors, even with POSTs (Just imagine the case a middlebox silently timed out the connection).
>
>>
>> > This may be convenient when streaming video or audio as it's being recorded, but it relinquishes all control over the size of the buffer, which grows without bound.
>>
>> Agreed.
>>
>> >
>> > Currently, Cronet on Android has a Java interface for this push-based chunked behavior, which ends up copying all data twice on its way to the network stack.
>>
>> That's odd, as we explicitly moved away from that sort of interface after it was repeatedly showing perf and correctness issues in Chrome's implementation.
>>
>> > It also has a interface to allow uploads when data size is known, that does not support retries or redirects. The latter API uses a blocking API to read data, but does manage to use two fewer copies.
>>
>> Blocking?
>>
>> :(
>>
>> >
>> > Neither of these APIs is great, and we're looking to replace them with a single non-blocking API. I've been toying with two different designs: One works just like the chunked design, except it allows buffering to be disabled, and has a notification when more data is needed and when the data is to be rewound. Advantage is it's completely compatible completely with the current chunked behavior, and works quite easily with the just want to push streaming input case. It also gives the embedder control over how much data is sent to the network stack at once. Downside is it has the same two extra copies.
>>
>> Where are the copies?
>
> We copy from the embedder-owned buffer to the UploadStream's buffer, and then we copy from the UploadStream's buffer to the buffer used to write to the socket. With the other API, we can write directly into the buffer used to write to the socket (Unless buffering is enabled).
Or using SPDY, QUIC, or TLS.
That was a real issue with the old code and something we tried to resolve. I'm surprised to hear you say its still an issue.
On Aug 22, 2014 10:28 AM, "Matt Menke" <mme...@google.com> wrote:
>
> On Fri, Aug 22, 2014 at 12:17 PM, Ryan Sleevi <rsl...@chromium.org> wrote:
>>
>>
>> On Aug 22, 2014 9:06 AM, "'Matt Menke' via net-dev" <net...@chromium.org> wrote:
>> >
>> > For anyone unfamiliar with Cronet, it's a project to give the Chrome network stack an officially supported API for external embedders. The project is currently focused on Android and iOS wrappers.
>> >
>> > Currently, the network stack supports two basic types of uploads: Those where the size is known in advance, which consist of one or more files and/or memory buffers, and chunked uploads, where the size is not know. Because of lack of server support, and existing web APIs don't need it, chunked uploads are only used for internal Chrome requests, to the extent of my knowledge.
>>
>> Nope. We use these on the Web in Chromium. Voice search, for example.
>
> XHRs don't support chunked uploads, do they?Voice search does it as a form post with chunked upload.
I think you may be confusing the two ways we have to voice search - one within web content, the other within Chromium itself (via URLRequest)
>>
>> >
>> > Currently, the way the network stack supports chunked uploads (Both within Chrome and outside of Chrome) is for the embedder to just keep on pushing data to the UploadDataStream object, which buffers all the data pushed to it until the request is complete. The buffering is needed for retries and redirects. There's no way for an embedder to know when the stream actually needs more data, so they just push data as soon as they get it.
>>
>> Are you sure about this? I worked with the voice team on refactoring UploadDataStream so that the IO completion of the previous write is the signal to write more data.
>
> I'm pretty sure. If we know size in advance, ElementReaders can provide data on demand. But if we're doing a chunked upload, this is our only interface. The HttpStream doesn't have support for another method of chunking, and UploadElementReaders are expected to always know their size, after initialization.Um, UploadDataStream explicitly doesn't use UploadElementReaders when chunking. Its initialized as chunk and uses AppendChunk directly. The only requirement is that each chunk have a discrete size, which is unsurprising.
Perhaps I misunderstood your response?
>
> Also, if you don't buffer, reusing a network connection is always dangerous - we have to retry on certain errors, even with POSTs (Just imagine the case a middlebox silently timed out the connection).
>
>>
>> > This may be convenient when streaming video or audio as it's being recorded, but it relinquishes all control over the size of the buffer, which grows without bound.
>>
>> Agreed.
>>
>> >
>> > Currently, Cronet on Android has a Java interface for this push-based chunked behavior, which ends up copying all data twice on its way to the network stack.
>>
>> That's odd, as we explicitly moved away from that sort of interface after it was repeatedly showing perf and correctness issues in Chrome's implementation.
>>
>> > It also has a interface to allow uploads when data size is known, that does not support retries or redirects. The latter API uses a blocking API to read data, but does manage to use two fewer copies.
>>
>> Blocking?
>>
>> :(
>>
>> >
>> > Neither of these APIs is great, and we're looking to replace them with a single non-blocking API. I've been toying with two different designs: One works just like the chunked design, except it allows buffering to be disabled, and has a notification when more data is needed and when the data is to be rewound. Advantage is it's completely compatible completely with the current chunked behavior, and works quite easily with the just want to push streaming input case. It also gives the embedder control over how much data is sent to the network stack at once. Downside is it has the same two extra copies.
>>
>> Where are the copies?
>
> We copy from the embedder-owned buffer to the UploadStream's buffer, and then we copy from the UploadStream's buffer to the buffer used to write to the socket. With the other API, we can write directly into the buffer used to write to the socket (Unless buffering is enabled).Or using SPDY, QUIC, or TLS.
On Fri, Aug 22, 2014 at 1:36 PM, Ryan Sleevi <rsl...@chromium.org> wrote:
On Aug 22, 2014 10:28 AM, "Matt Menke" <mme...@google.com> wrote:
>
> On Fri, Aug 22, 2014 at 12:17 PM, Ryan Sleevi <rsl...@chromium.org> wrote:
>>
>>
>> On Aug 22, 2014 9:06 AM, "'Matt Menke' via net-dev" <net...@chromium.org> wrote:
>> >
>> > For anyone unfamiliar with Cronet, it's a project to give the Chrome network stack an officially supported API for external embedders. The project is currently focused on Android and iOS wrappers.
>> >
>> > Currently, the network stack supports two basic types of uploads: Those where the size is known in advance, which consist of one or more files and/or memory buffers, and chunked uploads, where the size is not know. Because of lack of server support, and existing web APIs don't need it, chunked uploads are only used for internal Chrome requests, to the extent of my knowledge.
>>
>> Nope. We use these on the Web in Chromium. Voice search, for example.
>
> XHRs don't support chunked uploads, do they?Voice search does it as a form post with chunked upload.
I think you may be confusing the two ways we have to voice search - one within web content, the other within Chromium itself (via URLRequest)
>>
>> >
>> > Currently, the way the network stack supports chunked uploads (Both within Chrome and outside of Chrome) is for the embedder to just keep on pushing data to the UploadDataStream object, which buffers all the data pushed to it until the request is complete. The buffering is needed for retries and redirects. There's no way for an embedder to know when the stream actually needs more data, so they just push data as soon as they get it.
>>
>> Are you sure about this? I worked with the voice team on refactoring UploadDataStream so that the IO completion of the previous write is the signal to write more data.
>
> I'm pretty sure. If we know size in advance, ElementReaders can provide data on demand. But if we're doing a chunked upload, this is our only interface. The HttpStream doesn't have support for another method of chunking, and UploadElementReaders are expected to always know their size, after initialization.Um, UploadDataStream explicitly doesn't use UploadElementReaders when chunking. Its initialized as chunk and uses AppendChunk directly. The only requirement is that each chunk have a discrete size, which is unsurprising.
Perhaps I misunderstood your response?
UploadDataStream provides no callbacks to inform anything when it needs more data. And it's not completely clear from code that it's impossible to hack up an UploadElementReader to allow it, which is why I was arguing that's also not possible.
On Fri, Aug 22, 2014 at 1:43 PM, Matt Menke <mme...@google.com> wrote:
On Fri, Aug 22, 2014 at 1:36 PM, Ryan Sleevi <rsl...@chromium.org> wrote:
On Aug 22, 2014 10:28 AM, "Matt Menke" <mme...@google.com> wrote:
>
> On Fri, Aug 22, 2014 at 12:17 PM, Ryan Sleevi <rsl...@chromium.org> wrote:
>>
>>
>> On Aug 22, 2014 9:06 AM, "'Matt Menke' via net-dev" <net...@chromium.org> wrote:
>> >
>> > For anyone unfamiliar with Cronet, it's a project to give the Chrome network stack an officially supported API for external embedders. The project is currently focused on Android and iOS wrappers.
>> >
>> > Currently, the network stack supports two basic types of uploads: Those where the size is known in advance, which consist of one or more files and/or memory buffers, and chunked uploads, where the size is not know. Because of lack of server support, and existing web APIs don't need it, chunked uploads are only used for internal Chrome requests, to the extent of my knowledge.
>>
>> Nope. We use these on the Web in Chromium. Voice search, for example.
>
> XHRs don't support chunked uploads, do they?Voice search does it as a form post with chunked upload.
I think you may be confusing the two ways we have to voice search - one within web content, the other within Chromium itself (via URLRequest)
>>
>> >
>> > Currently, the way the network stack supports chunked uploads (Both within Chrome and outside of Chrome) is for the embedder to just keep on pushing data to the UploadDataStream object, which buffers all the data pushed to it until the request is complete. The buffering is needed for retries and redirects. There's no way for an embedder to know when the stream actually needs more data, so they just push data as soon as they get it.
>>
>> Are you sure about this? I worked with the voice team on refactoring UploadDataStream so that the IO completion of the previous write is the signal to write more data.
>
> I'm pretty sure. If we know size in advance, ElementReaders can provide data on demand. But if we're doing a chunked upload, this is our only interface. The HttpStream doesn't have support for another method of chunking, and UploadElementReaders are expected to always know their size, after initialization.Um, UploadDataStream explicitly doesn't use UploadElementReaders when chunking. Its initialized as chunk and uses AppendChunk directly. The only requirement is that each chunk have a discrete size, which is unsurprising.
Perhaps I misunderstood your response?
UploadDataStream provides no callbacks to inform anything when it needs more data. And it's not completely clear from code that it's impossible to hack up an UploadElementReader to allow it, which is why I was arguing that's also not possible.Oh, sorry - it is immediately obvious. I had been thinking UploadElementReaders could be appended to an UploadDataStream after creation, but before it was passed to the ULRRequest, but that's not the case (Other than for chunked uploads).
On Fri, Aug 22, 2014 at 11:02 AM, Matt Menke <mme...@google.com> wrote:
On Fri, Aug 22, 2014 at 1:43 PM, Matt Menke <mme...@google.com> wrote:
On Fri, Aug 22, 2014 at 1:36 PM, Ryan Sleevi <rsl...@chromium.org> wrote:
On Aug 22, 2014 10:28 AM, "Matt Menke" <mme...@google.com> wrote:
>
> On Fri, Aug 22, 2014 at 12:17 PM, Ryan Sleevi <rsl...@chromium.org> wrote:
>>
>>
>> On Aug 22, 2014 9:06 AM, "'Matt Menke' via net-dev" <net...@chromium.org> wrote:
>> >
>> > For anyone unfamiliar with Cronet, it's a project to give the Chrome network stack an officially supported API for external embedders. The project is currently focused on Android and iOS wrappers.
>> >
>> > Currently, the network stack supports two basic types of uploads: Those where the size is known in advance, which consist of one or more files and/or memory buffers, and chunked uploads, where the size is not know. Because of lack of server support, and existing web APIs don't need it, chunked uploads are only used for internal Chrome requests, to the extent of my knowledge.
>>
>> Nope. We use these on the Web in Chromium. Voice search, for example.
>
> XHRs don't support chunked uploads, do they?Voice search does it as a form post with chunked upload.
I think you may be confusing the two ways we have to voice search - one within web content, the other within Chromium itself (via URLRequest)
>>
>> >
>> > Currently, the way the network stack supports chunked uploads (Both within Chrome and outside of Chrome) is for the embedder to just keep on pushing data to the UploadDataStream object, which buffers all the data pushed to it until the request is complete. The buffering is needed for retries and redirects. There's no way for an embedder to know when the stream actually needs more data, so they just push data as soon as they get it.
>>
>> Are you sure about this? I worked with the voice team on refactoring UploadDataStream so that the IO completion of the previous write is the signal to write more data.
>
> I'm pretty sure. If we know size in advance, ElementReaders can provide data on demand. But if we're doing a chunked upload, this is our only interface. The HttpStream doesn't have support for another method of chunking, and UploadElementReaders are expected to always know their size, after initialization.Um, UploadDataStream explicitly doesn't use UploadElementReaders when chunking. Its initialized as chunk and uses AppendChunk directly. The only requirement is that each chunk have a discrete size, which is unsurprising.
Perhaps I misunderstood your response?
UploadDataStream provides no callbacks to inform anything when it needs more data. And it's not completely clear from code that it's impossible to hack up an UploadElementReader to allow it, which is why I was arguing that's also not possible.Oh, sorry - it is immediately obvious. I had been thinking UploadElementReaders could be appended to an UploadDataStream after creation, but before it was passed to the ULRRequest, but that's not the case (Other than for chunked uploads).No, the caller can't explicitly supply UploadElementReaders. They can only add chunks.I'm not sure the best way to describe it, but I think we're talking about related-but-different aspects.UploadDataStream always expects more data, until a terminal chunk is sent. After all, the request can't complete until it does.I think you're looking for/describing a way for UploadDataStream to signal back-off to the embedder? Where it's not the terminal chunk, but there's too much data internally?
--
On Aug 27, 2014 1:44 PM, "Matt Menke" <mme...@google.com> wrote:
>
> All callbacks into Java to request upload data will be called on a thread other than the network thread (Which will also be a Java-managed thread),
Why? Is this a technical constraint we aren't able to overcome?
> so we'll need to call into Java, and then hop over to another thread. The current push-based API only does thread hops in C++.
>
> You can control buffer utilization Java-side, though it doesn't work quite as well...I honestly hadn't given much thought to this, but tunable buffer sizes will either need an extra copy, or small code changes in SPDY, QUIC, and HTTP logic.
I'm confused why. We definitely have internal buffer copies in our network stack but URLRequest supports tunable read buffer sizes. Why is our java interface different?
On Aug 27, 2014 1:44 PM, "Matt Menke" <mme...@google.com> wrote:
>
> All callbacks into Java to request upload data will be called on a thread other than the network thread (Which will also be a Java-managed thread),Why? Is this a technical constraint we aren't able to overcome?
> so we'll need to call into Java, and then hop over to another thread. The current push-based API only does thread hops in C++.
>
> You can control buffer utilization Java-side, though it doesn't work quite as well...I honestly hadn't given much thought to this, but tunable buffer sizes will either need an extra copy, or small code changes in SPDY, QUIC, and HTTP logic.I'm confused why. We definitely have internal buffer copies in our network stack but URLRequest supports tunable read buffer sizes. Why is our java interface different?
On Wed, Aug 27, 2014 at 4:56 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Aug 27, 2014 1:44 PM, "Matt Menke" <mme...@google.com> wrote:
>
> All callbacks into Java to request upload data will be called on a thread other than the network thread (Which will also be a Java-managed thread),Why? Is this a technical constraint we aren't able to overcome?
We're called by a Java object, which gives us a Java Executor (Or some other Java interface which allows threadhops, so the caller manages threads). I don't think we can post a message from C++ directly to an Executor, or a thread whose message loop (Or equivalent) Java is managing in some other way.
That having been said, I really don't know anything about Java threading.
> so we'll need to call into Java, and then hop over to another thread. The current push-based API only does thread hops in C++.
>
> You can control buffer utilization Java-side, though it doesn't work quite as well...I honestly hadn't given much thought to this, but tunable buffer sizes will either need an extra copy, or small code changes in SPDY, QUIC, and HTTP logic.I'm confused why. We definitely have internal buffer copies in our network stack but URLRequest supports tunable read buffer sizes. Why is our java interface different?
We don't support tunable upload buffers, actually. It's always 16k (I had thought it was 32k): https://code.google.com/p/chromium/codesearch#chromium/src/net/http/http_stream_parser.cc&sq=package:chromium&type=cs&l=28
On Wed, Aug 27, 2014 at 2:02 PM, Matt Menke <mme...@google.com> wrote:
On Wed, Aug 27, 2014 at 4:56 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Aug 27, 2014 1:44 PM, "Matt Menke" <mme...@google.com> wrote:
>
> All callbacks into Java to request upload data will be called on a thread other than the network thread (Which will also be a Java-managed thread),Why? Is this a technical constraint we aren't able to overcome?
We're called by a Java object, which gives us a Java Executor (Or some other Java interface which allows threadhops, so the caller manages threads). I don't think we can post a message from C++ directly to an Executor, or a thread whose message loop (Or equivalent) Java is managing in some other way.That having been said, I really don't know anything about Java threading.My understanding is a Java Executor is basically a TaskRunner. They're abstract interfaces for posting work. So as long as the network stack internally uses the network TaskRunner everywhere (notably, we don't, because we directly access MessageLoop::current() in some places still), then when instantiating the network TaskRunner, it seems conceivable we can have it forward messages to the Java Executor.
> so we'll need to call into Java, and then hop over to another thread. The current push-based API only does thread hops in C++.
>
> You can control buffer utilization Java-side, though it doesn't work quite as well...I honestly hadn't given much thought to this, but tunable buffer sizes will either need an extra copy, or small code changes in SPDY, QUIC, and HTTP logic.I'm confused why. We definitely have internal buffer copies in our network stack but URLRequest supports tunable read buffer sizes. Why is our java interface different?
We don't support tunable upload buffers, actually. It's always 16k (I had thought it was 32k): https://code.google.com/p/chromium/codesearch#chromium/src/net/http/http_stream_parser.cc&sq=package:chromium&type=cs&l=28Sorry, we had a terminology conflict. I assumed when you said read, you meant URLRequest::Read(). I didn't realize read buffer size meant reading from the upload buffers. Fair enough, but I also believe that our current upload API should be fixed as well.
On Wed, Aug 27, 2014 at 5:12 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Wed, Aug 27, 2014 at 2:02 PM, Matt Menke <mme...@google.com> wrote:
On Wed, Aug 27, 2014 at 4:56 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Aug 27, 2014 1:44 PM, "Matt Menke" <mme...@google.com> wrote:
>
> All callbacks into Java to request upload data will be called on a thread other than the network thread (Which will also be a Java-managed thread),Why? Is this a technical constraint we aren't able to overcome?
We're called by a Java object, which gives us a Java Executor (Or some other Java interface which allows threadhops, so the caller manages threads). I don't think we can post a message from C++ directly to an Executor, or a thread whose message loop (Or equivalent) Java is managing in some other way.That having been said, I really don't know anything about Java threading.My understanding is a Java Executor is basically a TaskRunner. They're abstract interfaces for posting work. So as long as the network stack internally uses the network TaskRunner everywhere (notably, we don't, because we directly access MessageLoop::current() in some places still), then when instantiating the network TaskRunner, it seems conceivable we can have it forward messages to the Java Executor.I'd be surprised if we could get a native interface to an Executor. I'm also not really sure what the cost is here - is it starting a Java task on a thread (In which case, we obviously have to do that once, anyways, so which it does double the penalty, at least we have an idea how bad it is, relative to what we're already doing), passing objects between threads, or some specific Java->Java hop thing...
> so we'll need to call into Java, and then hop over to another thread. The current push-based API only does thread hops in C++.
>
> You can control buffer utilization Java-side, though it doesn't work quite as well...I honestly hadn't given much thought to this, but tunable buffer sizes will either need an extra copy, or small code changes in SPDY, QUIC, and HTTP logic.I'm confused why. We definitely have internal buffer copies in our network stack but URLRequest supports tunable read buffer sizes. Why is our java interface different?
We don't support tunable upload buffers, actually. It's always 16k (I had thought it was 32k): https://code.google.com/p/chromium/codesearch#chromium/src/net/http/http_stream_parser.cc&sq=package:chromium&type=cs&l=28Sorry, we had a terminology conflict. I assumed when you said read, you meant URLRequest::Read(). I didn't realize read buffer size meant reading from the upload buffers. Fair enough, but I also believe that our current upload API should be fixed as well.I absolutely agree here, but thought it was worth mentioning that a nice API may have a real performance cost.
On Wed, Aug 27, 2014 at 2:21 PM, Matt Menke <mme...@google.com> wrote:
On Wed, Aug 27, 2014 at 5:12 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Wed, Aug 27, 2014 at 2:02 PM, Matt Menke <mme...@google.com> wrote:
On Wed, Aug 27, 2014 at 4:56 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Aug 27, 2014 1:44 PM, "Matt Menke" <mme...@google.com> wrote:
>
> All callbacks into Java to request upload data will be called on a thread other than the network thread (Which will also be a Java-managed thread),Why? Is this a technical constraint we aren't able to overcome?
We're called by a Java object, which gives us a Java Executor (Or some other Java interface which allows threadhops, so the caller manages threads). I don't think we can post a message from C++ directly to an Executor, or a thread whose message loop (Or equivalent) Java is managing in some other way.That having been said, I really don't know anything about Java threading.My understanding is a Java Executor is basically a TaskRunner. They're abstract interfaces for posting work. So as long as the network stack internally uses the network TaskRunner everywhere (notably, we don't, because we directly access MessageLoop::current() in some places still), then when instantiating the network TaskRunner, it seems conceivable we can have it forward messages to the Java Executor.I'd be surprised if we could get a native interface to an Executor. I'm also not really sure what the cost is here - is it starting a Java task on a thread (In which case, we obviously have to do that once, anyways, so which it does double the penalty, at least we have an idea how bad it is, relative to what we're already doing), passing objects between threads, or some specific Java->Java hop thing...Sounds like we need to consult a domain expert before being able to make a decision here. Do we have one handy?
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/net-dev/CAA4WUYjzpWna%2BO0hJwJyBSQhezo0NQp7z6SAPbHGpH1WMCYErQ%40mail.gmail.com.
On Wed, Aug 27, 2014 at 2:32 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Wed, Aug 27, 2014 at 2:21 PM, Matt Menke <mme...@google.com> wrote:
On Wed, Aug 27, 2014 at 5:12 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Wed, Aug 27, 2014 at 2:02 PM, Matt Menke <mme...@google.com> wrote:
On Wed, Aug 27, 2014 at 4:56 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Aug 27, 2014 1:44 PM, "Matt Menke" <mme...@google.com> wrote:
>
> All callbacks into Java to request upload data will be called on a thread other than the network thread (Which will also be a Java-managed thread),Why? Is this a technical constraint we aren't able to overcome?
We're called by a Java object, which gives us a Java Executor (Or some other Java interface which allows threadhops, so the caller manages threads). I don't think we can post a message from C++ directly to an Executor, or a thread whose message loop (Or equivalent) Java is managing in some other way.That having been said, I really don't know anything about Java threading.My understanding is a Java Executor is basically a TaskRunner. They're abstract interfaces for posting work. So as long as the network stack internally uses the network TaskRunner everywhere (notably, we don't, because we directly access MessageLoop::current() in some places still), then when instantiating the network TaskRunner, it seems conceivable we can have it forward messages to the Java Executor.I'd be surprised if we could get a native interface to an Executor. I'm also not really sure what the cost is here - is it starting a Java task on a thread (In which case, we obviously have to do that once, anyways, so which it does double the penalty, at least we have an idea how bad it is, relative to what we're already doing), passing objects between threads, or some specific Java->Java hop thing...Sounds like we need to consult a domain expert before being able to make a decision here. Do we have one handy?Summoning two domain experts (bulach@, pliard@)
On Wed, Aug 27, 2014 at 2:32 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Wed, Aug 27, 2014 at 2:21 PM, Matt Menke <mme...@google.com> wrote:
On Wed, Aug 27, 2014 at 5:12 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Wed, Aug 27, 2014 at 2:02 PM, Matt Menke <mme...@google.com> wrote:
On Wed, Aug 27, 2014 at 4:56 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Aug 27, 2014 1:44 PM, "Matt Menke" <mme...@google.com> wrote:
>
> All callbacks into Java to request upload data will be called on a thread other than the network thread (Which will also be a Java-managed thread),Why? Is this a technical constraint we aren't able to overcome?
We're called by a Java object, which gives us a Java Executor (Or some other Java interface which allows threadhops, so the caller manages threads). I don't think we can post a message from C++ directly to an Executor, or a thread whose message loop (Or equivalent) Java is managing in some other way.That having been said, I really don't know anything about Java threading.My understanding is a Java Executor is basically a TaskRunner. They're abstract interfaces for posting work. So as long as the network stack internally uses the network TaskRunner everywhere (notably, we don't, because we directly access MessageLoop::current() in some places still), then when instantiating the network TaskRunner, it seems conceivable we can have it forward messages to the Java Executor.I'd be surprised if we could get a native interface to an Executor. I'm also not really sure what the cost is here - is it starting a Java task on a thread (In which case, we obviously have to do that once, anyways, so which it does double the penalty, at least we have an idea how bad it is, relative to what we're already doing), passing objects between threads, or some specific Java->Java hop thing...Sounds like we need to consult a domain expert before being able to make a decision here. Do we have one handy?Summoning two domain experts (bulach@, pliard@)
Java has its own way to handle concurrent file operations, so it was strongly suggested to us that getting a native file handle would mess with this and was just not a very good idea.
On Wed, Aug 27, 2014 at 3:02 PM, Ryan Sleevi <rsl...@chromium.org> wrote:
On Wed, Aug 27, 2014 at 2:32 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Wed, Aug 27, 2014 at 2:21 PM, Matt Menke <mme...@google.com> wrote:
On Wed, Aug 27, 2014 at 5:12 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Wed, Aug 27, 2014 at 2:02 PM, Matt Menke <mme...@google.com> wrote:
On Wed, Aug 27, 2014 at 4:56 PM, William Chan (陈智昌) <will...@chromium.org> wrote:
On Aug 27, 2014 1:44 PM, "Matt Menke" <mme...@google.com> wrote:
>
> All callbacks into Java to request upload data will be called on a thread other than the network thread (Which will also be a Java-managed thread),Why? Is this a technical constraint we aren't able to overcome?
We're called by a Java object, which gives us a Java Executor (Or some other Java interface which allows threadhops, so the caller manages threads). I don't think we can post a message from C++ directly to an Executor, or a thread whose message loop (Or equivalent) Java is managing in some other way.That having been said, I really don't know anything about Java threading.My understanding is a Java Executor is basically a TaskRunner. They're abstract interfaces for posting work. So as long as the network stack internally uses the network TaskRunner everywhere (notably, we don't, because we directly access MessageLoop::current() in some places still), then when instantiating the network TaskRunner, it seems conceivable we can have it forward messages to the Java Executor.I'd be surprised if we could get a native interface to an Executor. I'm also not really sure what the cost is here - is it starting a Java task on a thread (In which case, we obviously have to do that once, anyways, so which it does double the penalty, at least we have an idea how bad it is, relative to what we're already doing), passing objects between threads, or some specific Java->Java hop thing...Sounds like we need to consult a domain expert before being able to make a decision here. Do we have one handy?Summoning two domain experts (bulach@, pliard@)Ryan told me that in Android, Java callbacks can only run on the UI thread.
And Java threads are expensive due to memory overhead (8MB?) amongst other things. Scheduling is mostly OK, although Android does have some thread scheduling issues, but I'll ignore those for now. But there's a cost for the JNI marshalling/unmarshalling. In order to limit this cost and reduce copies, we'd probably want to pass file descriptors (so we can do something like sendfile()) if possible. It's unclear if the Android APIs support the necessary low level platform access in order to pass descriptors rather than having to buffer in Java and marshall/unmarshall across JNI. Marcus? Philippe?
Callbacks on android can happen on any thread, not only on the ui thread. The idea that thread contention could cause multi-millisecond delays is (I think) unsubstantiated unless there's a GC. There should not be much contention here. As for marshaling/unmarshalling data, since we're using direct ByteBuffers we don't actually have to do much work - it's basically handing off a pointer.
I will say that doing a disk read or write of the size typical for the stack in a typical pattern (i.e., sequential) is less than a millisecond on modern devices - flash storage, even crappy flash storage, handles this much better than hard drives do. Not all devices are modern however, so we should measure on an Asus transformer or similar device with notoriously bad storage speeds.
As for a CPU/memory tradeoff, CPU off the UI thread tends to be plentiful even on low end devices - it's garbage generation and UI thread work that causes dropped frames.
Passing file descriptors around isn't ideal, because in java files are guaranteed to be consistent per VM, and I think accessing them from native code breaks that guarantee.
Also worth noting that the interface is the same for the consumer regardless of whether we do the writing/reading on the network thread or not, which is nice. Same thing with two buffers vs one buffer. If we did do multiple buffers, we'd probably just have one per executor thread.
You received this message because you are subscribed to a topic in the Google Groups "net-dev" group.
To unsubscribe from this topic, visit https://groups.google.com/a/chromium.org/d/topic/net-dev/yvmWG4hexBE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to net-dev+u...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/net-dev/CAPDyj_on3572uUEthRWT1E%2BGCupiCYq7KRX%3DyAvBLb9spmzyXw%40mail.gmail.com.
Callbacks on android can happen on any thread, not only on the ui thread. The idea that thread contention could cause multi-millisecond delays is (I think) unsubstantiated unless there's a GC. There should not be much contention here. As for marshaling/unmarshalling data, since we're using direct ByteBuffers we don't actually have to do much work - it's basically handing off a pointer.
I will say that doing a disk read or write of the size typical for the stack in a typical pattern (i.e., sequential) is less than a millisecond on modern devices - flash storage, even crappy flash storage, handles this much better than hard drives do. Not all devices are modern however, so we should measure on an Asus transformer or similar device with notoriously bad storage speeds.
As for a CPU/memory tradeoff, CPU off the UI thread tends to be plentiful even on low end devices - it's garbage generation and UI thread work that causes dropped frames.
Passing file descriptors around isn't ideal, because in java files are guaranteed to be consistent per VM, and I think accessing them from native code breaks that guarantee.
On Wed, Aug 27, 2014 at 5:40 PM, Charles Munger <c...@google.com> wrote:
Callbacks on android can happen on any thread, not only on the ui thread. The idea that thread contention could cause multi-millisecond delays is (I think) unsubstantiated unless there's a GC. There should not be much contention here. As for marshaling/unmarshalling data, since we're using direct ByteBuffers we don't actually have to do much work - it's basically handing off a pointer.
I will say that doing a disk read or write of the size typical for the stack in a typical pattern (i.e., sequential) is less than a millisecond on modern devices - flash storage, even crappy flash storage, handles this much better than hard drives do. Not all devices are modern however, so we should measure on an Asus transformer or similar device with notoriously bad storage speeds.
There are lots of cases I can imagine where an app would want to be writing to a file while uploading it (a live streaming video app, for example).
There are lots of cases I can imagine where an app would want to be writing to a file while uploading it (a live streaming video app, for example).
I'm not sure how the stack works internally, but won't it not be zero copy because of gzip?
I think a one-copy is not the end of the world in exchange for the safety of channels, but we only need that for chunked uploads. If you know the length of the file, you shouldn't be writing to it, and we don't need to worry about consistency. It shouldn't be hard to special case FileChannel for a zero copy implementation.
I'm not sure how the stack works internally, but won't it not be zero copy because of gzip?
I think a one-copy is not the end of the world in exchange for the safety of channels, but we only need that for chunked uploads. If you know the length of the file, you shouldn't be writing to it, and we don't need to worry about consistency. It shouldn't be hard to special case FileChannel for a zero copy implementation.
You get 1 and 3 but not 2. The way direct bytebuffers in java work is that they just wrap a void pointer, so I just use them to wrap an IOBuffer's data. This is why I use channels over input streams.
In the non-chunked case, we absolutely can use the zero copy API, since people shouldn't be writing to files they already know the length of, and we can make this clear in the doc. However, upon further investigation there's no easy way to get the FD out of a FileChannel, so we'll have to provide another method for it.
That's actually quite desirable in general though, because http://developer.android.com/reference/android/os/ParcelFileDescriptor.html is the standard way of getting data from contentproviders, and they don't natively support channels without wrapping an inputstream. I don't know much about low-level kernel things like this, so I'm not 100% sure they're the same file descriptor, but it should work. This seems like it would only work for non gzip content, and at least for google servers gzip is widely supported, so I'd like it. Why don't we gzip on upload?
On Aug 31, 2014 6:52 PM, "William Chan (陈智昌)" <will...@chromium.org> wrote:
>
> On Fri, Aug 29, 2014 at 4:46 PM, Charles Munger <c...@google.com> wrote:
>>
>> You get 1 and 3 but not 2. The way direct bytebuffers in java work is that they just wrap a void pointer, so I just use them to wrap an IOBuffer's data. This is why I use channels over input streams.
>
>
> Ah, great. Anyway, saving 2 copies and buffer memory is great too :)
To clarify, is there any savings for encrypted connections?
>
>>
>>
>> In the non-chunked case, we absolutely can use the zero copy API, since people shouldn't be writing to files they already know the length of, and we can make this clear in the doc. However, upon further investigation there's no easy way to get the FD out of a FileChannel, so we'll have to provide another method for it.
>
>
> It's not just sendfile() with a known file length. There's also splice() where you're proxying. For example, reading data from Google Drive on one socket and outputting it to a destination socket. My specific example is of course broken because we'd have to pull into user space to decrypt, but you get the idea.
I knew there was something I was missing. I'm of the opinion that we should not complicate the API in order to obtain an optimization that only has a benefit for unencrypted connections.
>
>>
>>
>> That's actually quite desirable in general though, because http://developer.android.com/reference/android/os/ParcelFileDescriptor.html is the standard way of getting data from contentproviders, and they don't natively support channels without wrapping an inputstream. I don't know much about low-level kernel things like this, so I'm not 100% sure they're the same file descriptor, but it should work. This seems like it would only work for non gzip content, and at least for google servers gzip is widely supported, so I'd like it. Why don't we gzip on upload?
>
>
> We don't gzip on upload because the Chromium network stack's primary consumer is the open web. There are two problems with gzip on upload in the _general_ case: (1) intermediaries (2) origin servers. HTTPS mostly eliminates issues with (1), although I can't guarantee that a MITM proxy (when you have an administratively installed root cert, such as the enterprise case) won't barf on it. YMMV. (2) is simply the case because not all frontends expect to have to support gzip for request bodies. Unlike response bodies where you already have burned a roundtrip on negotiation (and thereby know it's safe), with requests you can't negotiate. Therefore, you need prior knowledge in order to gzip on upload via HTTP content coding. Of course, with Cronet, it's entirely reasonable to presume prior knowledge (since it's likely the client application and app server are owned by the same entities), so it's likely that we will eventually want to provide an option for this. Although we might be lazy about implementing this, since it's also entirely reasonable to perform the compression/decompression above the HTTP stack, rather than force Cronet to do this. This approach is also more resistant to interference from HTTP intermediaries.
Interesting. Can we not get this info after the SPDY handshake?
On Aug 31, 2014 11:18 PM, "Charles Munger" <c...@google.com> wrote:
>
>
> On Aug 31, 2014 6:52 PM, "William Chan (陈智昌)" <will...@chromium.org> wrote:
> >
> > On Fri, Aug 29, 2014 at 4:46 PM, Charles Munger <c...@google.com> wrote:
> >>
> >> You get 1 and 3 but not 2. The way direct bytebuffers in java work is that they just wrap a void pointer, so I just use them to wrap an IOBuffer's data. This is why I use channels over input streams.
> >
> >
> > Ah, great. Anyway, saving 2 copies and buffer memory is great too :)
>
> To clarify, is there any savings for encrypted connections?
Depends if you are terminating the encrypted connection. If you are a passthrough proxy that doesn't inspect the encrypted traffic, then there are some definite savings.
>
> >
> >>
> >>
>
> >> In the non-chunked case, we absolutely can use the zero copy API, since people shouldn't be writing to files they already know the length of, and we can make this clear in the doc. However, upon further investigation there's no easy way to get the FD out of a FileChannel, so we'll have to provide another method for it.
> >
> >
> > It's not just sendfile() with a known file length. There's also splice() where you're proxying. For example, reading data from Google Drive on one socket and outputting it to a destination socket. My specific example is of course broken because we'd have to pull into user space to decrypt, but you get the idea.
>
> I knew there was something I was missing. I'm of the opinion that we should not complicate the API in order to obtain an optimization that only has a benefit for unencrypted connections.
Haha! Well played sir, you are targeting my weakness for encouraging encryption. I mostly agree with that statement, but it also so happens that I believe (in general, although I'm still trying to understand the Android specific conditions here) that a pull based channel/stream approach is best. So I need to spend a bit more time to grok the ByteBuffer suggestions here.
>
> >> That's actually quite desirable in general though, because http://developer.android.com/reference/android/os/ParcelFileDescriptor.html is the standard way of getting data from contentproviders, and they don't natively support channels without wrapping an inputstream. I don't know much about low-level kernel things like this, so I'm not 100% sure they're the same file descriptor, but it should work. This seems like it would only work for non gzip content, and at least for google servers gzip is widely supported, so I'd like it. Why don't we gzip on upload?
> >
> >
> > We don't gzip on upload because the Chromium network stack's primary consumer is the open web. There are two problems with gzip on upload in the _general_ case: (1) intermediaries (2) origin servers. HTTPS mostly eliminates issues with (1), although I can't guarantee that a MITM proxy (when you have an administratively installed root cert, such as the enterprise case) won't barf on it. YMMV. (2) is simply the case because not all frontends expect to have to support gzip for request bodies. Unlike response bodies where you already have burned a roundtrip on negotiation (and thereby know it's safe), with requests you can't negotiate. Therefore, you need prior knowledge in order to gzip on upload via HTTP content coding. Of course, with Cronet, it's entirely reasonable to presume prior knowledge (since it's likely the client application and app server are owned by the same entities), so it's likely that we will eventually want to provide an option for this. Although we might be lazy about implementing this, since it's also entirely reasonable to perform the compression/decompression above the HTTP stack, rather than force Cronet to do this. This approach is also more resistant to interference from HTTP intermediaries.
>
> Interesting. Can we not get this info after the SPDY handshake?
No. This was a big subject of debate in the httpbis working group and it was decided that we couldn't support this in the initial handshake. It's complicated. I can send you a link to the thread if you want, but the short answer is no we can't do this in a standard way. It's possible if you control both the client and server to do something that's non-standard.
Also, it's still safer for the application to do the content encoding itself. It's risky to have the library do it, because you want to make sure the application is thinking about whether or not secrets are getting compressed with attacker controlled data. They should use a separate compression context so that attackers can't probe for the secret a la CRIME. Cronet doesn't have enough information about the content to be able to ensure this, so it's up to the embedder to do it. As long as you're not sending any secrets in the data stream, then it's safe for Cronet to do the compression itself, but then someone screws up and we have a security bug.
On Aug 31, 2014 6:52 PM, "William Chan (陈智昌)" <will...@chromium.org> wrote:
>
> On Fri, Aug 29, 2014 at 4:46 PM, Charles Munger <c...@google.com> wrote:
>>
>> You get 1 and 3 but not 2. The way direct bytebuffers in java work is that they just wrap a void pointer, so I just use them to wrap an IOBuffer's data. This is why I use channels over input streams.
>
>
> Ah, great. Anyway, saving 2 copies and buffer memory is great too :)To clarify, is there any savings for encrypted connections?
On Sep 1, 2014 2:24 PM, "William Chan (陈智昌)" <will...@chromium.org> wrote:
>
> On Aug 31, 2014 11:18 PM, "Charles Munger" <c...@google.com> wrote:
> >
> >
> > On Aug 31, 2014 6:52 PM, "William Chan (陈智昌)" <will...@chromium.org> wrote:
> > >
> > > On Fri, Aug 29, 2014 at 4:46 PM, Charles Munger <c...@google.com> wrote:
> > >>
> > >> You get 1 and 3 but not 2. The way direct bytebuffers in java work is that they just wrap a void pointer, so I just use them to wrap an IOBuffer's data. This is why I use channels over input streams.
> > >
> > >
> > > Ah, great. Anyway, saving 2 copies and buffer memory is great too :)
> >
> > To clarify, is there any savings for encrypted connections?
>
> Depends if you are terminating the encrypted connection. If you are a passthrough proxy that doesn't inspect the encrypted traffic, then there are some definite savings.
I don't think there are going to be a lot of consumers using this to run proxy servers on their phones.
>
> >
> > >
> > >>
> > >>
> >
> > >> In the non-chunked case, we absolutely can use the zero copy API, since people shouldn't be writing to files they already know the length of, and we can make this clear in the doc. However, upon further investigation there's no easy way to get the FD out of a FileChannel, so we'll have to provide another method for it.
> > >
> > >
> > > It's not just sendfile() with a known file length. There's also splice() where you're proxying. For example, reading data from Google Drive on one socket and outputting it to a destination socket. My specific example is of course broken because we'd have to pull into user space to decrypt, but you get the idea.
> >
> > I knew there was something I was missing. I'm of the opinion that we should not complicate the API in order to obtain an optimization that only has a benefit for unencrypted connections.
>
> Haha! Well played sir, you are targeting my weakness for encouraging encryption. I mostly agree with that statement, but it also so happens that I believe (in general, although I'm still trying to understand the Android specific conditions here) that a pull based channel/stream approach is best. So I need to spend a bit more time to grok the ByteBuffer suggestions here.
OK. We're in agreement that a pull based approach is best, that's how it's currently implemented. I have a pretty good understanding of the java and android requirements and options, if you want we could meet tomorrow and post a summary to the thread.
On Sep 1, 2014 2:58 PM, "Matt Menke" <mme...@google.com> wrote:
>
> On Mon, Sep 1, 2014 at 2:18 AM, Charles Munger <c...@google.com> wrote:
>>
>>
>> On Aug 31, 2014 6:52 PM, "William Chan (陈智昌)" <will...@chromium.org> wrote:
>> >
>> > On Fri, Aug 29, 2014 at 4:46 PM, Charles Munger <c...@google.com> wrote:
>> >>
>> >> You get 1 and 3 but not 2. The way direct bytebuffers in java work is that they just wrap a void pointer, so I just use them to wrap an IOBuffer's data. This is why I use channels over input streams.
>> >
>> >
>> > Ah, great. Anyway, saving 2 copies and buffer memory is great too :)
>>
>> To clarify, is there any savings for encrypted connections?
>
>
> We'd save the same number of copies for encrypted and unencrypted connections, though encrypted ones have extra copies, of course, and a lot of other work besides, so saving 2 copies may make a negligible difference in that case, but we'd still be saving the copies.
Are we? I thought the cost saved by sendfile was bringing the bytes into userspace. If we're doing that already for encryption then what are the copies we're saving?
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/net-dev/CAHktk4gxYJ_%3D2q4hTd5sV7kKzHjDzATcu%2BscG0KxECcmRMQN0A%40mail.gmail.com.
I'd like to voice my dissent against loopers.
I think the API guarantee you should provide is:
1. If a request is canceled during a listener callback, the listener will not get further calls other than the error call.
2. If the request is canceled from somewhere other than than inside the listener callback, regardless of thread, then the listener will receive at most one callback before the error callback, and might receive one.
This is the same as saying: "If a cancelation event happens-before the callback, the callback will not take place", using the definition of happens-before from the java memory model.
You can absolutely provide the guarantees you want with executors.
If we require cancel to be called on the Looper, too, problem solved - we can have a strong guarantee.
We don't need exclusive access to the thread - we don't do any blocking IO on it, so could even use the main thread. If you do blocking IO on it, you need another thread, regardless.
As for cancellation... Cancel the request, wait for your pending write to complete, if there is one, then delete the file. I don't see how the cancel callback affects anything.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/net-dev/CAEK7mvpQn6-rFa1tMCKegjHsd3fE%3Dn0rYYc_-%2BwaU3wMCpJ_Yg%40mail.gmail.com.
I'd also like to express my preference towards the Executor approach. One could easily provide an executor based on a Looper, making a Looper from an Executor is not always possible. Therefore, for apps that internally manage their multitasking with executors (which I believe applies to all large-enough apps) it will be much harder to use the new API.
Using loopers does not reduce complexity. Given that all the request callbacks for a given request are never concurrent, it doesn't matter whether they happen on one thread or ten. Forcing cancel to be called on the looper does not add any safety or even different behavior - if a task is running, cancel will not interrupt it, and if it isn't it has the same behavior as the executor.
Loopers are not better, they are strictly worse than using executors, and the behavior is exactly the same.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/net-dev/CAHktk4jML7gDp%2BxMoUiwKUg8etxQecHeUQ%3D2rc4MzyCFT4tnNQ%40mail.gmail.com.