github.com/cnotch/ipchub@v1.1.0/av/format/rtsp/response.go (about)

     1  // Copyright (c) 2019,CAOHONGJU All rights reserved.
     2  // Use of this source code is governed by a MIT-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package rtsp
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"io"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/cnotch/ipchub/utils/scan"
    15  )
    16  
    17  // RTSP 响应状态码.
    18  // See: https://tools.ietf.org/html/rfc2326#page-19
    19  const (
    20  	StatusContinue = 100
    21  
    22  	//======Success 2xx
    23  	StatusOK                = 200
    24  	StatusCreated           = 201 // only for RECORD
    25  	StatusLowOnStorageSpace = 250 //only for RECORD
    26  
    27  	//======Redirection 3xx
    28  	StatusMultipleChoices  = 300
    29  	StatusMovedPermanently = 301
    30  	StatusMovedTemporarily = 302 // 和http不同
    31  	StatusSeeOther         = 303
    32  	StatusNotModified      = 304
    33  	StatusUseProxy         = 305
    34  
    35  	//======Client Error 4xx
    36  	StatusBadRequest                = 400
    37  	StatusUnauthorized              = 401
    38  	StatusPaymentRequired           = 402
    39  	StatusForbidden                 = 403
    40  	StatusNotFound                  = 404
    41  	StatusMethodNotAllowed          = 405
    42  	StatusNotAcceptable             = 406
    43  	StatusProxyAuthRequired         = 407
    44  	StatusRequestTimeout            = 408
    45  	StatusGone                      = 410
    46  	StatusLengthRequired            = 411
    47  	StatusPreconditionFailed        = 412 // only for DESCRIBE, SETUP
    48  	StatusRequestEntityTooLarge     = 413
    49  	StatusRequestURITooLong         = 414
    50  	StatusUnsupportedMediaType      = 415
    51  	StatusInvalidParameter          = 451 // only for SETUP
    52  	StatusConferenceNotFound        = 452 // only for SETUP
    53  	StatusNotEnoughBandwidth        = 453 // only for SETUP
    54  	StatusSessionNotFound           = 454
    55  	StatusMethodNotValidInThisState = 455
    56  	StatusHeaderFieldNotValid       = 456
    57  	StatusInvalidRange              = 457 // only for PLAY
    58  	StatusParameterIsReadOnly       = 458 // only for SET_PARAMETER
    59  	StatusAggregateOpNotAllowed     = 459
    60  	StatusOnlyAggregateOpAllowed    = 460
    61  	StatusUnsupportedTransport      = 461
    62  	StatusDestinationUnreachable    = 462
    63  
    64  	StatusInternalServerError     = 500
    65  	StatusNotImplemented          = 501
    66  	StatusBadGateway              = 502
    67  	StatusServiceUnavailable      = 503
    68  	StatusGatewayTimeout          = 504
    69  	StatusRTSPVersionNotSupported = 505
    70  	StatusOptionNotSupported      = 551 // 和 http 不同
    71  )
    72  
    73  var statusText = map[int]string{
    74  	StatusContinue: "Continue",
    75  
    76  	StatusOK:                "OK",
    77  	StatusCreated:           "Created",
    78  	StatusLowOnStorageSpace: "Low on Storage Space",
    79  
    80  	StatusMultipleChoices:  "Multiple Choices",
    81  	StatusMovedPermanently: "Moved Permanently",
    82  	StatusMovedTemporarily: "Moved Temporarily",
    83  	StatusSeeOther:         "See Other",
    84  	StatusNotModified:      "Not Modified",
    85  	StatusUseProxy:         "Use Proxy",
    86  
    87  	StatusBadRequest:                "Bad Request",
    88  	StatusUnauthorized:              "Unauthorized",
    89  	StatusPaymentRequired:           "Payment Required",
    90  	StatusForbidden:                 "Forbidden",
    91  	StatusNotFound:                  "Not Found",
    92  	StatusMethodNotAllowed:          "Method Not Allowed",
    93  	StatusNotAcceptable:             "Not Acceptable",
    94  	StatusProxyAuthRequired:         "Proxy Authentication Required",
    95  	StatusRequestTimeout:            "Request Timeout",
    96  	StatusGone:                      "Gone",
    97  	StatusLengthRequired:            "Length Required",
    98  	StatusPreconditionFailed:        "Precondition Failed",
    99  	StatusRequestEntityTooLarge:     "Request Entity Too Large",
   100  	StatusRequestURITooLong:         "Request URI Too Long",
   101  	StatusUnsupportedMediaType:      "Unsupported Media Type",
   102  	StatusInvalidParameter:          "Invalid parameter", // 451~462 和 http 不同
   103  	StatusConferenceNotFound:        "Illegal Conference Identifier",
   104  	StatusNotEnoughBandwidth:        "Not Enough Bandwidth",
   105  	StatusSessionNotFound:           "Session Not Found",
   106  	StatusMethodNotValidInThisState: "Method Not Valid In This State",
   107  	StatusHeaderFieldNotValid:       "Header Field Not Valid",
   108  	StatusInvalidRange:              "Invalid Range",
   109  	StatusParameterIsReadOnly:       "Parameter Is Read-Only",
   110  	StatusAggregateOpNotAllowed:     "Aggregate Operation Not Allowed",
   111  	StatusOnlyAggregateOpAllowed:    "Only Aggregate Operation Allowed",
   112  	StatusUnsupportedTransport:      "Unsupported Transport",
   113  	StatusDestinationUnreachable:    "Destination Unreachable",
   114  
   115  	StatusInternalServerError:     "Internal Server Error",
   116  	StatusNotImplemented:          "Not Implemented",
   117  	StatusBadGateway:              "Bad Gateway",
   118  	StatusServiceUnavailable:      "Service Unavailable",
   119  	StatusGatewayTimeout:          "Gateway Timeout",
   120  	StatusRTSPVersionNotSupported: "RTSP Version Not Supported",
   121  	StatusOptionNotSupported:      "Option not support",
   122  }
   123  
   124  // StatusText 返回 RTSP 状态码的文本。如果 code 未知返回空字串。
   125  func StatusText(code int) string {
   126  	return statusText[code]
   127  }
   128  
   129  // Response 表示 RTSP 请求的响应
   130  type Response struct {
   131  	Proto      string // e.g. "RTSP/1.0"
   132  	StatusCode int    // e.g. 200
   133  	Status     string // e.g. "200 OK"
   134  	Header     Header
   135  	Body       string
   136  	Request    *Request
   137  }
   138  
   139  // ReadResponse 根据规范从 r 中读取 Response
   140  func ReadResponse(r *bufio.Reader) (*Response, error) {
   141  	var err error
   142  
   143  	// 读取并解析首行
   144  	var line string
   145  	if line, err = readLine(r); err != nil {
   146  		return nil, err
   147  	}
   148  	var resp = new(Response)
   149  
   150  	defer func() {
   151  		if err == io.EOF { // 结尾,返回非期望的结尾
   152  			err = io.ErrUnexpectedEOF
   153  		}
   154  	}()
   155  
   156  	var i int
   157  	if i = strings.IndexByte(line, ' '); i < 0 {
   158  		return nil, &badStringError{"malformed RTSP response", line}
   159  	}
   160  
   161  	resp.Proto = line[:i]
   162  	resp.Status = strings.TrimLeft(line[i+1:], " ")
   163  
   164  	statusCodeStr := resp.Status
   165  	if i := strings.IndexByte(statusCodeStr, ' '); i != -1 {
   166  		statusCodeStr = statusCodeStr[:i]
   167  	}
   168  	if len(statusCodeStr) != 3 {
   169  		return nil, &badStringError{"malformed RTSP status code", statusCodeStr}
   170  	}
   171  	resp.StatusCode, err = strconv.Atoi(statusCodeStr)
   172  	if err != nil || resp.StatusCode < 0 {
   173  		return nil, &badStringError{"malformed RTSP status code", statusCodeStr}
   174  	}
   175  
   176  	// 读取 Header
   177  	if resp.Header, err = ReadHeader(r); err != nil {
   178  		return nil, err
   179  	}
   180  
   181  	// 读取Body
   182  	cl := resp.Header.Int(FieldContentLength)
   183  	if cl > 0 {
   184  		// 读取 n 字节的字串Body
   185  		body := make([]byte, cl)
   186  		_, err = io.ReadFull(r, body)
   187  		resp.Body = string(body)
   188  	}
   189  	return resp, nil
   190  }
   191  
   192  // Write 根据规范将 Response 输出到 w
   193  func (resp *Response) Write(w io.Writer) error {
   194  	ws, ok := w.(writeStringer)
   195  	if !ok {
   196  		ws = stringWriter{w}
   197  	}
   198  
   199  	// Status line
   200  	text := resp.Status
   201  	if text == "" {
   202  		var ok bool
   203  		text, ok = statusText[resp.StatusCode]
   204  		if !ok {
   205  			text = "status code " + strconv.Itoa(resp.StatusCode)
   206  		}
   207  	} else {
   208  		// Just to reduce stutter, if user set r.Status to "200 OK" and StatusCode to 200.
   209  		// Not important.
   210  		text = strings.TrimPrefix(text, strconv.Itoa(resp.StatusCode)+" ")
   211  	}
   212  
   213  	ws.WriteString("RTSP/1.0 ")
   214  	ws.WriteString(strconv.Itoa(resp.StatusCode))
   215  	ws.WriteString(" ")
   216  	ws.WriteString(text)
   217  	ws.WriteString("\r\n")
   218  
   219  	// 写 Header
   220  	if len(resp.Body) > 0 {
   221  		resp.Header.SetInt(FieldContentLength, len(resp.Body))
   222  	} else {
   223  		delete(resp.Header, FieldContentLength)
   224  	}
   225  	if err := resp.Header.Write(w); err != nil {
   226  		return err
   227  	}
   228  
   229  	// 写 Content
   230  	if len(resp.Body) > 0 {
   231  		if _, err := ws.WriteString(resp.Body); err != nil {
   232  			return err
   233  		}
   234  	}
   235  
   236  	return nil
   237  }
   238  
   239  func (resp *Response) String() string {
   240  	buf := bytes.Buffer{}
   241  	resp.Write(&buf)
   242  	return buf.String()
   243  }
   244  
   245  // BasicAuth 获取基本认证信息
   246  func (resp *Response) BasicAuth() (realm string, ok bool) {
   247  	auth := resp.Header.get(FieldWWWAuthenticate)
   248  
   249  	// Case insensitive prefix match. See Issue 22736.
   250  	if len(auth) < len(basicAuthPrefix) || !strings.EqualFold(auth[:len(basicAuthPrefix)], basicAuthPrefix) {
   251  		return
   252  	}
   253  
   254  	auth = auth[len(basicAuthPrefix):]
   255  
   256  	advance := auth
   257  	token := ""
   258  	continueScan := true
   259  	for continueScan {
   260  		advance, token, continueScan = scan.Comma.Scan(advance)
   261  		k, v, _ := scan.EqualPair.Scan(token)
   262  		switch k {
   263  		case "realm":
   264  			realm = v
   265  		}
   266  	}
   267  
   268  	return realm, len(realm) > 0
   269  }
   270  
   271  // SetBasicAuth 设置摘要认证安全请求
   272  func (resp *Response) SetBasicAuth(realm string) {
   273  	buf := bytes.Buffer{}
   274  	buf.WriteString(basicAuthPrefix)
   275  	buf.WriteString("realm=\"")
   276  	buf.WriteString(realm)
   277  	buf.WriteByte('"')
   278  	resp.Header.set(FieldWWWAuthenticate, buf.String())
   279  }
   280  
   281  // DigestAuth 获取摘要认证信息
   282  func (resp *Response) DigestAuth() (realm, nonce string, ok bool) {
   283  	auth := resp.Header.get(FieldWWWAuthenticate)
   284  
   285  	// Case insensitive prefix match. See Issue 22736.
   286  	if len(auth) < len(digestAuthPrefix) || !strings.EqualFold(auth[:len(digestAuthPrefix)], digestAuthPrefix) {
   287  		return
   288  	}
   289  
   290  	auth = auth[len(digestAuthPrefix):]
   291  	advance := auth
   292  	token := ""
   293  	continueScan := true
   294  	for continueScan {
   295  		advance, token, continueScan = scan.Comma.Scan(advance)
   296  		k, v, _ := scan.EqualPair.Scan(token)
   297  		switch k {
   298  		case "realm":
   299  			realm = v
   300  		case "nonce":
   301  			nonce = v
   302  		}
   303  	}
   304  
   305  	return realm, nonce, len(realm) > 0 && len(nonce) > 0
   306  }
   307  
   308  // SetDigestAuth 设置摘要认证安全请求
   309  func (resp *Response) SetDigestAuth(realm, nonce string) {
   310  	buf := bytes.Buffer{}
   311  	buf.WriteString(digestAuthPrefix)
   312  	buf.WriteString("realm=\"")
   313  	buf.WriteString(realm)
   314  	buf.WriteString("\",nonce=\"")
   315  	buf.WriteString(nonce)
   316  	buf.WriteByte('"')
   317  	resp.Header.set(FieldWWWAuthenticate, buf.String())
   318  }