pacenotes_installer\http/
request_handle.rs1const NUL_TERMINATED: u32 = u32::MAX;
3
4struct Handle(*mut core::ffi::c_void);
6
7impl Drop for Handle {
8 fn drop(&mut self) {
9 if !self.0.is_null() {
10 unsafe { windows_sys::Win32::Networking::WinHttp::WinHttpCloseHandle(self.0) };
11 }
12 }
13}
14
15impl Handle {
16 fn checked(raw: *mut core::ffi::c_void, call: &str) -> Result<Handle, super::HttpError> {
17 if raw.is_null() {
18 Err(super::HttpError::last(call))
19 } else {
20 Ok(Handle(raw))
21 }
22 }
23}
24
25pub struct OpenRequest {
28 request: Handle,
29 _connection: Handle,
30 _session: Handle,
31}
32
33impl OpenRequest {
34 pub fn send(
36 target: &super::url_target::UrlTarget,
37 method: &str,
38 bearer: Option<&str>,
39 body: Option<&[u8]>,
40 ) -> Result<OpenRequest, super::HttpError> {
41 let user_agent = crate::i18n::to_wide(super::USER_AGENT);
42 let session = Handle::checked(
43 unsafe {
44 windows_sys::Win32::Networking::WinHttp::WinHttpOpen(
45 user_agent.as_ptr(),
46 windows_sys::Win32::Networking::WinHttp::WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,
47 core::ptr::null(),
48 core::ptr::null(),
49 0,
50 )
51 },
52 "WinHttpOpen",
53 )?;
54 unsafe {
55 windows_sys::Win32::Networking::WinHttp::WinHttpSetTimeouts(
56 session.0,
57 super::TIMEOUT_RESOLVE_MS,
58 super::TIMEOUT_CONNECT_MS,
59 super::TIMEOUT_SEND_MS,
60 super::TIMEOUT_RECEIVE_MS,
61 )
62 };
63
64 let host = crate::i18n::to_wide(&target.host);
65 let connection = Handle::checked(
66 unsafe {
67 windows_sys::Win32::Networking::WinHttp::WinHttpConnect(
68 session.0,
69 host.as_ptr(),
70 target.port,
71 0,
72 )
73 },
74 "WinHttpConnect",
75 )?;
76
77 let verb = crate::i18n::to_wide(method);
78 let path = crate::i18n::to_wide(&target.path);
79 let flags = if target.secure {
80 windows_sys::Win32::Networking::WinHttp::WINHTTP_FLAG_SECURE
81 } else {
82 0
83 };
84 let request = Handle::checked(
85 unsafe {
86 windows_sys::Win32::Networking::WinHttp::WinHttpOpenRequest(
87 connection.0,
88 verb.as_ptr(),
89 path.as_ptr(),
90 core::ptr::null(),
91 core::ptr::null(),
92 core::ptr::null(),
93 flags,
94 )
95 },
96 "WinHttpOpenRequest",
97 )?;
98 refuse_redirects(&request)?;
99
100 if let Some(token) = bearer {
101 let line = format!(
102 "{}: {}{token}",
103 pacenotes_shared::AUTH_HEADER,
104 pacenotes_shared::BEARER_PREFIX
105 );
106 add_header(&request, &line)?;
107 }
108 if body.is_some() {
109 add_header(&request, "Content-Type: application/json")?;
110 }
111 let payload = body.unwrap_or(&[]);
112 let payload_pointer = if body.is_some() {
113 payload.as_ptr() as *const core::ffi::c_void
114 } else {
115 core::ptr::null()
116 };
117 let sent = unsafe {
118 windows_sys::Win32::Networking::WinHttp::WinHttpSendRequest(
119 request.0,
120 core::ptr::null(),
121 0,
122 payload_pointer,
123 payload.len() as u32,
124 payload.len() as u32,
125 0,
126 )
127 };
128 if sent == 0 {
129 return Err(super::HttpError::last("WinHttpSendRequest"));
130 }
131 let received = unsafe {
132 windows_sys::Win32::Networking::WinHttp::WinHttpReceiveResponse(
133 request.0,
134 core::ptr::null_mut(),
135 )
136 };
137 if received == 0 {
138 return Err(super::HttpError::last("WinHttpReceiveResponse"));
139 }
140
141 Ok(OpenRequest {
142 request,
143 _connection: connection,
144 _session: session,
145 })
146 }
147
148 pub fn query_number(&self, info: u32) -> Result<u32, super::HttpError> {
150 let mut value: u32 = 0;
151 let mut length = core::mem::size_of::<u32>() as u32;
152 let found = unsafe {
153 windows_sys::Win32::Networking::WinHttp::WinHttpQueryHeaders(
154 self.request.0,
155 info | windows_sys::Win32::Networking::WinHttp::WINHTTP_QUERY_FLAG_NUMBER,
156 core::ptr::null(),
157 &mut value as *mut u32 as *mut core::ffi::c_void,
158 &mut length,
159 core::ptr::null_mut(),
160 )
161 };
162 if found == 0 {
163 return Err(super::HttpError::last("WinHttpQueryHeaders"));
164 }
165 Ok(value)
166 }
167
168 pub fn query_text(&self, info: u32) -> Option<String> {
171 let mut length: u32 = 0;
172 unsafe {
173 windows_sys::Win32::Networking::WinHttp::WinHttpQueryHeaders(
174 self.request.0,
175 info,
176 core::ptr::null(),
177 core::ptr::null_mut(),
178 &mut length,
179 core::ptr::null_mut(),
180 )
181 };
182 if length < 2 {
184 return None;
185 }
186 let mut buffer: Vec<u16> = vec![0; length as usize / 2 + 1];
187 let found = unsafe {
188 windows_sys::Win32::Networking::WinHttp::WinHttpQueryHeaders(
189 self.request.0,
190 info,
191 core::ptr::null(),
192 buffer.as_mut_ptr() as *mut core::ffi::c_void,
193 &mut length,
194 core::ptr::null_mut(),
195 )
196 };
197 if found == 0 {
198 return None;
199 }
200 let characters = (length as usize / 2).min(buffer.len());
201 Some(String::from_utf16_lossy(&buffer[..characters]))
202 }
203
204 pub fn read(&self, buffer: &mut [u8]) -> Result<usize, super::HttpError> {
206 let mut read: u32 = 0;
207 let succeeded = unsafe {
208 windows_sys::Win32::Networking::WinHttp::WinHttpReadData(
209 self.request.0,
210 buffer.as_mut_ptr() as *mut core::ffi::c_void,
211 buffer.len() as u32,
212 &mut read,
213 )
214 };
215 if succeeded == 0 {
216 return Err(super::HttpError::last("WinHttpReadData"));
217 }
218 Ok(read as usize)
219 }
220}
221
222fn refuse_redirects(request: &Handle) -> Result<(), super::HttpError> {
226 let never = windows_sys::Win32::Networking::WinHttp::WINHTTP_OPTION_REDIRECT_POLICY_NEVER;
227 let set = unsafe {
228 windows_sys::Win32::Networking::WinHttp::WinHttpSetOption(
229 request.0,
230 windows_sys::Win32::Networking::WinHttp::WINHTTP_OPTION_REDIRECT_POLICY,
231 &never as *const u32 as *const core::ffi::c_void,
232 core::mem::size_of::<u32>() as u32,
233 )
234 };
235 if set == 0 {
236 return Err(super::HttpError::last("disabling automatic redirects"));
237 }
238 Ok(())
239}
240
241fn add_header(request: &Handle, line: &str) -> Result<(), super::HttpError> {
242 let header = crate::i18n::to_wide(line);
243 let added = unsafe {
244 windows_sys::Win32::Networking::WinHttp::WinHttpAddRequestHeaders(
245 request.0,
246 header.as_ptr(),
247 NUL_TERMINATED,
248 windows_sys::Win32::Networking::WinHttp::WINHTTP_ADDREQ_FLAG_ADD,
249 )
250 };
251 if added == 0 {
252 return Err(super::HttpError::last("WinHttpAddRequestHeaders"));
253 }
254 Ok(())
255}