AWS SDK

AWS SDK

rev. 8fe0f8fb3465e339defef9eaa2edd3c58411b010 (ignoring whitespace)

Files changed:

tmp-codegen-diff/aws-sdk/sdk/aws-config/Cargo.toml

@@ -39,39 +157,157 @@
   59     59   
path = "../aws-smithy-http"
   60     60   
version = "0.62.3"
   61     61   
   62     62   
[dependencies.aws-smithy-json]
   63     63   
path = "../aws-smithy-json"
   64     64   
version = "0.61.4"
   65     65   
   66     66   
[dependencies.aws-smithy-runtime]
   67     67   
path = "../aws-smithy-runtime"
   68     68   
features = ["client"]
   69         -
version = "1.8.6"
          69  +
version = "1.9.0"
   70     70   
   71     71   
[dependencies.aws-smithy-runtime-api]
   72     72   
path = "../aws-smithy-runtime-api"
   73     73   
features = ["client"]
   74         -
version = "1.8.7"
          74  +
version = "1.9.0"
   75     75   
   76     76   
[dependencies.aws-smithy-types]
   77     77   
path = "../aws-smithy-types"
   78     78   
version = "1.3.2"
   79     79   
   80     80   
[dependencies.aws-types]
   81     81   
path = "../aws-types"
   82     82   
version = "1.3.8"
   83     83   
   84     84   
[dependencies.time]
   85     85   
version = "0.3.4"
   86     86   
features = ["parsing"]
   87     87   
   88     88   
[dependencies.tokio]
   89     89   
version = "1.13.1"
   90     90   
features = ["sync"]
   91     91   
   92     92   
[dependencies.tracing]
   93     93   
version = "0.1"
   94     94   
   95     95   
[dependencies.aws-sdk-sso]
   96     96   
path = "../sso"
   97     97   
default-features = false
   98     98   
optional = true
   99     99   
version = "0.0.0-local"
  100    100   
  101    101   
[dependencies.ring]
  102    102   
version = "0.17.5"
  103    103   
optional = true
  104    104   
  105    105   
[dependencies.hex]
  106    106   
version = "0.4.3"
  107    107   
optional = true
  108    108   
  109    109   
[dependencies.zeroize]
  110    110   
version = "1"
  111    111   
optional = true
  112    112   
  113    113   
[dependencies.aws-sdk-ssooidc]
  114    114   
path = "../ssooidc"
  115    115   
default-features = false
  116    116   
optional = true
  117    117   
version = "0.0.0-local"
  118    118   
  119    119   
[dev-dependencies]
  120    120   
tracing-test = "0.2.4"
  121    121   
serde_json = "1"
  122    122   
  123    123   
[dev-dependencies.aws-smithy-async]
  124    124   
path = "../aws-smithy-async"
  125    125   
features = ["rt-tokio", "test-util"]
  126    126   
version = "1.2.5"
  127    127   
  128    128   
[dev-dependencies.aws-smithy-http-client]
  129    129   
path = "../aws-smithy-http-client"
  130    130   
features = ["default-client", "test-util"]
  131         -
version = "1.0.6"
         131  +
version = "1.1.0"
  132    132   
  133    133   
[dev-dependencies.aws-smithy-runtime]
  134    134   
path = "../aws-smithy-runtime"
  135    135   
features = ["client", "test-util"]
  136         -
version = "1.8.6"
         136  +
version = "1.9.0"
  137    137   
  138    138   
[dev-dependencies.aws-smithy-runtime-api]
  139    139   
path = "../aws-smithy-runtime-api"
  140    140   
features = ["test-util"]
  141         -
version = "1.8.7"
         141  +
version = "1.9.0"
  142    142   
  143    143   
[dev-dependencies.futures-util]
  144    144   
version = "0.3.29"
  145    145   
default-features = false
  146    146   
  147    147   
[dev-dependencies.tracing-subscriber]
  148    148   
version = "0.3.16"
  149    149   
features = ["fmt", "json"]
  150    150   
  151    151   
[dev-dependencies.tokio]

tmp-codegen-diff/aws-sdk/sdk/aws-credential-types/Cargo.toml

@@ -7,7 +49,49 @@
   27     27   
path = "../aws-smithy-async"
   28     28   
version = "1.2.5"
   29     29   
   30     30   
[dependencies.aws-smithy-types]
   31     31   
path = "../aws-smithy-types"
   32     32   
version = "1.3.2"
   33     33   
   34     34   
[dependencies.aws-smithy-runtime-api]
   35     35   
path = "../aws-smithy-runtime-api"
   36     36   
features = ["client", "http-auth"]
   37         -
version = "1.8.7"
          37  +
version = "1.9.0"
   38     38   
   39     39   
[dev-dependencies]
   40     40   
async-trait = "0.1.74"
   41     41   
   42     42   
[dev-dependencies.aws-smithy-runtime-api]
   43     43   
path = "../aws-smithy-runtime-api"
   44     44   
features = ["test-util"]
   45         -
version = "1.8.7"
          45  +
version = "1.9.0"
   46     46   
   47     47   
[dev-dependencies.tokio]
   48     48   
version = "1.23.1"
   49     49   
features = ["full", "test-util", "rt"]

tmp-codegen-diff/aws-sdk/sdk/aws-runtime/Cargo.toml

@@ -28,28 +144,144 @@
   48     48   
optional = true
   49     49   
version = "0.60.10"
   50     50   
   51     51   
[dependencies.aws-smithy-http]
   52     52   
path = "../aws-smithy-http"
   53     53   
version = "0.62.3"
   54     54   
   55     55   
[dependencies.aws-smithy-runtime]
   56     56   
path = "../aws-smithy-runtime"
   57     57   
features = ["client"]
   58         -
version = "1.8.6"
          58  +
version = "1.9.0"
   59     59   
   60     60   
[dependencies.aws-smithy-runtime-api]
   61     61   
path = "../aws-smithy-runtime-api"
   62     62   
features = ["client"]
   63         -
version = "1.8.7"
          63  +
version = "1.9.0"
   64     64   
   65     65   
[dependencies.aws-smithy-types]
   66     66   
path = "../aws-smithy-types"
   67     67   
version = "1.3.2"
   68     68   
   69     69   
[dependencies.aws-types]
   70     70   
path = "../aws-types"
   71     71   
version = "1.3.8"
   72     72   
   73     73   
[dependencies.http-02x]
   74     74   
package = "http"
   75     75   
version = "0.2.9"
   76     76   
   77     77   
[dependencies.http-body-04x]
   78     78   
package = "http-body"
   79     79   
version = "0.4.5"
   80     80   
   81     81   
[dependencies.http-1x]
   82     82   
package = "http"
   83     83   
version = "1.1.0"
   84     84   
optional = true
   85     85   
   86     86   
[dependencies.http-body-1x]
   87     87   
package = "http-body"
   88     88   
version = "1.0.0"
   89     89   
optional = true
   90     90   
   91     91   
[dependencies.regex-lite]
   92     92   
version = "0.1.5"
   93     93   
optional = true
   94     94   
   95     95   
[dependencies.uuid]
   96     96   
version = "1"
   97     97   
   98     98   
[dev-dependencies]
   99     99   
arbitrary = "1.3"
  100    100   
bytes-utils = "0.1.2"
  101    101   
convert_case = "0.6.0"
  102    102   
proptest = "1.2"
  103    103   
serde_json = "1"
  104    104   
tracing-test = "0.2.4"
  105    105   
  106    106   
[dev-dependencies.aws-credential-types]
  107    107   
path = "../aws-credential-types"
  108    108   
features = ["test-util"]
  109    109   
version = "1.2.5"
  110    110   
  111    111   
[dev-dependencies.aws-smithy-async]
  112    112   
path = "../aws-smithy-async"
  113    113   
features = ["test-util"]
  114    114   
version = "1.2.5"
  115    115   
  116    116   
[dev-dependencies.aws-smithy-protocol-test]
  117    117   
path = "../aws-smithy-protocol-test"
  118    118   
version = "0.63.4"
  119    119   
  120    120   
[dev-dependencies.aws-smithy-runtime-api]
  121    121   
path = "../aws-smithy-runtime-api"
  122    122   
features = ["test-util"]
  123         -
version = "1.8.7"
         123  +
version = "1.9.0"
  124    124   
  125    125   
[dev-dependencies.aws-smithy-types]
  126    126   
path = "../aws-smithy-types"
  127    127   
features = ["test-util"]
  128    128   
version = "1.3.2"
  129    129   
  130    130   
[dev-dependencies.futures-util]
  131    131   
version = "0.3.29"
  132    132   
default-features = false
  133    133   

tmp-codegen-diff/aws-sdk/sdk/aws-sigv4/Cargo.toml

@@ -31,31 +91,91 @@
   51     51   
optional = true
   52     52   
version = "0.60.10"
   53     53   
   54     54   
[dependencies.aws-smithy-http]
   55     55   
path = "../aws-smithy-http"
   56     56   
version = "0.62.3"
   57     57   
   58     58   
[dependencies.aws-smithy-runtime-api]
   59     59   
path = "../aws-smithy-runtime-api"
   60     60   
features = ["client"]
   61         -
version = "1.8.7"
          61  +
version = "1.9.0"
   62     62   
   63     63   
[dependencies.aws-smithy-types]
   64     64   
path = "../aws-smithy-types"
   65     65   
version = "1.3.2"
   66     66   
   67     67   
[dependencies.form_urlencoded]
   68     68   
version = "1.2.1"
   69     69   
optional = true
   70     70   
   71     71   
[dependencies.http0]
@@ -94,94 +130,130 @@
  114    114   
criterion = "0.5"
  115    115   
  116    116   
[dev-dependencies.aws-credential-types]
  117    117   
path = "../aws-credential-types"
  118    118   
features = ["test-util", "hardcoded-credentials"]
  119    119   
version = "1.2.5"
  120    120   
  121    121   
[dev-dependencies.aws-smithy-runtime-api]
  122    122   
path = "../aws-smithy-runtime-api"
  123    123   
features = ["client", "test-util"]
  124         -
version = "1.8.7"
         124  +
version = "1.9.0"
  125    125   
  126    126   
[dev-dependencies.time]
  127    127   
version = "0.3.5"
  128    128   
features = ["parsing"]
  129    129   
[target."cfg(not(any(target_arch = \"powerpc\", target_arch = \"powerpc64\")))".dev-dependencies]
  130    130   
ring = "0.17.5"

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-compression/Cargo.toml

@@ -3,3 +63,63 @@
   23     23   
futures-util = "0.3"
   24     24   
pin-project-lite = "0.2.14"
   25     25   
tracing = "0.1.40"
   26     26   
   27     27   
[dependencies.aws-smithy-types]
   28     28   
path = "../aws-smithy-types"
   29     29   
version = "1.3.2"
   30     30   
   31     31   
[dependencies.aws-smithy-runtime-api]
   32     32   
path = "../aws-smithy-runtime-api"
   33         -
version = "1.8.7"
          33  +
version = "1.9.0"
   34     34   
   35     35   
[dependencies.http-0-2]
   36     36   
package = "http"
   37     37   
version = "0.2.9"
   38     38   
optional = true
   39     39   
   40     40   
[dependencies.http-1-0]
   41     41   
package = "http"
   42     42   
version = "1"
   43     43   
optional = true

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http-client/Cargo.toml

@@ -1,1 +221,232 @@
   16     16   
   17     17   
[[example]]
   18     18   
name = "custom-dns"
   19     19   
required-features = ["rustls-ring"]
   20     20   
doc-scrape-examples = true
   21     21   
   22     22   
[package]
   23     23   
name = "aws-smithy-http-client"
   24     24   
authors = ["AWS Rust SDK Team <aws-sdk-rust@amazon.com>"]
   25     25   
description = "HTTP client abstractions for generated smithy clients"
   26         -
version = "1.0.6"
          26  +
version = "1.1.0"
   27     27   
license = "Apache-2.0"
   28     28   
edition = "2021"
   29     29   
repository = "https://github.com/smithy-lang/smithy-rs"
   30     30   
[package.metadata.smithy-rs-release-tooling]
   31     31   
stable = true
   32     32   
[package.metadata.docs.rs]
   33     33   
all-features = false
   34     34   
features = ["default-client ", "wire-mock", "test-util", "rustls-ring", "rustls-aws-lc"]
   35     35   
targets = ["x86_64-unknown-linux-gnu"]
   36     36   
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
   37     37   
rustdoc-args = ["--cfg", "docsrs"]
   38     38   
   39     39   
[features]
   40     40   
hyper-014 = ["aws-smithy-runtime-api/http-02x", "aws-smithy-types/http-body-0-4-x", "dep:http-02x", "dep:http-body-04x", "dep:hyper-0-14", "dep:h2-0-3"]
   41         -
default-client = ["aws-smithy-runtime-api/http-1x", "aws-smithy-types/http-body-1-x", "dep:hyper", "dep:hyper-util", "hyper-util?/client-legacy", "dep:http-1x", "dep:tower", "dep:rustls-pki-types", "dep:rustls-native-certs"]
          41  +
default-client = ["aws-smithy-runtime-api/http-1x", "aws-smithy-types/http-body-1-x", "dep:hyper", "dep:hyper-util", "hyper-util?/client-legacy", "hyper-util?/client-proxy", "dep:http-1x", "dep:tower", "dep:rustls-pki-types", "dep:rustls-native-certs"]
   42     42   
wire-mock = ["test-util", "default-client", "hyper-util?/server", "hyper-util?/server-auto", "hyper-util?/service", "hyper-util?/server-graceful", "tokio/macros", "dep:http-body-util"]
   43     43   
test-util = ["dep:aws-smithy-protocol-test", "dep:serde", "dep:serde_json", "dep:indexmap", "dep:bytes", "dep:http-1x", "aws-smithy-runtime-api/http-1x", "dep:http-body-1x", "aws-smithy-types/http-body-1-x", "tokio/rt"]
   44     44   
legacy-test-util = ["test-util", "dep:http-02x", "aws-smithy-runtime-api/http-02x", "aws-smithy-types/http-body-0-4-x"]
   45     45   
legacy-rustls-ring = ["dep:legacy-hyper-rustls", "dep:legacy-rustls", "hyper-014"]
   46         -
rustls-ring = ["dep:rustls", "rustls?/ring", "dep:hyper-rustls", "default-client"]
   47         -
rustls-aws-lc = ["dep:rustls", "rustls?/aws_lc_rs", "dep:hyper-rustls", "default-client"]
   48         -
rustls-aws-lc-fips = ["dep:rustls", "rustls?/fips", "dep:hyper-rustls", "default-client"]
   49         -
s2n-tls = ["dep:s2n-tls", "dep:s2n-tls-hyper", "default-client"]
          46  +
rustls-ring = ["dep:rustls", "rustls?/ring", "dep:hyper-rustls", "dep:tokio-rustls", "default-client"]
          47  +
rustls-aws-lc = ["dep:rustls", "rustls?/aws_lc_rs", "dep:hyper-rustls", "dep:tokio-rustls", "default-client"]
          48  +
rustls-aws-lc-fips = ["dep:rustls", "rustls?/fips", "dep:hyper-rustls", "dep:tokio-rustls", "default-client"]
          49  +
s2n-tls = ["dep:s2n-tls", "dep:s2n-tls-hyper", "dep:s2n-tls-tokio", "default-client"]
   50     50   
   51     51   
[dependencies]
   52         -
pin-project-lite = "0.2.14"
   53         -
tracing = "0.1.40"
          52  +
pin-project-lite = "0.2.16"
          53  +
tracing = "0.1.41"
   54     54   
   55     55   
[dependencies.aws-smithy-async]
   56     56   
path = "../aws-smithy-async"
   57     57   
version = "1.2.5"
   58     58   
   59     59   
[dependencies.aws-smithy-runtime-api]
   60     60   
path = "../aws-smithy-runtime-api"
   61     61   
features = ["client"]
   62         -
version = "1.8.7"
          62  +
version = "1.9.0"
   63     63   
   64     64   
[dependencies.aws-smithy-types]
   65     65   
path = "../aws-smithy-types"
   66     66   
version = "1.3.2"
   67     67   
   68     68   
[dependencies.aws-smithy-protocol-test]
   69     69   
path = "../aws-smithy-protocol-test"
   70     70   
optional = true
   71     71   
version = "0.63.4"
   72     72   
   73     73   
[dependencies.h2]
   74         -
version = "0.4.2"
          74  +
version = "0.4.11"
   75     75   
default-features = false
   76     76   
   77     77   
[dependencies.tokio]
   78         -
version = "1.40"
          78  +
version = "1.47"
   79     79   
features = []
   80     80   
   81     81   
[dependencies.hyper]
   82     82   
version = "1.6.0"
   83     83   
features = ["client", "http1", "http2"]
   84     84   
optional = true
   85     85   
   86     86   
[dependencies.hyper-util]
   87         -
version = "0.1.10"
          87  +
version = "0.1.16"
   88     88   
features = ["http1", "http2"]
   89     89   
optional = true
   90     90   
   91     91   
[dependencies.http-1x]
   92     92   
package = "http"
   93     93   
version = "1"
   94     94   
optional = true
   95     95   
   96     96   
[dependencies.http-body-1x]
   97     97   
package = "http-body"
   98     98   
version = "1"
   99     99   
optional = true
  100    100   
  101    101   
[dependencies.hyper-rustls]
  102    102   
version = "0.27"
  103    103   
features = ["http2", "http1", "native-tokio", "tls12"]
  104    104   
default-features = false
  105    105   
optional = true
  106    106   
  107    107   
[dependencies.rustls]
  108    108   
version = "0.23"
  109    109   
default-features = false
  110    110   
optional = true
  111    111   
         112  +
[dependencies.tokio-rustls]
         113  +
version = "0.26.2"
         114  +
default-features = false
         115  +
optional = true
         116  +
  112    117   
[dependencies.s2n-tls-hyper]
  113         -
version = "0.0.4"
         118  +
version = "0.0.15"
  114    119   
optional = true
  115    120   
  116    121   
[dependencies.s2n-tls]
  117         -
version = "0.3.12"
         122  +
version = "0.3.23"
         123  +
optional = true
         124  +
         125  +
[dependencies.s2n-tls-tokio]
         126  +
version = "0.3.23"
  118    127   
optional = true
  119    128   
  120    129   
[dependencies.tower]
  121    130   
version = "0.5.2"
  122    131   
optional = true
  123    132   
  124    133   
[dependencies.rustls-pki-types]
  125         -
version = "1.11.0"
         134  +
version = "1.12.0"
  126    135   
features = ["std"]
  127    136   
optional = true
  128    137   
  129    138   
[dependencies.rustls-native-certs]
  130    139   
version = "0.8.1"
  131    140   
optional = true
  132    141   
  133    142   
[dependencies.http-02x]
  134    143   
package = "http"
  135    144   
version = "0.2.9"
  136    145   
optional = true
  137    146   
  138    147   
[dependencies.http-body-04x]
  139    148   
package = "http-body"
  140    149   
version = "0.4.5"
  141    150   
optional = true
  142    151   
  143    152   
[dependencies.hyper-0-14]
  144    153   
package = "hyper"
  145    154   
version = "0.14.26"
  146    155   
default-features = false
  147    156   
features = ["client", "http1", "http2", "tcp", "stream"]
  148    157   
optional = true
  149    158   
  150    159   
[dependencies.legacy-hyper-rustls]
  151    160   
package = "hyper-rustls"
  152    161   
version = "0.24"
  153    162   
features = ["rustls-native-certs", "http2"]
  154    163   
optional = true
  155    164   
  156    165   
[dependencies.legacy-rustls]
  157    166   
package = "rustls"
  158    167   
version = "0.21.8"
  159    168   
optional = true
  160    169   
  161    170   
[dependencies.h2-0-3]
  162    171   
package = "h2"
  163    172   
version = "0.3.24"
  164    173   
optional = true
  165    174   
  166    175   
[dependencies.bytes]
  167         -
version = "1.10.0"
         176  +
version = "1.10.1"
  168    177   
optional = true
  169    178   
  170    179   
[dependencies.serde]
  171         -
version = "1.0.210"
         180  +
version = "1.0.219"
  172    181   
features = ["derive"]
  173    182   
optional = true
  174    183   
  175    184   
[dependencies.serde_json]
  176         -
version = "1.0.128"
         185  +
version = "1.0.141"
  177    186   
features = ["preserve_order"]
  178    187   
optional = true
  179    188   
  180    189   
[dependencies.indexmap]
  181         -
version = "2.6.0"
         190  +
version = "2.10.0"
  182    191   
features = ["serde"]
  183    192   
optional = true
  184    193   
  185    194   
[dependencies.http-body-util]
  186         -
version = "0.1.2"
         195  +
version = "0.1.3"
  187    196   
optional = true
  188    197   
  189    198   
[dev-dependencies]
         199  +
serial_test = "3.2"
         200  +
base64 = "0.22"
  190    201   
rustls-pemfile = "2.2.0"
  191         -
tokio-rustls = "0.26.1"
         202  +
tokio-rustls = "0.26.2"
  192    203   
  193    204   
[dev-dependencies.aws-smithy-async]
  194    205   
path = "../aws-smithy-async"
  195    206   
features = ["rt-tokio", "test-util"]
  196    207   
version = "1.2.5"
  197    208   
  198    209   
[dev-dependencies.aws-smithy-runtime-api]
  199    210   
path = "../aws-smithy-runtime-api"
  200    211   
features = ["test-util"]
  201         -
version = "1.8.7"
         212  +
version = "1.9.0"
  202    213   
  203    214   
[dev-dependencies.aws-smithy-types]
  204    215   
path = "../aws-smithy-types"
  205    216   
features = ["http-body-0-4-x", "test-util"]
  206    217   
version = "1.3.2"
  207    218   
  208    219   
[dev-dependencies.http-body-util]
  209    220   
version = "0.1.2"
  210    221   
  211    222   
[dev-dependencies.hyper-util]
  212         -
version = "0.1.7"
         223  +
version = "0.1.16"
  213    224   
features = ["full"]
  214    225   
  215    226   
[dev-dependencies.rustls-pki-types]
  216         -
version = "1.11.0"
         227  +
version = "1.12.0"
  217    228   
features = ["std"]
  218    229   
  219    230   
[dev-dependencies.tokio]
  220    231   
version = "1"
  221    232   
features = ["macros", "rt", "rt-multi-thread", "test-util", "full"]

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http-client/src/client.rs

@@ -1,1 +71,76 @@
    1      1   
/*
    2      2   
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
    3      3   
 * SPDX-License-Identifier: Apache-2.0
    4      4   
 */
    5      5   
    6      6   
mod dns;
           7  +
/// Proxy configuration
           8  +
pub mod proxy;
    7      9   
mod timeout;
    8     10   
/// TLS connector(s)
    9     11   
pub mod tls;
   10     12   
          13  +
pub(crate) mod connect;
          14  +
   11     15   
use crate::cfg::cfg_tls;
   12     16   
use crate::tls::TlsContext;
   13     17   
use aws_smithy_async::future::timeout::TimedOutError;
   14     18   
use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep, SharedAsyncSleep};
   15     19   
use aws_smithy_runtime_api::box_error::BoxError;
   16     20   
use aws_smithy_runtime_api::client::connection::CaptureSmithyConnection;
   17     21   
use aws_smithy_runtime_api::client::connection::ConnectionMetadata;
   18     22   
use aws_smithy_runtime_api::client::connector_metadata::ConnectorMetadata;
   19     23   
use aws_smithy_runtime_api::client::http::{
   20     24   
    HttpClient, HttpConnector, HttpConnectorFuture, HttpConnectorSettings, SharedHttpClient,
   21     25   
    SharedHttpConnector,
   22     26   
};
   23     27   
use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, HttpResponse};
   24     28   
use aws_smithy_runtime_api::client::result::ConnectorError;
   25     29   
use aws_smithy_runtime_api::client::runtime_components::{
   26     30   
    RuntimeComponents, RuntimeComponentsBuilder,
   27     31   
};
   28     32   
use aws_smithy_runtime_api::shared::IntoShared;
   29     33   
use aws_smithy_types::body::SdkBody;
   30     34   
use aws_smithy_types::config_bag::ConfigBag;
   31     35   
use aws_smithy_types::error::display::DisplayErrorContext;
   32     36   
use aws_smithy_types::retry::ErrorKind;
   33     37   
use client::connect::Connection;
   34     38   
use h2::Reason;
   35     39   
use http_1x::{Extensions, Uri};
   36     40   
use hyper::rt::{Read, Write};
   37     41   
use hyper_util::client::legacy as client;
   38     42   
use hyper_util::client::legacy::connect::dns::GaiResolver;
   39     43   
use hyper_util::client::legacy::connect::{
   40     44   
    capture_connection, CaptureConnection, Connect, HttpConnector as HyperHttpConnector, HttpInfo,
   41     45   
};
          46  +
use hyper_util::client::proxy::matcher::Matcher;
   42     47   
use hyper_util::rt::TokioExecutor;
   43     48   
use std::borrow::Cow;
   44     49   
use std::collections::HashMap;
   45     50   
use std::error::Error;
   46     51   
use std::fmt;
   47     52   
use std::sync::RwLock;
   48     53   
use std::time::Duration;
   49     54   
   50     55   
/// Given `HttpConnectorSettings` and an `SharedAsyncSleep`, create a `SharedHttpConnector` from defaults depending on what cargo features are activated.
   51     56   
pub fn default_connector(
@@ -76,81 +228,265 @@
   96    101   
    }
   97    102   
}
   98    103   
   99    104   
impl HttpConnector for Connector {
  100    105   
    fn call(&self, request: HttpRequest) -> HttpConnectorFuture {
  101    106   
        self.adapter.call(request)
  102    107   
    }
  103    108   
}
  104    109   
  105    110   
/// Builder for [`Connector`].
  106         -
#[derive(Default, Debug)]
         111  +
#[derive(Default, Debug, Clone)]
  107    112   
pub struct ConnectorBuilder<Tls = TlsUnset> {
  108    113   
    connector_settings: Option<HttpConnectorSettings>,
  109    114   
    sleep_impl: Option<SharedAsyncSleep>,
  110    115   
    client_builder: Option<hyper_util::client::legacy::Builder>,
  111    116   
    enable_tcp_nodelay: bool,
  112    117   
    interface: Option<String>,
         118  +
    proxy_config: Option<proxy::ProxyConfig>,
  113    119   
    #[allow(unused)]
  114    120   
    tls: Tls,
  115    121   
}
  116    122   
  117    123   
/// Initial builder state, `TlsProvider` choice required
  118         -
#[derive(Default)]
         124  +
#[derive(Default, Debug, Clone)]
  119    125   
#[non_exhaustive]
  120    126   
pub struct TlsUnset {}
  121    127   
  122    128   
/// TLS implementation selected
         129  +
#[derive(Debug, Clone)]
  123    130   
pub struct TlsProviderSelected {
  124    131   
    #[allow(unused)]
  125    132   
    provider: tls::Provider,
  126    133   
    #[allow(unused)]
  127    134   
    context: TlsContext,
  128    135   
}
  129    136   
  130    137   
impl ConnectorBuilder<TlsUnset> {
  131    138   
    /// Set the TLS implementation to use for this connector
  132    139   
    pub fn tls_provider(self, provider: tls::Provider) -> ConnectorBuilder<TlsProviderSelected> {
  133    140   
        ConnectorBuilder {
  134    141   
            connector_settings: self.connector_settings,
  135    142   
            sleep_impl: self.sleep_impl,
  136    143   
            client_builder: self.client_builder,
  137    144   
            enable_tcp_nodelay: self.enable_tcp_nodelay,
  138    145   
            interface: self.interface,
         146  +
            proxy_config: self.proxy_config,
  139    147   
            tls: TlsProviderSelected {
  140    148   
                provider,
  141    149   
                context: TlsContext::default(),
  142    150   
            },
  143    151   
        }
  144    152   
    }
  145    153   
  146    154   
    /// Build an HTTP connector sans TLS
  147    155   
    #[doc(hidden)]
  148    156   
    pub fn build_http(self) -> Connector {
         157  +
        if let Some(ref proxy_config) = self.proxy_config {
         158  +
            if proxy_config.requires_tls() {
         159  +
                tracing::warn!(
         160  +
                    "HTTPS proxy configured but no TLS provider set. \
         161  +
                     Connections to HTTPS proxy servers will fail. \
         162  +
                     Consider configuring a TLS provider to enable TLS support."
         163  +
                );
         164  +
            }
         165  +
        }
         166  +
  149    167   
        let base = self.base_connector();
         168  +
         169  +
        // Wrap with HTTP proxy support if proxy is configured
         170  +
        let proxy_config = self
         171  +
            .proxy_config
         172  +
            .clone()
         173  +
            .unwrap_or_else(proxy::ProxyConfig::disabled);
         174  +
         175  +
        if !proxy_config.is_disabled() {
         176  +
            let http_proxy_connector = connect::HttpProxyConnector::new(base, proxy_config);
         177  +
            self.wrap_connector(http_proxy_connector)
         178  +
        } else {
  150    179   
            self.wrap_connector(base)
  151    180   
        }
         181  +
    }
  152    182   
}
  153    183   
  154    184   
impl<Any> ConnectorBuilder<Any> {
  155    185   
    /// Create a [`Connector`] from this builder and a given connector.
  156    186   
    pub(crate) fn wrap_connector<C>(self, tcp_connector: C) -> Connector
  157    187   
    where
  158    188   
        C: Send + Sync + 'static,
  159    189   
        C: Clone,
  160    190   
        C: tower::Service<Uri>,
  161    191   
        C::Response: Read + Write + Connection + Send + Sync + Unpin,
  162    192   
        C: Connect,
  163    193   
        C::Future: Unpin + Send + 'static,
  164    194   
        C::Error: Into<BoxError>,
  165    195   
    {
  166    196   
        let client_builder =
  167    197   
            self.client_builder
  168    198   
                .unwrap_or(hyper_util::client::legacy::Builder::new(
  169    199   
                    TokioExecutor::new(),
  170    200   
                ));
  171    201   
        let sleep_impl = self.sleep_impl.or_else(default_async_sleep);
  172    202   
        let (connect_timeout, read_timeout) = self
  173    203   
            .connector_settings
  174    204   
            .map(|c| (c.connect_timeout(), c.read_timeout()))
  175    205   
            .unwrap_or((None, None));
  176    206   
  177    207   
        let connector = match connect_timeout {
  178    208   
            Some(duration) => timeout::ConnectTimeout::new(
  179    209   
                tcp_connector,
  180    210   
                sleep_impl
  181    211   
                    .clone()
  182    212   
                    .expect("a sleep impl must be provided in order to have a connect timeout"),
  183    213   
                duration,
  184    214   
            ),
  185    215   
            None => timeout::ConnectTimeout::no_timeout(tcp_connector),
  186    216   
        };
  187    217   
        let base = client_builder.build(connector);
  188    218   
        let read_timeout = match read_timeout {
  189    219   
            Some(duration) => timeout::HttpReadTimeout::new(
  190    220   
                base,
  191    221   
                sleep_impl.expect("a sleep impl must be provided in order to have a read timeout"),
  192    222   
                duration,
  193    223   
            ),
  194    224   
            None => timeout::HttpReadTimeout::no_timeout(base),
  195    225   
        };
         226  +
         227  +
        let proxy_matcher = self
         228  +
            .proxy_config
         229  +
            .as_ref()
         230  +
            .map(|config| config.clone().into_hyper_util_matcher());
         231  +
  196    232   
        Connector {
  197    233   
            adapter: Box::new(Adapter {
  198    234   
                client: read_timeout,
         235  +
                proxy_matcher,
  199    236   
            }),
  200    237   
        }
  201    238   
    }
  202    239   
  203    240   
    /// Get the base TCP connector by mapping our config to the underlying `HttpConnector` from hyper
  204    241   
    /// (which is a base TCP connector with no TLS or any wrapping)
  205    242   
    fn base_connector(&self) -> HyperHttpConnector {
  206    243   
        self.base_connector_with_resolver(GaiResolver::new())
  207    244   
    }
  208    245   
@@ -254,291 +395,501 @@
  274    311   
    ///
  275    312   
    /// This function is only available on Android, Fuchsia, and Linux.
  276    313   
    ///
  277    314   
    /// [VRF]: https://www.kernel.org/doc/Documentation/networking/vrf.txt
  278    315   
    #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
  279    316   
    pub fn set_interface<S: Into<String>>(&mut self, interface: S) -> &mut Self {
  280    317   
        self.interface = Some(interface.into());
  281    318   
        self
  282    319   
    }
  283    320   
         321  +
    /// Configure proxy settings for this connector
         322  +
    ///
         323  +
    /// This method allows you to set explicit proxy configuration for the HTTP client.
         324  +
    /// The proxy configuration will be used to determine whether requests should be
         325  +
    /// routed through a proxy server or connect directly.
         326  +
    ///
         327  +
    /// # Examples
         328  +
    ///
         329  +
    /// ```rust
         330  +
    /// # #[cfg(feature = "rustls-aws-lc")]
         331  +
    /// # {
         332  +
    /// use aws_smithy_http_client::{Connector, proxy::ProxyConfig, tls};
         333  +
    ///
         334  +
    /// let proxy_config = ProxyConfig::http("http://proxy.example.com:8080")?;
         335  +
    /// let connector = Connector::builder()
         336  +
    ///     .proxy_config(proxy_config)
         337  +
    ///     .tls_provider(tls::Provider::Rustls(tls::rustls_provider::CryptoMode::AwsLc))
         338  +
    ///     .build();
         339  +
    /// # }
         340  +
    /// # Ok::<(), Box<dyn std::error::Error>>(())
         341  +
    /// ```
         342  +
    pub fn proxy_config(mut self, config: proxy::ProxyConfig) -> Self {
         343  +
        self.proxy_config = Some(config);
         344  +
        self
         345  +
    }
         346  +
         347  +
    /// Configure proxy settings for this connector
         348  +
    ///
         349  +
    /// This is the mutable version of [`proxy_config`](Self::proxy_config).
         350  +
    pub fn set_proxy_config(&mut self, config: Option<proxy::ProxyConfig>) -> &mut Self {
         351  +
        self.proxy_config = config;
         352  +
        self
         353  +
    }
         354  +
  284    355   
    /// Override the Hyper client [`Builder`](hyper_util::client::legacy::Builder) used to construct this client.
  285    356   
    ///
  286    357   
    /// This enables changing settings like forcing HTTP2 and modifying other default client behavior.
  287    358   
    pub(crate) fn hyper_builder(
  288    359   
        mut self,
  289    360   
        hyper_builder: hyper_util::client::legacy::Builder,
  290    361   
    ) -> Self {
  291    362   
        self.set_hyper_builder(Some(hyper_builder));
  292    363   
        self
  293    364   
    }
  294    365   
  295    366   
    /// Override the Hyper client [`Builder`](hyper_util::client::legacy::Builder) used to construct this client.
  296    367   
    ///
  297    368   
    /// This enables changing settings like forcing HTTP2 and modifying other default client behavior.
  298    369   
    pub(crate) fn set_hyper_builder(
  299    370   
        &mut self,
  300    371   
        hyper_builder: Option<hyper_util::client::legacy::Builder>,
  301    372   
    ) -> &mut Self {
  302    373   
        self.client_builder = hyper_builder;
  303    374   
        self
  304    375   
    }
  305    376   
}
  306    377   
  307    378   
