mirror of
https://github.com/aler9/gortsplib
synced 2025-09-27 19:42:11 +08:00
249 lines
8.4 KiB
Go
249 lines
8.4 KiB
Go
package base
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"strconv"
|
|
)
|
|
|
|
// StatusCode is the status code of a RTSP response.
|
|
type StatusCode int
|
|
|
|
// status codes.
|
|
const (
|
|
StatusContinue StatusCode = 100
|
|
StatusOK StatusCode = 200
|
|
StatusMovedPermanently StatusCode = 301
|
|
StatusFound StatusCode = 302
|
|
StatusSeeOther StatusCode = 303
|
|
StatusNotModified StatusCode = 304
|
|
StatusUseProxy StatusCode = 305
|
|
StatusBadRequest StatusCode = 400
|
|
StatusUnauthorized StatusCode = 401
|
|
StatusPaymentRequired StatusCode = 402
|
|
StatusForbidden StatusCode = 403
|
|
StatusNotFound StatusCode = 404
|
|
StatusMethodNotAllowed StatusCode = 405
|
|
StatusNotAcceptable StatusCode = 406
|
|
StatusProxyAuthRequired StatusCode = 407
|
|
StatusRequestTimeout StatusCode = 408
|
|
StatusGone StatusCode = 410
|
|
StatusPreconditionFailed StatusCode = 412
|
|
StatusRequestEntityTooLarge StatusCode = 413
|
|
StatusRequestURITooLong StatusCode = 414
|
|
StatusUnsupportedMediaType StatusCode = 415
|
|
StatusParameterNotUnderstood StatusCode = 451
|
|
StatusNotEnoughBandwidth StatusCode = 453
|
|
StatusSessionNotFound StatusCode = 454
|
|
StatusMethodNotValidInThisState StatusCode = 455
|
|
StatusHeaderFieldNotValidForResource StatusCode = 456
|
|
StatusInvalidRange StatusCode = 457
|
|
StatusParameterIsReadOnly StatusCode = 458
|
|
StatusAggregateOperationNotAllowed StatusCode = 459
|
|
StatusOnlyAggregateOperationAllowed StatusCode = 460
|
|
StatusUnsupportedTransport StatusCode = 461
|
|
StatusDestinationUnreachable StatusCode = 462
|
|
StatusDestinationProhibited StatusCode = 463
|
|
StatusDataTransportNotReadyYet StatusCode = 464
|
|
StatusNotificationReasonUnknown StatusCode = 465
|
|
StatusKeyManagementError StatusCode = 466
|
|
StatusConnectionAuthorizationRequired StatusCode = 470
|
|
StatusConnectionCredentialsNotAccepted StatusCode = 471
|
|
StatusFailureToEstablishSecureConnection StatusCode = 472
|
|
StatusInternalServerError StatusCode = 500
|
|
StatusNotImplemented StatusCode = 501
|
|
StatusBadGateway StatusCode = 502
|
|
StatusServiceUnavailable StatusCode = 503
|
|
StatusGatewayTimeout StatusCode = 504
|
|
StatusRTSPVersionNotSupported StatusCode = 505
|
|
StatusOptionNotSupported StatusCode = 551
|
|
StatusProxyUnavailable StatusCode = 553
|
|
)
|
|
|
|
// StatusMessages contains the status messages associated with each status code.
|
|
var StatusMessages = statusMessages
|
|
|
|
var statusMessages = map[StatusCode]string{
|
|
StatusContinue: "Continue",
|
|
|
|
StatusOK: "OK",
|
|
|
|
StatusMovedPermanently: "Moved Permanently",
|
|
StatusFound: "Found",
|
|
StatusSeeOther: "See Other",
|
|
StatusNotModified: "Not Modified",
|
|
StatusUseProxy: "Use Proxy",
|
|
|
|
StatusBadRequest: "Bad Request",
|
|
StatusUnauthorized: "Unauthorized",
|
|
StatusPaymentRequired: "Payment Required",
|
|
StatusForbidden: "Forbidden",
|
|
StatusNotFound: "Not Found",
|
|
StatusMethodNotAllowed: "Method Not Allowed",
|
|
StatusNotAcceptable: "Not Acceptable",
|
|
StatusProxyAuthRequired: "Proxy Auth Required",
|
|
StatusRequestTimeout: "Request Timeout",
|
|
StatusGone: "Gone",
|
|
StatusPreconditionFailed: "Precondition Failed",
|
|
StatusRequestEntityTooLarge: "Request Entity Too Large",
|
|
StatusRequestURITooLong: "Request URI Too Long",
|
|
StatusUnsupportedMediaType: "Unsupported Media Type",
|
|
StatusParameterNotUnderstood: "Parameter Not Understood",
|
|
StatusNotEnoughBandwidth: "Not Enough Bandwidth",
|
|
StatusSessionNotFound: "Session Not Found",
|
|
StatusMethodNotValidInThisState: "Method Not Valid In This State",
|
|
StatusHeaderFieldNotValidForResource: "Header Field Not Valid for Resource",
|
|
StatusInvalidRange: "Invalid Range",
|
|
StatusParameterIsReadOnly: "Parameter Is Read-Only",
|
|
StatusAggregateOperationNotAllowed: "Aggregate Operation Not Allowed",
|
|
StatusOnlyAggregateOperationAllowed: "Only Aggregate Operation Allowed",
|
|
StatusUnsupportedTransport: "Unsupported Transport",
|
|
StatusDestinationUnreachable: "Destination Unreachable",
|
|
StatusDestinationProhibited: "Destination Prohibited",
|
|
StatusDataTransportNotReadyYet: "Data Transport Not Ready Yet",
|
|
StatusNotificationReasonUnknown: "Notification Reason Unknown",
|
|
StatusKeyManagementError: "Key Management Error",
|
|
StatusConnectionAuthorizationRequired: "Connection Authorization Required",
|
|
StatusConnectionCredentialsNotAccepted: "Connection Credentials Not Accepted",
|
|
StatusFailureToEstablishSecureConnection: "Failure to Establish Secure Connection",
|
|
|
|
StatusInternalServerError: "Internal Server Error",
|
|
StatusNotImplemented: "Not Implemented",
|
|
StatusBadGateway: "Bad Gateway",
|
|
StatusServiceUnavailable: "Service Unavailable",
|
|
StatusGatewayTimeout: "Gateway Timeout",
|
|
StatusRTSPVersionNotSupported: "RTSP Version Not Supported",
|
|
StatusOptionNotSupported: "Option Not Supported",
|
|
StatusProxyUnavailable: "Proxy Unavailable",
|
|
}
|
|
|
|
// Response is a RTSP response.
|
|
type Response struct {
|
|
// numeric status code
|
|
StatusCode StatusCode
|
|
|
|
// status message
|
|
StatusMessage string
|
|
|
|
// map of header values
|
|
Header Header
|
|
|
|
// optional body
|
|
Body []byte
|
|
}
|
|
|
|
// Read reads a response.
|
|
func (res *Response) Read(rb *bufio.Reader) error {
|
|
byts, err := readBytesLimited(rb, ' ', 255)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
proto := byts[:len(byts)-1]
|
|
|
|
if string(proto) != rtspProtocol10 {
|
|
return fmt.Errorf("expected '%s', got %v", rtspProtocol10, proto)
|
|
}
|
|
|
|
byts, err = readBytesLimited(rb, ' ', 4)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
statusCodeStr := string(byts[:len(byts)-1])
|
|
|
|
statusCode64, err := strconv.ParseInt(statusCodeStr, 10, 32)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to parse status code")
|
|
}
|
|
res.StatusCode = StatusCode(statusCode64)
|
|
|
|
byts, err = readBytesLimited(rb, '\r', 255)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
res.StatusMessage = string(byts[:len(byts)-1])
|
|
|
|
if len(res.StatusMessage) == 0 {
|
|
return fmt.Errorf("empty status message")
|
|
}
|
|
|
|
err = readByteEqual(rb, '\n')
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = res.Header.read(rb)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = (*body)(&res.Body).read(res.Header, rb)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// MarshalSize returns the size of a Response.
|
|
func (res Response) MarshalSize() int {
|
|
n := 0
|
|
|
|
if res.StatusMessage == "" {
|
|
if status, ok := statusMessages[res.StatusCode]; ok {
|
|
res.StatusMessage = status
|
|
}
|
|
}
|
|
|
|
n += len([]byte(rtspProtocol10 + " " +
|
|
strconv.FormatInt(int64(res.StatusCode), 10) + " " +
|
|
res.StatusMessage + "\r\n"))
|
|
|
|
if len(res.Body) != 0 {
|
|
res.Header["Content-Length"] = HeaderValue{strconv.FormatInt(int64(len(res.Body)), 10)}
|
|
}
|
|
|
|
n += res.Header.marshalSize()
|
|
|
|
n += body(res.Body).marshalSize()
|
|
|
|
return n
|
|
}
|
|
|
|
// MarshalTo writes a Response.
|
|
func (res Response) MarshalTo(buf []byte) (int, error) {
|
|
if res.StatusMessage == "" {
|
|
if status, ok := statusMessages[res.StatusCode]; ok {
|
|
res.StatusMessage = status
|
|
}
|
|
}
|
|
|
|
pos := 0
|
|
|
|
pos += copy(buf[pos:], []byte(rtspProtocol10+" "+
|
|
strconv.FormatInt(int64(res.StatusCode), 10)+" "+
|
|
res.StatusMessage+"\r\n"))
|
|
|
|
if len(res.Body) != 0 {
|
|
res.Header["Content-Length"] = HeaderValue{strconv.FormatInt(int64(len(res.Body)), 10)}
|
|
}
|
|
|
|
pos += res.Header.marshalTo(buf[pos:])
|
|
|
|
pos += body(res.Body).marshalTo(buf[pos:])
|
|
|
|
return pos, nil
|
|
}
|
|
|
|
// Marshal writes a Response.
|
|
func (res Response) Marshal() ([]byte, error) {
|
|
buf := make([]byte, res.MarshalSize())
|
|
_, err := res.MarshalTo(buf)
|
|
return buf, err
|
|
}
|
|
|
|
// String implements fmt.Stringer.
|
|
func (res Response) String() string {
|
|
buf, _ := res.Marshal()
|
|
return string(buf)
|
|
}
|