parent
2b0bdc3257
commit
bd660b2afb
4 changed files with 66 additions and 33 deletions
|
|
@ -217,6 +217,12 @@ impl HandshakeEngine {
|
||||||
_authentication_context: &AuthenticationContext,
|
_authentication_context: &AuthenticationContext,
|
||||||
) -> Result<HandshakeResult, AcceptError> {
|
) -> Result<HandshakeResult, AcceptError> {
|
||||||
let mut first_msg = receiver.receive().await.map_err(AcceptError::Receive)?;
|
let mut first_msg = receiver.receive().await.map_err(AcceptError::Receive)?;
|
||||||
|
tracing::debug!(
|
||||||
|
message_type = ?first_msg.get_type(),
|
||||||
|
version = ?first_msg.get_str(DataType::Version),
|
||||||
|
client_id = ?first_msg.get_data(DataType::Id),
|
||||||
|
"received MTP opening message"
|
||||||
|
);
|
||||||
|
|
||||||
let version_str = match first_msg.get_data(DataType::Version) {
|
let version_str = match first_msg.get_data(DataType::Version) {
|
||||||
Some(DataValue::Str(s)) => s.clone(),
|
Some(DataValue::Str(s)) => s.clone(),
|
||||||
|
|
@ -271,6 +277,11 @@ impl HandshakeEngine {
|
||||||
return Err(AcceptError::UnsupportedVersion(client_version));
|
return Err(AcceptError::UnsupportedVersion(client_version));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
tracing::debug!(
|
||||||
|
client_version = %client_version,
|
||||||
|
negotiated_version = %negotiated,
|
||||||
|
"MTP protocol version negotiated"
|
||||||
|
);
|
||||||
|
|
||||||
let codec = VersionedCodec::for_version(self.registry.clone(), negotiated.clone())
|
let codec = VersionedCodec::for_version(self.registry.clone(), negotiated.clone())
|
||||||
.ok_or_else(|| AcceptError::UnsupportedVersion(negotiated.clone()))?;
|
.ok_or_else(|| AcceptError::UnsupportedVersion(negotiated.clone()))?;
|
||||||
|
|
@ -1105,6 +1116,12 @@ async fn send_rejection_generic<S: HandshakeSender>(
|
||||||
.add_typed_default(DataType::Connected, DataValue::BoolFalse)
|
.add_typed_default(DataType::Connected, DataValue::BoolFalse)
|
||||||
.add_typed_default(DataType::ErrorMessage, DataValue::Str(reason.to_string())),
|
.add_typed_default(DataType::ErrorMessage, DataValue::Str(reason.to_string())),
|
||||||
};
|
};
|
||||||
|
tracing::debug!(
|
||||||
|
reason = %reason,
|
||||||
|
response_type = ?response.get_type(),
|
||||||
|
has_version = response.get_data(DataType::Version).is_some(),
|
||||||
|
"sending MTP handshake rejection"
|
||||||
|
);
|
||||||
let _ = sender.send(&response).await;
|
let _ = sender.send(&response).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1138,6 +1155,11 @@ async fn send_accepted_generic<S: HandshakeSender>(
|
||||||
if let Some(id) = assigned_id {
|
if let Some(id) = assigned_id {
|
||||||
response = response.add_typed_default(DataType::Id, DataValue::UnsignedNumber(id as u128));
|
response = response.add_typed_default(DataType::Id, DataValue::UnsignedNumber(id as u128));
|
||||||
}
|
}
|
||||||
|
tracing::debug!(
|
||||||
|
version = %version,
|
||||||
|
assigned_id = ?assigned_id,
|
||||||
|
"sending accepted MTP handshake response"
|
||||||
|
);
|
||||||
sender.send(&response).await?;
|
sender.send(&response).await?;
|
||||||
sender.finish_stream().await
|
sender.finish_stream().await
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ impl TransportRecvStream for H3TransportReceiver {
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
remote = %self.quinn.remote_address(),
|
remote = %self.quinn.remote_address(),
|
||||||
bytes = buf.len(),
|
bytes = buf.len(),
|
||||||
|
header = ?buf,
|
||||||
"received first bytes from WebTransport MTP stream"
|
"received first bytes from WebTransport MTP stream"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -423,6 +423,12 @@ impl<C: TransportConnection> GenericReceiver<C> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
tracing::debug!(
|
||||||
|
frames,
|
||||||
|
frame_len,
|
||||||
|
message_type = ?message.get_type(),
|
||||||
|
"decoded MTP receive frame"
|
||||||
|
);
|
||||||
let negotiated_type_map = type_map.read().await.clone();
|
let negotiated_type_map = type_map.read().await.clone();
|
||||||
message.set_type_map(&negotiated_type_map);
|
message.set_type_map(&negotiated_type_map);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -128,8 +128,6 @@ pub struct WasmTransport {
|
||||||
buffer: Rc<RefCell<Vec<u8>>>,
|
buffer: Rc<RefCell<Vec<u8>>>,
|
||||||
/// Set to `true` when `open_next_stream` succeeds; cleared after the first frame is parsed.
|
/// Set to `true` when `open_next_stream` succeeds; cleared after the first frame is parsed.
|
||||||
new_stream_frame: Rc<Cell<bool>>,
|
new_stream_frame: Rc<Cell<bool>>,
|
||||||
/// A single ordered browser send stream shared by all cloned transports.
|
|
||||||
outgoing_writer: Rc<RefCell<Option<JsValue>>>,
|
|
||||||
/// Serializes stream creation and writes across concurrent callers.
|
/// Serializes stream creation and writes across concurrent callers.
|
||||||
send_lock: Rc<AsyncMutex<()>>,
|
send_lock: Rc<AsyncMutex<()>>,
|
||||||
type_map: Rc<RefCell<TypeMap>>,
|
type_map: Rc<RefCell<TypeMap>>,
|
||||||
|
|
@ -209,7 +207,6 @@ impl WasmTransport {
|
||||||
stream_reader: Rc::new(RefCell::new(None)),
|
stream_reader: Rc::new(RefCell::new(None)),
|
||||||
buffer: Rc::new(RefCell::new(Vec::new())),
|
buffer: Rc::new(RefCell::new(Vec::new())),
|
||||||
new_stream_frame: Rc::new(Cell::new(false)),
|
new_stream_frame: Rc::new(Cell::new(false)),
|
||||||
outgoing_writer: Rc::new(RefCell::new(None)),
|
|
||||||
send_lock: Rc::new(AsyncMutex::new(())),
|
send_lock: Rc::new(AsyncMutex::new(())),
|
||||||
type_map: Rc::new(RefCell::new(TypeMap::latest())),
|
type_map: Rc::new(RefCell::new(TypeMap::latest())),
|
||||||
decode_limits: Rc::new(RefCell::new(decode_limits)),
|
decode_limits: Rc::new(RefCell::new(decode_limits)),
|
||||||
|
|
@ -247,30 +244,30 @@ impl WasmTransport {
|
||||||
return Err(js_error("message too large"));
|
return Err(js_error("message too large"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let writer_val = if let Some(writer) = self.outgoing_writer.borrow().clone() {
|
// Use one WebTransport uni-stream per MTP frame. Chromium reliably
|
||||||
writer
|
// publishes a browser-created uni-stream to the peer when it is
|
||||||
} else {
|
// closed; leaving a shared stream open can leave the server waiting
|
||||||
let create_stream = js_sys::Reflect::get(
|
// in accept_uni() until the authentication deadline. The bytes are
|
||||||
&self.inner,
|
// already the canonical MTP self-framed value, so no extra stream
|
||||||
&JsValue::from_str("createUnidirectionalStream"),
|
// length prefix is added here.
|
||||||
)?
|
let create_stream = js_sys::Reflect::get(
|
||||||
|
&self.inner,
|
||||||
|
&JsValue::from_str("createUnidirectionalStream"),
|
||||||
|
)?
|
||||||
|
.dyn_into::<js_sys::Function>()
|
||||||
|
.map_err(|_| js_error("createUnidirectionalStream not a function"))?;
|
||||||
|
let stream_promise = create_stream
|
||||||
|
.call0(&self.inner)?
|
||||||
|
.dyn_into::<js_sys::Promise>()
|
||||||
|
.map_err(|_| js_error("createUnidirectionalStream did not return a Promise"))?;
|
||||||
|
let stream = JsFuture::from(stream_promise).await?;
|
||||||
|
let writable_or_stream = resolve_stream_writable(&stream)?;
|
||||||
|
let writer_val = js_sys::Reflect::get(&writable_or_stream, &JsValue::from_str("getWriter"))
|
||||||
|
.map_err(|_| js_error("missing getWriter"))?
|
||||||
.dyn_into::<js_sys::Function>()
|
.dyn_into::<js_sys::Function>()
|
||||||
.map_err(|_| js_error("createUnidirectionalStream not a function"))?;
|
.map_err(|_| js_error("getWriter not a function"))?
|
||||||
let stream_promise = create_stream
|
.call0(&writable_or_stream)
|
||||||
.call0(&self.inner)?
|
.map_err(|_| js_error("getWriter call failed"))?;
|
||||||
.dyn_into::<js_sys::Promise>()
|
|
||||||
.map_err(|_| js_error("createUnidirectionalStream did not return a Promise"))?;
|
|
||||||
let stream = JsFuture::from(stream_promise).await?;
|
|
||||||
let writable_or_stream = resolve_stream_writable(&stream)?;
|
|
||||||
let writer = js_sys::Reflect::get(&writable_or_stream, &JsValue::from_str("getWriter"))
|
|
||||||
.map_err(|_| js_error("missing getWriter"))?
|
|
||||||
.dyn_into::<js_sys::Function>()
|
|
||||||
.map_err(|_| js_error("getWriter not a function"))?
|
|
||||||
.call0(&writable_or_stream)
|
|
||||||
.map_err(|_| js_error("getWriter call failed"))?;
|
|
||||||
*self.outgoing_writer.borrow_mut() = Some(writer.clone());
|
|
||||||
writer
|
|
||||||
};
|
|
||||||
|
|
||||||
let chunk = js_sys::Uint8Array::from(frame);
|
let chunk = js_sys::Uint8Array::from(frame);
|
||||||
|
|
||||||
|
|
@ -283,11 +280,24 @@ impl WasmTransport {
|
||||||
.map_err(|e| js_error(format!("write failed: {:?}", e)))?;
|
.map_err(|e| js_error(format!("write failed: {:?}", e)))?;
|
||||||
if let Err(e) = JsFuture::from(write_promise.unchecked_into::<js_sys::Promise>()).await {
|
if let Err(e) = JsFuture::from(write_promise.unchecked_into::<js_sys::Promise>()).await {
|
||||||
log_stream_error_code(&e, "send_frame write");
|
log_stream_error_code(&e, "send_frame write");
|
||||||
self.outgoing_writer.borrow_mut().take();
|
|
||||||
release_writer_lock(&writer_val);
|
release_writer_lock(&writer_val);
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let close_fn = js_sys::Reflect::get(&writer_val, &JsValue::from_str("close"))
|
||||||
|
.map_err(|_| js_error("missing close"))?
|
||||||
|
.dyn_into::<js_sys::Function>()
|
||||||
|
.map_err(|_| js_error("close not a function"))?;
|
||||||
|
let close_promise = close_fn
|
||||||
|
.call0(&writer_val)
|
||||||
|
.map_err(|e| js_error(format!("close failed: {:?}", e)))?;
|
||||||
|
if let Err(e) = JsFuture::from(close_promise.unchecked_into::<js_sys::Promise>()).await {
|
||||||
|
// The frame was already written; do not retry it merely because
|
||||||
|
// FIN failed, as that would duplicate the MTP frame.
|
||||||
|
log_stream_error_code(&e, "send_frame close");
|
||||||
|
}
|
||||||
|
release_writer_lock(&writer_val);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -677,12 +687,6 @@ impl WasmTransport {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(&self) {
|
pub fn close(&self) {
|
||||||
if let Some(writer) = self.outgoing_writer.borrow_mut().take() {
|
|
||||||
// The WebTransport session close below terminates the stream. The
|
|
||||||
// lock must be released first so dropping it is not interpreted as
|
|
||||||
// an application abort.
|
|
||||||
release_writer_lock(&writer);
|
|
||||||
}
|
|
||||||
// Release reader locks before closing so they aren't treated as cancels.
|
// Release reader locks before closing so they aren't treated as cancels.
|
||||||
if let Some(reader) = self.stream_reader.borrow_mut().take() {
|
if let Some(reader) = self.stream_reader.borrow_mut().take() {
|
||||||
release_reader_lock(&reader);
|
release_reader_lock(&reader);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue