vendor: update all dependencies
This commit is contained in:
+2077
-2077
File diff suppressed because it is too large
Load Diff
+14
-14
@@ -589,20 +589,20 @@ func TestConvertNewlines(t *testing.T) {
|
||||
"Mac\rDOS\r\nUnix\n": "Mac\nDOS\nUnix\n",
|
||||
"Unix\nMac\rDOS\r\n": "Unix\nMac\nDOS\n",
|
||||
"DOS\r\nDOS\r\nDOS\r\n": "DOS\nDOS\nDOS\n",
|
||||
"": "",
|
||||
"\n": "\n",
|
||||
"\n\r": "\n\n",
|
||||
"\r": "\n",
|
||||
"\r\n": "\n",
|
||||
"\r\n\n": "\n\n",
|
||||
"\r\n\r": "\n\n",
|
||||
"\r\n\r\n": "\n\n",
|
||||
"\r\r": "\n\n",
|
||||
"\r\r\n": "\n\n",
|
||||
"\r\r\n\n": "\n\n\n",
|
||||
"\r\r\r\n": "\n\n\n",
|
||||
"\r \n": "\n \n",
|
||||
"xyz": "xyz",
|
||||
"": "",
|
||||
"\n": "\n",
|
||||
"\n\r": "\n\n",
|
||||
"\r": "\n",
|
||||
"\r\n": "\n",
|
||||
"\r\n\n": "\n\n",
|
||||
"\r\n\r": "\n\n",
|
||||
"\r\n\r\n": "\n\n",
|
||||
"\r\r": "\n\n",
|
||||
"\r\r\n": "\n\n",
|
||||
"\r\r\n\n": "\n\n\n",
|
||||
"\r\r\r\n": "\n\n\n",
|
||||
"\r \n": "\n \n",
|
||||
"xyz": "xyz",
|
||||
}
|
||||
for in, want := range testCases {
|
||||
if got := string(convertNewlines([]byte(in))); got != want {
|
||||
|
||||
Generated
Vendored
+1
-6
@@ -2,12 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package httplex contains rules around lexical matters of various
|
||||
// HTTP-related specifications.
|
||||
//
|
||||
// This package is shared by the standard library (which vendors it)
|
||||
// and x/net/http2. It comes with no API stability promise.
|
||||
package httplex
|
||||
package httpguts
|
||||
|
||||
import (
|
||||
"net"
|
||||
Generated
Vendored
+1
-1
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package httplex
|
||||
package httpguts
|
||||
|
||||
import (
|
||||
"testing"
|
||||
+2
-2
@@ -14,8 +14,8 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/lex/httplex"
|
||||
)
|
||||
|
||||
const frameHeaderLen = 9
|
||||
@@ -1462,7 +1462,7 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
|
||||
if VerboseLogs && fr.logReads {
|
||||
fr.debugReadLoggerf("http2: decoded hpack field %+v", hf)
|
||||
}
|
||||
if !httplex.ValidHeaderFieldValue(hf.Value) {
|
||||
if !httpguts.ValidHeaderFieldValue(hf.Value) {
|
||||
invalid = headerFieldValueError(hf.Value)
|
||||
}
|
||||
isPseudo := strings.HasPrefix(hf.Name, ":")
|
||||
|
||||
+6
@@ -389,6 +389,12 @@ func (d *Decoder) callEmit(hf HeaderField) error {
|
||||
|
||||
// (same invariants and behavior as parseHeaderFieldRepr)
|
||||
func (d *Decoder) parseDynamicTableSizeUpdate() error {
|
||||
// RFC 7541, sec 4.2: This dynamic table size update MUST occur at the
|
||||
// beginning of the first header block following the change to the dynamic table size.
|
||||
if d.dynTab.size > 0 {
|
||||
return DecodingError{errors.New("dynamic table size update MUST occur at the beginning of a header block")}
|
||||
}
|
||||
|
||||
buf := d.buf
|
||||
size, buf, err := readVarInt(5, buf)
|
||||
if err != nil {
|
||||
|
||||
+19
@@ -720,3 +720,22 @@ func TestSaveBufLimit(t *testing.T) {
|
||||
t.Fatalf("Write error = %v; want ErrStringLength", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDynamicSizeUpdate(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
enc := NewEncoder(&buf)
|
||||
enc.SetMaxDynamicTableSize(255)
|
||||
enc.WriteField(HeaderField{Name: "foo", Value: "bar"})
|
||||
|
||||
d := NewDecoder(4096, nil)
|
||||
_, err := d.DecodeFull(buf.Bytes())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: got = %v", err)
|
||||
}
|
||||
|
||||
// must fail since the dynamic table update must be at the beginning
|
||||
_, err = d.DecodeFull(buf.Bytes())
|
||||
if err == nil {
|
||||
t.Fatalf("dynamic table size update not at the beginning of a header block")
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -29,7 +29,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/lex/httplex"
|
||||
"golang.org/x/net/http/httpguts"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -179,7 +179,7 @@ var (
|
||||
)
|
||||
|
||||
// validWireHeaderFieldName reports whether v is a valid header field
|
||||
// name (key). See httplex.ValidHeaderName for the base rules.
|
||||
// name (key). See httpguts.ValidHeaderName for the base rules.
|
||||
//
|
||||
// Further, http2 says:
|
||||
// "Just as in HTTP/1.x, header field names are strings of ASCII
|
||||
@@ -191,7 +191,7 @@ func validWireHeaderFieldName(v string) bool {
|
||||
return false
|
||||
}
|
||||
for _, r := range v {
|
||||
if !httplex.IsTokenRune(r) {
|
||||
if !httpguts.IsTokenRune(r) {
|
||||
return false
|
||||
}
|
||||
if 'A' <= r && r <= 'Z' {
|
||||
|
||||
+28
@@ -14,6 +14,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http2/hpack"
|
||||
)
|
||||
@@ -197,3 +198,30 @@ func TestSorterPoolAllocs(t *testing.T) {
|
||||
t.Logf("Keys allocs = %v; want <1", allocs)
|
||||
}
|
||||
}
|
||||
|
||||
// waitCondition reports whether fn eventually returned true,
|
||||
// checking immediately and then every checkEvery amount,
|
||||
// until waitFor has elapsed, at which point it returns false.
|
||||
func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool {
|
||||
deadline := time.Now().Add(waitFor)
|
||||
for time.Now().Before(deadline) {
|
||||
if fn() {
|
||||
return true
|
||||
}
|
||||
time.Sleep(checkEvery)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// waitErrCondition is like waitCondition but with errors instead of bools.
|
||||
func waitErrCondition(waitFor, checkEvery time.Duration, fn func() error) error {
|
||||
deadline := time.Now().Add(waitFor)
|
||||
var err error
|
||||
for time.Now().Before(deadline) {
|
||||
if err = fn(); err == nil {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(checkEvery)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
+4
-1
@@ -1608,7 +1608,10 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
||||
// Sender sending more than they'd declared?
|
||||
if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes {
|
||||
st.body.CloseWithError(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes))
|
||||
return streamError(id, ErrCodeStreamClosed)
|
||||
// RFC 7540, sec 8.1.2.6: A request or response is also malformed if the
|
||||
// value of a content-length header field does not equal the sum of the
|
||||
// DATA frame payload lengths that form the body.
|
||||
return streamError(id, ErrCodeProtocol)
|
||||
}
|
||||
if f.Length > 0 {
|
||||
// Check whether the client has flow control quota.
|
||||
|
||||
+19
@@ -3759,3 +3759,22 @@ func TestIssue20704Race(t *testing.T) {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_Rejects_TooSmall(t *testing.T) {
|
||||
testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
|
||||
return nil
|
||||
}, func(st *serverTester) {
|
||||
st.writeHeaders(HeadersFrameParam{
|
||||
StreamID: 1, // clients send odd numbers
|
||||
BlockFragment: st.encodeHeader(
|
||||
":method", "POST",
|
||||
"content-length", "4",
|
||||
),
|
||||
EndStream: false, // to say DATA frames are coming
|
||||
EndHeaders: true,
|
||||
})
|
||||
st.writeData(1, true, []byte("12345"))
|
||||
|
||||
st.wantRSTStream(1, ErrCodeProtocol)
|
||||
})
|
||||
}
|
||||
|
||||
+10
-6
@@ -27,9 +27,9 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/idna"
|
||||
"golang.org/x/net/lex/httplex"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -567,6 +567,10 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
|
||||
// henc in response to SETTINGS frames?
|
||||
cc.henc = hpack.NewEncoder(&cc.hbuf)
|
||||
|
||||
if t.AllowHTTP {
|
||||
cc.nextStreamID = 3
|
||||
}
|
||||
|
||||
if cs, ok := c.(connectionStater); ok {
|
||||
state := cs.ConnectionState()
|
||||
cc.tlsState = &state
|
||||
@@ -1177,7 +1181,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
|
||||
if host == "" {
|
||||
host = req.URL.Host
|
||||
}
|
||||
host, err := httplex.PunycodeHostPort(host)
|
||||
host, err := httpguts.PunycodeHostPort(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1202,11 +1206,11 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
|
||||
// potentially pollute our hpack state. (We want to be able to
|
||||
// continue to reuse the hpack encoder for future requests)
|
||||
for k, vv := range req.Header {
|
||||
if !httplex.ValidHeaderFieldName(k) {
|
||||
if !httpguts.ValidHeaderFieldName(k) {
|
||||
return nil, fmt.Errorf("invalid HTTP header name %q", k)
|
||||
}
|
||||
for _, v := range vv {
|
||||
if !httplex.ValidHeaderFieldValue(v) {
|
||||
if !httpguts.ValidHeaderFieldValue(v) {
|
||||
return nil, fmt.Errorf("invalid HTTP header value %q for header %q", v, k)
|
||||
}
|
||||
}
|
||||
@@ -2247,7 +2251,7 @@ func (t *Transport) getBodyWriterState(cs *clientStream, body io.Reader) (s body
|
||||
}
|
||||
s.delay = t.expectContinueTimeout()
|
||||
if s.delay == 0 ||
|
||||
!httplex.HeaderValuesContainsToken(
|
||||
!httpguts.HeaderValuesContainsToken(
|
||||
cs.req.Header["Expect"],
|
||||
"100-continue") {
|
||||
return
|
||||
@@ -2302,5 +2306,5 @@ func (s bodyWriterState) scheduleBodyWrite() {
|
||||
// isConnectionCloseRequest reports whether req should use its own
|
||||
// connection for a single request and then close the connection.
|
||||
func isConnectionCloseRequest(req *http.Request) bool {
|
||||
return req.Close || httplex.HeaderValuesContainsToken(req.Header["Connection"], "close")
|
||||
return req.Close || httpguts.HeaderValuesContainsToken(req.Header["Connection"], "close")
|
||||
}
|
||||
|
||||
+4
-3
@@ -2394,11 +2394,12 @@ func TestTransportHandlerBodyClose(t *testing.T) {
|
||||
}
|
||||
tr.CloseIdleConnections()
|
||||
|
||||
gd := runtime.NumGoroutine() - g0
|
||||
if gd > numReq/2 {
|
||||
if !waitCondition(5*time.Second, 100*time.Millisecond, func() bool {
|
||||
gd := runtime.NumGoroutine() - g0
|
||||
return gd < numReq/2
|
||||
}) {
|
||||
t.Errorf("appeared to leak goroutines")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// https://golang.org/issue/15930
|
||||
|
||||
+2
-2
@@ -11,8 +11,8 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/lex/httplex"
|
||||
)
|
||||
|
||||
// writeFramer is implemented by any type that is used to write frames.
|
||||
@@ -350,7 +350,7 @@ func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) {
|
||||
}
|
||||
isTE := k == "transfer-encoding"
|
||||
for _, v := range vv {
|
||||
if !httplex.ValidHeaderFieldValue(v) {
|
||||
if !httpguts.ValidHeaderFieldValue(v) {
|
||||
// TODO: return an error? golang.org/issue/14048
|
||||
// For now just omit it.
|
||||
continue
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ package icmp
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
// A MPLSLabel represents a MPLS label stack entry.
|
||||
// MPLSLabel represents an MPLS label stack entry.
|
||||
type MPLSLabel struct {
|
||||
Label int // label value
|
||||
TC int // traffic class; formerly experimental use
|
||||
@@ -19,7 +19,7 @@ const (
|
||||
typeIncomingMPLSLabelStack = 1
|
||||
)
|
||||
|
||||
// A MPLSLabelStack represents a MPLS label stack.
|
||||
// MPLSLabelStack represents an MPLS label stack.
|
||||
type MPLSLabelStack struct {
|
||||
Class int // extension object class number
|
||||
Type int // extension object sub-type
|
||||
|
||||
+28
-32
@@ -4,38 +4,34 @@
|
||||
// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).
|
||||
package iana // import "golang.org/x/net/internal/iana"
|
||||
|
||||
// Differentiated Services Field Codepoints (DSCP), Updated: 2017-05-12
|
||||
// Differentiated Services Field Codepoints (DSCP), Updated: 2018-05-04
|
||||
const (
|
||||
DiffServCS0 = 0x0 // CS0
|
||||
DiffServCS1 = 0x20 // CS1
|
||||
DiffServCS2 = 0x40 // CS2
|
||||
DiffServCS3 = 0x60 // CS3
|
||||
DiffServCS4 = 0x80 // CS4
|
||||
DiffServCS5 = 0xa0 // CS5
|
||||
DiffServCS6 = 0xc0 // CS6
|
||||
DiffServCS7 = 0xe0 // CS7
|
||||
DiffServAF11 = 0x28 // AF11
|
||||
DiffServAF12 = 0x30 // AF12
|
||||
DiffServAF13 = 0x38 // AF13
|
||||
DiffServAF21 = 0x48 // AF21
|
||||
DiffServAF22 = 0x50 // AF22
|
||||
DiffServAF23 = 0x58 // AF23
|
||||
DiffServAF31 = 0x68 // AF31
|
||||
DiffServAF32 = 0x70 // AF32
|
||||
DiffServAF33 = 0x78 // AF33
|
||||
DiffServAF41 = 0x88 // AF41
|
||||
DiffServAF42 = 0x90 // AF42
|
||||
DiffServAF43 = 0x98 // AF43
|
||||
DiffServEF = 0xb8 // EF
|
||||
DiffServVOICEADMIT = 0xb0 // VOICE-ADMIT
|
||||
)
|
||||
|
||||
// IPv4 TOS Byte and IPv6 Traffic Class Octet, Updated: 2001-09-06
|
||||
const (
|
||||
NotECNTransport = 0x0 // Not-ECT (Not ECN-Capable Transport)
|
||||
ECNTransport1 = 0x1 // ECT(1) (ECN-Capable Transport(1))
|
||||
ECNTransport0 = 0x2 // ECT(0) (ECN-Capable Transport(0))
|
||||
CongestionExperienced = 0x3 // CE (Congestion Experienced)
|
||||
DiffServCS0 = 0x00 // CS0
|
||||
DiffServCS1 = 0x20 // CS1
|
||||
DiffServCS2 = 0x40 // CS2
|
||||
DiffServCS3 = 0x60 // CS3
|
||||
DiffServCS4 = 0x80 // CS4
|
||||
DiffServCS5 = 0xa0 // CS5
|
||||
DiffServCS6 = 0xc0 // CS6
|
||||
DiffServCS7 = 0xe0 // CS7
|
||||
DiffServAF11 = 0x28 // AF11
|
||||
DiffServAF12 = 0x30 // AF12
|
||||
DiffServAF13 = 0x38 // AF13
|
||||
DiffServAF21 = 0x48 // AF21
|
||||
DiffServAF22 = 0x50 // AF22
|
||||
DiffServAF23 = 0x58 // AF23
|
||||
DiffServAF31 = 0x68 // AF31
|
||||
DiffServAF32 = 0x70 // AF32
|
||||
DiffServAF33 = 0x78 // AF33
|
||||
DiffServAF41 = 0x88 // AF41
|
||||
DiffServAF42 = 0x90 // AF42
|
||||
DiffServAF43 = 0x98 // AF43
|
||||
DiffServEF = 0xb8 // EF
|
||||
DiffServVOICEADMIT = 0xb0 // VOICE-ADMIT
|
||||
NotECNTransport = 0x00 // Not-ECT (Not ECN-Capable Transport)
|
||||
ECNTransport1 = 0x01 // ECT(1) (ECN-Capable Transport(1))
|
||||
ECNTransport0 = 0x02 // ECT(0) (ECN-Capable Transport(0))
|
||||
CongestionExperienced = 0x03 // CE (Congestion Experienced)
|
||||
)
|
||||
|
||||
// Protocol Numbers, Updated: 2017-10-13
|
||||
@@ -179,7 +175,7 @@ const (
|
||||
ProtocolReserved = 255 // Reserved
|
||||
)
|
||||
|
||||
// Address Family Numbers, Updated: 2016-10-25
|
||||
// Address Family Numbers, Updated: 2018-04-02
|
||||
const (
|
||||
AddrFamilyIPv4 = 1 // IP (IP version 4)
|
||||
AddrFamilyIPv6 = 2 // IP6 (IP version 6)
|
||||
|
||||
+92
-96
@@ -31,16 +31,12 @@ var registries = []struct {
|
||||
"https://www.iana.org/assignments/dscp-registry/dscp-registry.xml",
|
||||
parseDSCPRegistry,
|
||||
},
|
||||
{
|
||||
"https://www.iana.org/assignments/ipv4-tos-byte/ipv4-tos-byte.xml",
|
||||
parseTOSTCByte,
|
||||
},
|
||||
{
|
||||
"https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml",
|
||||
parseProtocolNumbers,
|
||||
},
|
||||
{
|
||||
"http://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml",
|
||||
"https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml",
|
||||
parseAddrFamilyNumbers,
|
||||
},
|
||||
}
|
||||
@@ -85,31 +81,39 @@ func parseDSCPRegistry(w io.Writer, r io.Reader) error {
|
||||
if err := dec.Decode(&dr); err != nil {
|
||||
return err
|
||||
}
|
||||
drs := dr.escape()
|
||||
fmt.Fprintf(w, "// %s, Updated: %s\n", dr.Title, dr.Updated)
|
||||
fmt.Fprintf(w, "const (\n")
|
||||
for _, dr := range drs {
|
||||
fmt.Fprintf(w, "DiffServ%s = %#x", dr.Name, dr.Value)
|
||||
for _, dr := range dr.escapeDSCP() {
|
||||
fmt.Fprintf(w, "DiffServ%s = %#02x", dr.Name, dr.Value)
|
||||
fmt.Fprintf(w, "// %s\n", dr.OrigName)
|
||||
}
|
||||
for _, er := range dr.escapeECN() {
|
||||
fmt.Fprintf(w, "%s = %#02x", er.Descr, er.Value)
|
||||
fmt.Fprintf(w, "// %s\n", er.OrigDescr)
|
||||
}
|
||||
fmt.Fprintf(w, ")\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
type dscpRegistry struct {
|
||||
XMLName xml.Name `xml:"registry"`
|
||||
Title string `xml:"title"`
|
||||
Updated string `xml:"updated"`
|
||||
Note string `xml:"note"`
|
||||
RegTitle string `xml:"registry>title"`
|
||||
PoolRecords []struct {
|
||||
Name string `xml:"name"`
|
||||
Space string `xml:"space"`
|
||||
} `xml:"registry>record"`
|
||||
Records []struct {
|
||||
Name string `xml:"name"`
|
||||
Space string `xml:"space"`
|
||||
} `xml:"registry>registry>record"`
|
||||
XMLName xml.Name `xml:"registry"`
|
||||
Title string `xml:"title"`
|
||||
Updated string `xml:"updated"`
|
||||
Note string `xml:"note"`
|
||||
Registries []struct {
|
||||
Title string `xml:"title"`
|
||||
Registries []struct {
|
||||
Title string `xml:"title"`
|
||||
Records []struct {
|
||||
Name string `xml:"name"`
|
||||
Space string `xml:"space"`
|
||||
} `xml:"record"`
|
||||
} `xml:"registry"`
|
||||
Records []struct {
|
||||
Value string `xml:"value"`
|
||||
Descr string `xml:"description"`
|
||||
} `xml:"record"`
|
||||
} `xml:"registry"`
|
||||
}
|
||||
|
||||
type canonDSCPRecord struct {
|
||||
@@ -118,92 +122,84 @@ type canonDSCPRecord struct {
|
||||
Value int
|
||||
}
|
||||
|
||||
func (drr *dscpRegistry) escape() []canonDSCPRecord {
|
||||
drs := make([]canonDSCPRecord, len(drr.Records))
|
||||
sr := strings.NewReplacer(
|
||||
"+", "",
|
||||
"-", "",
|
||||
"/", "",
|
||||
".", "",
|
||||
" ", "",
|
||||
)
|
||||
for i, dr := range drr.Records {
|
||||
s := strings.TrimSpace(dr.Name)
|
||||
drs[i].OrigName = s
|
||||
drs[i].Name = sr.Replace(s)
|
||||
n, err := strconv.ParseUint(dr.Space, 2, 8)
|
||||
if err != nil {
|
||||
func (drr *dscpRegistry) escapeDSCP() []canonDSCPRecord {
|
||||
var drs []canonDSCPRecord
|
||||
for _, preg := range drr.Registries {
|
||||
if !strings.Contains(preg.Title, "Differentiated Services Field Codepoints") {
|
||||
continue
|
||||
}
|
||||
drs[i].Value = int(n) << 2
|
||||
for _, reg := range preg.Registries {
|
||||
if !strings.Contains(reg.Title, "Pool 1 Codepoints") {
|
||||
continue
|
||||
}
|
||||
drs = make([]canonDSCPRecord, len(reg.Records))
|
||||
sr := strings.NewReplacer(
|
||||
"+", "",
|
||||
"-", "",
|
||||
"/", "",
|
||||
".", "",
|
||||
" ", "",
|
||||
)
|
||||
for i, dr := range reg.Records {
|
||||
s := strings.TrimSpace(dr.Name)
|
||||
drs[i].OrigName = s
|
||||
drs[i].Name = sr.Replace(s)
|
||||
n, err := strconv.ParseUint(dr.Space, 2, 8)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
drs[i].Value = int(n) << 2
|
||||
}
|
||||
}
|
||||
}
|
||||
return drs
|
||||
}
|
||||
|
||||
func parseTOSTCByte(w io.Writer, r io.Reader) error {
|
||||
dec := xml.NewDecoder(r)
|
||||
var ttb tosTCByte
|
||||
if err := dec.Decode(&ttb); err != nil {
|
||||
return err
|
||||
}
|
||||
trs := ttb.escape()
|
||||
fmt.Fprintf(w, "// %s, Updated: %s\n", ttb.Title, ttb.Updated)
|
||||
fmt.Fprintf(w, "const (\n")
|
||||
for _, tr := range trs {
|
||||
fmt.Fprintf(w, "%s = %#x", tr.Keyword, tr.Value)
|
||||
fmt.Fprintf(w, "// %s\n", tr.OrigKeyword)
|
||||
}
|
||||
fmt.Fprintf(w, ")\n")
|
||||
return nil
|
||||
type canonECNRecord struct {
|
||||
OrigDescr string
|
||||
Descr string
|
||||
Value int
|
||||
}
|
||||
|
||||
type tosTCByte struct {
|
||||
XMLName xml.Name `xml:"registry"`
|
||||
Title string `xml:"title"`
|
||||
Updated string `xml:"updated"`
|
||||
Note string `xml:"note"`
|
||||
RegTitle string `xml:"registry>title"`
|
||||
Records []struct {
|
||||
Binary string `xml:"binary"`
|
||||
Keyword string `xml:"keyword"`
|
||||
} `xml:"registry>record"`
|
||||
}
|
||||
|
||||
type canonTOSTCByteRecord struct {
|
||||
OrigKeyword string
|
||||
Keyword string
|
||||
Value int
|
||||
}
|
||||
|
||||
func (ttb *tosTCByte) escape() []canonTOSTCByteRecord {
|
||||
trs := make([]canonTOSTCByteRecord, len(ttb.Records))
|
||||
sr := strings.NewReplacer(
|
||||
"Capable", "",
|
||||
"(", "",
|
||||
")", "",
|
||||
"+", "",
|
||||
"-", "",
|
||||
"/", "",
|
||||
".", "",
|
||||
" ", "",
|
||||
)
|
||||
for i, tr := range ttb.Records {
|
||||
s := strings.TrimSpace(tr.Keyword)
|
||||
trs[i].OrigKeyword = s
|
||||
ss := strings.Split(s, " ")
|
||||
if len(ss) > 1 {
|
||||
trs[i].Keyword = strings.Join(ss[1:], " ")
|
||||
} else {
|
||||
trs[i].Keyword = ss[0]
|
||||
}
|
||||
trs[i].Keyword = sr.Replace(trs[i].Keyword)
|
||||
n, err := strconv.ParseUint(tr.Binary, 2, 8)
|
||||
if err != nil {
|
||||
func (drr *dscpRegistry) escapeECN() []canonECNRecord {
|
||||
var ers []canonECNRecord
|
||||
for _, reg := range drr.Registries {
|
||||
if !strings.Contains(reg.Title, "ECN Field") {
|
||||
continue
|
||||
}
|
||||
trs[i].Value = int(n)
|
||||
ers = make([]canonECNRecord, len(reg.Records))
|
||||
sr := strings.NewReplacer(
|
||||
"Capable", "",
|
||||
"Not-ECT", "",
|
||||
"ECT(1)", "",
|
||||
"ECT(0)", "",
|
||||
"CE", "",
|
||||
"(", "",
|
||||
")", "",
|
||||
"+", "",
|
||||
"-", "",
|
||||
"/", "",
|
||||
".", "",
|
||||
" ", "",
|
||||
)
|
||||
for i, er := range reg.Records {
|
||||
s := strings.TrimSpace(er.Descr)
|
||||
ers[i].OrigDescr = s
|
||||
ss := strings.Split(s, " ")
|
||||
if len(ss) > 1 {
|
||||
ers[i].Descr = strings.Join(ss[1:], " ")
|
||||
} else {
|
||||
ers[i].Descr = ss[0]
|
||||
}
|
||||
ers[i].Descr = sr.Replace(er.Descr)
|
||||
n, err := strconv.ParseUint(er.Value, 2, 8)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ers[i].Value = int(n)
|
||||
}
|
||||
}
|
||||
return trs
|
||||
return ers
|
||||
}
|
||||
|
||||
func parseProtocolNumbers(w io.Writer, r io.Reader) error {
|
||||
|
||||
+41
-29
@@ -17,19 +17,11 @@ import (
|
||||
"golang.org/x/net/internal/sockstest"
|
||||
)
|
||||
|
||||
const (
|
||||
targetNetwork = "tcp6"
|
||||
targetHostname = "fqdn.doesnotexist"
|
||||
targetHostIP = "2001:db8::1"
|
||||
targetPort = "5963"
|
||||
)
|
||||
|
||||
func TestDial(t *testing.T) {
|
||||
t.Run("Connect", func(t *testing.T) {
|
||||
ss, err := sockstest.NewServer(sockstest.NoAuthRequired, sockstest.NoProxyRequired)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ss.Close()
|
||||
d := socks.NewDialer(ss.Addr().Network(), ss.Addr().String())
|
||||
@@ -41,21 +33,45 @@ func TestDial(t *testing.T) {
|
||||
Username: "username",
|
||||
Password: "password",
|
||||
}).Authenticate
|
||||
c, err := d.Dial(targetNetwork, net.JoinHostPort(targetHostIP, targetPort))
|
||||
if err == nil {
|
||||
c.(*socks.Conn).BoundAddr()
|
||||
c.Close()
|
||||
}
|
||||
c, err := d.DialContext(context.Background(), ss.TargetAddr().Network(), ss.TargetAddr().String())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
t.Fatal(err)
|
||||
}
|
||||
c.(*socks.Conn).BoundAddr()
|
||||
c.Close()
|
||||
})
|
||||
t.Run("ConnectWithConn", func(t *testing.T) {
|
||||
ss, err := sockstest.NewServer(sockstest.NoAuthRequired, sockstest.NoProxyRequired)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ss.Close()
|
||||
c, err := net.Dial(ss.Addr().Network(), ss.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
d := socks.NewDialer(ss.Addr().Network(), ss.Addr().String())
|
||||
d.AuthMethods = []socks.AuthMethod{
|
||||
socks.AuthMethodNotRequired,
|
||||
socks.AuthMethodUsernamePassword,
|
||||
}
|
||||
d.Authenticate = (&socks.UsernamePassword{
|
||||
Username: "username",
|
||||
Password: "password",
|
||||
}).Authenticate
|
||||
a, err := d.DialWithConn(context.Background(), c, ss.TargetAddr().Network(), ss.TargetAddr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, ok := a.(*socks.Addr); !ok {
|
||||
t.Fatalf("got %+v; want socks.Addr", a)
|
||||
}
|
||||
})
|
||||
t.Run("Cancel", func(t *testing.T) {
|
||||
ss, err := sockstest.NewServer(sockstest.NoAuthRequired, blackholeCmdFunc)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ss.Close()
|
||||
d := socks.NewDialer(ss.Addr().Network(), ss.Addr().String())
|
||||
@@ -63,7 +79,7 @@ func TestDial(t *testing.T) {
|
||||
defer cancel()
|
||||
dialErr := make(chan error)
|
||||
go func() {
|
||||
c, err := d.DialContext(ctx, ss.TargetAddr().Network(), net.JoinHostPort(targetHostname, targetPort))
|
||||
c, err := d.DialContext(ctx, ss.TargetAddr().Network(), ss.TargetAddr().String())
|
||||
if err == nil {
|
||||
c.Close()
|
||||
}
|
||||
@@ -73,41 +89,37 @@ func TestDial(t *testing.T) {
|
||||
cancel()
|
||||
err = <-dialErr
|
||||
if perr, nerr := parseDialError(err); perr != context.Canceled && nerr == nil {
|
||||
t.Errorf("got %v; want context.Canceled or equivalent", err)
|
||||
return
|
||||
t.Fatalf("got %v; want context.Canceled or equivalent", err)
|
||||
}
|
||||
})
|
||||
t.Run("Deadline", func(t *testing.T) {
|
||||
ss, err := sockstest.NewServer(sockstest.NoAuthRequired, blackholeCmdFunc)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ss.Close()
|
||||
d := socks.NewDialer(ss.Addr().Network(), ss.Addr().String())
|
||||
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(100*time.Millisecond))
|
||||
defer cancel()
|
||||
c, err := d.DialContext(ctx, ss.TargetAddr().Network(), net.JoinHostPort(targetHostname, targetPort))
|
||||
c, err := d.DialContext(ctx, ss.TargetAddr().Network(), ss.TargetAddr().String())
|
||||
if err == nil {
|
||||
c.Close()
|
||||
}
|
||||
if perr, nerr := parseDialError(err); perr != context.DeadlineExceeded && nerr == nil {
|
||||
t.Errorf("got %v; want context.DeadlineExceeded or equivalent", err)
|
||||
return
|
||||
t.Fatalf("got %v; want context.DeadlineExceeded or equivalent", err)
|
||||
}
|
||||
})
|
||||
t.Run("WithRogueServer", func(t *testing.T) {
|
||||
ss, err := sockstest.NewServer(sockstest.NoAuthRequired, rogueCmdFunc)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ss.Close()
|
||||
d := socks.NewDialer(ss.Addr().Network(), ss.Addr().String())
|
||||
for i := 0; i < 2*len(rogueCmdList); i++ {
|
||||
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(100*time.Millisecond))
|
||||
defer cancel()
|
||||
c, err := d.DialContext(ctx, targetNetwork, net.JoinHostPort(targetHostIP, targetPort))
|
||||
c, err := d.DialContext(ctx, ss.TargetAddr().Network(), ss.TargetAddr().String())
|
||||
if err == nil {
|
||||
t.Log(c.(*socks.Conn).BoundAddr())
|
||||
c.Close()
|
||||
|
||||
+65
-14
@@ -75,7 +75,7 @@ const (
|
||||
|
||||
AuthMethodNotRequired AuthMethod = 0x00 // no authentication required
|
||||
AuthMethodUsernamePassword AuthMethod = 0x02 // use username/password
|
||||
AuthMethodNoAcceptableMethods AuthMethod = 0xff // no acceptable authetication methods
|
||||
AuthMethodNoAcceptableMethods AuthMethod = 0xff // no acceptable authentication methods
|
||||
|
||||
StatusSucceeded Reply = 0x00
|
||||
)
|
||||
@@ -149,20 +149,13 @@ type Dialer struct {
|
||||
// See func Dial of the net package of standard library for a
|
||||
// description of the network and address parameters.
|
||||
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
switch network {
|
||||
case "tcp", "tcp6", "tcp4":
|
||||
default:
|
||||
if err := d.validateTarget(network, address); err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("network not implemented")}
|
||||
}
|
||||
switch d.cmd {
|
||||
case CmdConnect, cmdBind:
|
||||
default:
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("command not implemented")}
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
|
||||
}
|
||||
var err error
|
||||
var c net.Conn
|
||||
@@ -185,11 +178,69 @@ func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.
|
||||
return &Conn{Conn: c, boundAddr: a}, nil
|
||||
}
|
||||
|
||||
// DialWithConn initiates a connection from SOCKS server to the target
|
||||
// network and address using the connection c that is already
|
||||
// connected to the SOCKS server.
|
||||
//
|
||||
// It returns the connection's local address assigned by the SOCKS
|
||||
// server.
|
||||
func (d *Dialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) {
|
||||
if err := d.validateTarget(network, address); err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
if ctx == nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
|
||||
}
|
||||
a, err := d.connect(ctx, c, address)
|
||||
if err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Dial connects to the provided address on the provided network.
|
||||
//
|
||||
// Deprecated: Use DialContext instead.
|
||||
// Unlike DialContext, it returns a raw transport connection instead
|
||||
// of a forward proxy connection.
|
||||
//
|
||||
// Deprecated: Use DialContext or DialWithConn instead.
|
||||
func (d *Dialer) Dial(network, address string) (net.Conn, error) {
|
||||
return d.DialContext(context.Background(), network, address)
|
||||
if err := d.validateTarget(network, address); err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
var err error
|
||||
var c net.Conn
|
||||
if d.ProxyDial != nil {
|
||||
c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress)
|
||||
} else {
|
||||
c, err = net.Dial(d.proxyNetwork, d.proxyAddress)
|
||||
}
|
||||
if err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (d *Dialer) validateTarget(network, address string) error {
|
||||
switch network {
|
||||
case "tcp", "tcp6", "tcp4":
|
||||
default:
|
||||
return errors.New("network not implemented")
|
||||
}
|
||||
switch d.cmd {
|
||||
case CmdConnect, cmdBind:
|
||||
default:
|
||||
return errors.New("command not implemented")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Dialer) pathAddrs(address string) (proxy, dst net.Addr, err error) {
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@ func (h *Header) Marshal() ([]byte, error) {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Parse parses b as an IPv4 header and sotres the result in h.
|
||||
// Parse parses b as an IPv4 header and stores the result in h.
|
||||
func (h *Header) Parse(b []byte) error {
|
||||
if h == nil || len(b) < HeaderLen {
|
||||
return errHeaderTooShort
|
||||
|
||||
+9662
-9451
File diff suppressed because it is too large
Load Diff
+364
-15
File diff suppressed because it is too large
Load Diff
+53
-1
@@ -7,6 +7,7 @@ package webdav
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
@@ -376,10 +377,37 @@ func findContentLength(ctx context.Context, fs FileSystem, ls LockSystem, name s
|
||||
}
|
||||
|
||||
func findLastModified(ctx context.Context, fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
|
||||
return fi.ModTime().Format(http.TimeFormat), nil
|
||||
return fi.ModTime().UTC().Format(http.TimeFormat), nil
|
||||
}
|
||||
|
||||
// ErrNotImplemented should be returned by optional interfaces if they
|
||||
// want the original implementation to be used.
|
||||
var ErrNotImplemented = errors.New("not implemented")
|
||||
|
||||
// ContentTyper is an optional interface for the os.FileInfo
|
||||
// objects returned by the FileSystem.
|
||||
//
|
||||
// If this interface is defined then it will be used to read the
|
||||
// content type from the object.
|
||||
//
|
||||
// If this interface is not defined the file will be opened and the
|
||||
// content type will be guessed from the initial contents of the file.
|
||||
type ContentTyper interface {
|
||||
// ContentType returns the content type for the file.
|
||||
//
|
||||
// If this returns error ErrNotImplemented then the error will
|
||||
// be ignored and the base implementation will be used
|
||||
// instead.
|
||||
ContentType(ctx context.Context) (string, error)
|
||||
}
|
||||
|
||||
func findContentType(ctx context.Context, fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
|
||||
if do, ok := fi.(ContentTyper); ok {
|
||||
ctype, err := do.ContentType(ctx)
|
||||
if err != ErrNotImplemented {
|
||||
return ctype, err
|
||||
}
|
||||
}
|
||||
f, err := fs.OpenFile(ctx, name, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -402,7 +430,31 @@ func findContentType(ctx context.Context, fs FileSystem, ls LockSystem, name str
|
||||
return ctype, err
|
||||
}
|
||||
|
||||
// ETager is an optional interface for the os.FileInfo objects
|
||||
// returned by the FileSystem.
|
||||
//
|
||||
// If this interface is defined then it will be used to read the ETag
|
||||
// for the object.
|
||||
//
|
||||
// If this interface is not defined an ETag will be computed using the
|
||||
// ModTime() and the Size() methods of the os.FileInfo object.
|
||||
type ETager interface {
|
||||
// ETag returns an ETag for the file. This should be of the
|
||||
// form "value" or W/"value"
|
||||
//
|
||||
// If this returns error ErrNotImplemented then the error will
|
||||
// be ignored and the base implementation will be used
|
||||
// instead.
|
||||
ETag(ctx context.Context) (string, error)
|
||||
}
|
||||
|
||||
func findETag(ctx context.Context, fs FileSystem, ls LockSystem, name string, fi os.FileInfo) (string, error) {
|
||||
if do, ok := fi.(ETager); ok {
|
||||
etag, err := do.ETag(ctx)
|
||||
if err != ErrNotImplemented {
|
||||
return etag, err
|
||||
}
|
||||
}
|
||||
// The Apache http 2.4 web server by default concatenates the
|
||||
// modification time and size of a file. We replicate the heuristic
|
||||
// with nanosecond granularity.
|
||||
|
||||
+105
-1
@@ -10,6 +10,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
@@ -29,7 +30,7 @@ func TestMemPS(t *testing.T) {
|
||||
for i, p := range pst.Props {
|
||||
switch p.XMLName {
|
||||
case xml.Name{Space: "DAV:", Local: "getlastmodified"}:
|
||||
p.InnerXML = []byte(fi.ModTime().Format(http.TimeFormat))
|
||||
p.InnerXML = []byte(fi.ModTime().UTC().Format(http.TimeFormat))
|
||||
pst.Props[i] = p
|
||||
case xml.Name{Space: "DAV:", Local: "getetag"}:
|
||||
if fi.IsDir() {
|
||||
@@ -611,3 +612,106 @@ func (f noDeadPropsFile) Readdir(count int) ([]os.FileInfo, error) { return f.f
|
||||
func (f noDeadPropsFile) Seek(off int64, whence int) (int64, error) { return f.f.Seek(off, whence) }
|
||||
func (f noDeadPropsFile) Stat() (os.FileInfo, error) { return f.f.Stat() }
|
||||
func (f noDeadPropsFile) Write(p []byte) (int, error) { return f.f.Write(p) }
|
||||
|
||||
type overrideContentType struct {
|
||||
os.FileInfo
|
||||
contentType string
|
||||
err error
|
||||
}
|
||||
|
||||
func (o *overrideContentType) ContentType(ctx context.Context) (string, error) {
|
||||
return o.contentType, o.err
|
||||
}
|
||||
|
||||
func TestFindContentTypeOverride(t *testing.T) {
|
||||
fs, err := buildTestFS([]string{"touch /file"})
|
||||
if err != nil {
|
||||
t.Fatalf("cannot create test filesystem: %v", err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
fi, err := fs.Stat(ctx, "/file")
|
||||
if err != nil {
|
||||
t.Fatalf("cannot Stat /file: %v", err)
|
||||
}
|
||||
|
||||
// Check non overridden case
|
||||
originalContentType, err := findContentType(ctx, fs, nil, "/file", fi)
|
||||
if err != nil {
|
||||
t.Fatalf("findContentType /file failed: %v", err)
|
||||
}
|
||||
if originalContentType != "text/plain; charset=utf-8" {
|
||||
t.Fatalf("ContentType wrong want %q got %q", "text/plain; charset=utf-8", originalContentType)
|
||||
}
|
||||
|
||||
// Now try overriding the ContentType
|
||||
o := &overrideContentType{fi, "OverriddenContentType", nil}
|
||||
ContentType, err := findContentType(ctx, fs, nil, "/file", o)
|
||||
if err != nil {
|
||||
t.Fatalf("findContentType /file failed: %v", err)
|
||||
}
|
||||
if ContentType != o.contentType {
|
||||
t.Fatalf("ContentType wrong want %q got %q", o.contentType, ContentType)
|
||||
}
|
||||
|
||||
// Now return ErrNotImplemented and check we get the original content type
|
||||
o = &overrideContentType{fi, "OverriddenContentType", ErrNotImplemented}
|
||||
ContentType, err = findContentType(ctx, fs, nil, "/file", o)
|
||||
if err != nil {
|
||||
t.Fatalf("findContentType /file failed: %v", err)
|
||||
}
|
||||
if ContentType != originalContentType {
|
||||
t.Fatalf("ContentType wrong want %q got %q", originalContentType, ContentType)
|
||||
}
|
||||
}
|
||||
|
||||
type overrideETag struct {
|
||||
os.FileInfo
|
||||
eTag string
|
||||
err error
|
||||
}
|
||||
|
||||
func (o *overrideETag) ETag(ctx context.Context) (string, error) {
|
||||
return o.eTag, o.err
|
||||
}
|
||||
|
||||
func TestFindETagOverride(t *testing.T) {
|
||||
fs, err := buildTestFS([]string{"touch /file"})
|
||||
if err != nil {
|
||||
t.Fatalf("cannot create test filesystem: %v", err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
fi, err := fs.Stat(ctx, "/file")
|
||||
if err != nil {
|
||||
t.Fatalf("cannot Stat /file: %v", err)
|
||||
}
|
||||
|
||||
// Check non overridden case
|
||||
originalETag, err := findETag(ctx, fs, nil, "/file", fi)
|
||||
if err != nil {
|
||||
t.Fatalf("findETag /file failed: %v", err)
|
||||
}
|
||||
matchETag := regexp.MustCompile(`^"-?[0-9a-f]{6,}"$`)
|
||||
if !matchETag.MatchString(originalETag) {
|
||||
t.Fatalf("ETag wrong, wanted something matching %v got %q", matchETag, originalETag)
|
||||
}
|
||||
|
||||
// Now try overriding the ETag
|
||||
o := &overrideETag{fi, `"OverriddenETag"`, nil}
|
||||
ETag, err := findETag(ctx, fs, nil, "/file", o)
|
||||
if err != nil {
|
||||
t.Fatalf("findETag /file failed: %v", err)
|
||||
}
|
||||
if ETag != o.eTag {
|
||||
t.Fatalf("ETag wrong want %q got %q", o.eTag, ETag)
|
||||
}
|
||||
|
||||
// Now return ErrNotImplemented and check we get the original Etag
|
||||
o = &overrideETag{fi, `"OverriddenETag"`, ErrNotImplemented}
|
||||
ETag, err = findETag(ctx, fs, nil, "/file", o)
|
||||
if err != nil {
|
||||
t.Fatalf("findETag /file failed: %v", err)
|
||||
}
|
||||
if ETag != originalETag {
|
||||
t.Fatalf("ETag wrong want %q got %q", originalETag, ETag)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user