Skip to content

Commit 78f589e

Browse files
author
Steven Vancoillie
committed
fix: allow lower-case header names as per RFC spec
Fixes: #310
1 parent 9f57fd7 commit 78f589e

File tree

1 file changed

+101
-16
lines changed

1 file changed

+101
-16
lines changed

lib/utils/protocols/rtsp.ts

+101-16
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,55 @@
11
/*
2-
Example data:
3-
RTSP/1.0 200 OK
4-
CSeq: 3
5-
Content-Type: application/sdp
6-
Content-Base: rtsp://192.168.0.3/axis-media/media.amp/
7-
Server: GStreamer RTSP server
8-
Date: Wed, 03 Jun 2015 14:23:42 GMT
9-
Content-Length: 623
2+
* The RTSP response format is defined in RFC 7826,
3+
* using ABNF notation specified in RFC 5234.
4+
* Strings in ABNF rules ("...") are always case insensitive!
5+
*
6+
* Basic rules to help with the headers below:
7+
* ====
8+
* CR = %x0D ; US-ASCII CR, carriage return (13)
9+
* LF = %x0A ; US-ASCII LF, linefeed (10)
10+
* SP = %x20 ; US-ASCII SP, space (32)
11+
* HT = %x09 ; US-ASCII HT, horizontal-tab (9)
12+
* CRLF = CR LF
13+
* LWS = [CRLF] 1*( SP / HT ) ; Line-breaking whitespace
14+
* SWS = [LWS] ; Separating whitespace
15+
* HCOLON = *( SP / HT ) ":" SWS
16+
*
17+
* RTSP response rules (a `*` means zero or more):
18+
* ====
19+
* Status-Line = RTSP-Version SP Status-Code SP Reason-Phrase CRLF
20+
* Response = Status-Line
21+
* *((general-header
22+
* / response-header
23+
* / message-body-header) CRLF)
24+
* CRLF
25+
* [ message-body-data ]
26+
*
27+
* Example response:
28+
* ====
29+
* RTSP/1.0 200 OK
30+
* CSeq: 3
31+
* Content-Type: application/sdp
32+
* Content-Base: rtsp://192.168.0.3/axis-media/media.amp/
33+
* Server: GStreamer RTSP server
34+
* Date: Wed, 03 Jun 2015 14:23:42 GMT
35+
* Content-Length: 623
36+
*
37+
* v=0
38+
* ....
39+
*/
1040

