/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ extern crate bhttp; extern crate nserror; extern crate nsstring; extern crate thin_vec; #[macro_use] extern crate xpcom; use bhttp::{Message, Mode}; use nserror::{nsresult, NS_ERROR_FAILURE, NS_ERROR_INVALID_ARG, NS_ERROR_UNEXPECTED, NS_OK}; use nsstring::{nsACString, nsCString}; use thin_vec::ThinVec; use xpcom::interfaces::{nsIBinaryHttpRequest, nsIBinaryHttpResponse}; use xpcom::RefPtr; enum HeaderComponent { Name, Value, } // Extracts either the names or the values of a slice of header (name, value) pairs. fn extract_header_components( headers: &[(Vec, Vec)], component: HeaderComponent, ) -> ThinVec { let mut header_components = ThinVec::with_capacity(headers.len()); for (name, value) in headers { match component { HeaderComponent::Name => header_components.push(nsCString::from(name.clone())), HeaderComponent::Value => header_components.push(nsCString::from(value.clone())), } } header_components } #[xpcom(implement(nsIBinaryHttpRequest), atomic)] struct BinaryHttpRequest { method: Vec, scheme: Vec, authority: Vec, path: Vec, headers: Vec<(Vec, Vec)>, content: Vec, } impl BinaryHttpRequest { xpcom_method!(get_method => GetMethod() -> nsACString); fn get_method(&self) -> Result { Ok(nsCString::from(self.method.clone())) } xpcom_method!(get_scheme => GetScheme() -> nsACString); fn get_scheme(&self) -> Result { Ok(nsCString::from(self.scheme.clone())) } xpcom_method!(get_authority => GetAuthority() -> nsACString); fn get_authority(&self) -> Result { Ok(nsCString::from(self.authority.clone())) } xpcom_method!(get_path => GetPath() -> nsACString); fn get_path(&self) -> Result { Ok(nsCString::from(self.path.clone())) } xpcom_method!(get_content => GetContent() -> ThinVec); fn get_content(&self) -> Result, nsresult> { Ok(self.content.clone().into_iter().collect()) } xpcom_method!(get_header_names => GetHeaderNames() -> ThinVec); fn get_header_names(&self) -> Result, nsresult> { Ok(extract_header_components( &self.headers, HeaderComponent::Name, )) } xpcom_method!(get_header_values => GetHeaderValues() -> ThinVec); fn get_header_values(&self) -> Result, nsresult> { Ok(extract_header_components( &self.headers, HeaderComponent::Value, )) } } #[xpcom(implement(nsIBinaryHttpResponse), atomic)] struct BinaryHttpResponse { status: u16, headers: Vec<(Vec, Vec)>, content: Vec, } impl BinaryHttpResponse { xpcom_method!(get_status => GetStatus() -> u16); fn get_status(&self) -> Result { Ok(self.status) } xpcom_method!(get_content => GetContent() -> ThinVec); fn get_content(&self) -> Result, nsresult> { Ok(self.content.clone().into_iter().collect()) } xpcom_method!(get_header_names => GetHeaderNames() -> ThinVec); fn get_header_names(&self) -> Result, nsresult> { Ok(extract_header_components( &self.headers, HeaderComponent::Name, )) } xpcom_method!(get_header_values => GetHeaderValues() -> ThinVec); fn get_header_values(&self) -> Result, nsresult> { Ok(extract_header_components( &self.headers, HeaderComponent::Value, )) } } #[xpcom(implement(nsIBinaryHttp), atomic)] struct BinaryHttp {} impl BinaryHttp { xpcom_method!(encode_request => EncodeRequest(request: *const nsIBinaryHttpRequest) -> ThinVec); fn encode_request(&self, request: &nsIBinaryHttpRequest) -> Result, nsresult> { let mut method = nsCString::new(); unsafe { request.GetMethod(&mut *method) }.to_result()?; let mut scheme = nsCString::new(); unsafe { request.GetScheme(&mut *scheme) }.to_result()?; let mut authority = nsCString::new(); unsafe { request.GetAuthority(&mut *authority) }.to_result()?; let mut path = nsCString::new(); unsafe { request.GetPath(&mut *path) }.to_result()?; let mut message = Message::request( method.to_vec(), scheme.to_vec(), authority.to_vec(), path.to_vec(), ); let mut header_names = ThinVec::new(); unsafe { request.GetHeaderNames(&mut header_names) }.to_result()?; let mut header_values = ThinVec::with_capacity(header_names.len()); unsafe { request.GetHeaderValues(&mut header_values) }.to_result()?; if header_names.len() != header_values.len() { return Err(NS_ERROR_INVALID_ARG); } for (name, value) in header_names.iter().zip(header_values.iter()) { message.put_header(name.to_vec(), value.to_vec()); } let mut content = ThinVec::new(); unsafe { request.GetContent(&mut content) }.to_result()?; message.write_content(content); let mut encoded = ThinVec::new(); message .write_bhttp(Mode::KnownLength, &mut encoded) .map_err(|_| NS_ERROR_FAILURE)?; Ok(encoded) } xpcom_method!(decode_response => DecodeResponse(response: *const ThinVec) -> *const nsIBinaryHttpResponse); fn decode_response( &self, response: &ThinVec, ) -> Result, nsresult> { let decoded = Message::read_bhttp(&mut response.as_slice()).map_err(|_| NS_ERROR_UNEXPECTED)?; let status = decoded.control().status().ok_or(NS_ERROR_UNEXPECTED)?; let headers = decoded .header() .iter() .map(|field| (field.name().to_vec(), field.value().to_vec())) .collect(); let content = decoded.content().to_vec(); let binary_http_response = BinaryHttpResponse::allocate(InitBinaryHttpResponse { status, headers, content, }); binary_http_response .query_interface::() .ok_or(NS_ERROR_FAILURE) } xpcom_method!(decode_request => DecodeRequest(request: *const ThinVec) -> *const nsIBinaryHttpRequest); fn decode_request( &self, request: &ThinVec, ) -> Result, nsresult> { let decoded = Message::read_bhttp(&mut request.as_slice()).map_err(|_| NS_ERROR_UNEXPECTED)?; let method = decoded .control() .method() .ok_or(NS_ERROR_UNEXPECTED)? .to_vec(); let scheme = decoded .control() .scheme() .ok_or(NS_ERROR_UNEXPECTED)? .to_vec(); // authority and path can be empty, in which case we return empty arrays let authority = decoded.control().authority().unwrap_or(&[]).to_vec(); let path = decoded.control().path().unwrap_or(&[]).to_vec(); let headers = decoded .header() .iter() .map(|field| (field.name().to_vec(), field.value().to_vec())) .collect(); let content = decoded.content().to_vec(); let binary_http_request = BinaryHttpRequest::allocate(InitBinaryHttpRequest { method, scheme, authority, path, headers, content, }); binary_http_request .query_interface::() .ok_or(NS_ERROR_FAILURE) } xpcom_method!(encode_response => EncodeResponse(response: *const nsIBinaryHttpResponse) -> ThinVec); fn encode_response(&self, response: &nsIBinaryHttpResponse) -> Result, nsresult> { let mut status = 0; unsafe { response.GetStatus(&mut status) }.to_result()?; let mut message = Message::response(status); let mut header_names = ThinVec::new(); unsafe { response.GetHeaderNames(&mut header_names) }.to_result()?; let mut header_values = ThinVec::with_capacity(header_names.len()); unsafe { response.GetHeaderValues(&mut header_values) }.to_result()?; if header_names.len() != header_values.len() { return Err(NS_ERROR_INVALID_ARG); } for (name, value) in header_values.iter().zip(header_names.iter()) { message.put_header(name.to_vec(), value.to_vec()); } let mut content = ThinVec::new(); unsafe { response.GetContent(&mut content) }.to_result()?; message.write_content(content); let mut encoded = ThinVec::new(); message .write_bhttp(Mode::KnownLength, &mut encoded) .map_err(|_| NS_ERROR_FAILURE)?; Ok(encoded) } } #[no_mangle] pub extern "C" fn binary_http_constructor( iid: *const xpcom::nsIID, result: *mut *mut xpcom::reexports::libc::c_void, ) -> nsresult { let binary_http = BinaryHttp::allocate(InitBinaryHttp {}); unsafe { binary_http.QueryInterface(iid, result) } }