溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

怎么用Go語(yǔ)言實(shí)現(xiàn)一個(gè)HTTP代理

發(fā)布時(shí)間:2022-04-21 14:12:59 來(lái)源:億速云 閱讀:661 作者:iii 欄目:大數(shù)據(jù)

這篇文章主要講解了“怎么用Go語(yǔ)言實(shí)現(xiàn)一個(gè)HTTP代理”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“怎么用Go語(yǔ)言實(shí)現(xiàn)一個(gè)HTTP代理”吧!

怎么用Go語(yǔ)言實(shí)現(xiàn)一個(gè)HTTP代理

我們這里主要講使用HTTP/1.1協(xié)議中的CONNECT方法建立起來(lái)的隧道連接,實(shí)現(xiàn)的HTTP Proxy。這種代理的好處就是不用知道客戶(hù)端請(qǐng)求的數(shù)據(jù),只需要原封不動(dòng)的轉(zhuǎn)發(fā)就可以了,對(duì)于處理HTTPS的請(qǐng)求就非常方便了,不用解析他的內(nèi)容,就可以實(shí)現(xiàn)代理。

啟動(dòng)代理監(jiān)聽(tīng)

要想做一個(gè)HTTP Proxy,我們需要啟動(dòng)一個(gè)服務(wù)器,監(jiān)聽(tīng)一個(gè)端口,用于接收客戶(hù)端的請(qǐng)求。Golang給我們提供了強(qiáng)大的net包供我們使用,我們啟動(dòng)一個(gè)代理服務(wù)器監(jiān)聽(tīng)非常方便。

    l, err := net.Listen("tcp", ":8080")    if err != nil {

        log.Panic(err)

    }

以上代理我們就實(shí)現(xiàn)了一個(gè)在8080端口上監(jiān)聽(tīng)的服務(wù)器,我們這里沒(méi)有寫(xiě)ip地址,默認(rèn)在所有ip地址上進(jìn)行監(jiān)聽(tīng)。如果你只想本機(jī)適用,可以使用127.0.0.1:8080,這樣機(jī)器就訪(fǎng)問(wèn)不了你的代理服務(wù)器了。

監(jiān)聽(tīng)接收代理請(qǐng)求

啟動(dòng)了代理服務(wù)器,就可以開(kāi)始接受不了代理請(qǐng)求了,有了請(qǐng)求,我們才能做進(jìn)一步的處理。

    for {

        client, err := l.Accept()        if err != nil {

            log.Panic(err)

        }        go handleClientRequest(client)

    }

Listener接口的Accept方法,會(huì)接受客戶(hù)端發(fā)來(lái)的連接數(shù)據(jù),這是一個(gè)阻塞型的方法,如果客戶(hù)端沒(méi)有連接數(shù)據(jù)發(fā)來(lái),他就是阻塞等待。接收來(lái)的連接數(shù)據(jù),會(huì)馬上交給handleClientRequest方法進(jìn)行處理,這里使用一個(gè)go關(guān)鍵字開(kāi)一個(gè)goroutine的目的是不阻塞客戶(hù)端的接收,代理服務(wù)器可以馬上接收下一個(gè)連接請(qǐng)求。

解析請(qǐng)求,獲取要訪(fǎng)問(wèn)的IP和端口

有了客戶(hù)端的代理請(qǐng)求了,我們還得從請(qǐng)求里提取客戶(hù)端要訪(fǎng)問(wèn)的遠(yuǎn)程主機(jī)的IP和端口,這樣我們的代理服務(wù)器才可以建立和遠(yuǎn)程主機(jī)的連接,代理轉(zhuǎn)發(fā)。

HTTP協(xié)議的頭信息里就包含有我們需要的主機(jī)名(IP)和端口信息,并且是明文的,協(xié)議很規(guī)范,類(lèi)似于:

CONNECT www.google.com:443 HTTP/1.1

Host: www.google.com:443

Proxy-Connection: keep-alive

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36

可以看到我們需要的在第一行,第一個(gè)行的信息以空格分開(kāi),第一部分CONNECT是請(qǐng)求方法,這里是CONNECT,除此之外還有GET,POST等,都是HTTP協(xié)議的標(biāo)準(zhǔn)方法。

第二部分是URL,https的請(qǐng)求只有host和port,http的請(qǐng)求是一個(gè)完成的url,等下會(huì)看個(gè)樣例,就明白了。

第三部是HTTP的協(xié)議和版本,這個(gè)我們不用太關(guān)注。

以上是一個(gè)https的請(qǐng)求,我們看下http的:

GET http://www.flysnow.org/ HTTP/1.1

Host: www.flysnow.org

Proxy-Connection: keep-alive

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36

