今天的目標是要實作轉發請求到 Github,那要怎麼轉發呢?先來分析一下平常上 Github 時瀏覽器幫你做了什麼:
而用了我們做的 Phish Github 之後應該要變成這樣:
其中 2,3 兩步就是我們今天要做的
因為第二步要發出 一模一樣 的請求給 Github,這邊實作一個 cloneRequest
來 複製請求
func cloneRequest(r *http.Request) *http.Request {
// 取得原請求的 method、body
method := r.Method
body := r.Body
// 取得原請求的 url,把它的域名替換成真正的 Github
path := r.URL.Path
rawQuery := r.URL.RawQuery
url := "https://github.com" + path + "?" + rawQuery
// 建立新的 http.Request
req, err := http.NewRequest(method, url, body)
if err != nil {
panic(err)
}
return req
}
這邊依據原請求 method
、body
還有替換過的網址去建立一個新的請求,如果原本的請求是 GET http://localhost/Larry850806
,那新的請求就會把它替換成 GET https://github.com/Larry850806
再寫一個 sendReqToUpStream
負責把請求送到 Upstream(上游),也就是真正的 Github。
func sendReqToUpstream(req *http.Request) []byte {
// 建立 http client
client := http.Client{}
// client.Do(req) 會發出請求到 Github、得到回覆 resp
resp, err := client.Do(req)
if err != nil {
panic(err)
}
// 把回覆的 body 從 Reader(串流)轉成 []byte
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
resp.Body.Close()
// 回傳 body
return respBody
}
在 Go 裡面要發請求必須先建立一個 http client,請求結束後會得到一個 resp.Body
是 Github 回覆回來的 body,body 的型別是 Reader
,有點像 Stream(串流)的概念,要用 ioutil.ReadAll
把它讀取出來變成 []byte
,最後回傳 body
func handler(w http.ResponseWriter, r *http.Request) {
req := cloneRequest(r)
body := sendReqToUpstream(req)
w.Write(body)
}
接下來就是最重要的 handler,針對每個請求都先用 cloneRequest
複製一個一模一樣的請求,如果原請求是要新增一個 repo,那就複製一個新增 repo 的請求,然後發送到 Github
取得 Github 的回覆之後(也許是新增成功之類的),再把 body 原封不動的傳回給瀏覽器,這樣使用者就會在我們的 Phish Github 上看到新增成功的畫面,而且這個畫面是來自真正的 Github
現在已經稍微做出一個雛形了,左邊的是真正的 Github,右邊則是自己架在 localhost 的假 Github,因為畫面是都是來自 Github,所以會長得一模一樣
眼尖的同學們有沒有發現右圖中左上角的 Github logo 會連回真正的 Github 呢?我們將在明天解決這個問題,聰明的各位也可以先想想看為什麼會這樣,怎麼解決?
現在 Phish Github 已經有點 Github 的樣子了,雖然很多功能都還沒支援就是了,今天的程式碼也有放在 Github,建議大家可以下載程式碼到自己電腦上跑跑看,把 Github 跑在 localhost 還滿有趣的
跟前幾天一樣,有什麼問題都歡迎留言發問,我會盡量回答,謝謝大家