1.5. 应用:简单HTTP服务器

未匹配的标注

应用:简单HTTP服务器

让我们使用async/ .await构建一个echo服务器!

首先,请rustup update stable确保您的Rust版本是标准版本1.39或更新的。完成后,运行 cargo new async-await-echo以创建新项目,然后打开生成的async-await-echo文件夹。

让我们在Cargo.toml文件中添加一些依赖项:

[dependencies]
# The latest version of the "futures" library, which has lots of utilities
# for writing async code. Enable the "compat" feature to include the
# functions for using futures 0.3 and async/await with the Hyper library,
# which use futures 0.1.
futures = { version = "0.3", features = ["compat"] }

# Hyper is an asynchronous HTTP library. We'll use it to power our HTTP
# server and to make HTTP requests.
hyper = "0.12.9"

现在我们已经完成依赖项了,让我们开始编写一些代码。我们先添加一些导入:

use {
    hyper::{
        // Hyper中的其他类型,用于构建HTTP
        Body, Client, Request, Response, Server, Uri,


        // 该函数将闭包转换为实现了Hyper的Service trait的future
        // 它是从通用Request到Response的异步函数。
        service::service_fn,

        // 使用Hyper运行时可以运行future到完成的函数。
        rt::run,
    },
    futures::{
        // futures 0.1版本的一个扩展trait,添加了'.compat()'方法
        // 允许我们在0.1版本的futures中使用'.await'语法
        compat::Future01CompatExt,
        // 扩展trait在futures上提供了额外的方法在
        // `FutureExt` 添加了适用于所有futures的方法,
        // `TryFutureExt` 给futures添加了可以放回‘Result’类型的方法
        future::{FutureExt, TryFutureExt},
    },
    std::net::SocketAddr,
};

一旦导入完成,我们就可以开始整理样板,以便我们提供请求服务:

async fn serve_req(_req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
    // 通常成功地返回一个包含友好问候的body的响应(response)
    Ok(Response::new(Body::from("hello, world!")))
}
async fn run_server(addr: SocketAddr) {
    println!("Listening on http://{}", addr);
     // 创建绑定到提供的地址的服务器
    let serve_future = Server::bind(&addr)
        // 服务器请求使用 `async serve_req` 这个函数.
        // `serve` 方法采取闭包操作只要求返回的值实现‘Service’这个trait,
        //  `service_fn` 这个函数正好返回一个实现'Service' 这个trait的值
        // 该方法也是采用闭包操作,返回的是一个响应future的请求
        // 为了使用Hyper的serve_req这个函数,我们必须用box把它包裹起来
        // 并且用compat方法让他获得0.1futures的兼容性(Hyper还是0.1的futures,所以显得麻烦)
        .serve(|| service_fn(|req|
            serve_req(req).boxed().compat()
        ));
     // 等待服务完成服务或者因为某个错误而退出
    // 如果一个错误出现,输出它到stderr
    if let Err(e) = serve_future.compat().await {
        eprintln!("server error: {}", e);
    }
}
fn main() {
    // 设置地址以运行我们的套接字
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
     // 调用 run_server 函数, 将返回一个future
    // 与每一个`async fn`一样, 对于`run_server`做任何事情,
    // 返回的future需要运行
    //   额外地,我们需要将返回的future从futures0.3转换成0.1版本的future
    let futures_03_future = run_server(addr);
    let futures_01_future =
        futures_03_future.unit_error().boxed().compat();
     // 最后我们用Hyper提供的run方法运行我们的future直到完成
    run(futures_01_future);
}

如果您现在cargo run,您应该在终端上看到"Listening on http://127.0.0.1:3000"消息。如果您在所选择的浏览器中打开该URL,你应该看到”hello, world!” 出现在浏览器中。恭喜!您刚刚在Rust中编写了第一个异步Web服务器。

您还可以检查请求本身,其中包含请求URI,HTTP版本,标头和其他元数据等信息。例如,我们可以打印出请求的URI,如下所示:

println!("Got request at {:?}", req.uri());

你可能已经注意到我们在处理请求时还没有做任何异步 - 我们只是立即回应,所以我们没有利用async fn给我们的灵活性。让我们尝试使用Hyper的HTTP客户端将用户的请求代理到另一个网站,而不仅仅是返回静态消息。

我们首先解析出我们想要请求的URL:

let url_str = "http://www.rust-lang.org/en-US/";
let url = url_str.parse::<Uri>().expect("failed to parse URL");

然后我们可以创建一个新的hyper::Client并使用它来发出GET请求,将响应返回给用户:

let res = Client::new().get(url).compat().await;
// Return the result of the request directly to the user
println!("request finished --returning response");
res

Client::get返回hyper::client::FutureResponse,他实现了 Future<Output = Result<Response, Error>> (或Future<Item = Response, Error = Error> 在futures 0.1)。当我们.await这个future时,HTTP请求已发出,当前任务被暂停,并且一旦响应可用,任务就排队等待继续。

现在,如果cargo run您在浏览器中打开http://127.0.0.1:3000/foo,您将看到Rust主页,以及以下终端输出:

Listening on http://127.0.0.1:3000
Got request at /foo
making request to http://www.rust-lang.org/en-US/
request finished-- returning response

恭喜!您刚刚完成了代理HTTP请求。

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
Summer
贡献者:2
讨论数量: 0
发起讨论 只看当前版本


暂无话题~