可以看到htt的,沒(méi)有端口號(hào)(默認(rèn)是80);比https多了schame—http://。

有了分析,下面我們就可以從HTTP頭信息中獲取請(qǐng)求的url和method信息了。

    var b [1024]byte

    n, err := client.Read(b[:])    if err != nil {

        log.Println(err)        return

    }    var method, host, address string

    fmt.Sscanf(string(b[:bytes.IndexByte(b[:], '\n')]), "%s%s", &method, &host)

    hostPortURL, err := url.Parse(host)    if err != nil {

        log.Println(err)        return

    }

然后需要進(jìn)一步對(duì)url進(jìn)行解析,獲取我們需要的遠(yuǎn)程服務(wù)器信息

    if hostPortURL.Opaque == "443" { //https訪(fǎng)問(wèn)

        address = hostPortURL.Scheme + ":443"

    } else { //http訪(fǎng)問(wèn)

        if strings.Index(hostPortURL.Host, ":") == -1 { //host不帶端口, 默認(rèn)80

            address = hostPortURL.Host + ":80"

        } else {

            address = hostPortURL.Host

        }

    }

這樣就完整了獲取了要請(qǐng)求服務(wù)器的信息,他們可能是以下幾種格式

ip:port

hostname:port

domainname:port

就是有可能是ip(v4orv6),有可能是主機(jī)名(內(nèi)網(wǎng)),有可能是域名(dns解析)

代理服務(wù)器和遠(yuǎn)程服務(wù)器建立連接

有了遠(yuǎn)程服務(wù)器的信息了,就可以進(jìn)行撥號(hào)建立連接了,有了連接,才可以通信。

    //獲得了請(qǐng)求的host和port,就開(kāi)始撥號(hào)吧

    server, err := net.Dial("tcp", address)    if err != nil {

        log.Println(err)        return

    }

數(shù)據(jù)轉(zhuǎn)發(fā)

撥號(hào)成功后,就可以進(jìn)行數(shù)據(jù)代理傳輸了

if method == "CONNECT" {

        fmt.Fprint(client, "HTTP/1.1 200 Connection established\r\n")

    } else {

        server.Write(b[:n])

    }    //進(jìn)行轉(zhuǎn)發(fā)

    go io.Copy(server, client)

    io.Copy(client, server)

其中對(duì)CONNECT方法有單獨(dú)的回應(yīng),客戶(hù)端說(shuō)要建立連接,代理服務(wù)器要回應(yīng)建立好了,然后才可以像HTTP一樣請(qǐng)求訪(fǎng)問(wèn)。

完整代碼

到這里,我們的代理服務(wù)器全部開(kāi)發(fā)完成了,下面是完整的源代碼:

package mainimport (    "bytes"

    "fmt"

    "io"

    "log"

    "net"

    "net/url"

    "strings")func main() {

    log.SetFlags(log.LstdFlags|log.Lshortfile)

    l, err := net.Listen("tcp", ":8081")    if err != nil {

        log.Panic(err)

    }    for {

        client, err := l.Accept()        if err != nil {

            log.Panic(err)

        }        go handleClientRequest(client)

    }

}func handleClientRequest(client net.Conn) {    if client == nil {        return

    }    defer client.Close()    var b [1024]byte

    n, err := client.Read(b[:])    if err != nil {

        log.Println(err)        return

    }    var method, host, address string

    fmt.Sscanf(string(b[:bytes.IndexByte(b[:], '\n')]), "%s%s", &method, &host)

    hostPortURL, err := url.Parse(host)    if err != nil {

        log.Println(err)        return

    }    if hostPortURL.Opaque == "443" { //https訪(fǎng)問(wèn)

        address = hostPortURL.Scheme + ":443"

    } else { //http訪(fǎng)問(wèn)

        if strings.Index(hostPortURL.Host, ":") == -1 { //host不帶端口, 默認(rèn)80

            address = hostPortURL.Host + ":80"

        } else {

            address = hostPortURL.Host

        }

    }    //獲得了請(qǐng)求的host和port,就開(kāi)始撥號(hào)吧

    server, err := net.Dial("tcp", address)    if err != nil {

        log.Println(err)        return

    }    if method == "CONNECT" {

        fmt.Fprint(client, "HTTP/1.1 200 Connection established\r\n")

    } else {

        server.Write(b[:n])

    }    //進(jìn)行轉(zhuǎn)發(fā)

    go io.Copy(server, client)

    io.Copy(client, server)

}

感謝各位的閱讀,以上就是“怎么用Go語(yǔ)言實(shí)現(xiàn)一個(gè)HTTP代理”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)怎么用Go語(yǔ)言實(shí)現(xiàn)一個(gè)HTTP代理這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI