Skip to content

Instantly share code, notes, and snippets.

@lenye
Forked from viggin543/rev_proxy.go
Created December 27, 2021 02:30
Show Gist options
  • Save lenye/117829ea7c426a85da44123f47d0c069 to your computer and use it in GitHub Desktop.
Save lenye/117829ea7c426a85da44123f47d0c069 to your computer and use it in GitHub Desktop.
rev_proxy.go
package reverse_proxy
import (
"bytes"
"fmt"
"github.com/viggin/svc-api-gateway/internal/models"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/palantir/stacktrace"
"github.com/sirupsen/logrus"
"io/ioutil"
"net/http"
"net/http/httputil"
"net/url"
"strings"
)
func ReverseProxy(ctx *gin.Context) {
path := ctx.Request.URL.Path
target, err := Target(path)
if err != nil {
fire404Metric()
ctx.AbortWithStatus(http.StatusNotFound)
return
}
if targetUrl, err := url.Parse(target); err != nil {
ctx.AbortWithStatus(http.StatusInternalServerError)
} else {
Proxy(targetUrl).ServeHTTP(ctx.Writer, ctx.Request)
}
}
func fire404Metric() {
models.FailedToFindProxyTarget.Inc()
}
func Target(path string) (string, error) {
parts := strings.Split(strings.TrimPrefix(path, "/"), "/")
if len(parts) <= 1 {
return "", stacktrace.RootCause(fmt.Errorf("failed to parse target host from path: %s", path))
}
targetHost := fmt.Sprintf("svc-%s", parts[1])
targetNamespace := fmt.Sprintf("svc-%s", parts[2])
if targetHost == "" {
return "", stacktrace.RootCause(fmt.Errorf("failed to parse target host from path: %s", path))
}
targetAddr := fmt.Sprintf(
"http://%s.%s:%d/api/%s",
targetHost, targetNamespace, 10000, strings.Join(parts[3:], "/"),
)
return targetAddr, nil
}
func Proxy(address *url.URL) *httputil.ReverseProxy {
p := httputil.NewSingleHostReverseProxy(address)
p.Director = func(request *http.Request) {
request.Host = address.Host
request.URL.Scheme = address.Scheme
request.URL.Host = address.Host
request.URL.Path = address.Path
}
p.ModifyResponse = func(response *http.Response) error {
if response.StatusCode == http.StatusInternalServerError {
u, s := readBody(response)
logrus.Errorf("%s ,req %s ,with error %d, body:%s", u.String(), address, response.StatusCode, s)
response.Body = ioutil.NopCloser(bytes.NewReader([]byte(fmt.Sprintf("error %s", u.String()))))
} else if response.StatusCode > 300 {
_, s := readBody(response)
logrus.Errorf("req %s ,with error %d, body:%s", address, response.StatusCode, s)
response.Body = ioutil.NopCloser(bytes.NewReader([]byte(s)))
}
return nil
}
return p
}
func readBody(response *http.Response) (uuid.UUID, string) {
defer response.Body.Close()
all, _ := ioutil.ReadAll(response.Body)
u := uuid.New()
var s string
if len(all) > 0 {
s = string(all)
}
return u, s
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment