From a0fb299b0673422504f8ba5e016a5e0bcb68f4fd Mon Sep 17 00:00:00 2001 From: Saxon Date: Fri, 26 Apr 2019 13:57:18 +0930 Subject: [PATCH] protocol/rtsp: moved client functionality into client.go file --- protocol/rtsp/client.go | 130 +++++++++++++++++++++++++++++++++++++ protocol/rtsp/rtsp.go | 95 --------------------------- protocol/rtsp/rtsp_test.go | 18 ++--- 3 files changed, 139 insertions(+), 104 deletions(-) create mode 100644 protocol/rtsp/client.go diff --git a/protocol/rtsp/client.go b/protocol/rtsp/client.go new file mode 100644 index 00000000..00a00b5a --- /dev/null +++ b/protocol/rtsp/client.go @@ -0,0 +1,130 @@ +/* +NAME + client.go + +DESCRIPTION + client.go provides a Client type providing functionality to send RTSP requests + of methods DESCRIBE, OPTIONS, SETUP and PLAY to an RTSP server. + +AUTHORS + Saxon A. Nelson-Milton + +LICENSE + This is Copyright (C) 2019 the Australian Ocean Lab (AusOcean). + + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + in gpl.txt. If not, see http://www.gnu.org/licenses. +*/ +package rtsp + +import ( + "bufio" + "io" + "net" + "net/url" + "strconv" +) + +// Client describes an RTSP Client. +type Client struct { + cSeq int + urlStr string + url *url.URL + conn net.Conn + Client string +} + +// NewClient returns a pointer to a new Client. The URL u will be parsed and +// a connection to the RTSP server will be made. +func NewClient(u string) (*Client, error) { + s := &Client{urlStr: u} + var err error + s.url, err = url.Parse(u) + if err != nil { + return nil, err + } + s.conn, err = net.Dial("tcp", s.url.Host) + if err != nil { + return nil, err + } + return s, nil +} + +// Describe forms and sends an RTSP request of method DESCRIBE to the RTSP server. +func (s *Client) Describe() (*Response, error) { + return s.writeRequest(s.url, describe, func(req *Request) { req.Header.Add("Accept", "application/sdp") }, nil) +} + +// Options forms and sends an RTSP request of method OPTIONS to the RTSP server. +func (s *Client) Options() (*Response, error) { + return s.writeRequest(s.url, options, nil, nil) +} + +// Setup forms and sends an RTSP request of method SETUP to the RTSP server. +func (s *Client) Setup(track, transport string) (*Response, error) { + url, err := url.Parse(s.urlStr + "/" + track) + if err != nil { + return nil, err + } + return s.writeRequest( + url, + setup, + func(req *Request) { + req.Header.Add("Transport", transport) + }, + func(resp *Response) { + s.Client = resp.Header.Get("Client") + }, + ) +} + +// Play forms and sends an RTSP request of method PLAY to the RTSP server +func (s *Client) Play() (*Response, error) { + return s.writeRequest(s.url, play, func(req *Request) { req.Header.Add("Client", s.Client) }, nil) +} + +// writeRequest creates an RTSP request of the method given and writes it to the +// the Client connection. +// +// headerOp and respOp define any operation that needs +// to occur to the request or response for the given method. +func (s *Client) writeRequest(url *url.URL, method string, headerOp func(*Request), respOp func(*Response)) (*Response, error) { + req, err := NewRequest(method, s.nextCSeq(), url, nil) + if err != nil { + return nil, err + } + + if headerOp != nil { + headerOp(req) + } + + _, err = io.WriteString(s.conn, req.String()) + if err != nil { + return nil, err + } + + res, err := ReadResponse(bufio.NewReader(s.conn)) + if err != nil { + return nil, err + } + + if respOp != nil { + respOp(res) + } + return res, nil +} + +func (s *Client) nextCSeq() string { + s.cSeq++ + return strconv.Itoa(s.cSeq) +} diff --git a/protocol/rtsp/rtsp.go b/protocol/rtsp/rtsp.go index 612225c1..c9806495 100644 --- a/protocol/rtsp/rtsp.go +++ b/protocol/rtsp/rtsp.go @@ -62,7 +62,6 @@ import ( "fmt" "io" "io/ioutil" - "net" "net/http" "net/url" "strconv" @@ -82,100 +81,6 @@ const minResponse = 15 var errSmallResponse = errors.New("response too small") -// Session describes an RTSP Session. -type Session struct { - cSeq int - urlStr string - url *url.URL - conn net.Conn - Session string -} - -// NewSession returns a pointer to a new Session. The URL u will be parsed and -// a connection to the RTSP server will be made. -func NewSession(u string) (*Session, error) { - s := &Session{urlStr: u} - var err error - s.url, err = url.Parse(u) - if err != nil { - return nil, err - } - s.conn, err = net.Dial("tcp", s.url.Host) - if err != nil { - return nil, err - } - return s, nil -} - -// Describe forms and sends an RTSP request of method DESCRIBE to the RTSP server. -func (s *Session) Describe() (*Response, error) { - return s.writeRequest(s.url, describe, func(req *Request) { req.Header.Add("Accept", "application/sdp") }, nil) -} - -// Options forms and sends an RTSP request of method OPTIONS to the RTSP server. -func (s *Session) Options() (*Response, error) { - return s.writeRequest(s.url, options, nil, nil) -} - -// Setup forms and sends an RTSP request of method SETUP to the RTSP server. -func (s *Session) Setup(track, transport string) (*Response, error) { - url, err := url.Parse(s.urlStr + "/" + track) - if err != nil { - return nil, err - } - return s.writeRequest( - url, - setup, - func(req *Request) { - req.Header.Add("Transport", transport) - }, - func(resp *Response) { - s.Session = resp.Header.Get("Session") - }, - ) -} - -// Play forms and sends an RTSP request of method PLAY to the RTSP server -func (s *Session) Play() (*Response, error) { - return s.writeRequest(s.url, play, func(req *Request) { req.Header.Add("Session", s.Session) }, nil) -} - -// writeRequest creates an RTSP request of the method given and writes it to the -// the Session connection. -// -// headerOp and respOp define any operation that needs -// to occur to the request or response for the given method. -func (s *Session) writeRequest(url *url.URL, method string, headerOp func(*Request), respOp func(*Response)) (*Response, error) { - req, err := NewRequest(method, s.nextCSeq(), url, nil) - if err != nil { - return nil, err - } - - if headerOp != nil { - headerOp(req) - } - - _, err = io.WriteString(s.conn, req.String()) - if err != nil { - return nil, err - } - - res, err := ReadResponse(bufio.NewReader(s.conn)) - if err != nil { - return nil, err - } - - if respOp != nil { - respOp(res) - } - return res, nil -} - -func (s *Session) nextCSeq() string { - s.cSeq++ - return strconv.Itoa(s.cSeq) -} - // Request describes an RTSP request. type Request struct { Method string diff --git a/protocol/rtsp/rtsp_test.go b/protocol/rtsp/rtsp_test.go index 7b5246d4..119e7264 100644 --- a/protocol/rtsp/rtsp_test.go +++ b/protocol/rtsp/rtsp_test.go @@ -51,13 +51,13 @@ func TestMethods(t *testing.T) { // tests holds tests which consist of a function used to create and write a // request of a particular method, and also the expected request bytes // to be received on the server side. The bytes in these tests have been - // obtained from a valid RTSP session. + // obtained from a valid RTSP communication session.. tests := []struct { - method func(s *Session) (*Response, error) + method func(s *Client) (*Response, error) expected []byte }{ { - method: func(s *Session) (*Response, error) { + method: func(s *Client) (*Response, error) { return s.writeRequest(url, describe, func(req *Request) { req.Header.Add("Accept", "application/sdp") }, nil) }, expected: []byte{ @@ -73,7 +73,7 @@ func TestMethods(t *testing.T) { }, }, { - method: func(s *Session) (*Response, error) { + method: func(s *Client) (*Response, error) { return s.writeRequest(url, options, nil, nil) }, expected: []byte{ @@ -87,7 +87,7 @@ func TestMethods(t *testing.T) { }, }, { - method: func(s *Session) (*Response, error) { + method: func(s *Client) (*Response, error) { url, err := url.Parse(dummyURL + "/track1") if err != nil { t.Fatalf("could not parse url with track, failed with err: %v", err) @@ -116,8 +116,8 @@ func TestMethods(t *testing.T) { }, }, { - method: func(s *Session) (*Response, error) { - return s.writeRequest(url, play, func(req *Request) { req.Header.Add("Session", "00000021") }, nil) + method: func(s *Client) (*Response, error) { + return s.writeRequest(url, play, func(req *Request) { req.Header.Add("Client", "00000021") }, nil) }, expected: []byte{ 0x50, 0x4c, 0x41, 0x59, 0x20, 0x72, 0x74, 0x73, 0x70, 0x3a, 0x2f, 0x2f, @@ -184,12 +184,12 @@ func TestMethods(t *testing.T) { // This routine acts as the client. go func() { - var sess *Session + var sess *Client var err error // Keep trying to connect to server. for retry := 0; ; retry++ { - sess, err = NewSession(serverAddr) + sess, err = NewClient(serverAddr) if err == nil { break }