为了账号安全,请及时绑定邮箱和手机立即绑定

使用 Go to Cloud Run 实例的反向代理

使用 Go to Cloud Run 实例的反向代理

Go
慕仙森 2022-10-17 16:08:01
我觉得我已经接近完成这项工作,但到目前为止,我遇到了一个问题,在 Go 中构建一个小型反向代理到 GCP Cloud Run 实例。请求“通过”,但请求的响应是默认的GCP Cloud Run 404。将请求返回到 Cloud Run 时会出现主机标头被忽略,因此请求没有被路由更正。我可能在这里缺少什么?package mainimport (    "log"    "net/http"    "net/http/httputil"    "net/url")const apiUrl = "MY_CLOUD_RUN.a.run.app"func main() {    http.HandleFunc("/", proxy)    log.Fatal(http.ListenAndServe(":8081", nil))}func proxy(res http.ResponseWriter, req *http.Request) {    // gets past CORS checks    if req.Method == http.MethodOptions {        headers := res.Header()        headers.Add("Access-Control-Allow-Origin", "*")        headers.Add("Vary", "Origin")        headers.Add("Vary", "Access-Control-Request-Method")        headers.Add("Vary", "Access-Control-Request-Headers")        headers.Add("Access-Control-Allow-Headers", "*")        headers.Add("Access-Control-Allow-Methods", "GET,HEAD,PUT,PATCH,POST,DELETE")        res.WriteHeader(http.StatusOK)        return    }    p := httputil.NewSingleHostReverseProxy(&url.URL{        Scheme: "http",        Host:   apiUrl,    })    p.Director = func(req *http.Request) {        req.Header.Add("X-Forwarded-Host", req.Host)        req.Header.Add("X-Origin-Host", apiUrl)        req.Header.Add("Host", apiUrl)        req.Header.Add("Access-Control-Allow-Origin", "*")        req.URL.Scheme = "https"        req.URL.Host = apiUrl    }    p.ModifyResponse = func(res *http.Response) error {        res.Header.Set("Access-Control-Allow-Methods", "GET,HEAD,PUT,PATCH,POST,DELETE")        res.Header.Set("Access-Control-Allow-Credentials", "true")        res.Header.Set("Access-Control-Allow-Origin", "*")        res.Header.Set("Access-Control-Allow-Headers", "*")        return nil    }    p.ServeHTTP(res, req)}
查看完整描述

1 回答

?
莫回无

TA贡献1865条经验 获得超7个赞

这比最初的最初写的要复杂一些,但我们最终得到的结果如下。


package main


import (

    "context"

    "fmt"

    "log"

    "net/http"

    "net/http/httputil"

    "net/url"

    "os"

    "os/signal"

    "time"


    "golang.org/x/oauth2"

    "google.golang.org/api/idtoken"

)


var port = ":8080"

var backend = "[CLOUD_RUN_INSTANCE_TO_PROXY].a.run.app"


func main() {

    logger := log.New(os.Stdout, "proxy: ", log.LstdFlags)

    logger.Println(fmt.Sprintf("Proxy server is starting for: %s on port: %s", backend, port))


    router := http.NewServeMux()

    router.Handle("/", proxyHandler())


    server := &http.Server{

        Addr:         port,

        Handler:      logging(logger)(router),

        ErrorLog:     logger,

        ReadTimeout:  30 * time.Second,

        WriteTimeout: 30 * time.Second,

        IdleTimeout:  15 * time.Second,

    }


    done := make(chan bool)

    quit := make(chan os.Signal, 1)

    signal.Notify(quit, os.Interrupt)


    go func() {

        <-quit

        logger.Println("Proxy server is shutting down...")


        ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)

        defer cancel()


        server.SetKeepAlivesEnabled(false)

        if err := server.Shutdown(ctx); err != nil {

            logger.Fatalf("Could not gracefully shutdown the server: %v\n", err)

        }

        close(done)

    }()


    if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {

        logger.Fatalf("Could not listen on %s: %v\n", port, err)

    }


    <-done

    logger.Println("Server stopped")

}


func proxyHandler() http.Handler {

    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

        if r.Method == http.MethodOptions {

            headers := w.Header()

            headers.Add("Access-Control-Allow-Origin", "*")

            headers.Add("Access-Control-Allow-Headers", "*")

            headers.Add("Access-Control-Allow-Methods", "GET,HEAD,PUT,PATCH,POST,DELETE")

            w.WriteHeader(http.StatusOK)

            return

        }


        path := fmt.Sprintf("https://%s%s", backend, r.RequestURI)

        at, _ := idTokenTokenSource(path)


        p := httputil.NewSingleHostReverseProxy(&url.URL{

            Scheme: "https",

            Host:   backend,

        })

        p.Director = func(r *http.Request) {

            if at != nil {

                at.SetAuthHeader(r)

            }

        }

        p.ModifyResponse = func(res *http.Response) error {

            res.Header.Set("Access-Control-Allow-Methods", "GET,HEAD,PUT,PATCH,POST,DELETE")

            res.Header.Set("Access-Control-Allow-Credentials", "true")

            res.Header.Set("Access-Control-Allow-Origin", "*")

            res.Header.Set("Access-Control-Allow-Headers", "*")

            return nil

        }


        r.URL.Scheme = "https"

        r.URL.Host = backend

        r.Header.Set("X-Forwarded-Host", r.Header.Get("Host"))

        r.Host = backend


        if at != nil {

            at.SetAuthHeader(r)

        }


        p.ServeHTTP(w, r)

    })

}


func logging(l *log.Logger) func(http.Handler) http.Handler {

    return func(next http.Handler) http.Handler {

        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

            defer func() {

                requestId := r.Header.Get("X-Request-Id")

                if requestId == "" {

                    requestId = fmt.Sprintf("%d", time.Now().UnixNano())

                }

                w.Header().Set("X-Request-Id", requestId)

                l.Println(requestId, r.Method, r.URL.Path, r.RemoteAddr, r.UserAgent())

            }()


            next.ServeHTTP(w, r)

        })

    }

}


func idTokenTokenSource(audience string) (*oauth2.Token, error) {

    ts, err := idtoken.NewTokenSource(context.Background(), audience)

    if err != nil {

        return nil, err

    }


    t, err := ts.Token()

    if err != nil {

        return nil, err

    }


    return t, nil

}


一些正常关闭、http 设置和日志记录的很大一部分来自:https ://gist.github.com/enricofoltran/10b4a980cd07cb02836f70a4ab3e72d7


查看完整回答
反对 回复 2022-10-17
  • 1 回答
  • 0 关注
  • 70 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信