Repository: googleapis/google-http-java-client
Pull request #1990 (2024-08-16 18:32:10)

deps: update dependency io.grpc:grpc-context to v1.66.0
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [io.grpc:grpc-context](https://togithub.com/grpc/grpc-java) | `1.60.1` -> `1.66.0` | [![age](https://developer.mend.io/api/mc/badges/age/maven/io.grpc:grpc-context/1.66.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/maven/io.grpc:grpc-context/1.66.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/maven/io.grpc:grpc-context/1.60.1/1.66.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/io.grpc:grpc-context/1.60.1/1.66.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>grpc/grpc-java (io.grpc:grpc-context)</summary>

### [`v1.66.0`](https://togithub.com/grpc/grpc-java/compare/v1.65.1...v1.66.0)

### [`v1.65.1`](https://togithub.com/grpc/grpc-java/compare/v1.65.0...v1.65.1)

### [`v1.65.0`](https://togithub.com/grpc/grpc-java/compare/v1.64.0...v1.65.0)

### [`v1.64.2`](https://togithub.com/grpc/grpc-java/releases/tag/v1.64.2)

#### What's Changed

-   netty: Restore old behavior of NettyAdaptiveCumulator, but avoid using that class if Netty is on version 4.1.111 or later

### [`v1.64.1`](https://togithub.com/grpc/grpc-java/releases/tag/v1.64.1)

##### What's Changed

-   netty:Fix Netty composite buffer merging to be compatible with Netty 4.1.111 (1.64.x backport) by [@&#8203;larry-safran](https://togithub.com/larry-safran) in [https://github.com/grpc/grpc-java/pull/11303](https://togithub.com/grpc/grpc-java/pull/11303)

### [`v1.64.0`](https://togithub.com/grpc/grpc-java/releases/tag/v1.64.0)

*Avoid upgrading your application to Netty 4.1.111, with this version as there is a possible corruption. For Netty 4.1.111 compatibility, it is best to use 1.63.2, 1.64.2, 1.65.1, and later. See [https://github.com/grpc/grpc-java/issues/11284](https://togithub.com/grpc/grpc-java/issues/11284) .*

#### API Changes

-   compiler: the option `jakarta_omit` was renamed `@generated=omit` ([#&#8203;11086](https://togithub.com/grpc/grpc-java/issues/11086)) ([`8a21afc`](https://togithub.com/grpc/grpc-java/commit/8a21afcc9))

#### New Features

-   New API LoadBalancer.getChannelTarget() ([`4561bb5`](https://togithub.com/grpc/grpc-java/commit/4561bb5b8))
-   opentelemetry: Publish new module grpc-opentelemetry ([`5ba1a55`](https://togithub.com/grpc/grpc-java/commit/5ba1a5563)). The feature is still missing documentation and an example. It only supports metrics; tracing and logs will be future enhancements. See [gRFC A66](https://togithub.com/grpc/proposal/blob/master/A66-otel-stats.md)
-   bazel: Add support for bzlmod ([#&#8203;11046](https://togithub.com/grpc/grpc-java/issues/11046)) ([`d1890c0`](https://togithub.com/grpc/grpc-java/commit/d1890c0ac))
-   bazel: Replace usages of the old compatibility maven targets with `@maven` targets ([`0064991`](https://togithub.com/grpc/grpc-java/commit/00649913b))
-   okhttp: Support serverBuilder.maxConcurrentCallsPerConnection (Fixes [#&#8203;11062](https://togithub.com/grpc/grpc-java/issues/11062)). ([#&#8203;11063](https://togithub.com/grpc/grpc-java/issues/11063)) ([`8050723`](https://togithub.com/grpc/grpc-java/commit/805072339))
-   xds: Experimental metrics recording in WRR LB ([`06df25b`](https://togithub.com/grpc/grpc-java/commit/06df25b65), [`35a171b`](https://togithub.com/grpc/grpc-java/commit/35a171bc1), [`2897b39`](https://togithub.com/grpc/grpc-java/commit/2897b3939)), to be exported by grpc-opentelemetry if explicitly enabled in GrpcOpenTelemetry. See [gRFC A78](https://togithub.com/grpc/proposal/blob/master/A78-grpc-metrics-wrr-pf-xds.md)
-   rls: Experimental metrics recording in RLS LB ([`a9fb272`](https://togithub.com/grpc/grpc-java/commit/a9fb272b7), [`a1d1932`](https://togithub.com/grpc/grpc-java/commit/a1d19327f), [`8133318`](https://togithub.com/grpc/grpc-java/commit/813331837)), to be exported by grpc-opentelemetry if explicitly enabled in GrpcOpenTelemetry

#### Improvements

-   examples: support bazel build for retry policy example ([`58de563`](https://togithub.com/grpc/grpc-java/commit/58de563fa))
-   netty: Allow deframer errors to close stream with a status code, as long as headers have not yet been sent ([`e036b1b`](https://togithub.com/grpc/grpc-java/commit/e036b1b19)). This will greatly improve the debuggability of certain server errors in particular cases. Instead of the client seeing “CANCELLED: RST_STREAM closed stream. HTTP/2 error code: CANCEL”, they could see “RESOURCE_EXHAUSTED: gRPC message exceeds maximum size [`4194304`](https://togithub.com/grpc/grpc-java/commit/4194304): [`6144592`](https://togithub.com/grpc/grpc-java/commit/6144592)”
-   netty: Improve handling of unexpected write queue promise failures ([#&#8203;11016](https://togithub.com/grpc/grpc-java/issues/11016))
-   servlet: Avoid unnecessary FINEST hex string conversion by checking log level. Fixes [#&#8203;11031](https://togithub.com/grpc/grpc-java/issues/11031). ([`f7ee5f3`](https://togithub.com/grpc/grpc-java/commit/f7ee5f318))
-   StatusException/StatusRuntimeException hide stack trace in a simpler way ([#&#8203;11064](https://togithub.com/grpc/grpc-java/issues/11064)) ([`e36f099`](https://togithub.com/grpc/grpc-java/commit/e36f099be))
-   util: Status desc for outlier detection ejection ([#&#8203;11036](https://togithub.com/grpc/grpc-java/issues/11036)) ([`10cb4a3`](https://togithub.com/grpc/grpc-java/commit/10cb4a3be))
-   binder: Helper class to allow in process servers to use peer uids in test ([#&#8203;11014](https://togithub.com/grpc/grpc-java/issues/11014)) ([`537dbe8`](https://togithub.com/grpc/grpc-java/commit/537dbe826))
-   Add `load()` statements for the Bazel builtin top-level java symbols ([#&#8203;11105](https://togithub.com/grpc/grpc-java/issues/11105)) ([`add8c37`](https://togithub.com/grpc/grpc-java/commit/add8c37a4))
-   Add `StatusProto.toStatusException` overload to accept `Throwable` ([#&#8203;11083](https://togithub.com/grpc/grpc-java/issues/11083)) ([`5c9b492`](https://togithub.com/grpc/grpc-java/commit/5c9b49231))

#### Bug fixes

-   Fix retry race condition that can lead to double decrementing inFlightSubStreams and so miss calling closed ([#&#8203;11026](https://togithub.com/grpc/grpc-java/issues/11026)) ([`bdb6230`](https://togithub.com/grpc/grpc-java/commit/bdb623031))
-   Change defaults to use the older PickFirstLoadBalancer and disable Happy Eyeballs.  This disables a performance optimization added in v1.63. ([#&#8203;11120](https://togithub.com/grpc/grpc-java/issues/11120)) We have had a report that the new implementation can trigger a NullPointerException
-   core: Transition to CONNECTING immediately when exiting idle ([`2c5f0c2`](https://togithub.com/grpc/grpc-java/commit/2c5f0c22c)). Previously the visible state change from `channel.getState()` was delayed until the name resolver returned results. This had no impact to RPC behavior
-   xds: Specify a locale for upper/lower case conversions ([`e630593`](https://togithub.com/grpc/grpc-java/commit/e6305930d))
-   rls: Synchronization fixes in CachingRlsLbClient ([`6e97b18`](https://togithub.com/grpc/grpc-java/commit/6e97b180b)). These races had not been witnessed in practice
-   rls: Guarantee backoff will update RLS picker ([`f9b6e5f`](https://togithub.com/grpc/grpc-java/commit/f9b6e5f92)). This fixes a regression introduced by [`6e97b18`](https://togithub.com/grpc/grpc-java/commit/6e97b180b) that could hang RPCs instead of using fallback, but fixes a pre-existing bug that could greatly delay RPCs from using fallback.
-   rls: Fix time handling in CachingRlsLbClient ([`da619e2`](https://togithub.com/grpc/grpc-java/commit/da619e2bd)). This could have caused backoff entries to improperly be considered expired
-   xds: Properly disable the default endpoint identification algorithm with XdsChannelCredentials ([`097a46b`](https://togithub.com/grpc/grpc-java/commit/097a46b76)). The credential does its own verification and the default needs to be disabled for SPIFFE
-   netty: Release SendGrpcFrameCommand when stream is missing ([#&#8203;11116](https://togithub.com/grpc/grpc-java/issues/11116)) ([`fb9a108`](https://togithub.com/grpc/grpc-java/commit/fb9a10809))
-   okhttp: Remove finished stream even if a pending stream was started ([`d21fe32`](https://togithub.com/grpc/grpc-java/commit/d21fe32be))

#### Dependencies

-   cronet: Update Cronet to latest release + Move to Stable Cronet APIs. ([`5a8da19`](https://togithub.com/grpc/grpc-java/commit/5a8da19f3))
-   cronet: [@&#8203;javadoc](https://togithub.com/javadoc) update android permission MODIFY_NETWORK_ACCOUNTING (deprecated) => UPDATE_DEVICE_STATS ([`c703a1e`](https://togithub.com/grpc/grpc-java/commit/c703a1ee0))
-   cronet: Update to Java-8 API's and tighten the scopes ([`163efa3`](https://togithub.com/grpc/grpc-java/commit/163efa371))
-   cronet: Update to StandardCharsets and assertNotNull API's ([`77e59b2`](https://togithub.com/grpc/grpc-java/commit/77e59b29d))

#### Acknowledgements

[@&#8203;panchenko](https://togithub.com/panchenko)
[@&#8203;Ashok-Varma](https://togithub.com/Ashok-Varma)
[@&#8203;benjaminp](https://togithub.com/benjaminp)
[@&#8203;AutomatedTester](https://togithub.com/AutomatedTester)
[@&#8203;hypnoce](https://togithub.com/hypnoce)
[@&#8203;keith](https://togithub.com/keith)
[@&#8203;laglangyue](https://togithub.com/laglangyue)
[@&#8203;rostik404](https://togithub.com/rostik404)
[@&#8203;ryanpbrewster](https://togithub.com/ryanpbrewster)
[@&#8203;abtom](https://togithub.com/abtom)
[@&#8203;hvadehra](https://togithub.com/hvadehra)
[@&#8203;rtadepalli](https://togithub.com/rtadepalli)

### [`v1.63.2`](https://togithub.com/grpc/grpc-java/releases/tag/v1.63.2)

#### What's Changed

-   okhttp: Workaround SSLSocket not noticing socket is closed
-   netty: skip using NettyAdaptiveCumulator when Netty is on version 4.1.111 or later

### [`v1.63.1`](https://togithub.com/grpc/grpc-java/releases/tag/v1.63.1)

#### Bug fixes

-   netty: Release SendGrpcFrameCommand when stream is missing ([#&#8203;11116](https://togithub.com/grpc/grpc-java/issues/11116)) ([`fb9a108`](https://togithub.com/grpc/grpc-java/commit/fb9a10809))
-   Change defaults to use the older PickFirstLoadBalancer and disable Happy Eyeballs.  This disables a performance optimization added in v1.63.0 ([#&#8203;11120](https://togithub.com/grpc/grpc-java/issues/11120)) We have had a report that the new implementation can trigger a NullPointerException

### [`v1.63.0`](https://togithub.com/grpc/grpc-java/releases/tag/v1.63.0)

#### API Changes

-   xds: Stabilize CsdsService ([#&#8203;11003](https://togithub.com/grpc/grpc-java/issues/11003)) ([`0d749c5`](https://togithub.com/grpc/grpc-java/commit/0d749c594))
-   api: Stabilize server.getListenerSockets ([#&#8203;10910](https://togithub.com/grpc/grpc-java/issues/10910)) ([`ff34d51`](https://togithub.com/grpc/grpc-java/commit/ff34d51c7))
-   servlet: Introduce ServletServerBuilder.buildServlet([#&#8203;10921](https://togithub.com/grpc/grpc-java/issues/10921)) ([`257d1c2`](https://togithub.com/grpc/grpc-java/commit/257d1c2db))
-   api: Allow configuration of the queued byte threshold at which a Stream is considered not ready ([#&#8203;10977](https://togithub.com/grpc/grpc-java/issues/10977)) ([`2c83ef0`](https://togithub.com/grpc/grpc-java/commit/2c83ef063))

#### New Features

xds, dual stack, happy eyeballs: Support dual stack in xds, change list includes:

-   Enable new PickFirst lb policy by default. The new PickFirst implements subchannel picking logic.  ([#&#8203;11002](https://togithub.com/grpc/grpc-java/issues/11002)) ([`8a9ce99`](https://togithub.com/grpc/grpc-java/commit/8a9ce990b))
-   EDS resource now supports additional addresses ([#&#8203;11011](https://togithub.com/grpc/grpc-java/issues/11011)) ([`38f968f`](https://togithub.com/grpc/grpc-java/commit/38f968faf))
-   Change address based outlier detection to endpoint based ([#&#8203;10939](https://togithub.com/grpc/grpc-java/issues/10939)) ([`c61fe69`](https://togithub.com/grpc/grpc-java/commit/c61fe6980))
-   Enable Happy Eyeballs by default ([#&#8203;11022](https://togithub.com/grpc/grpc-java/issues/11022)) ([`51f811d`](https://togithub.com/grpc/grpc-java/commit/51f811df8))

#### Improvements

-   rls: Adding extra debug logs ([#&#8203;10902](https://togithub.com/grpc/grpc-java/issues/10902)) ([`eba699a`](https://togithub.com/grpc/grpc-java/commit/eba699ad1))
-   binder: Add missing Android API annotations ([#&#8203;10841](https://togithub.com/grpc/grpc-java/issues/10841)) ([`ce2adcc`](https://togithub.com/grpc/grpc-java/commit/ce2adcca9))
-   core: Provide DEADLINE_EXCEEDED insights for context deadline ([`3abab95`](https://togithub.com/grpc/grpc-java/commit/3abab95e7)). When the deadline triggered and the deadline was set on the RPC via the stub or CallOptions, gRPC would gather additional debugging information to help understand where the RPC took so long. However if the deadline came from io.grpc.Context the error was simply “context timed out.” Now the debugging information is provided in both cases
-   examples: Fix file paths in debug example README ([`e19f1f1`](https://togithub.com/grpc/grpc-java/commit/e19f1f15a))
-   compiler: implement ability to skip generation of javax annotation ([#&#8203;10927](https://togithub.com/grpc/grpc-java/issues/10927)) ([`0d39c2c`](https://togithub.com/grpc/grpc-java/commit/0d39c2c70)). Pass the option `jakarta_omit` to protoc-gen-grpc-java when generating code
-   xds: Get rid of xDS v2 proto dependencies ([#&#8203;10968](https://togithub.com/grpc/grpc-java/issues/10968)) ([`feab4e5`](https://togithub.com/grpc/grpc-java/commit/feab4e544)). `grpc-xds` jar size has decreased by 35%.
-   xds: Support retrieving names from wrapped resource containers ([#&#8203;10975](https://togithub.com/grpc/grpc-java/issues/10975)) ([`867e469`](https://togithub.com/grpc/grpc-java/commit/867e46940))
-   netty: improve server handling of writes to reset streams ([#&#8203;10258](https://togithub.com/grpc/grpc-java/issues/10258)) ([`a68399a`](https://togithub.com/grpc/grpc-java/commit/a68399a9b))
-   api: Fix a typo in ServerInterceptor JavaDoc ([#&#8203;10990](https://togithub.com/grpc/grpc-java/issues/10990)) ([`0b82f01`](https://togithub.com/grpc/grpc-java/commit/0b82f0126))
-   servlet: Check log fine level before hex string conversion. ([#&#8203;11038](https://togithub.com/grpc/grpc-java/issues/11038))
-   auth: Specify a locale for upper/lower case conversions (1.63.x backport) [#&#8203;11050](https://togithub.com/grpc/grpc-java/issues/11050)

#### Bug Fixes

-   xds: Copy data in least request to avoid picker data race ([`f4cc166`](https://togithub.com/grpc/grpc-java/commit/f4cc166f1)). This fixes a possible regression introduced in 1.60.0. Auditing the buggy code showed it unlikely to cause problems in practice, but that was more by happenstance than by design
-   xds: Fix data race in the xds client that contacts the control plane ([`d7628a3`](https://togithub.com/grpc/grpc-java/commit/d7628a3ab))
-   rls: Fix a local and remote race ([`aa90768`](https://togithub.com/grpc/grpc-java/commit/aa9076812)). The remote race could cause an RPC to hang until its deadline. It had been seen in practice when the client was severely CPU under-provisioned
-   xds: Fix xdsNameResolver virtual host lookup authority with xdstp style names. Use service authority instead of ldsResourceName ([#&#8203;10960](https://togithub.com/grpc/grpc-java/issues/10960)) ([`78b3972`](https://togithub.com/grpc/grpc-java/commit/78b3972ff))
-   core: Fix retry race condition that can lead to double decrementing inFlightSubStreams and so miss calling closed ([#&#8203;11026](https://togithub.com/grpc/grpc-java/issues/11026)) ([#&#8203;11033](https://togithub.com/grpc/grpc-java/issues/11033))
-   okhttp: Fix OkHTTP client transport leak ([#&#8203;11060](https://togithub.com/grpc/grpc-java/issues/11060))
-   xds: Use empty string when disabling server hostname verification ( [#&#8203;11058](https://togithub.com/grpc/grpc-java/issues/11058))

#### Dependencies

-   Upgraded google-auth-library-java to 1.22.0

#### Acknowledgement

Alex Panchenko
Benjamin Peterson
David Ankin
Prashanth Swaminathan
Touko Vainio-Kaila

### [`v1.62.2`](https://togithub.com/grpc/grpc-java/releases/tag/v1.62.2)

[Compare Source](https://togithub.com/grpc/grpc-java/compare/v1.61.1...v1.62.2)

##### gRPC Java 1.62.2 Release Notes

Note that this is the initial 1.62.x release

##### API Changes

-   services: Remove `io.grpc.services.BinaryLogs`, which was deprecated since 2021. `io.grpc.protobuf.services.BinaryLogs` should be used instead ([#&#8203;10832](https://togithub.com/grpc/grpc-java/issues/10832)).
-   Allow users outside of io.grpc.xds package to create custom xDS resources ([#&#8203;10834](https://togithub.com/grpc/grpc-java/issues/10834)) ([`6d96e65`](https://togithub.com/grpc/grpc-java/commit/6d96e6588))

##### New Features

-   api:Add ClientTransportFilter.  Similarly to ServerTransportFilter, this will provide an observability hook and it allows direct modification of the transport's attributes. ([#&#8203;10646](https://togithub.com/grpc/grpc-java/issues/10646))

##### Improvements

-   java_grpc_library.bzl: Add support for Auto Exec Groups ([`cb03bd2`](https://togithub.com/grpc/grpc-java/commit/cb03bd234)). This is mostly a behind-the-scenes change to adjust to the newer way Bazel operates
-   java_grpc_library.bzl: Support runfiles for protoc and the plugin ([`65a6b3b`](https://togithub.com/grpc/grpc-java/commit/65a6b3bc2)). Neither binary uses runfiles, but the task will be ready if they need to in the future
-   xds: Add EC key support for XdsChannelCredentials/XdsServerCredentials ([`100d5a5`](https://togithub.com/grpc/grpc-java/commit/100d5a55f))
-   binder:Change log level from WARNING to FINER for expected exception during close with error, to reduce log spamming ([#&#8203;10899](https://togithub.com/grpc/grpc-java/issues/10899)) ([`7ba0718`](https://togithub.com/grpc/grpc-java/commit/7ba0718bb))

##### Bug Fixes

-   xds: Fix a bug in WeightedRoundRobinLoadBalancer policy that could raise NullPointerException and further cause channel panic when picking a subchannel. This bug can only be triggered when connection can not be established and the channel reports TRANSIENT_FAILURE state. ([#&#8203;10868](https://togithub.com/grpc/grpc-java/issues/10868))

##### Dependencies

-   The protoc plugin no longer supports macOS Big Sur (macOS 11). Binaries are now built using Monterey (macOS 12)

##### Acknowledgements

-   [@&#8203;joybestourous](https://togithub.com/joybestourous)

### [`v1.61.1`](https://togithub.com/grpc/grpc-java/releases/tag/v1.61.1)

[Compare Source](https://togithub.com/grpc/grpc-java/compare/v1.61.0...v1.61.1)

##### Bug Fixes

xds: Fix a bug in `WeightedRoundRobinLoadBalancer` policy that could raise `NullPointerException` and further cause channel panic when picking a subchannel. This bug can only be triggered when connection can not be established and the channel reports `TRANSIENT_FAILURE` state. ([#&#8203;10868](https://togithub.com/grpc/grpc-java/issues/10868))

### [`v1.61.0`](https://togithub.com/grpc/grpc-java/releases/tag/v1.61.0)

[Compare Source](https://togithub.com/grpc/grpc-java/compare/v1.60.2...v1.61.0)

##### API Changes

-   Remove unused experimental API ManagedChannelBuilder.enableFullStreamDecompression ([#&#8203;10744](https://togithub.com/grpc/grpc-java/issues/10744))
-   api: Deprecate LoadBalancer.EMPTY_PICKER added in 1.58.0 in favor of FixedResultPicker ([`860b5cb`](https://togithub.com/grpc/grpc-java/commit/860b5cb1f))

##### New Features

-   binder: Experimental support for asynchronous security policies ([#&#8203;10566](https://togithub.com/grpc/grpc-java/issues/10566))

##### Improvements

-   core: reduce CompositeReadableBuffer allocation ([#&#8203;3279](https://togithub.com/grpc/grpc-java/issues/3279))
-   core: Improve error message clarity when a channel leak is detected ([`201893f`](https://togithub.com/grpc/grpc-java/commit/201893f5e))
-   util: use shared index across `round_robin` pickers ([`dca89b2`](https://togithub.com/grpc/grpc-java/commit/dca89b25b)). This makes its implementation more similar to `weighted_round_robin`.
-   xds: Implement ADS stream flow control mechanism ([#&#8203;10674](https://togithub.com/grpc/grpc-java/issues/10674)). This limits the maximum memory consumed if the control plane sends updates more rapidly than they can be processed.

##### Bug Fixes

-   core: Check outbound maximum message size for the compressed size in addition to the already-checked uncompressed size ([#&#8203;10739](https://togithub.com/grpc/grpc-java/issues/10739)). Fixed the status code to be RESOURCE_EXHAUSTED instead of UNKNOWN.
-   util: Fix NPE when multiple addresses are in an address group for petiole load balancer policies ([#&#8203;10769](https://togithub.com/grpc/grpc-java/issues/10769))
-   util: Disable publishing of fixtures ([`8ac43dd`](https://togithub.com/grpc/grpc-java/commit/8ac43dd81)). The Gradle test fixtures are for use by grpc-java's internal tests.
-   okhttp: Ignore known conscrypt socket close issue ([#&#8203;10812](https://togithub.com/grpc/grpc-java/issues/10812)). This stops an exception from being thrown when a known Conscrypt synchronization issue happens.

##### Dependencies

-   Drop support for Bazel 5 ([`55a9c01`](https://togithub.com/grpc/grpc-java/commit/55a9c012c)). Bazel 7 is available, and Protobuf has already dropped support for Bazel 5.
-   Change many compile deps to runtime deps ([`d6830d7`](https://togithub.com/grpc/grpc-java/commit/d6830d7f9)). This reduces the transitive classes "leaked" into the compile classpath. In particular, grpc-core (`io.grpc.internal`) will be less frequently included transitively at compile time.
-   Upgrade dependencies ([`c985797`](https://togithub.com/grpc/grpc-java/commit/c985797d9))
    -   Protobuf to 3.25.1
    -   auto-value-annotations to 1.10.4
    -   error_prone_annotations to 2.23.0
    -   proto-google-common-protos to 2.29.0
    -   google-cloud-logging to 3.15.14
    -   guava to 32.1.3-android
    -   okio to 3.4.0

##### Acknowledgements

-   [@&#8203;Gordiychuk](https://togithub.com/Gordiychuk)
-   [@&#8203;jroper](https://togithub.com/jroper)
-   [@&#8203;jyane](https://togithub.com/jyane)
-   [@&#8203;ulfjack](https://togithub.com/ulfjack)

### [`v1.60.2`](https://togithub.com/grpc/grpc-java/releases/tag/v1.60.2)

[Compare Source](https://togithub.com/grpc/grpc-java/compare/v1.60.1...v1.60.2)

##### Bug Fixes

xds: Fix a bug in `WeightedRoundRobinLoadBalancer` policy that could raise `NullPointerException` and further cause channel panic when picking a subchannel. This bug can only be triggered when connection can not be established and the channel reports `TRANSIENT_FAILURE` state. ([#&#8203;10868](https://togithub.com/grpc/grpc-java/issues/10868))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR was generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View the [repository job log](https://developer.mend.io/github/googleapis/google-http-java-client).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOC4yNi4xIiwidXBkYXRlZEluVmVyIjoiMzguMjYuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->


dpebot: /gcbrun

Patch:
diff --git a/.github/workflows/auto-release.yaml b/.github/workflows/auto-release.yaml
index 7a106d00..18d92e5a 100644
--- a/.github/workflows/auto-release.yaml
+++ b/.github/workflows/auto-release.yaml
@@ -21,7 +21,7 @@ jobs:
     runs-on: ubuntu-latest
     if: contains(github.head_ref, 'release-please')
     steps:
-    - uses: actions/github-script@v6
+    - uses: actions/github-script@v7
       with:
         github-token: ${{secrets.YOSHI_APPROVER_TOKEN}}
         debug: true
diff --git a/.github/workflows/ci-java7.yaml b/.github/workflows/ci-java7.yaml
index 2c8257d4..4dec872e 100644
--- a/.github/workflows/ci-java7.yaml
+++ b/.github/workflows/ci-java7.yaml
@@ -24,7 +24,7 @@ jobs:
     name: "units (7)"
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: actions/setup-java@v1
       # setup-java v2 or higher does not have version 1.7
       with:
@@ -55,7 +55,7 @@ jobs:
         # dailymotion-simple-cmdline-sample and google-http-client-assembly depend on
         # google-http-client-jackson2 
         mvn --batch-mode --show-version -ntp test \
-            --projects '!google-http-client-jackson2,!google-http-client-appengine,!samples/dailymotion-simple-cmdline-sample,!google-http-client-assembly' \
+            --projects '!google-http-client-jackson2,!google-http-client-appengine,!samples/dailymotion-simple-cmdline-sample,!google-http-client-assembly,!google-http-client-apache-v5' \
             -Dclirr.skip=true -Denforcer.skip=true -Dmaven.javadoc.skip=true \
             -Dgcloud.download.skip=true -T 1C \
             -Dproject.surefire.version=2.22.2 \
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 32feef7d..0e87e017 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -27,7 +27,7 @@ jobs:
       matrix:
         java: [8, 11, 17]
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: actions/setup-java@v3
       with:
         distribution: temurin
@@ -41,7 +41,7 @@ jobs:
     steps:
     - name: Support longpaths
       run: git config --system core.longpaths true
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: actions/setup-java@v3
       with:
         distribution: temurin
@@ -56,7 +56,7 @@ jobs:
       matrix:
         java: [8, 11, 17]
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: actions/setup-java@v3
       with:
         distribution: temurin
@@ -66,7 +66,7 @@ jobs:
   lint:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: actions/setup-java@v3
       with:
         distribution: temurin
@@ -78,7 +78,7 @@ jobs:
   clirr:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: actions/setup-java@v3
       with:
         distribution: temurin
@@ -94,7 +94,7 @@ jobs:
     name: "units (21)"
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: actions/setup-java@v3
       with:
         java-version: 21
diff --git a/.github/workflows/downstream.yaml b/.github/workflows/downstream.yaml
index c4be85ea..8681fe5f 100644
--- a/.github/workflows/downstream.yaml
+++ b/.github/workflows/downstream.yaml
@@ -133,7 +133,7 @@ jobs:
         - workflow-executions
         - workflows
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: actions/setup-java@v3
       with:
         distribution: zulu
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index 7dd91434..f153dddd 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -32,12 +32,12 @@ jobs:
 
     steps:
       - name: "Checkout code"
-        uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
+        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
         with:
           persist-credentials: false
 
       - name: "Run analysis"
-        uses: ossf/scorecard-action@e38b1902ae4f44df626f11ba0734b14fb91f8f86 # v2.1.2
+        uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
         with:
           results_file: results.sarif
           results_format: sarif
@@ -59,7 +59,7 @@ jobs:
       # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
       # format to the repository Actions tab.
       - name: "Upload artifact"
-        uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+        uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
         with:
           name: SARIF file
           path: results.sarif
@@ -67,6 +67,6 @@ jobs:
 
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2.2.4
+        uses: github/codeql-action/upload-sarif@883d8588e56d1753a8a58c1c86e88976f0c23449 # v3.26.3
         with:
           sarif_file: results.sarif
diff --git a/.kokoro/presubmit/graalvm-native-a.cfg b/.kokoro/presubmit/graalvm-native-a.cfg
index ad109968..54dca4f2 100644
--- a/.kokoro/presubmit/graalvm-native-a.cfg
+++ b/.kokoro/presubmit/graalvm-native-a.cfg
@@ -3,7 +3,7 @@
 # Configure the docker image for kokoro-trampoline.
 env_vars: {
   key: "TRAMPOLINE_IMAGE"
-  value: "gcr.io/cloud-devrel-public-resources/graalvm_a:1.9.0"
+  value: "gcr.io/cloud-devrel-public-resources/graalvm_a:1.11.0"
 }
 
 env_vars: {
diff --git a/.kokoro/presubmit/graalvm-native-b.cfg b/.kokoro/presubmit/graalvm-native-b.cfg
index e5229b14..8b05c77f 100644
--- a/.kokoro/presubmit/graalvm-native-b.cfg
+++ b/.kokoro/presubmit/graalvm-native-b.cfg
@@ -3,7 +3,7 @@
 # Configure the docker image for kokoro-trampoline.
 env_vars: {
   key: "TRAMPOLINE_IMAGE"
-  value: "gcr.io/cloud-devrel-public-resources/graalvm_b:1.9.0"
+  value: "gcr.io/cloud-devrel-public-resources/graalvm_b:1.11.0"
 }
 
 env_vars: {
diff --git a/docs/http-transport.md b/docs/http-transport.md
index 0167db4a..b6d94cce 100644
--- a/docs/http-transport.md
+++ b/docs/http-transport.md
@@ -19,8 +19,12 @@ There are three built-in low-level HTTP transports:
 
 1. [`NetHttpTransport`][net-http-transport]: based on [`HttpURLConnection`][http-url-connection]
 that is found in all Java SDKs, and thus usually the simplest choice.
+1. [`Apache5HttpTransport`][apache-http-transport]: based on the popular
+   [Apache 5.x HttpClient][apache5-http-client] that allows for more customization.
 1. [`ApacheHttpTransport`][apache-http-transport]: based on the popular
-[Apache HttpClient][apache-http-client] that allows for more customization.
+[Apache 4.x HttpClient][apache-http-client] that allows for more customization. Note that this transport implementation
+relies on [Apache 4.x HttpCore][apache-http-core] which has reached end of life. It is recommended to use
+[`Apache5HttpTransport`][apache-http-transport] instead.
 1. [`UrlFetchTransport`][url-fetch-transport]: based on the [URL Fetch Java API][url-fetch] in the
 Google App Engine SDK.
 
@@ -124,7 +128,10 @@ HttpRequestFactory requestFactory = transport.createRequestFactory(new MyInitial
 [net-http-transport]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/http/javanet/NetHttpTransport.html
 [http-url-connection]: http://docs.oracle.com/javase/7/docs/api/java/net/HttpURLConnection.html
 [apache-http-transport]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/http/apache/v2/ApacheHttpTransport.html
-[apache-http-client]: http://hc.apache.org/httpcomponents-client-ga/index.html
+[apache5-http-transport]: https://github.com/googleapis/google-http-java-client/blob/de8743587d1415e8a6046096ac1fc0a5e81490c3/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpTransport.java
+[apache-http-client]: https://hc.apache.org/httpcomponents-client-4.5.x/index.html
+[apache-http-core]: https://hc.apache.org/httpcomponents-core-4.4.x/index.html
+[apache5-http-client]: https://hc.apache.org/httpcomponents-client-5.3.x/index.html
 [url-fetch-transport]: https://googleapis.dev/java/google-http-client/latest/index.html?com/google/api/client/extensions/appengine/http/UrlFetchTransport.html
 [url-fetch]: https://cloud.google.com/appengine/docs/java/javadoc/com/google/appengine/api/urlfetch/package-summary
 [logger]: https://docs.oracle.com/javase/7/docs/api/java/util/logging/Logger.html
diff --git a/google-http-client-apache-v5/pom.xml b/google-http-client-apache-v5/pom.xml
new file mode 100644
index 00000000..a6789797
--- /dev/null
+++ b/google-http-client-apache-v5/pom.xml
@@ -0,0 +1,116 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>com.google.http-client</groupId>
+    <artifactId>google-http-client-parent</artifactId>
+    <version>1.44.3-SNAPSHOT</version><!-- {x-version-update:google-http-client-parent:current} -->
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+  <artifactId>google-http-client-apache-v5</artifactId>
+  <version>1.44.3-SNAPSHOT</version><!-- {x-version-update:google-http-client-apache-v5:current} -->
+  <name>Apache HTTP transport v5 for the Google HTTP Client Library for Java.</name>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <configuration>
+          <links>
+            <link>https://download.oracle.com/javase/7/docs/api/</link>
+          </links>
+          <doctitle>${project.name} ${project.version}</doctitle>
+          <windowtitle>${project.artifactId} ${project.version}</windowtitle>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-source-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <version>3.3.0</version>
+        <executions>
+          <execution>
+            <id>add-test-source</id>
+            <phase>generate-test-sources</phase>
+            <goals>
+              <goal>add-test-source</goal>
+            </goals>
+            <configuration>
+              <sources>
+                <source>target/generated-test-sources</source>
+              </sources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+            <manifestEntries>
+              <Automatic-Module-Name>com.google.api.client.http.apache.v5</Automatic-Module-Name>
+            </manifestEntries>
+          </archive>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>5.1.9</version>
+        <executions>
+          <execution>
+            <id>bundle-manifest</id>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>manifest</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>3.13.0</version>
+        <configuration>
+          <source>1.8</source>
+          <target>1.8</target>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>com.google.http-client</groupId>
+      <artifactId>google-http-client</artifactId>
+      <exclusions>
+        <exclusion>
+          <groupId>org.apache.httpcomponents</groupId>
+          <artifactId>httpcore</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.apache.httpcomponents</groupId>
+          <artifactId>httpclient</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents.client5</groupId>
+      <artifactId>httpclient5</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents.core5</groupId>
+      <artifactId>httpcore5</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5ContentEntity.java b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5ContentEntity.java
new file mode 100644
index 00000000..4a5ab84e
--- /dev/null
+++ b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5ContentEntity.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.api.client.http.apache.v5;
+
+import com.google.api.client.util.Preconditions;
+import com.google.api.client.util.StreamingContent;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import org.apache.hc.core5.http.io.entity.AbstractHttpEntity;
+
+/**
+ * Translation class to make google-http-client entity conform with Apache 5.x {@link
+ * AbstractHttpEntity}
+ */
+final class Apache5ContentEntity extends AbstractHttpEntity {
+
+  /** Content length or less than zero if not known. */
+  private final long contentLength;
+
+  /** Streaming content. */
+  private final StreamingContent streamingContent;
+
+  /**
+   * @param contentLength content length or less than zero if not known
+   * @param streamingContent streaming content
+   */
+  Apache5ContentEntity(
+      long contentLength,
+      StreamingContent streamingContent,
+      String contentType,
+      String contentEncoding) {
+    super(contentType, contentEncoding, contentLength == -1);
+    this.contentLength = contentLength;
+    this.streamingContent = Preconditions.checkNotNull(streamingContent);
+  }
+
+  @Override
+  public InputStream getContent() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public long getContentLength() {
+    return contentLength;
+  }
+
+  @Override
+  public boolean isRepeatable() {
+    return false;
+  }
+
+  @Override
+  public boolean isStreaming() {
+    return true;
+  }
+
+  @Override
+  public void writeTo(OutputStream out) throws IOException {
+    if (contentLength != 0) {
+      streamingContent.writeTo(out);
+    }
+  }
+
+  @Override
+  public void close() throws IOException {}
+}
diff --git a/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpRequest.java b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpRequest.java
new file mode 100644
index 00000000..99d6eca8
--- /dev/null
+++ b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpRequest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.api.client.http.apache.v5;
+
+import com.google.api.client.http.LowLevelHttpRequest;
+import com.google.api.client.http.LowLevelHttpResponse;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+import org.apache.hc.client5.http.ClientProtocolException;
+import org.apache.hc.client5.http.classic.HttpClient;
+import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.routing.RoutingSupport;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.util.Timeout;
+
+public final class Apache5HttpRequest extends LowLevelHttpRequest {
+
+  private final HttpUriRequestBase request;
+
+  private final RequestConfig.Builder requestConfig;
+
+  private final HttpClient httpClient;
+
+  Apache5HttpRequest(HttpClient httpClient, HttpUriRequestBase request) {
+    this.httpClient = httpClient;
+    this.request = request;
+    // disable redirects as google-http-client handles redirects
+    this.requestConfig = RequestConfig.custom().setRedirectsEnabled(false);
+  }
+
+  @Override
+  public void addHeader(String name, String value) {
+    request.addHeader(name, value);
+  }
+
+  @Override
+  public void setTimeout(int connectTimeout, int readTimeout) throws IOException {
+    requestConfig
+        .setConnectTimeout(Timeout.of(connectTimeout, TimeUnit.MILLISECONDS))
+        // ResponseTimeout behaves the same as 4.x's SocketTimeout
+        .setResponseTimeout(Timeout.of(readTimeout, TimeUnit.MILLISECONDS));
+  }
+
+  @Override
+  public LowLevelHttpResponse execute() throws IOException {
+    if (getStreamingContent() != null) {
+      Apache5ContentEntity entity =
+          new Apache5ContentEntity(
+              getContentLength(), getStreamingContent(), getContentType(), getContentEncoding());
+      request.setEntity(entity);
+    }
+    request.setConfig(requestConfig.build());
+    HttpHost target;
+    try {
+      target = RoutingSupport.determineHost(request);
+    } catch (HttpException e) {
+      throw new ClientProtocolException("The request's host is invalid.", e);
+    }
+    // we use a null context so the client creates the default one internally
+    ClassicHttpResponse httpResponse = httpClient.executeOpen(target, request, null);
+    return new Apache5HttpResponse(request, httpResponse);
+  }
+}
diff --git a/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpResponse.java b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpResponse.java
new file mode 100644
index 00000000..1574c8c8
--- /dev/null
+++ b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpResponse.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.api.client.http.apache.v5;
+
+import com.google.api.client.http.LowLevelHttpResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Logger;
+import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.message.StatusLine;
+
+final class Apache5HttpResponse extends LowLevelHttpResponse {
+
+  private static final Logger LOGGER = Logger.getLogger(Apache5HttpResponse.class.getName());
+  private final HttpUriRequestBase request;
+  private final ClassicHttpResponse response;
+  private final Header[] allHeaders;
+  private final HttpEntity entity;
+
+  Apache5HttpResponse(HttpUriRequestBase request, ClassicHttpResponse response) {
+    this.request = request;
+    this.response = response;
+    this.allHeaders = response.getHeaders();
+    this.entity = response.getEntity();
+  }
+
+  @Override
+  public int getStatusCode() {
+    return response.getCode();
+  }
+
+  @Override
+  public InputStream getContent() throws IOException {
+    return new Apache5ResponseContent(entity.getContent(), response);
+  }
+
+  @Override
+  public String getContentEncoding() {
+    return entity != null ? entity.getContentEncoding() : null;
+  }
+
+  @Override
+  public long getContentLength() {
+    return entity == null ? -1 : entity.getContentLength();
+  }
+
+  @Override
+  public String getContentType() {
+    return entity == null ? null : entity.getContentType();
+  }
+
+  @Override
+  public String getReasonPhrase() {
+    return response.getReasonPhrase();
+  }
+
+  @Override
+  public String getStatusLine() {
+    return new StatusLine(response).toString();
+  }
+
+  public String getHeaderValue(String name) {
+    return response.getLastHeader(name).getValue();
+  }
+
+  @Override
+  public int getHeaderCount() {
+    return allHeaders.length;
+  }
+
+  @Override
+  public String getHeaderName(int index) {
+    return allHeaders[index].getName();
+  }
+
+  @Override
+  public String getHeaderValue(int index) {
+    return allHeaders[index].getValue();
+  }
+
+  /** Aborts execution of the request. */
+  @Override
+  public void disconnect() throws IOException {
+    request.abort();
+    response.close();
+  }
+}
diff --git a/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpTransport.java b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpTransport.java
new file mode 100644
index 00000000..868a2cf9
--- /dev/null
+++ b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5HttpTransport.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.api.client.http.apache.v5;
+
+import com.google.api.client.http.HttpMethods;
+import com.google.api.client.http.HttpTransport;
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import java.io.IOException;
+import java.net.ProxySelector;
+import java.net.URI;
+import java.util.concurrent.TimeUnit;
+import org.apache.hc.client5.http.classic.HttpClient;
+import org.apache.hc.client5.http.classic.methods.HttpDelete;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.classic.methods.HttpHead;
+import org.apache.hc.client5.http.classic.methods.HttpOptions;
+import org.apache.hc.client5.http.classic.methods.HttpPatch;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.classic.methods.HttpPut;
+import org.apache.hc.client5.http.classic.methods.HttpTrace;
+import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
+import org.apache.hc.client5.http.config.ConnectionConfig;
+import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
+import org.apache.hc.client5.http.impl.routing.SystemDefaultRoutePlanner;
+import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
+import org.apache.hc.core5.io.CloseMode;
+import org.apache.hc.core5.io.ModalCloseable;
+
+/**
+ * Thread-safe HTTP transport based on the Apache HTTP Client library.
+ *
+ * <p>Implementation is thread-safe, as long as any parameter modification to the {@link
+ * #getHttpClient() Apache HTTP Client} is only done at initialization time. For maximum efficiency,
+ * applications should use a single globally-shared instance of the HTTP transport.
+ *
+ * <p>Default settings are specified in {@link #newDefaultHttpClient()}. Use the {@link
+ * #Apache5HttpTransport(HttpClient)} constructor to override the Apache HTTP Client used. Please
+ * read the <a
+ * href="https://github.com/apache/httpcomponents-client/blob/f898f1aca38f77f62a007856a674629cae5a02e6/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientConfiguration.java">
+ * Apache HTTP Client 5.x configuration example</a> for more complex configuration options.
+ */
+public final class Apache5HttpTransport extends HttpTransport {
+
+  /** Apache HTTP client. */
+  private final HttpClient httpClient;
+
+  /** If the HTTP client uses mTLS channel. */
+  private final boolean isMtls;
+
+  /** Constructor that uses {@link #newDefaultHttpClient()} for the Apache HTTP client. */
+  public Apache5HttpTransport() {
+    this(newDefaultHttpClient(), false);
+  }
+
+  /**
+   * Constructor that allows an alternative Apache HTTP client to be used.
+   *
+   * <p>If you choose to provide your own Apache HttpClient implementation, be sure that
+   *
+   * <ul>
+   *   <li>HTTP version is set to 1.1.
+   *   <li>Retries are disabled (google-http-client handles retries).
+   * </ul>
+   *
+   * @param httpClient Apache HTTP client to use
+   */
+  public Apache5HttpTransport(HttpClient httpClient) {
+    this.httpClient = httpClient;
+    this.isMtls = false;
+  }
+
+  /**
+   * {@link Beta} <br>
+   * Constructor that allows an alternative CLoseable Apache HTTP client to be used.
+   *
+   * <p>If you choose to provide your own Apache HttpClient implementation, be sure that
+   *
+   * <ul>
+   *   <li>HTTP version is set to 1.1.
+   *   <li>Retries are disabled (google-http-client handles retries).
+   *   <li>Redirects are disabled (google-http-client handles retries).
+   * </ul>
+   *
+   * @param httpClient Apache HTTP client to use
+   * @param isMtls If the HTTP client is mutual TLS
+   */
+  @Beta
+  public Apache5HttpTransport(HttpClient httpClient, boolean isMtls) {
+    this.httpClient = httpClient;
+    this.isMtls = isMtls;
+  }
+
+  /**
+   * Creates a new instance of the Apache HTTP client that is used by the {@link
+   * #Apache5HttpTransport()} constructor.
+   *
+   * <p>Settings:
+   *
+   * <ul>
+   *   <li>The client connection manager is set to {@link PoolingHttpClientConnectionManager}.
+   *   <li>The retry mechanism is turned off using {@link
+   *       HttpClientBuilder#disableAutomaticRetries()}.
+   *   <li>Redirects are turned off using {@link HttpClientBuilder#disableRedirectHandling}.
+   *   <li>The route planner uses {@link SystemDefaultRoutePlanner} with {@link
+   *       ProxySelector#getDefault()}, which uses the proxy settings from <a
+   *       href="https://docs.oracle.com/javase/8/docs/api/java/net/doc-files/net-properties.html">system
+   *       properties</a>.
+   * </ul>
+   *
+   * @return new instance of the Apache HTTP client
+   */
+  public static HttpClient newDefaultHttpClient() {
+    return newDefaultHttpClientBuilder().build();
+  }
+
+  /**
+   * Creates a new Apache HTTP client builder that is used by the {@link #Apache5HttpTransport()}
+   * constructor.
+   *
+   * <p>Settings:
+   *
+   * <ul>
+   *   <li>The client connection manager is set to {@link PoolingHttpClientConnectionManager}.
+   *   <li>The retry mechanism is turned off using {@link
+   *       HttpClientBuilder#disableAutomaticRetries()}.
+   *   <li>Redirects are turned off using {@link HttpClientBuilder#disableRedirectHandling}.
+   *   <li>The route planner uses {@link SystemDefaultRoutePlanner} with {@link
+   *       ProxySelector#getDefault()}, which uses the proxy settings from <a
+   *       href="http://docs.oracle.com/javase/8/docs/api/java/net/doc-files/net-properties.html">system
+   *       properties</a>.
+   * </ul>
+   *
+   * @return new instance of the Apache HTTP client builder
+   */
+  public static HttpClientBuilder newDefaultHttpClientBuilder() {
+    PoolingHttpClientConnectionManager connectionManager =
+        PoolingHttpClientConnectionManagerBuilder.create()
+            .setSSLSocketFactory(SSLConnectionSocketFactory.getSocketFactory())
+            .setMaxConnTotal(200)
+            .setMaxConnPerRoute(20)
+            .setDefaultConnectionConfig(
+                ConnectionConfig.custom().setTimeToLive(-1, TimeUnit.MILLISECONDS).build())
+            .build();
+
+    return HttpClients.custom()
+        .useSystemProperties()
+        .setConnectionManager(connectionManager)
+        .setRoutePlanner(new SystemDefaultRoutePlanner(ProxySelector.getDefault()))
+        .disableRedirectHandling()
+        .disableAutomaticRetries();
+  }
+
+  @Override
+  public boolean supportsMethod(String method) {
+    return true;
+  }
+
+  @Override
+  protected Apache5HttpRequest buildRequest(String method, String url) {
+    HttpUriRequestBase requestBase;
+    if (method.equals(HttpMethods.DELETE)) {
+      requestBase = new HttpDelete(url);
+    } else if (method.equals(HttpMethods.GET)) {
+      requestBase = new HttpGet(url);
+    } else if (method.equals(HttpMethods.HEAD)) {
+      requestBase = new HttpHead(url);
+    } else if (method.equals(HttpMethods.PATCH)) {
+      requestBase = new HttpPatch(url);
+    } else if (method.equals(HttpMethods.POST)) {
+      requestBase = new HttpPost(url);
+    } else if (method.equals(HttpMethods.PUT)) {
+      requestBase = new HttpPut(url);
+    } else if (method.equals(HttpMethods.TRACE)) {
+      requestBase = new HttpTrace(url);
+    } else if (method.equals(HttpMethods.OPTIONS)) {
+      requestBase = new HttpOptions(url);
+    } else {
+      requestBase = new HttpUriRequestBase(Preconditions.checkNotNull(method), URI.create(url));
+    }
+    return new Apache5HttpRequest(httpClient, requestBase);
+  }
+
+  /**
+   * Gracefully shuts down the connection manager and releases allocated resources. This closes all
+   * connections, whether they are currently used or not.
+   */
+  @Override
+  public void shutdown() throws IOException {
+    if (httpClient instanceof ModalCloseable) {
+      ((ModalCloseable) httpClient).close(CloseMode.GRACEFUL);
+    }
+    // otherwise no-op
+  }
+
+  /** Returns the Apache HTTP client. */
+  public HttpClient getHttpClient() {
+    return httpClient;
+  }
+
+  /** Returns if the underlying HTTP client is mTLS. */
+  @Override
+  public boolean isMtls() {
+    return isMtls;
+  }
+}
diff --git a/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5ResponseContent.java b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5ResponseContent.java
new file mode 100644
index 00000000..c2d3091d
--- /dev/null
+++ b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/Apache5ResponseContent.java
@@ -0,0 +1,75 @@
+package com.google.api.client.http.apache.v5;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.io.IOException;
+import java.io.InputStream;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpResponse;
+
+/**
+ * Class that wraps an {@link org.apache.hc.core5.http.HttpEntity}'s content {@link InputStream}
+ * along with the {@link ClassicHttpResponse} that contains this entity. The main purpose is to be
+ * able to close the response as well as the content input stream when {@link #close()} is called,
+ * in order to not break the existing contract with clients using apache v4 that only required them
+ * to close the input stream to clean up all resources.
+ */
+public class Apache5ResponseContent extends InputStream {
+  private final ClassicHttpResponse response;
+  private final InputStream wrappedStream;
+
+  public Apache5ResponseContent(InputStream wrappedStream, ClassicHttpResponse response) {
+    this.response = response;
+    this.wrappedStream = wrappedStream;
+  }
+
+  @Override
+  public int read() throws IOException {
+    return wrappedStream.read();
+  }
+
+  @Override
+  public int read(byte b[]) throws IOException {
+    return wrappedStream.read(b);
+  }
+
+  @Override
+  public int read(byte b[], int off, int len) throws IOException {
+    return wrappedStream.read(b, off, len);
+  }
+
+  @Override
+  public long skip(long n) throws IOException {
+    return wrappedStream.skip(n);
+  }
+
+  @Override
+  public int available() throws IOException {
+    return wrappedStream.available();
+  }
+
+  @Override
+  public synchronized void mark(int readlimit) {
+    wrappedStream.mark(readlimit);
+  }
+
+  @Override
+  public synchronized void reset() throws IOException {
+    wrappedStream.reset();
+  }
+
+  @Override
+  public void close() throws IOException {
+    wrappedStream.close();
+    response.close();
+  }
+
+  @Override
+  public boolean markSupported() {
+    return wrappedStream.markSupported();
+  }
+
+  @VisibleForTesting
+  HttpResponse getResponse() {
+    return response;
+  }
+}
diff --git a/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/package-info.java b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/package-info.java
new file mode 100644
index 00000000..223edc82
--- /dev/null
+++ b/google-http-client-apache-v5/src/main/java/com/google/api/client/http/apache/v5/package-info.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/** HTTP Transport library for Google API's based on Apache HTTP Client/Core version 5.x */
+package com.google.api.client.http.apache.v5;
diff --git a/google-http-client-assembly/classpath-include b/google-http-client-assembly/classpath-include
index c1bd8032..c7bbd573 100644
--- a/google-http-client-assembly/classpath-include
+++ b/google-http-client-assembly/classpath-include
@@ -7,8 +7,8 @@
 	<classpathentry kind="lib" path="libs/google-http-client-xml-${project.version}.jar" sourcepath="libs-sources/google-http-client-xml-${project.version}-sources.jar"/>
 	<classpathentry kind="lib" path="libs/commons-logging-${project.commons-logging.version}.jar" sourcepath="libs-sources/commons-logging-${project.commons-logging.version}-sources.jar"/>
 	<classpathentry kind="lib" path="libs/gson-${project.gson.version}.jar" sourcepath="libs-sources/gson-${project.gson.version}-sources.jar"/>
-	<classpathentry kind="lib" path="libs/httpclient-${project.httpclient.version}.jar" sourcepath="libs-sources/httpclient-${project.httpclient.version}-sources.jar"/>
-	<classpathentry kind="lib" path="libs/httpcore-${project.httpcore.version}.jar" sourcepath="libs-sources/httpcore-${project.httpcore.version}-sources.jar"/>
+	<classpathentry kind="lib" path="libs/httpclient-${project.apache-httpclient-4.version}.jar" sourcepath="libs-sources/httpclient-${project.apache-httpclient-4.version}-sources.jar"/>
+	<classpathentry kind="lib" path="libs/httpcore-${project.apache-httpcore-4.version}.jar" sourcepath="libs-sources/httpcore-${project.apache-httpcore-4.version}-sources.jar"/>
 	<classpathentry kind="lib" path="libs/jackson-core-asl-${project.jackson-core-asl.version}.jar" sourcepath="libs-sources/jackson-core-asl-${project.jackson-core-asl.version}-sources.jar"/>
 	<classpathentry kind="lib" path="libs/jackson-core-${project.jackson-core2.version}.jar" sourcepath="libs-sources/jackson-core-${project.jackson-core2.version}-sources.jar"/>
 	<classpathentry kind="lib" path="libs/jsr305-${project.jsr305.version}.jar"/>
diff --git a/google-http-client-assembly/readme.html b/google-http-client-assembly/readme.html
index 5e7af564..8a146df1 100644
--- a/google-http-client-assembly/readme.html
+++ b/google-http-client-assembly/readme.html
@@ -135,8 +135,8 @@
   required for general purpose Java applications :
   <ul>
     <li>commons-logging-${project.commons-logging.version}.jar</li>
-    <li>httpclient-${project.httpclient.version}.jar</li>
-    <li>httpcore-${project.httpcore.version}.jar</li>
+    <li>httpclient-${project.apache-httpclient-4.version}.jar</li>
+    <li>httpcore-${project.apache-httpcore-4.version}.jar</li>
   </ul>
 
 </body>
diff --git a/pom.xml b/pom.xml
index 5760c3e6..ca96fb09 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,6 +62,7 @@
     <module>google-http-client-appengine</module>
     <module>google-http-client-android</module>
     <module>google-http-client-apache-v2</module>
+    <module>google-http-client-apache-v5</module>
     <module>google-http-client-protobuf</module>
     <module>google-http-client-gson</module>
     <module>google-http-client-jackson2</module>
@@ -92,7 +93,7 @@
   <parent>
     <groupId>com.google.cloud</groupId>
     <artifactId>native-image-shared-config</artifactId>
-    <version>1.9.0</version>
+    <version>1.11.0</version>
   </parent>
 
   <!--
@@ -156,12 +157,22 @@
       <dependency>
         <groupId>org.apache.httpcomponents</groupId>
         <artifactId>httpclient</artifactId>
-        <version>${project.httpclient.version}</version>
+        <version>${project.apache-httpclient-4.version}</version>
       </dependency>
       <dependency>
         <groupId>org.apache.httpcomponents</groupId>
         <artifactId>httpcore</artifactId>
-        <version>${project.httpcore.version}</version>
+        <version>${project.apache-httpcore-4.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.httpcomponents.client5</groupId>
+        <artifactId>httpclient5</artifactId>
+        <version>${project.apache-httpclient-5.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.httpcomponents.core5</groupId>
+        <artifactId>httpcore5</artifactId>
+        <version>${project.apache-httpcore-5.version}</version>
       </dependency>
       <dependency>
         <groupId>com.google.guava</groupId>
@@ -236,12 +247,12 @@
       <dependency>
         <groupId>com.google.j2objc</groupId>
         <artifactId>j2objc-annotations</artifactId>
-        <version>2.8</version>
+        <version>3.0.0</version>
       </dependency>
       <dependency>
         <groupId>io.grpc</groupId>
         <artifactId>grpc-context</artifactId>
-        <version>1.60.1</version>
+        <version>1.66.0</version>
       </dependency>
       <dependency>
         <groupId>io.opencensus</groupId>
@@ -361,7 +372,7 @@
         <plugin>
           <groupId>org.codehaus.mojo</groupId>
           <artifactId>animal-sniffer-maven-plugin</artifactId>
-          <version>1.23</version>
+          <version>1.24</version>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
@@ -597,12 +608,14 @@
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <project.jsr305.version>3.0.2</project.jsr305.version>
     <project.gson.version>2.10.1</project.gson.version>
-    <project.jackson-core2.version>2.14.2</project.jackson-core2.version>
+    <project.jackson-core2.version>2.17.2</project.jackson-core2.version>
     <project.protobuf-java.version>3.21.12</project.protobuf-java.version>
     <project.guava.version>30.1.1-android</project.guava.version>
     <project.xpp3.version>1.1.4c</project.xpp3.version>
-    <project.httpclient.version>4.5.14</project.httpclient.version>
-    <project.httpcore.version>4.4.16</project.httpcore.version>
+    <project.apache-httpclient-4.version>4.5.14</project.apache-httpclient-4.version>
+    <project.apache-httpcore-4.version>4.4.16</project.apache-httpcore-4.version>
+    <project.apache-httpclient-5.version>5.3.1</project.apache-httpclient-5.version>
+    <project.apache-httpcore-5.version>5.2.4</project.apache-httpcore-5.version>
     <project.opencensus.version>0.31.1</project.opencensus.version>
     <project.root-directory>..</project.root-directory>
     <project.surefire.version>3.2.5</project.surefire.version>
diff --git a/versions.txt b/versions.txt
index 963efeb8..87dbbb66 100644
--- a/versions.txt
+++ b/versions.txt
@@ -7,6 +7,7 @@ google-http-client-parent:1.44.2:1.44.3-SNAPSHOT
 google-http-client-android:1.44.2:1.44.3-SNAPSHOT
 google-http-client-android-test:1.44.2:1.44.3-SNAPSHOT
 google-http-client-apache-v2:1.44.2:1.44.3-SNAPSHOT
+google-http-client-apache-v5:1.44.2:1.44.3-SNAPSHOT
 google-http-client-appengine:1.44.2:1.44.3-SNAPSHOT
 google-http-client-assembly:1.44.2:1.44.3-SNAPSHOT
 google-http-client-findbugs:1.44.2:1.44.3-SNAPSHOT


Test patch:
diff --git a/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/Apache5HttpRequestTest.java b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/Apache5HttpRequestTest.java
new file mode 100644
index 00000000..3b7ca4a2
--- /dev/null
+++ b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/Apache5HttpRequestTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.api.client.http.apache.v5;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.api.client.http.ByteArrayContent;
+import com.google.api.client.http.HttpContent;
+import com.google.api.client.http.InputStreamContent;
+import com.google.api.client.http.LowLevelHttpResponse;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.io.entity.BasicHttpEntity;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.junit.Test;
+
+public class Apache5HttpRequestTest {
+  @Test
+  public void testContentLengthSet() throws Exception {
+    HttpUriRequestBase base = new HttpPost("http://www.google.com");
+    Apache5HttpRequest request =
+        new Apache5HttpRequest(
+            new MockHttpClient() {
+              @Override
+              public ClassicHttpResponse executeOpen(
+                  HttpHost target, ClassicHttpRequest request, HttpContext context) {
+                return new MockClassicHttpResponse();
+              }
+            },
+            base);
+    HttpContent content =
+        new ByteArrayContent("text/plain", "sample".getBytes(StandardCharsets.UTF_8));
+    request.setStreamingContent(content);
+    request.setContentLength(content.getLength());
+    request.execute();
+
+    assertFalse(base.getEntity().isChunked());
+    assertEquals(6, base.getEntity().getContentLength());
+  }
+
+  @Test
+  public void testChunked() throws Exception {
+    byte[] buf = new byte[300];
+    Arrays.fill(buf, (byte) ' ');
+    HttpUriRequestBase base = new HttpPost("http://www.google.com");
+    Apache5HttpRequest request =
+        new Apache5HttpRequest(
+            new MockHttpClient() {
+              @Override
+              public ClassicHttpResponse executeOpen(
+                  HttpHost target, ClassicHttpRequest request, HttpContext context) {
+                return new MockClassicHttpResponse();
+              }
+            },
+            base);
+    HttpContent content = new InputStreamContent("text/plain", new ByteArrayInputStream(buf));
+    request.setStreamingContent(content);
+    request.execute();
+
+    assertTrue(base.getEntity().isChunked());
+    assertEquals(-1, base.getEntity().getContentLength());
+  }
+
+  @Test
+  public void testExecute_closeContent_closesResponse() throws Exception {
+    HttpUriRequestBase base = new HttpPost("http://www.google.com");
+    final InputStream responseContentStream = new ByteArrayInputStream(new byte[] {1, 2, 3});
+    BasicHttpEntity testEntity =
+        new BasicHttpEntity(responseContentStream, ContentType.DEFAULT_BINARY);
+    AtomicInteger closedResponseCounter = new AtomicInteger(0);
+    ClassicHttpResponse classicResponse =
+        new MockClassicHttpResponse() {
+          @Override
+          public HttpEntity getEntity() {
+            return testEntity;
+          }
+
+          @Override
+          public void close() {
+            closedResponseCounter.incrementAndGet();
+          }
+        };
+
+    Apache5HttpRequest request =
+        new Apache5HttpRequest(
+            new MockHttpClient() {
+              @Override
+              public ClassicHttpResponse executeOpen(
+                  HttpHost target, ClassicHttpRequest request, HttpContext context) {
+                return classicResponse;
+              }
+            },
+            base);
+    LowLevelHttpResponse response = request.execute();
+    assertTrue(response instanceof Apache5HttpResponse);
+
+    // we confirm that the classic response we prepared in this test is the same as the content's
+    // response
+    assertTrue(response.getContent() instanceof Apache5ResponseContent);
+    assertEquals(classicResponse, ((Apache5ResponseContent) response.getContent()).getResponse());
+
+    // we close the response's content stream and confirm the response is also closed
+    assertEquals(0, closedResponseCounter.get());
+    response.getContent().close();
+    assertEquals(1, closedResponseCounter.get());
+  }
+}
diff --git a/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/Apache5HttpTransportTest.java b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/Apache5HttpTransportTest.java
new file mode 100644
index 00000000..99045d99
--- /dev/null
+++ b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/Apache5HttpTransportTest.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.api.client.http.apache.v5;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import com.google.api.client.http.GenericUrl;
+import com.google.api.client.http.HttpResponseException;
+import com.google.api.client.http.HttpTransport;
+import com.google.api.client.http.LowLevelHttpResponse;
+import com.google.api.client.util.ByteArrayStreamingContent;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.hc.client5.http.ConnectTimeoutException;
+import org.apache.hc.client5.http.HttpHostConnectException;
+import org.apache.hc.client5.http.classic.HttpClient;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.EntityDetails;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.HttpRequestInterceptor;
+import org.apache.hc.core5.http.HttpRequestMapper;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
+import org.apache.hc.core5.http.impl.io.HttpRequestExecutor;
+import org.apache.hc.core5.http.impl.io.HttpService;
+import org.apache.hc.core5.http.io.HttpClientConnection;
+import org.apache.hc.core5.http.io.HttpRequestHandler;
+import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
+import org.apache.hc.core5.http.io.support.BasicHttpServerRequestHandler;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.http.protocol.HttpProcessor;
+import org.junit.Assert;
+import org.junit.Test;
+
+/** Tests {@link Apache5HttpTransport}. */
+public class Apache5HttpTransportTest {
+
+  @Test
+  public void testApacheHttpTransport() {
+    Apache5HttpTransport transport = new Apache5HttpTransport();
+    checkHttpTransport(transport);
+    assertFalse(transport.isMtls());
+  }
+
+  @Test
+  public void testApacheHttpTransportWithParam() {
+    Apache5HttpTransport transport = new Apache5HttpTransport(HttpClients.custom().build(), true);
+    checkHttpTransport(transport);
+    assertTrue(transport.isMtls());
+  }
+
+  @Test
+  public void testNewDefaultHttpClient() {
+    HttpClient client = Apache5HttpTransport.newDefaultHttpClient();
+    checkHttpClient(client);
+  }
+
+  private void checkHttpTransport(Apache5HttpTransport transport) {
+    assertNotNull(transport);
+    HttpClient client = transport.getHttpClient();
+    checkHttpClient(client);
+  }
+
+  private void checkHttpClient(HttpClient client) {
+    assertNotNull(client);
+    // TODO(chingor): Is it possible to test this effectively? The newer HttpClient implementations
+    // are read-only and we're testing that we built the client with the right configuration
+  }
+
+  @Test
+  public void testRequestsWithContent() throws IOException {
+    // This test confirms that we can set the content on any type of request
+    HttpClient mockClient =
+        new MockHttpClient() {
+          @Override
+          public ClassicHttpResponse executeOpen(
+              HttpHost target, ClassicHttpRequest request, HttpContext context) {
+            return new MockClassicHttpResponse();
+          }
+        };
+    Apache5HttpTransport transport = new Apache5HttpTransport(mockClient);
+
+    // Test GET.
+    execute(transport.buildRequest("GET", "http://www.test.url"));
+    // Test DELETE.
+    execute(transport.buildRequest("DELETE", "http://www.test.url"));
+    // Test HEAD.
+    execute(transport.buildRequest("HEAD", "http://www.test.url"));
+
+    // Test PATCH.
+    execute(transport.buildRequest("PATCH", "http://www.test.url"));
+    // Test PUT.
+    execute(transport.buildRequest("PUT", "http://www.test.url"));
+    // Test POST.
+    execute(transport.buildRequest("POST", "http://www.test.url"));
+    // Test PATCH.
+    execute(transport.buildRequest("PATCH", "http://www.test.url"));
+  }
+
+  private void execute(Apache5HttpRequest request) throws IOException {
+    byte[] bytes = "abc".getBytes(StandardCharsets.UTF_8);
+    request.setStreamingContent(new ByteArrayStreamingContent(bytes));
+    request.setContentType("text/html");
+    request.setContentLength(bytes.length);
+    request.execute();
+  }
+
+  @Test
+  public void testRequestShouldNotFollowRedirects() throws IOException {
+    final AtomicInteger requestsAttempted = new AtomicInteger(0);
+    HttpRequestExecutor requestExecutor =
+        new HttpRequestExecutor() {
+          @Override
+          public ClassicHttpResponse execute(
+              ClassicHttpRequest request, HttpClientConnection connection, HttpContext context)
+              throws IOException, HttpException {
+            ClassicHttpResponse response = new MockClassicHttpResponse();
+            response.setCode(302);
+            response.setReasonPhrase(null);
+            response.addHeader("location", "https://google.com/path");
+            response.addHeader(HttpHeaders.SET_COOKIE, "");
+            requestsAttempted.incrementAndGet();
+            return response;
+          }
+        };
+    HttpClient client = HttpClients.custom().setRequestExecutor(requestExecutor).build();
+    Apache5HttpTransport transport = new Apache5HttpTransport(client);
+    Apache5HttpRequest request = transport.buildRequest("GET", "https://google.com");
+    LowLevelHttpResponse response = request.execute();
+    assertEquals(1, requestsAttempted.get());
+    assertEquals(302, response.getStatusCode());
+  }
+
+  @Test
+  public void testRequestCanSetHeaders() {
+    final AtomicBoolean interceptorCalled = new AtomicBoolean(false);
+    HttpClient client =
+        HttpClients.custom()
+            .addRequestInterceptorFirst(
+                new HttpRequestInterceptor() {
+                  @Override
+                  public void process(
+                      HttpRequest request, EntityDetails details, HttpContext context)
+                      throws HttpException, IOException {
+                    Header header = request.getFirstHeader("foo");
+                    assertNotNull("Should have found header", header);
+                    assertEquals("bar", header.getValue());
+                    interceptorCalled.set(true);
+                    throw new IOException("cancelling request");
+                  }
+                })
+            .build();
+
+    Apache5HttpTransport transport = new Apache5HttpTransport(client);
+    Apache5HttpRequest request = transport.buildRequest("GET", "https://google.com");
+    request.addHeader("foo", "bar");
+    try {
+      LowLevelHttpResponse response = request.execute();
+      fail("should not actually make the request");
+    } catch (IOException exception) {
+      assertEquals("cancelling request", exception.getMessage());
+    }
+    assertTrue("Expected to have called our test interceptor", interceptorCalled.get());
+  }
+
+  @Test(timeout = 10_000L)
+  public void testConnectTimeout() {
+    // TODO(chanseok): Java 17 returns an IOException (SocketException: Network is unreachable).
+    // Figure out a way to verify connection timeout works on Java 17+.
+    assumeTrue(System.getProperty("java.version").compareTo("17") < 0);
+
+    HttpTransport httpTransport = new Apache5HttpTransport();
+    GenericUrl url = new GenericUrl("http://google.com:81");
+    try {
+      httpTransport.createRequestFactory().buildGetRequest(url).setConnectTimeout(100).execute();
+      fail("should have thrown an exception");
+    } catch (HttpHostConnectException | ConnectTimeoutException expected) {
+      // expected
+    } catch (IOException e) {
+      fail("unexpected IOException: " + e.getClass().getName() + ": " + e.getMessage());
+    }
+  }
+
+  private static class FakeServer implements AutoCloseable {
+    private final HttpServer server;
+
+    FakeServer(final HttpRequestHandler httpHandler) throws IOException {
+      HttpRequestMapper<HttpRequestHandler> mapper =
+          new HttpRequestMapper<HttpRequestHandler>() {
+            @Override
+            public HttpRequestHandler resolve(HttpRequest request, HttpContext context)
+                throws HttpException {
+              return httpHandler;
+            };
+          };
+      server =
+          new HttpServer(
+              0,
+              HttpService.builder()
+                  .withHttpProcessor(
+                      new HttpProcessor() {
+                        @Override
+                        public void process(
+                            HttpRequest request, EntityDetails entity, HttpContext context)
+                            throws HttpException, IOException {}
+
+                        @Override
+                        public void process(
+                            HttpResponse response, EntityDetails entity, HttpContext context)
+                            throws HttpException, IOException {}
+                      })
+                  .withHttpServerRequestHandler(new BasicHttpServerRequestHandler(mapper))
+                  .build(),
+              null,
+              null,
+              null,
+              null,
+              null,
+              null);
+      //       server.createContext("/", httpHandler);
+      server.start();
+    }
+
+    public int getPort() {
+      return server.getLocalPort();
+    }
+
+    @Override
+    public void close() {
+      server.initiateShutdown();
+    }
+  }
+
+  @Test
+  public void testNormalizedUrl() throws IOException {
+    final HttpRequestHandler handler =
+        new HttpRequestHandler() {
+          @Override
+          public void handle(
+              ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context)
+              throws HttpException, IOException {
+            // Extract the request URI and convert to bytes
+            byte[] responseData = request.getRequestUri().getBytes(StandardCharsets.UTF_8);
+
+            // Set the response headers (status code and content length)
+            response.setCode(HttpStatus.SC_OK);
+            response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(responseData.length));
+
+            // Set the response entity (body)
+            ByteArrayEntity entity = new ByteArrayEntity(responseData, ContentType.TEXT_PLAIN);
+            response.setEntity(entity);
+          }
+        };
+    try (FakeServer server = new FakeServer(handler)) {
+      HttpTransport transport = new Apache5HttpTransport();
+      GenericUrl testUrl = new GenericUrl("http://localhost/foo//bar");
+      testUrl.setPort(server.getPort());
+      com.google.api.client.http.HttpResponse response =
+          transport.createRequestFactory().buildGetRequest(testUrl).execute();
+      assertEquals(200, response.getStatusCode());
+      assertEquals("/foo//bar", response.parseAsString());
+    }
+  }
+
+  @Test
+  public void testReadErrorStream() throws IOException {
+    final HttpRequestHandler handler =
+        new HttpRequestHandler() {
+          @Override
+          public void handle(
+              ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context)
+              throws HttpException, IOException {
+            byte[] responseData = "Forbidden".getBytes(StandardCharsets.UTF_8);
+            response.setCode(HttpStatus.SC_FORBIDDEN); // 403 Forbidden
+            response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(responseData.length));
+            ByteArrayEntity entity = new ByteArrayEntity(responseData, ContentType.TEXT_PLAIN);
+            response.setEntity(entity);
+          }
+        };
+    try (FakeServer server = new FakeServer(handler)) {
+      HttpTransport transport = new Apache5HttpTransport();
+      GenericUrl testUrl = new GenericUrl("http://localhost/foo//bar");
+      testUrl.setPort(server.getPort());
+      com.google.api.client.http.HttpRequest getRequest =
+          transport.createRequestFactory().buildGetRequest(testUrl);
+      getRequest.setThrowExceptionOnExecuteError(false);
+      com.google.api.client.http.HttpResponse response = getRequest.execute();
+      assertEquals(403, response.getStatusCode());
+      assertEquals("Forbidden", response.parseAsString());
+    }
+  }
+
+  @Test
+  public void testReadErrorStream_withException() throws IOException {
+    final HttpRequestHandler handler =
+        new HttpRequestHandler() {
+          @Override
+          public void handle(
+              ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context)
+              throws HttpException, IOException {
+            byte[] responseData = "Forbidden".getBytes(StandardCharsets.UTF_8);
+            response.setCode(HttpStatus.SC_FORBIDDEN); // 403 Forbidden
+            response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(responseData.length));
+            ByteArrayEntity entity = new ByteArrayEntity(responseData, ContentType.TEXT_PLAIN);
+            response.setEntity(entity);
+          }
+        };
+    try (FakeServer server = new FakeServer(handler)) {
+      HttpTransport transport = new Apache5HttpTransport();
+      GenericUrl testUrl = new GenericUrl("http://localhost/foo//bar");
+      testUrl.setPort(server.getPort());
+      com.google.api.client.http.HttpRequest getRequest =
+          transport.createRequestFactory().buildGetRequest(testUrl);
+      try {
+        getRequest.execute();
+        Assert.fail();
+      } catch (HttpResponseException ex) {
+        assertEquals("Forbidden", ex.getContent());
+      }
+    }
+  }
+
+  private boolean isWindows() {
+    return System.getProperty("os.name").startsWith("Windows");
+  }
+}
diff --git a/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/MockClassicHttpResponse.java b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/MockClassicHttpResponse.java
new file mode 100644
index 00000000..09172174
--- /dev/null
+++ b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/MockClassicHttpResponse.java
@@ -0,0 +1,182 @@
+package com.google.api.client.http.apache.v5;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.stream.Collectors;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpVersion;
+import org.apache.hc.core5.http.ProtocolException;
+import org.apache.hc.core5.http.ProtocolVersion;
+
+public class MockClassicHttpResponse implements ClassicHttpResponse {
+  List<Header> headers = new ArrayList<>();
+  int code = 200;
+
+  @Override
+  public int getCode() {
+    return code;
+  }
+
+  @Override
+  public void setCode(int code) {
+    this.code = code;
+  }
+
+  @Override
+  public String getReasonPhrase() {
+    return null;
+  }
+
+  @Override
+  public void setReasonPhrase(String reason) {}
+
+  @Override
+  public Locale getLocale() {
+    return null;
+  }
+
+  @Override
+  public void setLocale(Locale loc) {}
+
+  @Override
+  public void setVersion(ProtocolVersion version) {}
+
+  @Override
+  public ProtocolVersion getVersion() {
+    return HttpVersion.HTTP_1_1;
+  }
+
+  @Override
+  public void addHeader(Header header) {
+    headers.add(header);
+  }
+
+  @Override
+  public void addHeader(String name, Object value) {
+    addHeader(newHeader(name, value));
+  }
+
+  private Header newHeader(String key, Object value) {
+    return new Header() {
+      @Override
+      public boolean isSensitive() {
+        return false;
+      }
+
+      @Override
+      public String getName() {
+        return key;
+      }
+
+      @Override
+      public String getValue() {
+        return value.toString();
+      }
+    };
+  }
+
+  @Override
+  public void setHeader(Header header) {
+    if (headers.contains(header)) {
+      int index = headers.indexOf(header);
+      headers.set(index, header);
+    } else {
+      addHeader(header);
+    }
+  }
+
+  @Override
+  public void setHeader(String name, Object value) {
+    setHeader(newHeader(name, value));
+  }
+
+  @Override
+  public void setHeaders(Header... headers) {
+    for (Header header : headers) {
+      setHeader(header);
+    }
+  }
+
+  @Override
+  public boolean removeHeader(Header header) {
+    if (headers.contains(header)) {
+      headers.remove(headers.indexOf(header));
+      return true;
+    }
+    return false;
+  }
+
+  @Override
+  public boolean removeHeaders(String name) {
+    int initialSize = headers.size();
+    for (Header header :
+        headers.stream().filter(h -> h.getName() == name).collect(Collectors.toList())) {
+      removeHeader(header);
+    }
+    return headers.size() < initialSize;
+  }
+
+  @Override
+  public boolean containsHeader(String name) {
+    return headers.stream().anyMatch(h -> h.getName() == name);
+  }
+
+  @Override
+  public int countHeaders(String name) {
+    return headers.size();
+  }
+
+  @Override
+  public Header getFirstHeader(String name) {
+    return headers.stream().findFirst().orElse(null);
+  }
+
+  @Override
+  public Header getHeader(String name) throws ProtocolException {
+    return headers.stream().filter(h -> h.getName() == name).findFirst().orElse(null);
+  }
+
+  @Override
+  public Header[] getHeaders() {
+    return headers.toArray(new Header[0]);
+  }
+
+  @Override
+  public Header[] getHeaders(String name) {
+    return headers.stream()
+        .filter(h -> h.getName() == name)
+        .collect(Collectors.toList())
+        .toArray(new Header[0]);
+  }
+
+  @Override
+  public Header getLastHeader(String name) {
+    return headers.isEmpty() ? null : headers.get(headers.size() - 1);
+  }
+
+  @Override
+  public Iterator<Header> headerIterator() {
+    return headers.iterator();
+  }
+
+  @Override
+  public Iterator<Header> headerIterator(String name) {
+    return headers.stream().filter(h -> h.getName() == name).iterator();
+  }
+
+  @Override
+  public void close() throws IOException {}
+
+  @Override
+  public HttpEntity getEntity() {
+    return null;
+  }
+
+  @Override
+  public void setEntity(HttpEntity entity) {}
+}
diff --git a/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/MockHttpClient.java b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/MockHttpClient.java
new file mode 100644
index 00000000..8d26096c
--- /dev/null
+++ b/google-http-client-apache-v5/src/test/java/com/google/api/client/http/apache/v5/MockHttpClient.java
@@ -0,0 +1,86 @@
+package com.google.api.client.http.apache.v5;
+
+import com.google.api.client.util.Preconditions;
+import java.io.IOException;
+import org.apache.hc.client5.http.classic.HttpClient;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.io.HttpClientResponseHandler;
+import org.apache.hc.core5.http.protocol.HttpContext;
+
+public class MockHttpClient implements HttpClient {
+
+  /** HTTP response code to use. */
+  int responseCode;
+
+  /** Returns the HTTP response code to use. */
+  public final int getResponseCode() {
+    return responseCode;
+  }
+
+  /** Sets the HTTP response code to use. */
+  public MockHttpClient setResponseCode(int responseCode) {
+    Preconditions.checkArgument(responseCode >= 0);
+    this.responseCode = responseCode;
+    return this;
+  }
+
+  @Override
+  public HttpResponse execute(ClassicHttpRequest request) throws IOException {
+    return null;
+  }
+
+  @Override
+  public HttpResponse execute(ClassicHttpRequest request, HttpContext context) throws IOException {
+    return null;
+  }
+
+  @Override
+  public ClassicHttpResponse execute(HttpHost target, ClassicHttpRequest request)
+      throws IOException {
+    return null;
+  }
+
+  @Override
+  public HttpResponse execute(HttpHost target, ClassicHttpRequest request, HttpContext context)
+      throws IOException {
+    return null;
+  }
+
+  @Override
+  public <T> T execute(
+      ClassicHttpRequest request, HttpClientResponseHandler<? extends T> responseHandler)
+      throws IOException {
+    return null;
+  }
+
+  @Override
+  public <T> T execute(
+      ClassicHttpRequest request,
+      HttpContext context,
+      HttpClientResponseHandler<? extends T> responseHandler)
+      throws IOException {
+    return null;
+  }
+
+  @Override
+  public <T> T execute(
+      HttpHost target,
+      ClassicHttpRequest request,
+      HttpClientResponseHandler<? extends T> responseHandler)
+      throws IOException {
+    return null;
+  }
+
+  @Override
+  public <T> T execute(
+      HttpHost target,
+      ClassicHttpRequest request,
+      HttpContext context,
+      HttpClientResponseHandler<? extends T> responseHandler)
+      throws IOException {
+    return null;
+  }
+}