Comments (7)
I agree that this is a problem. The design of tide and async-h1 requires having multiple clones of the AsyncRead+AsyncWrite around, because both the request and response need access to it, in an indeterminate order
from async-h1.
We tried implementing async-h1
without be Clone
bound because we indeed never concurrently access the underlying stream — but this requires GATs to express in the language. Without that it's mostly trading off workarounds.
One thing I'm working on right now is to make it so std::sync::Arc<T>
implements Read
or Write
if &T: Read
or &T: Write
. This can already be done through workarounds, but having it directly from std would make this easier to do — and would make the workaround for GATs we're relying on in async-h1
easier to use.
from async-h1.
The design of tide and async-h1 requires having multiple clones of the AsyncRead+AsyncWrite around, because both the request and response need access to it, in an indeterminate order
Well... concurrent access to the reader and writer sides makes sense. Would the order still be indeterminate if those two sides were split? ie. I would expect the reader side to have a determined order (read the request header, read the request body, repeat) and the writer side to have a determined order.
Each side could be leased to the request handler independently.
from async-h1.
Would the order still be indeterminate if those two sides were split?
I don't understand why we'd want to split these sides? Generally both types will refer to the same item: e.g. a single TcpStream
. So by "splitting" them all we're really doing is wrapping them in something like async-dup
(or in the future Arc
with the right traits on it) and passing the same item twice.
The reason why e.g. &File: Read + Write
is because at the OS-level nothing is stopping the system from opening a handle to the same file descriptor twice. Users of these APIs are expected to uphold invariants here, and ensure at a system level no races occur. So Rust treats this as a shared resource and implements Read / Write ops accordingly.
from async-h1.
I don't understand why we'd want to split these sides?
There's "does it make sense" and "is it desirable" as two separate questions.
For why it makes sense: a TcpStream is a duplex channel, you could easily imagine one thread writing to a stream, whilst another thread is reading from it, without any need for the two to coordinate. This differs from eg. a file, where reads and writes to the same file must be carefully synchronized, as they modify shared state (the read/write position).
The reason why e.g. &File: Read + Write is because at the OS-level nothing is stopping the system from opening a handle to the same file descriptor twice.
Yeah, the OS primitives are effectively internally mutable. A bit like the Atomic* types, in that operations on them are generally atomic but that does not guarantee sane results.
However, it's generally considered bad practice in Rust to make everything be internally mutable: the opposite is true. We usually opt-into that, so eg. references to adaptors for AsyncRead / AsyncWrite will not generally implement those traits. While an OS file may be readable via shared reference, a BufReader
constructed around that will require a mutable reference.
By splitting the read/write sides, it avoids unnecessary synchonization: splitting the OS primitives is effectively free, because you can just clone them and they internally synchronize. Splitting the adapted type is unnecessary, because you can adapt the two sides of the OS primitive separately.
It also allows higher level APIs to exist that do prevent footguns like two threads concurrently writing to the same TcpStream, even if the lower-level OS primitives don't prevent that.
Finally, as it is used in async-h1, it seems like there could also be security impliciations to sharing the reader in this way, unless you implement some kind of "poisoning" mechanism. I could smuggle one request inside another:
POST /bleh
,...
<begin request body>
<bad data which causes request handler to fail or panic>
POST /internal/dangerous_endpoint
<smuggled request>
If each side of the TcpStream is uniquely owned, then this kind of situation can't happen.
from async-h1.
It's also not clear how #151 could be solved without either removing buffering entirely, which would seriously degrade performance, or by requiring the caller to pass a BufRead
instead of a Read
, which would not implement Clone
.
from async-h1.
I implemented the primitive I described in the issue: https://github.com/rust-lang/futures-rs/pull/2328/files
This could be used instead of Arc<T>
to allow multiple tasks access to the same stream, but restricted to a specific order.
from async-h1.
Related Issues (20)
- gracefully fail HTTP/1.0 connections HOT 2
- Fix Wasm build HOT 5
- Cache `Date` timestamps up to the minute HOT 2
- body is interpreted as next request if handler doesn't read it HOT 1
- Investigate the `http-desync-guardian` crate
- "Continue" response can interleave with response data HOT 7
- Parsing Url is one of the most expensive things that async-h1 does HOT 3
- CloseableCursor implementation is racey HOT 1
- tcp_nodelay HOT 1
- Panic in both client and server: "Empty response" HOT 2
- Header DoS (surf#298)
- Panic in server when sending non-ASCII header value HOT 5
- Keep-Alive and persistent connection in client code
- Handle responses without Content-Length
- When acting as a client, processing HTTP/1.0 responses fails HOT 4
- Decode inserts malformed date header HOT 2
- Chunked decoding fails on a chunk of length 0xFF7 (4087), 9 bytes less than the buffer size HOT 2
- How to send head and body on single tcp packet? HOT 1
- Server response sending triggers Nagle's algorithm/delayed ACK interaction
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from async-h1.