mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-03 23:01:06 +00:00 
			
		
		
		
	encode,staticfiles: Content negotiation, precompressed files (#4045)
* encode: implement prefer setting * encode: minimum_length configurable via caddyfile * encode: configurable content-types which to encode * file_server: support precompressed files * encode: use ReponseMatcher for conditional encoding of content * linting error & documentation of encode.PrecompressedOrder * encode: allow just one response matcher also change the namespace of the encoders back, I accidently changed to precompressed >.> default matchers include a * to match to any charset, that may be appended * rounding of the PR * added integration tests for new caddyfile directives * improved various doc strings (punctuation and typos) * added json tag for file_server precompress order and encode matcher * file_server: add vary header, remove accept-ranges when serving precompressed files * encode: move Suffix implementation to precompressed modules
This commit is contained in:
		
							parent
							
								
									75f797debd
								
							
						
					
					
						commit
						f35a7fa466
					
				
					 12 changed files with 768 additions and 49 deletions
				
			
		| 
						 | 
				
			
			@ -1,6 +1,8 @@
 | 
			
		|||
package encode
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -10,3 +12,252 @@ func BenchmarkOpenResponseWriter(b *testing.B) {
 | 
			
		|||
		enc.openResponseWriter("test", nil)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPreferOrder(t *testing.T) {
 | 
			
		||||
	testCases := []struct {
 | 
			
		||||
		name     string
 | 
			
		||||
		accept   string
 | 
			
		||||
		prefer   []string
 | 
			
		||||
		expected []string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:     "PreferOrder(): 4 accept, 3 prefer",
 | 
			
		||||
			accept:   "deflate, gzip, br, zstd",
 | 
			
		||||
			prefer:   []string{"zstd", "br", "gzip"},
 | 
			
		||||
			expected: []string{"zstd", "br", "gzip", "deflate"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "PreferOrder(): 2 accept, 3 prefer",
 | 
			
		||||
			accept:   "deflate, zstd",
 | 
			
		||||
			prefer:   []string{"zstd", "br", "gzip"},
 | 
			
		||||
			expected: []string{"zstd", "deflate"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "PreferOrder(): 2 accept (1 empty), 3 prefer",
 | 
			
		||||
			accept:   "gzip,,zstd",
 | 
			
		||||
			prefer:   []string{"zstd", "br", "gzip"},
 | 
			
		||||
			expected: []string{"zstd", "gzip", ""},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "PreferOrder(): 1 accept, 2 prefer",
 | 
			
		||||
			accept:   "gzip",
 | 
			
		||||
			prefer:   []string{"zstd", "gzip"},
 | 
			
		||||
			expected: []string{"gzip"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "PreferOrder(): 4 accept (1 duplicate), 1 prefer",
 | 
			
		||||
			accept:   "deflate, gzip, br, br",
 | 
			
		||||
			prefer:   []string{"br"},
 | 
			
		||||
			expected: []string{"br", "br", "deflate", "gzip"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "PreferOrder(): empty accept, 0 prefer",
 | 
			
		||||
			accept:   "",
 | 
			
		||||
			prefer:   []string{},
 | 
			
		||||
			expected: []string{},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "PreferOrder(): empty accept, 1 prefer",
 | 
			
		||||
			accept:   "",
 | 
			
		||||
			prefer:   []string{"gzip"},
 | 
			
		||||
			expected: []string{},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "PreferOrder(): with q-factor",
 | 
			
		||||
			accept:   "deflate;q=0.8, gzip;q=0.4, br;q=0.2, zstd",
 | 
			
		||||
			prefer:   []string{"gzip"},
 | 
			
		||||
			expected: []string{"zstd", "deflate", "gzip", "br"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "PreferOrder(): with q-factor, no prefer",
 | 
			
		||||
			accept:   "deflate;q=0.8, gzip;q=0.4, br;q=0.2, zstd",
 | 
			
		||||
			prefer:   []string{},
 | 
			
		||||
			expected: []string{"zstd", "deflate", "gzip", "br"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "PreferOrder(): q-factor=0 filtered out",
 | 
			
		||||
			accept:   "deflate;q=0.1, gzip;q=0.4, br;q=0.5, zstd;q=0",
 | 
			
		||||
			prefer:   []string{"gzip"},
 | 
			
		||||
			expected: []string{"br", "gzip", "deflate"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "PreferOrder(): q-factor=0 filtered out, no prefer",
 | 
			
		||||
			accept:   "deflate;q=0.1, gzip;q=0.4, br;q=0.5, zstd;q=0",
 | 
			
		||||
			prefer:   []string{},
 | 
			
		||||
			expected: []string{"br", "gzip", "deflate"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "PreferOrder(): with invalid q-factor",
 | 
			
		||||
			accept:   "br, deflate, gzip;q=2, zstd;q=0.1",
 | 
			
		||||
			prefer:   []string{"zstd", "gzip"},
 | 
			
		||||
			expected: []string{"gzip", "br", "deflate", "zstd"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "PreferOrder(): with invalid q-factor, no prefer",
 | 
			
		||||
			accept:   "br, deflate, gzip;q=2, zstd;q=0.1",
 | 
			
		||||
			prefer:   []string{},
 | 
			
		||||
			expected: []string{"br", "deflate", "gzip", "zstd"},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	enc := new(Encode)
 | 
			
		||||
	r, _ := http.NewRequest("", "", nil)
 | 
			
		||||
 | 
			
		||||
	for _, test := range testCases {
 | 
			
		||||
		t.Run(test.name, func(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
			if test.accept == "" {
 | 
			
		||||
				r.Header.Del("Accept-Encoding")
 | 
			
		||||
			} else {
 | 
			
		||||
				r.Header.Set("Accept-Encoding", test.accept)
 | 
			
		||||
			}
 | 
			
		||||
			enc.Prefer = test.prefer
 | 
			
		||||
			result := AcceptedEncodings(r, enc.Prefer)
 | 
			
		||||
			if !sliceEqual(result, test.expected) {
 | 
			
		||||
				t.Errorf("AcceptedEncodings() actual: %s expected: %s",
 | 
			
		||||
					result,
 | 
			
		||||
					test.expected)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sliceEqual(a, b []string) bool {
 | 
			
		||||
	if len(a) != len(b) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	for i := range a {
 | 
			
		||||
		if a[i] != b[i] {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestValidate(t *testing.T) {
 | 
			
		||||
	type testCase struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		prefer  []string
 | 
			
		||||
		wantErr bool
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	var testCases []testCase
 | 
			
		||||
	enc := new(Encode)
 | 
			
		||||
 | 
			
		||||
	enc.writerPools = map[string]*sync.Pool{
 | 
			
		||||
		"zstd": nil,
 | 
			
		||||
		"gzip": nil,
 | 
			
		||||
		"br":   nil,
 | 
			
		||||
	}
 | 
			
		||||
	testCases = []testCase{
 | 
			
		||||
		{
 | 
			
		||||
			name:    "ValidatePrefer (zstd, gzip & br enabled): valid order with all encoder",
 | 
			
		||||
			prefer:  []string{"zstd", "br", "gzip"},
 | 
			
		||||
			wantErr: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "ValidatePrefer (zstd, gzip & br enabled): valid order with 2 out of 3 encoders",
 | 
			
		||||
			prefer:  []string{"br", "gzip"},
 | 
			
		||||
			wantErr: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "ValidatePrefer (zstd, gzip & br enabled): valid order with 1 out of 3 encoders",
 | 
			
		||||
			prefer:  []string{"gzip"},
 | 
			
		||||
			wantErr: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "ValidatePrefer (zstd, gzip & br enabled): 1 duplicated (once) encoder",
 | 
			
		||||
			prefer:  []string{"gzip", "zstd", "gzip"},
 | 
			
		||||
			wantErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "ValidatePrefer (zstd, gzip & br enabled): 1 not enabled encoder in prefer list",
 | 
			
		||||
			prefer:  []string{"br", "zstd", "gzip", "deflate"},
 | 
			
		||||
			wantErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "ValidatePrefer (zstd, gzip & br enabled): no prefer list",
 | 
			
		||||
			prefer:  []string{},
 | 
			
		||||
			wantErr: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range testCases {
 | 
			
		||||
		t.Run(test.name, func(t *testing.T) {
 | 
			
		||||
			enc.Prefer = test.prefer
 | 
			
		||||
			err = enc.Validate()
 | 
			
		||||
			if (err != nil) != test.wantErr {
 | 
			
		||||
				t.Errorf("Validate() error = %v, wantErr = %v", err, test.wantErr)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	enc.writerPools = map[string]*sync.Pool{
 | 
			
		||||
		"zstd": nil,
 | 
			
		||||
		"gzip": nil,
 | 
			
		||||
	}
 | 
			
		||||
	testCases = []testCase{
 | 
			
		||||
		{
 | 
			
		||||
			name:    "ValidatePrefer (zstd & gzip enabled): 1 not enabled encoder in prefer list",
 | 
			
		||||
			prefer:  []string{"zstd", "br", "gzip"},
 | 
			
		||||
			wantErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "ValidatePrefer (zstd & gzip enabled): 2 not enabled encoder in prefer list",
 | 
			
		||||
			prefer:  []string{"br", "zstd", "gzip", "deflate"},
 | 
			
		||||
			wantErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "ValidatePrefer (zstd & gzip enabled): only not enabled encoder in prefer list",
 | 
			
		||||
			prefer:  []string{"deflate", "br", "gzip"},
 | 
			
		||||
			wantErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "ValidatePrefer (zstd & gzip enabled): 1 duplicated (once) encoder in prefer list",
 | 
			
		||||
			prefer:  []string{"gzip", "zstd", "gzip"},
 | 
			
		||||
			wantErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "ValidatePrefer (zstd & gzip enabled): 1 duplicated (twice) encoder in prefer list",
 | 
			
		||||
			prefer:  []string{"gzip", "zstd", "gzip", "gzip"},
 | 
			
		||||
			wantErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "ValidatePrefer (zstd & gzip enabled): 1 duplicated encoder in prefer list",
 | 
			
		||||
			prefer:  []string{"zstd", "zstd", "gzip", "gzip"},
 | 
			
		||||
			wantErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "ValidatePrefer (zstd & gzip enabled): 1 duplicated not enabled encoder in prefer list",
 | 
			
		||||
			prefer:  []string{"br", "br", "gzip"},
 | 
			
		||||
			wantErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "ValidatePrefer (zstd & gzip enabled): 2 duplicated not enabled encoder in prefer list",
 | 
			
		||||
			prefer:  []string{"br", "deflate", "br", "deflate"},
 | 
			
		||||
			wantErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "ValidatePrefer (zstd & gzip enabled): valid order zstd first",
 | 
			
		||||
			prefer:  []string{"zstd", "gzip"},
 | 
			
		||||
			wantErr: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:    "ValidatePrefer (zstd & gzip enabled): valid order gzip first",
 | 
			
		||||
			prefer:  []string{"gzip", "zstd"},
 | 
			
		||||
			wantErr: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range testCases {
 | 
			
		||||
		t.Run(test.name, func(t *testing.T) {
 | 
			
		||||
			enc.Prefer = test.prefer
 | 
			
		||||
			err = enc.Validate()
 | 
			
		||||
			if (err != nil) != test.wantErr {
 | 
			
		||||
				t.Errorf("Validate() error = %v, wantErr = %v", err, test.wantErr)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue