Bug 1474517: merge rsdparsa from upstream draft
authorNils Ohlmeier [:drno] <drno@ohlmeier.org>
Mon, 09 Jul 2018 23:56:42 -0700
changeset 818489 881f97fee191aa9756b875c64ddd0667d82642f7
parent 818433 b79457b703d9b51cf3e38199f4e154d21bc6fc97
push id116271
push userdrno@ohlmeier.org
push dateSat, 14 Jul 2018 03:39:40 +0000
bugs1474517
milestone63.0a1
Bug 1474517: merge rsdparsa from upstream Updated rsdparsa to 75d5c6df6728fbab502db06940062e0358536f9f from github upstream MozReview-Commit-ID: 9hr7DV6KTkK
Cargo.lock
media/webrtc/signaling/src/sdp/rsdparsa/Cargo.toml
media/webrtc/signaling/src/sdp/rsdparsa/src/attribute_type.rs
media/webrtc/signaling/src/sdp/rsdparsa/src/lib.rs
media/webrtc/signaling/src/sdp/rsdparsa/src/media_type.rs
media/webrtc/signaling/src/sdp/rsdparsa/tests/unit_tests.rs
media/webrtc/signaling/src/sdp/rsdparsa_capi/src/lib.rs
media/webrtc/signaling/src/sdp/rsdparsa_capi/src/media_section.rs
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1729,16 +1729,21 @@ version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "rsdparsa"
 version = "0.1.0"
+dependencies = [
+ "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.66 (git+https://github.com/servo/serde?branch=deserialize_from_enums8)",
+]
 
 [[package]]
 name = "rsdparsa_capi"
 version = "0.1.0"
 dependencies = [
  "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "nserror 0.1.0",
--- a/media/webrtc/signaling/src/sdp/rsdparsa/Cargo.toml
+++ b/media/webrtc/signaling/src/sdp/rsdparsa/Cargo.toml
@@ -1,7 +1,18 @@
 [package]
 name = "rsdparsa"
 version = "0.1.0"
 authors = ["Nils Ohlmeier <github@ohlmeier.org>"]
 
 [features]
 default = []
+# serializability
+serialize = ["serde", "serde_derive"]
+
+[dependencies]
+# clippy = {version = "*", optional = true}
+log = {version = "*"}
+serde = {version = "1.0" , optional = true}
+serde_derive = {version = "1.0" , optional = true}
+
+[dev-dependencies]
+# serde_json = {version = "1.0"}
--- a/media/webrtc/signaling/src/sdp/rsdparsa/src/attribute_type.rs
+++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/attribute_type.rs
@@ -768,17 +768,16 @@ fn parse_single_direction(to_parse: &str
         "send" => Ok(SdpSingleDirection::Send),
         "recv" => Ok(SdpSingleDirection::Recv),
         x @ _ => Err(SdpParserInternalError::Generic(
             format!("Unknown direction description found: '{:}'",x).to_string()
         ))
     }
 }
 
-
 fn parse_sctp_port(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
     let port = to_parse.parse()?;
     if port > 65535 {
         return Err(SdpParserInternalError::Generic(format!("Sctpport port {} can only be a bit 16bit number",
                                                            port)));
     }
     Ok(SdpAttribute::SctpPort(port))
 }
@@ -1097,16 +1096,17 @@ fn parse_fmtp(to_parse: &str) -> Result<
         }
     }
 
     Ok(SdpAttribute::Fmtp(SdpAttributeFmtp {
                               payload_type: payload_token.parse::<u8>()?,
                               parameters: parameters,
                           }))
 }
