mirror of
https://github.com/aler9/gortsplib
synced 2025-10-07 08:01:14 +08:00
HeaderAuth: use struct instead of map for storing
This commit is contained in:
73
auth.go
73
auth.go
@@ -64,19 +64,15 @@ func (as *AuthServer) GenerateHeader() HeaderValue {
|
||||
switch m {
|
||||
case Basic:
|
||||
ret = append(ret, (&HeaderAuth{
|
||||
Prefix: "Basic",
|
||||
Values: map[string]string{
|
||||
"realm": as.realm,
|
||||
},
|
||||
Method: Basic,
|
||||
Realm: &as.realm,
|
||||
}).Write()...)
|
||||
|
||||
case Digest:
|
||||
ret = append(ret, (&HeaderAuth{
|
||||
Prefix: "Digest",
|
||||
Values: map[string]string{
|
||||
"realm": as.realm,
|
||||
"nonce": as.nonce,
|
||||
},
|
||||
Method: Digest,
|
||||
Realm: &as.realm,
|
||||
Nonce: &as.nonce,
|
||||
}).Write()...)
|
||||
}
|
||||
}
|
||||
@@ -110,46 +106,41 @@ func (as *AuthServer) ValidateHeader(v HeaderValue, method Method, ur *url.URL)
|
||||
return err
|
||||
}
|
||||
|
||||
inRealm, ok := auth.Values["realm"]
|
||||
if !ok {
|
||||
if auth.Realm == nil {
|
||||
return fmt.Errorf("realm not provided")
|
||||
}
|
||||
|
||||
inNonce, ok := auth.Values["nonce"]
|
||||
if !ok {
|
||||
if auth.Nonce == nil {
|
||||
return fmt.Errorf("nonce not provided")
|
||||
}
|
||||
|
||||
inUsername, ok := auth.Values["username"]
|
||||
if !ok {
|
||||
if auth.Username == nil {
|
||||
return fmt.Errorf("username not provided")
|
||||
}
|
||||
|
||||
inUri, ok := auth.Values["uri"]
|
||||
if !ok {
|
||||
if auth.URI == nil {
|
||||
return fmt.Errorf("uri not provided")
|
||||
}
|
||||
|
||||
inResponse, ok := auth.Values["response"]
|
||||
if !ok {
|
||||
if auth.Response == nil {
|
||||
return fmt.Errorf("response not provided")
|
||||
}
|
||||
|
||||
if inNonce != as.nonce {
|
||||
if *auth.Nonce != as.nonce {
|
||||
return fmt.Errorf("wrong nonce")
|
||||
}
|
||||
|
||||
if inRealm != as.realm {
|
||||
if *auth.Realm != as.realm {
|
||||
return fmt.Errorf("wrong realm")
|
||||
}
|
||||
|
||||
if inUsername != as.user {
|
||||
if *auth.Username != as.user {
|
||||
return fmt.Errorf("wrong username")
|
||||
}
|
||||
|
||||
uri := ur.String()
|
||||
|
||||
if inUri != uri {
|
||||
if *auth.URI != uri {
|
||||
// VLC strips the subpath
|
||||
newUrl := *ur
|
||||
newUrl.Path = func() string {
|
||||
@@ -163,7 +154,7 @@ func (as *AuthServer) ValidateHeader(v HeaderValue, method Method, ur *url.URL)
|
||||
}()
|
||||
uri = newUrl.String()
|
||||
|
||||
if inUri != uri {
|
||||
if *auth.URI != uri {
|
||||
return fmt.Errorf("wrong url")
|
||||
}
|
||||
}
|
||||
@@ -171,7 +162,7 @@ func (as *AuthServer) ValidateHeader(v HeaderValue, method Method, ur *url.URL)
|
||||
response := md5Hex(md5Hex(as.user+":"+as.realm+":"+as.pass) +
|
||||
":" + as.nonce + ":" + md5Hex(string(method)+":"+uri))
|
||||
|
||||
if inResponse != response {
|
||||
if *auth.Response != response {
|
||||
return fmt.Errorf("wrong response")
|
||||
}
|
||||
|
||||
@@ -209,13 +200,11 @@ func newAuthClient(v HeaderValue, user string, pass string) (*authClient, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
realm, ok := auth.Values["realm"]
|
||||
if !ok {
|
||||
if auth.Realm == nil {
|
||||
return nil, fmt.Errorf("realm not provided")
|
||||
}
|
||||
|
||||
nonce, ok := auth.Values["nonce"]
|
||||
if !ok {
|
||||
if auth.Nonce == nil {
|
||||
return nil, fmt.Errorf("nonce not provided")
|
||||
}
|
||||
|
||||
@@ -223,8 +212,8 @@ func newAuthClient(v HeaderValue, user string, pass string) (*authClient, error)
|
||||
user: user,
|
||||
pass: pass,
|
||||
method: Digest,
|
||||
realm: realm,
|
||||
nonce: nonce,
|
||||
realm: *auth.Realm,
|
||||
nonce: *auth.Nonce,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -241,8 +230,7 @@ func newAuthClient(v HeaderValue, user string, pass string) (*authClient, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
realm, ok := auth.Values["realm"]
|
||||
if !ok {
|
||||
if auth.Realm == nil {
|
||||
return nil, fmt.Errorf("realm not provided")
|
||||
}
|
||||
|
||||
@@ -250,7 +238,7 @@ func newAuthClient(v HeaderValue, user string, pass string) (*authClient, error)
|
||||
user: user,
|
||||
pass: pass,
|
||||
method: Basic,
|
||||
realm: realm,
|
||||
realm: *auth.Realm,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -271,14 +259,15 @@ func (ac *authClient) GenerateHeader(method Method, ur *url.URL) HeaderValue {
|
||||
ac.nonce + ":" + md5Hex(string(method)+":"+ur.String()))
|
||||
|
||||
return (&HeaderAuth{
|
||||
Prefix: "Digest",
|
||||
Values: map[string]string{
|
||||
"username": ac.user,
|
||||
"realm": ac.realm,
|
||||
"nonce": ac.nonce,
|
||||
"uri": ur.String(),
|
||||
"response": response,
|
||||
},
|
||||
Method: Digest,
|
||||
Username: &ac.user,
|
||||
Realm: &ac.realm,
|
||||
Nonce: &ac.nonce,
|
||||
URI: func() *string {
|
||||
v := ur.String()
|
||||
return &v
|
||||
}(),
|
||||
Response: &response,
|
||||
}).Write()
|
||||
}
|
||||
|
||||
|
220
header-auth.go
220
header-auth.go
@@ -2,18 +2,68 @@ package gortsplib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HeaderAuth is an Authenticate or a WWWW-Authenticate header.
|
||||
type HeaderAuth struct {
|
||||
Prefix string
|
||||
Values map[string]string
|
||||
// authentication method
|
||||
Method AuthMethod
|
||||
|
||||
// (optional) username
|
||||
Username *string
|
||||
|
||||
// (optional) realm
|
||||
Realm *string
|
||||
|
||||
// (optional) nonce
|
||||
Nonce *string
|
||||
|
||||
// (optional) uri
|
||||
URI *string
|
||||
|
||||
// (optional) response
|
||||
Response *string
|
||||
|
||||
// (optional) opaque
|
||||
Opaque *string
|
||||
|
||||
// (optional) stale
|
||||
Stale *string
|
||||
|
||||
// (optional) algorithm
|
||||
Algorithm *string
|
||||
}
|
||||
|
||||
var regHeaderAuthKeyValue = regexp.MustCompile("^([a-z]+)=(\"(.*?)\"|([a-zA-Z0-9]+))(, *|$)")
|
||||
func findValue(v0 string) (string, string, error) {
|
||||
if v0 == "" {
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
if v0[0] == '"' {
|
||||
i := 1
|
||||
for {
|
||||
if i >= len(v0) {
|
||||
return "", "", fmt.Errorf("apices not closed (%v)", v0)
|
||||
}
|
||||
|
||||
if v0[i] == '"' {
|
||||
return v0[1:i], v0[i+1:], nil
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
i := 0
|
||||
for {
|
||||
if i >= len(v0) || v0[i] == ',' {
|
||||
return v0[:i], v0[i:], nil
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// ReadHeaderAuth parses an Authenticate or a WWW-Authenticate header.
|
||||
func ReadHeaderAuth(v HeaderValue) (*HeaderAuth, error) {
|
||||
@@ -25,28 +75,79 @@ func ReadHeaderAuth(v HeaderValue) (*HeaderAuth, error) {
|
||||
return nil, fmt.Errorf("value provided multiple times (%v)", v)
|
||||
}
|
||||
|
||||
ha := &HeaderAuth{
|
||||
Values: make(map[string]string),
|
||||
}
|
||||
ha := &HeaderAuth{}
|
||||
|
||||
v0 := v[0]
|
||||
|
||||
i := strings.IndexByte(v[0], ' ')
|
||||
i := strings.IndexByte(v0, ' ')
|
||||
if i < 0 {
|
||||
return nil, fmt.Errorf("unable to find prefix (%s)", v0)
|
||||
return nil, fmt.Errorf("unable to find method (%s)", v0)
|
||||
}
|
||||
ha.Prefix, v0 = v0[:i], v0[i+1:]
|
||||
|
||||
switch v0[:i] {
|
||||
case "Basic":
|
||||
ha.Method = Basic
|
||||
|
||||
case "Digest":
|
||||
ha.Method = Digest
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid method (%s)", v0[:i])
|
||||
}
|
||||
v0 = v0[i+1:]
|
||||
|
||||
for len(v0) > 0 {
|
||||
m := regHeaderAuthKeyValue.FindStringSubmatch(v0)
|
||||
if m == nil {
|
||||
return nil, fmt.Errorf("unable to parse key-value (%s)", v0)
|
||||
i := strings.IndexByte(v0, '=')
|
||||
if i < 0 {
|
||||
return nil, fmt.Errorf("unable to find key (%s)", v0)
|
||||
}
|
||||
v0 = v0[len(m[0]):]
|
||||
var key string
|
||||
key, v0 = v0[:i], v0[i+1:]
|
||||
|
||||
m[2] = strings.TrimPrefix(m[2], "\"")
|
||||
m[2] = strings.TrimSuffix(m[2], "\"")
|
||||
ha.Values[m[1]] = m[2]
|
||||
var val string
|
||||
var err error
|
||||
val, v0, err = findValue(v0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch key {
|
||||
case "username":
|
||||
ha.Username = &val
|
||||
|
||||
case "realm":
|
||||
ha.Realm = &val
|
||||
|
||||
case "nonce":
|
||||
ha.Nonce = &val
|
||||
|
||||
case "uri":
|
||||
ha.URI = &val
|
||||
|
||||
case "response":
|
||||
ha.Response = &val
|
||||
|
||||
case "opaque":
|
||||
ha.Opaque = &val
|
||||
|
||||
case "stale":
|
||||
ha.Stale = &val
|
||||
|
||||
case "algorithm":
|
||||
ha.Algorithm = &val
|
||||
|
||||
// ignore non-standard keys
|
||||
}
|
||||
|
||||
// skip comma
|
||||
if len(v0) > 0 && v0[0] == ',' {
|
||||
v0 = v0[1:]
|
||||
}
|
||||
|
||||
// skip spaces
|
||||
for len(v0) > 0 && v0[0] == ' ' {
|
||||
v0 = v0[1:]
|
||||
}
|
||||
}
|
||||
|
||||
return ha, nil
|
||||
@@ -54,48 +155,53 @@ func ReadHeaderAuth(v HeaderValue) (*HeaderAuth, error) {
|
||||
|
||||
// Write encodes an Authenticate or a WWW-Authenticate header.
|
||||
func (ha *HeaderAuth) Write() HeaderValue {
|
||||
ret := ha.Prefix + " "
|
||||
ret := ""
|
||||
|
||||
// follow a specific order, otherwise some clients/servers do not work correctly
|
||||
var sortedKeys []string
|
||||
for key := range ha.Values {
|
||||
sortedKeys = append(sortedKeys, key)
|
||||
}
|
||||
score := func(v string) int {
|
||||
switch v {
|
||||
case "username":
|
||||
return 0
|
||||
case "realm":
|
||||
return 1
|
||||
case "nonce":
|
||||
return 2
|
||||
case "uri":
|
||||
return 3
|
||||
case "response":
|
||||
return 4
|
||||
case "opaque":
|
||||
return 5
|
||||
case "stale":
|
||||
return 6
|
||||
case "algorithm":
|
||||
return 7
|
||||
}
|
||||
return 8
|
||||
}
|
||||
sort.Slice(sortedKeys, func(a, b int) bool {
|
||||
sa := score(sortedKeys[a])
|
||||
sb := score(sortedKeys[b])
|
||||
if sa != sb {
|
||||
return sa < sb
|
||||
}
|
||||
return a < b
|
||||
})
|
||||
switch ha.Method {
|
||||
case Basic:
|
||||
ret += "Basic"
|
||||
|
||||
var tmp []string
|
||||
for _, key := range sortedKeys {
|
||||
tmp = append(tmp, key+"=\""+ha.Values[key]+"\"")
|
||||
case Digest:
|
||||
ret += "Digest"
|
||||
}
|
||||
ret += strings.Join(tmp, ", ")
|
||||
|
||||
ret += " "
|
||||
|
||||
var vals []string
|
||||
|
||||
if ha.Username != nil {
|
||||
vals = append(vals, "username=\""+*ha.Username+"\"")
|
||||
}
|
||||
|
||||
if ha.Realm != nil {
|
||||
vals = append(vals, "realm=\""+*ha.Realm+"\"")
|
||||
}
|
||||
|
||||
if ha.Nonce != nil {
|
||||
vals = append(vals, "nonce=\""+*ha.Nonce+"\"")
|
||||
}
|
||||
|
||||
if ha.URI != nil {
|
||||
vals = append(vals, "uri=\""+*ha.URI+"\"")
|
||||
}
|
||||
|
||||
if ha.Response != nil {
|
||||
vals = append(vals, "response=\""+*ha.Response+"\"")
|
||||
}
|
||||
|
||||
if ha.Opaque != nil {
|
||||
vals = append(vals, "opaque=\""+*ha.Opaque+"\"")
|
||||
}
|
||||
|
||||
if ha.Stale != nil {
|
||||
vals = append(vals, "stale=\""+*ha.Stale+"\"")
|
||||
}
|
||||
|
||||
if ha.Algorithm != nil {
|
||||
vals = append(vals, "algorithm=\""+*ha.Algorithm+"\"")
|
||||
}
|
||||
|
||||
ret += strings.Join(vals, ", ")
|
||||
|
||||
return HeaderValue{ret}
|
||||
}
|
||||
|
@@ -17,10 +17,11 @@ var casesHeaderAuth = []struct {
|
||||
HeaderValue{`Basic realm="4419b63f5e51"`},
|
||||
HeaderValue{`Basic realm="4419b63f5e51"`},
|
||||
&HeaderAuth{
|
||||
Prefix: "Basic",
|
||||
Values: map[string]string{
|
||||
"realm": "4419b63f5e51",
|
||||
},
|
||||
Method: Basic,
|
||||
Realm: func() *string {
|
||||
v := "4419b63f5e51"
|
||||
return &v
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -28,12 +29,19 @@ var casesHeaderAuth = []struct {
|
||||
HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`},
|
||||
HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`},
|
||||
&HeaderAuth{
|
||||
Prefix: "Digest",
|
||||
Values: map[string]string{
|
||||
"realm": "4419b63f5e51",
|
||||
"nonce": "8b84a3b789283a8bea8da7fa7d41f08b",
|
||||
"stale": "FALSE",
|
||||
},
|
||||
Method: Digest,
|
||||
Realm: func() *string {
|
||||
v := "4419b63f5e51"
|
||||
return &v
|
||||
}(),
|
||||
Nonce: func() *string {
|
||||
v := "8b84a3b789283a8bea8da7fa7d41f08b"
|
||||
return &v
|
||||
}(),
|
||||
Stale: func() *string {
|
||||
v := "FALSE"
|
||||
return &v
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -41,12 +49,19 @@ var casesHeaderAuth = []struct {
|
||||
HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale=FALSE`},
|
||||
HeaderValue{`Digest realm="4419b63f5e51", nonce="8b84a3b789283a8bea8da7fa7d41f08b", stale="FALSE"`},
|
||||
&HeaderAuth{
|
||||
Prefix: "Digest",
|
||||
Values: map[string]string{
|
||||
"realm": "4419b63f5e51",
|
||||
"nonce": "8b84a3b789283a8bea8da7fa7d41f08b",
|
||||
"stale": "FALSE",
|
||||
},
|
||||
Method: Digest,
|
||||
Realm: func() *string {
|
||||
v := "4419b63f5e51"
|
||||
return &v
|
||||
}(),
|
||||
Nonce: func() *string {
|
||||
v := "8b84a3b789283a8bea8da7fa7d41f08b"
|
||||
return &v
|
||||
}(),
|
||||
Stale: func() *string {
|
||||
v := "FALSE"
|
||||
return &v
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -54,12 +69,19 @@ var casesHeaderAuth = []struct {
|
||||
HeaderValue{`Digest realm="4419b63f5e51",nonce="133767111917411116111311118211673010032", stale="FALSE"`},
|
||||
HeaderValue{`Digest realm="4419b63f5e51", nonce="133767111917411116111311118211673010032", stale="FALSE"`},
|
||||
&HeaderAuth{
|
||||
Prefix: "Digest",
|
||||
Values: map[string]string{
|
||||
"realm": "4419b63f5e51",
|
||||
"nonce": "133767111917411116111311118211673010032",
|
||||
"stale": "FALSE",
|
||||
},
|
||||
Method: Digest,
|
||||
Realm: func() *string {
|
||||
v := "4419b63f5e51"
|
||||
return &v
|
||||
}(),
|
||||
Nonce: func() *string {
|
||||
v := "133767111917411116111311118211673010032"
|
||||
return &v
|
||||
}(),
|
||||
Stale: func() *string {
|
||||
v := "FALSE"
|
||||
return &v
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -67,14 +89,27 @@ var casesHeaderAuth = []struct {
|
||||
HeaderValue{`Digest username="aa", realm="bb", nonce="cc", uri="dd", response="ee"`},
|
||||
HeaderValue{`Digest username="aa", realm="bb", nonce="cc", uri="dd", response="ee"`},
|
||||
&HeaderAuth{
|
||||
Prefix: "Digest",
|
||||
Values: map[string]string{
|
||||
"username": "aa",
|
||||
"realm": "bb",
|
||||
"nonce": "cc",
|
||||
"uri": "dd",
|
||||
"response": "ee",
|
||||
},
|
||||
Method: Digest,
|
||||
Username: func() *string {
|
||||
v := "aa"
|
||||
return &v
|
||||
}(),
|
||||
Realm: func() *string {
|
||||
v := "bb"
|
||||
return &v
|
||||
}(),
|
||||
Nonce: func() *string {
|
||||
v := "cc"
|
||||
return &v
|
||||
}(),
|
||||
URI: func() *string {
|
||||
v := "dd"
|
||||
return &v
|
||||
}(),
|
||||
Response: func() *string {
|
||||
v := "ee"
|
||||
return &v
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -82,14 +117,27 @@ var casesHeaderAuth = []struct {
|
||||
HeaderValue{`Digest username="", realm="IPCAM", nonce="5d17cd12b9fa8a85ac5ceef0926ea5a6", uri="rtsp://localhost:8554/mystream", response="c072ae90eb4a27f4cdcb90d62266b2a1"`},
|
||||
HeaderValue{`Digest username="", realm="IPCAM", nonce="5d17cd12b9fa8a85ac5ceef0926ea5a6", uri="rtsp://localhost:8554/mystream", response="c072ae90eb4a27f4cdcb90d62266b2a1"`},
|
||||
&HeaderAuth{
|
||||
Prefix: "Digest",
|
||||
Values: map[string]string{
|
||||
"username": "",
|
||||
"realm": "IPCAM",
|
||||
"nonce": "5d17cd12b9fa8a85ac5ceef0926ea5a6",
|
||||
"uri": "rtsp://localhost:8554/mystream",
|
||||
"response": "c072ae90eb4a27f4cdcb90d62266b2a1",
|
||||
},
|
||||
Method: Digest,
|
||||
Username: func() *string {
|
||||
v := ""
|
||||
return &v
|
||||
}(),
|
||||
Realm: func() *string {
|
||||
v := "IPCAM"
|
||||
return &v
|
||||
}(),
|
||||
Nonce: func() *string {
|
||||
v := "5d17cd12b9fa8a85ac5ceef0926ea5a6"
|
||||
return &v
|
||||
}(),
|
||||
URI: func() *string {
|
||||
v := "rtsp://localhost:8554/mystream"
|
||||
return &v
|
||||
}(),
|
||||
Response: func() *string {
|
||||
v := "c072ae90eb4a27f4cdcb90d62266b2a1"
|
||||
return &v
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -97,14 +145,27 @@ var casesHeaderAuth = []struct {
|
||||
HeaderValue{`Digest realm="Please log in with a valid username",nonce="752a62306daf32b401a41004555c7663",opaque="",stale=FALSE,algorithm=MD5`},
|
||||
HeaderValue{`Digest realm="Please log in with a valid username", nonce="752a62306daf32b401a41004555c7663", opaque="", stale="FALSE", algorithm="MD5"`},
|
||||
&HeaderAuth{
|
||||
Prefix: "Digest",
|
||||
Values: map[string]string{
|
||||
"realm": "Please log in with a valid username",
|
||||
"nonce": "752a62306daf32b401a41004555c7663",
|
||||
"opaque": "",
|
||||
"stale": "FALSE",
|
||||
"algorithm": "MD5",
|
||||
},
|
||||
Method: Digest,
|
||||
Realm: func() *string {
|
||||
v := "Please log in with a valid username"
|
||||
return &v
|
||||
}(),
|
||||
Nonce: func() *string {
|
||||
v := "752a62306daf32b401a41004555c7663"
|
||||
return &v
|
||||
}(),
|
||||
Opaque: func() *string {
|
||||
v := ""
|
||||
return &v
|
||||
}(),
|
||||
Stale: func() *string {
|
||||
v := "FALSE"
|
||||
return &v
|
||||
}(),
|
||||
Algorithm: func() *string {
|
||||
v := "MD5"
|
||||
return &v
|
||||
}(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@@ -75,15 +75,14 @@ func ReadHeaderTransport(v HeaderValue) (*HeaderTransport, error) {
|
||||
switch parts[0] {
|
||||
case "RTP/AVP", "RTP/AVP/UDP":
|
||||
ht.Protocol = StreamProtocolUDP
|
||||
parts = parts[1:]
|
||||
|
||||
case "RTP/AVP/TCP":
|
||||
ht.Protocol = StreamProtocolTCP
|
||||
parts = parts[1:]
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid protocol (%v)", v)
|
||||
}
|
||||
parts = parts[1:]
|
||||
|
||||
switch parts[0] {
|
||||
case "unicast":
|
||||
@@ -144,6 +143,8 @@ func ReadHeaderTransport(v HeaderValue) (*HeaderTransport, error) {
|
||||
v = strings.TrimSuffix(v, "\"")
|
||||
ht.Mode = &v
|
||||
}
|
||||
|
||||
// ignore non-standard keys
|
||||
}
|
||||
|
||||
return ht, nil
|
||||
|
Reference in New Issue
Block a user