1 //! Implementation for the `wasi:http/types` interface. 2 3 use crate::FieldMap; 4 use crate::get_content_length; 5 use crate::p2::bindings::http::types::{self, Method, Scheme, StatusCode, Trailers}; 6 use crate::p2::body::{HostFutureTrailers, HostIncomingBody, HostOutgoingBody, StreamContext}; 7 use crate::p2::types::{ 8 HostFutureIncomingResponse, HostIncomingRequest, HostIncomingResponse, HostOutgoingRequest, 9 HostOutgoingResponse, HostResponseOutparam, remove_forbidden_headers, 10 }; 11 use crate::p2::{HeaderError, HeaderResult, HttpError, HttpResult, WasiHttpCtxView}; 12 use http::{HeaderName, HeaderValue}; 13 use std::str::FromStr; 14 use wasmtime::component::Resource; 15 use wasmtime::{error::Context as _, format_err}; 16 use wasmtime_wasi::p2::{DynInputStream, DynOutputStream, DynPollable}; 17 18 impl types::Host for WasiHttpCtxView<'_> { convert_error_code(&mut self, err: HttpError) -> wasmtime::Result<types::ErrorCode>19 fn convert_error_code(&mut self, err: HttpError) -> wasmtime::Result<types::ErrorCode> { 20 err.downcast() 21 } 22 convert_header_error(&mut self, err: HeaderError) -> wasmtime::Result<types::HeaderError>23 fn convert_header_error(&mut self, err: HeaderError) -> wasmtime::Result<types::HeaderError> { 24 err.downcast() 25 } 26 http_error_code( &mut self, err: wasmtime::component::Resource<types::IoError>, ) -> wasmtime::Result<Option<types::ErrorCode>>27 fn http_error_code( 28 &mut self, 29 err: wasmtime::component::Resource<types::IoError>, 30 ) -> wasmtime::Result<Option<types::ErrorCode>> { 31 let e = self.table.get(&err)?; 32 Ok(e.downcast_ref::<types::ErrorCode>().cloned()) 33 } 34 } 35 36 impl types::HostFields for WasiHttpCtxView<'_> { new(&mut self) -> wasmtime::Result<Resource<FieldMap>>37 fn new(&mut self) -> wasmtime::Result<Resource<FieldMap>> { 38 let limit = self.ctx.field_size_limit; 39 let id = self 40 .table 41 .push(FieldMap::new_mutable(limit)) 42 .context("[new_fields] pushing fields")?; 43 44 Ok(id) 45 } 46 from_list(&mut self, entries: Vec<(String, Vec<u8>)>) -> HeaderResult<Resource<FieldMap>>47 fn from_list(&mut self, entries: Vec<(String, Vec<u8>)>) -> HeaderResult<Resource<FieldMap>> { 48 let mut fields = FieldMap::new_mutable(self.ctx.field_size_limit); 49 50 for (header, value) in entries { 51 let header = HeaderName::from_bytes(header.as_bytes())?; 52 if self.hooks.is_forbidden_header(&header) { 53 return Err(types::HeaderError::Forbidden.into()); 54 } 55 let value = HeaderValue::from_bytes(&value)?; 56 fields.append(header, value)?; 57 } 58 59 Ok(self.table.push(fields)?) 60 } 61 drop(&mut self, fields: Resource<FieldMap>) -> wasmtime::Result<()>62 fn drop(&mut self, fields: Resource<FieldMap>) -> wasmtime::Result<()> { 63 self.table 64 .delete(fields) 65 .context("[drop_fields] deleting fields")?; 66 Ok(()) 67 } 68 get(&mut self, fields: Resource<FieldMap>, name: String) -> wasmtime::Result<Vec<Vec<u8>>>69 fn get(&mut self, fields: Resource<FieldMap>, name: String) -> wasmtime::Result<Vec<Vec<u8>>> { 70 let fields = self.table.get(&fields)?; 71 72 let header = match HeaderName::from_bytes(name.as_bytes()) { 73 Ok(header) => header, 74 Err(_) => return Ok(vec![]), 75 }; 76 77 if !fields.contains_key(&header) { 78 return Ok(vec![]); 79 } 80 81 let res = fields 82 .get_all(&header) 83 .into_iter() 84 .map(|val| val.as_bytes().to_owned()) 85 .collect(); 86 Ok(res) 87 } 88 has(&mut self, fields: Resource<FieldMap>, name: String) -> wasmtime::Result<bool>89 fn has(&mut self, fields: Resource<FieldMap>, name: String) -> wasmtime::Result<bool> { 90 let fields = self.table.get(&fields)?; 91 92 match HeaderName::from_bytes(name.as_bytes()) { 93 Ok(header) => Ok(fields.contains_key(&header)), 94 Err(_) => Ok(false), 95 } 96 } 97 set( &mut self, fields: Resource<FieldMap>, name: String, byte_values: Vec<Vec<u8>>, ) -> HeaderResult<()>98 fn set( 99 &mut self, 100 fields: Resource<FieldMap>, 101 name: String, 102 byte_values: Vec<Vec<u8>>, 103 ) -> HeaderResult<()> { 104 let header = HeaderName::from_bytes(name.as_bytes())?; 105 106 if self.hooks.is_forbidden_header(&header) { 107 return Err(types::HeaderError::Forbidden.into()); 108 } 109 110 let mut values = Vec::with_capacity(byte_values.len()); 111 for value in byte_values { 112 values.push(HeaderValue::from_bytes(&value)?); 113 } 114 115 let fields = self.table.get_mut(&fields)?; 116 fields.set(header, values)?; 117 Ok(()) 118 } 119 delete(&mut self, fields: Resource<FieldMap>, name: String) -> HeaderResult<()>120 fn delete(&mut self, fields: Resource<FieldMap>, name: String) -> HeaderResult<()> { 121 let header = HeaderName::from_bytes(name.as_bytes())?; 122 123 if self.hooks.is_forbidden_header(&header) { 124 return Err(types::HeaderError::Forbidden.into()); 125 } 126 127 let fields = self.table.get_mut(&fields)?; 128 fields.remove_all(header)?; 129 Ok(()) 130 } 131 append( &mut self, fields: Resource<FieldMap>, name: String, value: Vec<u8>, ) -> HeaderResult<()>132 fn append( 133 &mut self, 134 fields: Resource<FieldMap>, 135 name: String, 136 value: Vec<u8>, 137 ) -> HeaderResult<()> { 138 let header = HeaderName::from_bytes(name.as_bytes())?; 139 140 if self.hooks.is_forbidden_header(&header) { 141 return Err(types::HeaderError::Forbidden.into()); 142 } 143 144 let value = HeaderValue::from_bytes(&value)?; 145 146 let fields = self.table.get_mut(&fields)?; 147 fields.append(header, value)?; 148 Ok(()) 149 } 150 entries(&mut self, fields: Resource<FieldMap>) -> wasmtime::Result<Vec<(String, Vec<u8>)>>151 fn entries(&mut self, fields: Resource<FieldMap>) -> wasmtime::Result<Vec<(String, Vec<u8>)>> { 152 Ok(self 153 .table 154 .get(&fields)? 155 .iter() 156 .map(|(name, value)| (name.as_str().to_owned(), value.as_bytes().to_owned())) 157 .collect()) 158 } 159 clone(&mut self, fields: Resource<FieldMap>) -> wasmtime::Result<Resource<FieldMap>>160 fn clone(&mut self, fields: Resource<FieldMap>) -> wasmtime::Result<Resource<FieldMap>> { 161 let mut fields = self.table.get(&fields)?.clone(); 162 fields.set_mutable(self.ctx.field_size_limit); 163 let id = self.table.push(fields)?; 164 Ok(id) 165 } 166 } 167 168 impl types::HostIncomingRequest for WasiHttpCtxView<'_> { method(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Method>169 fn method(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Method> { 170 let method = self.table.get(&id)?.method.clone(); 171 Ok(method.into()) 172 } path_with_query( &mut self, id: Resource<HostIncomingRequest>, ) -> wasmtime::Result<Option<String>>173 fn path_with_query( 174 &mut self, 175 id: Resource<HostIncomingRequest>, 176 ) -> wasmtime::Result<Option<String>> { 177 let req = self.table.get(&id)?; 178 Ok(req 179 .uri 180 .path_and_query() 181 .map(|path_and_query| path_and_query.as_str().to_owned())) 182 } scheme(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Option<Scheme>>183 fn scheme(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Option<Scheme>> { 184 let req = self.table.get(&id)?; 185 Ok(Some(req.scheme.clone())) 186 } authority(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Option<String>>187 fn authority(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<Option<String>> { 188 let req = self.table.get(&id)?; 189 Ok(Some(req.authority.clone())) 190 } 191 headers( &mut self, id: Resource<HostIncomingRequest>, ) -> wasmtime::Result<Resource<FieldMap>>192 fn headers( 193 &mut self, 194 id: Resource<HostIncomingRequest>, 195 ) -> wasmtime::Result<Resource<FieldMap>> { 196 let req = self.table.get(&id)?; 197 Ok(self.table.push(req.headers.clone())?) 198 } 199 consume( &mut self, id: Resource<HostIncomingRequest>, ) -> wasmtime::Result<Result<Resource<HostIncomingBody>, ()>>200 fn consume( 201 &mut self, 202 id: Resource<HostIncomingRequest>, 203 ) -> wasmtime::Result<Result<Resource<HostIncomingBody>, ()>> { 204 let req = self.table.get_mut(&id)?; 205 match req.body.take() { 206 Some(body) => { 207 let id = self.table.push(body)?; 208 Ok(Ok(id)) 209 } 210 211 None => Ok(Err(())), 212 } 213 } 214 drop(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<()>215 fn drop(&mut self, id: Resource<HostIncomingRequest>) -> wasmtime::Result<()> { 216 let _ = self.table.delete(id)?; 217 Ok(()) 218 } 219 } 220 221 impl types::HostOutgoingRequest for WasiHttpCtxView<'_> { new( &mut self, headers: Resource<FieldMap>, ) -> wasmtime::Result<Resource<HostOutgoingRequest>>222 fn new( 223 &mut self, 224 headers: Resource<FieldMap>, 225 ) -> wasmtime::Result<Resource<HostOutgoingRequest>> { 226 let mut headers = self.table.delete(headers)?; 227 headers.set_immutable(); 228 229 self.table 230 .push(HostOutgoingRequest { 231 path_with_query: None, 232 authority: None, 233 method: types::Method::Get, 234 headers, 235 scheme: None, 236 body: None, 237 }) 238 .context("[new_outgoing_request] pushing request") 239 } 240 body( &mut self, request: Resource<HostOutgoingRequest>, ) -> wasmtime::Result<Result<Resource<HostOutgoingBody>, ()>>241 fn body( 242 &mut self, 243 request: Resource<HostOutgoingRequest>, 244 ) -> wasmtime::Result<Result<Resource<HostOutgoingBody>, ()>> { 245 let buffer_chunks = self.hooks.outgoing_body_buffer_chunks(); 246 let chunk_size = self.hooks.outgoing_body_chunk_size(); 247 let req = self 248 .table 249 .get_mut(&request) 250 .context("[outgoing_request_write] getting request")?; 251 252 if req.body.is_some() { 253 return Ok(Err(())); 254 } 255 256 let size = match get_content_length(&req.headers) { 257 Ok(size) => size, 258 Err(..) => return Ok(Err(())), 259 }; 260 261 let (host_body, hyper_body) = 262 HostOutgoingBody::new(StreamContext::Request, size, buffer_chunks, chunk_size); 263 264 req.body = Some(hyper_body); 265 266 // The output stream will necessarily outlive the request, because we could be still 267 // writing to the stream after `outgoing-handler.handle` is called. 268 let outgoing_body = self.table.push(host_body)?; 269 270 Ok(Ok(outgoing_body)) 271 } 272 drop(&mut self, request: Resource<HostOutgoingRequest>) -> wasmtime::Result<()>273 fn drop(&mut self, request: Resource<HostOutgoingRequest>) -> wasmtime::Result<()> { 274 let _ = self.table.delete(request)?; 275 Ok(()) 276 } 277 method( &mut self, request: wasmtime::component::Resource<types::OutgoingRequest>, ) -> wasmtime::Result<Method>278 fn method( 279 &mut self, 280 request: wasmtime::component::Resource<types::OutgoingRequest>, 281 ) -> wasmtime::Result<Method> { 282 Ok(self.table.get(&request)?.method.clone()) 283 } 284 set_method( &mut self, request: wasmtime::component::Resource<types::OutgoingRequest>, method: Method, ) -> wasmtime::Result<Result<(), ()>>285 fn set_method( 286 &mut self, 287 request: wasmtime::component::Resource<types::OutgoingRequest>, 288 method: Method, 289 ) -> wasmtime::Result<Result<(), ()>> { 290 let req = self.table.get_mut(&request)?; 291 292 if let Method::Other(s) = &method { 293 if let Err(_) = http::Method::from_str(s) { 294 return Ok(Err(())); 295 } 296 } 297 298 req.method = method; 299 300 Ok(Ok(())) 301 } 302 path_with_query( &mut self, request: wasmtime::component::Resource<types::OutgoingRequest>, ) -> wasmtime::Result<Option<String>>303 fn path_with_query( 304 &mut self, 305 request: wasmtime::component::Resource<types::OutgoingRequest>, 306 ) -> wasmtime::Result<Option<String>> { 307 Ok(self.table.get(&request)?.path_with_query.clone()) 308 } 309 set_path_with_query( &mut self, request: wasmtime::component::Resource<types::OutgoingRequest>, path_with_query: Option<String>, ) -> wasmtime::Result<Result<(), ()>>310 fn set_path_with_query( 311 &mut self, 312 request: wasmtime::component::Resource<types::OutgoingRequest>, 313 path_with_query: Option<String>, 314 ) -> wasmtime::Result<Result<(), ()>> { 315 let req = self.table.get_mut(&request)?; 316 317 if let Some(s) = path_with_query.as_ref() { 318 if let Err(_) = http::uri::PathAndQuery::from_str(s) { 319 return Ok(Err(())); 320 } 321 } 322 323 req.path_with_query = path_with_query; 324 325 Ok(Ok(())) 326 } 327 scheme( &mut self, request: wasmtime::component::Resource<types::OutgoingRequest>, ) -> wasmtime::Result<Option<Scheme>>328 fn scheme( 329 &mut self, 330 request: wasmtime::component::Resource<types::OutgoingRequest>, 331 ) -> wasmtime::Result<Option<Scheme>> { 332 Ok(self.table.get(&request)?.scheme.clone()) 333 } 334 set_scheme( &mut self, request: wasmtime::component::Resource<types::OutgoingRequest>, scheme: Option<Scheme>, ) -> wasmtime::Result<Result<(), ()>>335 fn set_scheme( 336 &mut self, 337 request: wasmtime::component::Resource<types::OutgoingRequest>, 338 scheme: Option<Scheme>, 339 ) -> wasmtime::Result<Result<(), ()>> { 340 let req = self.table.get_mut(&request)?; 341 342 if let Some(types::Scheme::Other(s)) = scheme.as_ref() { 343 if let Err(_) = http::uri::Scheme::from_str(s.as_str()) { 344 return Ok(Err(())); 345 } 346 } 347 348 req.scheme = scheme; 349 350 Ok(Ok(())) 351 } 352 authority( &mut self, request: wasmtime::component::Resource<types::OutgoingRequest>, ) -> wasmtime::Result<Option<String>>353 fn authority( 354 &mut self, 355 request: wasmtime::component::Resource<types::OutgoingRequest>, 356 ) -> wasmtime::Result<Option<String>> { 357 Ok(self.table.get(&request)?.authority.clone()) 358 } 359 set_authority( &mut self, request: wasmtime::component::Resource<types::OutgoingRequest>, authority: Option<String>, ) -> wasmtime::Result<Result<(), ()>>360 fn set_authority( 361 &mut self, 362 request: wasmtime::component::Resource<types::OutgoingRequest>, 363 authority: Option<String>, 364 ) -> wasmtime::Result<Result<(), ()>> { 365 let req = self.table.get_mut(&request)?; 366 367 if let Some(s) = authority.as_ref() { 368 if let Err(_) = http::uri::Authority::from_str(s.as_str()) { 369 return Ok(Err(())); 370 } 371 } 372 373 req.authority = authority; 374 375 Ok(Ok(())) 376 } 377 headers( &mut self, request: wasmtime::component::Resource<types::OutgoingRequest>, ) -> wasmtime::Result<wasmtime::component::Resource<FieldMap>>378 fn headers( 379 &mut self, 380 request: wasmtime::component::Resource<types::OutgoingRequest>, 381 ) -> wasmtime::Result<wasmtime::component::Resource<FieldMap>> { 382 let req = self.table.get(&request)?; 383 let id = self.table.push(req.headers.clone())?; 384 Ok(id) 385 } 386 } 387 388 impl types::HostResponseOutparam for WasiHttpCtxView<'_> { drop(&mut self, id: Resource<HostResponseOutparam>) -> wasmtime::Result<()>389 fn drop(&mut self, id: Resource<HostResponseOutparam>) -> wasmtime::Result<()> { 390 let _ = self.table.delete(id)?; 391 Ok(()) 392 } set( &mut self, id: Resource<HostResponseOutparam>, resp: Result<Resource<HostOutgoingResponse>, types::ErrorCode>, ) -> wasmtime::Result<()>393 fn set( 394 &mut self, 395 id: Resource<HostResponseOutparam>, 396 resp: Result<Resource<HostOutgoingResponse>, types::ErrorCode>, 397 ) -> wasmtime::Result<()> { 398 let val = match resp { 399 Ok(resp) => Ok(self.table.delete(resp)?.try_into()?), 400 Err(e) => Err(e), 401 }; 402 403 let resp = self.table.delete(id)?; 404 // Giving the API doesn't return any error, it's probably 405 // better to ignore the error than trap the guest, in case of 406 // host timeout and dropped the receiver side of the channel. 407 // See also: #10784 408 let _ = resp.result.send(val); 409 Ok(()) 410 } 411 send_informational( &mut self, _id: Resource<HostResponseOutparam>, _status: u16, _headers: Resource<FieldMap>, ) -> HttpResult<()>412 fn send_informational( 413 &mut self, 414 _id: Resource<HostResponseOutparam>, 415 _status: u16, 416 _headers: Resource<FieldMap>, 417 ) -> HttpResult<()> { 418 Err(HttpError::trap(format_err!("not implemented"))) 419 } 420 } 421 422 impl types::HostIncomingResponse for WasiHttpCtxView<'_> { drop(&mut self, response: Resource<HostIncomingResponse>) -> wasmtime::Result<()>423 fn drop(&mut self, response: Resource<HostIncomingResponse>) -> wasmtime::Result<()> { 424 let _ = self 425 .table 426 .delete(response) 427 .context("[drop_incoming_response] deleting response")?; 428 Ok(()) 429 } 430 status(&mut self, response: Resource<HostIncomingResponse>) -> wasmtime::Result<StatusCode>431 fn status(&mut self, response: Resource<HostIncomingResponse>) -> wasmtime::Result<StatusCode> { 432 let r = self 433 .table 434 .get(&response) 435 .context("[incoming_response_status] getting response")?; 436 Ok(r.status) 437 } 438 headers( &mut self, response: Resource<HostIncomingResponse>, ) -> wasmtime::Result<Resource<FieldMap>>439 fn headers( 440 &mut self, 441 response: Resource<HostIncomingResponse>, 442 ) -> wasmtime::Result<Resource<FieldMap>> { 443 let resp = self.table.get(&response)?; 444 let id = self.table.push(resp.headers.clone())?; 445 Ok(id) 446 } 447 consume( &mut self, response: Resource<HostIncomingResponse>, ) -> wasmtime::Result<Result<Resource<HostIncomingBody>, ()>>448 fn consume( 449 &mut self, 450 response: Resource<HostIncomingResponse>, 451 ) -> wasmtime::Result<Result<Resource<HostIncomingBody>, ()>> { 452 let r = self 453 .table 454 .get_mut(&response) 455 .context("[incoming_response_consume] getting response")?; 456 457 match r.body.take() { 458 Some(body) => { 459 let id = self.table.push(body)?; 460 Ok(Ok(id)) 461 } 462 463 None => Ok(Err(())), 464 } 465 } 466 } 467 468 impl types::HostFutureTrailers for WasiHttpCtxView<'_> { drop(&mut self, id: Resource<HostFutureTrailers>) -> wasmtime::Result<()>469 fn drop(&mut self, id: Resource<HostFutureTrailers>) -> wasmtime::Result<()> { 470 let _ = self 471 .table 472 .delete(id) 473 .context("[drop future-trailers] deleting future-trailers")?; 474 Ok(()) 475 } 476 subscribe( &mut self, index: Resource<HostFutureTrailers>, ) -> wasmtime::Result<Resource<DynPollable>>477 fn subscribe( 478 &mut self, 479 index: Resource<HostFutureTrailers>, 480 ) -> wasmtime::Result<Resource<DynPollable>> { 481 wasmtime_wasi::p2::subscribe(self.table, index) 482 } 483 get( &mut self, id: Resource<HostFutureTrailers>, ) -> wasmtime::Result<Option<Result<Result<Option<Resource<Trailers>>, types::ErrorCode>, ()>>>484 fn get( 485 &mut self, 486 id: Resource<HostFutureTrailers>, 487 ) -> wasmtime::Result<Option<Result<Result<Option<Resource<Trailers>>, types::ErrorCode>, ()>>> 488 { 489 let trailers = self.table.get_mut(&id)?; 490 match trailers { 491 HostFutureTrailers::Waiting { .. } => return Ok(None), 492 HostFutureTrailers::Consumed => return Ok(Some(Err(()))), 493 HostFutureTrailers::Done(_) => {} 494 }; 495 496 let res = match std::mem::replace(trailers, HostFutureTrailers::Consumed) { 497 HostFutureTrailers::Done(res) => res, 498 _ => unreachable!(), 499 }; 500 501 let mut fields = match res { 502 Ok(Some(fields)) => fields, 503 Ok(None) => return Ok(Some(Ok(Ok(None)))), 504 Err(e) => return Ok(Some(Ok(Err(e)))), 505 }; 506 507 remove_forbidden_headers(self.hooks, &mut fields); 508 509 let ts = self.table.push(FieldMap::new_immutable(fields))?; 510 511 Ok(Some(Ok(Ok(Some(ts))))) 512 } 513 } 514 515 impl types::HostIncomingBody for WasiHttpCtxView<'_> { stream( &mut self, id: Resource<HostIncomingBody>, ) -> wasmtime::Result<Result<Resource<DynInputStream>, ()>>516 fn stream( 517 &mut self, 518 id: Resource<HostIncomingBody>, 519 ) -> wasmtime::Result<Result<Resource<DynInputStream>, ()>> { 520 let body = self.table.get_mut(&id)?; 521 522 if let Some(stream) = body.take_stream() { 523 let stream: DynInputStream = Box::new(stream); 524 let stream = self.table.push_child(stream, &id)?; 525 return Ok(Ok(stream)); 526 } 527 528 Ok(Err(())) 529 } 530 finish( &mut self, id: Resource<HostIncomingBody>, ) -> wasmtime::Result<Resource<HostFutureTrailers>>531 fn finish( 532 &mut self, 533 id: Resource<HostIncomingBody>, 534 ) -> wasmtime::Result<Resource<HostFutureTrailers>> { 535 let body = self.table.delete(id)?; 536 let trailers = self.table.push(body.into_future_trailers())?; 537 Ok(trailers) 538 } 539 drop(&mut self, id: Resource<HostIncomingBody>) -> wasmtime::Result<()>540 fn drop(&mut self, id: Resource<HostIncomingBody>) -> wasmtime::Result<()> { 541 let _ = self.table.delete(id)?; 542 Ok(()) 543 } 544 } 545 546 impl types::HostOutgoingResponse for WasiHttpCtxView<'_> { new( &mut self, headers: Resource<FieldMap>, ) -> wasmtime::Result<Resource<HostOutgoingResponse>>547 fn new( 548 &mut self, 549 headers: Resource<FieldMap>, 550 ) -> wasmtime::Result<Resource<HostOutgoingResponse>> { 551 let mut fields = self.table.delete(headers)?; 552 fields.set_immutable(); 553 554 let id = self.table.push(HostOutgoingResponse { 555 status: http::StatusCode::OK, 556 headers: fields, 557 body: None, 558 })?; 559 560 Ok(id) 561 } 562 body( &mut self, id: Resource<HostOutgoingResponse>, ) -> wasmtime::Result<Result<Resource<HostOutgoingBody>, ()>>563 fn body( 564 &mut self, 565 id: Resource<HostOutgoingResponse>, 566 ) -> wasmtime::Result<Result<Resource<HostOutgoingBody>, ()>> { 567 let buffer_chunks = self.hooks.outgoing_body_buffer_chunks(); 568 let chunk_size = self.hooks.outgoing_body_chunk_size(); 569 let resp = self.table.get_mut(&id)?; 570 571 if resp.body.is_some() { 572 return Ok(Err(())); 573 } 574 575 let size = match get_content_length(&resp.headers) { 576 Ok(size) => size, 577 Err(..) => return Ok(Err(())), 578 }; 579 580 let (host, body) = 581 HostOutgoingBody::new(StreamContext::Response, size, buffer_chunks, chunk_size); 582 583 resp.body.replace(body); 584 585 let id = self.table.push(host)?; 586 587 Ok(Ok(id)) 588 } 589 status_code( &mut self, id: Resource<HostOutgoingResponse>, ) -> wasmtime::Result<types::StatusCode>590 fn status_code( 591 &mut self, 592 id: Resource<HostOutgoingResponse>, 593 ) -> wasmtime::Result<types::StatusCode> { 594 Ok(self.table.get(&id)?.status.into()) 595 } 596 set_status_code( &mut self, id: Resource<HostOutgoingResponse>, status: types::StatusCode, ) -> wasmtime::Result<Result<(), ()>>597 fn set_status_code( 598 &mut self, 599 id: Resource<HostOutgoingResponse>, 600 status: types::StatusCode, 601 ) -> wasmtime::Result<Result<(), ()>> { 602 let resp = self.table.get_mut(&id)?; 603 604 match http::StatusCode::from_u16(status) { 605 Ok(status) => resp.status = status, 606 Err(_) => return Ok(Err(())), 607 }; 608 609 Ok(Ok(())) 610 } 611 headers( &mut self, id: Resource<HostOutgoingResponse>, ) -> wasmtime::Result<Resource<FieldMap>>612 fn headers( 613 &mut self, 614 id: Resource<HostOutgoingResponse>, 615 ) -> wasmtime::Result<Resource<FieldMap>> { 616 let resp = self.table.get(&id)?; 617 Ok(self.table.push(resp.headers.clone())?) 618 } 619 drop(&mut self, id: Resource<HostOutgoingResponse>) -> wasmtime::Result<()>620 fn drop(&mut self, id: Resource<HostOutgoingResponse>) -> wasmtime::Result<()> { 621 let _ = self.table.delete(id)?; 622 Ok(()) 623 } 624 } 625 626 impl types::HostFutureIncomingResponse for WasiHttpCtxView<'_> { drop(&mut self, id: Resource<HostFutureIncomingResponse>) -> wasmtime::Result<()>627 fn drop(&mut self, id: Resource<HostFutureIncomingResponse>) -> wasmtime::Result<()> { 628 let _ = self.table.delete(id)?; 629 Ok(()) 630 } 631 get( &mut self, id: Resource<HostFutureIncomingResponse>, ) -> wasmtime::Result< Option<Result<Result<Resource<HostIncomingResponse>, types::ErrorCode>, ()>>, >632 fn get( 633 &mut self, 634 id: Resource<HostFutureIncomingResponse>, 635 ) -> wasmtime::Result< 636 Option<Result<Result<Resource<HostIncomingResponse>, types::ErrorCode>, ()>>, 637 > { 638 let resp = self.table.get_mut(&id)?; 639 640 match resp { 641 HostFutureIncomingResponse::Pending(_) => return Ok(None), 642 HostFutureIncomingResponse::Consumed => return Ok(Some(Err(()))), 643 HostFutureIncomingResponse::Ready(_) => {} 644 } 645 646 let resp = 647 match std::mem::replace(resp, HostFutureIncomingResponse::Consumed).unwrap_ready() { 648 Err(e) => { 649 // Trapping if it's not possible to downcast to an wasi-http error 650 let e = e.downcast::<types::ErrorCode>()?; 651 return Ok(Some(Ok(Err(e)))); 652 } 653 654 Ok(Ok(resp)) => resp, 655 Ok(Err(e)) => return Ok(Some(Ok(Err(e)))), 656 }; 657 658 let (mut parts, body) = resp.resp.into_parts(); 659 remove_forbidden_headers(self.hooks, &mut parts.headers); 660 let headers = FieldMap::new_immutable(parts.headers); 661 662 let resp = self.table.push(HostIncomingResponse { 663 status: parts.status.as_u16(), 664 headers, 665 body: Some({ 666 let mut body = HostIncomingBody::new(body, resp.between_bytes_timeout); 667 if let Some(worker) = resp.worker { 668 body.retain_worker(worker); 669 } 670 body 671 }), 672 })?; 673 674 Ok(Some(Ok(Ok(resp)))) 675 } 676 subscribe( &mut self, id: Resource<HostFutureIncomingResponse>, ) -> wasmtime::Result<Resource<DynPollable>>677 fn subscribe( 678 &mut self, 679 id: Resource<HostFutureIncomingResponse>, 680 ) -> wasmtime::Result<Resource<DynPollable>> { 681 wasmtime_wasi::p2::subscribe(self.table, id) 682 } 683 } 684 685 impl types::HostOutgoingBody for WasiHttpCtxView<'_> { write( &mut self, id: Resource<HostOutgoingBody>, ) -> wasmtime::Result<Result<Resource<DynOutputStream>, ()>>686 fn write( 687 &mut self, 688 id: Resource<HostOutgoingBody>, 689 ) -> wasmtime::Result<Result<Resource<DynOutputStream>, ()>> { 690 let body = self.table.get_mut(&id)?; 691 if let Some(stream) = body.take_output_stream() { 692 let id = self.table.push_child(stream, &id)?; 693 Ok(Ok(id)) 694 } else { 695 Ok(Err(())) 696 } 697 } 698 finish( &mut self, id: Resource<HostOutgoingBody>, ts: Option<Resource<Trailers>>, ) -> HttpResult<()>699 fn finish( 700 &mut self, 701 id: Resource<HostOutgoingBody>, 702 ts: Option<Resource<Trailers>>, 703 ) -> HttpResult<()> { 704 let body = self.table.delete(id)?; 705 706 let ts = if let Some(ts) = ts { 707 Some(self.table.delete(ts)?) 708 } else { 709 None 710 }; 711 712 body.finish(ts)?; 713 Ok(()) 714 } 715 drop(&mut self, id: Resource<HostOutgoingBody>) -> wasmtime::Result<()>716 fn drop(&mut self, id: Resource<HostOutgoingBody>) -> wasmtime::Result<()> { 717 self.table.delete(id)?.abort(); 718 Ok(()) 719 } 720 } 721 722 impl types::HostRequestOptions for WasiHttpCtxView<'_> { new(&mut self) -> wasmtime::Result<Resource<types::RequestOptions>>723 fn new(&mut self) -> wasmtime::Result<Resource<types::RequestOptions>> { 724 let id = self.table.push(types::RequestOptions::default())?; 725 Ok(id) 726 } 727 connect_timeout( &mut self, opts: Resource<types::RequestOptions>, ) -> wasmtime::Result<Option<types::Duration>>728 fn connect_timeout( 729 &mut self, 730 opts: Resource<types::RequestOptions>, 731 ) -> wasmtime::Result<Option<types::Duration>> { 732 let nanos = self.table.get(&opts)?.connect_timeout.map(|d| d.as_nanos()); 733 734 if let Some(nanos) = nanos { 735 Ok(Some(nanos.try_into()?)) 736 } else { 737 Ok(None) 738 } 739 } 740 set_connect_timeout( &mut self, opts: Resource<types::RequestOptions>, duration: Option<types::Duration>, ) -> wasmtime::Result<Result<(), ()>>741 fn set_connect_timeout( 742 &mut self, 743 opts: Resource<types::RequestOptions>, 744 duration: Option<types::Duration>, 745 ) -> wasmtime::Result<Result<(), ()>> { 746 self.table.get_mut(&opts)?.connect_timeout = duration.map(std::time::Duration::from_nanos); 747 Ok(Ok(())) 748 } 749 first_byte_timeout( &mut self, opts: Resource<types::RequestOptions>, ) -> wasmtime::Result<Option<types::Duration>>750 fn first_byte_timeout( 751 &mut self, 752 opts: Resource<types::RequestOptions>, 753 ) -> wasmtime::Result<Option<types::Duration>> { 754 let nanos = self 755 .table 756 .get(&opts)? 757 .first_byte_timeout 758 .map(|d| d.as_nanos()); 759 760 if let Some(nanos) = nanos { 761 Ok(Some(nanos.try_into()?)) 762 } else { 763 Ok(None) 764 } 765 } 766 set_first_byte_timeout( &mut self, opts: Resource<types::RequestOptions>, duration: Option<types::Duration>, ) -> wasmtime::Result<Result<(), ()>>767 fn set_first_byte_timeout( 768 &mut self, 769 opts: Resource<types::RequestOptions>, 770 duration: Option<types::Duration>, 771 ) -> wasmtime::Result<Result<(), ()>> { 772 self.table.get_mut(&opts)?.first_byte_timeout = 773 duration.map(std::time::Duration::from_nanos); 774 Ok(Ok(())) 775 } 776 between_bytes_timeout( &mut self, opts: Resource<types::RequestOptions>, ) -> wasmtime::Result<Option<types::Duration>>777 fn between_bytes_timeout( 778 &mut self, 779 opts: Resource<types::RequestOptions>, 780 ) -> wasmtime::Result<Option<types::Duration>> { 781 let nanos = self 782 .table 783 .get(&opts)? 784 .between_bytes_timeout 785 .map(|d| d.as_nanos()); 786 787 if let Some(nanos) = nanos { 788 Ok(Some(nanos.try_into()?)) 789 } else { 790 Ok(None) 791 } 792 } 793 set_between_bytes_timeout( &mut self, opts: Resource<types::RequestOptions>, duration: Option<types::Duration>, ) -> wasmtime::Result<Result<(), ()>>794 fn set_between_bytes_timeout( 795 &mut self, 796 opts: Resource<types::RequestOptions>, 797 duration: Option<types::Duration>, 798 ) -> wasmtime::Result<Result<(), ()>> { 799 self.table.get_mut(&opts)?.between_bytes_timeout = 800 duration.map(std::time::Duration::from_nanos); 801 Ok(Ok(())) 802 } 803 drop(&mut self, rep: Resource<types::RequestOptions>) -> wasmtime::Result<()>804 fn drop(&mut self, rep: Resource<types::RequestOptions>) -> wasmtime::Result<()> { 805 let _ = self.table.delete(rep)?; 806 Ok(()) 807 } 808 } 809