Skip to content

Commit a309938

Browse files
committed
Update to ureq 3.x
1 parent 88da963 commit a309938

File tree

3 files changed

+99
-50
lines changed

3 files changed

+99
-50
lines changed

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# UNRELEASED
22
- Add coordinate metadata creation and query functions
33
- Add method for Proj creation from existing Proj instances, optionally containing epochs
4+
- Update ureq to 3.x and adapt network functionality to its new API
45

56
## 0.29.0 - 2025-03-17
67

Cargo.toml

+4-3
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ rust-version = "1.82"
1717
[dependencies]
1818
proj-sys = { version = "0.25.0", path = "proj-sys" }
1919
geo-types = { version = "0.7.10", optional = true }
20-
libc = "0.2.119"
20+
libc = "0.2.172"
2121
num-traits = "0.2.14"
2222
thiserror = "2.0.0"
23-
ureq = { version = "2.0.0", optional = true }
23+
ureq = { version = "3.0.11", optional = true }
24+
http = { version = "1.3.0", optional = true }
2425

2526
[workspace]
2627
members = ["proj-sys"]
@@ -29,7 +30,7 @@ members = ["proj-sys"]
2930
default = ["geo-types"]
3031
bundled_proj = [ "proj-sys/bundled_proj" ]
3132
pkg_config = [ "proj-sys/pkg_config" ]
32-
network = ["ureq", "proj-sys/network"]
33+
network = ["ureq", "http", "proj-sys/network"]
3334

3435
[dev-dependencies]
3536
# approx version must match the one used in geo-types

src/network.rs

+94-47
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use std::io::Read;
2020
use std::ops::Range;
2121
use std::os::raw::c_ulonglong;
2222
use std::ptr::{self, NonNull};
23-
use ureq::{Agent, Request, Response};
23+
use ureq::Agent;
2424

