一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

http代理概述及代碼實(shí)現(xiàn)方法

馬哥Linux運(yùn)維 ? 來(lái)源:staight.github.io ? 作者:staight.github.io ? 2022-05-14 15:02 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

本文詳細(xì)介紹了Golang 實(shí)現(xiàn) http 代理的實(shí)現(xiàn),在實(shí)際業(yè)務(wù)中有需求的同學(xué)可以學(xué)起來(lái)了!

代理是網(wǎng)絡(luò)中的一項(xiàng)重要的功能,其功能就是代理網(wǎng)絡(luò)用戶去取得網(wǎng)絡(luò)信息。形象的說(shuō):它是網(wǎng)絡(luò)信息的中轉(zhuǎn)站,對(duì)于客戶端來(lái)說(shuō),代理扮演的是服務(wù)器的角色,接收請(qǐng)求報(bào)文,返回響應(yīng)報(bào)文;對(duì)于 web 服務(wù)器來(lái)說(shuō),代理扮演的是客戶端的角色,發(fā)送請(qǐng)求報(bào)文,接收響應(yīng)報(bào)文。

代理具有多種類型,如果是根據(jù)網(wǎng)絡(luò)用戶劃分的話,可以劃分為正向代理和反向代理:

  • 正向代理:將客戶端作為網(wǎng)絡(luò)用戶。客戶端訪問(wèn)服務(wù)端時(shí),先訪問(wèn)代理服務(wù)器,隨后代理服務(wù)器再訪問(wèn)服務(wù)端。此過(guò)程需客戶端進(jìn)行代理配置,對(duì)服務(wù)端透明。
  • 反向代理:將服務(wù)端作為網(wǎng)絡(luò)用戶。訪問(wèn)過(guò)程與正向代理相同,不過(guò)此過(guò)程對(duì)客戶端透明,需服務(wù)端進(jìn)行代理配置(也可不配置)。

針對(duì)正向代理和反向代理,分別有不同的代理協(xié)議,即代理服務(wù)器和網(wǎng)絡(luò)用戶之間通信所使用的協(xié)議:

  • 正向代理:
    • http
    • https
    • socks4
    • socks5
    • vpn:就功能而言,vpn 也可以被認(rèn)為是代理
  • 反向代理:
    • tcp
    • udp
    • http
    • https

接下來(lái)我們就說(shuō)說(shuō) http 代理。

http 代理概述

http 代理是正向代理中較為簡(jiǎn)單的代理方式,它使用 http 協(xié)議作為客戶端和代理服務(wù)器的傳輸協(xié)議。

http 代理可以承載 http 協(xié)議,https 協(xié)議,ftp 協(xié)議等等。對(duì)于不同的協(xié)議,客戶端和代理服務(wù)器間的數(shù)據(jù)格式略有不同。

http 協(xié)議

我們先來(lái)看看 http 協(xié)議下客戶端發(fā)送給代理服務(wù)器的 HTTP Header:

// 直接連接GET / HTTP/1.1Host: staight.github.ioConnection: keep-alive
// http 代理GET http://staight.github.io/ HTTP/1.1Host: staight.github.ioProxy-Connection: keep-alive

		

可以看到,http 代理比起直接連接:

  • url 變成完整路徑,/->http://staight.github.io/
  • Connection字段變成Proxy-Connection字段
  • 其余保持原樣

為什么使用完整路徑?

為了識(shí)別目標(biāo)服務(wù)器。如果沒(méi)有完整路徑,且沒(méi)有 Host 字段的話,代理服務(wù)器將無(wú)法得知目標(biāo)服務(wù)器的地址。

為什么使用 Proxy-Connection 字段代替 Connection 字段?

為了兼容使用 HTTP/1.0 協(xié)議的過(guò)時(shí)的代理服務(wù)器。HTTP/1.1 才開(kāi)始有長(zhǎng)連接功能,直接連接的情況下,客戶端發(fā)送的 HTTP Header 中如果有Connection: keep-alive字段,表示使用長(zhǎng)連接和服務(wù)端進(jìn)行 http 通信,但如果中間有過(guò)時(shí)的代理服務(wù)器,該代理服務(wù)器將無(wú)法與客戶端和服務(wù)端進(jìn)行長(zhǎng)連接,造成客戶端和服務(wù)端一直等待,白白浪費(fèi)時(shí)間。

