Skip to content

Instantly share code, notes, and snippets.

@viggin543
Created June 2, 2021 08:50
Show Gist options
  • Select an option

  • Save viggin543/acbfcc8be071041f719f9268ddd79076 to your computer and use it in GitHub Desktop.

Select an option

Save viggin543/acbfcc8be071041f719f9268ddd79076 to your computer and use it in GitHub Desktop.

Revisions

  1. Domrev Igor renamed this gist Jun 2, 2021. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  2. Domrev Igor created this gist Jun 2, 2021.
    86 changes: 86 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,86 @@
    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
    }