• go语言 反向代理


    实现简单的http反向代理 还没有加入负载均衡 新手推荐

    下游服务器代码

    package main
    
    import (
    	"fmt"
    	"io"
    	"log"
    	"net/http"
    	"os"
    	"os/signal"
    	"syscall"
    	"time"
    )
    
    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)
    	mux.HandleFunc("/test_http_string/test_http_string/aaa", r.TimeoutHandler)
    	server := &http.Server{
    		Addr:         r.Addr,
    		WriteTimeout: time.Second * 3,
    		Handler:      mux,
    	}
    	go func() {
    		log.Fatal(server.ListenAndServe())
    	}()
    }
    
    func (r *RealServer) HelloHandler(w http.ResponseWriter, req *http.Request) {
    
    	upath := fmt.Sprintf("http://%s%s\n", r.Addr, req.URL.Path)
    	realIP := fmt.Sprintf("RemoteAddr=%s,X-Forwarded-For=%v,X-Real-Ip=%v\n", req.RemoteAddr, req.Header.Get("X-Forwarded-For"), req.Header.Get("X-Real-Ip"))
    	header := fmt.Sprintf("headers =%v\n", req.Header)
    	io.WriteString(w, upath)
    	io.WriteString(w, realIP)
    	io.WriteString(w, header)
    
    }
    
    func (r *RealServer) ErrorHandler(w http.ResponseWriter, req *http.Request) {
    	upath := "error handler"
    	w.WriteHeader(500)
    	io.WriteString(w, upath)
    }
    
    func (r *RealServer) TimeoutHandler(w http.ResponseWriter, req *http.Request) {
    	time.Sleep(6 * time.Second)
    	upath := "timeout handler"
    	w.WriteHeader(200)
    	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
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    反向代理代码

    主要的思路是拿到httputil.ReverseProxy,修改里面内置的一些方法
    该结构体有ServeHttp接口 因此可以直接使用到funhandlc中

    package main
    
    import (
    	"bytes"
    	"errors"
    	"fmt"
    	"io"
    	"log"
    	"net/http"
    	"net/http/httputil"
    	"net/url"
    	"regexp"
    	"strings"
    )
    
    // 当前服务器地址
    var addr = "127.0.0.1:2222"
    
    func main() {
    
    	//设置下游的地址
    	rs1 := "http://127.0.0.1:2003"
    	url1, err := url.Parse(rs1)
    	if err != nil {
    		log.Println(err)
    	}
    
    	proxy := NewSingleHostReverseProxy(url1)
    	log.Println("Starting httpserver at " + addr)
    	log.Fatal(http.ListenAndServe(addr, proxy))
    }
    
    func NewSingleHostReverseProxy(target *url.URL) *httputil.ReverseProxy {
    	targetQuery := target.RawQuery
    
    	//设置新的请求信息
    	director := func(req *http.Request) {
    		//url_rewrite
    		//127.0.0.1:2002/dir/abc ==> 127.0.0.1:2003/base/abc ??
    		//127.0.0.1:2002/dir/abc ==> 127.0.0.1:2002/abc
    		//127.0.0.1:2002/abc ==> 127.0.0.1:2003/base/abc
    		re, _ := regexp.Compile("^/dir(.*)")
    		req.URL.Path = re.ReplaceAllString(req.URL.Path, "$1")
    
    		req.URL.Scheme = target.Scheme
    		req.URL.Host = target.Host
    
    		//target.Path : /base
    		//req.URL.Path : /dir
    		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 {
    			req.Header.Set("User-Agent", "")
    		}
    	}
    
    	//修改返回值
    	modifyFunc := func(res *http.Response) error {
    		if res.StatusCode != 200 {
    			return errors.New("error statusCode")
    
    		}
    		oldPayload, err := io.ReadAll(res.Body)
    		if err != nil {
    			return err
    		}
    		newPayLoad := []byte("hello " + string(oldPayload))
    		res.Body = io.NopCloser(bytes.NewBuffer(newPayLoad))
    		res.ContentLength = int64(len(newPayLoad))
    		res.Header.Set("Content-Length", fmt.Sprint(len(newPayLoad)))
    		return nil
    	}
    	//错误处理
    	errorHandler := func(res http.ResponseWriter, req *http.Request, err error) {
    		res.Write([]byte(err.Error()))
    	}
    	return &httputil.ReverseProxy{Director: director, ModifyResponse: modifyFunc, ErrorHandler: errorHandler}
    }
    
    // 重新组合URL
    func singleJoiningSlash(a, b string) string {
    	aslash := strings.HasSuffix(a, "/")
    	bslash := strings.HasPrefix(b, "/")
    	switch {
    	case aslash && bslash:
    		return a + b[1:]
    	case !aslash && !bslash:
    		return a + "/" + b
    	}
    	return a + b
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95

    通过反向代理修改返回结果 添加了hello

    oldPayload, err := io.ReadAll(res.Body)
    		if err != nil {
    			return err
    		}
    		newPayLoad := []byte("hello " + string(oldPayload))
    		res.Body = io.NopCloser(bytes.NewBuffer(newPayLoad))
    		res.ContentLength = int64(len(newPayLoad))
    		res.Header.Set("Content-Length", fmt.Sprint(len(newPayLoad)))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    X-Forwarded-For 头部

    记录整个代理过程的IP
    可能会被仿造 也就是发送请求的时候改变X-Forwarded-For的内容
    在这里插入图片描述

    X-Real-IP 头部

    拿到请求的真实服务器的IP
    每次代理都会被覆盖,只需要在第一层代理设置转发
    不会被伪造
    在这里插入图片描述

  • 相关阅读:
    XXE XML外部实体注入
    2024 年(第 12 届)“泰迪杯”数据挖掘挑战赛—— C 题:竞赛论文的辅助自动评阅完整思路与源代码分享
    微服务从代码到k8s部署应有尽有系列(十三、服务监控)
    瑞芯微RK3568|SDK开发之环境安装及编译操作
    使用流水线插件实现持续集成、持续部署
    实现登陆模块时Cookie,Session,Token的理解
    前端学习笔记--React
    一文让你理解Linux权限问题
    BUG记录:springMVC引入vue报错---not defined
    Spring-Bean的生命周期
  • 原文地址:https://blog.csdn.net/weixin_43898670/article/details/133096151