因此使用Proxy-Connection字段代替Connection字段,如果代理服務(wù)器使用 HTTP/1.1 協(xié)議,能夠識(shí)別Proxy-Connection字段,則將該字段轉(zhuǎn)換成Connection再發(fā)送給服務(wù)端;如果不能識(shí)別,直接發(fā)送給服務(wù)端,因?yàn)榉?wù)端也無(wú)法識(shí)別,則使用短連接進(jìn)行通信。

http 代理 http 協(xié)議交互過(guò)程如圖:

79e26f24-d2c2-11ec-bce3-dac502259ad0.png

http 代理 http 協(xié)議

https 協(xié)議

接下來(lái)我們來(lái)看看 https 協(xié)議下,客戶端發(fā)送給代理服務(wù)器的 HTTP Header:

CONNECT staight.github.io:443 HTTP/1.1Host: staight.github.io:443Proxy-Connection: keep-alive

		

如上,https 協(xié)議和 http 協(xié)議相比:

  • 請(qǐng)求方法從GET變成CONNECT
  • url 沒(méi)有 protocol 字段

實(shí)際上,由于 https 下客戶端和服務(wù)端的通信除了開(kāi)頭的協(xié)商以外都是密文,中間的代理服務(wù)器不再承擔(dān)修改 http 報(bào)文再轉(zhuǎn)發(fā)的功能,而是一開(kāi)始就和客戶端協(xié)商好服務(wù)端的地址,隨后的 tcp 密文直接轉(zhuǎn)發(fā)即可。

http 代理 https 協(xié)議交互過(guò)程如圖:

7a2228b2-d2c2-11ec-bce3-dac502259ad0.png

http 代理 https 協(xié)議

代碼實(shí)現(xiàn)

首先,創(chuàng)建 tcp 服務(wù),并且對(duì)于每個(gè) tcp 請(qǐng)求,均調(diào)用 handle 函數(shù):

// tcp 連接,監(jiān)聽(tīng) 8080 端口l, err := net.Listen("tcp", ":8080")if err != nil { log.Panic(err)}
// 死循環(huán),每當(dāng)遇到連接時(shí),調(diào)用 handlefor { client, err := l.Accept() if err != nil {  log.Panic(err) }
 go handle(client)   }

		

然后將獲取的數(shù)據(jù)放入緩沖區(qū):

// 用來(lái)存放客戶端數(shù)據(jù)的緩沖區(qū)var b [1024]byte//從客戶端獲取數(shù)據(jù)n, err := client.Read(b[:])if err != nil { log.Println(err) return   }

		

從緩沖區(qū)讀取 HTTP 請(qǐng)求方法,URL 等信息:

var method, URL, address string// 從客戶端數(shù)據(jù)讀入 method,urlfmt.Sscanf(string(b[:bytes.IndexByte(b[:], '
')]), "%s%s", &method, &URL)hostPortURL, err := url.Parse(URL)if err != nil { log.Println(err) return   }

http 協(xié)議和 https 協(xié)議獲取地址的方式不同,分別處理:

// 如果方法是 CONNECT,則為 https 協(xié)議if method == "CONNECT" { address = hostPortURL.Scheme + ":" + hostPortURL.Opaque} else { //否則為 http 協(xié)議 address = hostPortURL.Host // 如果 host 不帶端口,則默認(rèn)為 80 if strings.Index(hostPortURL.Host, ":") == -1 { //host 不帶端口, 默認(rèn) 80  address = hostPortURL.Host + ":80" }   }

		

用獲取到的地址向服務(wù)端發(fā)起請(qǐng)求。如果是 http 協(xié)議,將客戶端的請(qǐng)求直接轉(zhuǎn)發(fā)給服務(wù)端;如果是 https 協(xié)議,發(fā)送 http 響應(yīng):

//獲得了請(qǐng)求的 host 和 port,向服務(wù)端發(fā)起 tcp 連接server, err := net.Dial("tcp", address)if err != nil { log.Println(err) return}//如果使用 https 協(xié)議,需先向客戶端表示連接建立完畢if method == "CONNECT" { fmt.Fprint(client, "HTTP/1.1 200 Connection established

