在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\r\n\r\n{}", 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 <= Some(0) {
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<&str>) -> 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\r\n\r\n{}", contents);
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
我們可以按照這種方式,將下列命令先以佔位符的形式寫出來,我在這裏就不重複貼代碼了,在下一篇文章中,我們將對每個命令,學習怎樣向Libra Core發送命令,以及怎樣解析命令的返回結果。