Files
go2rtc/pkg/tcp/textproto.go
2022-08-22 11:43:26 +03:00

151 lines
2.6 KiB
Go

package tcp
import (
"bufio"
"errors"
"fmt"
"io"
"net/textproto"
"net/url"
"strconv"
"strings"
)
const EndLine = "\r\n"
// Response like http.Response, but with any proto
type Response struct {
Status string
StatusCode int
Proto string
Header textproto.MIMEHeader
Body []byte
Request *Request
}
func (r Response) String() string {
s := r.Proto + " " + r.Status + EndLine
for k, v := range r.Header {
s += k + ": " + v[0] + EndLine
}
s += EndLine
if r.Body != nil {
s += string(r.Body)
}
return s
}
func (r *Response) Write(w io.Writer) (err error) {
_, err = w.Write([]byte(r.String()))
return
}
func ReadResponse(r *bufio.Reader) (*Response, error) {
tp := textproto.NewReader(r)
line, err := tp.ReadLine()
if err != nil {
return nil, err
}
if line == "" {
return nil, errors.New("empty response on RTSP request")
}
ss := strings.SplitN(line, " ", 3)
if len(ss) != 3 {
return nil, fmt.Errorf("malformed response: %s", line)
}
res := &Response{
Status: ss[1] + " " + ss[2],
Proto: ss[0],
}
res.StatusCode, err = strconv.Atoi(ss[1])
if err != nil {
return nil, err
}
res.Header, err = tp.ReadMIMEHeader()
if err != nil {
return nil, err
}
if val := res.Header.Get("Content-Length"); val != "" {
var i int
i, err = strconv.Atoi(val)
res.Body = make([]byte, i)
if _, err = io.ReadAtLeast(r, res.Body, i); err != nil {
return nil, err
}
}
return res, nil
}
// Request like http.Request, but with any proto
type Request struct {
Method string
URL *url.URL
Proto string
Header textproto.MIMEHeader
Body []byte
}
func (r *Request) String() string {
s := r.Method + " " + r.URL.String() + " " + r.Proto + EndLine
for k, v := range r.Header {
s += k + ": " + v[0] + EndLine
}
s += EndLine
if r.Body != nil {
s += string(r.Body)
}
return s
}
func (r *Request) Write(w io.Writer) (err error) {
_, err = w.Write([]byte(r.String()))
return
}
func ReadRequest(r *bufio.Reader) (*Request, error) {
tp := textproto.NewReader(r)
line, err := tp.ReadLine()
if err != nil {
return nil, err
}
ss := strings.SplitN(line, " ", 3)
if len(ss) != 3 {
return nil, fmt.Errorf("wrong request: %s", line)
}
req := &Request{
Method: ss[0],
Proto: ss[2],
}
req.URL, err = url.Parse(ss[1])
if err != nil {
return nil, err
}
req.Header, err = tp.ReadMIMEHeader()
if err != nil {
return nil, err
}
if val := req.Header.Get("Content-Length"); val != "" {
var i int
i, err = strconv.Atoi(val)
req.Body = make([]byte, i)
if _, err = io.ReadAtLeast(r, req.Body, i); err != nil {
return nil, err
}
}
return req, nil
}