123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761 |
- package blademaster
- import (
- "bytes"
- "context"
- "crypto/tls"
- "encoding/json"
- "encoding/xml"
- "fmt"
- "io/ioutil"
- "net/http"
- "os"
- "path"
- "path/filepath"
- "regexp"
- "strconv"
- "strings"
- "sync"
- "sync/atomic"
- "testing"
- "time"
- "go-common/library/ecode"
- "go-common/library/log"
- "go-common/library/net/http/blademaster/binding"
- "go-common/library/net/http/blademaster/render"
- "go-common/library/net/http/blademaster/tests"
- "go-common/library/net/metadata"
- xtime "go-common/library/time"
- "github.com/gogo/protobuf/proto"
- "github.com/pkg/errors"
- "github.com/stretchr/testify/assert"
- )
- var (
- sonce sync.Once
- once sync.Once
- curEngine atomic.Value
- ByteContent = []byte(`<html>
- <meta http-equiv="refresh" content="0;uri=http://www.bilibili.com/">
- </html>`)
- CertPEM = `-----BEGIN CERTIFICATE-----
- MIIDJzCCAg8CCQDHIbk1Vp7UbzANBgkqhkiG9w0BAQsFADCBkDELMAkGA1UEBhMC
- Q04xETAPBgNVBAgMCFNoYW5naGFpMREwDwYDVQQHDAhTaGFuZ2hhaTERMA8GA1UE
- CgwIYmlsaWJpbGkxETAPBgNVBAsMCGJpbGliaWxpMRUwEwYDVQQDDAxiaWxpYmls
- aS5jb20xHjAcBgkqhkiG9w0BCQEWD2l0QGJpbGliaWxpLmNvbTAeFw0xNzExMDcw
- NDI1MzJaFw0xODExMDcwNDI1MzJaMBoxGDAWBgNVBAMMD2xvY2FsaG9zdDoxODA4
- MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALNKS9f7sEE+xx2SiIgI
- tfmUVi3HkbWRWiZxZ82gRQnYUvgNsQUenyMH4ukViC7rEqBomscP0YjWhIvunhKe
- RzXqWxZyKF86lL0+n1X+USESWdMQe8nYhCAwTE3JIykBrqjEiYMSI5TLwQrqFUJ9
- nd7EywdlUgolJFO2pbltU9a8stlto9OOLXo5veb30nAW5tnDF5Q1jlKBRpGV4+Wy
- 3Tn9V9a6mPaoLQOLQzLWfjIWok0UKdYOWZUwmfboFloI0J0VA8Dn3qr2VGEucUG4
- C5pIzV7/ke0Ymca8H2O1Gt5jrhbieY1XLP7NEoic1xdKTa6TLbReWTUEfqErCD3X
- b28CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAS+tB9PDV0tzFYtVzaWGrqypfnbEC
- l5XoT6kRKn3Pf74MQQDMmuPCNZqf8aznx7+inzf4xeMsM68mkbaWvDkD2W8rSn47
- tnFZNPLzlP5sXrt1RgfEK1azVOqX+PSNqDxFetB/smvsVr0EduX0tcmSNMChzx7I
- Igy/I93TVf/hzu3MubZlCjTIQEvfek/Qc/eei7SQYS3dauSKaLfOwMdan9U2gmSr
- byb4f0vI1wuBSAEceJMrHcPGNgibAUMBMdSOYljYxSgmC0gFW68aD3gdn1Z/KOyd
- r1VaEkBHRoXvVUYPrFDFYO4nP65aZBLWgIn/EtilNektlAZhljSzk6bWXA==
- -----END CERTIFICATE-----
- `
- KeyPEM = `-----BEGIN RSA PRIVATE KEY-----
- MIIEowIBAAKCAQEAs0pL1/uwQT7HHZKIiAi1+ZRWLceRtZFaJnFnzaBFCdhS+A2x
- BR6fIwfi6RWILusSoGiaxw/RiNaEi+6eEp5HNepbFnIoXzqUvT6fVf5RIRJZ0xB7
- ydiEIDBMTckjKQGuqMSJgxIjlMvBCuoVQn2d3sTLB2VSCiUkU7aluW1T1ryy2W2j
- 044tejm95vfScBbm2cMXlDWOUoFGkZXj5bLdOf1X1rqY9qgtA4tDMtZ+MhaiTRQp
- 1g5ZlTCZ9ugWWgjQnRUDwOfeqvZUYS5xQbgLmkjNXv+R7RiZxrwfY7Ua3mOuFuJ5
- jVcs/s0SiJzXF0pNrpMttF5ZNQR+oSsIPddvbwIDAQABAoIBACLW2hwTNXHIQCr3
- 8X31q17fO0vUDvVoVEtKGDC15xR9q8m152MmSygkfSxr2bW8SjdPfFwYL9BWVxVV
- /fOCPDY23xJihoPSv1py08WDEMLLbRb9igB0CWCz4e/vmNx8DjOPVWVZ3f4pBc8Y
- I59zB31lYkqCnsmH5CI8SMHag8MjPmzc7TtoROUbV0rcyoOx/JwrgwuGsC/UP9GY
- Oj021xiLcwP5qD4sBIuXxIPx9zwCDowujjBQ3ViSgcQk92Z1fsPMBvKYUuP2hHcZ
- M/Dnuzz/OyzfP0u3Aq+VpQlXHVmEptU6kfFjK8X3J9tWr7PAvAaFqIs2xL5p6gz8
- q50gfzkCgYEA5Wa89L30UD17s+2RT2Slg5fmSvn2gO0Dz2Dhav/G2UPfbW+22rAC
- iotIfnF4IuA6QQX6tW5L/nVbLYQUtWNxzWsYCQAbRrBQi1BBfh0EOli52ZicuWd3
- 6rOqeOzqsXRdyrwVnpfKf1Hdh7Dc++zG920ktXbC33jgGDLmSLxnysMCgYEAyBQf
- du67z3//yOt3z+zl1Tug3t/ioPSWijrlUtsqbRuOWvxRwUtyOB4Tm171j5sWPiJu
- YOisOvjrRI2L9xjdtP4HTAiO4//YiNkCHdFiyHTzMqb1RBbHVQZ6eFmjvdmrgIkG
- 4vXt3mZ1kQY264APB99W+BKFtLDPvaz+Hgy9xeUCgYEAhPMcA8OrOm3Hqam/k4HD
- Ixb/0ug3YtT6Zk/BlN+UAQsDDEu4b9meP2klpJJii+PkHxc2C7xWsqyVITXxQobV
- x7WPgnfbVwaMR5FFw69RafdODrwR6Kn8p7tkyxyTkDDewsZqyTUzmMJ7X06zZBX/
- 4hoRMlIX8qf9SEkHiZQXmz0CgYAGNamsVUh67iwQHk6/o0iWz5z0jdpIyI6Lh7xq
- T+cHL93BMSeQajkHSNeu8MmKRXPxRbxLQa1mvyb+H66CYsEuxtuPHozgwqYDyUhp
- iIAaXJbXsZrXHCXfm63dYlrUn5bVDGusS5mwV1m6wIif0n+k7OeUF28S5pHr/xx7
- 7kVNiQKBgDeXBYkZIRlm0PwtmZcoAU9bVK2m7MnLj6G/MebpzSwSFbbFhxZnnwS9
- EOUtzcssIiFpJiN+ZKFOV+9S9ylYFSYlxGwrWvE3+nmzm8X04tsYhvjO5Q1w1Egs
- U31DFsHpaXEAfBc7ugLimKKKbptJJDPzUfUtXIltCxI9YCzFFRXY
- -----END RSA PRIVATE KEY-----
- `
- )
- const (
- SockAddr = "localhost:18080"
- AdHocSockAddr = "localhost:18081"
- )
- func cleanup(dir string) {
- matches, err := filepath.Glob(dir)
- if err != nil {
- return
- }
- for _, p := range matches {
- os.RemoveAll(p)
- }
- }
- func setupLogging() {
- dir, err := ioutil.TempDir("", "blademaster")
- defer func() {
- baseDir := path.Dir(dir)
- cleanup(path.Join(baseDir, "blademaster*"))
- }()
- if err != nil {
- panic(err)
- }
- for l, f := range map[string]string{
- "info.log": "/dev/stdout",
- "warning.log": "/dev/stdout",
- "error.log": "/dev/stderr",
- } {
- p := filepath.Join(dir, l)
- if err := os.Symlink(f, p); err != nil {
- panic(err)
- }
- }
- logConf := &log.Config{
- Dir: dir,
- }
- log.Init(logConf)
- }
- func init() {
- if os.Getenv("TEST_LOGGING") == "1" {
- setupLogging()
- }
- }
- func startTestServer() {
- _httpDSN = "tcp://0.0.0.0:18090/?maxlisten=20000&timeout=1s&readtimeout=1s&writetimeout=1s"
- engine := NewServer(nil)
- engine.GET("/test", func(ctx *Context) {
- ctx.JSON("", nil)
- })
- engine.GET("/mirrortest", func(ctx *Context) {
- ctx.JSON(strconv.FormatBool(metadata.Bool(ctx, metadata.Mirror)), nil)
- })
- engine.Start()
- }
- func TestServer2(t *testing.T) {
- sonce.Do(startTestServer)
- resp, err := http.Get("http://localhost:18090/test")
- if err != nil {
- t.Errorf("HTTPServ: get error(%v)", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Errorf("http.Get get error code:%d", resp.StatusCode)
- }
- resp.Body.Close()
- }
- func BenchmarkServer2(b *testing.B) {
- once.Do(startServer)
- b.ResetTimer()
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- resp, err := http.Get("http://localhost:18090/test")
- if err != nil {
- b.Errorf("HTTPServ: get error(%v)", err)
- continue
- }
- if resp.StatusCode != http.StatusOK {
- b.Errorf("HTTPServ: get error status code:%d", resp.StatusCode)
- }
- resp.Body.Close()
- }
- })
- }
- func TestDSNParse(t *testing.T) {
- conf := parseDSN("tcp://0.0.0.0:18090/?maxlisten=20000&timeout=1s&readTimeout=1s&writeTimeout=1s")
- assert.Equal(t, ServerConfig{
- Network: "tcp",
- Addr: "0.0.0.0:18090",
- Timeout: xtime.Duration(time.Second),
- ReadTimeout: xtime.Duration(time.Second),
- WriteTimeout: xtime.Duration(time.Second),
- }, *conf)
- conf = parseDSN("unix:///temp/bm.sock?maxlisten=20000&timeout=2s&readTimeout=1s&writeTimeout=1s")
- assert.Equal(t, ServerConfig{
- Network: "unix",
- Addr: "/temp/bm.sock",
- Timeout: xtime.Duration(time.Second * 2),
- ReadTimeout: xtime.Duration(time.Second),
- WriteTimeout: xtime.Duration(time.Second),
- }, *conf)
- }
- type Timestamp struct {
- Now int64 `json:"now"`
- }
- type Archive struct {
- Mids []int64 `json:"mids" form:"mids,split" validate:"dive,gt=1,required"`
- Cid int `json:"cid" form:"cid" validate:"max=10,min=1"`
- Title string `json:"title" form:"title" validate:"required"`
- Content string `json:"content" form:"content"`
- }
- type DevResp struct {
- Code int `json:"code"`
- Data *Device `json:"data"`
- }
- func uri(base, path string) string {
- return fmt.Sprintf("%s://%s%s", "http", base, path)
- }
- func now() Timestamp {
- return Timestamp{
- Now: time.Now().Unix(),
- }
- }
- func setupHandler(engine *Engine) {
- // engine.GET("/", func(ctx *Context) {
- // ctx.Status(200)
- // })
- engine.Use(CORS(), CSRF(), Mobile())
- // set the global timeout is 2 second
- engine.conf.Timeout = xtime.Duration(time.Second * 2)
- engine.Ping(func(ctx *Context) {
- ctx.AbortWithStatus(200)
- })
- engine.Register(func(ctx *Context) {
- ctx.JSONMap(map[string]interface{}{
- "region": "aws",
- }, nil)
- })
- engine.HEAD("/head", func(ctx *Context) {
- ctx.Status(200)
- })
- engine.GET("/get", func(ctx *Context) {
- ctx.String(200, "%s", "pong")
- })
- engine.POST("/post", func(ctx *Context) {
- ctx.String(200, "%s", "pong")
- })
- engine.PUT("/put", func(ctx *Context) {
- ctx.String(200, "%s", "pong")
- })
- engine.DELETE("/delete", func(ctx *Context) {
- ctx.String(200, "%s", "pong")
- })
- engine.GET("/json", func(ctx *Context) {
- ctx.JSON(now(), nil)
- })
- engine.GET("/null-json", func(ctx *Context) {
- ctx.JSON(nil, nil)
- })
- engine.GET("/err", func(ctx *Context) {
- ctx.JSON(now(), errors.New("A error raised from handler func"))
- })
- engine.GET("/xml", func(ctx *Context) {
- ctx.XML(now(), nil)
- })
- engine.GET("/bytes", func(ctx *Context) {
- ctx.Bytes(200, "text/html", ByteContent)
- })
- engine.GET("/bench", func(ctx *Context) {
- ctx.JSON(now(), nil)
- })
- engine.GET("/sleep5", func(ctx *Context) {
- time.Sleep(time.Second * 30)
- ctx.JSON(now(), nil)
- })
- engine.GET("/timeout", func(ctx *Context) {
- start := time.Now()
- <-ctx.Done()
- ctx.String(200, "Timeout within %s", time.Since(start))
- })
- engine.GET("/timeout-from-method-config", func(ctx *Context) {
- start := time.Now()
- <-ctx.Done()
- ctx.String(200, "Timeout within %s", time.Since(start))
- })
- engine.SetMethodConfig("/timeout-from-method-config", &MethodConfig{Timeout: xtime.Duration(time.Second * 3)})
- engine.GET("/redirect", func(ctx *Context) {
- ctx.Redirect(301, "/bytes")
- })
- engine.GET("/panic", func(_ *Context) {
- panic("Oupps, Houston, we have a problem")
- })
- engine.GET("/json-map", func(ctx *Context) {
- ctx.JSONMap(map[string]interface{}{
- "tid": 1,
- }, nil)
- })
- engine.GET("/bind", func(ctx *Context) {
- v := new(Archive)
- err := ctx.Bind(v)
- if err != nil {
- return
- }
- ctx.JSON(v, nil)
- })
- engine.POST("/bindwith", func(ctx *Context) {
- v := new(Archive)
- err := ctx.BindWith(v, binding.JSON)
- if err != nil {
- return
- }
- ctx.JSON(v, nil)
- })
- engine.GET("/pb", func(ctx *Context) {
- now := &tests.Time{
- Now: time.Now().Unix(),
- }
- ctx.Protobuf(now, nil)
- })
- engine.GET("/pb-error", func(ctx *Context) {
- ctx.Protobuf(nil, ecode.RequestErr)
- })
- engine.GET("/pb-nildata-nilerr", func(ctx *Context) {
- ctx.Protobuf(nil, nil)
- })
- engine.GET("/pb-data-err", func(ctx *Context) {
- now := &tests.Time{
- Now: time.Now().Unix(),
- }
- ctx.Protobuf(now, ecode.RequestErr)
- })
- engine.GET("/member-blocked", func(ctx *Context) {
- ctx.JSON(nil, ecode.MemberBlocked)
- })
- engine.GET("/device", func(ctx *Context) {
- dev, ok := ctx.Get("device")
- if !ok {
- ctx.JSON(nil, ecode.RequestErr)
- return
- }
- ctx.JSON(dev, nil)
- })
- engine.GET("/device-from-meta", func(ctx *Context) {
- dev, ok := metadata.Value(ctx, "device").(*Device)
- if !ok {
- ctx.JSON(nil, ecode.RequestErr)
- return
- }
- ctx.JSON(dev, nil)
- })
- engine.GET("/remote-ip", func(ctx *Context) {
- ctx.JSON(metadata.String(ctx, metadata.RemoteIP), nil)
- })
- m1 := func() HandlerFunc {
- return func(ctx *Context) {
- ctx.Set("m1", "middleware pong")
- ctx.Next()
- }
- }
- m2 := func() func(ctx *Context) {
- return func(ctx *Context) {
- v, isok := ctx.Get("m1")
- if !isok {
- ctx.AbortWithStatus(500)
- return
- }
- ctx.Set("m2", v)
- ctx.Next()
- }
- }
- engine.Use(m1())
- engine.UseFunc(m2())
- engine.GET("/use", func(ctx *Context) {
- v, isok := ctx.Get("m2")
- if !isok {
- ctx.AbortWithStatus(500)
- return
- }
- ctx.String(200, "%s", v.(string))
- })
- r := engine.Group("/group", func(ctx *Context) {
- })
- r.GET("/get", func(ctx *Context) {
- ctx.String(200, "%s", "pong")
- })
- rr := r.Group("/abort", func(ctx *Context) {
- ctx.String(200, "%s", "pong")
- ctx.Abort()
- })
- rr.GET("", func(ctx *Context) {
- ctx.String(500, "never get this echo")
- })
- rrr := r.Group("/test", func(ctx *Context) {
- })
- g1 := func() HandlerFunc {
- return func(ctx *Context) {
- v, isok := ctx.Get("m2")
- if !isok {
- ctx.AbortWithStatus(500)
- return
- }
- ctx.Set("g1", v)
- ctx.Next()
- }
- }
- g2 := func() func(ctx *Context) {
- return func(ctx *Context) {
- v, isok := ctx.Get("g1")
- if !isok {
- ctx.AbortWithStatus(500)
- return
- }
- ctx.Next()
- ctx.String(200, "%s", v.(string))
- }
- }
- rrr.Use(g1()).UseFunc(g2()).GET("/use", func(ctx *Context) {})
- groupInject := engine.Group("/inject")
- engine.Inject("^/inject", func(ctx *Context) {
- ctx.Set("injected", "injected")
- })
- groupInject.GET("/index", func(ctx *Context) {
- injected, _ := ctx.Get("injected")
- injectedString, _ := injected.(string)
- ctx.String(200, strings.Join([]string{"index", injectedString}, "-"))
- })
- engine.GET("/group/test/json-status", func(ctx *Context) {
- ctx.JSON(nil, ecode.MemberBlocked)
- })
- engine.GET("/group/test/xml-status", func(ctx *Context) {
- ctx.XML(nil, ecode.MemberBlocked)
- })
- engine.GET("/group/test/proto-status", func(ctx *Context) {
- ctx.Protobuf(nil, ecode.MemberBlocked)
- })
- engine.GET("/group/test/json-map-status", func(ctx *Context) {
- ctx.JSONMap(map[string]interface{}{}, ecode.MemberBlocked)
- })
- }
- func startServer() {
- e := Default()
- setupHandler(e)
- go e.Run(SockAddr)
- curEngine.Store(e)
- time.Sleep(time.Second)
- }
- func TestSetupHandler(t *testing.T) {
- engine := Default()
- setupHandler(engine)
- }
- func TestServeUnix(t *testing.T) {
- engine := Default()
- setupHandler(engine)
- closed := make(chan struct{})
- defer func() {
- if err := engine.Shutdown(context.TODO()); err != nil {
- t.Errorf("Failed to shutdown engine: %s", err)
- }
- <-closed
- }()
- unixs, err := ioutil.TempFile("", "engine.sock")
- if err != nil {
- t.Fatalf("Failed to create temp file: %s", err)
- }
- go func() {
- if err := engine.RunUnix(unixs.Name()); err != nil {
- if errors.Cause(err) == http.ErrServerClosed {
- t.Logf("Server stopped due to shutting down command")
- } else {
- t.Errorf("Failed to serve with unix socket: %s", err)
- }
- }
- closed <- struct{}{}
- }()
- // connection test required
- time.Sleep(time.Second)
- }
- func shutdown() {
- if err := curEngine.Load().(*Engine).Shutdown(context.TODO()); err != nil {
- panic(err)
- }
- }
- func TestServeTLS(t *testing.T) {
- engine := New()
- setupHandler(engine)
- closed := make(chan struct{})
- defer func() {
- if err := engine.Shutdown(context.TODO()); err != nil {
- t.Errorf("Failed to shutdown engine: %s", err)
- }
- <-closed
- }()
- cert, err := ioutil.TempFile("", "cert.pem")
- if err != nil {
- t.Fatalf("Failed to create temp file: %s", err)
- }
- key, err := ioutil.TempFile("", "key.pem")
- if err != nil {
- t.Fatalf("Failed to create temp file: %s", err)
- }
- if _, err = cert.WriteString(CertPEM); err != nil {
- t.Fatalf("Failed to write cert file: %s", err)
- }
- if _, err = key.WriteString(KeyPEM); err != nil {
- t.Fatalf("Failed to write key file: %s", err)
- }
- go func() {
- if rerr := engine.RunTLS(AdHocSockAddr, cert.Name(), key.Name()); rerr != nil {
- if errors.Cause(rerr) == http.ErrServerClosed {
- t.Logf("Server stopped due to shutting down command")
- } else {
- t.Errorf("Failed to serve with tls: %s", rerr)
- }
- }
- closed <- struct{}{}
- }()
- tr := &http.Transport{
- TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
- }
- client := &http.Client{Transport: tr}
- resp, err := client.Get(fmt.Sprintf("%s://%s%s", "https", AdHocSockAddr, "/get"))
- if err != nil {
- t.Fatalf("Failed to send https request: %s", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Errorf("http.Get get error code:%d", resp.StatusCode)
- }
- resp.Body.Close()
- }
- func TestServer(t *testing.T) {
- once.Do(startServer)
- resp, err := http.Get(uri(SockAddr, "/get"))
- if err != nil {
- t.Fatalf("BladeMaster: get error(%v)", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Errorf("http.Get get error code:%d", resp.StatusCode)
- }
- resp.Body.Close()
- }
- func pongValidator(expected string, t *testing.T) func(*http.Response) error {
- return func(resp *http.Response) error {
- if resp.StatusCode != http.StatusOK {
- return fmt.Errorf("http.Get get error code: %d", resp.StatusCode)
- }
- bs, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Logf("ioutil.ReadAll() failed: %s", err)
- return err
- }
- resp.Body.Close()
- if ps := string(bs); ps != expected {
- return fmt.Errorf("Response body not expected: %s != %s", ps, expected)
- }
- return nil
- }
- }
- func TestPathHandle(t *testing.T) {
- once.Do(startServer)
- expected := "pong"
- validateFn := pongValidator(expected, t)
- testCase := map[string]map[string]func(*http.Response) error{
- "/monitor/ping": {
- "GET": func(resp *http.Response) error {
- assert.Equal(t, 200, resp.StatusCode)
- return nil
- },
- },
- "/register": {
- "GET": func(resp *http.Response) error {
- assert.Equal(t, 200, resp.StatusCode)
- bs, err := ioutil.ReadAll(resp.Body)
- assert.NoError(t, err)
- md := make(map[string]interface{})
- assert.NoError(t, json.Unmarshal(bs, &md))
- assert.Equal(t, "aws", md["region"])
- assert.Equal(t, 0, int(md["code"].(float64)))
- assert.Equal(t, "0", md["message"])
- return nil
- },
- },
- "/head": {
- "HEAD": func(resp *http.Response) error {
- assert.Equal(t, 200, resp.StatusCode)
- return nil
- },
- },
- "/get": {
- "GET": validateFn,
- },
- "/post": {
- "POST": validateFn,
- },
- "/put": {
- "PUT": validateFn,
- },
- "/delete": {
- "DELETE": validateFn,
- },
- "/not-exist-path": {
- "GET": func(resp *http.Response) error {
- assert.Equal(t, 404, resp.StatusCode)
- return nil
- },
- },
- }
- c := &http.Client{}
- for path := range testCase {
- for method := range testCase[path] {
- validator := testCase[path][method]
- req, err := http.NewRequest(method, uri(SockAddr, path), nil)
- if err != nil {
- t.Fatalf("Failed to build request: %s", err)
- }
- resp, err := c.Do(req)
- if err != nil {
- t.Fatalf("Failed to send request: %s", err)
- }
- defer resp.Body.Close()
- if err := validator(resp); err != nil {
- t.Errorf("Failed to validate request to `%s %s`: %s", method, path, err)
- }
- }
- }
- }
- func TestMiddleWare(t *testing.T) {
- once.Do(startServer)
- expected := "middleware pong"
- validateFn := pongValidator(expected, t)
- testCase := map[string]map[string]func(*http.Response) error{
- "/use": {
- "GET": validateFn,
- },
- "/group/test/use": {
- "GET": validateFn,
- },
- }
- c := &http.Client{}
- for path := range testCase {
- for method := range testCase[path] {
- validator := testCase[path][method]
- req, err := http.NewRequest(method, uri(SockAddr, path), nil)
- if err != nil {
- t.Fatalf("Failed to build request: %s", err)
- }
- resp, err := c.Do(req)
- if err != nil {
- t.Errorf("Failed to send request: %s", err)
- }
- defer resp.Body.Close()
- if err := validator(resp); err != nil {
- t.Errorf("Failed to validate request to `%s %s`: %s", method, path, err)
- }
- }
- }
- }
- func TestRouterGroup(t *testing.T) {
- once.Do(startServer)
- expected := "pong"
- validateFn := pongValidator(expected, t)
- testCase := map[string]map[string]func(*http.Response) error{
- "/group/get": {
- "GET": validateFn,
- },
- "/group/abort": {
- "GET": validateFn,
- },
- }
- c := &http.Client{}
- for path := range testCase {
- for method := range testCase[path] {
- validator := testCase[path][method]
- req, err := http.NewRequest(method, uri(SockAddr, path), nil)
- if err != nil {
- t.Fatalf("Failed to build request: %s", err)
- }
- resp, err := c.Do(req)
- if err != nil {
- t.Errorf("Failed to send request: %s", err)
- }
- defer resp.Body.Close()
- if err := validator(resp); err != nil {
- t.Errorf("Failed to validate request to `%s %s`: %s", method, path, err)
- }
- }
- }
- }
- func TestMonitor(t *testing.T) {
- once.Do(startServer)
- path := "/metrics"
- resp, err := http.Get(uri(SockAddr, path))
- if err != nil {
- t.Fatalf("Failed to send request: %s", err)
- }
- defer resp.Body.Close()
- assert.Equal(t, http.StatusOK, resp.StatusCode)
- bs, err := ioutil.ReadAll(resp.Body)
- assert.Equal(t, nil, err)
- assert.False(t, len(bs) <= 0)
- }
- func TestMetadata(t *testing.T) {
- once.Do(startServer)
- path := "/metadata"
- resp, err := http.Get(uri(SockAddr, path))
- if err != nil {
- t.Fatalf("Failed to send request: %s", err)
- }
- defer resp.Body.Close()
- assert.Equal(t, http.StatusOK, resp.StatusCode)
- bs, err := ioutil.ReadAll(resp.Body)
- assert.Equal(t, nil, err)
- assert.False(t, len(bs) <= 0)
- }
- func TestRender(t *testing.T) {
- once.Do(startServer)
- bodyTestCase := map[string]map[string]func([]byte) error{
- "/json": {
- "GET": func(bs []byte) error {
- var data struct {
- Code int `json:"code"`
- TTL int `json:"ttl"`
- Ts *Timestamp `json:"data"`
- }
- if err := json.Unmarshal(bs, &data); err != nil {
- t.Logf("json.Unmarshal() failed: %s", err)
- }
- assert.Zero(t, data.Code)
- assert.NotZero(t, data.Ts.Now)
- assert.NotZero(t, data.TTL)
- return nil
- },
- },
- "/null-json": {
- "GET": func(bs []byte) error {
- res := map[string]interface{}{}
- if err := json.Unmarshal(bs, &res); err != nil {
- t.Logf("json.Unmarshal() failed: %s", err)
- }
- if _, ok := res["data"]; ok {
- t.Errorf("Field `data` should be omitted")
- }
- return nil
- },
- },
- "/xml": {
- "GET": func(bs []byte) error {
- var ts Timestamp
- if err := xml.Unmarshal(bs, &ts); err != nil {
- t.Logf("xml.Unmarshal() failed: %s", err)
- }
- if ts.Now == 0 {
- return fmt.Errorf("Timestamp.Now field cannot be zero: %+v", ts)
- }
- return nil
- },
- },
- "/bytes": {
- "GET": func(bs []byte) error {
- respStr := string(bs)
- oriContent := string(ByteContent)
- if respStr != oriContent {
- return fmt.Errorf("Bytes response not expected: %s != %s", respStr, oriContent)
- }
- return nil
- },
- },
- "/json-map": {
- "GET": func(bs []byte) error {
- var data struct {
- Code int `json:"code"`
- Tid int `json:"tid"`
- Message string `json:"message"`
- }
- if err := json.Unmarshal(bs, &data); err != nil {
- t.Logf("json.Unmarshal() failed: %s", err)
- }
- if data.Tid != 1 || data.Code != 0 || data.Message != "0" {
- return fmt.Errorf("Invalid respones: %+v", data)
- }
- return nil
- },
- },
- }
- contentTypeTest := map[string]string{
- "/json": "application/json; charset=utf-8",
- "/xml": "application/xml; charset=utf-8",
- "/bytes": "text/html",
- "/json-map": "application/json; charset=utf-8",
- "/null-json": "application/json; charset=utf-8",
- }
- c := &http.Client{}
- for path := range bodyTestCase {
- for method := range bodyTestCase[path] {
- validator := bodyTestCase[path][method]
- expHeader := contentTypeTest[path]
- req, err := http.NewRequest(method, uri(SockAddr, path), nil)
- if err != nil {
- t.Fatalf("Failed to build request: %s", err)
- }
- resp, err := c.Do(req)
- if err != nil {
- t.Fatalf("Failed to send request: %s", err)
- }
- defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- t.Errorf("http.%s(%s) get error code: %d", method, path, resp.StatusCode)
- }
- bs, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Errorf("ioutil.ReadAll() failed: %s", err)
- }
- if ct := resp.Header.Get("Content-Type"); ct != expHeader {
- t.Errorf("Unexpected content-type header on path `%s`: expected %s got %s", path, expHeader, ct)
- }
- if err := validator(bs); err != nil {
- t.Errorf("Failed to validate request to `%s %s`: %s", method, path, err)
- }
- }
- }
- }
- func TestRedirect(t *testing.T) {
- c := &http.Client{
- CheckRedirect: func(req *http.Request, via []*http.Request) error {
- req.Header["Referer"] = []string{"http://www.bilibili.com/"}
- return nil
- },
- }
- req, err := http.NewRequest("GET", uri(SockAddr, "/redirect"), nil)
- if err != nil {
- t.Fatalf("Failed to build request: %s", err)
- }
- resp, err := c.Do(req)
- if err != nil {
- t.Fatalf("Failed to send request: %s", err)
- }
- defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- t.Errorf("Redirect test filed: %d", resp.StatusCode)
- }
- bs, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Errorf("ioutil.ReadAll() failed: %s", err)
- }
- respStr := string(bs)
- oriContent := string(ByteContent)
- if respStr != oriContent {
- t.Errorf("Bytes response not expected: %s != %s", respStr, oriContent)
- }
- }
- func TestCORSPreflight(t *testing.T) {
- once.Do(startServer)
- origin := "ccc.bilibili.com"
- c := &http.Client{}
- req, err := http.NewRequest("OPTIONS", uri(SockAddr, "/get"), nil)
- if err != nil {
- t.Fatalf("Failed to build request: %s", err)
- }
- req.Header.Set("Referer", "http://www.bilibili.com/")
- req.Header.Set("Origin", origin)
- resp, err := c.Do(req)
- if err != nil {
- t.Fatalf("Failed to send request: %s", err)
- }
- defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- t.Errorf("CORS preflight request status code unexpected: %d", resp.StatusCode)
- }
- preflightHeaders := map[string]string{
- "Access-Control-Allow-Origin": origin,
- "Access-Control-Allow-Credentials": "true",
- "Access-Control-Allow-Methods": "GET,POST",
- "Access-Control-Allow-Headers": "Origin,Content-Length,Content-Type",
- }
- for k, v := range preflightHeaders {
- if hv := resp.Header.Get(k); hv != v {
- t.Errorf("Unexpected header %s: %s != %s", k, hv, v)
- }
- }
- varys := map[string]int{
- "Origin": 0,
- "Access-Control-Request-Method": 0,
- "Access-Control-Request-Headers": 0,
- }
- reqVarys := make(map[string]int)
- rv := resp.Header["Vary"]
- for _, v := range rv {
- reqVarys[v] = 0
- }
- for v := range varys {
- if _, ok := reqVarys[v]; !ok {
- t.Errorf("%s is missed in Vary", v)
- }
- }
- }
- func TestCORSNormal(t *testing.T) {
- once.Do(startServer)
- origin := "ccc.bilibili.com"
- c := &http.Client{}
- req, err := http.NewRequest("GET", uri(SockAddr, "/get"), nil)
- if err != nil {
- t.Fatalf("Failed to build request: %s", err)
- }
- req.Header.Set("Referer", "http://www.bilibili.com/")
- req.Header.Set("Origin", origin)
- resp, err := c.Do(req)
- if err != nil {
- t.Fatalf("Failed to send request: %s", err)
- }
- defer resp.Body.Close()
- normalHeaders := map[string]string{
- "Access-Control-Allow-Origin": origin,
- "Access-Control-Allow-Credentials": "true",
- "Access-Control-Allow-Methods": "GET,POST",
- }
- for k, v := range normalHeaders {
- if hv := resp.Header.Get(k); hv != v {
- t.Errorf("Unexpected header %s: %s != %s", k, hv, v)
- }
- }
- }
- func TestJSONP(t *testing.T) {
- once.Do(startServer)
- origin := "ccc.bilibili.com"
- r := regexp.MustCompile(`onsuccess\((.*)\)`)
- c := &http.Client{}
- req, err := http.NewRequest("GET", uri(SockAddr, "/json?cross_domain=true&jsonp=jsonp&callback=onsuccess"), nil)
- if err != nil {
- t.Fatalf("Failed to build request: %s", err)
- }
- req.Header.Set("Referer", "http://www.bilibili.com/")
- req.Header.Set("Origin", origin)
- resp, err := c.Do(req)
- if err != nil {
- t.Fatalf("Failed to send request: %s", err)
- }
- defer resp.Body.Close()
- bs, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Errorf("ioutil.ReadAll() failed: %s", err)
- }
- matched := r.FindSubmatch(bs)
- if matched == nil {
- t.Errorf("Response not matched pattern: %s", r)
- }
- var data struct {
- Code int `json:"code"`
- Ts *Timestamp `json:"data"`
- }
- if err := json.Unmarshal(matched[1], &data); err != nil {
- t.Logf("json.Unmarshal() failed: %s", err)
- }
- if data.Code != 0 || data.Ts.Now == 0 {
- t.Errorf("Request should succeed but got blocked with code(%d) or Timestamp.Now field cannot be zero: %+v",
- data.Code, data)
- }
- }
- func TestCORSFailed(t *testing.T) {
- once.Do(startServer)
- c := &http.Client{}
- req, err := http.NewRequest("GET", uri(SockAddr, "/json?cross_domain=true&jsonp=jsonp&callback=onsuccess"), nil)
- if err != nil {
- t.Fatalf("Failed to build request: %s", err)
- }
- req.Header.Set("Referer", "http://www.bilibili2.com/")
- req.Header.Set("Origin", "ccc.bilibili2.com")
- resp, err := c.Do(req)
- if err != nil {
- t.Fatalf("Failed to send request: %s", err)
- }
- defer resp.Body.Close()
- if resp.StatusCode == 200 {
- t.Errorf("This request should be blocked, but got status %d", resp.StatusCode)
- }
- }
- func TestCSRF(t *testing.T) {
- once.Do(startServer)
- allowed := []string{
- "http://www.bilibili.com/",
- "http://www.biligame.com/",
- "http://www.im9.com/",
- "http://www.acg.tv/",
- "http://www.hdslb.com/",
- "http://www.bilibili.co/",
- // should match by appid
- "https://servicewechat.com/wx7564fd5313d24844/devtools/page-frame.html",
- "http://servicewechat.com/wx7564fd5313d24844/",
- "http://servicewechat.com/wx7564fd5313d24844",
- "http://servicewechat.com/wx618ca8c24bf06c33",
- // "http://bilibili.co/",
- // "http://hdslb.com/",
- // "http://acg.tv/",
- // "http://im9.com/",
- // "http://biligame.com/",
- // "http://bilibili.com/",
- }
- notAllowed := []string{
- "http://www.bilibili2.com/",
- "http://www.biligame2.com/",
- "http://www.im92.com/",
- "http://www.acg2.tv/",
- "http://www.hdslb2.com/",
- "http://servicewechat.com/",
- "http://servicewechat.com/wx7564fd5313d24842",
- "http://servicewechat.com/wx618ca8c24bf06c34",
- }
- c := &http.Client{}
- for _, r := range allowed {
- req, err := http.NewRequest("GET", uri(SockAddr, "/json"), nil)
- assert.Nil(t, err)
- req.Header.Set("Referer", r)
- resp, err := c.Do(req)
- assert.Nil(t, err)
- resp.Body.Close()
- assert.Equal(t, 200, resp.StatusCode)
- }
- for _, r := range notAllowed {
- req, err := http.NewRequest("GET", uri(SockAddr, "/json"), nil)
- assert.Nil(t, err)
- req.Header.Set("Referer", r)
- resp, err := c.Do(req)
- assert.Nil(t, err)
- resp.Body.Close()
- assert.Equal(t, 403, resp.StatusCode)
- }
- req, err := http.NewRequest("GET", uri(SockAddr, "/json"), nil)
- assert.Nil(t, err)
- resp, err := c.Do(req)
- assert.Nil(t, err)
- resp.Body.Close()
- assert.Equal(t, 200, resp.StatusCode)
- req, err = http.NewRequest("GET", uri(SockAddr, "/json?callback=123&jsonp=jsonp"), nil)
- assert.Nil(t, err)
- resp, err = c.Do(req)
- assert.Nil(t, err)
- resp.Body.Close()
- assert.Equal(t, 403, resp.StatusCode)
- req, err = http.NewRequest("GET", uri(SockAddr, "/json?cross_domain=123"), nil)
- assert.Nil(t, err)
- resp, err = c.Do(req)
- assert.Nil(t, err)
- resp.Body.Close()
- assert.Equal(t, 403, resp.StatusCode)
- }
- func TestOnError(t *testing.T) {
- once.Do(startServer)
- c := &http.Client{}
- resp, err := c.Get(uri(SockAddr, "/err"))
- if err != nil {
- t.Fatalf("Failed to send request: %s", err)
- }
- defer resp.Body.Close()
- bs, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Errorf("ioutil.ReadAll() failed: %s", err)
- }
- var data struct {
- Code int `json:"code"`
- Ts *Timestamp `json:"data"`
- }
- if err := json.Unmarshal(bs, &data); err != nil {
- t.Logf("json.Unmarshal() failed: %s", err)
- }
- if data.Code != ecode.ServerErr.Code() {
- t.Errorf("Error code is not expected: %d != %d", data.Code, ecode.ServerErr.Code())
- }
- }
- func TestRecovery(t *testing.T) {
- once.Do(startServer)
- c := &http.Client{}
- resp, err := c.Get(uri(SockAddr, "/panic"))
- if err != nil {
- t.Fatalf("Failed to send request: %s", err)
- }
- if resp.StatusCode != http.StatusInternalServerError {
- t.Fatalf("Expect status code 500 get %d, maybe recovery not working as expected", resp.StatusCode)
- }
- }
- func TestGlobalTimeout(t *testing.T) {
- once.Do(startServer)
- t.Run("Should timeout by default", func(t *testing.T) {
- c := &http.Client{}
- req, err := http.NewRequest("GET", uri(SockAddr, "/timeout"), nil)
- assert.Nil(t, err)
- start := time.Now()
- resp, err := c.Do(req)
- assert.Nil(t, err)
- assert.Equal(t, 200, resp.StatusCode)
- assert.InDelta(t, float64(time.Second*2), float64(time.Since(start)), float64(time.Second))
- })
- t.Run("Should timeout by delivered", func(t *testing.T) {
- c := &http.Client{}
- req, err := http.NewRequest("GET", uri(SockAddr, "/timeout"), nil)
- assert.Nil(t, err)
- td := int64(time.Second / time.Millisecond)
- req.Header.Set(_httpHeaderTimeout, strconv.FormatInt(td, 10))
- start := time.Now()
- resp, err := c.Do(req)
- assert.Nil(t, err)
- assert.Equal(t, 200, resp.StatusCode)
- assert.InDelta(t, float64(time.Second), float64(time.Since(start)), float64(time.Second))
- })
- t.Run("Should not timeout by delivered", func(t *testing.T) {
- c := &http.Client{}
- req, err := http.NewRequest("GET", uri(SockAddr, "/timeout"), nil)
- assert.Nil(t, err)
- td := int64(time.Second * 10 / time.Millisecond)
- req.Header.Set(_httpHeaderTimeout, strconv.FormatInt(td, 10))
- start := time.Now()
- resp, err := c.Do(req)
- assert.Nil(t, err)
- assert.Equal(t, 200, resp.StatusCode)
- assert.InDelta(t, float64(time.Second*2), float64(time.Since(start)), float64(time.Second))
- })
- t.Run("Should timeout by method config", func(t *testing.T) {
- c := &http.Client{}
- req, err := http.NewRequest("GET", uri(SockAddr, "/timeout-from-method-config"), nil)
- assert.Nil(t, err)
- start := time.Now()
- resp, err := c.Do(req)
- assert.Nil(t, err)
- assert.Equal(t, 200, resp.StatusCode)
- assert.InDelta(t, float64(time.Second*3), float64(time.Since(start)), float64(time.Second))
- })
- }
- func TestServerConfigReload(t *testing.T) {
- engine := Default()
- setupHandler(engine)
- engine.lock.RLock()
- startTm := engine.conf.Timeout
- engine.lock.RUnlock()
- toTm := startTm * 5
- wg := &sync.WaitGroup{}
- closed := make(chan struct{})
- go func() {
- if err := engine.Run(AdHocSockAddr); err != nil {
- if errors.Cause(err) == http.ErrServerClosed {
- t.Logf("Server stopped due to shutting down command")
- } else {
- t.Errorf("Failed to serve with tls: %s", err)
- }
- }
- closed <- struct{}{}
- }()
- time.Sleep(time.Second)
- clientNum := 20
- for i := 0; i < clientNum; i++ {
- wg.Add(1)
- go func() {
- defer wg.Done()
- c := &http.Client{}
- resp, err := c.Get(uri(AdHocSockAddr, "/get"))
- if err != nil {
- assert.Nil(t, err)
- return
- }
- assert.Equal(t, http.StatusOK, resp.StatusCode)
- }()
- }
- for i := 2; i < clientNum; i++ {
- wg.Add(1)
- conf := &ServerConfig{
- Timeout: toTm,
- }
- if i%2 == 0 {
- conf.Timeout = 0
- }
- go func() {
- defer wg.Done()
- err := engine.SetConfig(conf)
- if conf.Timeout <= 0 {
- assert.NotNil(t, err)
- return
- }
- assert.Nil(t, err)
- }()
- }
- wg.Wait()
- engine.lock.RLock()
- endTm := engine.conf.Timeout
- engine.lock.RUnlock()
- assert.NotEqual(t, toTm, startTm)
- assert.Equal(t, toTm, endTm)
- if err := engine.Shutdown(context.TODO()); err != nil {
- assert.Nil(t, err)
- }
- <-closed
- }
- func TestGracefulShutdown(t *testing.T) {
- engine := Default()
- setupHandler(engine)
- closed := make(chan struct{})
- go func() {
- if err := engine.Run(AdHocSockAddr); err != nil {
- if errors.Cause(err) == http.ErrServerClosed {
- t.Logf("Server stopped due to shutting down command")
- } else {
- t.Errorf("Failed to serve with tls: %s", err)
- }
- }
- closed <- struct{}{}
- }()
- time.Sleep(time.Second)
- clientNum := 10
- wg := &sync.WaitGroup{}
- for i := 0; i < clientNum; i++ {
- wg.Add(1)
- go func() {
- defer wg.Done()
- c := &http.Client{}
- resp, err := c.Get(uri(AdHocSockAddr, "/sleep5"))
- if err != nil {
- t.Error(err)
- return
- }
- if resp.StatusCode != http.StatusOK {
- t.Errorf("Unexpected status code: %d", resp.StatusCode)
- return
- }
- t.Logf("Request finished at: %v", time.Now())
- }()
- }
- time.Sleep(time.Second)
- t.Logf("Invoke Shutdown method at: %v", time.Now())
- if err := engine.Shutdown(context.TODO()); err != nil {
- t.Fatalf("Failed to shutdown engine: %s", err)
- }
- wg.Wait()
- <-closed
- }
- func TestProtobuf(t *testing.T) {
- once.Do(startServer)
- c := &http.Client{}
- t.Run("On-Success", func(t *testing.T) {
- req, err := http.NewRequest("GET", uri(SockAddr, "/pb"), nil)
- assert.Nil(t, err)
- resp, err := c.Do(req)
- assert.Nil(t, err)
- defer resp.Body.Close()
- assert.Equal(t, resp.StatusCode, http.StatusOK)
- assert.Equal(t, resp.Header.Get("Content-Type"), "application/x-protobuf")
- bs, err := ioutil.ReadAll(resp.Body)
- assert.Nil(t, err)
- d := &render.PB{}
- err = proto.Unmarshal(bs, d)
- assert.Nil(t, err)
- assert.Equal(t, int(d.Code), 0)
- assert.NotNil(t, d.Data)
- tt := &tests.Time{}
- err = proto.Unmarshal(d.Data.Value, tt)
- assert.Nil(t, err)
- assert.NotZero(t, tt.Now)
- })
- t.Run("On-Error", func(t *testing.T) {
- req, err := http.NewRequest("GET", uri(SockAddr, "/pb-error"), nil)
- assert.Nil(t, err)
- resp, err := c.Do(req)
- assert.Nil(t, err)
- defer resp.Body.Close()
- assert.Equal(t, resp.StatusCode, http.StatusOK)
- assert.Equal(t, resp.Header.Get("Content-Type"), "application/x-protobuf")
- bs, err := ioutil.ReadAll(resp.Body)
- assert.Nil(t, err)
- d := &render.PB{}
- err = proto.Unmarshal(bs, d)
- assert.Nil(t, err)
- assert.Equal(t, int(d.Code), ecode.RequestErr.Code())
- })
- t.Run("On-NilData-NilErr", func(t *testing.T) {
- req, err := http.NewRequest("GET", uri(SockAddr, "/pb-nildata-nilerr"), nil)
- assert.Nil(t, err)
- resp, err := c.Do(req)
- assert.Nil(t, err)
- defer resp.Body.Close()
- assert.Equal(t, resp.StatusCode, http.StatusOK)
- assert.Equal(t, resp.Header.Get("Content-Type"), "application/x-protobuf")
- bs, err := ioutil.ReadAll(resp.Body)
- assert.Nil(t, err)
- d := &render.PB{}
- err = proto.Unmarshal(bs, d)
- assert.Nil(t, err)
- assert.Equal(t, int(d.Code), 0)
- })
- t.Run("On-Data-Err", func(t *testing.T) {
- req, err := http.NewRequest("GET", uri(SockAddr, "/pb-data-err"), nil)
- assert.Nil(t, err)
- resp, err := c.Do(req)
- assert.Nil(t, err)
- defer resp.Body.Close()
- assert.Equal(t, resp.StatusCode, http.StatusOK)
- assert.Equal(t, resp.Header.Get("Content-Type"), "application/x-protobuf")
- bs, err := ioutil.ReadAll(resp.Body)
- assert.Nil(t, err)
- d := &render.PB{}
- err = proto.Unmarshal(bs, d)
- assert.Nil(t, err)
- assert.Equal(t, int(d.Code), ecode.RequestErr.Code())
- })
- }
- func TestBind(t *testing.T) {
- once.Do(startServer)
- c := &http.Client{}
- req, err := http.NewRequest("GET", uri(SockAddr, "/bind?mids=2,3,4&title=hello&content=world&cid=8"), nil)
- if err != nil {
- t.Fatalf("Failed to build request: %s", err)
- }
- resp, err := c.Do(req)
- if err != nil {
- t.Fatalf("Failed to send request: %s", err)
- }
- assert.Equal(t, resp.StatusCode, 200)
- bs, err := ioutil.ReadAll(resp.Body)
- assert.NoError(t, err)
- resp.Body.Close()
- var p struct {
- Code int `json:"code"`
- Data *Archive `json:"data"`
- }
- if err := json.Unmarshal(bs, &p); err != nil {
- t.Fatalf("Failed to json.Unmarshal: %v", resp.StatusCode)
- }
- assert.Equal(t, p.Code, 0)
- assert.Equal(t, []int64{2, 3, 4}, p.Data.Mids)
- assert.Equal(t, "hello", p.Data.Title)
- assert.Equal(t, "world", p.Data.Content)
- assert.Equal(t, 8, p.Data.Cid)
- }
- func TestBindWith(t *testing.T) {
- once.Do(startServer)
- a := &Archive{
- Mids: []int64{2, 3, 4},
- Title: "hello",
- Content: "world",
- Cid: 8,
- }
- d, err := json.Marshal(a)
- if err != nil {
- t.Fatalf("Failed to json.Marshal: %v", err)
- }
- c := &http.Client{}
- req, err := http.NewRequest("POST", uri(SockAddr, "/bindwith"), bytes.NewBuffer(d))
- if err != nil {
- t.Fatalf("Failed to build request: %s", err)
- }
- resp, err := c.Do(req)
- if err != nil {
- t.Fatalf("Failed to send request: %s", err)
- }
- assert.Equal(t, resp.StatusCode, 200)
- bs, err := ioutil.ReadAll(resp.Body)
- assert.NoError(t, err)
- resp.Body.Close()
- var p struct {
- Code int `json:"code"`
- Data *Archive `json:"data"`
- }
- if err := json.Unmarshal(bs, &p); err != nil {
- t.Fatalf("Failed to json.Unmarshal: %v", resp.StatusCode)
- }
- assert.Equal(t, p.Code, 0)
- assert.Equal(t, a.Mids, p.Data.Mids)
- assert.Equal(t, a.Title, p.Data.Title)
- assert.Equal(t, a.Content, p.Data.Content)
- assert.Equal(t, a.Cid, p.Data.Cid)
- }
- func TestMethodNotAllowed(t *testing.T) {
- once.Do(startServer)
- resp, err := http.Get(uri(SockAddr, "/post"))
- assert.NoError(t, err)
- bs, err := ioutil.ReadAll(resp.Body)
- resp.Body.Close()
- assert.NoError(t, err)
- assert.Equal(t, 405, resp.StatusCode)
- assert.Equal(t, http.StatusText(405), strings.TrimSpace(string(bs)))
- }
- func TestDevice(t *testing.T) {
- testDevice(t, "/device")
- }
- func TestDeviceMeta(t *testing.T) {
- testDevice(t, "/device-from-meta")
- }
- func testDevice(t *testing.T, path string) {
- once.Do(startServer)
- req, err := http.NewRequest("GET", uri(SockAddr, path), nil)
- assert.NoError(t, err)
- assert.NotNil(t, req)
- req.Header.Set("Buvid", "9346b9ca66dangerous4764eede8bb2")
- req.AddCookie(&http.Cookie{Name: "buvid3", Value: "25213BD4-841C-449F-8BBF-B96B58A8Fdangerousinfoc"})
- req.AddCookie(&http.Cookie{Name: "sid", Value: "70***dpi"})
- query := req.URL.Query()
- query.Set("build", "6280")
- query.Set("device", "phone")
- query.Set("mobi_app", "iphone")
- query.Set("platform", "ios")
- query.Set("channel", "appstore")
- req.URL.RawQuery = query.Encode()
- resp, err := http.DefaultClient.Do(req)
- assert.NoError(t, err)
- assert.NotNil(t, resp)
- assert.Equal(t, 200, resp.StatusCode)
- defer resp.Body.Close()
- rdev := new(DevResp)
- bs, err := ioutil.ReadAll(resp.Body)
- assert.NoError(t, err)
- assert.NoError(t, json.Unmarshal(bs, rdev))
- dev := rdev.Data
- assert.Equal(t, "70***dpi", dev.Sid)
- assert.Equal(t, int64(6280), dev.Build)
- assert.Equal(t, "phone", dev.Device)
- assert.Equal(t, "iphone", dev.RawMobiApp)
- assert.Equal(t, "ios", dev.RawPlatform)
- assert.Equal(t, "appstore", dev.Channel)
- assert.Equal(t, "9346b9ca66dangerous4764eede8bb2", dev.Buvid)
- assert.Equal(t, "25213BD4-841C-449F-8BBF-B96B58A8Fdangerousinfoc", dev.Buvid3)
- assert.Equal(t, PlatIPhone, dev.Plat())
- assert.Equal(t, false, dev.IsAndroid())
- assert.Equal(t, true, dev.IsIOS())
- assert.Equal(t, false, dev.IsOverseas())
- assert.Equal(t, false, dev.InvalidChannel("*"))
- assert.Equal(t, false, dev.InvalidChannel("appstore"))
- assert.Equal(t, "iphone", dev.MobiApp())
- assert.Equal(t, "iphone", dev.MobiAPPBuleChange())
- dev.RawPlatform = "android"
- dev.RawMobiApp = "android"
- dev.Channel = "test.channel"
- assert.Equal(t, true, dev.InvalidChannel("test.channel2"))
- }
- func TestRemoteIPFromContext(t *testing.T) {
- once.Do(startServer)
- ip := "192.168.22.33"
- req, err := http.NewRequest("GET", uri(SockAddr, "/remote-ip"), nil)
- assert.NoError(t, err)
- assert.NotNil(t, req)
- req.Header.Set(_httpHeaderRemoteIP, ip)
- resp, err := http.DefaultClient.Do(req)
- assert.NoError(t, err)
- assert.NotNil(t, resp)
- defer resp.Body.Close()
- bs, err := ioutil.ReadAll(resp.Body)
- assert.NoError(t, err)
- var response struct {
- Data string `json:"data"`
- }
- err = json.Unmarshal(bs, &response)
- assert.NoError(t, err)
- assert.Equal(t, ip, response.Data)
- }
- func TestNewServerMiddleware(t *testing.T) {
- e := DefaultServer(nil)
- // should contain 4 default handlers
- assert.Len(t, e.RouterGroup.Handlers, 5)
- }
- func TestMethodConfig(t *testing.T) {
- e := New()
- tg := e.Group("/timeout").SetMethodConfig(&MethodConfig{Timeout: xtime.Duration(time.Second * 10)})
- tg.GET("/default", func(ctx *Context) {})
- e.GET("/timeout/5s", func(ctx *Context) {})
- e.SetMethodConfig("/timeout/5s", &MethodConfig{Timeout: xtime.Duration(time.Second * 5)})
- pc := e.methodConfig("/timeout/default")
- assert.NotNil(t, pc)
- assert.Equal(t, xtime.Duration(time.Second*10), pc.Timeout)
- pc = e.methodConfig("/timeout/5s")
- assert.NotNil(t, pc)
- assert.Equal(t, xtime.Duration(time.Second*5), pc.Timeout)
- }
- func BenchmarkServer(b *testing.B) {
- startServer()
- defer shutdown()
- b.ResetTimer()
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- resp, err := http.Get(uri(SockAddr, "/bench"))
- if err != nil {
- b.Errorf("HTTPServ: get error(%v)", err)
- continue
- }
- if resp.StatusCode != http.StatusOK {
- b.Errorf("HTTPServ: get error status code:%d", resp.StatusCode)
- }
- resp.Body.Close()
- }
- })
- }
- func TestServerWithMirror(t *testing.T) {
- var response struct {
- Data string `json:"data"`
- }
- //sonce.Do(startMirrorServer)
- client := &http.Client{}
- reqest, _ := http.NewRequest("GET", "http://localhost:18090/mirrortest", nil)
- reqest.Header.Add("x1-bilispy-mirror", "1")
- resp, err := client.Do(reqest)
- assert.NoError(t, err)
- assert.NotNil(t, resp)
- assert.Equal(t, 200, resp.StatusCode)
- bs, _ := ioutil.ReadAll(resp.Body)
- err = json.Unmarshal(bs, &response)
- assert.NoError(t, err)
- assert.Equal(t, "true", response.Data)
- resp.Body.Close()
- reqest, _ = http.NewRequest("GET", "http://localhost:18090/mirrortest", nil)
- reqest.Header.Add("x1-bilispy-mirror", "0")
- resp, err = client.Do(reqest)
- assert.NoError(t, err)
- assert.NotNil(t, resp)
- assert.Equal(t, 200, resp.StatusCode)
- bs, _ = ioutil.ReadAll(resp.Body)
- err = json.Unmarshal(bs, &response)
- assert.NoError(t, err)
- assert.Equal(t, "false", response.Data)
- resp.Body.Close()
- reqest, _ = http.NewRequest("GET", "http://localhost:18090/mirrortest", nil)
- reqest.Header.Add("x1-bilispy-mirror", "xxxxxxxxxx")
- resp, err = client.Do(reqest)
- assert.NoError(t, err)
- assert.NotNil(t, resp)
- assert.Equal(t, 200, resp.StatusCode)
- bs, _ = ioutil.ReadAll(resp.Body)
- err = json.Unmarshal(bs, &response)
- assert.NoError(t, err)
- assert.Equal(t, "false", response.Data)
- resp.Body.Close()
- reqest, _ = http.NewRequest("GET", "http://localhost:18090/mirrortest", nil)
- resp, err = client.Do(reqest)
- assert.NoError(t, err)
- assert.NotNil(t, resp)
- assert.Equal(t, 200, resp.StatusCode)
- bs, _ = ioutil.ReadAll(resp.Body)
- err = json.Unmarshal(bs, &response)
- assert.NoError(t, err)
- assert.Equal(t, "false", response.Data)
- resp.Body.Close()
- }
- func TestEngineInject(t *testing.T) {
- once.Do(startServer)
- client := &http.Client{}
- resp, err := client.Get(uri(SockAddr, "/inject/index"))
- assert.NoError(t, err)
- defer resp.Body.Close()
- bs, err := ioutil.ReadAll(resp.Body)
- assert.NoError(t, err)
- assert.Equal(t, "index-injected", string(bs))
- }
- func TestBiliStatusCode(t *testing.T) {
- once.Do(startServer)
- tests := map[string]int{
- "/group/test/json-status": ecode.MemberBlocked.Code(),
- "/group/test/xml-status": ecode.MemberBlocked.Code(),
- "/group/test/proto-status": ecode.MemberBlocked.Code(),
- "/group/test/json-map-status": ecode.MemberBlocked.Code(),
- }
- client := &http.Client{}
- for path, code := range tests {
- resp, err := client.Get(uri(SockAddr, path))
- assert.NoError(t, err)
- assert.Equal(t, resp.Header.Get("bili-status-code"), strconv.FormatInt(int64(code), 10))
- }
- }
|