+
 fn parse_group(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
     let mut tokens = to_parse.split_whitespace();
     let semantics = match tokens.next() {
         None => {
             return Err(SdpParserInternalError::Generic("Group attribute is missing semantics token"
                                                            .to_string()))
         }
         Some(x) => {
@@ -1993,17 +1993,40 @@ fn test_parse_attribute_rtcp() {
     assert!(parse_attribute("rtcp:70000").is_err());
     assert!(parse_attribute("rtcp:9 IN").is_err());
     assert!(parse_attribute("rtcp:9 IN IP4").is_err());
     assert!(parse_attribute("rtcp:9 IN IP4 ::1").is_err());
 }
 
 #[test]
 fn test_parse_attribute_rtcp_fb() {
-    assert!(parse_attribute("rtcp-fb:101 ccm fir").is_ok())
+    assert!(parse_attribute("rtcp-fb:101 ack rpsi").is_ok());
+    assert!(parse_attribute("rtcp-fb:101 ack app").is_ok());
+    assert!(parse_attribute("rtcp-fb:101 ccm").is_ok());
+    assert!(parse_attribute("rtcp-fb:101 ccm fir").is_ok());
+    assert!(parse_attribute("rtcp-fb:101 ccm tmmbr").is_ok());
+    assert!(parse_attribute("rtcp-fb:101 ccm tstr").is_ok());
+    assert!(parse_attribute("rtcp-fb:101 ccm vbcm").is_ok());
+    assert!(parse_attribute("rtcp-fb:101 nack").is_ok());
+    assert!(parse_attribute("rtcp-fb:101 nack sli").is_ok());
+    assert!(parse_attribute("rtcp-fb:101 nack pli").is_ok());
+    assert!(parse_attribute("rtcp-fb:101 nack rpsi").is_ok());
+    assert!(parse_attribute("rtcp-fb:101 nack app").is_ok());
+    assert!(parse_attribute("rtcp-fb:101 trr-int 1").is_ok());
+    assert!(parse_attribute("rtcp-fb:101 goog-remb").is_ok());
+    assert!(parse_attribute("rtcp-fb:101 transport-cc").is_ok());
+
+    assert!(parse_attribute("rtcp-fb:101 unknown").is_err());
+    assert!(parse_attribute("rtcp-fb:101 ack").is_err());
+    assert!(parse_attribute("rtcp-fb:101 ccm unknwon").is_err());
+    assert!(parse_attribute("rtcp-fb:101 nack unknown").is_err());
+    assert!(parse_attribute("rtcp-fb:101 trr-int").is_err());
+    assert!(parse_attribute("rtcp-fb:101 trr-int a").is_err());
+    assert!(parse_attribute("rtcp-fb:101 goog-remb unknown").is_err());
+    assert!(parse_attribute("rtcp-fb:101 transport-cc unknown").is_err());
 }
 
 #[test]
 fn test_parse_attribute_rtcp_mux() {
     assert!(parse_attribute("rtcp-mux").is_ok());
     assert!(parse_attribute("rtcp-mux foobar").is_err());
 }
 
--- a/media/webrtc/signaling/src/sdp/rsdparsa/src/lib.rs
+++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/lib.rs
@@ -1,10 +1,12 @@
 #![cfg_attr(feature="clippy", feature(plugin))]
 
+#[macro_use]
+extern crate log;
 #[cfg(feature="serialize")]
 #[macro_use]
 extern crate serde_derive;
 #[cfg(feature="serialize")]
 extern crate serde;
 
 use std::net::IpAddr;
 use std::str::FromStr;
@@ -133,17 +135,17 @@ impl SdpSession {
     pub fn get_version(&self) -> u64 {
         self.version
     }
 
     pub fn get_origin(&self) -> &SdpOrigin {
         &self.origin
     }
 
-    pub fn get_session(&self) -> &String {
+    pub fn get_session(&self) -> &str {
         &self.session
     }
 
     pub fn get_connection(&self) -> &Option<SdpConnection> {
         &self.connection
     }
 
     pub fn set_connection(&mut self, c: &SdpConnection) {
@@ -165,32 +167,20 @@ impl SdpSession {
         };
         Ok(self.attribute.push(a.clone()))
     }
 
     pub fn extend_media(&mut self, v: Vec<SdpMedia>) {
         self.media.extend(v)
     }
 
-    pub fn has_timing(&self) -> bool {
-        self.timing.is_some()
-    }
-
-    pub fn has_attributes(&self) -> bool {
-        !self.attribute.is_empty()
-    }
-
     pub fn get_attribute(&self, t: SdpAttributeType) -> Option<&SdpAttribute> {
        self.attribute.iter().filter(|a| SdpAttributeType::from(*a) == t).next()
     }
 
-    pub fn has_media(&self) -> bool {
-        !self.media.is_empty()
-    }
-
     pub fn add_media(&mut self, media_type: SdpMediaValue, direction: SdpAttribute, port: u32,
                      protocol: SdpProtocolValue, addr: String)
                      -> Result<(),SdpParserInternalError> {
        let mut media = SdpMedia::new(SdpMediaLine {
            media: media_type,
            port,
            port_count: 1,
            proto: protocol,
@@ -207,33 +197,33 @@ impl SdpSession {
 
        self.media.push(media);
 
        Ok(())
     }
 }
 
 fn parse_session(value: &str) -> Result<SdpType, SdpParserInternalError> {
-    println!("session: {}", value);
+    trace!("session: {}", value);
     Ok(SdpType::Session(String::from(value)))
 }
 
 #[test]
 fn test_session_works() {
     assert!(parse_session("topic").is_ok());
 }
 
 
 fn parse_version(value: &str) -> Result<SdpType, SdpParserInternalError> {
     let ver = value.parse::<u64>()?;
     if ver != 0 {
         return Err(SdpParserInternalError::Generic(format!("version type contains unsupported value {}",
                                                            ver)));
     };
-    println!("version: {}", ver);
+    trace!("version: {}", ver);
     Ok(SdpType::Version(ver))
 }
 
 #[test]
 fn test_version_works() {
     assert!(parse_version("0").is_ok());
 }
 
@@ -295,17 +285,17 @@ fn parse_origin(value: &str) -> Result<S
                                                        .to_string()));
     }
     let o = SdpOrigin {
         username: String::from(username),
         session_id,
         session_version,
         unicast_addr,
     };
-    println!("{}", o);
+    trace!("origin: {}", o);
     Ok(SdpType::Origin(o))
 }
 
 #[test]
 fn test_origin_works() {
     assert!(parse_origin("mozilla 506705521068071134 0 IN IP4 0.0.0.0").is_ok());
     assert!(parse_origin("mozilla 506705521068071134 0 IN IP6 ::1").is_ok());
 }
@@ -357,17 +347,17 @@ fn parse_connection(value: &str) -> Resu
         addr_token = addr_tokens[0];
     }
     let addr = parse_unicast_addr(addr_token)?;
     if !addrtype.same_protocol(&addr) {
         return Err(SdpParserInternalError::Generic("connection addrtype does not match address."
                                                        .to_string()));
     }
     let c = SdpConnection { addr, ttl, amount };
-    println!("connection: {}", c.addr);
+    trace!("connection: {}", c.addr);
     Ok(SdpType::Connection(c))
 }
 
 #[test]
 fn connection_works() {
     assert!(parse_connection("IN IP4 127.0.0.1").is_ok());
     assert!(parse_connection("IN IP4 127.0.0.1/10/10").is_ok());
 }
@@ -412,17 +402,17 @@ fn parse_bandwidth(value: &str) -> Resul
     }
     let bandwidth = bv[1].parse::<u32>()?;
     let bw = match bv[0].to_uppercase().as_ref() {
         "AS" => SdpBandwidth::As(bandwidth),
         "CT" => SdpBandwidth::Ct(bandwidth),
         "TIAS" => SdpBandwidth::Tias(bandwidth),
         _ => SdpBandwidth::Unknown(String::from(bv[0]), bandwidth),
     };