/// Adapter to use a Hyper 1.0-based Client as an `HttpConnector`
  308    379   
///
  309    380   
/// This adapter also enables TCP `CONNECT` and HTTP `READ` timeouts via [`Connector::builder`].
  310    381   
struct Adapter<C> {
  311    382   
    client: timeout::HttpReadTimeout<
  312    383   
        hyper_util::client::legacy::Client<timeout::ConnectTimeout<C>, SdkBody>,
  313    384   
    >,
         385  +
    proxy_matcher: Option<Matcher>,
  314    386   
}
  315    387   
  316    388   
impl<C> fmt::Debug for Adapter<C> {
  317    389   
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  318    390   
        f.debug_struct("Adapter")
  319    391   
            .field("client", &"** hyper client **")
         392  +
            .field("proxy_matcher", &self.proxy_matcher.is_some())
  320    393   
            .finish()
  321    394   
    }
  322    395   
}
  323    396   
  324    397   
/// Extract a smithy connection from a hyper CaptureConnection
  325    398   
fn extract_smithy_connection(capture_conn: &CaptureConnection) -> Option<ConnectionMetadata> {
  326    399   
    let capture_conn = capture_conn.clone();
  327    400   
    if let Some(conn) = capture_conn.clone().connection_metadata().as_ref() {
  328    401   
        let mut extensions = Extensions::new();
  329    402   
        conn.get_extras(&mut extensions);
  330    403   
        let http_info = extensions.get::<HttpInfo>();
  331    404   
        let mut builder = ConnectionMetadata::builder()
  332    405   
            .proxied(conn.is_proxied())
  333    406   
            .poison_fn(move || match capture_conn.connection_metadata().as_ref() {
  334    407   
                Some(conn) => conn.poison(),
  335    408   
                None => tracing::trace!("no connection existed to poison"),
  336    409   
            });
  337    410   
  338    411   
        builder
  339    412   
            .set_local_addr(http_info.map(|info| info.local_addr()))
  340    413   
            .set_remote_addr(http_info.map(|info| info.remote_addr()));
  341    414   
  342    415   
        let smithy_connection = builder.build();
  343    416   
  344    417   
        Some(smithy_connection)
  345    418   
    } else {
  346    419   
        None
  347    420   
    }
  348    421   
}
  349    422   
         423  +
impl<C> Adapter<C> {
         424  +
    /// Add proxy authentication header to the request if needed
         425  +
    fn add_proxy_auth_header(&self, request: &mut http_1x::Request<SdkBody>) {
         426  +
        // Only add auth for HTTP requests (not HTTPS which uses CONNECT tunneling)
         427  +
        if request.uri().scheme() != Some(&http_1x::uri::Scheme::HTTP) {
         428  +
            return;
         429  +
        }
         430  +
         431  +
        // Don't override existing proxy authorization header
         432  +
        if request
         433  +
            .headers()
         434  +
            .contains_key(http_1x::header::PROXY_AUTHORIZATION)
         435  +
        {
         436  +
            return;
         437  +
        }
         438  +
         439  +
        if let Some(ref matcher) = self.proxy_matcher {
         440  +
            if let Some(intercept) = matcher.intercept(request.uri()) {
         441  +
                // Add basic auth header if available
         442  +
                if let Some(auth_header) = intercept.basic_auth() {
         443  +
                    request
         444  +
                        .headers_mut()
         445  +
                        .insert(http_1x::header::PROXY_AUTHORIZATION, auth_header.clone());
         446  +
                    tracing::debug!("added proxy authentication header for {}", request.uri());
         447  +
                }
         448  +
            }
         449  +
        }
         450  +
    }
         451  +
}
         452  +
  350    453   
impl<C> HttpConnector for Adapter<C>
  351    454   
where
  352    455   
    C: Clone + Send + Sync + 'static,
  353    456   
    C: tower::Service<Uri>,
  354    457   
    C::Response: Connection + Read + Write + Unpin + 'static,
  355    458   
    timeout::ConnectTimeout<C>: Connect,
  356    459   
    C::Future: Unpin + Send + 'static,
  357    460   
    C::Error: Into<BoxError>,
  358    461   
{
  359    462   
    fn call(&self, request: HttpRequest) -> HttpConnectorFuture {
  360    463   
        let mut request = match request.try_into_http1x() {
  361    464   
            Ok(request) => request,
  362    465   
            Err(err) => {
  363    466   
                return HttpConnectorFuture::ready(Err(ConnectorError::user(err.into())));
  364    467   
            }
  365    468   
        };
         469  +
         470  +
        self.add_proxy_auth_header(&mut request);
         471  +
  366    472   
        let capture_connection = capture_connection(&mut request);
  367    473   
        if let Some(capture_smithy_connection) =
  368    474   
            request.extensions().get::<CaptureSmithyConnection>()
  369    475   
        {
  370    476   
            capture_smithy_connection
  371    477   
                .set_connection_retriever(move || extract_smithy_connection(&capture_connection));
  372    478   
        }
  373    479   
        let mut client = self.client.clone();
  374    480   
        use tower::Service;
  375    481   
        let fut = client.call(request);
@@ -587,693 +709,843 @@
  607    713   
            R::Error: Into<Box<dyn Error + Send + Sync>>,
  608    714   
        {
  609    715   
            match &self.tls.provider {
  610    716   
                // TODO(hyper1) - fix cfg_rustls! to allow matching on patterns so we can re-use it and not duplicate these cfg matches everywhere
  611    717   
                #[cfg(any(
  612    718   
                    feature = "rustls-aws-lc",
  613    719   
                    feature = "rustls-aws-lc-fips",
  614    720   
                    feature = "rustls-ring"
  615    721   
                ))]
  616    722   
                tls::Provider::Rustls(crypto_mode) => {
         723  +
                    let proxy_config = self.proxy_config.clone()
         724  +
                        .unwrap_or_else(proxy::ProxyConfig::disabled);
         725  +
  617    726   
                    let https_connector = tls::rustls_provider::build_connector::wrap_connector(
  618    727   
                        http_connector,
  619    728   
                        crypto_mode.clone(),
  620    729   
                        &self.tls.context,
         730  +
                        proxy_config,
  621    731   
                    );
  622    732   
                    self.wrap_connector(https_connector)
  623    733   
                },
  624    734   
                #[cfg(feature = "s2n-tls")]
  625    735   
                tls::Provider::S2nTls  => {
  626         -
                    let https_connector = tls::s2n_tls_provider::build_connector::wrap_connector(http_connector, &self.tls.context);
         736  +
                    let proxy_config = self.proxy_config.clone()
         737  +
                        .unwrap_or_else(proxy::ProxyConfig::disabled);
         738  +
         739  +
                    let https_connector = tls::s2n_tls_provider::build_connector::wrap_connector(
         740  +
                        http_connector,
         741  +
                        &self.tls.context,
         742  +
                        proxy_config,
         743  +
                    );
  627    744   
                    self.wrap_connector(https_connector)
  628    745   
                }
  629    746   
            }
  630    747   
        }
  631    748   
    }
  632    749   
  633    750   
    impl Builder<TlsProviderSelected> {
  634    751   
        /// Create an HTTPS client with the selected TLS provider.
  635    752   
        ///
  636    753   
        /// The trusted certificates will be loaded later when this becomes the selected
  637    754   
        /// HTTP client for a Smithy client.
  638    755   
        pub fn build_https(self) -> SharedHttpClient {
  639    756   
            build_with_conn_fn(
  640    757   
                self.client_builder,
  641    758   
                move |client_builder, settings, runtime_components| {
  642    759   
                    let builder = new_conn_builder(client_builder, settings, runtime_components)
  643    760   
                        .tls_provider(self.tls_provider.provider.clone())
  644    761   
                        .tls_context(self.tls_provider.context.clone());
  645    762   
                    builder.build()
  646    763   
                },
  647    764   
            )
  648    765   
        }
  649    766   
  650    767   
        /// Create an HTTPS client using a custom DNS resolver
  651    768   
        pub fn build_with_resolver(
  652    769   
            self,
  653    770   
            resolver: impl ResolveDns + Clone + 'static,
  654    771   
        ) -> SharedHttpClient {
  655    772   
            build_with_conn_fn(
  656    773   
                self.client_builder,
  657    774   
                move |client_builder, settings, runtime_components| {
  658    775   
                    let builder = new_conn_builder(client_builder, settings, runtime_components)
  659    776   
                        .tls_provider(self.tls_provider.provider.clone())
  660    777   
                        .tls_context(self.tls_provider.context.clone());
  661    778   
                    builder.build_with_resolver(resolver.clone())
  662    779   
                },
  663    780   
            )
  664    781   
        }
  665    782   
  666    783   
        /// Configure the TLS context
  667    784   
        pub fn tls_context(mut self, ctx: TlsContext) -> Self {
  668    785   
            self.tls_provider.context = ctx;
  669    786   
            self
  670    787   
        }
  671    788   
    }
  672    789   
}
  673    790   
  674    791   