")} else { //如果使用 http 協(xié)議,需將從客戶端得到的 http 請(qǐng)求轉(zhuǎn)發(fā)給服務(wù)端 server.Write(b[:n])   }

		

最后,將所有客戶端的請(qǐng)求轉(zhuǎn)發(fā)至服務(wù)端,將所有服務(wù)端的響應(yīng)轉(zhuǎn)發(fā)給客戶端:

//將客戶端的請(qǐng)求轉(zhuǎn)發(fā)至服務(wù)端,將服務(wù)端的響應(yīng)轉(zhuǎn)發(fā)給客戶端。io.Copy 為阻塞函數(shù),文件描述符不關(guān)閉就不停止go io.Copy(server, client)   io.Copy(client, server

		

完整的源代碼:

package main
import ( "bytes" "fmt" "io" "log" "net" "net/url" "strings")
func main() { // tcp 連接,監(jiān)聽(tīng) 8080 端口 l, err := net.Listen("tcp", ":8080") if err != nil {  log.Panic(err) }
 // 死循環(huán),每當(dāng)遇到連接時(shí),調(diào)用 handle for {  client, err := l.Accept()  if err != nil {   log.Panic(err)  }
  go handle(client) }}
func handle(client net.Conn) { if client == nil {  return } defer client.Close()
 log.Printf("remote addr: %v
", client.RemoteAddr())
 // 用來(lái)存放客戶端數(shù)據(jù)的緩沖區(qū) var b [1024]byte //從客戶端獲取數(shù)據(jù) n, err := client.Read(b[:]) if err != nil {  log.Println(err)  return }
 var method, URL, address string // 從客戶端數(shù)據(jù)讀入 method,url fmt.Sscanf(string(b[:bytes.IndexByte(b[:], '
')]), "%s%s", &method, &URL) hostPortURL, err := url.Parse(URL) if err != nil {  log.Println(err)  return }
 // 如果方法是 CONNECT,則為 https 協(xié)議 if method == "CONNECT" {  address = hostPortURL.Scheme + ":" + hostPortURL.Opaque } else { //否則為 http 協(xié)議  address = hostPortURL.Host  // 如果 host 不帶端口,則默認(rèn)為 80  if strings.Index(hostPortURL.Host, ":") == -1 { //host 不帶端口, 默認(rèn) 80   address = hostPortURL.Host + ":80"  } }
 //獲得了請(qǐng)求的 host 和 port,向服務(wù)端發(fā)起 tcp 連接 server, err := net.Dial("tcp", address) if err != nil {  log.Println(err)  return } //如果使用 https 協(xié)議,需先向客戶端表示連接建立完畢 if method == "CONNECT" {  fmt.Fprint(client, "HTTP/1.1 200 Connection established

") } else { //如果使用 http 協(xié)議,需將從客戶端得到的 http 請(qǐng)求轉(zhuǎn)發(fā)給服務(wù)端  server.Write(b[:n]) }
 //將客戶端的請(qǐng)求轉(zhuǎn)發(fā)至服務(wù)端,將服務(wù)端的響應(yīng)轉(zhuǎn)發(fā)給客戶端。io.Copy 為阻塞函數(shù),文件描述符不關(guān)閉就不停止 go io.Copy(server, client) io.Copy(client, server)}

		

添加代理,然后運(yùn)行:

7ab1f91a-d2c2-11ec-bce3-dac502259ad0.png

7aeba5de-d2c2-11ec-bce3-dac502259ad0.png




原文標(biāo)題:Golang 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 http 代理

文章出處:【微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 服務(wù)器
    +關(guān)注

    關(guān)注

    13

    文章

    9795

    瀏覽量

    88009
  • HTTP
    +關(guān)注

    關(guān)注

    0

    文章

    525

    瀏覽量

    33534
  • 代理
    +關(guān)注

    關(guān)注

    1

    文章

    44

    瀏覽量

    11343

原文標(biāo)題:Golang 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 http 代理

文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    內(nèi)存管理概述及原理

    記錄一下,方便以后翻閱~主要內(nèi)容:1) 內(nèi)存管理概述及原理;2)相關(guān)實(shí)驗(yàn)代碼解讀。官方資料:《STM32中文參考手冊(cè)_V10》-第19章 靈活的靜態(tài)存儲(chǔ)器控制器(FSMC)。實(shí)驗(yàn)要求:系統(tǒng)啟動(dòng)后
    發(fā)表于 02-23 06:15

    icepeak教程概述及工程應(yīng)用

    icepeak教程概述及工程應(yīng)用
    發(fā)表于 09-16 10:18 ?13次下載
    icepeak教程<b class='flag-5'>概述及</b>工程應(yīng)用

    如何在java代碼中使用HTTP代理IP

    如何在java代碼中使用HTTP代理IP。
    的頭像 發(fā)表于 08-04 15:38 ?2499次閱讀

    python代碼中使用HTTP代理IP,demo注釋清晰

    如何再python代碼中使用HTTP代理IP。 以下代碼主要圍繞第一次接觸HTTP代理IP的py
    的頭像 發(fā)表于 08-04 15:40 ?1247次閱讀

    如何在python代碼中使用HTTP代理IP

    如何在python代碼中使用HTTP代理IP。
    的頭像 發(fā)表于 08-04 15:46 ?1499次閱讀

    如何在PHP代碼中使用HTTP代理IP

    如何在PHP代碼中使用HTTP代理IP。
    的頭像 發(fā)表于 08-04 16:08 ?2701次閱讀

    go語(yǔ)言代碼中使用HTTP代理IP的方法

    如何在go語(yǔ)言代碼中使用HTTP代理IP。
    的頭像 發(fā)表于 08-04 16:13 ?3477次閱讀

    如何在易e語(yǔ)言代碼中使用HTTP代理IP

    如何在易e語(yǔ)言代碼中使用HTTP代理IP,示例代碼demo直接可用(步驟注釋清晰)
    的頭像 發(fā)表于 08-05 16:29 ?7352次閱讀

    如何在c語(yǔ)言代碼中使用HTTP代理IP

    如何在c語(yǔ)言代碼中使用HTTP代理IP,示例代碼demo直接可用(步驟注釋清晰)
    的頭像 發(fā)表于 08-05 16:31 ?2615次閱讀

    如何在c#語(yǔ)言代碼中使用HTTP代理IP

    如何在c#語(yǔ)言代碼中使用HTTP代理IP,示例代碼demo直接可用(步驟注釋清晰)
    的頭像 發(fā)表于 08-05 16:33 ?3024次閱讀

    c語(yǔ)言中怎么使用HTTP代理

    如何再c語(yǔ)言代碼中使用HTTP代理IP。
    的頭像 發(fā)表于 09-01 14:44 ?1664次閱讀

    python代碼中如何使用HTTP代理

    華益云HTTP代理API有效期是一年,也就是說(shuō)一年內(nèi)這1萬(wàn)IP用完就沒(méi)了,如果你一年都用不完那到時(shí)候剩余IP才會(huì)被清零,對(duì)于調(diào)試代碼來(lái)說(shuō)時(shí)間充足靈活。
    的頭像 發(fā)表于 09-01 14:50 ?1080次閱讀

    python代碼中如何使用HTTP代理

    HTTP代理就是介于瀏覽器和web服務(wù)器之間的一臺(tái)服務(wù)器,連接代理后,瀏覽器不再直接向web服務(wù)器取回網(wǎng)頁(yè),而是向代理服務(wù)器發(fā)出request信號(hào),
    的頭像 發(fā)表于 09-13 09:24 ?1510次閱讀

    如何在python代碼中使用HTTP代理IP

    如何再python代碼中使用HTTP代理IP。
    的頭像 發(fā)表于 09-13 09:25 ?1274次閱讀

    Golang實(shí)現(xiàn)一個(gè)簡(jiǎn)單的http代理

    本文詳細(xì)介紹了Golang 實(shí)現(xiàn) http 代理實(shí)現(xiàn),在實(shí)際業(yè)務(wù)中有需求的同學(xué)可以學(xué)起來(lái)了!
    的頭像 發(fā)表于 04-10 11:29 ?1719次閱讀