-    println!("bandwidth: {}, {}", bv[0], bandwidth);
+    trace!("bandwidth: {}, {}", bv[0], bandwidth);
     Ok(SdpType::Bandwidth(bw))
 }
 
 #[test]
 fn bandwidth_works() {
     assert!(parse_bandwidth("AS:1").is_ok());
     assert!(parse_bandwidth("CT:123").is_ok());
     assert!(parse_bandwidth("TIAS:12345").is_ok());
@@ -443,17 +433,17 @@ fn parse_timing(value: &str) -> Result<S
     let tv: Vec<&str> = value.split_whitespace().collect();
     if tv.len() != 2 {
         return Err(SdpParserInternalError::Generic("timing attribute must have two tokens"
                                                        .to_string()));
     }
     let start = tv[0].parse::<u64>()?;
     let stop = tv[1].parse::<u64>()?;
     let t = SdpTiming { start, stop };
-    println!("timing: {}, {}", t.start, t.stop);
+    trace!("timing: {}, {}", t.start, t.stop);
     Ok(SdpType::Timing(t))
 }
 
 #[test]
 fn test_timing_works() {
     assert!(parse_timing("0 0").is_ok());
 }
 
@@ -621,33 +611,33 @@ fn test_parse_sdp_line_invalid_a_line() 
 }
 
 fn sanity_check_sdp_session(session: &SdpSession) -> Result<(), SdpParserError> {
     let make_error = |x: &str| SdpParserError::Sequence {
         message: x.to_string(),
         line_number: 0,
     };
 
-    if !session.has_timing() {
+    if !session.timing.is_some() {
         return Err(SdpParserError::Sequence {
                        message: "Missing timing type".to_string(),
                        line_number: 0,
                    });
     }
 
-    if !session.has_media() {
+    if session.media.is_empty() {
         return Err(SdpParserError::Sequence {
-                       message: "Missing media setion".to_string(),
+                       message: "Missing media section".to_string(),
                        line_number: 0,
                    });
     }
 
     if session.get_connection().is_none() {
         for msection in &session.media {
-            if !msection.has_connection() {
+            if msection.get_connection().is_none() {
                 return Err(SdpParserError::Sequence {
                     message: "Each media section must define a connection
                               if it is not defined on session level".to_string(),
                     line_number: 0,
                 });
             }
         }
     }
@@ -912,17 +902,17 @@ fn parse_sdp_vector(lines: &[SdpLine]) -
             SdpType::Email(_) |
             SdpType::Information(_) |
             SdpType::Key(_) |
             SdpType::Phone(_) |
             SdpType::Repeat(_) |
             SdpType::Uri(_) |
             SdpType::Zone(_) => (),
         };
-        if sdp_session.has_media() {
+        if !sdp_session.media.is_empty() {
             break;
         };
     }
     sanity_check_sdp_session(&sdp_session)?;
     Ok(sdp_session)
 }
 
 pub fn parse_sdp(sdp: &str, fail_on_warning: bool) -> Result<SdpSession, SdpParserError> {
@@ -1001,17 +991,17 @@ pub fn parse_sdp(sdp: &str, fail_on_warn
     if let Some(e) = errors.pop() {
         return Err(e);
     };
 
     let mut session = parse_sdp_vector(&sdp_lines)?;
     session.warnings = warnings;
 
     for warning in &session.warnings {
-        println!("Warning: {}", &warning);
+        warn!("Warning: {}", &warning);
     }
 
     Ok(session)
 }
 
 #[test]
 fn test_parse_sdp_zero_length_string_fails() {
     assert!(parse_sdp("", true).is_err());
--- a/media/webrtc/signaling/src/sdp/rsdparsa/src/media_type.rs
+++ b/media/webrtc/signaling/src/sdp/rsdparsa/src/media_type.rs
@@ -113,32 +113,24 @@ impl SdpMedia {
     pub fn get_proto(&self) -> &SdpProtocolValue {
         &self.media.proto
     }
 
     pub fn get_formats(&self) -> &SdpFormatList {
         &self.media.formats
     }
 
-    pub fn has_bandwidth(&self) -> bool {
-        !self.bandwidth.is_empty()
-    }
-
     pub fn get_bandwidth(&self) -> &Vec<SdpBandwidth> {
         &self.bandwidth
     }
 
     pub fn add_bandwidth(&mut self, bw: &SdpBandwidth) {
         self.bandwidth.push(bw.clone())
     }
 
-    pub fn has_attributes(&self) -> bool {
-        !self.attribute.is_empty()
-    }
-
     pub fn get_attributes(&self) -> &Vec<SdpAttribute> {
         &self.attribute
     }
 
     pub fn add_attribute(&mut self, attr: &SdpAttribute) -> Result<(), SdpParserInternalError> {
         if !attr.allowed_at_media_level() {
             return Err(SdpParserInternalError::Generic(format!("{} not allowed at media level",
                                                                attr)));
@@ -185,20 +177,16 @@ impl SdpMedia {
         self.add_attribute(&SdpAttribute::Rtpmap(rtpmap))?;
         Ok(())
     }
 
     pub fn get_attributes_of_type(&self, t: SdpAttributeType) -> Vec<&SdpAttribute> {
         self.attribute.iter().filter(|a| SdpAttributeType::from(*a) == t).collect()
     }
 
-    pub fn has_connection(&self) -> bool {
-        self.connection.is_some()
-    }
-
     pub fn get_connection(&self) -> &Option<SdpConnection> {
         &self.connection
     }
 
     pub fn set_connection(&mut self, c: &SdpConnection) -> Result<(), SdpParserInternalError> {
         if self.connection.is_some() {
             return Err(SdpParserInternalError::Generic("connection type already exists at this media level"
                                .to_string(),
@@ -367,17 +355,17 @@ pub fn parse_media(value: &str) -> Resul
     };
     let m = SdpMediaLine {
         media,
         port,
         port_count,
         proto,
         formats,
     };
-    println!("media: {}, {}, {}, {}", m.media, m.port, m.proto, m.formats);
+    trace!("media: {}, {}, {}, {}", m.media, m.port, m.proto, m.formats);
     Ok(SdpType::Media(m))
 }
 
 #[test]
 fn test_media_works() {
     assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 109").is_ok());
     assert!(parse_media("video 9 UDP/TLS/RTP/SAVPF 126").is_ok());
     assert!(parse_media("application 9 DTLS/SCTP 5000").is_ok());
@@ -494,9 +482,102 @@ pub fn parse_media_vector(lines: &[SdpLi
             SdpType::Key(_) => (),
         };
     }
 
     media_sections.push(sdp_media);
 
     Ok(media_sections)
 }
-// TODO add unit tests for parse_media_vector
+
+#[test]
+fn test_media_vector_first_line_failure() {
+    let mut sdp_lines: Vec<SdpLine> = Vec::new();
+    let line = SdpLine {
+        line_number: 0,
+        sdp_type: SdpType::Session("hello".to_string())
+    };
+    sdp_lines.push(line);
+    assert!(parse_media_vector(&sdp_lines).is_err());
+}
+
+#[test]
+fn test_media_vector_multiple_connections() {
+    let mut sdp_lines: Vec<SdpLine> = Vec::new();
+    let media_line = SdpMediaLine {
+        media: SdpMediaValue::Audio,
+        port: 9,
+        port_count: 0,
+        proto: SdpProtocolValue::RtpSavpf,
+        formats: SdpFormatList::Integers(Vec::new()),
+    };
+    let media = SdpLine {
+        line_number: 0,
+        sdp_type: SdpType::Media(media_line)
+    };
+    sdp_lines.push(media);
+    use network::{parse_unicast_addr};
+    let addr = parse_unicast_addr("127.0.0.1").unwrap();
+    let c = SdpConnection {
+        addr,
+        ttl: None,
+        amount: None };
+    let c1 = SdpLine {
+        line_number: 1,
+        sdp_type: SdpType::Connection(c.clone())
+    };
+    sdp_lines.push(c1);
+    let c2 = SdpLine {
+        line_number: 2,
+        sdp_type: SdpType::Connection(c)
+    };
+    sdp_lines.push(c2);
+    assert!(parse_media_vector(&sdp_lines).is_err());
+}
+
+#[test]
+fn test_media_vector_invalid_types() {
+    let mut sdp_lines: Vec<SdpLine> = Vec::new();
+    let media_line = SdpMediaLine {
+        media: SdpMediaValue::Audio,
+        port: 9,
+        port_count: 0,
+        proto: SdpProtocolValue::RtpSavpf,
+        formats: SdpFormatList::Integers(Vec::new()),
+    };
+    let media = SdpLine {
+        line_number: 0,
+        sdp_type: SdpType::Media(media_line)
+    };
+    sdp_lines.push(media);
+    use {SdpTiming};
+    let t = SdpTiming { start: 0, stop: 0 };
+    let tline = SdpLine {
+        line_number: 1,
+        sdp_type: SdpType::Timing(t)
+    };
+    sdp_lines.push(tline);
+    assert!(parse_media_vector(&sdp_lines).is_err());
+}
+
+#[test]
+fn test_media_vector_invalid_media_level_attribute() {
+    let mut sdp_lines: Vec<SdpLine> = Vec::new();
+    let media_line = SdpMediaLine {
+        media: SdpMediaValue::Audio,
+        port: 9,
+        port_count: 0,
+        proto: SdpProtocolValue::RtpSavpf,
+        formats: SdpFormatList::Integers(Vec::new()),
+    };
+    let media = SdpLine {
+        line_number: 0,
+        sdp_type: SdpType::Media(media_line)
+    };
+    sdp_lines.push(media);
+    let a = SdpAttribute::IceLite;
+    let aline = SdpLine {
+        line_number: 1,
+        sdp_type: SdpType::Attribute(a)
+    };
+    sdp_lines.push(aline);
+    assert!(parse_media_vector(&sdp_lines).is_err());
+}
--- a/media/webrtc/signaling/src/sdp/rsdparsa/tests/unit_tests.rs
+++ b/media/webrtc/signaling/src/sdp/rsdparsa/tests/unit_tests.rs
@@ -20,19 +20,18 @@ m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n";
     assert_eq!(sdp.media.len(), 1);
 
     let msection = &(sdp.media[0]);
     assert_eq!(*msection.get_type(),
                rsdparsa::media_type::SdpMediaValue::Audio);
     assert_eq!(msection.get_port(), 0);
     assert_eq!(*msection.get_proto(),
                rsdparsa::media_type::SdpProtocolValue::UdpTlsRtpSavpf);
-    assert!(!msection.has_attributes());
-    assert!(!msection.has_bandwidth());
-    assert!(!msection.has_connection());
+    assert!(msection.get_attributes().is_empty());
+    assert!(msection.get_bandwidth().is_empty());
     assert!(msection.get_connection().is_none());
 }
 
 #[test]
 fn parse_minimal_sdp_with_emtpy_lines() {
     let sdp = "v=0\r\n
 \r\n
 o=- 0 0 IN IP4 0.0.0.0\r\n
@@ -68,16 +67,50 @@ m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n";
     assert!(sdp_opt.is_some());
     let sdp = sdp_opt.unwrap();
     assert_eq!(sdp.version, 0);
     assert_eq!(sdp.session, "-");
     assert!(sdp.get_connection().is_some());
 }
 
 #[test]
+fn parse_minimal_sdp_with_most_media_types() {
+    let sdp = "v=0\r\n
+o=- 0 0 IN IP4 0.0.0.0\r\n
+s=-\r\n
+t=0 0\r\n
+m=video 0 UDP/TLS/RTP/SAVPF 0\r\n
+b=AS:1\r\n
+b=CT:123\r\n
+b=TIAS:12345\r\n
+c=IN IP4 0.0.0.0\r\n
+a=sendrecv\r\n";
+    let sdp_res = rsdparsa::parse_sdp(sdp, false);
+    assert!(sdp_res.is_ok());
+    let sdp_opt = sdp_res.ok();
+    assert!(sdp_opt.is_some());
+    let sdp = sdp_opt.unwrap();
+    assert_eq!(sdp.version, 0);
+    assert_eq!(sdp.session, "-");
+    assert_eq!(sdp.attribute.len(), 0);
+    assert_eq!(sdp.media.len(), 1);
+
+    let msection = &(sdp.media[0]);
+    assert_eq!(*msection.get_type(),
+               rsdparsa::media_type::SdpMediaValue::Video);
+    assert_eq!(msection.get_port(), 0);
+    assert_eq!(*msection.get_proto(),
+               rsdparsa::media_type::SdpProtocolValue::UdpTlsRtpSavpf);
+    assert!(!msection.get_bandwidth().is_empty());
+    assert!(!msection.get_connection().is_none());
+    assert!(!msection.get_attributes().is_empty());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Sendrecv).is_some());
+}
+
+#[test]
 fn parse_firefox_audio_offer() {
     let sdp = "v=0\r\n
 o=mozilla...THIS_IS_SDPARTA-52.0a1 506705521068071134 0 IN IP4 0.0.0.0\r\n
 s=-\r\n
 t=0 0\r\n
 a=fingerprint:sha-256 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:BF:2F:E3:91:CB:57:A9:9D:4A:A2:0B:40\r\n
 a=group:BUNDLE sdparta_0\r\n
 a=ice-options:trickle\r\n
@@ -107,20 +140,31 @@ a=ssrc:2655508255 cname:{735484ea-4f6c-f
     assert_eq!(sdp.media.len(), 1);
 
     let msection = &(sdp.media[0]);
     assert_eq!(*msection.get_type(),
                rsdparsa::media_type::SdpMediaValue::Audio);
     assert_eq!(msection.get_port(), 9);
     assert_eq!(*msection.get_proto(),
                rsdparsa::media_type::SdpProtocolValue::UdpTlsRtpSavpf);
-    assert!(msection.has_attributes());
-    assert!(msection.has_connection());
     assert!(msection.get_connection().is_some());
-    assert!(!msection.has_bandwidth());
+    assert!(msection.get_bandwidth().is_empty());
+    assert!(!msection.get_attributes().is_empty());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Sendrecv).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Extmap).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Fmtp).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::IcePwd).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::IceUfrag).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Mid).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Mid).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Msid).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::RtcpMux).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Rtpmap).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Setup).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Ssrc).is_some());
 }
 
 #[test]
 fn parse_firefox_video_offer() {
     let sdp = "v=0\r\n
 o=mozilla...THIS_IS_SDPARTA-52.0a1 506705521068071134 0 IN IP4 0.0.0.0\r\n
 s=-\r\n
 t=0 0\r\n
@@ -164,16 +208,32 @@ a=ssrc:2709871439 cname:{735484ea-4f6c-f
     assert_eq!(sdp.media.len(), 1);
 
     let msection = &(sdp.media[0]);
     assert_eq!(*msection.get_type(),
                rsdparsa::media_type::SdpMediaValue::Video);
     assert_eq!(msection.get_port(), 9);
     assert_eq!(*msection.get_proto(),
                rsdparsa::media_type::SdpProtocolValue::UdpTlsRtpSavpf);
+    assert!(msection.get_connection().is_some());
+    assert!(msection.get_bandwidth().is_empty());
+    assert!(!msection.get_attributes().is_empty());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Recvonly).is_some());
+    assert!(!msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Extmap).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Fmtp).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::IcePwd).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::IceUfrag).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Mid).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Mid).is_some());
+    assert!(!msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Msid).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Rtcpfb).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::RtcpMux).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Rtpmap).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Setup).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Ssrc).is_some());
 }
 
 #[test]
 fn parse_firefox_datachannel_offer() {
     let sdp = "v=0\r\n
 o=mozilla...THIS_IS_SDPARTA-52.0a2 3327975756663609975 0 IN IP4 0.0.0.0\r\n
 s=-\r\n
 t=0 0\r\n
@@ -201,16 +261,32 @@ a=ssrc:3376683177 cname:{62f78ee0-620f-a
     assert_eq!(sdp.media.len(), 1);
 
     let msection = &(sdp.media[0]);
     assert_eq!(*msection.get_type(),
                rsdparsa::media_type::SdpMediaValue::Application);
     assert_eq!(msection.get_port(), 49760);
     assert_eq!(*msection.get_proto(),
                rsdparsa::media_type::SdpProtocolValue::DtlsSctp);
+    assert!(msection.get_connection().is_some());
+    assert!(msection.get_bandwidth().is_empty());
+    assert!(!msection.get_attributes().is_empty());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Sendrecv).is_some());
+    assert!(!msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Extmap).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::IcePwd).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::IceUfrag).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::EndOfCandidates).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Mid).is_some());
+    assert!(!msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Msid).is_some());
+    assert!(!msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Rtcpfb).is_some());
+    assert!(!msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::RtcpMux).is_some());
+    assert!(!msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Rtpmap).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Sctpmap).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Setup).is_some());
+    assert!(msection.get_attribute(rsdparsa::attribute_type::SdpAttributeType::Ssrc).is_some());
 }
 
 #[test]
 fn parse_chrome_audio_video_offer() {
     let sdp = "v=0\r\n
 o=- 3836772544440436510 2 IN IP4 127.0.0.1\r\n
 s=-\r\n
 t=0 0\r\n
@@ -306,29 +382,29 @@ a=ssrc:2673335628 label:b6ec5178-c611-40
     assert_eq!(sdp.media.len(), 2);
 
     let msection1 = &(sdp.media[0]);
     assert_eq!(*msection1.get_type(),
                rsdparsa::media_type::SdpMediaValue::Audio);
     assert_eq!(msection1.get_port(), 9);
     assert_eq!(*msection1.get_proto(),
                rsdparsa::media_type::SdpProtocolValue::UdpTlsRtpSavpf);
-    assert!(msection1.has_attributes());
-    assert!(msection1.has_connection());
-    assert!(!msection1.has_bandwidth());
+    assert!(!msection1.get_attributes().is_empty());
+    assert!(msection1.get_connection().is_some());
+    assert!(msection1.get_bandwidth().is_empty());
 
     let msection2 = &(sdp.media[1]);
     assert_eq!(*msection2.get_type(),
                rsdparsa::media_type::SdpMediaValue::Video);
     assert_eq!(msection2.get_port(), 9);
     assert_eq!(*msection2.get_proto(),
                rsdparsa::media_type::SdpProtocolValue::UdpTlsRtpSavpf);
-    assert!(msection2.has_attributes());
-    assert!(msection2.has_connection());
-    assert!(!msection2.has_bandwidth());
+    assert!(!msection2.get_attributes().is_empty());
+    assert!(msection2.get_connection().is_some());
+    assert!(msection2.get_bandwidth().is_empty());
 }
 
 #[test]
 fn parse_firefox_simulcast_offer() {
     let sdp = "v=0\r\n
 o=mozilla...THIS_IS_SDPARTA-55.0a1 983028567300715536 0 IN IP4 0.0.0.0\r\n
 s=-\r\n
 t=0 0\r\n
--- a/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/lib.rs
+++ b/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/lib.rs
@@ -104,17 +104,17 @@ pub unsafe extern "C" fn get_version(ses
 
 #[no_mangle]
 pub unsafe extern "C" fn sdp_get_origin(session: *const SdpSession) ->  RustSdpOrigin {
     origin_view_helper((*session).get_origin())
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn session_view(session: *const SdpSession) -> StringView {
-    StringView::from((*session).get_session().as_str())
+    StringView::from((*session).get_session())
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn sdp_session_has_connection(session: *const SdpSession) -> bool {
     (*session).connection.is_some()
 }
 
 #[no_mangle]
--- a/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/media_section.rs
+++ b/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/media_section.rs
@@ -129,17 +129,17 @@ pub unsafe extern "C" fn sdp_get_media_b
 
 #[no_mangle]
 pub unsafe extern "C" fn sdp_get_media_bandwidth_vec(sdp_media: *const SdpMedia) -> *const Vec<SdpBandwidth> {
     (*sdp_media).get_bandwidth()
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn sdp_media_has_connection(sdp_media: *const SdpMedia) -> bool {
-    (*sdp_media).has_connection()
+    (*sdp_media).get_connection().is_some()
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn sdp_get_media_connection(sdp_media: *const SdpMedia, ret: *mut RustSdpConnection) -> nsresult {
     if let &Some(ref connection) = (*sdp_media).get_connection() {
         *ret = RustSdpConnection::from(connection);
         return NS_OK;
     }