简单网络通信

wasmedge_wasi_socket 让 Rust 开发者可以创建简单的网络通信应用程序,然后将它编译为在 WasmEdge Runtime 中运行的 WebAssembly。WasmEdge 的一项关键特性便是对非阻塞套接字的支持,可以让一个单线程的 WASM 应用程序处理并发的网络请求。比如,当程序在等待一个连接传输的数据时,它可以开始或处理另一个连接。

在本章节中,我们将从一个简单的 HTTP 客户端以及服务器示例开始。然后在下个章节中,我们会介绍更为复杂的非阻塞示例。

HTTP 客户端示例

HTTP 客户端的源代码 如下。

  1. use wasmedge_http_req::request;
  2. fn main() {
  3. let mut writer = Vec::new(); //container for body of a response
  4. let res = request::get("http://127.0.0.1:1234/get", &mut writer).unwrap();
  5. println!("GET");
  6. println!("Status: {} {}", res.status_code(), res.reason());
  7. println!("Headers {}", res.headers());
  8. println!("{}", String::from_utf8_lossy(&writer));
  9. let mut writer = Vec::new(); //container for body of a response
  10. const BODY: &[u8; 27] = b"field1=value1&field2=value2";
  11. // let res = request::post("https://httpbin.org/post", BODY, &mut writer).unwrap();
  12. // no https , no dns
  13. let res = request::post("http://127.0.0.1:1234/post", BODY, &mut writer).unwrap();
  14. println!("POST");
  15. println!("Status: {} {}", res.status_code(), res.reason());
  16. println!("Headers {}", res.headers());
  17. println!("{}", String::from_utf8_lossy(&writer));
  18. }

使用如下命令来编译 Rust 程序。

cargo build --target wasm32-wasi --release

使用如下命令在 WasmEdge 中运行程序。

wasmedge target/wasm32-wasi/release/http_client.wasm

HTTP 服务器示例

HTTP 服务器的源代码 如下。

  1. use bytecodec::DecodeExt;
  2. use httpcodec::{HttpVersion, ReasonPhrase, Request, RequestDecoder, Response, StatusCode};
  3. use std::io::{Read, Write};
  4. #[cfg(feature = "std")]
  5. use std::net::{Shutdown, TcpListener, TcpStream};
  6. #[cfg(not(feature = "std"))]
  7. use wasmedge_wasi_socket::{Shutdown, TcpListener, TcpStream};
  8. fn handle_http(req: Request<String>) -> bytecodec::Result<Response<String>> {
  9. Ok(Response::new(
  10. HttpVersion::V1_0,
  11. StatusCode::new(200)?,
  12. ReasonPhrase::new("")?,
  13. format!("echo: {}", req.body()),
  14. ))
  15. }
  16. fn handle_client(mut stream: TcpStream) -> std::io::Result<()> {
  17. let mut buff = [0u8; 1024];
  18. let mut data = Vec::new();
  19. loop {
  20. let n = stream.read(&mut buff)?;
  21. data.extend_from_slice(&buff[0..n]);
  22. if n < 1024 {
  23. break;
  24. }
  25. }
  26. let mut decoder =
  27. RequestDecoder::<httpcodec::BodyDecoder<bytecodec::bytes::Utf8Decoder>>::default();
  28. let req = match decoder.decode_from_bytes(data.as_slice()) {
  29. Ok(req) => handle_http(req),
  30. Err(e) => Err(e),
  31. };
  32. let r = match req {
  33. Ok(r) => r,
  34. Err(e) => {
  35. let err = format!("{:?}", e);
  36. Response::new(
  37. HttpVersion::V1_0,
  38. StatusCode::new(500).unwrap(),
  39. ReasonPhrase::new(err.as_str()).unwrap(),
  40. err.clone(),
  41. )
  42. }
  43. };
  44. let write_buf = r.to_string();
  45. stream.write(write_buf.as_bytes())?;
  46. stream.shutdown(Shutdown::Both)?;
  47. Ok(())
  48. }
  49. fn main() -> std::io::Result<()> {
  50. let port = std::env::var("PORT").unwrap_or(1234.to_string());
  51. println!("new connection at {}", port);
  52. let listener = TcpListener::bind(format!("0.0.0.0:{}", port))?;
  53. loop {
  54. let _ = handle_client(listener.accept()?.0);
  55. }
  56. }

使用如下命令来编译 Rust 程序。

cargo build --target wasm32-wasi --release

使用如下命令在 WasmEdge 中运行程序。

$ wasmedge target/wasm32-wasi/release/http_server.wasm new connection at 1234

你可以用 curl 发送一个 HTTP 请求,来测试 HTTP 服务器。

$ curl -d "name=WasmEdge" -X POST http://127.0.0.1:1234 echo: name=WasmEdge