2525
use crate::proj::{ProjError, _string};
2626
use libc::c_char;
@@ -75,34 +75,46 @@ fn get_wait_time_exp(retrycount: i32) -> u64 {
7575

7676
/// Process CDN response: handle retries in case of server error, or early return for client errors
7777
/// Successful retry data is stored into res
78-
fn error_handler(res: &mut Response, rb: Request) -> Result<&Response, ProjError> {
78+
fn error_handler<'a>(
79+
res: &'a mut http::Response<ureq::Body>,
80+
url: &str,
81+
headers: &[(&str, &str)],
82+
) -> Result<&'a mut http::Response<ureq::Body>, ProjError> {
7983
let mut retries = 0;
8084
// Check whether something went wrong on the server, or if it's an S3 retry code
81-
if SERVER_ERROR_CODES.contains(&res.status()) || RETRY_CODES.contains(&res.status()) {
85+
if SERVER_ERROR_CODES.contains(&res.status().as_u16())
86+
|| RETRY_CODES.contains(&res.status().as_u16())
87+
{
8288
// Start retrying: up to MAX_RETRIES
83-
while (SERVER_ERROR_CODES.contains(&res.status()) || RETRY_CODES.contains(&res.status()))
89+
while (SERVER_ERROR_CODES.contains(&res.status().as_u16())
90+
|| RETRY_CODES.contains(&res.status().as_u16()))
8491
&& retries <= MAX_RETRIES
8592
{
8693
retries += 1;
8794
let wait = time::Duration::from_millis(get_wait_time_exp(retries as i32));
8895
thread::sleep(wait);
89-
let retry = rb.clone();
90-
*res = retry.call()?;
96+
let agent = Agent::new_with_defaults();
97+
let mut req = agent.get(url);
98+
// Apply all headers
99+
for (name, value) in headers {
100+
req = req.header(*name, *value);
101+
}
102+
*res = req.call()?;
91103
}
92104
// Not a timeout or known S3 retry code: bail out
93-
} else if CLIENT_ERROR_CODES.contains(&res.status()) {
105+
} else if CLIENT_ERROR_CODES.contains(&res.status().as_u16()) {
94106
return Err(ProjError::DownloadError(
95-
res.status_text().to_string(),
96-
res.get_url().to_string(),
107+
res.status().to_string(),
108+
url.to_string(),
97109
retries,
98110
));
99111
}
100112
// Retries have been exhausted OR
101113
// The loop ended prematurely due to a different error
102-
if !SUCCESS_ERROR_CODES.contains(&res.status()) {
114+
if !SUCCESS_ERROR_CODES.contains(&res.status().as_u16()) {
103115
return Err(ProjError::DownloadError(
104-
res.status_text().to_string(),
105-
res.get_url().to_string(),
116+
res.status().to_string(),
117+
url.to_string(),
106118
retries,
107119
));
108120
}
@@ -148,7 +160,7 @@ pub(crate) unsafe extern "C" fn network_open(
148160
let err_string = e.to_string();
149161
out_error_string.copy_from_nonoverlapping(err_string.as_ptr().cast(), err_string.len());
150162
out_error_string.add(err_string.len()).write(0);
151-
ptr::null_mut() as *mut PROJ_NETWORK_HANDLE
163+
ptr::null_mut()
152164
}
153165
}
154166
}
@@ -172,44 +184,62 @@ unsafe fn _network_open(
172184
// RANGE header definition is "bytes=x-y"
173185
let hvalue = format!("bytes={offset}-{end}");
174186
// Create a new client that can be reused for subsequent queries
175-
let clt = Agent::new();
176-
let req = clt.get(&url);
177-
let with_headers = req.set("Range", &hvalue).set("Client", CLIENT);
178-
let in_case_of_error = with_headers.clone();
179-
let mut res = with_headers.call()?;
187+
let clt = Agent::new_with_defaults();
188+
let req = clt
189+
.get(&url)
190+
.header("Range", &hvalue)
191+
.header("Client", CLIENT);
192+
193+
let mut res = req.call()?;
194+
195+
// Define headers for potential retries
196+
let headers = [("Range", hvalue.as_str()), ("Client", CLIENT)];
197+
180198
// hand the response off to the error-handler, continue on success
181-
error_handler(&mut res, in_case_of_error)?;
199+
error_handler(&mut res, &url, &headers)?;
200+
182201
// Write the initial read length value into the pointer
183-
let Some(Ok(contentlength)) = res.header("Content-Length").map(str::parse::<usize>) else {
184-
return Err(ProjError::ContentLength);
185-
};
202+
let contentlength = res
203+
.headers()
204+
.get("Content-Length")
205+
.and_then(|val| val.to_str().ok())
206+
.and_then(|s| s.parse::<usize>().ok())
207+
.ok_or(ProjError::ContentLength)?;
208+
186209
let headers = res
187-
.headers_names()
188-
.into_iter()
210+
.headers()
211+
.keys()
189212
.filter_map(|h| {
190-
Some({
191-
let v = res.header(&h)?.to_string();
192-
(h, v)
193-
})
213+
let header_name = h.as_str().to_string();
214+
let header_value = res.headers().get(h)?.to_str().ok()?.to_string();
215+
Some((header_name, header_value))
194216
})
195217
.collect();
218+
196219
// Copy the downloaded bytes into the buffer so it can be passed around
197220
let capacity = contentlength.min(size_to_read);
198-
let mut buf = Vec::with_capacity(capacity);
199-
res.into_reader()
221+
let mut buf = Vec::<u8>::with_capacity(capacity);
222+
223+
// Read from body into our buffer
224+
let body_reader = res.body_mut().as_reader();
225+
body_reader
200226
.take(size_to_read as u64)
201227
.read_to_end(&mut buf)?;
228+
202229
out_size_read.write(buf.len());
203230
buf.as_ptr().copy_to_nonoverlapping(buffer.cast(), capacity);
231+
204232
let hd = HandleData::new(url, headers, None);
205233
// heap-allocate the struct and cast it to a void pointer so it can be passed around to PROJ
206234
let hd_boxed = Box::new(hd);
207235
let void: *mut c_void = Box::into_raw(hd_boxed).cast::<libc::c_void>();
208236
let opaque: *mut PROJ_NETWORK_HANDLE = void.cast::<proj_sys::PROJ_NETWORK_HANDLE>();
237+
209238
// If everything's OK, set the error string to empty
210239
let err_string = "";
211240
out_error_string.copy_from_nonoverlapping(err_string.as_ptr().cast(), err_string.len());
212241
out_error_string.add(err_string.len()).write(0);
242+
213243
Ok(opaque)
214244
}
215245

@@ -334,41 +364,58 @@ fn _network_read_range(
334364
let end = offset as usize + size_to_read - 1;
335365
let hvalue = format!("bytes={offset}-{end}");
336366
let hd = unsafe { &mut *(handle as *const c_void as *mut HandleData) };
337-
let clt = Agent::new();
338-
let initial = clt.get(&hd.url);
339-
let in_case_of_error = initial.clone().set("Range", &hvalue).set("Client", CLIENT);
340-
let req = in_case_of_error.clone();
367+
let clt = Agent::new_with_defaults();
368+
let req = clt
369+
.get(&hd.url)
370+
.header("Range", &hvalue)
371+
.header("Client", CLIENT);
372+
341373
let mut res = req.call()?;
342-
// hand the response and retry instance off to the error-handler, continue on success
343-
error_handler(&mut res, in_case_of_error)?;
374+
375+
// Define headers for potential retries
376+
let headers = [("Range", hvalue.as_str()), ("Client", CLIENT)];
377+
378+
// hand the response off to the error-handler, continue on success
379+
error_handler(&mut res, &hd.url, &headers)?;
380+
344381
let headers = res
345-
.headers_names()
346-
.into_iter()
382+
.headers()
383+
.keys()
347384
.filter_map(|h| {
348-
Some({
349-
let v = res.header(&h)?.to_string();
350-
(h, v)
351-
})
385+
let header_name = h.as_str().to_string();
386+
let header_value = res.headers().get(h)?.to_str().ok()?.to_string();
387+
Some((header_name, header_value))
352388
})
353389
.collect();
354-
let Some(Ok(contentlength)) = res.header("Content-Length").map(str::parse::<usize>) else {
355-
return Err(ProjError::ContentLength);
356-
};
390+
391+
let contentlength = res
392+
.headers()
393+
.get("Content-Length")
394+
.and_then(|val| val.to_str().ok())
395+
.and_then(|s| s.parse::<usize>().ok())
396+
.ok_or(ProjError::ContentLength)?;
397+
357398
// Copy the downloaded bytes into the buffer so it can be passed around
358399
let capacity = contentlength.min(size_to_read);
359-
let mut buf = Vec::with_capacity(capacity);
360-
res.into_reader()
400+
let mut buf = Vec::<u8>::with_capacity(capacity);
401+
402+
// Read from body into our buffer
403+
let body_reader = res.body_mut().as_reader();
404+
body_reader
361405
.take(size_to_read as u64)
362406
.read_to_end(&mut buf)?;
407+
363408
unsafe {
364409
buf.as_ptr()
365410
.copy_to_nonoverlapping(buffer.cast::<u8>(), capacity);
366411
}
412+
367413
let err_string = "";
368414
unsafe {
369415
out_error_string.copy_from_nonoverlapping(err_string.as_ptr().cast(), err_string.len());
370416
out_error_string.add(err_string.len()).write(0);
371417
}
418+
372419
hd.headers = headers;
373420
Ok(buf.len())
374421
}

0 commit comments

Comments
 (0)