1 // Copyright © 2020 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 use std::fmt; 7 use std::io::{Read, Write}; 8 use std::os::unix::io::RawFd; 9 use vmm_sys_util::sock_ctrl_msg::ScmSocket; 10 11 #[derive(Debug)] 12 pub enum Error { 13 Socket(std::io::Error), 14 SocketSendFds(vmm_sys_util::errno::Error), 15 StatusCodeParsing(std::num::ParseIntError), 16 MissingProtocol, 17 ContentLengthParsing(std::num::ParseIntError), 18 ServerResponse(StatusCode, Option<String>), 19 } 20 21 impl fmt::Display for Error { 22 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 23 use Error::*; 24 match self { 25 Socket(e) => write!(f, "Error writing to or reading from HTTP socket: {}", e), 26 SocketSendFds(e) => write!(f, "Error writing to or reading from HTTP socket: {}", e), 27 StatusCodeParsing(e) => write!(f, "Error parsing HTTP status code: {}", e), 28 MissingProtocol => write!(f, "HTTP output is missing protocol statement"), 29 ContentLengthParsing(e) => write!(f, "Error parsing HTTP Content-Length field: {}", e), 30 ServerResponse(s, o) => { 31 if let Some(o) = o { 32 write!(f, "Server responded with an error: {:?}: {}", s, o) 33 } else { 34 write!(f, "Server responded with an error: {:?}", s) 35 } 36 } 37 } 38 } 39 } 40 41 #[derive(Clone, Copy, Debug)] 42 pub enum StatusCode { 43 Continue, 44 Ok, 45 NoContent, 46 BadRequest, 47 NotFound, 48 InternalServerError, 49 NotImplemented, 50 Unknown, 51 } 52 53 impl StatusCode { 54 fn from_raw(code: usize) -> StatusCode { 55 match code { 56 100 => StatusCode::Continue, 57 200 => StatusCode::Ok, 58 204 => StatusCode::NoContent, 59 400 => StatusCode::BadRequest, 60 404 => StatusCode::NotFound, 61 500 => StatusCode::InternalServerError, 62 501 => StatusCode::NotImplemented, 63 _ => StatusCode::Unknown, 64 } 65 } 66 67 fn parse(code: &str) -> Result<StatusCode, Error> { 68 Ok(StatusCode::from_raw( 69 code.trim().parse().map_err(Error::StatusCodeParsing)?, 70 )) 71 } 72 73 fn is_server_error(self) -> bool { 74 !matches!( 75 self, 76 StatusCode::Ok | StatusCode::Continue | StatusCode::NoContent 77 ) 78 } 79 } 80 81 fn get_header<'a>(res: &'a str, header: &'a str) -> Option<&'a str> { 82 let header_str = format!("{}: ", header); 83 res.find(&header_str) 84 .map(|o| &res[o + header_str.len()..o + res[o..].find('\r').unwrap()]) 85 } 86 87 fn get_status_code(res: &str) -> Result<StatusCode, Error> { 88 if let Some(o) = res.find("HTTP/1.1") { 89 Ok(StatusCode::parse( 90 &res[o + "HTTP/1.1 ".len()..res[o..].find('\r').unwrap()], 91 )?) 92 } else { 93 Err(Error::MissingProtocol) 94 } 95 } 96 97 fn parse_http_response(socket: &mut dyn Read) -> Result<Option<String>, Error> { 98 let mut res = String::new(); 99 let mut body_offset = None; 100 let mut content_length: Option<usize> = None; 101 loop { 102 let mut bytes = vec![0; 256]; 103 let count = socket.read(&mut bytes).map_err(Error::Socket)?; 104 // If the return value is 0, the peer has performed an orderly shutdown. 105 if count == 0 { 106 break; 107 } 108 res.push_str(std::str::from_utf8(&bytes[0..count]).unwrap()); 109 110 // End of headers 111 if let Some(o) = res.find("\r\n\r\n") { 112 body_offset = Some(o + "\r\n\r\n".len()); 113 114 // With all headers available we can see if there is any body 115 content_length = if let Some(length) = get_header(&res, "Content-Length") { 116 Some(length.trim().parse().map_err(Error::ContentLengthParsing)?) 117 } else { 118 None 119 }; 120 121 if content_length.is_none() { 122 break; 123 } 124 } 125 126 if let Some(body_offset) = body_offset { 127 if let Some(content_length) = content_length { 128 if res.len() >= content_length + body_offset { 129 break; 130 } 131 } 132 } 133 } 134 let body_string = content_length.and(body_offset.map(|o| String::from(&res[o..]))); 135 let status_code = get_status_code(&res)?; 136 137 if status_code.is_server_error() { 138 Err(Error::ServerResponse(status_code, body_string)) 139 } else { 140 Ok(body_string) 141 } 142 } 143 144 /// Make an API request using the fully qualified command name. 145 /// For example, full_command could be "vm.create" or "vmm.ping". 146 pub fn simple_api_full_command_with_fds_and_response<T: Read + Write + ScmSocket>( 147 socket: &mut T, 148 method: &str, 149 full_command: &str, 150 request_body: Option<&str>, 151 request_fds: Vec<RawFd>, 152 ) -> Result<Option<String>, Error> { 153 socket 154 .send_with_fds( 155 &[format!( 156 "{} /api/v1/{} HTTP/1.1\r\nHost: localhost\r\nAccept: */*\r\n", 157 method, full_command 158 ) 159 .as_bytes()], 160 &request_fds, 161 ) 162 .map_err(Error::SocketSendFds)?; 163 164 if let Some(request_body) = request_body { 165 socket 166 .write_all(format!("Content-Length: {}\r\n", request_body.len()).as_bytes()) 167 .map_err(Error::Socket)?; 168 } 169 170 socket.write_all(b"\r\n").map_err(Error::Socket)?; 171 172 if let Some(request_body) = request_body { 173 socket 174 .write_all(request_body.as_bytes()) 175 .map_err(Error::Socket)?; 176 } 177 178 socket.flush().map_err(Error::Socket)?; 179 180 parse_http_response(socket) 181 } 182 183 pub fn simple_api_full_command_with_fds<T: Read + Write + ScmSocket>( 184 socket: &mut T, 185 method: &str, 186 full_command: &str, 187 request_body: Option<&str>, 188 request_fds: Vec<RawFd>, 189 ) -> Result<(), Error> { 190 let response = simple_api_full_command_with_fds_and_response( 191 socket, 192 method, 193 full_command, 194 request_body, 195 request_fds, 196 )?; 197 198 if response.is_some() { 199 println!("{}", response.unwrap()); 200 } 201 202 Ok(()) 203 } 204 205 pub fn simple_api_full_command<T: Read + Write + ScmSocket>( 206 socket: &mut T, 207 method: &str, 208 full_command: &str, 209 request_body: Option<&str>, 210 ) -> Result<(), Error> { 211 simple_api_full_command_with_fds(socket, method, full_command, request_body, Vec::new()) 212 } 213 214 pub fn simple_api_full_command_and_response<T: Read + Write + ScmSocket>( 215 socket: &mut T, 216 method: &str, 217 full_command: &str, 218 request_body: Option<&str>, 219 ) -> Result<Option<String>, Error> { 220 simple_api_full_command_with_fds_and_response( 221 socket, 222 method, 223 full_command, 224 request_body, 225 Vec::new(), 226 ) 227 } 228 229 pub fn simple_api_command_with_fds<T: Read + Write + ScmSocket>( 230 socket: &mut T, 231 method: &str, 232 c: &str, 233 request_body: Option<&str>, 234 request_fds: Vec<RawFd>, 235 ) -> Result<(), Error> { 236 // Create the full VM command. For VMM commands, use 237 // simple_api_full_command(). 238 let full_command = format!("vm.{}", c); 239 240 simple_api_full_command_with_fds(socket, method, &full_command, request_body, request_fds) 241 } 242 243 pub fn simple_api_command<T: Read + Write + ScmSocket>( 244 socket: &mut T, 245 method: &str, 246 c: &str, 247 request_body: Option<&str>, 248 ) -> Result<(), Error> { 249 simple_api_command_with_fds(socket, method, c, request_body, Vec::new()) 250 } 251