impl Builder<TlsUnset> {
  675    792   
    /// Creates a new builder.
  676    793   
    pub fn new() -> Self {
  677    794   
        Self::default()
  678    795   
    }
  679    796   
         797  +
    /// Returns a [`SharedHttpClient`] that calls the given `connector` function to select an HTTP(S) connector.
         798  +
    #[doc(hidden)]
         799  +
    pub fn build_with_connector_fn<F>(self, connector_fn: F) -> SharedHttpClient
         800  +
    where
         801  +
        F: Fn(Option<&HttpConnectorSettings>, Option<&RuntimeComponents>) -> Connector
         802  +
            + Send
         803  +
            + Sync
         804  +
            + 'static,
         805  +
    {
         806  +
        build_with_conn_fn(
         807  +
            self.client_builder,
         808  +
            move |_builder, settings, runtime_components| {
         809  +
                connector_fn(settings, runtime_components)
         810  +
            },
         811  +
        )
         812  +
    }
         813  +
  680    814   
    /// Build a new HTTP client without TLS enabled
  681    815   
    #[doc(hidden)]
  682    816   
    pub fn build_http(self) -> SharedHttpClient {
  683    817   
        build_with_conn_fn(
  684    818   
            self.client_builder,
  685    819   
            move |client_builder, settings, runtime_components| {
  686    820   
                let builder = new_conn_builder(client_builder, settings, runtime_components);
  687    821   
                builder.build_http()
  688    822   
            },
  689    823   
        )
@@ -966,1100 +1026,1160 @@
  986   1120   
                let localhost_v4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
  987   1121   
                DnsFuture::ready(Ok(vec![localhost_v4]))
  988   1122   
            }
  989   1123   
        }
  990   1124   
  991   1125   
        let connector_settings = HttpConnectorSettings::builder()
  992   1126   
            .connect_timeout(Duration::from_secs(20))
  993   1127   
            .build();
  994   1128   
  995   1129   
        let resolver = HyperUtilResolver {
  996         -
            resolver: TestResolver::default(),
        1130  +
            resolver: TestResolver,
  997   1131   
        };
  998   1132   
        let connector = Connector::builder().base_connector_with_resolver(resolver);
  999   1133   
 1000   1134   
        let hyper = Connector::builder()
 1001   1135   
            .connector_settings(connector_settings)
 1002   1136   
            .sleep_impl(SharedAsyncSleep::new(TokioSleep::new()))
 1003   1137   
            .wrap_connector(connector)
 1004   1138   
            .adapter;
 1005   1139   
 1006   1140   
        let resp = hyper

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http-client/src/client/connect.rs

@@ -0,1 +0,164 @@
           1  +
/*
           2  +
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
           3  +
 * SPDX-License-Identifier: Apache-2.0
           4  +
 */
           5  +
use crate::client::connect;
           6  +
use crate::proxy;
           7  +
use aws_smithy_runtime_api::box_error::BoxError;
           8  +
use http_1x::Uri;
           9  +
use hyper::rt::{Read, ReadBufCursor, Write};
          10  +
use hyper_util::client::legacy::connect::{Connected, Connection};
          11  +
use pin_project_lite::pin_project;
          12  +
use std::future::Future;
          13  +
use std::io;
          14  +
use std::io::IoSlice;
          15  +
use std::pin::Pin;
          16  +
use std::task::{Context, Poll};
          17  +
          18  +
pub(crate) trait AsyncConn:
          19  +
    Read + Write + Connection + Send + Sync + Unpin + 'static
          20  +
{
          21  +
}
          22  +
          23  +
impl<T: Read + Write + Connection + Send + Sync + Unpin + 'static> AsyncConn for T {}
          24  +
          25  +
pub(crate) type BoxConn = Box<dyn AsyncConn>;
          26  +
          27  +
// Future for connecting
          28  +
pub(crate) type Connecting = Pin<Box<dyn Future<Output = Result<Conn, BoxError>> + Send>>;
          29  +
          30  +
pin_project! {
          31  +
    pub(crate) struct Conn {
          32  +
        #[pin]
          33  +
        pub(super)inner: BoxConn,
          34  +
        pub(super) is_proxy: bool,
          35  +
    }
          36  +
}
          37  +
          38  +
impl Connection for Conn {
          39  +
    fn connected(&self) -> Connected {
          40  +
        self.inner.connected().proxy(self.is_proxy)
          41  +
    }
          42  +
}
          43  +
          44  +
impl Read for Conn {
          45  +
    fn poll_read(
          46  +
        self: Pin<&mut Self>,
          47  +
        cx: &mut Context<'_>,
          48  +
        buf: ReadBufCursor<'_>,
          49  +
    ) -> Poll<io::Result<()>> {
          50  +
        let this = self.project();
          51  +
        Read::poll_read(this.inner, cx, buf)
          52  +
    }
          53  +
}
          54  +
          55  +
impl Write for Conn {
          56  +
    fn poll_write(
          57  +
        self: Pin<&mut Self>,
          58  +
        cx: &mut Context<'_>,
          59  +
        buf: &[u8],
          60  +
    ) -> Poll<Result<usize, io::Error>> {
          61  +
        let this = self.project();
          62  +
        Write::poll_write(this.inner, cx, buf)
          63  +
    }
          64  +
          65  +
    fn poll_write_vectored(
          66  +
        self: Pin<&mut Self>,
          67  +
        cx: &mut Context<'_>,
          68  +
        bufs: &[IoSlice<'_>],
          69  +
    ) -> Poll<Result<usize, io::Error>> {
          70  +
        let this = self.project();
          71  +
        Write::poll_write_vectored(this.inner, cx, bufs)
          72  +
    }
          73  +
          74  +
    fn is_write_vectored(&self) -> bool {
          75  +
        self.inner.is_write_vectored()
          76  +
    }
          77  +
          78  +
    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
          79  +
        let this = self.project();
          80  +
        Write::poll_flush(this.inner, cx)
          81  +
    }
          82  +
          83  +
    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
          84  +
        let this = self.project();
          85  +
        Write::poll_shutdown(this.inner, cx)
          86  +
    }
          87  +
}
          88  +
          89  +
/// HTTP-only proxy connector for handling HTTP requests through HTTP proxies
          90  +
///
          91  +
/// This connector handles the HTTP proxy logic when no TLS provider is selected,
          92  +
/// including request URL modification and proxy authentication.
          93  +
#[derive(Debug, Clone)]
          94  +
pub(crate) struct HttpProxyConnector<C> {
          95  +
    inner: C,
          96  +
    proxy_config: proxy::ProxyConfig,
          97  +
}
          98  +
          99  +
impl<C> HttpProxyConnector<C> {
         100  +
    pub(crate) fn new(inner: C, proxy_config: proxy::ProxyConfig) -> Self {
         101  +
        Self {
         102  +
            inner,
         103  +
            proxy_config,
         104  +
        }
         105  +
    }
         106  +
}
         107  +
         108  +
impl<C> tower::Service<Uri> for HttpProxyConnector<C>
         109  +
where
         110  +
    C: tower::Service<Uri> + Clone + Send + 'static,
         111  +
    C::Response: hyper::rt::Read
         112  +
        + hyper::rt::Write
         113  +
        + hyper_util::client::legacy::connect::Connection
         114  +
        + Send
         115  +
        + Sync
         116  +
        + Unpin
         117  +
        + 'static,
         118  +
    C::Future: Send + 'static,
         119  +
    C::Error: Into<BoxError>,
         120  +
{
         121  +
    type Response = connect::Conn;
         122  +
    type Error = BoxError;
         123  +
    type Future = connect::Connecting;
         124  +
         125  +
    fn poll_ready(
         126  +
        &mut self,
         127  +
        cx: &mut std::task::Context<'_>,
         128  +
    ) -> std::task::Poll<Result<(), Self::Error>> {
         129  +
        self.inner.poll_ready(cx).map_err(Into::into)
         130  +
    }
         131  +
         132  +
    fn call(&mut self, dst: Uri) -> Self::Future {
         133  +
        // Check if this request should be proxied
         134  +
        let proxy_intercept = if !self.proxy_config.is_disabled() {
         135  +
            let matcher = self.proxy_config.clone().into_hyper_util_matcher();
         136  +
            matcher.intercept(&dst)
         137  +
        } else {
         138  +
            None
         139  +
        };
         140  +
         141  +
        if let Some(intercept) = proxy_intercept {
         142  +
            // HTTP through proxy: Connect to proxy server
         143  +
            let proxy_uri = intercept.uri().clone();
         144  +
            let fut = self.inner.call(proxy_uri);
         145  +
            Box::pin(async move {
         146  +
                let conn = fut.await.map_err(Into::into)?;
         147  +
                Ok(connect::Conn {
         148  +
                    inner: Box::new(conn),
         149  +
                    is_proxy: true,
         150  +
                })
         151  +
            })
         152  +
        } else {
         153  +
            // Direct connection
         154  +
            let fut = self.inner.call(dst);
         155  +
            Box::pin(async move {
         156  +
                let conn = fut.await.map_err(Into::into)?;
         157  +
                Ok(connect::Conn {
         158  +
                    inner: Box::new(conn),
         159  +
                    is_proxy: false,
         160  +
                })
         161  +
            })
         162  +
        }
         163  +
    }
         164  +
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http-client/src/client/proxy.rs

@@ -0,1 +0,811 @@
           1  +
/*
           2  +
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
           3  +
 * SPDX-License-Identifier: Apache-2.0
           4  +
 */
           5  +
           6  +
//! Proxy configuration for HTTP clients
           7  +
//!
           8  +
//! This module provides types and utilities for configuring HTTP and HTTPS proxies,
           9  +
//! including support for environment variable detection, authentication, and bypass rules.
          10  +
          11  +
use http_1x::Uri;
          12  +
use hyper_util::client::proxy::matcher::Matcher;
          13  +
use std::fmt;
          14  +
          15  +
/// Proxy configuration for HTTP clients
          16  +
///
          17  +
/// Supports HTTP and HTTPS proxy configuration with authentication and bypass rules.
          18  +
/// Can be configured programmatically or automatically detected from environment variables.
          19  +
///
          20  +
/// # Examples
          21  +
///
          22  +
/// ```rust
          23  +
/// use aws_smithy_http_client::proxy::ProxyConfig;
          24  +
///
          25  +
/// // HTTP proxy for all traffic
          26  +
/// let config = ProxyConfig::http("http://proxy.example.com:8080")?;
          27  +
///
          28  +
/// // HTTPS traffic through HTTP proxy (common case - no TLS needed for proxy connection)
          29  +
/// let config = ProxyConfig::https("http://proxy.example.com:8080")?
          30  +
///     .with_basic_auth("username", "password")
          31  +
///     .no_proxy("localhost,*.internal");
          32  +
///
          33  +
/// // Detect from environment variables
          34  +
/// let config = ProxyConfig::from_env();
          35  +
/// # Ok::<(), Box<dyn std::error::Error>>(())
          36  +
/// ```
          37  +
#[derive(Debug, Clone)]
          38  +
pub struct ProxyConfig {
          39  +
    inner: ProxyConfigInner,
          40  +
}
          41  +
          42  +
/// Internal configuration representation
          43  +
#[derive(Debug, Clone)]
          44  +
enum ProxyConfigInner {
          45  +
    /// Use environment variable detection
          46  +
    FromEnvironment,
          47  +
    /// Explicit HTTP proxy
          48  +
    Http {
          49  +
        uri: Uri,
          50  +
        auth: Option<ProxyAuth>,
          51  +
        no_proxy: Option<String>,
          52  +
    },
          53  +
    /// Explicit HTTPS proxy
          54  +
    Https {
          55  +
        uri: Uri,
          56  +
        auth: Option<ProxyAuth>,
          57  +
        no_proxy: Option<String>,
          58  +
    },
          59  +
    /// Proxy for all traffic
          60  +
    All {
          61  +
        uri: Uri,
          62  +
        auth: Option<ProxyAuth>,
          63  +
        no_proxy: Option<String>,
          64  +
    },
          65  +
    /// Explicitly disabled
          66  +
    Disabled,
          67  +
}
          68  +
          69  +
/// Proxy authentication configuration
          70  +
///
          71  +
/// Stored for later conversion to hyper-util format.
          72  +
#[derive(Debug, Clone)]
          73  +
struct ProxyAuth {
          74  +
    /// Username for authentication
          75  +
    username: String,
          76  +
    /// Password for authentication
          77  +
    password: String,
          78  +
}
          79  +
          80  +
/// Errors that can occur during proxy configuration
          81  +
#[derive(Debug)]
          82  +
pub enum ProxyError {
          83  +
    /// Invalid proxy URL
          84  +
    InvalidUrl(String),
          85  +
    /// Environment variable parsing error
          86  +
    EnvVarError(String),
          87  +
}
          88  +
          89  +
impl fmt::Display for ProxyError {
          90  +
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
          91  +
        match self {
          92  +
            ProxyError::InvalidUrl(url) => write!(f, "invalid proxy URL: {}", url),
          93  +
            ProxyError::EnvVarError(msg) => write!(f, "environment variable error: {}", msg),
          94  +
        }
          95  +
    }
          96  +
}
          97  +
          98  +
