Skip to content

Commit 1fc3495

Browse files
committed
Rework fields, but make the borrow checker bigmad
1 parent cd8ede9 commit 1fc3495

5 files changed

Lines changed: 116 additions & 75 deletions

File tree

crates/wasi-http/src/body.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use crate::bindings::http::types::{self, Headers, HeadersRef, Method, Scheme};
1+
use crate::{
2+
bindings::http::types,
3+
types::FieldMap,
4+
};
25
use bytes::Bytes;
36
use std::{pin, task};
47
use tokio::sync::{mpsc, oneshot};
@@ -200,7 +203,7 @@ impl HostIncomingBody {
200203

201204
pub struct HostFutureTrailers {
202205
pub worker: AbortOnDropJoinHandle<()>,
203-
pub received: Option<Result<HeadersRef, types::Error>>,
206+
pub received: Option<Result<FieldMap, types::Error>>,
204207
pub receiver: oneshot::Receiver<Result<hyper::HeaderMap, hyper::Error>>,
205208
}
206209

@@ -221,7 +224,9 @@ impl HostFutureTrailers {
221224
}
222225

223226
match Pin::new(&mut self.0.receiver).poll(cx) {
224-
Poll::Ready(Ok(Ok(headers))) => self.0.received = Some(Ok(headers)),
227+
Poll::Ready(Ok(Ok(headers))) => {
228+
self.0.received = Some(Ok(FieldMap::from(headers)))
229+
}
225230

226231
Poll::Ready(Ok(Err(e))) => {
227232
self.0.received = Some(Err(types::Error::ProtocolError(format!(

crates/wasi-http/src/types.rs

Lines changed: 60 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
//! implementation of the wasi-http API.
33
44
use crate::{
5-
bindings::http::types::{Headers, IncomingBody, Method, Scheme, FutureTrailers},
6-
body::{HostIncomingBody, HostFutureTrailers},
5+
bindings::http::types::{FutureTrailers, Headers, IncomingBody, Method, Scheme},
6+
body::{HostFutureTrailers, HostIncomingBody},
77
};
8-
use std::collections::HashMap;
98
use std::pin::Pin;
109
use std::task;
11-
use wasmtime_wasi::preview2::{pipe::AsyncReadStream, AbortOnDropJoinHandle, Table, TableError};
10+
use std::{any::Any, collections::HashMap};
11+
use wasmtime_wasi::preview2::{pipe::AsyncReadStream, AbortOnDropJoinHandle, Table, TableError, OccupiedEntry};
1212

1313
const MAX_BUF_SIZE: usize = 65_536;
1414

@@ -20,14 +20,12 @@ pub trait WasiHttpView: Send {
2020
fn table(&mut self) -> &mut Table;
2121
}
2222

23-
pub type FieldsMap = HashMap<String, Vec<Vec<u8>>>;
24-
2523
pub struct HostOutgoingRequest {
2624
pub method: Method,
2725
pub scheme: Option<Scheme>,
2826
pub path_with_query: String,
2927
pub authority: String,
30-
pub headers: HostFields,
28+
pub headers: FieldMap,
3129
pub body: Option<AsyncReadStream>,
3230
}
3331

@@ -46,26 +44,15 @@ pub struct HostOutgoingRequest {
4644

4745
pub struct HostIncomingResponse {
4846
pub status: u16,
49-
pub headers: HeadersRef,
47+
pub headers: FieldMap,
5048
pub body: Option<hyper::body::Incoming>,
5149
pub worker: AbortOnDropJoinHandle<anyhow::Result<()>>,
5250
}
5351

54-
pub enum HeadersRef {
55-
Value(hyper::HeaderMap),
56-
Resource(Headers),
57-
}
58-
59-
#[derive(Clone, Debug)]
60-
pub struct HostFields(pub HashMap<String, Vec<Vec<u8>>>);
61-
62-
impl HostFields {
63-
pub fn new() -> Self {
64-
Self(FieldsMap::new())
65-
}
66-
}
52+
#[derive(Clone)]
53+
pub struct FieldMap(pub HashMap<String, Vec<Vec<u8>>>);
6754

68-
impl From<hyper::HeaderMap> for HostFields {
55+
impl From<hyper::HeaderMap> for FieldMap {
6956
fn from(headers: hyper::HeaderMap) -> Self {
7057
use std::collections::hash_map::Entry;
7158

@@ -85,6 +72,21 @@ impl From<hyper::HeaderMap> for HostFields {
8572
}
8673
}
8774

75+
pub enum HostFields {
76+
Ref {
77+
parent: u32,
78+
79+
// NOTE: there's not failure in the result here because we assume that HostFields will
80+
// always be registered as a child of the entry with the `parent` id. This ensures that the
81+
// entry will always exist while this `HostFields::Ref` entry exists in the table, thus we
82+
// don't need to account for failure when fetching the fields ref from the parent.
83+
get_fields: for<'a> fn(elem: &'a mut dyn Any) -> &'a mut FieldMap,
84+
},
85+
Owned {
86+
fields: FieldMap,
87+
},
88+
}
89+
8890
pub struct IncomingResponseInternal {
8991
pub resp: hyper::Response<hyper::body::Incoming>,
9092
pub worker: AbortOnDropJoinHandle<anyhow::Result<()>>,
@@ -153,8 +155,7 @@ pub trait TableHttpExt {
153155
fn delete_incoming_response(&mut self, id: u32) -> Result<HostIncomingResponse, TableError>;
154156

155157
fn push_fields(&mut self, fields: HostFields) -> Result<u32, TableError>;
156-
fn get_fields(&self, id: u32) -> Result<&HostFields, TableError>;
157-
fn get_fields_mut(&mut self, id: u32) -> Result<&mut HostFields, TableError>;
158+
fn get_fields(&mut self, id: u32) -> Result<&mut FieldMap, TableError>;
158159
fn delete_fields(&mut self, id: u32) -> Result<HostFields, TableError>;
159160

160161
fn push_future_incoming_response(
@@ -178,9 +179,18 @@ pub trait TableHttpExt {
178179
fn get_incoming_body(&mut self, id: IncomingBody) -> Result<&mut HostIncomingBody, TableError>;
179180
fn delete_incoming_body(&mut self, id: IncomingBody) -> Result<HostIncomingBody, TableError>;
180181

181-
fn push_future_trailers(&mut self, trailers: HostFutureTrailers) -> Result<FutureTrailers, TableError>;
182-
fn get_future_trailers(&mut self, id: FutureTrailers) -> Result<&mut HostFutureTrailers, TableError>;
183-
fn delete_future_trailers(&mut self, id: FutureTrailers) -> Result<HostFutureTrailers, TableError>;
182+
fn push_future_trailers(
183+
&mut self,
184+
trailers: HostFutureTrailers,
185+
) -> Result<FutureTrailers, TableError>;
186+
fn get_future_trailers(
187+
&mut self,
188+
id: FutureTrailers,
189+
) -> Result<&mut HostFutureTrailers, TableError>;
190+
fn delete_future_trailers(
191+
&mut self,
192+
id: FutureTrailers,
193+
) -> Result<HostFutureTrailers, TableError>;
184194
}
185195

186196
#[async_trait::async_trait]
@@ -223,13 +233,18 @@ impl TableHttpExt for Table {
223233
}
224234

225235
fn push_fields(&mut self, fields: HostFields) -> Result<u32, TableError> {
226-
self.push(Box::new(fields))
227-
}
228-
fn get_fields(&self, id: u32) -> Result<&HostFields, TableError> {
229-
self.get::<HostFields>(id)
236+
match fields {
237+
HostFields::Ref { parent, .. } => self.push_child(Box::new(fields), parent),
238+
HostFields::Owned { .. } => self.push(Box::new(fields)),
239+
}
230240
}
231-
fn get_fields_mut(&mut self, id: u32) -> Result<&mut HostFields, TableError> {
232-
self.get_mut::<HostFields>(id)
241+
fn get_fields<'a>(&'a mut self, id: u32) -> Result<&'a mut FieldMap, TableError> {
242+
let (parent, get_fields) = match self.get_mut::<HostFields>(id)? {
243+
HostFields::Ref { parent, get_fields } => (*parent, *get_fields),
244+
HostFields::Owned { fields } => return Ok(fields),
245+
};
246+
let mut entry: OccupiedEntry<'a> = self.entry(parent).unwrap();
247+
Ok(get_fields(entry.get_mut()))
233248
}
234249
fn delete_fields(&mut self, id: u32) -> Result<HostFields, TableError> {
235250
let fields = self.delete::<HostFields>(id)?;
@@ -273,15 +288,24 @@ impl TableHttpExt for Table {
273288
self.delete(id)
274289
}
275290

276-
fn push_future_trailers(&mut self, trailers: HostFutureTrailers) -> Result<FutureTrailers, TableError> {
291+
fn push_future_trailers(
292+
&mut self,
293+
trailers: HostFutureTrailers,
294+
) -> Result<FutureTrailers, TableError> {
277295
self.push(Box::new(trailers))
278296
}
279297

280-
fn get_future_trailers(&mut self, id: FutureTrailers) -> Result<&mut HostFutureTrailers, TableError> {
298+
fn get_future_trailers(
299+
&mut self,
300+
id: FutureTrailers,
301+
) -> Result<&mut HostFutureTrailers, TableError> {
281302
self.get_mut(id)
282303
}
283304

284-
fn delete_future_trailers(&mut self, id: FutureTrailers) -> Result<HostFutureTrailers, TableError> {
305+
fn delete_future_trailers(
306+
&mut self,
307+
id: FutureTrailers,
308+
) -> Result<HostFutureTrailers, TableError> {
285309
self.delete(id)
286310
}
287311
}

crates/wasi-http/src/types_impl.rs

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ use crate::bindings::http::types::{
66
Scheme, StatusCode, Trailers,
77
};
88
use crate::body::HostFutureTrailers;
9+
use crate::types::FieldMap;
910
use crate::WasiHttpView;
1011
use crate::{
1112
body::HostIncomingBody,
1213
types::{
13-
HeadersRef, HostFields, HostFutureIncomingResponse, HostIncomingResponse,
14-
HostOutgoingRequest, TableHttpExt,
14+
HostFields, HostFutureIncomingResponse, HostIncomingResponse, HostOutgoingRequest,
15+
TableHttpExt,
1516
},
1617
};
1718
use anyhow::{anyhow, bail, Context};
@@ -31,14 +32,24 @@ impl<T: WasiHttpView> crate::bindings::http::types::Host for T {
3132
Ok(())
3233
}
3334
async fn new_fields(&mut self, entries: Vec<(String, Vec<u8>)>) -> wasmtime::Result<Fields> {
34-
let mut map = HostFields::new();
35+
use std::collections::{hash_map::Entry, HashMap};
36+
37+
let mut map: HashMap<String, Vec<Vec<u8>>> = HashMap::new();
38+
3539
for (key, value) in entries {
36-
map.0.insert(key, vec![value.clone()]);
40+
match map.entry(key) {
41+
Entry::Occupied(mut entry) => entry.get_mut().push(value),
42+
Entry::Vacant(entry) => {
43+
entry.insert(vec![value]);
44+
}
45+
}
3746
}
3847

3948
let id = self
4049
.table()
41-
.push_fields(map)
50+
.push_fields(HostFields::Owned {
51+
fields: FieldMap(map),
52+
})
4253
.context("[new_fields] pushing fields")?;
4354
Ok(id)
4455
}
@@ -59,12 +70,12 @@ impl<T: WasiHttpView> crate::bindings::http::types::Host for T {
5970
name: String,
6071
value: Vec<Vec<u8>>,
6172
) -> wasmtime::Result<()> {
62-
let m = self.table().get_fields_mut(fields)?;
73+
let m = self.table().get_fields(fields)?;
6374
m.0.insert(name, value.clone());
6475
Ok(())
6576
}
6677
async fn fields_delete(&mut self, fields: Fields, name: String) -> wasmtime::Result<()> {
67-
let m = self.table().get_fields_mut(fields)?;
78+
let m = self.table().get_fields(fields)?;
6879
m.0.remove(&name);
6980
Ok(())
7081
}
@@ -76,7 +87,7 @@ impl<T: WasiHttpView> crate::bindings::http::types::Host for T {
7687
) -> wasmtime::Result<()> {
7788
let m = self
7889
.table()
79-
.get_fields_mut(fields)
90+
.get_fields(fields)
8091
.context("[fields_append] getting mutable fields")?;
8192
match m.0.get_mut(&name) {
8293
Some(v) => v.push(value),
@@ -99,12 +110,14 @@ impl<T: WasiHttpView> crate::bindings::http::types::Host for T {
99110
Ok(result)
100111
}
101112
async fn fields_clone(&mut self, fields: Fields) -> wasmtime::Result<Fields> {
102-
let table = self.table();
103-
let m = table
113+
let fields = self
114+
.table()
104115
.get_fields(fields)
105-
.context("[fields_clone] getting fields")?;
106-
let id = table
107-
.push_fields(m.clone())
116+
.context("[fields_clone] getting fields")?
117+
.clone();
118+
let id = self
119+
.table()
120+
.push_fields(HostFields::Owned { fields })
108121
.context("[fields_clone] pushing fields")?;
109122
Ok(id)
110123
}
@@ -159,8 +172,8 @@ impl<T: WasiHttpView> crate::bindings::http::types::Host for T {
159172
authority: Option<String>,
160173
headers: Headers,
161174
) -> wasmtime::Result<OutgoingRequest> {
162-
// We're taking ownership of the header data, so remove it from the table.
163-
let headers = self.table().delete_fields(headers)?;
175+
let headers = self.table().get_fields(headers)?.clone();
176+
164177
let req = HostOutgoingRequest {
165178
path_with_query: path_with_query.unwrap_or("".to_string()),
166179
authority: authority.unwrap_or("".to_string()),
@@ -237,18 +250,19 @@ impl<T: WasiHttpView> crate::bindings::http::types::Host for T {
237250
&mut self,
238251
response: IncomingResponse,
239252
) -> wasmtime::Result<Headers> {
240-
let r = self
253+
let _ = self
241254
.table()
242255
.get_incoming_response_mut(response)
243256
.context("[incoming_response_headers] getting response")?;
244257

245-
let hdrs = match r.headers {
246-
HeadersRef::Value(ref mut hdrs) => std::mem::take(hdrs),
247-
HeadersRef::Resource(id) => return Ok(id),
248-
};
258+
fn get_fields(elem: &mut dyn Any) -> &mut FieldMap {
259+
&mut elem.downcast_mut::<HostIncomingResponse>().unwrap().headers
260+
}
249261

250-
let id = self.table().push_fields(HostFields::from(hdrs))?;
251-
self.table().get_incoming_response_mut(response)?.headers = HeadersRef::Resource(id);
262+
let id = self.table().push_fields(HostFields::Ref {
263+
parent: response,
264+
get_fields,
265+
})?;
252266
Ok(id)
253267
}
254268
async fn incoming_response_consume(
@@ -311,20 +325,18 @@ impl<T: WasiHttpView> crate::bindings::http::types::Host for T {
311325
return Ok(Some(Err(e.clone())));
312326
}
313327

314-
let hdrs = match res.unwrap() {
315-
HeadersRef::Resource(id) => return Ok(Some(Ok(id))),
316-
HeadersRef::Value(ref mut hdrs) => std::mem::take(hdrs),
317-
};
318-
319328
drop(res);
320329
drop(trailers);
321330

322-
let hdrs = self.table().push_fields(HostFields::from(hdrs))?;
331+
fn get_fields(elem: &mut dyn Any) -> &mut FieldMap {
332+
let trailers = elem.downcast_mut::<HostFutureTrailers>().unwrap();
333+
trailers.received.as_mut().unwrap().as_mut().unwrap()
334+
}
323335

324-
self.table()
325-
.get_future_trailers(id)?
326-
.received
327-
.replace(Ok(HeadersRef::Resource(hdrs)));
336+
let hdrs = self.table().push_fields(HostFields::Ref {
337+
parent: id,
338+
get_fields,
339+
})?;
328340

329341
Ok(Some(Ok(hdrs)))
330342
}
@@ -373,7 +385,7 @@ impl<T: WasiHttpView> crate::bindings::http::types::Host for T {
373385

374386
let resp = self.table().push_incoming_response(HostIncomingResponse {
375387
status: parts.status.as_u16(),
376-
headers: HeadersRef::Value(parts.headers),
388+
headers: FieldMap::from(parts.headers),
377389
body: Some(body),
378390
worker: resp.worker,
379391
})?;

crates/wasi-http/wit/deps/http/types.wit

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ interface types {
9090
path-with-query: option<string>,
9191
scheme: option<scheme>,
9292
authority: option<string>,
93-
headers: /* own */ headers
93+
headers: /* borrow */ headers
9494
) -> outgoing-request
9595

9696
// Will return the outgoing-body child at most once. If called more than
@@ -169,7 +169,7 @@ interface types {
169169
drop-outgoing-response: func(response: /* own */ outgoing-response)
170170
new-outgoing-response: func(
171171
status-code: status-code,
172-
headers: /* own */ headers
172+
headers: /* borrow */ headers
173173
) -> outgoing-response
174174

175175
/// Will give the child outgoing-response at most once. subsequent calls will

crates/wasi/src/preview2/table.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ impl Table {
203203
/// remove or replace the entry based on its contents. The methods available are a subset of
204204
/// [`std::collections::hash_map::OccupiedEntry`] - it does not give access to the key, it
205205
/// restricts replacing the entry to items of the same type, and it does not allow for deletion.
206-
pub fn entry(&mut self, index: u32) -> Result<OccupiedEntry, TableError> {
206+
pub fn entry<'a>(&'a mut self, index: u32) -> Result<OccupiedEntry<'a>, TableError> {
207207
if self.map.contains_key(&index) {
208208
Ok(OccupiedEntry { table: self, index })
209209
} else {

0 commit comments

Comments
 (0)