1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
//! ref: composer/src/Composer/Util/Http/RequestProxy.php
use indexmap::IndexMap;
use shirabe_php_shim::{
CURL_VERSION_HTTPS_PROXY, CURLAUTH_BASIC, CURLOPT_NOPROXY, CURLOPT_PROXY, CURLOPT_PROXY_CAINFO,
CURLOPT_PROXY_CAPATH, CURLOPT_PROXYAUTH, CURLOPT_PROXYUSERPWD, InvalidArgumentException,
PhpMixed, curl_version,
};
use crate::downloader::TransportException;
// contextOptions = array{http: array{proxy: string, header?: string, request_fulluri?: bool}}
type ContextOptions = IndexMap<String, IndexMap<String, PhpMixed>>;
#[derive(Debug)]
pub struct RequestProxy {
context_options: Option<ContextOptions>,
status: Option<String>,
url: Option<String>,
auth: Option<String>,
}
impl RequestProxy {
pub fn new(
url: Option<String>,
auth: Option<String>,
context_options: Option<ContextOptions>,
status: Option<String>,
) -> Self {
Self {
url,
auth,
context_options,
status,
}
}
pub fn none() -> Self {
Self::new(None, None, None, None)
}
pub fn no_proxy() -> Self {
Self::new(None, None, None, Some("excluded by no_proxy".to_string()))
}
pub fn get_context_options(&self) -> Option<&ContextOptions> {
self.context_options.as_ref()
}
pub fn get_curl_options(
&self,
ssl_options: &IndexMap<String, PhpMixed>,
) -> Result<IndexMap<i64, PhpMixed>, TransportException> {
if self.is_secure() && !self.supports_secure_proxy() {
return Err(TransportException::new(
"Cannot use an HTTPS proxy. PHP >= 7.3 and cUrl >= 7.52.0 are required."
.to_string(),
0,
));
}
let mut options: IndexMap<i64, PhpMixed> = IndexMap::new();
options.insert(
CURLOPT_PROXY,
PhpMixed::String(self.url.as_deref().unwrap_or("").to_string()),
);
if self.url.is_some() {
options.insert(CURLOPT_NOPROXY, PhpMixed::String(String::new()));
}
if let Some(auth) = &self.auth {
options.insert(CURLOPT_PROXYAUTH, PhpMixed::Int(CURLAUTH_BASIC));
options.insert(CURLOPT_PROXYUSERPWD, PhpMixed::String(auth.clone()));
}
if self.is_secure() {
if let Some(cafile) = ssl_options.get("cafile") {
options.insert(CURLOPT_PROXY_CAINFO, cafile.clone());
}
if let Some(capath) = ssl_options.get("capath") {
options.insert(CURLOPT_PROXY_CAPATH, capath.clone());
}
}
Ok(options)
}
pub fn get_status(&self, format: Option<&str>) -> Result<String, InvalidArgumentException> {
if self.status.is_none() {
return Ok(String::new());
}
let format = format.unwrap_or("%s");
if format.contains("%s") {
return Ok(format.replace("%s", self.status.as_deref().unwrap()));
}
Err(InvalidArgumentException {
message: "String format specifier is missing".to_string(),
code: 0,
})
}
pub fn is_excluded_by_no_proxy(&self) -> bool {
self.status.is_some() && self.url.is_none()
}
pub fn is_secure(&self) -> bool {
self.url.as_deref().unwrap_or("").starts_with("https://")
}
pub fn supports_secure_proxy(&self) -> bool {
let version = match curl_version() {
None => return false,
Some(v) => v,
};
if !shirabe_php_shim::defined("CURL_VERSION_HTTPS_PROXY") {
return false;
}
let features = version
.get("features")
.and_then(|v| v.as_int())
.unwrap_or(0);
(features & CURL_VERSION_HTTPS_PROXY) != 0
}
}
|