impl std::error::Error for ProxyError {}
          99  +
         100  +
impl ProxyConfig {
         101  +
    /// Create a new proxy configuration for HTTP traffic only
         102  +
    ///
         103  +
    /// # Arguments
         104  +
    /// * `proxy_url` - The HTTP proxy URL (e.g., "http://proxy.example.com:8080")
         105  +
    ///
         106  +
    /// # Examples
         107  +
    /// ```rust
         108  +
    /// use aws_smithy_http_client::proxy::ProxyConfig;
         109  +
    ///
         110  +
    /// let config = ProxyConfig::http("http://proxy.example.com:8080")?;
         111  +
    /// # Ok::<(), Box<dyn std::error::Error>>(())
         112  +
    /// ```
         113  +
    pub fn http<U>(proxy_url: U) -> Result<Self, ProxyError>
         114  +
    where
         115  +
        U: TryInto<Uri>,
         116  +
        U::Error: fmt::Display,
         117  +
    {
         118  +
        let uri = proxy_url
         119  +
            .try_into()
         120  +
            .map_err(|e| ProxyError::InvalidUrl(e.to_string()))?;
         121  +
         122  +
        Self::validate_proxy_uri(&uri)?;
         123  +
         124  +
        Ok(ProxyConfig {
         125  +
            inner: ProxyConfigInner::Http {
         126  +
                uri,
         127  +
                auth: None,
         128  +
                no_proxy: None,
         129  +
            },
         130  +
        })
         131  +
    }
         132  +
         133  +
    /// Create a new proxy configuration for HTTPS traffic only
         134  +
    ///
         135  +
    /// This proxy will only be used for `https://` requests. HTTP requests
         136  +
    /// will connect directly unless a separate HTTP proxy is configured.
         137  +
    ///
         138  +
    /// The proxy URL itself can use either HTTP or HTTPS scheme:
         139  +
    /// - `http://proxy.example.com:8080` - Connect to proxy using HTTP (no TLS needed)
         140  +
    /// - `https://proxy.example.com:8080` - Connect to proxy using HTTPS (TLS required)
         141  +
    ///
         142  +
    /// **Note**: If the proxy URL itself uses HTTPS scheme, TLS support must be
         143  +
    /// available when building the connector, otherwise connections will fail.
         144  +
    ///
         145  +
    /// # Arguments
         146  +
    /// * `proxy_url` - The proxy URL (e.g., "http://proxy.example.com:8080" or "https://proxy.example.com:8080")
         147  +
    ///
         148  +
    /// # Examples
         149  +
    /// ```rust
         150  +
    /// use aws_smithy_http_client::proxy::ProxyConfig;
         151  +
    ///
         152  +
    /// // HTTPS traffic through HTTP proxy (no TLS needed for proxy connection)
         153  +
    /// let config = ProxyConfig::https("http://proxy.example.com:8080")?;
         154  +
    ///
         155  +
    /// // HTTPS traffic through HTTPS proxy (TLS needed for proxy connection)
         156  +
    /// let config = ProxyConfig::https("https://secure-proxy.example.com:8080")?;
         157  +
    /// # Ok::<(), Box<dyn std::error::Error>>(())
         158  +
    /// ```
         159  +
    pub fn https<U>(proxy_url: U) -> Result<Self, ProxyError>
         160  +
    where
         161  +
        U: TryInto<Uri>,
         162  +
        U::Error: fmt::Display,
         163  +
    {
         164  +
        let uri = proxy_url
         165  +
            .try_into()
         166  +
            .map_err(|e| ProxyError::InvalidUrl(e.to_string()))?;
         167  +
         168  +
        Self::validate_proxy_uri(&uri)?;
         169  +
         170  +
        Ok(ProxyConfig {
         171  +
            inner: ProxyConfigInner::Https {
         172  +
                uri,
         173  +
                auth: None,
         174  +
                no_proxy: None,
         175  +
            },
         176  +
        })
         177  +
    }
         178  +
         179  +
    /// Create a new proxy configuration for all HTTP and HTTPS traffic
         180  +
    ///
         181  +
    /// This proxy will be used for both `http://` and `https://` requests.
         182  +
    /// This is equivalent to setting both HTTP and HTTPS proxies to the same URL.
         183  +
    ///
         184  +
    /// **Note**: If the proxy URL itself uses HTTPS scheme, TLS support must be
         185  +
    /// available when building the connector, otherwise connections will fail.
         186  +
    ///
         187  +
    /// # Arguments
         188  +
    /// * `proxy_url` - The proxy URL (e.g., "http://proxy.example.com:8080")
         189  +
    ///
         190  +
    /// # Examples
         191  +
    /// ```rust
         192  +
    /// use aws_smithy_http_client::proxy::ProxyConfig;
         193  +
    ///
         194  +
    /// let config = ProxyConfig::all("http://proxy.example.com:8080")?;
         195  +
    /// # Ok::<(), Box<dyn std::error::Error>>(())
         196  +
    /// ```
         197  +
    pub fn all<U>(proxy_url: U) -> Result<Self, ProxyError>
         198  +
    where
         199  +
        U: TryInto<Uri>,
         200  +
        U::Error: fmt::Display,
         201  +
    {
         202  +
        let uri = proxy_url
         203  +
            .try_into()
         204  +
            .map_err(|e| ProxyError::InvalidUrl(e.to_string()))?;
         205  +
         206  +
        Self::validate_proxy_uri(&uri)?;
         207  +
         208  +
        Ok(ProxyConfig {
         209  +
            inner: ProxyConfigInner::All {
         210  +
                uri,
         211  +
                auth: None,
         212  +
                no_proxy: None,
         213  +
            },
         214  +
        })
         215  +
    }
         216  +
         217  +
    /// Create a proxy configuration that disables all proxy usage
         218  +
    ///
         219  +
    /// This is useful for explicitly disabling proxy support even when
         220  +
    /// environment variables are set.
         221  +
    ///
         222  +
    /// # Examples
         223  +
    /// ```rust
         224  +
    /// use aws_smithy_http_client::proxy::ProxyConfig;
         225  +
    ///
         226  +
    /// let config = ProxyConfig::disabled();
         227  +
    /// ```
         228  +
    pub fn disabled() -> Self {
         229  +
        ProxyConfig {
         230  +
            inner: ProxyConfigInner::Disabled,
         231  +
        }
         232  +
    }
         233  +
         234  +
    /// Add basic authentication to this proxy configuration
         235  +
    ///
         236  +
    /// # Arguments
         237  +
    /// * `username` - Username for proxy authentication
         238  +
    /// * `password` - Password for proxy authentication
         239  +
    ///
         240  +
    /// # Examples
         241  +
    /// ```rust
         242  +
    /// use aws_smithy_http_client::proxy::ProxyConfig;
         243  +
    ///
         244  +
    /// let config = ProxyConfig::http("http://proxy.example.com:8080")?
         245  +
    ///     .with_basic_auth("username", "password");
         246  +
    /// # Ok::<(), Box<dyn std::error::Error>>(())
         247  +
    /// ```
         248  +
    pub fn with_basic_auth<U, P>(mut self, username: U, password: P) -> Self
         249  +
    where
         250  +
        U: Into<String>,
         251  +
        P: Into<String>,
         252  +
    {
         253  +
        let auth = ProxyAuth {
         254  +
            username: username.into(),
         255  +
            password: password.into(),
         256  +
        };
         257  +
         258  +
        match &mut self.inner {
         259  +
            ProxyConfigInner::Http {
         260  +
                auth: ref mut a, ..
         261  +
            } => *a = Some(auth),
         262  +
            ProxyConfigInner::Https {
         263  +
                auth: ref mut a, ..
         264  +
            } => *a = Some(auth),
         265  +
            ProxyConfigInner::All {
         266  +
                auth: ref mut a, ..
         267  +
            } => *a = Some(auth),
         268  +
            ProxyConfigInner::FromEnvironment | ProxyConfigInner::Disabled => {
         269  +
                // Cannot add auth to environment or disabled configs
         270  +
            }
         271  +
        }
         272  +
         273  +
        self
         274  +
    }
         275  +
         276  +
    /// Add NO_PROXY rules to this configuration
         277  +
    ///
         278  +
    /// NO_PROXY rules specify hosts that should bypass the proxy and connect directly.
         279  +
    ///
         280  +
    /// # Arguments
         281  +
    /// * `rules` - Comma-separated list of bypass rules
         282  +
    ///
         283  +
    /// # Examples
         284  +
    /// ```rust
         285  +
    /// use aws_smithy_http_client::proxy::ProxyConfig;
         286  +
    ///
         287  +
    /// let config = ProxyConfig::http("http://proxy.example.com:8080")?
         288  +
    ///     .no_proxy("localhost,127.0.0.1,*.internal,10.0.0.0/8");
         289  +
    /// # Ok::<(), Box<dyn std::error::Error>>(())
         290  +
    /// ```
         291  +
    pub fn no_proxy<S: AsRef<str>>(mut self, rules: S) -> Self {
         292  +
        let rules_str = rules.as_ref().to_string();
         293  +
         294  +
        match &mut self.inner {
         295  +
            ProxyConfigInner::Http {
         296  +
                no_proxy: ref mut n,
         297  +
                ..
         298  +
            } => *n = Some(rules_str),
         299  +
            ProxyConfigInner::Https {
         300  +
                no_proxy: ref mut n,
         301  +
                ..
         302  +
            } => *n = Some(rules_str),
         303  +
            ProxyConfigInner::All {
         304  +
                no_proxy: ref mut n,
         305  +
                ..
         306  +
            } => *n = Some(rules_str),
         307  +
            ProxyConfigInner::FromEnvironment | ProxyConfigInner::Disabled => {
         308  +
                // Cannot add no_proxy to environment or disabled configs
         309  +
                // Environment configs will use NO_PROXY env var
         310  +
                // FIXME - is this what we want?
         311  +
            }
         312  +
        }
         313  +
         314  +
        self
         315  +
    }
         316  +
         317  +
    /// Create proxy configuration from environment variables
         318  +
    ///
         319  +
    /// Reads standard proxy environment variables:
         320  +
    /// - `HTTP_PROXY` / `http_proxy`: HTTP proxy URL
         321  +
    /// - `HTTPS_PROXY` / `https_proxy`: HTTPS proxy URL
         322  +
    /// - `ALL_PROXY` / `all_proxy`: Proxy for all protocols (fallback)
         323  +
    /// - `NO_PROXY` / `no_proxy`: Comma-separated bypass rules
         324  +
    ///
         325  +
    /// If no proxy environment variables are set, this returns a configuration
         326  +
    /// that won't intercept any requests (equivalent to no proxy).
         327  +
    ///
         328  +
    /// # Examples
         329  +
    /// ```rust
         330  +
    /// use aws_smithy_http_client::proxy::ProxyConfig;
         331  +
    ///
         332  +
    /// // Always succeeds, even if no environment variables are set
         333  +
    /// let config = ProxyConfig::from_env();
         334  +
    /// ```
         335  +
    pub fn from_env() -> Self {
         336  +
        // Delegate to environment variable parsing
         337  +
        // If no env vars are set, creates a matcher that doesn't intercept anything
         338  +
        ProxyConfig {
         339  +
            inner: ProxyConfigInner::FromEnvironment,
         340  +
        }
         341  +
    }
         342  +
         343  +
    /// Check if proxy is disabled (no proxy configuration)
         344  +
    pub fn is_disabled(&self) -> bool {
         345  +
        matches!(self.inner, ProxyConfigInner::Disabled)
         346  +
    }
         347  +
         348  +
    /// Check if this configuration uses environment variables
         349  +
    pub fn is_from_env(&self) -> bool {
         350  +
        matches!(self.inner, ProxyConfigInner::FromEnvironment)
         351  +
    }
         352  +
         353  +
    /// Convert this configuration to internal proxy matcher
         354  +
    ///
         355  +
    /// This method converts the user-friendly configuration to the internal
         356  +
    /// proxy matching implementation used by the HTTP client.
         357  +
    pub(crate) fn into_hyper_util_matcher(self) -> Matcher {
         358  +
        match self.inner {
         359  +
            ProxyConfigInner::FromEnvironment => Matcher::from_env(),
         360  +
            ProxyConfigInner::Http {
         361  +
                uri,
         362  +
                auth,
         363  +
                no_proxy,
         364  +
            } => {
         365  +
                let mut builder = Matcher::builder();
         366  +
         367  +
                // Set HTTP proxy with authentication embedded in URL if present
         368  +
                let proxy_url = Self::build_proxy_url(uri, auth);
         369  +
                builder = builder.http(proxy_url);
         370  +
         371  +
                // Add NO_PROXY rules if present
         372  +
                if let Some(no_proxy_rules) = no_proxy {
         373  +
                    builder = builder.no(no_proxy_rules);
         374  +
                }
         375  +
         376  +
                builder.build()
         377  +
            }
         378  +
            ProxyConfigInner::Https {
         379  +
                uri,
         380  +
                auth,
         381  +
                no_proxy,
         382  +
            } => {
         383  +
                let mut builder = Matcher::builder();
         384  +
         385  +
                // Set HTTPS proxy with authentication embedded in URL if present
         386  +
                let proxy_url = Self::build_proxy_url(uri, auth);
         387  +
                builder = builder.https(proxy_url);
         388  +
         389  +
                // Add NO_PROXY rules if present
         390  +
                if let Some(no_proxy_rules) = no_proxy {
         391  +
                    builder = builder.no(no_proxy_rules);
         392  +
                }
         393  +
         394  +
                builder.build()
         395  +
            }
         396  +
            ProxyConfigInner::All {
         397  +
                uri,
         398  +
                auth,
         399  +
                no_proxy,
         400  +
            } => {
         401  +
                let mut builder = Matcher::builder();
         402  +
         403  +
                // Set proxy for all traffic with authentication embedded in URL if present
         404  +
                let proxy_url = Self::build_proxy_url(uri, auth);
         405  +
                builder = builder.all(proxy_url);
         406  +
         407  +
                // Add NO_PROXY rules if present
         408  +
                if let Some(no_proxy_rules) = no_proxy {
         409  +
                    builder = builder.no(no_proxy_rules);
         410  +
                }
         411  +
         412  +
                builder.build()
         413  +
            }
         414  +
            ProxyConfigInner::Disabled => {
         415  +
                // Create an empty matcher that won't intercept anything
         416  +
                Matcher::builder().build()
         417  +
            }
         418  +
        }
         419  +
    }
         420  +
         421  +
    /// Check if this proxy configuration requires TLS support
         422  +
    ///
         423  +
    /// Returns true if any of the configured proxy URLs use HTTPS scheme,
         424  +
    /// which requires TLS to establish the connection to the proxy server.
         425  +
    pub(crate) fn requires_tls(&self) -> bool {
         426  +
        match &self.inner {
         427  +
            ProxyConfigInner::Http { uri, .. } => uri.scheme_str() == Some("https"),
         428  +
            ProxyConfigInner::Https { uri, .. } => uri.scheme_str() == Some("https"),
         429  +
            ProxyConfigInner::All { uri, .. } => uri.scheme_str() == Some("https"),
         430  +
            ProxyConfigInner::FromEnvironment => {
         431  +
                // Check environment variables for HTTPS proxy URLs
         432  +
                Self::env_vars_require_tls()
         433  +
            }
         434  +
            ProxyConfigInner::Disabled => false,
         435  +
        }
         436  +
    }
         437  +
         438  +
    /// Check if any environment proxy variables contain HTTPS URLs
         439  +
    fn env_vars_require_tls() -> bool {
         440  +
        let proxy_vars = [
         441  +
            "HTTP_PROXY",
         442  +
            "http_proxy",
         443  +
            "HTTPS_PROXY",
         444  +
            "https_proxy",
         445  +
            "ALL_PROXY",
         446  +
            "all_proxy",
         447  +
        ];
         448  +
         449  +
        for var in &proxy_vars {
         450  +
            if let Ok(proxy_url) = std::env::var(var) {
         451  +
                if !proxy_url.is_empty() {
         452  +
                    // Simple check for https:// scheme
         453  +
                    if proxy_url.starts_with("https://") {
         454  +
                        return true;
         455  +
                    }
         456  +
                }
         457  +
            }
         458  +
        }
         459  +
        false
         460  +
    }
         461  +
         462  +
    fn validate_proxy_uri(uri: &Uri) -> Result<(), ProxyError> {
         463  +
        // Validate scheme
         464  +
        match uri.scheme_str() {
         465  +
            Some("http") | Some("https") => {}
         466  +
            Some(scheme) => {
         467  +
                return Err(ProxyError::InvalidUrl(format!(
         468  +
                    "unsupported proxy scheme: {}",
         469  +
                    scheme
         470  +
                )));
         471  +
            }
         472  +
            None => {
         473  +
                return Err(ProxyError::InvalidUrl(
         474  +
                    "proxy URL must include scheme (http:// or https://)".to_string(),
         475  +
                ));
         476  +
            }
         477  +
        }
         478  +
         479  +
        // Validate host
         480  +
        if uri.host().is_none() {
         481  +
            return Err(ProxyError::InvalidUrl(
         482  +
                "proxy URL must include host".to_string(),
         483  +
            ));
         484  +
        }
         485  +
         486  +
        Ok(())
         487  +
    }
         488  +
         489  +
    fn build_proxy_url(uri: Uri, auth: Option<ProxyAuth>) -> String {
         490  +
        let uri_str = uri.to_string();
         491  +
         492  +
        if let Some(auth) = auth {
         493  +
            // Embed authentication in the URL: scheme://username:password@host:port/path
         494  +
            if let Some(scheme_end) = uri_str.find("://") {
         495  +
                let scheme = &uri_str[..scheme_end + 3];
         496  +
                let rest = &uri_str[scheme_end + 3..];
         497  +
         498  +
                // Check if auth is already present in the URI
         499  +
                if rest.contains('@') {
         500  +
                    // Auth already present, return as-is
         501  +
                    uri_str
         502  +
                } else {
         503  +
                    // Add auth to the URI
         504  +
                    format!("{}{}:{}@{}", scheme, auth.username, auth.password, rest)
         505  +
                }
         506  +
            } else {
         507  +
                // Invalid URI format, return as-is
         508  +
                uri_str
         509  +
            }
         510  +
        } else {
         511  +
            // No authentication, return URI as-is
         512  +
            uri_str
         513  +
        }
         514  +
    }
         515  +
}
         516  +
         517  +
