From 9d2081e29ccc6c946ecd193419c37a5ae7f0e7dd Mon Sep 17 00:00:00 2001 From: aler9 <46489434+aler9@users.noreply.github.com> Date: Sun, 26 Jan 2020 11:45:32 +0100 Subject: [PATCH] move interleaved frames into dedicated struct --- authclientprovider.go | 8 +-- conn.go | 118 ------------------------------------------ connclient.go | 74 ++++++++++++++++++++++++++ connserver.go | 38 ++++++++++++++ interleavedframe.go | 73 ++++++++++++++++++++++++++ 5 files changed, 189 insertions(+), 122 deletions(-) delete mode 100644 conn.go create mode 100644 connclient.go create mode 100644 connserver.go create mode 100644 interleavedframe.go diff --git a/authclientprovider.go b/authclientprovider.go index 80e7e100..14223042 100644 --- a/authclientprovider.go +++ b/authclientprovider.go @@ -12,15 +12,15 @@ func md5Hex(in string) string { return hex.EncodeToString(h.Sum(nil)) } -type AuthClientProvider struct { +type authClientProvider struct { user string pass string realm string nonce string } -func NewAuthClientProvider(user string, pass string, realm string, nonce string) *AuthClientProvider { - return &AuthClientProvider{ +func newAuthClientProvider(user string, pass string, realm string, nonce string) *authClientProvider { + return &authClientProvider{ user: user, pass: pass, realm: realm, @@ -28,7 +28,7 @@ func NewAuthClientProvider(user string, pass string, realm string, nonce string) } } -func (ap *AuthClientProvider) generateHeader(method string, path string) string { +func (ap *authClientProvider) generateHeader(method string, path string) string { ha1 := md5Hex(ap.user + ":" + ap.realm + ":" + ap.pass) ha2 := md5Hex(method + ":" + path) response := md5Hex(ha1 + ":" + ap.nonce + ":" + ha2) diff --git a/conn.go b/conn.go deleted file mode 100644 index 5c2608cb..00000000 --- a/conn.go +++ /dev/null @@ -1,118 +0,0 @@ -package gortsplib - -import ( - "encoding/binary" - "fmt" - "io" - "net" - "strconv" -) - -type Conn struct { - nconn net.Conn - writeBuf []byte - session string - clientCseqEnabled bool - clientCseq int - clientAuthProv *AuthClientProvider -} - -func NewConn(nconn net.Conn) *Conn { - return &Conn{ - nconn: nconn, - writeBuf: make([]byte, 2048), - } -} - -func (c *Conn) NetConn() net.Conn { - return c.nconn -} - -func (c *Conn) SetSession(v string) { - c.session = v -} - -func (c *Conn) ClientEnableCseq() { - c.clientCseqEnabled = true -} - -func (c *Conn) ClientSetCredentials(user string, pass string, realm string, nonce string) { - c.clientAuthProv = NewAuthClientProvider(user, pass, realm, nonce) -} - -func (c *Conn) ReadRequest() (*Request, error) { - return readRequest(c.nconn) -} - -func (c *Conn) WriteRequest(req *Request) error { - if c.session != "" { - if req.Header == nil { - req.Header = make(Header) - } - req.Header["Session"] = []string{c.session} - } - if c.clientCseqEnabled { - if req.Header == nil { - req.Header = make(Header) - } - c.clientCseq += 1 - req.Header["CSeq"] = []string{strconv.FormatInt(int64(c.clientCseq), 10)} - } - if c.clientAuthProv != nil { - if req.Header == nil { - req.Header = make(Header) - } - req.Header["Authorization"] = []string{c.clientAuthProv.generateHeader(req.Method, req.Url)} - } - return req.write(c.nconn) -} - -func (c *Conn) ReadResponse() (*Response, error) { - return readResponse(c.nconn) -} - -func (c *Conn) WriteResponse(res *Response) error { - return res.write(c.nconn) -} - -func (c *Conn) ReadInterleavedFrame(buf []byte) (int, int, error) { - var header [4]byte - _, err := io.ReadFull(c.nconn, header[:]) - if err != nil { - return 0, 0, err - } - - // connection terminated - if header[0] == 0x54 { - return 0, 0, io.EOF - } - - if header[0] != 0x24 { - return 0, 0, fmt.Errorf("wrong magic byte (0x%.2x)", header[0]) - } - - framelen := binary.BigEndian.Uint16(header[2:]) - if int(framelen) > len(buf) { - return 0, 0, fmt.Errorf("frame length greater than buffer length") - } - - _, err = io.ReadFull(c.nconn, buf[:framelen]) - if err != nil { - return 0, 0, err - } - - return int(header[1]), int(framelen), nil -} - -func (c *Conn) WriteInterleavedFrame(channel int, frame []byte) error { - c.writeBuf[0] = 0x24 - c.writeBuf[1] = byte(channel) - binary.BigEndian.PutUint16(c.writeBuf[2:], uint16(len(frame))) - n := copy(c.writeBuf[4:], frame) - - _, err := c.nconn.Write(c.writeBuf[:4+n]) - if err != nil { - return err - } - return nil -} diff --git a/connclient.go b/connclient.go new file mode 100644 index 00000000..e358c1f5 --- /dev/null +++ b/connclient.go @@ -0,0 +1,74 @@ +package gortsplib + +import ( + "bufio" + "net" + "strconv" +) + +type ConnClient struct { + nconn net.Conn + bw *bufio.Writer + session string + cseqEnabled bool + cseq int + authProv *authClientProvider +} + +func NewConnClient(nconn net.Conn) *ConnClient { + return &ConnClient{ + nconn: nconn, + bw: bufio.NewWriterSize(nconn, _INTERLEAVED_FRAME_MAX_SIZE), + } +} + +func (c *ConnClient) NetConn() net.Conn { + return c.nconn +} + +func (c *ConnClient) SetSession(v string) { + c.session = v +} + +func (c *ConnClient) EnableCseq() { + c.cseqEnabled = true +} + +func (c *ConnClient) SetCredentials(user string, pass string, realm string, nonce string) { + c.authProv = newAuthClientProvider(user, pass, realm, nonce) +} + +func (c *ConnClient) WriteRequest(req *Request) error { + if c.session != "" { + if req.Header == nil { + req.Header = make(Header) + } + req.Header["Session"] = []string{c.session} + } + if c.cseqEnabled { + if req.Header == nil { + req.Header = make(Header) + } + c.cseq += 1 + req.Header["CSeq"] = []string{strconv.FormatInt(int64(c.cseq), 10)} + } + if c.authProv != nil { + if req.Header == nil { + req.Header = make(Header) + } + req.Header["Authorization"] = []string{c.authProv.generateHeader(req.Method, req.Url)} + } + return req.write(c.nconn) +} + +func (c *ConnClient) ReadResponse() (*Response, error) { + return readResponse(c.nconn) +} + +func (c *ConnClient) ReadInterleavedFrame() (*InterleavedFrame, error) { + return readInterleavedFrame(c.nconn) +} + +func (c *ConnClient) WriteInterleavedFrame(frame *InterleavedFrame) error { + return frame.write(c.bw) +} diff --git a/connserver.go b/connserver.go new file mode 100644 index 00000000..1bf8bc41 --- /dev/null +++ b/connserver.go @@ -0,0 +1,38 @@ +package gortsplib + +import ( + "bufio" + "net" +) + +type ConnServer struct { + nconn net.Conn + bw *bufio.Writer +} + +func NewConnServer(nconn net.Conn) *ConnServer { + return &ConnServer{ + nconn: nconn, + bw: bufio.NewWriterSize(nconn, _INTERLEAVED_FRAME_MAX_SIZE), + } +} + +func (s *ConnServer) NetConn() net.Conn { + return s.nconn +} + +func (s *ConnServer) ReadRequest() (*Request, error) { + return readRequest(s.nconn) +} + +func (s *ConnServer) WriteResponse(res *Response) error { + return res.write(s.nconn) +} + +func (s *ConnServer) ReadInterleavedFrame() (*InterleavedFrame, error) { + return readInterleavedFrame(s.nconn) +} + +func (s *ConnServer) WriteInterleavedFrame(frame *InterleavedFrame) error { + return frame.write(s.bw) +} diff --git a/interleavedframe.go b/interleavedframe.go new file mode 100644 index 00000000..08a03e4e --- /dev/null +++ b/interleavedframe.go @@ -0,0 +1,73 @@ +package gortsplib + +import ( + "bufio" + "encoding/binary" + "fmt" + "io" +) + +const ( + _INTERLEAVED_FRAME_MAX_SIZE = 2048 + _INTERLEAVED_FRAME_MAX_CONTENT_SIZE = (_INTERLEAVED_FRAME_MAX_SIZE - 4) +) + +type InterleavedFrame struct { + Channel uint8 + Content []byte +} + +func readInterleavedFrame(r io.Reader) (*InterleavedFrame, error) { + var header [4]byte + _, err := io.ReadFull(r, header[:]) + if err != nil { + return nil, err + } + + // connection terminated + if header[0] == 0x54 { + return nil, io.EOF + } + + if header[0] != 0x24 { + return nil, fmt.Errorf("wrong magic byte (0x%.2x)", header[0]) + } + + framelen := binary.BigEndian.Uint16(header[2:]) + if int(framelen) > _INTERLEAVED_FRAME_MAX_SIZE { + return nil, fmt.Errorf("frame length greater than maximum allowed") + } + + f := &InterleavedFrame{ + Channel: header[1], + Content: make([]byte, framelen), + } + + _, err = io.ReadFull(r, f.Content) + if err != nil { + return nil, err + } + + return f, nil +} + +func (f *InterleavedFrame) write(bw *bufio.Writer) error { + _, err := bw.Write([]byte{0x24, f.Channel}) + if err != nil { + return err + } + + buf := make([]byte, 2) + binary.BigEndian.PutUint16(buf, uint16(len(f.Content))) + _, err = bw.Write(buf) + if err != nil { + return err + } + + _, err = bw.Write(f.Content) + if err != nil { + return err + } + + return bw.Flush() +}