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 }