Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["proxy_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["proxy.go"],
importpath = "go-common/library/net/http/blademaster/middleware/proxy",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/conf/env:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/metadata:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
go_test(
name = "go_default_xtest",
srcs = ["example_test.go"],
tags = ["automanaged"],
deps = [
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/proxy:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,5 @@
### business/blademaster/proxy
##### Version 1.0.0
1. 完成基本功能与测试

View File

@@ -0,0 +1,5 @@
# Author
zhoujiahui
# Reviewer
maojian

View File

@@ -0,0 +1,7 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- zhoujiahui
reviewers:
- maojian
- zhoujiahui

View File

@@ -0,0 +1,13 @@
#### business/blademaster/proxy
##### 项目简介
blademaster 的 reverse proxy middleware主要用于转发一些 API 请求
##### 编译环境
- **请只用 Golang v1.8.x 以上版本编译执行**
##### 依赖包
- No other dependency

View File

@@ -0,0 +1,49 @@
package proxy_test
import (
"go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/proxy"
)
// This example create several reverse proxy to show how to use proxy middleware.
// We proxy three path to `api.bilibili.com` and return response without any changes.
func Example() {
proxies := map[string]string{
"/index": "http://api.bilibili.com/html/index",
"/ping": "http://api.bilibili.com/api/ping",
"/api/versions": "http://api.bilibili.com/api/web/versions",
}
engine := blademaster.Default()
for path, ep := range proxies {
engine.GET(path, proxy.NewAlways(ep))
}
engine.Run(":18080")
}
// This example create several reverse proxy to show how to use jd proxy middleware.
// The request will be proxied to destination only when request is from specified datacenter.
func ExampleNewZoneProxy() {
proxies := map[string]string{
"/index": "http://api.bilibili.com/html/index",
"/ping": "http://api.bilibili.com/api/ping",
"/api/versions": "http://api.bilibili.com/api/web/versions",
}
engine := blademaster.Default()
// proxy to specified destination
for path, ep := range proxies {
engine.GET(path, proxy.NewZoneProxy("sh004", ep), func(ctx *blademaster.Context) {
ctx.String(200, "Origin")
})
}
// proxy with request path
ug := engine.Group("/update", proxy.NewZoneProxy("sh004", "http://sh001-api.bilibili.com"))
ug.POST("/name", func(ctx *blademaster.Context) {
ctx.String(500, "Should not be accessed")
})
ug.POST("/sign", func(ctx *blademaster.Context) {
ctx.String(500, "Should not be accessed")
})
engine.Run(":18080")
}

View File

@@ -0,0 +1,118 @@
package proxy
import (
"bytes"
"io"
"io/ioutil"
stdlog "log"
"net/http"
"net/http/httputil"
"net/url"
"go-common/library/conf/env"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/metadata"
"github.com/pkg/errors"
)
type endpoint struct {
url *url.URL
proxy *httputil.ReverseProxy
condition func(ctx *bm.Context) bool
}
type logger struct{}
func (logger) Write(p []byte) (int, error) {
log.Warn("%s", string(p))
return len(p), nil
}
func newep(rawurl string, condition func(ctx *bm.Context) bool) *endpoint {
u, err := url.Parse(rawurl)
if err != nil {
panic(errors.Errorf("Invalid URL: %s", rawurl))
}
e := &endpoint{
url: u,
}
e.proxy = &httputil.ReverseProxy{
Director: e.director,
ErrorLog: stdlog.New(logger{}, "bm.proxy: ", stdlog.LstdFlags),
}
e.condition = condition
return e
}
func (e *endpoint) director(req *http.Request) {
req.URL.Scheme = e.url.Scheme
req.URL.Host = e.url.Host
// keep the origin request path
if e.url.Path != "" {
req.URL.Path = e.url.Path
}
body, length := rebuildBody(req)
req.Body = body
req.ContentLength = int64(length)
}
func (e *endpoint) ServeHTTP(ctx *bm.Context) {
req := ctx.Request
ip := metadata.String(ctx, metadata.RemoteIP)
logArgs := []log.D{
log.KV("method", req.Method),
log.KV("ip", ip),
log.KV("path", req.URL.Path),
log.KV("params", req.Form.Encode()),
}
if !e.condition(ctx) {
logArgs = append(logArgs, log.KV("proxied", "false"))
log.Infov(ctx, logArgs...)
return
}
logArgs = append(logArgs, log.KV("proxied", "true"))
log.Infov(ctx, logArgs...)
e.proxy.ServeHTTP(ctx.Writer, ctx.Request)
ctx.Abort()
}
func rebuildBody(req *http.Request) (io.ReadCloser, int) {
// GET request
if req.Body == nil {
return nil, 0
}
// Submit with form
if len(req.PostForm) > 0 {
br := bytes.NewReader([]byte(req.PostForm.Encode()))
return ioutil.NopCloser(br), br.Len()
}
// copy the original body
bodyBytes, _ := ioutil.ReadAll(req.Body)
br := bytes.NewReader(bodyBytes)
return ioutil.NopCloser(br), br.Len()
}
func always(ctx *bm.Context) bool {
return true
}
// NewZoneProxy is
func NewZoneProxy(matchZone, dst string) bm.HandlerFunc {
ep := newep(dst, func(*bm.Context) bool {
if env.Zone == matchZone {
return true
}
return false
})
return ep.ServeHTTP
}
// NewAlways is
func NewAlways(dst string) bm.HandlerFunc {
ep := newep(dst, always)
return ep.ServeHTTP
}

View File

@@ -0,0 +1,170 @@
package proxy
import (
"bytes"
"context"
"net/http"
"net/url"
"sync"
"testing"
"time"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"github.com/stretchr/testify/assert"
)
func init() {
log.Init(nil)
}
func TestProxy(t *testing.T) {
engine := bm.Default()
engine.GET("/icon", NewAlways("http://api.bilibili.com/x/web-interface/index/icon"))
engine.POST("/x/web-interface/archive/like", NewAlways("http://api.bilibili.com"))
go engine.Run(":18080")
defer func() {
engine.Server().Shutdown(context.TODO())
}()
time.Sleep(time.Second)
req, err := http.NewRequest("GET", "http://127.0.0.1:18080/icon", nil)
assert.NoError(t, err)
req.Host = "api.bilibili.com"
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, 200, resp.StatusCode)
// proxy form request
form := url.Values{}
form.Set("arg1", "1")
form.Set("arg2", "2")
req, err = http.NewRequest("POST", "http://127.0.0.1:18080/x/web-interface/archive/like?param=test", bytes.NewReader([]byte(form.Encode())))
assert.NoError(t, err)
req.Host = "api.bilibili.com"
resp, err = http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, 200, resp.StatusCode)
// proxy json request
bs := []byte(`{"arg1": 1, "arg2": 2}`)
req, err = http.NewRequest("POST", "http://127.0.0.1:18080/x/web-interface/archive/like?param=test", bytes.NewReader(bs))
assert.NoError(t, err)
req.Host = "api.bilibili.com"
req.Header.Set("Content-Type", "application/json; charset=utf-8")
resp, err = http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, 200, resp.StatusCode)
}
func TestProxyRace(t *testing.T) {
engine := bm.Default()
engine.GET("/icon", NewAlways("http://api.bilibili.com/x/web-interface/index/icon"))
go engine.Run(":18080")
defer func() {
engine.Server().Shutdown(context.TODO())
}()
time.Sleep(time.Second)
wg := sync.WaitGroup{}
for i := 0; i < 20; i++ {
wg.Add(1)
go func() {
defer wg.Done()
req, err := http.NewRequest("GET", "http://127.0.0.1:18080/icon", nil)
assert.NoError(t, err)
req.Host = "api.bilibili.com"
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, 200, resp.StatusCode)
}()
}
wg.Wait()
}
func TestZoneProxy(t *testing.T) {
engine := bm.Default()
engine.GET("/icon", NewZoneProxy("sh004", "http://api.bilibili.com/x/web-interface/index/icon"), func(ctx *bm.Context) {
ctx.AbortWithStatus(500)
})
engine.GET("/icon2", NewZoneProxy("none", "http://api.bilibili.com/x/web-interface/index/icon2"), func(ctx *bm.Context) {
ctx.AbortWithStatus(200)
})
ug := engine.Group("/update", NewZoneProxy("sh004", "http://api.bilibili.com"))
ug.POST("/name", func(ctx *bm.Context) {
ctx.AbortWithStatus(500)
})
ug.POST("/sign", func(ctx *bm.Context) {
ctx.AbortWithStatus(500)
})
go engine.Run(":18080")
defer func() {
engine.Server().Shutdown(context.TODO())
}()
time.Sleep(time.Second)
req, err := http.NewRequest("GET", "http://127.0.0.1:18080/icon", nil)
assert.NoError(t, err)
req.Host = "api.bilibili.com"
req.Header.Set("X-BILI-SLB", "shjd-out-slb")
resp, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, 200, resp.StatusCode)
req.URL.Path = "/icon2"
resp, err = http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, 200, resp.StatusCode)
req.URL.Path = "/update/name"
resp, err = http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, 200, resp.StatusCode)
req.URL.Path = "/update/sign"
resp, err = http.DefaultClient.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, 200, resp.StatusCode)
}
func BenchmarkProxy(b *testing.B) {
engine := bm.Default()
engine.GET("/icon", NewAlways("http://api.bilibili.com/x/web-interface/index/icon"))
go engine.Run(":18080")
defer func() {
engine.Server().Shutdown(context.TODO())
}()
time.Sleep(time.Second)
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
req, err := http.NewRequest("GET", "http://127.0.0.1:18080/icon", nil)
assert.NoError(b, err)
req.Host = "api.bilibili.com"
resp, err := http.DefaultClient.Do(req)
assert.NoError(b, err)
defer resp.Body.Close()
assert.Equal(b, 200, resp.StatusCode)
}
})
}