Hyper server APIs – building a URL shortener 

In this section, we'll build a URL shortener server that exposes a /shorten endpoint. This endpoint accepts a POST request, with the body containing the URL to be shortened. Let's fire up a new project by running cargo new hyperurl with the following dependencies in Cargo.toml:

# hyperurl/Cargo.toml

[dependencies]
hyper = "0.12.17"
serde_json = "1.0.33"
futures = "0.1.25"
lazy_static = "1.2.0"
rust-crypto = "0.2.36"
log = "0.4"
pretty_env_logger = "0.3"

We'll name our URL shortening server, hyperurl. A URL shortener service is a service that provides the functionality to create a shorter URL for any given URL. When you have a really long URL, it becomes tedious to share it with someone. A lot of URL shortening services exist today, such as bit.ly. If you have used Twitter, users use short URL in tweets quite often, to save space.

 Here's our initial implementation in main.rs:

// hyperurl/src/main.rs

use log::{info, error};
use std::env;

use hyper::Server;
use hyper::service::service_fn;

use hyper::rt::{self, Future};

mod shortener;
mod service;
use crate::service::url_service;

fn main() {
env::set_var("RUST_LOG","hyperurl=info");
pretty_env_logger::init();

let addr = "127.0.0.1:3002".parse().unwrap();
let server = Server::bind(&addr)
.serve(|| service_fn(url_service))
.map_err(|e| error!("server error: {}", e));
info!("URL shortener listening on http://{}", addr);
rt::run(server);
}

In main, we create a Server instance and bind it to our loopback address and port string "127.0.0.1:3002". This returns a builder instance on which we call serve before passing in the function url_service which implements the Service trait. The function url_service maps a Request to a future of Responseservice_fn is a factory function that has the following signature:

pub fn service_fn<F, R, S>(f: F) -> ServiceFn<F, R> where
F: Fn(Request<R>) -> S,
S: IntoFuture,

As you can see, F needs to be a Fn closure that

Our function url_service implements the Service trait. Next, let's see the code in service.rs:

// hyperurl/src/service.rs

use std::sync::RwLock;
use std::collections::HashMap;
use std::sync::{Arc};
use std::str;
use hyper::Request;
use hyper::{Body, Response};
use hyper::rt::{Future, Stream};

use lazy_static::lazy_static;

use crate::shortener::shorten_url;

type UrlDb = Arc<RwLock<HashMap<String, String>>>;
type BoxFut = Box<Future<Item = Response<Body>, Error = hyper::Error> + Send>;

lazy_static! {
static ref SHORT_URLS: UrlDb = Arc::new(RwLock::new(HashMap::new()));
}

pub(crate) fn url_service(req: Request<Body>) -> BoxFut {
let reply = req.into_body().concat2().map(move |chunk| {
let c = chunk.iter().cloned().collect::<Vec<u8>>();
let url_to_shorten = str::from_utf8(&c).unwrap();
let shortened_url = shorten_url(url_to_shorten);
SHORT_URLS.write().unwrap().insert(shortened_url, url_to_shorten.to_string());
let a = &*SHORT_URLS.read().unwrap();
Response::new(Body::from(format!("{:#?}", a)))
});

Box::new(reply)
}

This module exposes a single function url_service, which implements the Service trait. Our url_service method implements the method call by taking in a req of the Request<Body> type and returns a future that is behind a Box

Next, is our shortener module:

// hyperurl/src/shortener.rs

use crypto::digest::Digest;
use crypto::sha2::Sha256;

pub(crate) fn shorten_url(url: &str) -> String {
let mut sha = Sha256::new();
sha.input_str(url);
let mut s = sha.result_str();
s.truncate(5);
format!("https://u.rl/{}", s)
}

Our shorten_url function takes in a URL to shorten as a &str. It then computes the SHA-256 hash of the URL and truncates it to a string of length five. This is obviously not how a real URL shortener works and is not a scalable solution either. However, it's fine for our demonstration purposes.

Let's take this for a spin:

Our server is running. At this point we can either send requests POST through curl. We'll do this the other way by building a command-line client for sending URLs to shorten this server.

While Hyper is recommended for complex HTTP applications, it's quite cumbersome every time to create a handler service, register it, and run it in a runtime. Often, for to build smaller tools such as a CLI application that needs to make a couple of GET requests, this becomes overkill. Fortunately, we have another opinionated wrapper over hyper called the reqwest crate. As the name suggests, it is inspired by Python's Requests library. We'll use this to build our hyperurl client that sends URL shorten requests.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset