github.com/vicanso/pike@v1.0.1-0.20210630235453-9099e041f6ec/server/proxy_test.go (about)

     1  // MIT License
     2  
     3  // Copyright (c) 2020 Tree Xie
     4  
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy
     6  // of this software and associated documentation files (the "Software"), to deal
     7  // in the Software without restriction, including without limitation the rights
     8  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  // copies of the Software, and to permit persons to whom the Software is
    10  // furnished to do so, subject to the following conditions:
    11  
    12  // The above copyright notice and this permission notice shall be included in all
    13  // copies or substantial portions of the Software.
    14  
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    21  // SOFTWARE.
    22  
    23  package server
    24  
    25  import (
    26  	"bytes"
    27  	"net"
    28  	"net/http"
    29  	"net/http/httptest"
    30  	"regexp"
    31  	"strconv"
    32  	"strings"
    33  	"testing"
    34  	"time"
    35  
    36  	"github.com/stretchr/testify/assert"
    37  	"github.com/vicanso/elton"
    38  	"github.com/vicanso/elton/middleware"
    39  	"github.com/vicanso/pike/cache"
    40  	"github.com/vicanso/pike/config"
    41  	"github.com/vicanso/pike/location"
    42  	"github.com/vicanso/pike/upstream"
    43  )
    44  
    45  func TestGetCacheMaxAge(t *testing.T) {
    46  	assert := assert.New(t)
    47  
    48  	tests := []struct {
    49  		key       string
    50  		value     string
    51  		age       int
    52  		existsAge int
    53  	}{
    54  		// 设置了 set cookie
    55  		{
    56  			key:   elton.HeaderSetCookie,
    57  			value: "set cookie",
    58  			age:   0,
    59  		},
    60  		// 未设置cache control
    61  		{
    62  			age: 0,
    63  		},
    64  		// 设置了cache control 为 no cache
    65  		{
    66  			key:   elton.HeaderCacheControl,
    67  			value: "no-cache",
    68  			age:   0,
    69  		},
    70  		// 设置了cache control 为 no store
    71  		{
    72  			key:   elton.HeaderCacheControl,
    73  			value: "no-store",
    74  			age:   0,
    75  		},
    76  		// 设置了cache control 为 private
    77  		{
    78  			key:   elton.HeaderCacheControl,
    79  			value: "private, max-age=10",
    80  			age:   0,
    81  		},
    82  		// 设置了max-age
    83  		{
    84  			key:   elton.HeaderCacheControl,
    85  			value: "max-age=10",
    86  			age:   10,
    87  		},
    88  		// 设置了s-maxage
    89  		{
    90  			key:   elton.HeaderCacheControl,
    91  			value: "max-age=10, s-maxage=1 ",
    92  			age:   1,
    93  		},
    94  		// 设置了age
    95  		{
    96  			key:       elton.HeaderCacheControl,
    97  			value:     "max-age=10",
    98  			age:       8,
    99  			existsAge: 2,
   100  		},
   101  	}
   102  
   103  	for _, tt := range tests {
   104  		h := http.Header{}
   105  		h.Add(tt.key, tt.value)
   106  		if tt.existsAge != 0 {
   107  			h.Add("Age", strconv.Itoa(tt.existsAge))
   108  		}
   109  		age := getCacheMaxAge(h)
   110  		assert.Equal(tt.age, age)
   111  	}
   112  }
   113  
   114  func TestProxyMiddleware(t *testing.T) {
   115  	assert := assert.New(t)
   116  	ln, err := net.Listen("tcp", "127.0.0.1:")
   117  	assert.Nil(err)
   118  	defer ln.Close()
   119  
   120  	cacheResp := []byte("cache response")
   121  
   122  	go func() {
   123  		e := elton.New()
   124  		e.Use(middleware.NewDefaultBodyParser())
   125  		e.GET("/ping", func(c *elton.Context) error {
   126  			c.BodyBuffer = bytes.NewBufferString("pong")
   127  			return nil
   128  		})
   129  
   130  		e.GET("/cache", func(c *elton.Context) error {
   131  			c.CacheMaxAge(time.Minute)
   132  			c.BodyBuffer = bytes.NewBuffer(cacheResp)
   133  			return nil
   134  		})
   135  
   136  		e.GET("/accept-encoding", func(c *elton.Context) error {
   137  			c.BodyBuffer = bytes.NewBufferString(c.GetRequestHeader(elton.HeaderAcceptEncoding))
   138  			return nil
   139  		})
   140  		e.GET("/remove-304-header", func(c *elton.Context) error {
   141  			values := make([]string, 0)
   142  			for _, key := range []string{
   143  				elton.HeaderIfModifiedSince,
   144  				elton.HeaderIfNoneMatch,
   145  				"X-Custom",
   146  			} {
   147  				values = append(values, c.GetRequestHeader(key))
   148  			}
   149  			c.BodyBuffer = bytes.NewBufferString(strings.Join(values, ","))
   150  			return nil
   151  		})
   152  
   153  		// e.POST("/")
   154  		_ = e.Serve(ln)
   155  	}()
   156  	time.Sleep(50 * time.Millisecond)
   157  	reqHeader := http.Header{
   158  		"X-Request-ID": []string{
   159  			"1",
   160  		},
   161  	}
   162  	respHeader := http.Header{
   163  		"X-Response-ID": []string{
   164  			"2",
   165  		},
   166  	}
   167  	location.Reset([]config.LocationConfig{
   168  		{
   169  			Name:     "test",
   170  			Upstream: "test",
   171  			ReqHeaders: []string{
   172  				"X-Request-ID:1",
   173  			},
   174  			RespHeaders: []string{
   175  				"X-Response-ID:2",
   176  			},
   177  			Rewrites: []string{
   178  				"/api/*:/$1",
   179  			},
   180  			ProxyTimeout: "1s",
   181  		},
   182  	})
   183  	upstream.Reset([]config.UpstreamConfig{
   184  		{
   185  			Name: "test",
   186  			Servers: []config.UpstreamServerConfig{
   187  				{
   188  					Addr: "http://" + ln.Addr().String(),
   189  				},
   190  			},
   191  			AcceptEncoding: "snappy",
   192  		},
   193  	})
   194  
   195  	tests := []struct {
   196  		create                 func() *elton.Context
   197  		body                   string
   198  		age                    int
   199  		originalAcceptEncoding string
   200  	}{
   201  		// 正常fetching,可缓存请求
   202  		{
   203  			create: func() *elton.Context {
   204  				req := httptest.NewRequest("GET", "/cache", nil)
   205  				c := elton.NewContext(httptest.NewRecorder(), req)
   206  				setCacheStatus(c, cache.StatusFetching)
   207  				return c
   208  			},
   209  			body: string(cacheResp),
   210  			age:  60,
   211  		},
   212  		// url rewrite
   213  		{
   214  			create: func() *elton.Context {
   215  				req := httptest.NewRequest("GET", "/api/cache", nil)
   216  				return elton.NewContext(httptest.NewRecorder(), req)
   217  			},
   218  			body: string(cacheResp),
   219  		},
   220  		// 修改accept encoding
   221  		{
   222  			create: func() *elton.Context {
   223  				req := httptest.NewRequest("GET", "/accept-encoding", nil)
   224  				req.Header.Set(elton.HeaderAcceptEncoding, "lz4")
   225  				return elton.NewContext(httptest.NewRecorder(), req)
   226  			},
   227  			body:                   "snappy",
   228  			originalAcceptEncoding: "lz4",
   229  		},
   230  		// remove 304 header
   231  		{
   232  			create: func() *elton.Context {
   233  				req := httptest.NewRequest("GET", "/remove-304-header", nil)
   234  				c := elton.NewContext(httptest.NewRecorder(), req)
   235  				// 设置为fetching的才会影响304的请求头
   236  				setCacheStatus(c, cache.StatusFetching)
   237  				c.SetRequestHeader(elton.HeaderIfModifiedSince, "if modified since")
   238  				c.SetRequestHeader(elton.HeaderIfNoneMatch, "if none match")
   239  				c.SetRequestHeader("X-Custom", "1")
   240  				return c
   241  			},
   242  			body: ",,1",
   243  		},
   244  	}
   245  
   246  	serverOption := ServerOption{
   247  		Locations: []string{
   248  			"test",
   249  		},
   250  		Compress:                  "test-compress",
   251  		CompressMinLength:         100,
   252  		CompressContentTypeFilter: regexp.MustCompile(`text|json`),
   253  	}
   254  	fn := NewProxy(NewServer(serverOption))
   255  
   256  	for _, tt := range tests {
   257  		c := tt.create()
   258  		c.Next = func() error {
   259  			return nil
   260  		}
   261  		err := fn(c)
   262  		assert.Nil(err)
   263  		httpResp := getHTTPResp(c)
   264  		for key, value := range reqHeader {
   265  			assert.Equal(value[0], c.GetRequestHeader(key))
   266  		}
   267  		for key, value := range respHeader {
   268  			assert.Equal(value[0], httpResp.Header.Get(key))
   269  			// 确认context中的header并没有设置
   270  			assert.Empty(c.GetHeader(key))
   271  		}
   272  		assert.Equal(tt.originalAcceptEncoding, c.GetRequestHeader(elton.HeaderAcceptEncoding))
   273  		assert.Equal(tt.body, string(httpResp.RawBody))
   274  		assert.Equal(tt.age, getHTTPCacheMaxAge(c))
   275  		assert.Equal(serverOption.CompressContentTypeFilter, httpResp.CompressContentTypeFilter)
   276  		assert.Equal(serverOption.CompressMinLength, httpResp.CompressMinLength)
   277  		assert.Equal(serverOption.Compress, httpResp.CompressSrv)
   278  	}
   279  }