mirror of
https://github.com/soheilhy/cmux.git
synced 2024-11-10 03:31:52 +08:00
Add matchers for header value prefixes
This commit is contained in:
parent
7e08502c7a
commit
bb79a83465
21
cmux_test.go
21
cmux_test.go
@ -478,6 +478,20 @@ func TestHTTP2(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHTTP2MatchHeaderField(t *testing.T) {
|
func TestHTTP2MatchHeaderField(t *testing.T) {
|
||||||
|
testHTTP2MatchHeaderField(t, HTTP2HeaderField, "value", "value", "anothervalue")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHTTP2MatchHeaderFieldPrefix(t *testing.T) {
|
||||||
|
testHTTP2MatchHeaderField(t, HTTP2HeaderFieldPrefix, "application/grpc+proto", "application/grpc", "application/json")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testHTTP2MatchHeaderField(
|
||||||
|
t *testing.T,
|
||||||
|
matcherConstructor func(string, string) Matcher,
|
||||||
|
headerValue string,
|
||||||
|
matchValue string,
|
||||||
|
notMatchValue string,
|
||||||
|
) {
|
||||||
defer leakCheck(t)()
|
defer leakCheck(t)()
|
||||||
errCh := make(chan error)
|
errCh := make(chan error)
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -488,7 +502,6 @@ func TestHTTP2MatchHeaderField(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
name := "name"
|
name := "name"
|
||||||
value := "value"
|
|
||||||
writer, reader := net.Pipe()
|
writer, reader := net.Pipe()
|
||||||
go func() {
|
go func() {
|
||||||
if _, err := io.WriteString(writer, http2.ClientPreface); err != nil {
|
if _, err := io.WriteString(writer, http2.ClientPreface); err != nil {
|
||||||
@ -496,7 +509,7 @@ func TestHTTP2MatchHeaderField(t *testing.T) {
|
|||||||
}
|
}
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
enc := hpack.NewEncoder(&buf)
|
enc := hpack.NewEncoder(&buf)
|
||||||
if err := enc.WriteField(hpack.HeaderField{Name: name, Value: value}); err != nil {
|
if err := enc.WriteField(hpack.HeaderField{Name: name, Value: headerValue}); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
framer := http2.NewFramer(writer, nil)
|
framer := http2.NewFramer(writer, nil)
|
||||||
@ -524,9 +537,9 @@ func TestHTTP2MatchHeaderField(t *testing.T) {
|
|||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
// Create a matcher that cannot match the response.
|
// Create a matcher that cannot match the response.
|
||||||
muxl.Match(HTTP2HeaderField(name, "another"+value))
|
muxl.Match(matcherConstructor(name, notMatchValue))
|
||||||
// Then match with the expected field.
|
// Then match with the expected field.
|
||||||
h2l := muxl.Match(HTTP2HeaderField(name, value))
|
h2l := muxl.Match(matcherConstructor(name, matchValue))
|
||||||
go safeServe(errCh, muxl)
|
go safeServe(errCh, muxl)
|
||||||
muxedConn, err := h2l.Accept()
|
muxedConn, err := h2l.Accept()
|
||||||
close(l.connCh)
|
close(l.connCh)
|
||||||
|
@ -112,7 +112,7 @@ func Example() {
|
|||||||
|
|
||||||
// We first match the connection against HTTP2 fields. If matched, the
|
// We first match the connection against HTTP2 fields. If matched, the
|
||||||
// connection will be sent through the "grpcl" listener.
|
// connection will be sent through the "grpcl" listener.
|
||||||
grpcl := m.Match(cmux.HTTP2HeaderField("content-type", "application/grpc"))
|
grpcl := m.Match(cmux.HTTP2HeaderFieldPrefix("content-type", "application/grpc"))
|
||||||
//Otherwise, we match it againts a websocket upgrade request.
|
//Otherwise, we match it againts a websocket upgrade request.
|
||||||
wsl := m.Match(cmux.HTTP1HeaderField("Upgrade", "websocket"))
|
wsl := m.Match(cmux.HTTP1HeaderField("Upgrade", "websocket"))
|
||||||
|
|
||||||
|
55
matchers.go
55
matchers.go
@ -127,15 +127,41 @@ func HTTP2() Matcher {
|
|||||||
// request of an HTTP 1 connection.
|
// request of an HTTP 1 connection.
|
||||||
func HTTP1HeaderField(name, value string) Matcher {
|
func HTTP1HeaderField(name, value string) Matcher {
|
||||||
return func(r io.Reader) bool {
|
return func(r io.Reader) bool {
|
||||||
return matchHTTP1Field(r, name, value)
|
return matchHTTP1Field(r, name, func(gotValue string) bool {
|
||||||
|
return gotValue == value
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP2HeaderField resturns a matcher matching the header fields of the first
|
// HTTP1HeaderFieldPrefix returns a matcher matching the header fields of the
|
||||||
|
// first request of an HTTP 1 connection. If the header with key name has a
|
||||||
|
// value prefixed with valuePrefix, this will match.
|
||||||
|
func HTTP1HeaderFieldPrefix(name, valuePrefix string) Matcher {
|
||||||
|
return func(r io.Reader) bool {
|
||||||
|
return matchHTTP1Field(r, name, func(gotValue string) bool {
|
||||||
|
return strings.HasPrefix(gotValue, valuePrefix)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP2HeaderField returns a matcher matching the header fields of the first
|
||||||
// headers frame.
|
// headers frame.
|
||||||
func HTTP2HeaderField(name, value string) Matcher {
|
func HTTP2HeaderField(name, value string) Matcher {
|
||||||
return func(r io.Reader) bool {
|
return func(r io.Reader) bool {
|
||||||
return matchHTTP2Field(ioutil.Discard, r, name, value)
|
return matchHTTP2Field(ioutil.Discard, r, name, func(gotValue string) bool {
|
||||||
|
return gotValue == value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP2HeaderFieldPrefix returns a matcher matching the header fields of the
|
||||||
|
// first headers frame. If the header with key name has a value prefixed with
|
||||||
|
// valuePrefix, this will match.
|
||||||
|
func HTTP2HeaderFieldPrefix(name, valuePrefix string) Matcher {
|
||||||
|
return func(r io.Reader) bool {
|
||||||
|
return matchHTTP2Field(ioutil.Discard, r, name, func(gotValue string) bool {
|
||||||
|
return strings.HasPrefix(gotValue, valuePrefix)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +170,20 @@ func HTTP2HeaderField(name, value string) Matcher {
|
|||||||
// does not block on receiving a SETTING frame.
|
// does not block on receiving a SETTING frame.
|
||||||
func HTTP2MatchHeaderFieldSendSettings(name, value string) MatchWriter {
|
func HTTP2MatchHeaderFieldSendSettings(name, value string) MatchWriter {
|
||||||
return func(w io.Writer, r io.Reader) bool {
|
return func(w io.Writer, r io.Reader) bool {
|
||||||
return matchHTTP2Field(w, r, name, value)
|
return matchHTTP2Field(w, r, name, func(gotValue string) bool {
|
||||||
|
return gotValue == value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP2MatchHeaderFieldPrefixSendSettings matches the header field prefix
|
||||||
|
// and writes the settings to the server. Prefer HTTP2HeaderFieldPrefix over
|
||||||
|
// this one, if the client does not block on receiving a SETTING frame.
|
||||||
|
func HTTP2MatchHeaderFieldPrefixSendSettings(name, valuePrefix string) MatchWriter {
|
||||||
|
return func(w io.Writer, r io.Reader) bool {
|
||||||
|
return matchHTTP2Field(w, r, name, func(gotValue string) bool {
|
||||||
|
return strings.HasPrefix(gotValue, valuePrefix)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,16 +208,16 @@ func hasHTTP2Preface(r io.Reader) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchHTTP1Field(r io.Reader, name, value string) (matched bool) {
|
func matchHTTP1Field(r io.Reader, name string, matches func(string) bool) (matched bool) {
|
||||||
req, err := http.ReadRequest(bufio.NewReader(r))
|
req, err := http.ReadRequest(bufio.NewReader(r))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return req.Header.Get(name) == value
|
return matches(req.Header.Get(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchHTTP2Field(w io.Writer, r io.Reader, name, value string) (matched bool) {
|
func matchHTTP2Field(w io.Writer, r io.Reader, name string, matches func(string) bool) (matched bool) {
|
||||||
if !hasHTTP2Preface(r) {
|
if !hasHTTP2Preface(r) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -188,7 +227,7 @@ func matchHTTP2Field(w io.Writer, r io.Reader, name, value string) (matched bool
|
|||||||
hdec := hpack.NewDecoder(uint32(4<<10), func(hf hpack.HeaderField) {
|
hdec := hpack.NewDecoder(uint32(4<<10), func(hf hpack.HeaderField) {
|
||||||
if hf.Name == name {
|
if hf.Name == name {
|
||||||
done = true
|
done = true
|
||||||
if hf.Value == value {
|
if matches(hf.Value) {
|
||||||
matched = true
|
matched = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user