概念
正向代理
正向代理是一种客户端代理技术,帮助客户端访问无法访问的服务器资源,可以隐藏客户端的真实ip
反向代理
服务器端的代理技术,帮助服务器负载均衡、缓存、安全校验,可以隐藏服务器的真实ip
正向代理实现
实现原理:正向代理服务器接收到客户端到request请求,把request拷贝为自己的request发送给服务器,并把服务器返回的数据包装给客户端
type Pxy struct {
}
func (p *Pxy) ServeHTTP(rw http.ResponseWriter,req *http.Request) {
fmt.Printf("正向代理接收到请求:%s %s %s\n",req.Method,req.Host,req.RemoteAddr)
transport:=http.DefaultTransport
//浅拷贝
outReq:=new (http.Request)
*outReq = *req
if clientIP,_,err :=net.SplitHostPort(req.RemoteAddr); err==nil{
if prior,ok:=outReq.Header["X-Forwarded-For"];ok{
clientIP = strings.Join(prior,", ")+", "+clientIP
}
outReq.Header.Set("X-Forwarded-For",clientIP)
}
//把消息利用该正向代理服务器发送给下游
res,err:=transport.RoundTrip(outReq)
if err!=nil{
rw.WriteHeader(http.StatusBadGateway)
return
}
//把下游得到的数据返回给上游
//复制头部信息
for key,value:=range res.Header{
for _,v := range value{
rw.Header().Add(key,v)
}
}
rw.WriteHeader(res.StatusCode)
io.Copy(rw,res.Body)
res.Body.Close()
}
func main() {
fmt.Println("服务器正向代理启动:8080")
http.Handle("/",&Pxy{})
http.ListenAndServe("0.0.0.0:8080",nil)
}
这段代码涉及的知识点:
深拷贝和浅拷贝:
浅拷贝是指只拷贝了引用并没有拷贝内容,b浅拷贝了a,那么a和b的内存地址是一样的,a的值改变那么b的值也会改变
a := temp{
1,2,
}
go实现浅拷贝:
//浅拷贝
b := &a
go实现深拷贝
//深拷贝
c:=a
最终测试结果
a的地址为0xc00008a010,值为(1,2)
b的地址为0xc00008a010,值为(1,2)
c的地址为0xc00008a050,值为(1,2)
修改a的值为(3,4)
a的地址为0xc00008a010,值为(3,4)
b的地址为0xc00008a010,值为(3,4)
c的地址为0xc00008a050,值为(1,2)
反向代理
实现原理:客户端发送请求到反向代理服务器,反向代理服务器将请求转发给其他到实现了功能的真正的服务器,从此可以隐藏服务器的真实地址
创建真实服务器:
type RealServer struct {
Addr string
}
func (r * RealServer) Run() {
log.Println("Starting httpServer at "+r.Addr)
mux :=http.NewServeMux()
mux.HandleFunc("/",r.HelloHandler)
mux.HandleFunc("/base/error",r.ErrorHandler)
server:=http.Server{
Addr:r.Addr,
WriteTimeout:3*time.Second,
Handler:mux,
}
go func() {
log.Fatal(server.ListenAndServe())
}()
}
func (r *RealServer) HelloHandler(w http.ResponseWriter ,req *http.Request) {
//127.0.0.1:8080/abc?daw=1
//r.Addr = 127.0.0.1:8080
//req.URL.Path = /abc
upath:=fmt.Sprintf("客户端的地址为:http://%s%s\n",r.Addr,req.URL.Path)
io.WriteString(w,upath)
}
func (r *RealServer) ErrorHandler(w http.ResponseWriter ,req *http.Request) {
upath:="error handler"
w.WriteHeader(500)
io.WriteString(w,upath)
}
func main() {
rs1 := &RealServer{Addr:"127.0.0.1:2003"}
rs1.Run()
rs2 := &RealServer{Addr:"127.0.0.1:2004"}
rs2.Run()
//监听关闭信号
quit := make(chan os.Signal)
signal.Notify(quit,syscall.SIGINT,syscall.SIGTERM)
<-quit
}
创建反向代理服务器,并绑定127.0.0.1:2003作为代理转发服务器
var(
provy_addr = "http://127.0.0.1:2003"
port = "2002"
)
func handler (w http.ResponseWriter,r *http.Request) {
//解析代理地址,更改请求体的协议和主机
proxy,err:=url.Parse(provy_addr)
r.URL.Scheme= proxy.Scheme
r.URL.Host = proxy.Host
if err!=nil{
log.Println(err)
return
}
//请求下游
transport:=http.DefaultTransport
resp,err:=transport.RoundTrip(r)
if err!=nil{
log.Println(err)
return
}
//把下游请求内容返回给上游
for k,vv:=range resp.Header{
for _,v:=range vv{
w.Header().Add(k,v)
}
}
defer resp.Body.Close()
bufio.NewReader(resp.Body).WriteTo(w)
}
func main() {
http.HandleFunc("/",handler)
log.Println("转发代理服务器开启 port "+port)
err:=http.ListenAndServe(":"+port,nil)
if err!=nil{
log.Fatal(err)
}
}
反向代理修改返回的reponse内容
如果我们在反向代理服务器返回结果时需要修改一些内容时,比如更新时间戳等,那么就需要修改NewSingleHostReverseProxy函数并定义ModifyResponse函数了,在NewSingleHostReverseProxy函数中我们需要在调用httputil.ReverseProxy的时候修改我们的ModifyResponse
其中ModifyResponse的作用是修改拷贝response的body的内容,并修改,最后回写到response.body,最后还需要修改contentLength的值,才能正确解析response
func NewSingleHostReverseProxy(target *url.URL) *httputil.ReverseProxy {
targetQuery := target.RawQuery
director := func(req *http.Request) {
req.URL.Scheme = target.Scheme
req.URL.Host = target.Host
req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
if targetQuery == "" || req.URL.RawQuery == "" {
req.URL.RawQuery = targetQuery + req.URL.RawQuery
} else {
req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
}
if _, ok := req.Header["User-Agent"]; !ok {
// explicitly disable User-Agent so it's not set to default value
req.Header.Set("User-Agent", "")
}
}
//修改返回内容
modifyFunc:=func(res *http.Response) error{
if res.StatusCode !=200{
oldPayload,err:=ioutil.ReadAll(res.Body)
if err!=nil{
return err
}
newPayLoad := []byte("hello "+string(oldPayload))
//回写到res
res.Body =ioutil.NopCloser(bytes.NewBuffer(newPayLoad))
//修改contentLength
res.ContentLength = int64(len(newPayLoad))
res.Header.Set("Content-Length",fmt.Sprint(len(newPayLoad)))
}
return nil
}
return &httputil.ReverseProxy{Director: director,ModifyResponse:modifyFunc}
}
简单实现了反向代理服务器,但是没有实现轮询转发的功能,以后的博文会进行改进,实现轮询转发的功能