github.com/cnotch/ipchub@v1.1.0/av/format/rtsp/header.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 "fmt" 10 "io" 11 "sort" 12 "strconv" 13 "strings" 14 "sync" 15 ) 16 17 // RTSP 头部域定义 18 // type:support:methods 19 // type: "g"通用的请求头部;"R"请求头部;"r"响应头部;"e"实体Body头部域。 20 // support: opt. 可选; req. 必须 21 // methods: 头部域应用范围 22 const ( 23 FieldAccept = "Accept" // (R:opt.:entity) 24 FieldAcceptEncoding = "Accept-Encoding" // (R:opt.:entity) 25 FieldAcceptLanguage = "Accept-Language" // (R:opt.:all) 26 FieldAllow = "Allow" // (R:opt.:all) 27 FieldAuthorization = "Authorization" // (R:opt.:all) 28 FieldBandwidth = "Bandwidth" // (R:opt.all) 29 FieldBlocksize = "Blocksize" // (R:opt.:all but OPTIONS, TEARDOWN) 30 FieldCacheControl = "Cache-Control" // (g:opt.:SETUP) 31 FieldConference = "Conference" // (R:opt.:SETUP) 32 FieldConnection = "Connection" // (g:req.:all) 33 FieldContentBase = "Content-Base" // (e:opt.:entity) 34 FieldContentEncoding = "Content-Encoding" // (e:req.:SET_PARAMETER ; e:req.:DESCRIBE, ANNOUNCE ) 35 FieldContentLanguage = "Content-Language" // (e:req.:DESCRIBE, ANNOUNCE) 36 FieldContentLength = "Content-Length" // (e:req.:SET_PARAMETER, ANNOUNCE; e:req.:entity) 37 FieldContentLocation = "Content-Location" // (e:opt.:entity) 38 FieldContentType = "Content-Type" // (e:req.:SET_PARAMETER, ANNOUNCE; r:req.:entity ) 39 FieldCSeq = "CSeq" // (g:req.:all) 40 FieldDate = "Date" // (g:opt.:all) 41 FieldExpires = "Expires" // (e:opt.:DESCRIBE, ANNOUNCE) 42 FieldFrom = "From" // (R:opt.:all) 43 FieldIfModifiedSince = "If-Modified-Since" // (R:opt.:DESCRIBE, SETUP) 44 FieldLastModified = "Last-Modified" // (e:opt.:entity) 45 FieldProxyAuthenticate = "Proxy-Authenticate" // 46 FieldProxyRequire = "Proxy-Require" // (R:req.:all) 47 FieldPublic = "Public" // (r:opt.:all) 48 FieldRange = "Range" // (R:opt.:PLAY, PAUSE, RECORD; r:opt.:PLAY, PAUSE, RECORD) 49 FieldReferer = "Referer" // (R:opt.:all) 50 FieldRequire = "Require" // (R:req.:all) 51 FieldRetryAfter = "Retry-After" // (r:opt.:all) 52 FieldRTPInfo = "RTP-Info" // (r:req.:PLAY) 53 FieldScale = "Scale" // (Rr:opt.:PLAY, RECORD) 54 FieldSession = "Session" // (Rr:req.:all but SETUP, OPTIONS) 55 FieldServer = "Server" // (r:opt.:all) 56 FieldSpeed = "Speed" // (Rr:opt.:PLAY) 57 FieldTransport = "Transport" // (Rr:req.:SETUP) 58 FieldUnsupported = "Unsupported" // (r:req.:all) 59 FieldUserAgent = "User-Agent" // (R:opt.:all) 60 FieldVia = "Via" // (g:opt.:all) 61 FieldWWWAuthenticate = "WWW-Authenticate" // (r:opt.:all) 62 ) 63 64 type badStringError struct { 65 what string 66 str string 67 } 68 69 func (e *badStringError) Error() string { return fmt.Sprintf("%s %q", e.what, e.str) } 70 71 // Header 表示 RTSP 头部域键值对. 72 type Header map[string][]string 73 74 // Add 添加键值对到头部. 75 // 如果建已经存在,则对键值做append操作. 76 func (h Header) Add(key, value string) { 77 key = canonicalKV(key) 78 value = canonicalKV(value) 79 h[key] = append(h[key], value) 80 } 81 82 // Set 设置头部指定键的值,操作后该键只有单值. 83 // 如果键已经存在,则覆盖其值. 84 func (h Header) Set(key, value string) { 85 key = canonicalKV(key) 86 value = canonicalKV(value) 87 h[key] = []string{value} 88 } 89 90 // Get 获取指定键的值,方法对键会做规范化处理(textproto.CanonicalMIMEHeaderKey) 91 // 如果没有值返回“”,如果存在返回第一个值 92 // 想访问多值,直接使用map方法. 93 func (h Header) Get(key string) string { 94 key = canonicalKV(key) 95 v := h[key] 96 if len(v) == 0 { 97 return "" 98 } 99 return v[0] 100 } 101 102 // set 和 Set方法类似,不对 key 进行规范化处理 103 func (h Header) set(key, value string) { 104 h[key] = []string{value} 105 } 106 107 // get 和 Get方法类似,不对 key 进行规范化处理. 108 func (h Header) get(key string) string { 109 if v := h[key]; len(v) > 0 { 110 return v[0] 111 } 112 return "" 113 } 114 115 // Del 删除指定 key 的值. 116 func (h Header) Del(key string) { 117 delete(h, canonicalKV(key)) 118 } 119 120 // SetInt 设置头部域整数值 121 func (h Header) SetInt(key string, value int) { 122 h[key] = []string{strconv.Itoa(value)} 123 } 124 125 // Int 获取头部整数域值 126 func (h Header) Int(key string) int { 127 fv := h.get(key) 128 if len(fv) < 1 { 129 return 0 130 } 131 132 n, err := strconv.ParseInt(fv, 10, 32) 133 if err != nil || n < 0 { 134 return 0 135 } 136 137 return int(n) 138 } 139 140 // Setf 格式化的设置头部域 141 func (h Header) Setf(key, format string, a ...interface{}) string { 142 value := fmt.Sprintf(format, a...) 143 h.set(key, value) 144 return value 145 } 146 147 // clone 克隆头部 148 func (h Header) clone() Header { 149 h2 := make(Header, len(h)) 150 for k, vv := range h { 151 vv2 := make([]string, len(vv)) 152 copy(vv2, vv) 153 h2[k] = vv2 154 } 155 return h2 156 } 157 158 // ReadHeader 根据规范的格式从 r 中读取 Header 159 func ReadHeader(r *bufio.Reader) (Header, error) { 160 h := make(Header, 6) // 多数情况够了 161 for { 162 var kv string 163 kv, err := readLine(r) 164 // 返回错误 165 if err != nil { 166 return nil, err 167 } 168 169 // 空行,Header读取完成退出循环; 170 // 根据 Content-Length 的值来决定是否需要读取Body 171 if len(kv) == 0 { 172 break 173 } 174 175 i := strings.Index(kv, ":") 176 // 没有找到分割符,格式错误 177 if i < 0 { 178 return nil, &badStringError{"malformed header line: ", kv} 179 } 180 181 key := canonicalKV(kv[:i]) 182 // 忽略,跳过 183 if key == "" { 184 continue 185 } 186 // 忽略key的大小写 187 if canonicalKey, ok := canonicalKeys[strings.ToUpper(key)]; ok { 188 key = canonicalKey 189 } 190 191 value := canonicalKV(kv[i+1:]) 192 h[key] = append(h[key], value) 193 194 // // 可能存在多个值 195 // values := strings.Split(kv[i+1:], ",") 196 // for _, value := range values { 197 // value = strings.TrimSpace(value) 198 // if value == "" { // 忽略空 Value 199 // continue 200 // } 201 // h[key] = append(h[key], value) 202 // } 203 } 204 return h, nil 205 } 206 207 // Write 根据规范将 Header 输出到 w 208 func (h Header) Write(w io.Writer) error { 209 ws, ok := w.(writeStringer) 210 if !ok { 211 ws = stringWriter{w} 212 } 213 214 kvs, sorter := h.sortedKeyValues() 215 defer headerSorterPool.Put(sorter) 216 217 for _, kv := range kvs { 218 value := strings.Join(kv.values, ", ") 219 220 for _, s := range []string{kv.key, ": ", value, "\r\n"} { 221 if _, err := ws.WriteString(s); err != nil { 222 return err 223 } 224 } 225 } 226 227 // 写 Header 结束行 228 ws.WriteString("\r\n") 229 return nil 230 } 231 232 type keyValues struct { 233 key string 234 values []string 235 } 236 237 // A headerSorter implements sort.Interface by sorting a []keyValues 238 // by key. It's used as a pointer, so it can fit in a sort.Interface 239 // interface value without allocation. 240 type headerSorter struct { 241 kvs []keyValues 242 } 243 244 func (s *headerSorter) Len() int { return len(s.kvs) } 245 func (s *headerSorter) Swap(i, j int) { s.kvs[i], s.kvs[j] = s.kvs[j], s.kvs[i] } 246 func (s *headerSorter) Less(i, j int) bool { return s.kvs[i].key < s.kvs[j].key } 247 248 var headerSorterPool = sync.Pool{ 249 New: func() interface{} { return new(headerSorter) }, 250 } 251 252 // sortedKeyValues returns h's keys sorted in the returned kvs 253 // slice. The headerSorter used to sort is also returned, for possible 254 // return to headerSorterCache. 255 func (h Header) sortedKeyValues() (kvs []keyValues, hs *headerSorter) { 256 hs = headerSorterPool.Get().(*headerSorter) 257 if cap(hs.kvs) < len(h) { 258 hs.kvs = make([]keyValues, 0, len(h)) 259 } 260 kvs = hs.kvs[:0] 261 for k, vv := range h { 262 kvs = append(kvs, keyValues{k, vv}) 263 } 264 hs.kvs = kvs 265 sort.Sort(hs) 266 return kvs, hs 267 } 268 269 // readLine 读取一行 270 func readLine(r *bufio.Reader) (string, error) { 271 const maxLineLenght = 16 * 1024 272 273 var line []byte 274 for { 275 l, more, err := r.ReadLine() 276 if err != nil { 277 return "", err 278 } 279 // Avoid the copy if the first call produced a full line. 280 if line == nil && !more { 281 return string(l), nil 282 } 283 line = append(line, l...) 284 if !more { 285 break 286 } 287 // if len(line) >maxLineLenght { 288 // return string(line),errors.New("line over the maximum length") 289 // } 290 } 291 return string(line), nil 292 } 293 294 var headerNewlineToSpace = strings.NewReplacer("\n", " ", "\r", " ") 295 296 func canonicalKV(s string) string { 297 return strings.TrimSpace(headerNewlineToSpace.Replace(s)) 298 } 299 300 var canonicalKeys = map[string]string{ 301 strings.ToUpper(FieldAccept): FieldAccept, 302 strings.ToUpper(FieldAcceptEncoding): FieldAcceptEncoding, 303 strings.ToUpper(FieldAcceptLanguage): FieldAcceptLanguage, 304 strings.ToUpper(FieldAllow): FieldAllow, 305 strings.ToUpper(FieldAuthorization): FieldAuthorization, 306 strings.ToUpper(FieldBandwidth): FieldBandwidth, 307 strings.ToUpper(FieldBlocksize): FieldBlocksize, 308 strings.ToUpper(FieldCacheControl): FieldCacheControl, 309 strings.ToUpper(FieldConference): FieldConference, 310 strings.ToUpper(FieldConnection): FieldConnection, 311 strings.ToUpper(FieldContentBase): FieldContentBase, 312 strings.ToUpper(FieldContentEncoding): FieldContentEncoding, 313 strings.ToUpper(FieldContentLanguage): FieldContentLanguage, 314 strings.ToUpper(FieldContentLength): FieldContentLength, 315 strings.ToUpper(FieldContentLocation): FieldContentLocation, 316 strings.ToUpper(FieldContentType): FieldContentType, 317 strings.ToUpper(FieldCSeq): FieldCSeq, 318 strings.ToUpper(FieldDate): FieldDate, 319 strings.ToUpper(FieldExpires): FieldExpires, 320 strings.ToUpper(FieldFrom): FieldFrom, 321 strings.ToUpper(FieldIfModifiedSince): FieldIfModifiedSince, 322 strings.ToUpper(FieldLastModified): FieldLastModified, 323 strings.ToUpper(FieldProxyAuthenticate): FieldProxyAuthenticate, 324 strings.ToUpper(FieldProxyRequire): FieldProxyRequire, 325 strings.ToUpper(FieldPublic): FieldPublic, 326 strings.ToUpper(FieldRange): FieldRange, 327 strings.ToUpper(FieldReferer): FieldReferer, 328 strings.ToUpper(FieldRequire): FieldRequire, 329 strings.ToUpper(FieldRetryAfter): FieldRetryAfter, 330 strings.ToUpper(FieldRTPInfo): FieldRTPInfo, 331 strings.ToUpper(FieldScale): FieldScale, 332 strings.ToUpper(FieldSession): FieldSession, 333 strings.ToUpper(FieldServer): FieldServer, 334 strings.ToUpper(FieldSpeed): FieldSpeed, 335 strings.ToUpper(FieldTransport): FieldTransport, 336 strings.ToUpper(FieldUnsupported): FieldUnsupported, 337 strings.ToUpper(FieldUserAgent): FieldUserAgent, 338 strings.ToUpper(FieldVia): FieldVia, 339 strings.ToUpper(FieldWWWAuthenticate): FieldWWWAuthenticate, 340 }