(socket: &mut dyn Read)
| 90 | } |
| 91 | |
| 92 | fn parse_http_response(socket: &mut dyn Read) -> Result<Option<String>, Error> { |
| 93 | let mut res = String::new(); |
| 94 | let mut body_offset = None; |
| 95 | let mut content_length: Option<usize> = None; |
| 96 | loop { |
| 97 | let mut bytes = vec![0; 256]; |
| 98 | let count = socket.read(&mut bytes).map_err(Error::Socket)?; |
| 99 | // If the return value is 0, the peer has performed an orderly shutdown. |
| 100 | if count == 0 { |
| 101 | break; |
| 102 | } |
| 103 | res.push_str(std::str::from_utf8(&bytes[0..count]).unwrap()); |
| 104 | |
| 105 | // End of headers |
| 106 | if let Some(o) = res.find("\r\n\r\n") { |
| 107 | body_offset = Some(o + "\r\n\r\n".len()); |
| 108 | |
| 109 | // With all headers available we can see if there is any body |
| 110 | content_length = if let Some(length) = get_header(&res, "Content-Length") { |
| 111 | Some(length.trim().parse().map_err(Error::ContentLengthParsing)?) |
| 112 | } else { |
| 113 | None |
| 114 | }; |
| 115 | |
| 116 | if content_length.is_none() { |
| 117 | break; |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | if let Some(body_offset) = body_offset |
| 122 | && let Some(content_length) = content_length |
| 123 | && res.len() >= content_length + body_offset |
| 124 | { |
| 125 | break; |
| 126 | } |
| 127 | } |
| 128 | let body_string = content_length.and(body_offset.map(|o| String::from(&res[o..]))); |
| 129 | let status_code = get_status_code(&res)?; |
| 130 | |
| 131 | if status_code.is_server_error() { |
| 132 | Err(Error::ServerResponse(status_code, body_string)) |
| 133 | } else { |
| 134 | Ok(body_string) |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | /// Make an API request using the fully qualified command name. |
| 139 | /// For example, full_command could be "vm.create" or "vmm.ping". |
no test coverage detected