#[cfg(test)]
         518  +
mod tests {
         519  +
    use super::*;
         520  +
    use std::env;
         521  +
         522  +
    #[test]
         523  +
    fn test_proxy_config_http() {
         524  +
        let config = ProxyConfig::http("http://proxy.example.com:8080").unwrap();
         525  +
        assert!(!config.is_disabled());
         526  +
        assert!(!config.is_from_env());
         527  +
    }
         528  +
         529  +
    #[test]
         530  +
    fn test_proxy_config_https() {
         531  +
        let config = ProxyConfig::https("http://proxy.example.com:8080").unwrap();
         532  +
        assert!(!config.is_disabled());
         533  +
        assert!(!config.is_from_env());
         534  +
    }
         535  +
         536  +
    #[test]
         537  +
    fn test_proxy_config_all() {
         538  +
        let config = ProxyConfig::all("http://proxy.example.com:8080").unwrap();
         539  +
        assert!(!config.is_disabled());
         540  +
        assert!(!config.is_from_env());
         541  +
    }
         542  +
         543  +
    #[test]
         544  +
    fn test_proxy_config_disabled() {
         545  +
        let config = ProxyConfig::disabled();
         546  +
        assert!(config.is_disabled());
         547  +
        assert!(!config.is_from_env());
         548  +
    }
         549  +
         550  +
    #[test]
         551  +
    fn test_proxy_config_with_auth() {
         552  +
        let config = ProxyConfig::http("http://proxy.example.com:8080")
         553  +
            .unwrap()
         554  +
            .with_basic_auth("user", "pass");
         555  +
         556  +
        // Auth is stored internally
         557  +
        assert!(!config.is_disabled());
         558  +
    }
         559  +
         560  +
    #[test]
         561  +
    fn test_proxy_config_with_no_proxy() {
         562  +
        let config = ProxyConfig::http("http://proxy.example.com:8080")
         563  +
            .unwrap()
         564  +
            .no_proxy("localhost,*.internal");
         565  +
         566  +
        // NO_PROXY rules are stored internally
         567  +
        assert!(!config.is_disabled());
         568  +
    }
         569  +
         570  +
    #[test]
         571  +
    fn test_proxy_config_invalid_url() {
         572  +
        let result = ProxyConfig::http("not-a-url");
         573  +
        assert!(result.is_err());
         574  +
    }
         575  +
         576  +
    #[test]
         577  +
    fn test_proxy_config_invalid_scheme() {
         578  +
        let result = ProxyConfig::http("ftp://proxy.example.com:8080");
         579  +
        assert!(result.is_err());
         580  +
    }
         581  +
         582  +
    #[test]
         583  +
    #[serial_test::serial]
         584  +
    fn test_proxy_config_from_env_with_vars() {
         585  +
        // Save original environment
         586  +
        let original_http = env::var("HTTP_PROXY");
         587  +
         588  +
        // Set test environment
         589  +
        env::set_var("HTTP_PROXY", "http://test-proxy:8080");
         590  +
         591  +
        let config = ProxyConfig::from_env();
         592  +
        assert!(config.is_from_env());
         593  +
         594  +
        // Restore original environment
         595  +
        match original_http {
         596  +
            Ok(val) => env::set_var("HTTP_PROXY", val),
         597  +
            Err(_) => env::remove_var("HTTP_PROXY"),
         598  +
        }
         599  +
    }
         600  +
         601  +
    #[test]
         602  +
    #[serial_test::serial]
         603  +
    fn test_proxy_config_from_env_without_vars() {
         604  +
        // Save original environment
         605  +
        let original_vars: Vec<_> = [
         606  +
            "HTTP_PROXY",
         607  +
            "http_proxy",
         608  +
            "HTTPS_PROXY",
         609  +
            "https_proxy",
         610  +
            "ALL_PROXY",
         611  +
            "all_proxy",
         612  +
        ]
         613  +
        .iter()
         614  +
        .map(|var| (*var, env::var(var)))
         615  +
        .collect();
         616  +
         617  +
        // Clear all proxy environment variables
         618  +
        for (var, _) in &original_vars {
         619  +
            env::remove_var(var);
         620  +
        }
         621  +
         622  +
        let config = ProxyConfig::from_env();
         623  +
        assert!(config.is_from_env());
         624  +
         625  +
        // Restore original environment
         626  +
        for (var, original_value) in original_vars {
         627  +
            match original_value {
         628  +
                Ok(val) => env::set_var(var, val),
         629  +
                Err(_) => env::remove_var(var),
         630  +
            }
         631  +
        }
         632  +
    }
         633  +
         634  +
    #[test]
         635  +
    #[serial_test::serial]
         636  +
    fn test_auth_cannot_be_added_to_env_config() {
         637  +
        // Save original environment
         638  +
        let original_http = env::var("HTTP_PROXY");
         639  +
        env::set_var("HTTP_PROXY", "http://test-proxy:8080");
         640  +
         641  +
        let config = ProxyConfig::from_env().with_basic_auth("user", "pass"); // This should be ignored
         642  +
         643  +
        assert!(config.is_from_env());
         644  +
         645  +
        // Restore original environment
         646  +
        match original_http {
         647  +
            Ok(val) => env::set_var("HTTP_PROXY", val),
         648  +
            Err(_) => env::remove_var("HTTP_PROXY"),
         649  +
        }
         650  +
    }
         651  +
         652  +
    #[test]
         653  +
    #[serial_test::serial]
         654  +
    fn test_no_proxy_cannot_be_added_to_env_config() {
         655  +
        // Save original environment
         656  +
        let original_http = env::var("HTTP_PROXY");
         657  +
        env::set_var("HTTP_PROXY", "http://test-proxy:8080");
         658  +
         659  +
        let config = ProxyConfig::from_env().no_proxy("localhost"); // This should be ignored
         660  +
         661  +
        assert!(config.is_from_env());
         662  +
         663  +
        // Restore original environment
         664  +
        match original_http {
         665  +
            Ok(val) => env::set_var("HTTP_PROXY", val),
         666  +
            Err(_) => env::remove_var("HTTP_PROXY"),
         667  +
        }
         668  +
    }
         669  +
         670  +
    #[test]
         671  +
    fn test_build_proxy_url_without_auth() {
         672  +
        let uri = "http://proxy.example.com:8080".parse().unwrap();
         673  +
        let url = ProxyConfig::build_proxy_url(uri, None);
         674  +
        assert_eq!(url, "http://proxy.example.com:8080/");
         675  +
    }
         676  +
         677  +
    #[test]
         678  +
    fn test_build_proxy_url_with_auth() {
         679  +
        let uri = "http://proxy.example.com:8080".parse().unwrap();
         680  +
        let auth = ProxyAuth {
         681  +
            username: "user".to_string(),
         682  +
            password: "pass".to_string(),
         683  +
        };
         684  +
        let url = ProxyConfig::build_proxy_url(uri, Some(auth));
         685  +
        assert_eq!(url, "http://user:pass@proxy.example.com:8080/");
         686  +
    }
         687  +
         688  +
    #[test]
         689  +
    fn test_build_proxy_url_with_existing_auth() {
         690  +
        let uri = "http://existing:creds@proxy.example.com:8080"
         691  +
            .parse()
         692  +
            .unwrap();
         693  +
        let auth = ProxyAuth {
         694  +
            username: "user".to_string(),
         695  +
            password: "pass".to_string(),
         696  +
        };
         697  +
        let url = ProxyConfig::build_proxy_url(uri, Some(auth));
         698  +
        // Should not override existing auth
         699  +
        assert_eq!(url, "http://existing:creds@proxy.example.com:8080/");
         700  +
    }
         701  +
         702  +
    #[test]
         703  +
    #[serial_test::serial]
         704  +
    fn test_into_hyper_util_matcher_from_env() {
         705  +
        // Save original environment
         706  +
        let original_http = env::var("HTTP_PROXY");
         707  +
        env::set_var("HTTP_PROXY", "http://test-proxy:8080");
         708  +
         709  +
        let config = ProxyConfig::from_env();
         710  +
        let matcher = config.into_hyper_util_matcher();
         711  +
         712  +
        // Test that the matcher intercepts HTTP requests
         713  +
        let test_uri = "http://example.com".parse().unwrap();
         714  +
        let intercept = matcher.intercept(&test_uri);
         715  +
        assert!(intercept.is_some());
         716  +
         717  +
        // Restore original environment
         718  +
        match original_http {
         719  +
            Ok(val) => env::set_var("HTTP_PROXY", val),
         720  +
            Err(_) => env::remove_var("HTTP_PROXY"),
         721  +
        }
         722  +
    }
         723  +
         724  +
    #[test]
         725  +
    fn test_into_hyper_util_matcher_http() {
         726  +
        let config = ProxyConfig::http("http://proxy.example.com:8080").unwrap();
         727  +
        let matcher = config.into_hyper_util_matcher();
         728  +
         729  +
        // Test that the matcher intercepts HTTP requests
         730  +
        let test_uri = "http://example.com".parse().unwrap();
         731  +
        let intercept = matcher.intercept(&test_uri);
         732  +
        assert!(intercept.is_some());
         733  +
        // The intercept URI might be normalized
         734  +
        assert!(intercept
         735  +
            .unwrap()
         736  +
            .uri()
         737  +
            .to_string()
         738  +
            .starts_with("http://proxy.example.com:8080"));
         739  +
         740  +
        // Test that it doesn't intercept HTTPS requests
         741  +
        let https_uri = "https://example.com".parse().unwrap();
         742  +
        let https_intercept = matcher.intercept(&https_uri);
         743  +
        assert!(https_intercept.is_none());
         744  +
    }
         745  +
         746  +
    #[test]
         747  +
    fn test_into_hyper_util_matcher_with_auth() {
         748  +
        let config = ProxyConfig::http("http://proxy.example.com:8080")
         749  +
            .unwrap()
         750  +
            .with_basic_auth("user", "pass");
         751  +
        let matcher = config.into_hyper_util_matcher();
         752  +
         753  +
        // Test that the matcher intercepts HTTP requests
         754  +
        let test_uri = "http://example.com".parse().unwrap();
         755  +
        let intercept = matcher.intercept(&test_uri);
         756  +
        assert!(intercept.is_some());
         757  +
         758  +
        let intercept = intercept.unwrap();
         759  +
        // The proxy URI should contain the host (auth is handled separately)
         760  +
        assert!(intercept
         761  +
            .uri()
         762  +
            .to_string()
         763  +
            .contains("proxy.example.com:8080"));
         764  +
         765  +
        // Test that basic auth is available
         766  +
        assert!(intercept.basic_auth().is_some());
         767  +
    }
         768  +
         769  +
    #[test]
         770  +
    fn test_into_hyper_util_matcher_disabled() {
         771  +
        let config = ProxyConfig::disabled();
         772  +
        let matcher = config.into_hyper_util_matcher();
         773  +
         774  +
        // Test that the matcher doesn't intercept any requests
         775  +
        let test_uri = "http://example.com".parse().unwrap();
         776  +
        let intercept = matcher.intercept(&test_uri);
         777  +
        assert!(intercept.is_none());
         778  +
    }
         779  +
         780  +
    #[test]
         781  +
    #[serial_test::serial]
         782  +
    fn test_requires_tls_detection() {
         783  +
        // HTTP proxy should not require TLS
         784  +
        let http_config = ProxyConfig::http("http://proxy.example.com:8080").unwrap();
         785  +
        assert!(!http_config.requires_tls());
         786  +
         787  +
        // HTTPS proxy URL should require TLS
         788  +
        let https_config = ProxyConfig::http("https://proxy.example.com:8080").unwrap();
         789  +
        assert!(https_config.requires_tls());
         790  +
         791  +
        // All proxy with HTTP URL should not require TLS
         792  +
        let all_http_config = ProxyConfig::all("http://proxy.example.com:8080").unwrap();
         793  +
        assert!(!all_http_config.requires_tls());
         794  +
         795  +
        // Environment config with HTTPS proxy should require TLS
         796  +
        env::set_var("HTTP_PROXY", "https://proxy.example.com:8080");
         797  +
        let env_config = ProxyConfig::from_env();
         798  +
        assert!(env_config.requires_tls()); // Now detects HTTPS in env vars
         799  +
        env::remove_var("HTTP_PROXY");
         800  +
         801  +
        // Environment config with HTTP proxy should not require TLS
         802  +
        env::set_var("HTTP_PROXY", "http://proxy.example.com:8080");
         803  +
        let env_config = ProxyConfig::from_env();
         804  +
        assert!(!env_config.requires_tls());
         805  +
        env::remove_var("HTTP_PROXY");
         806  +
         807  +
        // Disabled config should not require TLS
         808  +
        let disabled_config = ProxyConfig::disabled();
         809  +
        assert!(!disabled_config.requires_tls());
         810  +
    }
         811  +
}