xref: /xiu/protocol/rtsp/src/http/mod.rs (revision b36cf5da)
1 use crate::global_trait::Marshal;
2 use crate::global_trait::Unmarshal;
3 use crate::rtsp_utils;
4 use indexmap::IndexMap;
5 
6 #[derive(Debug, Clone, Default)]
7 pub struct RtspRequest {
8     pub method: String,
9     pub url: String,
10     //url = "rtsp://{}:{}/{}", address, port, path
11     pub address: String,
12     pub port: u16,
13     pub path: String,
14     pub version: String,
15     pub headers: IndexMap<String, String>,
16     pub body: Option<String>,
17 }
18 
19 impl RtspRequest {
get_header(&self, header_name: &String) -> Option<&String>20     pub fn get_header(&self, header_name: &String) -> Option<&String> {
21         self.headers.get(header_name)
22     }
23 }
24 
25 impl Unmarshal for RtspRequest {
unmarshal(request_data: &str) -> Option<Self>26     fn unmarshal(request_data: &str) -> Option<Self> {
27         let mut rtsp_request = RtspRequest::default();
28         let header_end_idx = if let Some(idx) = request_data.find("\r\n\r\n") {
29             let data_except_body = &request_data[..idx];
30             let mut lines = data_except_body.lines();
31             //parse the first line
32             if let Some(request_first_line) = lines.next() {
33                 let mut fields = request_first_line.split_ascii_whitespace();
34                 if let Some(method) = fields.next() {
35                     rtsp_request.method = method.to_string();
36                 }
37                 if let Some(url) = fields.next() {
38                     rtsp_request.url = url.to_string();
39 
40                     if let Some(val) = url.strip_prefix("rtsp://") {
41                         if let Some(index) = val.find('/') {
42                             let path = &url[7 + index + 1..];
43                             rtsp_request.path = String::from(path);
44                             let address_with_port = &url[7..7 + index];
45 
46                             let (address_val, port_val) =
47                                 rtsp_utils::scanf!(address_with_port, ':', String, u16);
48 
49                             if let Some(address) = address_val {
50                                 rtsp_request.address = address;
51                             }
52                             if let Some(port) = port_val {
53                                 rtsp_request.port = port;
54                             }
55 
56                             print!("address_with_port: {address_with_port}");
57                         }
58                     }
59                 }
60                 if let Some(version) = fields.next() {
61                     rtsp_request.version = version.to_string();
62                 }
63             }
64             //parse headers
65             for line in lines {
66                 if let Some(index) = line.find(": ") {
67                     let name = line[..index].to_string();
68                     let value = line[index + 2..].to_string();
69                     rtsp_request.headers.insert(name, value);
70                 }
71             }
72             idx + 4
73         } else {
74             return None;
75         };
76 
77         if request_data.len() > header_end_idx {
78             //parse body
79             rtsp_request.body = Some(request_data[header_end_idx..].to_string());
80         }
81 
82         Some(rtsp_request)
83     }
84 }
85 
86 impl Marshal for RtspRequest {
marshal(&self) -> String87     fn marshal(&self) -> String {
88         let mut request_str = format!("{} {} {}\r\n", self.method, self.url, self.version);
89         for (header_name, header_value) in &self.headers {
90             if header_name != &"Content-Length".to_string() {
91                 request_str += &format!("{header_name}: {header_value}\r\n");
92             }
93         }
94         if let Some(body) = &self.body {
95             request_str += &format!("Content-Length: {}\r\n", body.len());
96         }
97         request_str += "\r\n";
98         if let Some(body) = &self.body {
99             request_str += body;
100         }
101         request_str
102     }
103 }
104 
105 #[derive(Debug, Clone, Default)]
106 pub struct RtspResponse {
107     pub version: String,
108     pub status_code: u16,
109     pub reason_phrase: String,
110     pub headers: IndexMap<String, String>,
111     pub body: Option<String>,
112 }
113 
114 impl Unmarshal for RtspResponse {
unmarshal(request_data: &str) -> Option<Self>115     fn unmarshal(request_data: &str) -> Option<Self> {
116         let mut rtsp_response = RtspResponse::default();
117         let header_end_idx = if let Some(idx) = request_data.find("\r\n\r\n") {
118             let data_except_body = &request_data[..idx];
119             let mut lines = data_except_body.lines();
120             //parse the first line
121             if let Some(request_first_line) = lines.next() {
122                 let mut fields = request_first_line.split_ascii_whitespace();
123 
124                 if let Some(version) = fields.next() {
125                     rtsp_response.version = version.to_string();
126                 }
127                 if let Some(status) = fields.next() {
128                     if let Ok(status) = status.parse::<u16>() {
129                         rtsp_response.status_code = status;
130                     }
131                 }
132                 if let Some(reason_phrase) = fields.next() {
133                     rtsp_response.reason_phrase = reason_phrase.to_string();
134                 }
135             }
136             //parse headers
137             for line in lines {
138                 if let Some(index) = line.find(": ") {
139                     let name = line[..index].to_string();
140                     let value = line[index + 2..].to_string();
141                     rtsp_response.headers.insert(name, value);
142                 }
143             }
144             idx + 4
145         } else {
146             return None;
147         };
148 
149         if request_data.len() > header_end_idx {
150             //parse body
151             rtsp_response.body = Some(request_data[header_end_idx..].to_string());
152         }
153 
154         Some(rtsp_response)
155     }
156 }
157 
158 impl Marshal for RtspResponse {
marshal(&self) -> String159     fn marshal(&self) -> String {
160         let mut response_str = format!(
161             "{} {} {}\r\n",
162             self.version, self.status_code, self.reason_phrase
163         );
164         for (header_name, header_value) in &self.headers {
165             if header_name != &"Content-Length".to_string() {
166                 response_str += &format!("{header_name}: {header_value}\r\n");
167             }
168         }
169         if let Some(body) = &self.body {
170             response_str += &format!("Content-Length: {}\r\n", body.len());
171         }
172         response_str += "\r\n";
173         if let Some(body) = &self.body {
174             response_str += body;
175         }
176         response_str
177     }
178 }
179 
180 #[cfg(test)]
181 mod tests {
182 
183     use crate::{global_trait::Marshal, http::Unmarshal};
184 
185     use super::RtspRequest;
186 
187     use indexmap::IndexMap;
188     use std::io::BufRead;
189     #[allow(dead_code)]
read_headers(reader: &mut dyn BufRead) -> Option<IndexMap<String, String>>190     fn read_headers(reader: &mut dyn BufRead) -> Option<IndexMap<String, String>> {
191         let mut headers = IndexMap::new();
192         loop {
193             let mut line = String::new();
194             match reader.read_line(&mut line) {
195                 Ok(0) => break,
196                 Ok(_) => {
197                     if let Some(index) = line.find(": ") {
198                         let name = line[..index].to_string();
199                         let value = line[index + 2..].trim().to_string();
200                         headers.insert(name, value);
201                     }
202                 }
203                 Err(_) => return None,
204             }
205         }
206         Some(headers)
207     }
208 
209     // #[test]
210     // fn test_parse_rtsp_request_chatgpt() {
211     //     let data1 = "ANNOUNCE rtsp://127.0.0.1:5544/stream RTSP/1.0\r\n\
212     //     Content-Type: application/sdp\r\n\
213     //     CSeq: 2\r\n\
214     //     User-Agent: Lavf58.76.100\r\n\
215     //     Content-Length: 500\r\n\
216     //     \r\n\
217     //     v=0\r\n\
218     //     o=- 0 0 IN IP4 127.0.0.1\r\n\
219     //     s=No Name\r\n\
220     //     c=IN IP4 127.0.0.1\r\n\
221     //     t=0 0\r\n\
222     //     a=tool:libavformat 58.76.100\r\n\
223     //     m=video 0 RTP/AVP 96\r\n\
224     //     b=AS:284\r\n\
225     //     a=rtpmap:96 H264/90000
226     //     a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAHqzZQKAv+XARAAADAAEAAAMAMg8WLZY=,aOvjyyLA; profile-level-id=64001E\r\n\
227     //     a=control:streamid=0\r\n\
228     //     m=audio 0 RTP/AVP 97\r\n\
229     //     b=AS:128\r\n\
230     //     a=rtpmap:97 MPEG4-GENERIC/48000/2\r\n\
231     //     a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=119056E500\r\n\
232     //     a=control:streamid=1\r\n";
233 
234     //     // v=0:SDP版本号,通常为0。
235     //     // o=- 0 0 IN IP4 127.0.0.1:会话的所有者和会话ID,以及会话开始时间和会话结束时间的信息。
236     //     // s=No Name:会话名称或标题。
237     //     // c=IN IP4 127.0.0.1:表示会话数据传输的地址类型(IPv4)和地址(127.0.0.1)。
238     //     // t=0 0:会话时间,包括会话开始时间和结束时间,这里的值都是0,表示会话没有预定义的结束时间。
239     //     // a=tool:libavformat 58.76.100:会话所使用的工具或软件名称和版本号。
240 
241     //     // m=video 0 RTP/AVP 96:媒体类型(video或audio)、媒体格式(RTP/AVP)、媒体格式编号(96)和媒体流的传输地址。
242     //     // b=AS:284:视频流所使用的带宽大小。
243     //     // a=rtpmap:96 H264/90000:视频流所使用的编码方式(H.264)和时钟频率(90000)。
244     //     // a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAHqzZQKAv+XARAAADAAEAAAMAMg8WLZY=,aOvjyyLA; profile-level-id=64001E:视频流的格式参数,如分片方式、SPS和PPS等。
245     //     // a=control:streamid=0:指定视频流的流ID。
246 
247     //     // m=audio 0 RTP/AVP 97:媒体类型(audio)、媒体格式(RTP/AVP)、媒体格式编号(97)和媒体流的传输地址。
248     //     // b=AS:128:音频流所使用的带宽大小。
249     //     // a=rtpmap:97 MPEG4-GENERIC/48000/2:音频流所使用的编码方式(MPEG4-GENERIC)、采样率(48000Hz)、和通道数(2)。
250     //     // a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=119056E500:音频流的格式参数,如编码方式、采样长度、索引长度等。
251     //     // a=control:streamid=1:指定音频流的流ID。
252 
253     //     if let Some(request) = parse_request_bytes(&data1.as_bytes()) {
254     //         println!(" parser: {:?}", request);
255     //     }
256     // }
257 
258     #[test]
test_parse_rtsp_request()259     fn test_parse_rtsp_request() {
260         let data1 = "SETUP rtsp://127.0.0.1/stream/streamid=0 RTSP/1.0\r\n\
261         Transport: RTP/AVP/TCP;unicast;interleaved=0-1;mode=record\r\n\
262         CSeq: 3\r\n\
263         User-Agent: Lavf58.76.100\r\n\
264         \r\n";
265 
266         if let Some(parser) = RtspRequest::unmarshal(data1) {
267             println!(" parser: {parser:?}");
268             let marshal_result = parser.marshal();
269             print!("marshal result: =={marshal_result}==");
270             assert_eq!(data1, marshal_result);
271         }
272 
273         let data2 = "ANNOUNCE rtsp://127.0.0.1/stream RTSP/1.0\r\n\
274         Content-Type: application/sdp\r\n\
275         CSeq: 2\r\n\
276         User-Agent: Lavf58.76.100\r\n\
277         Content-Length: 500\r\n\
278         \r\n\
279         v=0\r\n\
280         o=- 0 0 IN IP4 127.0.0.1\r\n\
281         s=No Name\r\n\
282         c=IN IP4 127.0.0.1\r\n\
283         t=0 0\r\n\
284         a=tool:libavformat 58.76.100\r\n\
285         m=video 0 RTP/AVP 96\r\n\
286         b=AS:284\r\n\
287         a=rtpmap:96 H264/90000\r\n\
288         a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAHqzZQKAv+XARAAADAAEAAAMAMg8WLZY=,aOvjyyLA; profile-level-id=64001E\r\n\
289         a=control:streamid=0\r\n\
290         m=audio 0 RTP/AVP 97\r\n\
291         b=AS:128\r\n\
292         a=rtpmap:97 MPEG4-GENERIC/48000/2\r\n\
293         a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=119056E500\r\n\
294         a=control:streamid=1\r\n";
295 
296         if let Some(parser) = RtspRequest::unmarshal(data2) {
297             println!(" parser: {parser:?}");
298             let marshal_result = parser.marshal();
299             print!("marshal result: =={marshal_result}==");
300             assert_eq!(data2, marshal_result);
301         }
302     }
303 
304     #[test]
test_http_status_code()305     fn test_http_status_code() {
306         let stats_code = http::StatusCode::OK;
307 
308         println!(
309             "stats_code: {}, {}",
310             stats_code.canonical_reason().unwrap(),
311             stats_code.as_u16()
312         )
313     }
314 }
315