以 Rust 撰寫的 HTTP 重新導向探測工具程式

如果說你在某處看到了一個短網址,想先確認這個網址會跑道哪裡時,有哪些方法呢?

  • 開開看

    可是就是不想開才想要想辦法不是嗎…

  • curl -I http://example.com | grep Location

    可是必須開個 terminal …

  • 使用第三方工具

    http://www.redirect-checker.org/

  • 自己做個小工具

    原理上來說蠻容易的,正好當練習不是嗎?

原理很容易是為何呢? 轉網址的方法有三個

  • HTTP 3XX

  • HTML meta tag <meta http-equiv="refresh" content="0; URL=http://www.example.com/" />

  • JavaScript window.location

而最常見的方法是使用 HTTP 3XX 回應。因此,想知道有沒有轉址可以

  • 使用 HTTP HEAD 詢問該連結

  • 若狀態碼是 3XX ,則查看回傳訊息的 Location header 項目

依據這樣的規則就可以寫程式了

練習 take one !

這樣的工作感覺很適合 JavaScript 來做不是嗎?想想,若使用 Fetch API 加上瀏覽器,還可以有漂亮的使用者界面呢!

不過很不幸這個方法完全沒有用,因為有 cross origin 的限制。所以如果要透過網頁的話,詢問連結的邏輯必須放在伺服器,而不是瀏覽器端。

練習 take two !

那這時候,不如就把製作此工具的工程當作是練習 Rust 程式語言的契機吧。

規劃

要用 Rust 來完成這個工具,最基本上需要

  • hyper crate 提供 HTTP 服務

  • tokio-core crate 提供非同步處理功能

  • hyper-tls crate 提供 HTTPS 的功能

上面不包括使用者界面可能會使用到的其他元件

軟體的架構上,把後面的探測邏輯包裝在 struct Fetch 內,並且放入自己的 crate 中。

在自己的 Rust 專案中區分 subcrate 的好處是

  • 更加明確的區分非界面以及界面的程式碼

  • 加快編譯速度。在開發使用者界面時,不需要重新編譯內部邏輯的部份

難處

使用 Rust 的難處在於,要符合生命週期標記 (lifetime) 、借用檢查等機制 (borrow checker) 的要求。此外,還有多型物件的回傳問題。

以這個程式來說,遇到比較難處理的是多型物件回傳的問題。因為 hyper 使用了非同步模型,牽扯了 futures crate 的 trait Future 。在撰寫這樣的程式時, refactor 完成後,最終一定會有 function 的回傳結果是 trait Future 的物件,那這時該如何描述呢?其實用 Box 就可以解決了大部分的問題了。

那,第二不好處理的問題是 lifetime 的問題。在這個程式裡面遇到的是 Box 內的 lifetime 。預設中, Box 內物件的 lifetime 是 'static (等同程式的壽命),但有時候某些物件無法存留如此久,就必須在 Box 內加上註記。

第三個問題點,在於對 hyper 的不熟悉,以及沒有仔細讀 hyper 的文件所導致。仔細來說,就是在利用 future 設定好進行 HTTP query 的流程之後,必須把該 future 傳送給 Core::run 。沒有傳送的話會導致程式停頓,或者發生找不到事件處理迴圈的錯誤。

第四個問題是加入 HTTPS 支援的方法。 hyper crate 本身似乎沒有明確指出(不然就是漏看了)如何增加 HTTPS 支援。 Google 後發現可以增加 hyper-tls crate ,然後使用 HttpsConnector 來初始化 Client 來達成功用。

最終成品:練習二號 rust-redirection-probe

所以,這樣練習得出的結果就是 rust-redirection-probe 了。

請移步到 https://gitlab.com/chiakikame/rust-redirection-probe 來取得程式碼。拿到程式碼之後請利用 cargo 來建置。

功能

  • 可以看 HTTP / HTTPS 連結是否會經由 3XX 重新導向

  • 可以一口氣查詢多組連結

  • 可以用來觀摩如何把內部邏輯(程式庫)以及呈現邏輯(目前只有文字界面)區分開

  • 相對容易使用:在 MS Windows 之下可以不用先打開 terminal 再使用。

用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ # Command-line argument mode
$ ./redirect-probe-tui https://www.google.com https://twitter.com http://twitter.com
Link https://www.google.com ... => redirects (via 3XX) to https://www.google.com.tw/?gfe_rd=cr&dcr=0&ei=lDPmWeq5GvH88we8iZjICA
Link https://twitter.com ... => does not redirect.
Link http://twitter.com ... => redirects (via 3XX) to https://twitter.com/
$ # Manual input / stdin mode
$ ./redirect-probe-tui
Entering stdin mode
https://www.google.com
Link https://www.google.com ... => redirects (via 3XX) to https://www.google.com.tw/?gfe_rd=cr&dcr=0&ei=1TPmWcDfK_D88welgp6gAQ
http://www.google.com
Link http://www.google.com ... => redirects (via 3XX) to http://www.google.com.tw/?gfe_rd=cr&dcr=0&ei=2zPmWcjWDfL88wfblZTQCw
http://twitter.com
Link http://twitter.com ... => redirects (via 3XX) to https://twitter.com/
http://facebook.org
Link http://facebook.org ... => redirects (via 3XX) to https://facebook.com/

可改進處

  • 更好看的使用者界面

  • Terminal 可以給更多提示

  • 試著在程式碼中標注 rustdoc 註解

文章目錄
  1. 1. 練習 take one !
  2. 2. 練習 take two !
    1. 2.1. 規劃
    2. 2.2. 難處
  3. 3. 最終成品:練習二號 rust-redirection-probe
    1. 3.1. 功能
    2. 3.2. 用法
    3. 3.3. 可改進處