11-
v=0
12-
....
13-
*/
41+
/**
42+
* Extract the value of a header.
43+
*
44+
* @param buffer The response bytes
45+
* @param header The header to search for
46+
*/
1447
export const extractHeaderValue = (buffer: Buffer, header: string) => {
15-
const anchor = `\n${header}: `
16-
const start = buffer.indexOf(anchor)
48+
const anchor = `\n${header.toLowerCase()}: `
49+
const start = buffer
50+
.toString()
51+
.toLowerCase()
52+
.indexOf(anchor)
1753
if (start >= 0) {
1854
const end = buffer.indexOf('\n', start + anchor.length)
1955
const headerValue = buffer
@@ -25,6 +61,10 @@ export const extractHeaderValue = (buffer: Buffer, header: string) => {
2561
}
2662

2763
export const sequence = (buffer: Buffer) => {
64+
/**
65+
* CSeq = "CSeq" HCOLON cseq-nr
66+
* cseq-nr = 1*9DIGIT
67+
*/
2868
const val = extractHeaderValue(buffer, 'CSeq')
2969
if (val !== null) {
3070
return Number(val)
@@ -33,17 +73,29 @@ export const sequence = (buffer: Buffer) => {
3373
}
3474

3575
export const sessionId = (buffer: Buffer) => {
76+
/**
77+
* Session = "Session" HCOLON session-id
78+
* [ SEMI "timeout" EQUAL delta-seconds ]
79+
* session-id = 1*256( ALPHA / DIGIT / safe )
80+
* delta-seconds = 1*19DIGIT
81+
*/
3682
const val = extractHeaderValue(buffer, 'Session')
3783
return val ? val.split(';')[0] : null
3884
}
3985

4086
export const sessionTimeout = (buffer: Buffer) => {
87+
/**
88+
* Session = "Session" HCOLON session-id
89+
* [ SEMI "timeout" EQUAL delta-seconds ]
90+
* session-id = 1*256( ALPHA / DIGIT / safe )
91+
* delta-seconds = 1*19DIGIT
92+
*/
4193
const val = extractHeaderValue(buffer, 'Session')
4294
if (val === null) {
4395
return null
4496
}
4597
const timeoutToken = 'timeout='
46-
const timeoutPosition = val.indexOf(timeoutToken)
98+
const timeoutPosition = val.toLowerCase().indexOf(timeoutToken)
4799
if (timeoutPosition !== -1) {
48100
let timeoutVal = val.substring(timeoutPosition + timeoutToken.length)
49101
timeoutVal = timeoutVal.split(';')[0]
@@ -58,15 +110,43 @@ export const statusCode = (buffer: Buffer) => {
58110
}
59111

60112
export const contentBase = (buffer: Buffer) => {
113+
/**
114+
* Content-Base = "Content-Base" HCOLON RTSP-URI
115+
*/
61116
return extractHeaderValue(buffer, 'Content-Base')
62117
}
63118

64119
export const connectionEnded = (buffer: Buffer) => {
65-
return extractHeaderValue(buffer, 'Connection') === 'close'
120+
/**
121+
* Connection = "Connection" HCOLON connection-token
122+
* *(COMMA connection-token)
123+
* connection-token = "close" / token
124+
*/
125+
const connectionToken = extractHeaderValue(buffer, 'Connection')
126+
return connectionToken !== null && connectionToken.toLowerCase() === 'close'
66127
}
67128

68129
export const range = (buffer: Buffer) => {
69-
// Possible range headers:
130+
/**
131+
* Range = "Range" HCOLON ranges-spec
132+
* ranges-spec = npt-range / utc-range / smpte-range
133+
* / range-ext
134+
* npt-range = "npt" [EQUAL npt-range-spec]
135+
* npt-range-spec = ( npt-time "-" [ npt-time ] ) / ( "-" npt-time )
136+
* npt-time = "now" / npt-sec / npt-hhmmss / npt-hhmmss-comp
137+
* npt-sec = 1*19DIGIT [ "." 1*9DIGIT ]
138+
* npt-hhmmss = npt-hh ":" npt-mm ":" npt-ss [ "." 1*9DIGIT ]
139+
* npt-hh = 2*19DIGIT ; any positive number
140+
* npt-mm = 2*2DIGIT ; 0-59
141+
* npt-ss = 2*2DIGIT ; 0-59
142+
* npt-hhmmss-comp = npt-hh-comp ":" npt-mm-comp ":" npt-ss-comp
143+
* [ "." 1*9DIGIT ] ; Compatibility format
144+
* npt-hh-comp = 1*19DIGIT ; any positive number
145+
* npt-mm-comp = 1*2DIGIT ; 0-59
146+
* npt-ss-comp = 1*2DIGIT ; 0-59
147+
*/
148+
149+
// Example range headers:
70150
// Range: npt=now-
71151
// Range: npt=1154.598701-3610.259146
72152
const npt = extractHeaderValue(buffer, 'Range')
@@ -83,6 +163,11 @@ export const range = (buffer: Buffer) => {
83163
* @return {Number} The body offset, or -1 if no header end found
84164
*/
85165
export const bodyOffset = (chunk: Buffer) => {
166+
/**
167+
* Strictly speaking, it seems RTSP MUST have CRLF and doesn't allow CR or LF on its own.
168+
* That means that the end of the header part should be a pair of CRLF, but we're being
169+
* flexible here and also allow LF LF or CR CR instead of CRLF CRLF.
170+
*/
86171
const bodyOffsets = ['\n\n', '\r\r', '\r\n\r\n']
87172
.map(s => {
88173
const offset = chunk.indexOf(s)

0 commit comments

Comments
 (0)