在Libra Core中,官方提供了一个命令列工具,可以实现建立账户、挖矿和转账等基本操作,但是没有提供Restful界面,使我们想要开发的应用系统,将区块链逻辑移植到Libra Testnet上去。在本篇博文中,我们将利用Rust语言,将官方的命令列界面,改造成RESTful界面。由于我们只是临时改造,相信官方的RESTful界面很快就会出现,因此我们在这里仅使用最简实现,实现一个单执行绪的Web服务器来完成这一工作。
最简Web服务器
我们需要一个最简单的Web服务器,来接收客户端的请求,然后呼叫系统功能完成Libra Core中相关的区块链操作操作。我们先开发一个独立的应用,实现最基本的Web服务器功能,然后再将其整合到Libra Core的命令列工具中。我们首先通过如下命令建立一个新工程:
cargo new libra_server --bin
我们建立一个名称为libra_server的工程,其为可执行档案形式。上面的命令会在当前目录下建立libra_server目录,并建立libra_server/src/main.rs档案,这个档案就是整个专案的主程式档案。
下面我们建立一个Hello World的Web服务器:
use std::io::prelude::*;
use std::net::TcpStream;
use std::net::TcpListener;
fn main() {
start_server();
}
fn start_server() {
println!("Libra Server v0.0.2 Starting up ...");
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
handle_connection(stream);
}
}
fn handle_connection(mut stream: TcpStream) {
let mut buffer = [0; 512];
stream.read(&mut buffer).unwrap();
println!("Request: {}", String::from_utf8_lossy(&buffer[..]));
let contents = "Hello World!";
let response = format!("HTTP/1.1 200 OK {}", contents);
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
我们直接编译执行程式:
cargo run
执行结果如下所示:
使用浏览器访问如下地址:http://127.0.0.1:7878/account_list/88 ,会显示Hello World资讯,这就说明我们的Web服务器可以正常运行了。
下面我们在handle_connection方法中,求出cmd引数的值,根据cmd呼叫不同的处理函式,在这些函式中呼叫Libra Core的区块链服务,解析区块链服务的返回结果,最后再以Http响应的形式返回给客户端。
我们要做的第一件事就是求出QueryString,程式码如下所示:
/**
* 获取请求中的Query String,规定引数以?cmd=开头
* @version v0.0.1 闫涛 2019.06.23
*/
fn get_query_string(request: &str) -> String {
let pos = request.find("?cmd=");
if pos return "Has no parameters in request".to_string();
}
let end_pos = request.find(" HTTP/1.1");
return (&request[(pos.unwrap()+1)..end_pos.unwrap()]).to_string();
}
在这段程式码中,我们首先找到QueryString开始位置,如果没找到则返回出错资讯。接着我们找到结束资讯,最后我们截取出子字串作为QueryString返回。
在得到QueryString之后,我们需要找出引数cmd的值,这样我们才能根据cmd引数的值呼叫对应的命令处理函式,如下所示:
/**
* 获取请求cmd引数值
* @version v0.0.1 闫涛 2019.06.23
*/
fn get_cmd_param(query_string: String) -> String {
let params: Vec = query_string.split("&").collect();
for param in params.iter() {
println!("item: {}!", param);
if param.find("cmd=") >= Some(0) {
let cmd = ¶m[4..];
return cmd.to_string();
}
}
return "".to_string();
}
接下来我们定义生成账户的命令处理函式,如下所示:
/**
* 生成账户命令处理函式
* @version v0.0.1 闫涛 2019.06.23
*/
fn handle_account_create(_params: Vec) -> String {
println!("生成新账户!");
let rst: String = String::from("create account: 0");
return rst;
}
实际中,这个函式需要呼叫Libra Core来建立账户,我们在这里先简单的返回一个字串,在下一篇文章中我们再来具体讲怎么呼叫Libra Core服务以及解析响应结果。
接下来我们看handle_connection方法,这时这个方法的逻辑就变为接到一个请求后,首先得到QueryString,然后从QueryString得到cmd引数,然后根据cmd的值呼叫对应的命令处理函式,如下所示:
fn handle_connection(mut stream: TcpStream) {
let mut contents: String = String::from("Hello World!");
let mut buffer = [0; 1024];
// 获取请求资讯
stream.read(&mut buffer).unwrap();
println!("Request: {}", String::from_utf8_lossy(&buffer[..]));
let request = String::from_utf8_lossy(&buffer[..]);
// 不处理请求网站图示请求
if request.find("GET /favicon.ico HTTP/1.1") >= Some(0) {
return ;
}
// 请出请求中的query string
let query_string = &get_query_string(&request);
println!("query_string:{}", query_string);
let cmd = get_cmd_param(query_string.to_string());
println!("接收到命令:cmd={}!", cmd);
let params: Vec = query_string.split("&").collect();
if cmd.find("account_create")>=Some(0) {
contents = handle_account_create(params);
} else if cmd.find("account_list")>=Some(0) {
contents = handle_account_list(params);
}
let response = format!("HTTP/1.1 200 OK {}", contents);
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
我们可以按照这种方式,将下列命令先以占位符的形式写出来,我在这里就不重复贴程式码了,在下一篇文章中,我们将对每个命令,学习怎样向Libra Core传送命令,以及怎样解析命令的返回结果。