mirror of
https://github.com/aler9/gortsplib
synced 2025-10-06 23:52:46 +08:00
decode urls in requests
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -61,11 +62,11 @@ func NewAuthClient(header []string, user string, pass string) (*AuthClient, erro
|
|||||||
|
|
||||||
// GenerateHeader generates an Authorization Header that allows to authenticate a request with
|
// GenerateHeader generates an Authorization Header that allows to authenticate a request with
|
||||||
// the given method and path.
|
// the given method and path.
|
||||||
func (ac *AuthClient) GenerateHeader(method Method, path string) []string {
|
func (ac *AuthClient) GenerateHeader(method Method, ur *url.URL) []string {
|
||||||
ha1 := md5Hex(ac.user + ":" + ac.realm + ":" + ac.pass)
|
ha1 := md5Hex(ac.user + ":" + ac.realm + ":" + ac.pass)
|
||||||
ha2 := md5Hex(string(method) + ":" + path)
|
ha2 := md5Hex(string(method) + ":" + ur.String())
|
||||||
response := md5Hex(ha1 + ":" + ac.nonce + ":" + ha2)
|
response := md5Hex(ha1 + ":" + ac.nonce + ":" + ha2)
|
||||||
|
|
||||||
return []string{fmt.Sprintf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
|
return []string{fmt.Sprintf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
|
||||||
ac.user, ac.realm, ac.nonce, path, response)}
|
ac.user, ac.realm, ac.nonce, ur.String(), response)}
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AuthServer is an object that helps a server validating the credentials of a client.
|
// AuthServer is an object that helps a server validating the credentials of a client.
|
||||||
@@ -35,7 +36,7 @@ func (as *AuthServer) GenerateHeader() []string {
|
|||||||
|
|
||||||
// ValidateHeader validates the Authorization header sent by a client after receiving the
|
// ValidateHeader validates the Authorization header sent by a client after receiving the
|
||||||
// WWW-Authenticate header provided by GenerateHeader().
|
// WWW-Authenticate header provided by GenerateHeader().
|
||||||
func (as *AuthServer) ValidateHeader(header []string, method Method, path string) error {
|
func (as *AuthServer) ValidateHeader(header []string, method Method, ur *url.URL) error {
|
||||||
if len(header) != 1 {
|
if len(header) != 1 {
|
||||||
return fmt.Errorf("Authorization header not provided")
|
return fmt.Errorf("Authorization header not provided")
|
||||||
}
|
}
|
||||||
@@ -82,12 +83,12 @@ func (as *AuthServer) ValidateHeader(header []string, method Method, path string
|
|||||||
return fmt.Errorf("wrong username")
|
return fmt.Errorf("wrong username")
|
||||||
}
|
}
|
||||||
|
|
||||||
if inUri != path {
|
if inUri != ur.String() {
|
||||||
return fmt.Errorf("wrong uri")
|
return fmt.Errorf("wrong url")
|
||||||
}
|
}
|
||||||
|
|
||||||
ha1 := md5Hex(as.user + ":" + as.realm + ":" + as.pass)
|
ha1 := md5Hex(as.user + ":" + as.realm + ":" + as.pass)
|
||||||
ha2 := md5Hex(string(method) + ":" + path)
|
ha2 := md5Hex(string(method) + ":" + ur.String())
|
||||||
response := md5Hex(ha1 + ":" + as.nonce + ":" + ha2)
|
response := md5Hex(ha1 + ":" + as.nonce + ":" + ha2)
|
||||||
|
|
||||||
if inResponse != response {
|
if inResponse != response {
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package gortsplib
|
package gortsplib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -12,8 +13,10 @@ func TestAuthClientServer(t *testing.T) {
|
|||||||
|
|
||||||
ac, err := NewAuthClient(wwwAuthenticate, "testuser", "testpass")
|
ac, err := NewAuthClient(wwwAuthenticate, "testuser", "testpass")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
authorization := ac.GenerateHeader(ANNOUNCE, "rtsp://myhost/mypath")
|
authorization := ac.GenerateHeader(ANNOUNCE,
|
||||||
|
&url.URL{Scheme: "rtsp", Host: "myhost", Path: "mypath"})
|
||||||
|
|
||||||
err = as.ValidateHeader(authorization, ANNOUNCE, "rtsp://myhost/mypath")
|
err = as.ValidateHeader(authorization, ANNOUNCE,
|
||||||
|
&url.URL{Scheme: "rtsp", Host: "myhost", Path: "mypath"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
21
request.go
21
request.go
@@ -3,6 +3,7 @@ package gortsplib
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -32,7 +33,7 @@ const (
|
|||||||
// Request is a RTSP request.
|
// Request is a RTSP request.
|
||||||
type Request struct {
|
type Request struct {
|
||||||
Method Method
|
Method Method
|
||||||
Url string
|
Url *url.URL
|
||||||
Header Header
|
Header Header
|
||||||
Content []byte
|
Content []byte
|
||||||
}
|
}
|
||||||
@@ -46,7 +47,7 @@ func readRequest(br *bufio.Reader) (*Request, error) {
|
|||||||
}
|
}
|
||||||
req.Method = Method(byts[:len(byts)-1])
|
req.Method = Method(byts[:len(byts)-1])
|
||||||
|
|
||||||
if len(req.Method) == 0 {
|
if req.Method == "" {
|
||||||
return nil, fmt.Errorf("empty method")
|
return nil, fmt.Errorf("empty method")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,12 +55,22 @@ func readRequest(br *bufio.Reader) (*Request, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
req.Url = string(byts[:len(byts)-1])
|
rawUrl := string(byts[:len(byts)-1])
|
||||||
|
|
||||||
if len(req.Url) == 0 {
|
if rawUrl == "" {
|
||||||
return nil, fmt.Errorf("empty url")
|
return nil, fmt.Errorf("empty url")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ur, err := url.Parse(rawUrl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse url '%s'", rawUrl)
|
||||||
|
}
|
||||||
|
req.Url = ur
|
||||||
|
|
||||||
|
if req.Url.Scheme != "rtsp" {
|
||||||
|
return nil, fmt.Errorf("invalid url scheme '%s'", req.Url.Scheme)
|
||||||
|
}
|
||||||
|
|
||||||
byts, err = readBytesLimited(br, '\r', _MAX_PROTOCOL_LENGTH)
|
byts, err = readBytesLimited(br, '\r', _MAX_PROTOCOL_LENGTH)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -89,7 +100,7 @@ func readRequest(br *bufio.Reader) (*Request, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (req *Request) write(bw *bufio.Writer) error {
|
func (req *Request) write(bw *bufio.Writer) error {
|
||||||
_, err := bw.Write([]byte(string(req.Method) + " " + req.Url + " " + _RTSP_PROTO + "\r\n"))
|
_, err := bw.Write([]byte(string(req.Method) + " " + req.Url.String() + " " + _RTSP_PROTO + "\r\n"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ package gortsplib
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -22,7 +23,7 @@ var casesRequest = []struct {
|
|||||||
"\r\n"),
|
"\r\n"),
|
||||||
&Request{
|
&Request{
|
||||||
Method: "OPTIONS",
|
Method: "OPTIONS",
|
||||||
Url: "rtsp://example.com/media.mp4",
|
Url: &url.URL{Scheme: "rtsp", Host: "example.com", Path: "/media.mp4"},
|
||||||
Header: Header{
|
Header: Header{
|
||||||
"CSeq": []string{"1"},
|
"CSeq": []string{"1"},
|
||||||
"Require": []string{"implicit-play"},
|
"Require": []string{"implicit-play"},
|
||||||
@@ -37,7 +38,7 @@ var casesRequest = []struct {
|
|||||||
"\r\n"),
|
"\r\n"),
|
||||||
&Request{
|
&Request{
|
||||||
Method: "DESCRIBE",
|
Method: "DESCRIBE",
|
||||||
Url: "rtsp://example.com/media.mp4",
|
Url: &url.URL{Scheme: "rtsp", Host: "example.com", Path: "/media.mp4"},
|
||||||
Header: Header{
|
Header: Header{
|
||||||
"CSeq": []string{"2"},
|
"CSeq": []string{"2"},
|
||||||
},
|
},
|
||||||
@@ -65,7 +66,7 @@ var casesRequest = []struct {
|
|||||||
"m=video 2232 RTP/AVP 31\n"),
|
"m=video 2232 RTP/AVP 31\n"),
|
||||||
&Request{
|
&Request{
|
||||||
Method: "ANNOUNCE",
|
Method: "ANNOUNCE",
|
||||||
Url: "rtsp://example.com/media.mp4",
|
Url: &url.URL{Scheme: "rtsp", Host: "example.com", Path: "/media.mp4"},
|
||||||
Header: Header{
|
Header: Header{
|
||||||
"CSeq": []string{"7"},
|
"CSeq": []string{"7"},
|
||||||
"Date": []string{"23 Jan 1997 15:35:06 GMT"},
|
"Date": []string{"23 Jan 1997 15:35:06 GMT"},
|
||||||
@@ -99,7 +100,7 @@ var casesRequest = []struct {
|
|||||||
"jitter\n"),
|
"jitter\n"),
|
||||||
&Request{
|
&Request{
|
||||||
Method: "GET_PARAMETER",
|
Method: "GET_PARAMETER",
|
||||||
Url: "rtsp://example.com/media.mp4",
|
Url: &url.URL{Scheme: "rtsp", Host: "example.com", Path: "/media.mp4"},
|
||||||
Header: Header{
|
Header: Header{
|
||||||
"CSeq": []string{"9"},
|
"CSeq": []string{"9"},
|
||||||
"Content-Type": []string{"text/parameters"},
|
"Content-Type": []string{"text/parameters"},
|
||||||
|
Reference in New Issue
Block a user