Full Code of kptfh/feign-reactive for AI

master ae31dddc5fef cached
154 files
370.5 KB
88.7k tokens
716 symbols
1 requests
Download .txt
Showing preview only (420K chars total). Download the full file or copy to clipboard to get everything.
Repository: kptfh/feign-reactive
Branch: master
Commit: ae31dddc5fef
Files: 154
Total size: 370.5 KB

Directory structure:
gitextract_wskod754/

├── .gitignore
├── LICENSE
├── README.md
├── feign-reactor-cloud/
│   ├── pom.xml
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── reactivefeign/
│       │           └── cloud/
│       │               ├── CloudReactiveFeign.java
│       │               ├── methodhandler/
│       │               │   ├── HystrixMethodHandler.java
│       │               │   └── HystrixMethodHandlerFactory.java
│       │               └── publisher/
│       │                   └── RibbonPublisherClient.java
│       └── test/
│           └── java/
│               └── reactivefeign/
│                   └── cloud/
│                       ├── HystrixReactiveHttpClientTest.java
│                       └── LoadBalancingReactiveHttpClientTest.java
├── feign-reactor-core/
│   ├── pom.xml
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── reactivefeign/
│       │           ├── ReactiveContract.java
│       │           ├── ReactiveFeign.java
│       │           ├── ReactiveInvocationHandler.java
│       │           ├── ReactiveOptions.java
│       │           ├── ReactiveRetryPolicy.java
│       │           ├── ReactiveRetryers.java
│       │           ├── client/
│       │           │   ├── DelegatingReactiveHttpResponse.java
│       │           │   ├── InterceptorReactiveHttpClient.java
│       │           │   ├── LoggerReactiveHttpClient.java
│       │           │   ├── ReactiveHttpClient.java
│       │           │   ├── ReactiveHttpRequest.java
│       │           │   ├── ReactiveHttpRequestInterceptor.java
│       │           │   ├── ReactiveHttpResponse.java
│       │           │   ├── ReadTimeoutException.java
│       │           │   ├── ResponseMappers.java
│       │           │   ├── StatusHandlerReactiveHttpClient.java
│       │           │   └── statushandler/
│       │           │       ├── CompositeStatusHandler.java
│       │           │       ├── ReactiveStatusHandler.java
│       │           │       └── ReactiveStatusHandlers.java
│       │           ├── methodhandler/
│       │           │   ├── DefaultMethodHandler.java
│       │           │   ├── FluxMethodHandler.java
│       │           │   ├── MethodHandler.java
│       │           │   ├── MethodHandlerFactory.java
│       │           │   ├── MonoMethodHandler.java
│       │           │   ├── PublisherClientMethodHandler.java
│       │           │   └── ReactiveMethodHandlerFactory.java
│       │           ├── publisher/
│       │           │   ├── FluxPublisherHttpClient.java
│       │           │   ├── FluxRetryPublisherHttpClient.java
│       │           │   ├── MonoPublisherHttpClient.java
│       │           │   ├── MonoRetryPublisherHttpClient.java
│       │           │   ├── PublisherClientFactory.java
│       │           │   ├── PublisherHttpClient.java
│       │           │   └── RetryPublisherHttpClient.java
│       │           └── utils/
│       │               ├── FeignUtils.java
│       │               ├── HttpUtils.java
│       │               ├── MultiValueMapUtils.java
│       │               └── Pair.java
│       └── test/
│           └── java/
│               └── reactivefeign/
│                   ├── CompressionTest.java
│                   ├── ConnectionTimeoutTest.java
│                   ├── ContractTest.java
│                   ├── DefaultMethodTest.java
│                   ├── LoggerTest.java
│                   ├── NotFoundTest.java
│                   ├── ReactivityTest.java
│                   ├── ReadTimeoutTest.java
│                   ├── RequestInterceptorTest.java
│                   ├── RetryingTest.java
│                   ├── SmokeTest.java
│                   ├── StatusHandlerTest.java
│                   ├── TestUtils.java
│                   ├── allfeatures/
│                   │   ├── AllFeaturesApi.java
│                   │   ├── AllFeaturesController.java
│                   │   └── AllFeaturesTest.java
│                   ├── resttemplate/
│                   │   ├── CompressionTest.java
│                   │   ├── ConnectionTimeoutTest.java
│                   │   ├── ContractTest.java
│                   │   ├── DefaultMethodTest.java
│                   │   ├── LoggerTest.java
│                   │   ├── NotFoundTest.java
│                   │   ├── ReactivityTest.java
│                   │   ├── ReadTimeoutTest.java
│                   │   ├── RequestInterceptorTest.java
│                   │   ├── RetryingTest.java
│                   │   ├── SmokeTest.java
│                   │   ├── StatusHandlerTest.java
│                   │   └── client/
│                   │       ├── RestTemplateFakeReactiveFeign.java
│                   │       └── RestTemplateFakeReactiveHttpClient.java
│                   └── testcase/
│                       ├── IcecreamServiceApi.java
│                       ├── IcecreamServiceApiBroken.java
│                       ├── IcecreamServiceApiBrokenByCopy.java
│                       └── domain/
│                           ├── Bill.java
│                           ├── Flavor.java
│                           ├── IceCreamOrder.java
│                           ├── Mixin.java
│                           └── OrderGenerator.java
├── feign-reactor-jetty/
│   ├── pom.xml
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── reactivefeign/
│       │           └── jetty/
│       │               ├── JettyReactiveFeign.java
│       │               ├── client/
│       │               │   ├── JettyReactiveHttpClient.java
│       │               │   └── JettyReactiveHttpResponse.java
│       │               └── utils/
│       │                   └── ProxyPostProcessor.java
│       └── test/
│           ├── java/
│           │   └── reactivefeign/
│           │       └── jetty/
│           │           ├── CompressionTest.java
│           │           ├── ConnectionTimeoutTest.java
│           │           ├── ContractTest.java
│           │           ├── DefaultMethodTest.java
│           │           ├── LoggerTest.java
│           │           ├── NotFoundTest.java
│           │           ├── ReactivityTest.java
│           │           ├── ReadTimeoutTest.java
│           │           ├── RequestInterceptorTest.java
│           │           ├── RetryingTest.java
│           │           ├── SmokeTest.java
│           │           ├── StatusHandlerTest.java
│           │           └── allfeatures/
│           │               └── AllFeaturesTest.java
│           └── resources/
│               └── log4j2.xml
├── feign-reactor-rx2/
│   ├── pom.xml
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── reactivefeign/
│       │           └── rx2/
│       │               ├── Rx2Contract.java
│       │               ├── Rx2ReactiveFeign.java
│       │               ├── client/
│       │               │   └── statushandler/
│       │               │       ├── Rx2ReactiveStatusHandler.java
│       │               │       ├── Rx2StatusHandler.java
│       │               │       └── Rx2StatusHandlers.java
│       │               └── methodhandler/
│       │                   ├── Rx2MethodHandler.java
│       │                   ├── Rx2MethodHandlerFactory.java
│       │                   └── Rx2PublisherClientMethodHandler.java
│       └── test/
│           └── java/
│               └── reactivefeign/
│                   └── rx2/
│                       ├── ContractTest.java
│                       ├── DefaultMethodTest.java
│                       ├── LoggerTest.java
│                       ├── NotFoundTest.java
│                       ├── ReactivityTest.java
│                       ├── ReadTimeoutTest.java
│                       ├── RequestInterceptorTest.java
│                       ├── SmokeTest.java
│                       ├── StatusHandlerTest.java
│                       ├── TestUtils.java
│                       └── testcase/
│                           ├── IcecreamServiceApi.java
│                           ├── IcecreamServiceApiBroken.java
│                           └── domain/
│                               ├── Bill.java
│                               ├── Flavor.java
│                               ├── IceCreamOrder.java
│                               ├── Mixin.java
│                               └── OrderGenerator.java
├── feign-reactor-webclient/
│   ├── pom.xml
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── reactivefeign/
│       │           └── webclient/
│       │               ├── WebReactiveFeign.java
│       │               └── client/
│       │                   ├── WebReactiveHttpClient.java
│       │                   └── WebReactiveHttpResponse.java
│       └── test/
│           ├── java/
│           │   └── reactivefeign/
│           │       └── webclient/
│           │           ├── CompressionTest.java
│           │           ├── ConnectionTimeoutTest.java
│           │           ├── ContractTest.java
│           │           ├── DefaultMethodTest.java
│           │           ├── LoggerTest.java
│           │           ├── NotFoundTest.java
│           │           ├── ReactivityTest.java
│           │           ├── ReadTimeoutTest.java
│           │           ├── RequestInterceptorTest.java
│           │           ├── RetryingTest.java
│           │           ├── SmokeTest.java
│           │           ├── StatusHandlerTest.java
│           │           └── allfeatures/
│           │               ├── AllFeaturesTest.java
│           │               ├── WebClientFeaturesApi.java
│           │               ├── WebClientFeaturesController.java
│           │               └── WebClientFeaturesTest.java
│           └── resources/
│               └── log4j2.xml
├── pom.xml
└── settings.xml

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
target/
!.mvn/wrapper/maven-wrapper.jar

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr

### NetBeans ###
nbproject/private/
build/
nbbuild/
dist/
nbdist/
.nb-gradle/

================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "{}"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright {yyyy} {name of copyright owner}

   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.


================================================
FILE: README.md
================================================
Happy to announce that from now Java Reactive Feign client is officially backed by Playtika. All development will be conducted in Playtika fork https://github.com/Playtika/feign-reactive

[Subscribe to stay up to date 🙂](https://www.youtube.com/channel/UCAIRpdkVAj1RT6butHUV9yg)

# feign-reactive

[ ![Download](https://api.bintray.com/packages/kptfh/feign-reactive/client/images/download.svg) ](https://bintray.com/kptfh/feign-reactive/client/_latestVersion)

Use Feign with Spring WebFlux

## Overview

Implementation of Feign on Spring WebClient. Brings you the best of two worlds together : 
concise syntax of Feign to write client side API on fast, asynchronous and
non-blocking HTTP client of Spring WebClient.

## Modules
  
  **_feign-reactor-core_** : base classes and interfaces that should allow to implement alternative reactor Feign
  
  **_feign-reactor-webclient_** : Spring WebClient based implementation of reactor Feign 
  
  **_feign-reactor-cloud_** : Spring Cloud implementation of reactor Feign (Ribbon/Hystrix)
  
  **_feign-reactor-rx2_** : Rx2 compatible implementation of reactor Feign (depends on feign-reactor-webclient)
  
  **_feign-reactor-jetty_** : experimental Reactive Jetty client based implementation of reactor Feign (doesn't depend on feign-reactor-webclient). In future will allow to write pure Rx2 version.
  - have greater reactivity level then Spring WebClient. By default don't collect body to list instead starts sending request body as stream. 
  - starts receiving reactive response before all reactive request body has been sent
  - process Flux<`String`> correctly in request and response body  

## Usage

Write Feign API as usual, but every method of interface
 - may accept `org.reactivestreams.Publisher` as body
 - must return `reactor.core.publisher.Mono` or `reactor.core.publisher.Flux`.

```java
@Headers({ "Accept: application/json" })
public interface IcecreamServiceApi {

  @RequestLine("GET /icecream/flavors")
  Flux<Flavor> getAvailableFlavors();

  @RequestLine("GET /icecream/mixins")
  Flux<Mixin> getAvailableMixins();

  @RequestLine("POST /icecream/orders")
  @Headers("Content-Type: application/json")
  Mono<Bill> makeOrder(IceCreamOrder order);

  @RequestLine("GET /icecream/orders/{orderId}")
  Mono<IceCreamOrder> findOrder(@Param("orderId") int orderId);

  @RequestLine("POST /icecream/bills/pay")
  @Headers("Content-Type: application/json")
  Mono<Void> payBill(Publisher<Bill> bill);
}
```
Build the client :

```java

/* Create instance of your API */
IcecreamServiceApi client = ReactiveFeign
    .builder()
    .target(IcecreamServiceApi.class, "http://www.icecreame.com")

/* Execute nonblocking requests */
Flux<Flavor> flavors = icecreamApi.getAvailableFlavors();
Flux<Mixin> mixins = icecreamApi.getAvailableMixins();
```

or cloud aware client :

```java
 IcecreamServiceApi client = CloudReactiveFeign.<IcecreamServiceApi>builder()
    .setFallback(new TestInterface() {
        @Override
        public Mono<String> get() {
            return Mono.just("fallback");
        }
    })
    .setLoadBalancerCommand(
         LoadBalancerCommand.builder()
                 .withLoadBalancer(AbstractLoadBalancer.class.cast(getNamedLoadBalancer(serviceName)))
                 .withRetryHandler(new DefaultLoadBalancerRetryHandler(1, 1, true))
                 .build()
    )
    .target(IcecreamServiceApi.class, "http://" + serviceName);

/* Execute nonblocking requests */
Flux<Flavor> flavors = icecreamApi.getAvailableFlavors();
Flux<Mixin> mixins = icecreamApi.getAvailableMixins();
```

## Rx2 Usage 

Write Feign API as usual, but every method of interface
 - may accept `Flowable`, `Observable`, `Single` or `Maybe` as body
 - must return `Flowable`, `Observable`, `Single` or `Maybe`.

```java
@Headers({"Accept: application/json"})
public interface IcecreamServiceApi {

  @RequestLine("GET /icecream/flavors")
  Flowable<Flavor> getAvailableFlavors();

  @RequestLine("GET /icecream/mixins")
  Observable<Mixin> getAvailableMixins();

  @RequestLine("POST /icecream/orders")
  @Headers("Content-Type: application/json")
  Single<Bill> makeOrder(IceCreamOrder order);

  @RequestLine("GET /icecream/orders/{orderId}")
  Maybe<IceCreamOrder> findOrder(@Param("orderId") int orderId);

  @RequestLine("POST /icecream/bills/pay")
  @Headers("Content-Type: application/json")
  Single<Long> payBill(Bill bill);
```
Build the client :

```java

/* Create instance of your API */
IcecreamServiceApi client = Rx2ReactiveFeign
    .builder()
    .target(IcecreamServiceApi.class, "http://www.icecreame.com")

/* Execute nonblocking requests */
Flowable<Flavor> flavors = icecreamApi.getAvailableFlavors();
Observable<Mixin> mixins = icecreamApi.getAvailableMixins();
```

## Maven

```xml
<repositories>
    <repository>
        <id>bintray-kptfh-feign-reactive</id>
        <name>bintray</name>
        <url>https://dl.bintray.com/kptfh/feign-reactive</url>
    </repository>
</repositories>
...
<dependencies>
    ...
    
    <dependency>
        <groupId>io.github.reactivefeign</groupId>
        <artifactId>feign-reactor-cloud</artifactId>
        <version>1.0.0</version>
    </dependency>
    
    or if you don't need cloud specific functionality
    
    <dependency>
        <groupId>io.github.reactivefeign</groupId>
        <artifactId>feign-reactor-webclient</artifactId>
        <version>1.0.0</version>
    </dependency>
    
    or if you tend to use Rx2 interfaces
    
    <dependency>
            <groupId>io.github.reactivefeign</groupId>
            <artifactId>feign-reactor-rx2</artifactId>
            <version>1.0.0</version>
        </dependency>
    ...
</dependencies>
```

## License

Library distributed under Apache License Version 2.0.


================================================
FILE: feign-reactor-cloud/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<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>io.github.reactivefeign</groupId>
        <artifactId>feign-reactor</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <artifactId>feign-reactor-cloud</artifactId>

    <properties>
    </properties>

    <dependencies>

        <dependency>
            <groupId>io.github.reactivefeign</groupId>
            <artifactId>feign-reactor-webclient</artifactId>
        </dependency>

        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-core</artifactId>
        </dependency>

        <dependency>
            <groupId>com.netflix.archaius</groupId>
            <artifactId>archaius-core</artifactId>
        </dependency>

        <dependency>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon-core</artifactId>
        </dependency>

        <dependency>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon-loadbalancer</artifactId>
        </dependency>

        <dependency>
            <groupId>io.reactivex</groupId>
            <artifactId>rxjava</artifactId>
        </dependency>

        <dependency>
            <groupId>io.reactivex</groupId>
            <artifactId>rxjava-reactive-streams</artifactId>
        </dependency>

        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-core</artifactId>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>

        <!-- Tests -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.github.tomakehurst</groupId>
            <artifactId>wiremock</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

</project>

================================================
FILE: feign-reactor-cloud/src/main/java/reactivefeign/cloud/CloudReactiveFeign.java
================================================
package reactivefeign.cloud;

import com.netflix.client.ClientFactory;
import com.netflix.client.RetryHandler;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixObservableCommand;
import com.netflix.loadbalancer.reactive.LoadBalancerCommand;
import feign.Contract;
import feign.InvocationHandlerFactory;
import feign.MethodMetadata;
import feign.Target;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.reactive.function.client.WebClient;
import reactivefeign.ReactiveFeign;
import reactivefeign.ReactiveOptions;
import reactivefeign.ReactiveRetryPolicy;
import reactivefeign.client.ReactiveHttpRequestInterceptor;
import reactivefeign.client.ReactiveHttpResponse;
import reactivefeign.client.statushandler.ReactiveStatusHandler;
import reactivefeign.cloud.methodhandler.HystrixMethodHandlerFactory;
import reactivefeign.cloud.publisher.RibbonPublisherClient;
import reactivefeign.methodhandler.MethodHandlerFactory;
import reactivefeign.publisher.PublisherClientFactory;
import reactivefeign.publisher.PublisherHttpClient;
import reactivefeign.webclient.WebReactiveFeign;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.function.BiFunction;
import java.util.function.Function;

import static reactivefeign.utils.FeignUtils.returnPublisherType;

/**
 * Allows to specify ribbon {@link LoadBalancerCommand}
 * and HystrixObservableCommand.Setter with fallback factory.
 *
 * @author Sergii Karpenko
 */
public class CloudReactiveFeign extends ReactiveFeign {

    private static final Logger logger = LoggerFactory.getLogger(CloudReactiveFeign.class);

    private CloudReactiveFeign(ReactiveFeign.ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory) {
        super(targetToHandlersByName, factory);
    }

    public static <T> Builder<T> builder() {
        return new Builder<>();
    }

    public static <T> Builder<T> builder(WebClient webClient) {
        return new Builder<>(webClient);
    }

    public static class Builder<T> extends WebReactiveFeign.Builder<T> {

        private boolean hystrixEnabled = true;
        private SetterFactory commandSetterFactory = new DefaultSetterFactory();
        private Function<Throwable, ? extends T> fallbackFactory;
        private Function<String, LoadBalancerCommand<Object>> loadBalancerCommandFactory = s -> null;

        protected Builder() {
            super();
        }

        protected Builder(WebClient webClient) {
            super(webClient);
        }

        public void disableHystrix() {
            this.hystrixEnabled = false;
        }

        public Builder<T> setHystrixCommandSetterFactory(SetterFactory commandSetterFactory) {
            this.commandSetterFactory = commandSetterFactory;
            return this;
        }

        public Builder<T> setFallback(T fallback) {
            return setFallbackFactory(throwable -> fallback);
        }

        public Builder<T> setFallbackFactory(Function<Throwable, ? extends T> fallbackFactory) {
            this.fallbackFactory = fallbackFactory;
            return this;
        }

        public Builder<T> enableLoadBalancer(){
            return setLoadBalancerCommandFactory(serviceName ->
                    LoadBalancerCommand.builder()
                            .withLoadBalancer(ClientFactory.getNamedLoadBalancer(serviceName))
                            .build());
        }

        public Builder<T> enableLoadBalancer(RetryHandler retryHandler){
            if(retryHandler.getMaxRetriesOnSameServer() > 0){
                logger.warn("Use retryWhen(ReactiveRetryPolicy retryPolicy) " +
                        "as it allow to configure retry delays (backoff)");
            }
            return setLoadBalancerCommandFactory(serviceName ->
                    LoadBalancerCommand.builder()
                    .withLoadBalancer(ClientFactory.getNamedLoadBalancer(serviceName))
                    .withRetryHandler(retryHandler)
                    .build());
        }


        public Builder<T> setLoadBalancerCommandFactory(
                Function<String, LoadBalancerCommand<Object>> loadBalancerCommandFactory) {
            this.loadBalancerCommandFactory = loadBalancerCommandFactory;
            return this;
        }

        @Override
        protected MethodHandlerFactory buildReactiveMethodHandlerFactory() {
            MethodHandlerFactory methodHandlerFactory = super.buildReactiveMethodHandlerFactory();
            return hystrixEnabled
                    ? new HystrixMethodHandlerFactory(
					methodHandlerFactory,
                    commandSetterFactory,
                    (Function<Throwable, Object>) fallbackFactory)
                    : methodHandlerFactory;
        }

        @Override
        protected PublisherClientFactory buildReactiveClientFactory() {
            PublisherClientFactory publisherClientFactory = super.buildReactiveClientFactory();
            return methodMetadata -> {
                PublisherHttpClient publisherClient = publisherClientFactory.apply(methodMetadata);
                String serviceName = extractServiceName(target.url());
                return new RibbonPublisherClient(loadBalancerCommandFactory.apply(serviceName),
                        publisherClient, returnPublisherType(methodMetadata));
            };
        }

        private String extractServiceName(String url){
            try {
                return new URI(url).getHost();
            } catch (URISyntaxException e) {
                throw new IllegalArgumentException("Can't extract service name from url", e);
            }
        }

        @Override
        public Builder<T> contract(final Contract contract) {
            super.contract(contract);
            return this;
        }

        @Override
        public Builder<T> requestInterceptor(ReactiveHttpRequestInterceptor requestInterceptor) {
            super.requestInterceptor(requestInterceptor);
            return this;
        }

        @Override
        public Builder<T> decode404() {
            super.decode404();
            return this;
        }

        @Override
        public Builder<T> statusHandler(ReactiveStatusHandler statusHandler) {
            super.statusHandler(statusHandler);
            return this;
        }

        @Override
        public ReactiveFeign.Builder<T> responseMapper(
                BiFunction<MethodMetadata, ReactiveHttpResponse, ReactiveHttpResponse> responseMapper) {
            super.responseMapper(responseMapper);
            return this;
        }

        @Override
        public Builder<T> retryWhen(ReactiveRetryPolicy retryPolicy){
            super.retryWhen(retryPolicy);
            return this;
        }

        @Override
        public Builder<T> options(final ReactiveOptions options) {
            super.options(options);
            return this;
        }
    }

    public interface SetterFactory {
        HystrixObservableCommand.Setter create(Target<?> target, MethodMetadata methodMetadata);
    }

    public static class DefaultSetterFactory implements SetterFactory {
        @Override
        public HystrixObservableCommand.Setter create(Target<?> target, MethodMetadata methodMetadata) {
            String groupKey = target.name();
            String commandKey = methodMetadata.configKey();
            return HystrixObservableCommand.Setter
                    .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
                    .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey));
        }
    }

}


================================================
FILE: feign-reactor-cloud/src/main/java/reactivefeign/cloud/methodhandler/HystrixMethodHandler.java
================================================
package reactivefeign.cloud.methodhandler;

import com.netflix.hystrix.HystrixObservableCommand;
import feign.MethodMetadata;
import feign.Target;
import org.reactivestreams.Publisher;
import org.springframework.lang.Nullable;
import reactivefeign.cloud.CloudReactiveFeign;
import reactivefeign.methodhandler.MethodHandler;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import rx.Observable;
import rx.RxReactiveStreams;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.function.Function;

import static feign.Feign.configKey;
import static feign.Util.checkNotNull;

/**
 * @author Sergii Karpenko
 */
public class HystrixMethodHandler implements MethodHandler {

    private final Method method;
    private final Type returnPublisherType;
    private final MethodHandler methodHandler;
    private final Function<Throwable, Object> fallbackFactory;
    private final HystrixObservableCommand.Setter hystrixObservableCommandSetter;

    HystrixMethodHandler(
            Target target, MethodMetadata methodMetadata,
            MethodHandler methodHandler,
            CloudReactiveFeign.SetterFactory setterFactory,
            @Nullable
                    Function<Throwable, Object> fallbackFactory) {
        checkNotNull(target, "target must be not null");

        checkNotNull(methodMetadata, "methodMetadata must be not null");
        method = Arrays.stream(target.type().getMethods())
                .filter(method -> configKey(target.type(), method).equals(methodMetadata.configKey()))
                .findFirst().orElseThrow(() -> new IllegalArgumentException());
        method.setAccessible(true);

        returnPublisherType = ((ParameterizedType) methodMetadata.returnType()).getRawType();
        this.methodHandler = checkNotNull(methodHandler, "methodHandler must be not null");
        this.fallbackFactory = fallbackFactory;
        checkNotNull(setterFactory, "setterFactory must be not null");
        hystrixObservableCommandSetter = setterFactory.create(target, methodMetadata);
    }

    @Override
    @SuppressWarnings("unchecked")
    public Publisher<Object> invoke(final Object[] argv) {

        Observable<Object> observable = new HystrixObservableCommand<Object>(hystrixObservableCommandSetter) {
            @Override
            protected Observable<Object> construct() {
                Publisher publisher;
                try {
                    publisher = (Publisher) methodHandler.invoke(argv);
                } catch (Throwable throwable) {
                    publisher = Mono.error(throwable);
                }
                return RxReactiveStreams.toObservable(publisher);
            }

            @Override
            protected Observable<Object> resumeWithFallback() {
                if (fallbackFactory != null) {
                    Object fallback = fallbackFactory.apply(getExecutionException());
                    try {
                        Object fallbackValue = getFallbackValue(fallback, method, argv);
                        return RxReactiveStreams.toObservable((Publisher<Object>) fallbackValue);
                    } catch (Throwable e) {
                        return Observable.error(e);
                    }

                } else {
                    return super.resumeWithFallback();
                }
            }
        }.toObservable();

        if(returnPublisherType == Mono.class){
            return Mono.from(RxReactiveStreams.toPublisher(observable.toSingle()));
        } else if(returnPublisherType == Flux.class){
            return Flux.from(RxReactiveStreams.toPublisher(observable));
        } else {
            throw new IllegalArgumentException("Unknown returnPublisherType: " + returnPublisherType);
        }
    }

    protected Object getFallbackValue(Object target, Method method, Object[] argv) throws Throwable {
        return method.invoke(target, argv);
    }
}


================================================
FILE: feign-reactor-cloud/src/main/java/reactivefeign/cloud/methodhandler/HystrixMethodHandlerFactory.java
================================================
package reactivefeign.cloud.methodhandler;

import feign.MethodMetadata;
import feign.Target;
import org.springframework.lang.Nullable;
import reactivefeign.cloud.CloudReactiveFeign;
import reactivefeign.methodhandler.MethodHandler;
import reactivefeign.methodhandler.MethodHandlerFactory;

import java.lang.reflect.Method;
import java.util.function.Function;

import static feign.Util.checkNotNull;

public class HystrixMethodHandlerFactory implements MethodHandlerFactory {

    private final MethodHandlerFactory methodHandlerFactory;
    private final CloudReactiveFeign.SetterFactory commandSetterFactory;
    private final Function<Throwable, Object> fallbackFactory;

    public HystrixMethodHandlerFactory(MethodHandlerFactory methodHandlerFactory,
                                       CloudReactiveFeign.SetterFactory commandSetterFactory,
                                       @Nullable Function<Throwable, Object> fallbackFactory) {
        this.methodHandlerFactory = checkNotNull(methodHandlerFactory, "methodHandlerFactory must not be null");
        this.commandSetterFactory = checkNotNull(commandSetterFactory, "hystrixObservableCommandSetter must not be null");
        this.fallbackFactory = fallbackFactory;
    }

    @Override
    public MethodHandler create(final Target target, final MethodMetadata metadata) {
        return new HystrixMethodHandler(
                target, metadata,
                methodHandlerFactory.create(target, metadata),
                commandSetterFactory,
                fallbackFactory);
    }

    @Override
    public MethodHandler createDefault(Method method) {
        return methodHandlerFactory.createDefault(method);
    }
}


================================================
FILE: feign-reactor-cloud/src/main/java/reactivefeign/cloud/publisher/RibbonPublisherClient.java
================================================
package reactivefeign.cloud.publisher;

import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.reactive.LoadBalancerCommand;
import org.reactivestreams.Publisher;
import org.springframework.lang.Nullable;
import reactivefeign.client.ReactiveHttpRequest;
import reactivefeign.publisher.PublisherHttpClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import rx.Observable;
import rx.RxReactiveStreams;

import java.lang.reflect.Type;
import java.net.URI;
import java.net.URISyntaxException;

/**
 * @author Sergii Karpenko
 */
public class RibbonPublisherClient implements PublisherHttpClient {

    private final LoadBalancerCommand<Object> loadBalancerCommand;
    private final PublisherHttpClient publisherClient;
    private final Type publisherType;

    public RibbonPublisherClient(@Nullable LoadBalancerCommand<Object> loadBalancerCommand,
                                 PublisherHttpClient publisherClient,
                                 Type publisherType) {
        this.loadBalancerCommand = loadBalancerCommand;
        this.publisherClient = publisherClient;
        this.publisherType = publisherType;
    }

    @Override
    public Publisher<?> executeRequest(ReactiveHttpRequest request) {

        if (loadBalancerCommand != null) {
            Observable<?> observable = loadBalancerCommand.submit(server -> {

                ReactiveHttpRequest lbRequest = loadBalanceRequest(request, server);

                Publisher<Object> publisher = (Publisher<Object>)publisherClient.executeRequest(lbRequest);
                return RxReactiveStreams.toObservable(publisher);
            });

            Publisher<?> publisher = RxReactiveStreams.toPublisher(observable);

            if(publisherType == Mono.class){
                return Mono.from(publisher);
            } else if(publisherType == Flux.class){
                return Flux.from(publisher);
            } else {
                throw new IllegalArgumentException("Unknown publisherType: " + publisherType);
            }
        } else {
            return publisherClient.executeRequest(request);
        }
    }

    protected ReactiveHttpRequest loadBalanceRequest(ReactiveHttpRequest request, Server server) {
        URI uri = request.uri();
        try {
            URI lbUrl = new URI(uri.getScheme(), uri.getUserInfo(), server.getHost(), server.getPort(),
                    uri.getPath(), uri.getQuery(), uri.getFragment());
            return new ReactiveHttpRequest(request.method(), lbUrl, request.headers(), request.body());
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException(e);
        }
    }
}


================================================
FILE: feign-reactor-cloud/src/test/java/reactivefeign/cloud/HystrixReactiveHttpClientTest.java
================================================
package reactivefeign.cloud;

import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import com.netflix.hystrix.*;
import com.netflix.hystrix.exception.HystrixRuntimeException;
import feign.MethodMetadata;
import feign.Target;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static org.apache.http.HttpStatus.SC_SERVICE_UNAVAILABLE;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;

/**
 * @author Sergii Karpenko
 */
public class HystrixReactiveHttpClientTest {

    public static final int SLEEP_WINDOW = 100;
    public static final int VOLUME_THRESHOLD = 1;
    public static final String FALLBACK = "fallback";
    public static final String SUCCESS = "success!";
    @ClassRule
    public static WireMockClassRule server = new WireMockClassRule(wireMockConfig().dynamicPort());

    @Rule
    public ExpectedException expectedException = ExpectedException.none();

    private static int testNo = 0;

    private AtomicReference<HystrixCommandKey> lastCommandKey = new AtomicReference<>();

    @Before
    public void resetServers() {
        server.resetAll();

        testNo++;
    }

    @Test
    public void shouldFailAsNoFallback() {

        expectedException.expect(HystrixRuntimeException.class);
        expectedException.expectMessage(containsString("failed and no fallback available"));

        String body = "success!";
        LoadBalancingReactiveHttpClientTest.mockSuccessAfterSeveralAttempts(server, "/", 1, 598,
                aResponse()
                        .withStatus(200)
                        .withHeader("Content-Type", "application/json")
                        .withBody(body));

        LoadBalancingReactiveHttpClientTest.TestInterface client = CloudReactiveFeign.<LoadBalancingReactiveHttpClientTest.TestInterface>builder()
                .setHystrixCommandSetterFactory(getSetterFactory(testNo))
                .target(LoadBalancingReactiveHttpClientTest.TestInterface.class, "http://localhost:" + server.port());

        client.get().block();
    }

    @Test
    public void shouldNotFailDueToFallback() {

        String body = "success!";
        LoadBalancingReactiveHttpClientTest.mockSuccessAfterSeveralAttempts(server, "/", 1, 598,
                aResponse()
                        .withStatus(200)
                        .withHeader("Content-Type", "application/json")
                        .withBody(body));

        LoadBalancingReactiveHttpClientTest.TestInterface client = CloudReactiveFeign.<LoadBalancingReactiveHttpClientTest.TestInterface>builder()
                .setHystrixCommandSetterFactory(getSetterFactory(testNo))
                .setFallback(() -> Mono.just(FALLBACK))
                .target(LoadBalancingReactiveHttpClientTest.TestInterface.class, "http://localhost:" + server.port());

        String result = client.get().block();
        assertThat(result).isEqualTo(FALLBACK);
    }

    @Test
    public void shouldOpenCircuitBreakerAndCloseAfterSleepTime() throws InterruptedException {

        int callsNo = VOLUME_THRESHOLD + 1;
        LoadBalancingReactiveHttpClientTest.mockSuccessAfterSeveralAttempts(server, "/", VOLUME_THRESHOLD, SC_SERVICE_UNAVAILABLE,
                aResponse()
                        .withStatus(200)
                        .withHeader("Content-Type", "application/json")
                        .withBody(SUCCESS));

        LoadBalancingReactiveHttpClientTest.TestInterface client = CloudReactiveFeign.<LoadBalancingReactiveHttpClientTest.TestInterface>builder()
                .setHystrixCommandSetterFactory(getSetterFactory(testNo))
                .target(LoadBalancingReactiveHttpClientTest.TestInterface.class, "http://localhost:" + server.port());

        //check that circuit breaker get opened on volume threshold
        List<Object> results = IntStream.range(0, callsNo).mapToObj(i -> {
            try {
                return client.get().block();
            } catch (Throwable t) {
                return t;
            }
        }).collect(Collectors.toList());

        assertThat(server.getAllServeEvents().size()).isLessThan(callsNo);
        Throwable firstError = (Throwable) results.get(0);
        assertThat(firstError).isInstanceOf(HystrixRuntimeException.class);
        assertThat(firstError.getMessage())
                .contains("and no fallback available")
                .doesNotContain("short-circuited");
        assertThat(HystrixCircuitBreaker.Factory.getInstance(lastCommandKey.get())
                .isOpen())
                .isTrue();

        Throwable lastError = (Throwable) results.get(results.size() - 1);
        assertThat(lastError).isInstanceOf(HystrixRuntimeException.class);
        assertThat(lastError.getMessage())
                .contains("short-circuited and no fallback available.");

        //wait to circuit breaker get closed again
        Thread.sleep(SLEEP_WINDOW);

        //check that circuit breaker get closed again
        List<Object> resultsAfterSleep = IntStream.range(0, callsNo).mapToObj(i -> {
            try {
                return client.get().block();
            } catch (Throwable t) {
                return t;
            }
        }).collect(Collectors.toList());

        assertThat(resultsAfterSleep).containsOnly(SUCCESS);
        assertThat(HystrixCircuitBreaker.Factory.getInstance(lastCommandKey.get())
                .isOpen())
                .isFalse();
    }

    CloudReactiveFeign.SetterFactory getSetterFactory(int testNo) {
        return new CloudReactiveFeign.SetterFactory() {
            @Override
            public HystrixObservableCommand.Setter create(Target<?> target, MethodMetadata methodMetadata) {
                String groupKey = target.name();
                HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey(methodMetadata.configKey() + testNo);
                lastCommandKey.set(commandKey);
                return HystrixObservableCommand.Setter
                        .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
                        .andCommandKey(commandKey)
                        .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                                .withCircuitBreakerRequestVolumeThreshold(VOLUME_THRESHOLD)
                                .withExecutionTimeoutEnabled(false)
                                .withCircuitBreakerSleepWindowInMilliseconds(SLEEP_WINDOW)
                                .withMetricsHealthSnapshotIntervalInMilliseconds(10)
                        );
            }
        };
    }

}


================================================
FILE: feign-reactor-cloud/src/test/java/reactivefeign/cloud/LoadBalancingReactiveHttpClientTest.java
================================================
package reactivefeign.cloud;

import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import com.netflix.client.*;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixObservableCommand;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import feign.RequestLine;
import feign.RetryableException;
import org.junit.*;
import org.junit.rules.ExpectedException;
import reactivefeign.publisher.RetryPublisherHttpClient;
import reactor.core.publisher.Mono;

import java.util.stream.Stream;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.*;
import static reactivefeign.ReactiveRetryers.retry;

/**
 * @author Sergii Karpenko
 */
public class LoadBalancingReactiveHttpClientTest {

    @ClassRule
    public static WireMockClassRule server1 = new WireMockClassRule(wireMockConfig().dynamicPort());
    @ClassRule
    public static WireMockClassRule server2 = new WireMockClassRule(wireMockConfig().dynamicPort());

    @Rule
    public ExpectedException expectedException = ExpectedException.none();

    private static String serviceName = "LoadBalancingTargetTest-loadBalancingDefaultPolicyRoundRobin";

    @BeforeClass
    public static void setupServersList() throws ClientException {
        DefaultClientConfigImpl clientConfig = new DefaultClientConfigImpl();
        clientConfig.loadDefaultValues();
        clientConfig.setProperty(CommonClientConfigKey.NFLoadBalancerClassName, BaseLoadBalancer.class.getName());
        ILoadBalancer lb = ClientFactory.registerNamedLoadBalancerFromclientConfig(serviceName, clientConfig);
        lb.addServers(asList(new Server("localhost", server1.port()), new Server("localhost", server2.port())));
    }

    @Before
    public void resetServers() {
        server1.resetAll();
        server2.resetAll();
    }

    @Test
    public void shouldLoadBalanceRequests() {
        String body = "success!";
        mockSuccess(server1, body);
        mockSuccess(server2, body);

        TestInterface client = CloudReactiveFeign.<TestInterface>builder()
                .enableLoadBalancer()
                .setHystrixCommandSetterFactory(getSetterFactoryWithTimeoutDisabled())
                .target(TestInterface.class, "http://" + serviceName);

        String result1 = client.get().block();
        String result2 = client.get().block();

        assertThat(result1)
                .isEqualTo(result2)
                .isEqualTo(body);

        server1.verify(1, getRequestedFor(urlEqualTo("/")));
        server2.verify(1, getRequestedFor(urlEqualTo("/")));
    }

    @Test
    public void shouldFailAsPolicyWoRetries() {

        expectedException.expect(RuntimeException.class);
        expectedException.expectCause(allOf(isA(RetryPublisherHttpClient.OutOfRetriesException.class),
                hasProperty("cause", isA(RetryableException.class))));

        try {
            loadBalancingWithRetry(2, 0, 0);
        } catch (Throwable t) {
            assertThat(server1.getAllServeEvents().size() == 1
                    ^ server2.getAllServeEvents().size() == 1);
            throw t;
        }
    }

    @Test
    public void shouldRetryOnSameAndFail() {

        expectedException.expect(RuntimeException.class);
        expectedException.expectCause(allOf(isA(RetryPublisherHttpClient.OutOfRetriesException.class),
                                            hasProperty("cause", isA(RetryableException.class))));

        try {
            loadBalancingWithRetry(2, 1, 0);
        } catch (Throwable t) {
            assertThat(server1.getAllServeEvents().size() == 2
                    ^ server2.getAllServeEvents().size() == 2);
            throw t;
        }
    }

    @Test
    public void shouldRetryOnNextAndFail() {

        expectedException.expect(RuntimeException.class);
        expectedException.expectCause(isA(ClientException.class));

        try {
            loadBalancingWithRetry(2, 1, 1);
        } catch (Throwable t) {
            assertThat(server1.getAllServeEvents().size() == 2
                    && server2.getAllServeEvents().size() == 2);
            throw t;
        }
    }

    @Test
    public void shouldRetryOnSameAndSuccess() {

        loadBalancingWithRetry(2, 2, 0);

        assertThat(server1.getAllServeEvents().size() == 3
                ^ server2.getAllServeEvents().size() == 3);

    }

    private void loadBalancingWithRetry(int failedAttemptsNo, int retryOnSame, int retryOnNext) {
        String body = "success!";
        Stream.of(server1, server2).forEach(server -> {
            mockSuccessAfterSeveralAttempts(server, "/",
                    failedAttemptsNo, 503,
                    aResponse()
                            .withStatus(200)
                            .withHeader("Content-Type", "application/json")
                            .withBody(body));
        });

        RetryHandler retryHandler = new RequestSpecificRetryHandler(true, true,
                new DefaultLoadBalancerRetryHandler(0, retryOnNext, true), null);

        TestInterface client = CloudReactiveFeign.<TestInterface>builder()
                .retryWhen(retry(retryOnSame))
                .enableLoadBalancer(retryHandler)
                .setHystrixCommandSetterFactory(getSetterFactoryWithTimeoutDisabled())
                .target(TestInterface.class, "http://" + serviceName);

        String result = client.get().block();
        assertThat(result).isEqualTo(body);
    }

    private CloudReactiveFeign.SetterFactory getSetterFactoryWithTimeoutDisabled() {
        return (target, methodMetadata) -> {
			String groupKey = target.name();
			HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey(methodMetadata.configKey());
			return HystrixObservableCommand.Setter
					.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
					.andCommandKey(commandKey)
					.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
							.withExecutionTimeoutEnabled(false)
					);
		};
    }

    static void mockSuccess(WireMockClassRule server, String body) {
        server.stubFor(get(urlEqualTo("/"))
                .willReturn(aResponse()
                        .withStatus(200)
                        .withHeader("Content-Type", "application/json")
                        .withBody(body)));
    }

    static void mockSuccessAfterSeveralAttempts(WireMockClassRule server, String url,
                                                int failedAttemptsNo, int errorCode, ResponseDefinitionBuilder response) {
        String state = STARTED;
        for (int attempt = 0; attempt < failedAttemptsNo; attempt++) {
            String nextState = "attempt" + attempt;
            server.stubFor(get(urlEqualTo(url))
                    .inScenario("testScenario")
                    .whenScenarioStateIs(state)
                    .willReturn(aResponse()
                            .withStatus(errorCode)
                            .withHeader("Retry-After", "1"))
                    .willSetStateTo(nextState));

            state = nextState;
        }

        server.stubFor(get(urlEqualTo(url))
                .inScenario("testScenario")
                .whenScenarioStateIs(state)
                .willReturn(response));
    }


    interface TestInterface {

        @RequestLine("GET /")
        Mono<String> get();
    }
}


================================================
FILE: feign-reactor-core/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!--

    Copyright 2018 The Feign Authors

    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.

-->
<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>io.github.reactivefeign</groupId>
        <artifactId>feign-reactor</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <artifactId>feign-reactor-core</artifactId>
    <packaging>jar</packaging>
    <name>Feign Reactive Core</name>

    <properties>
        <main.basedir>${project.basedir}/..</main.basedir>
    </properties>

    <dependencies>

        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-core</artifactId>
        </dependency>

        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-core</artifactId>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>

        <dependency>
            <groupId>commons-httpclient</groupId>
            <artifactId>commons-httpclient</artifactId>
        </dependency>

        <!-- Tests -->
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.github.tomakehurst</groupId>
            <artifactId>wiremock</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-library</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.awaitility</groupId>
            <artifactId>awaitility</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-starter-logging</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.1.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>test-jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/ReactiveContract.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

import feign.Contract;
import feign.MethodMetadata;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import static feign.Util.checkNotNull;
import static reactivefeign.utils.FeignUtils.bodyActualType;
import static reactivefeign.utils.FeignUtils.returnActualType;

/**
 * Contract allowing only {@link Mono} and {@link Flux} return type.
 *
 * @author Sergii Karpenko
 */
public class ReactiveContract implements Contract {

  private final Contract delegate;

  public ReactiveContract(final Contract delegate) {
    this.delegate = checkNotNull(delegate, "delegate must not be null");
  }

  @Override
  public List<MethodMetadata> parseAndValidatateMetadata(final Class<?> targetType) {
    final List<MethodMetadata> methodsMetadata =
        this.delegate.parseAndValidatateMetadata(targetType);

    for (final MethodMetadata metadata : methodsMetadata) {
      final Type type = metadata.returnType();
      if (!isReactorType(type)) {
        throw new IllegalArgumentException(String.format(
            "Method %s of contract %s doesn't returns reactor.core.publisher.Mono or reactor.core.publisher.Flux",
            metadata.configKey(), targetType.getSimpleName()));
      }

      if(returnActualType(metadata) == byte[].class || bodyActualType(metadata) == byte[].class){
        throw new IllegalArgumentException(String.format(
                "Method %s of contract %s will cause data to be copied, use ByteBuffer instead",
                metadata.configKey(), targetType.getSimpleName()));
      }
    }

    return methodsMetadata;
  }

  private boolean isReactorType(final Type type) {
    return (type instanceof ParameterizedType)
        && (((ParameterizedType) type).getRawType() == Mono.class
            || ((ParameterizedType) type).getRawType() == Flux.class);
  }
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/ReactiveFeign.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

import feign.*;
import feign.codec.ErrorDecoder;
import org.reactivestreams.Publisher;
import reactivefeign.client.ReactiveHttpClient;
import reactivefeign.client.ReactiveHttpRequestInterceptor;
import reactivefeign.client.ReactiveHttpResponse;
import reactivefeign.client.statushandler.ReactiveStatusHandler;
import reactivefeign.client.statushandler.ReactiveStatusHandlers;
import reactivefeign.methodhandler.MethodHandler;
import reactivefeign.methodhandler.DefaultMethodHandler;
import reactivefeign.methodhandler.MethodHandlerFactory;
import reactivefeign.methodhandler.ReactiveMethodHandlerFactory;
import reactivefeign.publisher.*;
import reactivefeign.utils.Pair;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static feign.Util.checkNotNull;
import static feign.Util.isDefault;
import static reactivefeign.client.InterceptorReactiveHttpClient.intercept;
import static reactivefeign.client.LoggerReactiveHttpClient.log;
import static reactivefeign.client.ResponseMappers.ignore404;
import static reactivefeign.client.ResponseMappers.mapResponse;
import static reactivefeign.client.StatusHandlerReactiveHttpClient.handleStatus;
import static reactivefeign.utils.FeignUtils.returnPublisherType;
import static reactivefeign.utils.MultiValueMapUtils.addOrdered;

/**
 * Allows Feign interfaces to accept {@link Publisher} as body and return reactive {@link Mono} or
 * {@link Flux}.
 *
 * @author Sergii Karpenko
 */
public class ReactiveFeign {

  private final ParseHandlersByName targetToHandlersByName;
  private final InvocationHandlerFactory factory;

  protected ReactiveFeign(
      final ParseHandlersByName targetToHandlersByName,
      final InvocationHandlerFactory factory) {
    this.targetToHandlersByName = targetToHandlersByName;
    this.factory = factory;
  }

  @SuppressWarnings("unchecked")
  public <T> T newInstance(Target<T> target) {
    final Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    final Map<Method, InvocationHandlerFactory.MethodHandler> methodToHandler = new LinkedHashMap<>();
    final List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<>();

    for (final Method method : target.type().getMethods()) {
      if (isDefault(method)) {
        final DefaultMethodHandler handler = new DefaultMethodHandler(method);
        defaultMethodHandlers.add(handler);
        methodToHandler.put(method, handler);
      } else {
        methodToHandler.put(method,
                nameToHandler.get(Feign.configKey(target.type(), method)));
      }
    }

    final InvocationHandler handler = factory.create(target, methodToHandler);
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
            new Class<?>[] {target.type()}, handler);

    for (final DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }

    return proxy;
  }

  /**
   * ReactiveFeign builder.
   */
  public abstract static class Builder<T> {
    protected Contract contract;
    protected Function<MethodMetadata, ReactiveHttpClient> clientFactory;
    protected ReactiveHttpRequestInterceptor requestInterceptor;
    protected BiFunction<MethodMetadata, ReactiveHttpResponse, ReactiveHttpResponse> responseMapper;
    protected ReactiveStatusHandler statusHandler =
        ReactiveStatusHandlers.defaultFeign(new ErrorDecoder.Default());
    protected InvocationHandlerFactory invocationHandlerFactory =
        new ReactiveInvocationHandler.Factory();
    protected boolean decode404 = false;
    protected Target<T> target;

    private Function<Flux<Throwable>, Flux<Throwable>> retryFunction;

    protected Builder(){
      contract(new Contract.Default());
    }

    abstract public Builder<T> options(ReactiveOptions options);

    protected Builder<T> clientFactory(Function<MethodMetadata, ReactiveHttpClient> clientFactory) {
      this.clientFactory = clientFactory;
      return this;
    }

    /**
     * Sets contract. Provided contract will be wrapped in {@link ReactiveContract}
     *
     * @param contract contract.
     * @return this builder
     */
    public Builder<T> contract(final Contract contract) {
      this.contract = new ReactiveContract(contract);
      return this;
    }

    public Builder<T> addHeaders(List<Pair<String, String>> headers) {
      this.requestInterceptor = request -> {
        headers.forEach(header -> addOrdered(request.headers(), header.left, header.right));
        return request;
      };
      return this;
    }

    public Builder<T> requestInterceptor(ReactiveHttpRequestInterceptor requestInterceptor) {
      this.requestInterceptor = requestInterceptor;
      return this;
    }

    /**
     * This flag indicates that the reactive feign client should process responses with 404 status,
     * specifically returning empty {@link Mono} or {@link Flux} instead of throwing
     * {@link FeignException}.
     * <p>
     * <p>
     * This flag only works with 404, as opposed to all or arbitrary status codes. This was an
     * explicit decision: 404 - empty is safe, common and doesn't complicate redirection, retry or
     * fallback policy.
     *
     * @return this builder
     */
    public Builder<T> decode404() {
      this.decode404 = true;
      return this;
    }

    public Builder<T> statusHandler(ReactiveStatusHandler statusHandler) {
      this.statusHandler = statusHandler;
      return this;
    }

    /**
     * The most common way to introduce custom logic on handling http response
     *
     * @param responseMapper
     * @return
     */
    public Builder<T> responseMapper(BiFunction<MethodMetadata, ReactiveHttpResponse, ReactiveHttpResponse> responseMapper) {
      this.responseMapper = responseMapper;
      return this;
    }

    public Builder<T> retryWhen(Function<Flux<Throwable>, Flux<Throwable>> retryFunction) {
      this.retryFunction = retryFunction;
      return this;
    }

    public Builder<T> retryWhen(ReactiveRetryPolicy retryPolicy) {
      return retryWhen(retryPolicy.toRetryFunction());
    }

    /**
     * Defines target and builds client.
     *
     * @param apiType API interface
     * @param url base URL
     * @return built client
     */
    public T target(final Class<T> apiType, final String url) {
      return target(new Target.HardCodedTarget<>(apiType, url));
    }

    /**
     * Defines target and builds client.
     *
     * @param target target instance
     * @return built client
     */
    public T target(final Target<T> target) {
      this.target = target;
      return build().newInstance(target);
    }

    protected ReactiveFeign build() {
      final ParseHandlersByName handlersByName = new ParseHandlersByName(
              contract, buildReactiveMethodHandlerFactory());
      return new ReactiveFeign(handlersByName, invocationHandlerFactory);
    }

    protected MethodHandlerFactory buildReactiveMethodHandlerFactory() {
      return new ReactiveMethodHandlerFactory(buildReactiveClientFactory());
    }

    protected PublisherClientFactory buildReactiveClientFactory() {
      return methodMetadata -> {

        checkNotNull(clientFactory,
                "clientFactory wasn't provided in ReactiveFeign builder");

        ReactiveHttpClient reactiveClient = clientFactory.apply(methodMetadata);

        if (requestInterceptor != null) {
          reactiveClient = intercept(reactiveClient, requestInterceptor);
        }

        reactiveClient = log(reactiveClient, methodMetadata);

        if (responseMapper != null) {
          reactiveClient = mapResponse(reactiveClient, methodMetadata, responseMapper);
        }

        if (decode404) {
          reactiveClient = mapResponse(reactiveClient, methodMetadata, ignore404());
        }

        if (statusHandler != null) {
          reactiveClient = handleStatus(reactiveClient, methodMetadata, statusHandler);
        }

        reactivefeign.publisher.PublisherHttpClient publisherClient = toPublisher(reactiveClient, methodMetadata);
        if (retryFunction != null) {
          publisherClient = retry(publisherClient, methodMetadata, retryFunction);
        }

        return publisherClient;
      };
    }

    protected PublisherHttpClient retry(
            PublisherHttpClient publisherClient,
            MethodMetadata methodMetadata,
            Function<Flux<Throwable>, Flux<Throwable>> retryFunction) {
      Type returnPublisherType = returnPublisherType(methodMetadata);
      if(returnPublisherType == Mono.class){
        return new MonoRetryPublisherHttpClient(
                (MonoPublisherHttpClient)publisherClient, methodMetadata, retryFunction);
      } else if(returnPublisherType == Flux.class) {
        return new FluxRetryPublisherHttpClient(
                (FluxPublisherHttpClient)publisherClient, methodMetadata, retryFunction);
      } else {
        throw new IllegalArgumentException("Unknown returnPublisherType: " + returnPublisherType);
      }
    }

    protected PublisherHttpClient toPublisher(ReactiveHttpClient reactiveHttpClient, MethodMetadata methodMetadata){
      Type returnPublisherType = returnPublisherType(methodMetadata);
      if(returnPublisherType == Mono.class){
        return new MonoPublisherHttpClient(reactiveHttpClient);
      } else if(returnPublisherType == Flux.class){
        return new FluxPublisherHttpClient(reactiveHttpClient);
      } else {
        throw new IllegalArgumentException("Unknown returnPublisherType: " + returnPublisherType);
      }
    }
  }

  public static final class ParseHandlersByName {
    private final Contract contract;
    private final MethodHandlerFactory factory;

    ParseHandlersByName(final Contract contract, final MethodHandlerFactory factory) {
      this.contract = contract;
      this.factory = factory;
    }

    Map<String, MethodHandler> apply(final Target target) {
      Map<String, MethodMetadata> metadata = contract.parseAndValidatateMetadata(target.type())
              .stream()
              .collect(Collectors.toMap(
                      MethodMetadata::configKey,
                      md -> md
              ));
      Map<String, Method> configKeyToMethod = Stream.of(target.type().getMethods())
              .collect(Collectors.toMap(
                      method -> Feign.configKey(target.type(), method),
                      method -> method
              ));

      final Map<String, MethodHandler> result = new LinkedHashMap<>();

      for (final Map.Entry<String, Method> entry : configKeyToMethod.entrySet()) {
        String configKey = entry.getKey();
        MethodMetadata md = metadata.get(configKey);
        MethodHandler methodHandler = md != null
                ? factory.create(target, md)
                : factory.createDefault(entry.getValue());  //isDefault(entry.getValue())
        result.put(configKey, methodHandler);
      }

      return result;
    }
  }
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/ReactiveInvocationHandler.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

import feign.InvocationHandlerFactory;
import feign.InvocationHandlerFactory.MethodHandler;
import feign.Target;
import org.reactivestreams.Publisher;
import reactivefeign.client.ReactiveHttpClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;

import static feign.Util.checkNotNull;

/**
 * {@link InvocationHandler} implementation that transforms calls to methods of feign contract into
 * asynchronous HTTP requests via spring WebClient.
 *
 * @author Sergii Karpenko
 */
public final class ReactiveInvocationHandler implements InvocationHandler {
  private final Target<?> target;
  private final Map<Method, MethodHandler> dispatch;

  private ReactiveInvocationHandler(final Target<?> target,
      final Map<Method, MethodHandler> dispatch) {
    this.target = checkNotNull(target, "target must not be null");
    this.dispatch = checkNotNull(dispatch, "dispatch must not be null");
    defineObjectMethodsHandlers();
  }

  private void defineObjectMethodsHandlers() {
    try {
      dispatch.put(Object.class.getMethod("equals", Object.class),
          args -> {
            Object otherHandler = args.length > 0 && args[0] != null
                ? Proxy.getInvocationHandler(args[0])
                : null;
            return equals(otherHandler);
          });
      dispatch.put(Object.class.getMethod("hashCode"),
          args -> hashCode());
      dispatch.put(Object.class.getMethod("toString"),
          args -> toString());
    } catch (NoSuchMethodException e) {
      throw new RuntimeException(e);
    }
  }

  @Override
  public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
    return dispatch.get(method).invoke(args);
  }

  @Override
  public boolean equals(final Object other) {
    if (other instanceof ReactiveInvocationHandler) {
      final ReactiveInvocationHandler otherHandler = (ReactiveInvocationHandler) other;
      return this.target.equals(otherHandler.target);
    }
    return false;
  }

  @Override
  public int hashCode() {
    return target.hashCode();
  }

  @Override
  public String toString() {
    return target.toString();
  }

  /**
   * Factory for ReactiveInvocationHandler.
   */
  public static final class Factory implements InvocationHandlerFactory {

    @Override
    public InvocationHandler create(final Target target,
                                    final Map<Method, MethodHandler> dispatch) {
      return new ReactiveInvocationHandler(target, dispatch);
    }
  }
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/ReactiveOptions.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

/**
 * @author Sergii Karpenko
 */
public class ReactiveOptions {

  private final Long connectTimeoutMillis;
  private final Long readTimeoutMillis;
  private final Boolean tryUseCompression;

  private ReactiveOptions(Long connectTimeoutMillis, Long readTimeoutMillis,
      Boolean tryUseCompression) {
    this.connectTimeoutMillis = connectTimeoutMillis;
    this.readTimeoutMillis = readTimeoutMillis;
    this.tryUseCompression = tryUseCompression;
  }

  public Long getConnectTimeoutMillis() {
    return connectTimeoutMillis;
  }

  public Long getReadTimeoutMillis() {
    return readTimeoutMillis;
  }

  public Boolean isTryUseCompression() {
    return tryUseCompression;
  }

  public boolean isEmpty() {
    return connectTimeoutMillis == null && readTimeoutMillis == null
        && tryUseCompression == null;
  }

  public static class Builder {
    private Long connectTimeoutMillis;
    private Long readTimeoutMillis;
    private Boolean tryUseCompression;

    public Builder() {}

    public Builder setConnectTimeoutMillis(long connectTimeoutMillis) {
      this.connectTimeoutMillis = connectTimeoutMillis;
      return this;
    }

    public Builder setReadTimeoutMillis(long readTimeoutMillis) {
      this.readTimeoutMillis = readTimeoutMillis;
      return this;
    }

    public Builder setTryUseCompression(boolean tryUseCompression) {
      this.tryUseCompression = tryUseCompression;
      return this;
    }

    public ReactiveOptions build() {
      return new ReactiveOptions(connectTimeoutMillis, readTimeoutMillis,
          tryUseCompression);
    }
  }
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/ReactiveRetryPolicy.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

import reactor.core.Exceptions;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuples;

import java.time.Duration;
import java.util.function.Function;

public interface ReactiveRetryPolicy {
  /**
   * @param error
   * @param attemptNo
   * @return -1 if should not be retried, 0 if retry immediately
   */
  long retryDelay(Throwable error, int attemptNo);

  default Function<Flux<Throwable>, Flux<Throwable>> toRetryFunction() {
    return errors -> errors
        .zipWith(Flux.range(1, Integer.MAX_VALUE), (error, index) -> {
          long delay = retryDelay(error, index);
          if (delay >= 0) {
            return Tuples.of(delay, error);
          } else {
            throw Exceptions.propagate(error);
          }
        }).flatMap(
            tuple2 -> tuple2.getT1() > 0
                ? Mono.delay(Duration.ofMillis(tuple2.getT1()))
                    .map(time -> tuple2.getT2())
                : Mono.just(tuple2.getT2()));
  }
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/ReactiveRetryers.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

import feign.RetryableException;

import java.util.Date;

/**
 * @author Sergii Karpenko
 */
public class ReactiveRetryers {

  public static ReactiveRetryPolicy retry(int maxRetries) {
    return (error, attemptNo) -> attemptNo <= maxRetries ? 0 : -1;
  }

  public static ReactiveRetryPolicy retryWithBackoff(int maxRetries, long periodInMs) {
    return (error, attemptNo) -> {
      if (attemptNo <= maxRetries) {
        long delay;
        Date retryAfter;
        // "Retry-After" header set
        if (error instanceof RetryableException
            && (retryAfter = ((RetryableException) error)
                .retryAfter()) != null) {
          delay = retryAfter.getTime() - System.currentTimeMillis();
          delay = Math.min(delay, periodInMs);
          delay = Math.max(delay, 0);
        } else {
          delay = periodInMs;
        }
        return delay;
      } else {
        return -1;
      }
    };
  }

}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/client/DelegatingReactiveHttpResponse.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.client;

import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;

/**
 * @author Sergii Karpenko
 */
abstract public class DelegatingReactiveHttpResponse implements ReactiveHttpResponse {

  private final ReactiveHttpResponse response;

  protected DelegatingReactiveHttpResponse(ReactiveHttpResponse response) {
    this.response = response;
  }

  protected ReactiveHttpResponse getResponse() {
    return response;
  }

  @Override
  public int status() {
    return response.status();
  }

  @Override
  public Map<String, List<String>> headers() {
    return response.headers();
  }

  @Override
  public Mono<byte[]> bodyData() {
    throw new UnsupportedOperationException();
  }
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/client/InterceptorReactiveHttpClient.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.client;

/**
 * Used to modify request before call. May be used to set auth headers to all requests.
 *
 * @author Sergii Karpenko
 */
public class InterceptorReactiveHttpClient {

  public static ReactiveHttpClient intercept(ReactiveHttpClient reactiveHttpClient,
											  ReactiveHttpRequestInterceptor interceptor) {
    return request -> reactiveHttpClient.executeRequest(interceptor.apply(request));
  }

}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/client/LoggerReactiveHttpClient.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.client;

import feign.MethodMetadata;
import org.reactivestreams.Publisher;
import org.slf4j.LoggerFactory;
import reactivefeign.utils.Pair;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import static reactivefeign.utils.FeignUtils.methodTag;
import static reactor.core.publisher.Mono.just;

/**
 * Wraps {@link ReactiveHttpClient} with log logic
 *
 * @author Sergii Karpenko
 */
public class LoggerReactiveHttpClient implements ReactiveHttpClient {

  private final org.slf4j.Logger logger = LoggerFactory.getLogger(LoggerReactiveHttpClient.class);

  private final ReactiveHttpClient reactiveClient;
  private final String methodTag;

  public static ReactiveHttpClient log(ReactiveHttpClient reactiveClient, MethodMetadata methodMetadata) {
    return new LoggerReactiveHttpClient(reactiveClient, methodMetadata);
  }

  private LoggerReactiveHttpClient(ReactiveHttpClient reactiveClient,
      MethodMetadata methodMetadata) {
    this.reactiveClient = reactiveClient;
    this.methodTag = methodTag(methodMetadata);
  }

  @Override
  public Mono<ReactiveHttpResponse> executeRequest(ReactiveHttpRequest request) {

    AtomicLong start = new AtomicLong(-1);
    return Mono
        .defer(() -> {
          start.set(System.currentTimeMillis());
          return just(request);
        })
        .flatMap(req -> {
          req = logRequest(methodTag, req);

          return reactiveClient.executeRequest(req)
              .doOnNext(resp -> logResponseHeaders(methodTag, resp,
                  System.currentTimeMillis() - start.get()));
        })
        .map(resp -> new LoggerReactiveHttpResponse(resp, start));
  }

  private ReactiveHttpRequest logRequest(
          String feignMethodTag, ReactiveHttpRequest request) {
    if (logger.isDebugEnabled()) {
      logger.debug("[{}]--->{} {} HTTP/1.1", feignMethodTag, request.method(),
          request.uri());
    }

    if (logger.isTraceEnabled()) {
      logger.trace("[{}] REQUEST HEADERS\n{}", feignMethodTag,
          msg(() -> request.headers().entrySet().stream()
              .map(entry -> String.format("%s:%s", entry.getKey(),
                  entry.getValue()))
              .collect(Collectors.joining("\n"))));

      if(request.body() != null) {
        Publisher<Object> bodyLogged;
        if (request.body() instanceof Mono) {
          bodyLogged = ((Mono<Object>) request.body()).doOnNext(body -> logger.trace(
                  "[{}] REQUEST BODY\n{}", feignMethodTag, body));
        } else if (request.body() instanceof Flux) {
          bodyLogged = ((Flux<Object>) request.body()).doOnNext(body -> logger.trace(
                  "[{}] REQUEST BODY ELEMENT\n{}", feignMethodTag, body));
        } else {
          throw new IllegalArgumentException("Unsupported publisher type: " + request.body().getClass());
        }
        return new ReactiveHttpRequest(request, bodyLogged);
      }
    }

    return request;
  }

  private void logResponseHeaders(String feignMethodTag,
                                  ReactiveHttpResponse httpResponse,
                                  long elapsedTime) {
    if (logger.isTraceEnabled()) {
      logger.trace("[{}] RESPONSE HEADERS\n{}", feignMethodTag,
          msg(() -> httpResponse.headers().entrySet().stream()
              .flatMap(entry -> entry.getValue().stream()
                  .map(value -> new Pair<>(entry.getKey(), value)))
              .map(pair -> String.format("%s:%s", pair.left, pair.right))
              .collect(Collectors.joining("\n"))));
    }
    if (logger.isDebugEnabled()) {
      logger.debug("[{}]<--- headers takes {} milliseconds", feignMethodTag,
          elapsedTime);
    }
  }

  private void logResponseBodyAndTime(String feignMethodTag, Object response, long elapsedTime) {
    if (logger.isTraceEnabled()) {
      logger.trace("[{}] RESPONSE BODY\n{}", feignMethodTag, response);
    }

    if (logger.isDebugEnabled()) {
      logger.debug("[{}]<--- body takes {} milliseconds", feignMethodTag, elapsedTime);
    }
  }

  private class LoggerReactiveHttpResponse extends DelegatingReactiveHttpResponse {

    private final AtomicLong start;

    private LoggerReactiveHttpResponse(ReactiveHttpResponse response, AtomicLong start) {
      super(response);
      this.start = start;
    }

    @Override
    public Publisher<?> body() {
      Publisher<?> publisher = getResponse().body();

      if (publisher instanceof Mono) {
        return ((Mono<?>) publisher).doOnNext(responseBodyLogger(start));
      } else {
        return ((Flux<?>) publisher).doOnNext(responseBodyLogger(start));
      }

    }

    @Override
    public Mono<byte[]> bodyData() {
      Mono<byte[]> publisher = getResponse().bodyData();

      return publisher.doOnNext(responseBodyLogger(start));
    }

    private Consumer<Object> responseBodyLogger(AtomicLong start) {
      return result -> logResponseBodyAndTime(methodTag, result,
          System.currentTimeMillis() - start.get());
    }
  }

  private static MessageSupplier msg(Supplier<?> supplier) {
    return new MessageSupplier(supplier);
  }

  static class MessageSupplier {
    private Supplier<?> supplier;

    public MessageSupplier(Supplier<?> supplier) {
      this.supplier = supplier;
    }

    @Override
    public String toString() {
      return supplier.get().toString();
    }
  }
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/client/ReactiveHttpClient.java
================================================
package reactivefeign.client;

import reactor.core.publisher.Mono;

public interface ReactiveHttpClient {

	Mono<ReactiveHttpResponse> executeRequest(ReactiveHttpRequest request);
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/client/ReactiveHttpRequest.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.client;

import org.reactivestreams.Publisher;

import java.net.URI;
import java.util.List;
import java.util.Map;

import static feign.Util.checkNotNull;

/**
 * An immutable reactive request to an http server.
 * 
 * @author Sergii Karpenko
 */
public final class ReactiveHttpRequest {

  private final String method;
  private final URI uri;
  private final Map<String, List<String>> headers;
  private final Publisher<Object> body;

  /**
   * No parameters can be null except {@code body}. All parameters must be effectively immutable,
   * via safe copies, not mutating or otherwise.
   */
  public ReactiveHttpRequest(String method, URI uri,
      Map<String, List<String>> headers, Publisher<Object> body) {
    this.method = checkNotNull(method, "method of %s", uri);
    this.uri = checkNotNull(uri, "url");
    this.headers = checkNotNull(headers, "headers of %s %s", method, uri);
    this.body = body; // nullable
  }

  public ReactiveHttpRequest(ReactiveHttpRequest request, Publisher<Object> body){
     this(request.method, request.uri, request.headers, body);
  }

  /* Method to invoke on the server. */
  public String method() {
    return method;
  }

  /* Fully resolved URL including query. */
  public URI uri() {
    return uri;
  }

  /* Ordered list of headers that will be sent to the server. */
  public Map<String, List<String>> headers() {
    return headers;
  }

  /**
   * If present, this is the replayable body to send to the server.
   */
  public Publisher<Object> body() {
    return body;
  }

}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/client/ReactiveHttpRequestInterceptor.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.client;

import java.util.function.Function;

/**
 * Used to modify request before call. May be used to set auth headers to all requests.
 *
 * @author Sergii Karpenko
 *
 */
public interface ReactiveHttpRequestInterceptor
    extends Function<ReactiveHttpRequest, ReactiveHttpRequest> {
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/client/ReactiveHttpResponse.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.client;

import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;

/**
 * Reactive response from an http server.
 * 
 * @author Sergii Karpenko
 */
public interface ReactiveHttpResponse {

  int status();

  Map<String, List<String>> headers();

  Publisher<?> body();

  /**
   * used by error decoders
   * 
   * @return error message data
   */
  Mono<byte[]> bodyData();
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/client/ReadTimeoutException.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.client;

public class ReadTimeoutException extends RuntimeException {

  public ReadTimeoutException(Throwable cause) {
    super(cause);
  }
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/client/ResponseMappers.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.client;

import feign.MethodMetadata;
import org.apache.commons.httpclient.HttpStatus;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;

import java.util.function.BiFunction;

/**
 * Maps 404 error response to successful empty response
 *
 * @author Sergii Karpenko
 */
public class ResponseMappers {

  public static BiFunction<MethodMetadata, ReactiveHttpResponse, ReactiveHttpResponse> ignore404() {
    return (MethodMetadata methodMetadata, ReactiveHttpResponse response) -> {
      if (response.status() == HttpStatus.SC_NOT_FOUND) {
        return new DelegatingReactiveHttpResponse(response) {
          @Override
          public int status() {
            return HttpStatus.SC_OK;
          }

          @Override
          public Publisher<Object> body() {
            return Mono.empty();
          }
        };
      }
      return response;
    };
  }

  public static ReactiveHttpClient mapResponse(
          ReactiveHttpClient reactiveHttpClient,
          MethodMetadata methodMetadata,
          BiFunction<MethodMetadata, ReactiveHttpResponse, ReactiveHttpResponse> responseMapper) {
    return request -> reactiveHttpClient.executeRequest(request)
        .map(response -> responseMapper.apply(methodMetadata, response));
  }

}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/client/StatusHandlerReactiveHttpClient.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.client;

import feign.MethodMetadata;
import org.reactivestreams.Publisher;
import reactivefeign.client.statushandler.ReactiveStatusHandler;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import static reactivefeign.utils.FeignUtils.methodTag;

/**
 * Uses statusHandlers to process status of http response
 *
 * @author Sergii Karpenko
 */

public class StatusHandlerReactiveHttpClient implements ReactiveHttpClient {

  private final ReactiveHttpClient reactiveClient;
  private final String methodTag;

  private final ReactiveStatusHandler statusHandler;

  public static ReactiveHttpClient handleStatus(
          ReactiveHttpClient reactiveClient,
          MethodMetadata methodMetadata,
          ReactiveStatusHandler statusHandler) {
    return new StatusHandlerReactiveHttpClient(reactiveClient, methodMetadata, statusHandler);
  }

  private StatusHandlerReactiveHttpClient(ReactiveHttpClient reactiveClient,
                                          MethodMetadata methodMetadata,
                                          ReactiveStatusHandler statusHandler) {
    this.reactiveClient = reactiveClient;
    this.methodTag = methodTag(methodMetadata);
    this.statusHandler = statusHandler;
  }

  @Override
  public Mono<ReactiveHttpResponse> executeRequest(ReactiveHttpRequest request) {
    return reactiveClient.executeRequest(request).map(response -> {
      if (statusHandler.shouldHandle(response.status())) {
        return new ErrorReactiveHttpResponse(response, statusHandler.decode(methodTag, response));
      } else {
        return response;
      }
    });
  }

  private class ErrorReactiveHttpResponse extends DelegatingReactiveHttpResponse {

    private final Mono<? extends Throwable> error;

    protected ErrorReactiveHttpResponse(ReactiveHttpResponse response, Mono<? extends Throwable> error) {
      super(response);
      this.error = error;
    }

    @Override
    public Publisher<Object> body() {
      if (getResponse().body() instanceof Mono) {
        return error.flatMap(Mono::error);
      } else {
        return error.flatMapMany(Flux::error);
      }
    }
  }

}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/client/statushandler/CompositeStatusHandler.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.client.statushandler;

import reactivefeign.client.ReactiveHttpResponse;
import reactor.core.publisher.Mono;

import java.util.List;

import static java.util.Arrays.asList;

/**
 * @author Sergii Karpenko
 */
public class CompositeStatusHandler implements ReactiveStatusHandler {

  private final List<ReactiveStatusHandler> handlers;

  public static CompositeStatusHandler compose(ReactiveStatusHandler... handlers) {
    return new CompositeStatusHandler(asList(handlers));
  }

  private CompositeStatusHandler(List<ReactiveStatusHandler> handlers) {
    this.handlers = handlers;
  }

  @Override
  public boolean shouldHandle(int status) {
    return handlers.stream().anyMatch(handler -> handler.shouldHandle(status));
  }

  @Override
  public Mono<? extends Throwable> decode(String methodKey, ReactiveHttpResponse response) {
    return handlers.stream()
        .filter(statusHandler -> statusHandler
            .shouldHandle(response.status()))
        .findFirst()
        .map(statusHandler -> statusHandler.decode(methodKey, response))
        .orElse(null);
  }
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/client/statushandler/ReactiveStatusHandler.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.client.statushandler;

import reactivefeign.client.ReactiveHttpResponse;
import reactor.core.publisher.Mono;

/**
 * @author Sergii Karpenko
 */
public interface ReactiveStatusHandler {

  boolean shouldHandle(int status);

  Mono<? extends Throwable> decode(String methodKey, ReactiveHttpResponse response);
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/client/statushandler/ReactiveStatusHandlers.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.client.statushandler;

import feign.Response;
import feign.codec.ErrorDecoder;
import org.apache.commons.httpclient.HttpStatus;
import reactivefeign.client.ReactiveHttpResponse;
import reactor.core.publisher.Mono;

import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static reactivefeign.utils.HttpUtils.familyOf;

public class ReactiveStatusHandlers {

  public static ReactiveStatusHandler defaultFeign(ErrorDecoder errorDecoder) {
    return new ReactiveStatusHandler() {

      @Override
      public boolean shouldHandle(int status) {
        return familyOf(status).isError();
      }

      @Override
      public Mono<? extends Throwable> decode(String methodTag, ReactiveHttpResponse response) {
        return response.bodyData()
                .defaultIfEmpty(new byte[0])
                .map(bodyData -> errorDecoder.decode(methodTag,
                        Response.builder().status(response.status())
                                .reason(HttpStatus.getStatusText(response.status()))
                                .headers(response.headers().entrySet()
                                        .stream()
                                        .collect(Collectors.toMap(Map.Entry::getKey,
                                                Map.Entry::getValue)))
                                .body(bodyData).build()));
      }
    };
  }

  public static ReactiveStatusHandler throwOnStatus(
          Predicate<Integer> statusPredicate,
          BiFunction<String, ReactiveHttpResponse, Throwable> errorFunction) {
    return new ReactiveStatusHandler() {
      @Override
      public boolean shouldHandle(int status) {
        return statusPredicate.test(status);
      }

      @Override
      public Mono<? extends Throwable> decode(String methodKey, ReactiveHttpResponse response) {
        return Mono.just(errorFunction.apply(methodKey, response));
      }
    };
  }
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/methodhandler/DefaultMethodHandler.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.methodhandler;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * Handles default methods by directly invoking the default method code on the interface. The bindTo
 * method must be called on the result before invoke is called.
 */
public final class DefaultMethodHandler implements MethodHandler {
  // Uses Java 7 MethodHandle based reflection. As default methods will only exist when
  // run on a Java 8 JVM this will not affect use on legacy JVMs.
  private final MethodHandle unboundHandle;

  // handle is effectively final after bindTo has been called.
  private MethodHandle handle;

  public DefaultMethodHandler(Method defaultMethod) {
    try {
      Class<?> declaringClass = defaultMethod.getDeclaringClass();
      Field field = Lookup.class.getDeclaredField("IMPL_LOOKUP");
      field.setAccessible(true);
      Lookup lookup = (Lookup) field.get(null);

      this.unboundHandle = lookup.unreflectSpecial(defaultMethod, declaringClass);
    } catch (NoSuchFieldException | IllegalAccessException ex) {
      throw new IllegalStateException(ex);
    }
  }

  /**
   * Bind this handler to a proxy object. After bound, DefaultMethodHandler#invoke will act as if it
   * was called on the proxy object. Must be called once and only once for a given instance of
   * DefaultMethodHandler
   */
  public void bindTo(Object proxy) {
    if (handle != null) {
      throw new IllegalStateException(
          "Attempted to rebind a default method handler that was already bound");
    }
    handle = unboundHandle.bindTo(proxy);
  }

  /**
   * Invoke this method. DefaultMethodHandler#bindTo must be called before the first time invoke is
   * called.
   */
  @Override
  public Object invoke(Object[] argv) throws Throwable {
    if (handle == null) {
      throw new IllegalStateException(
          "Default method handler invoked before proxy has been bound.");
    }
    return handle.invokeWithArguments(argv);
  }
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/methodhandler/FluxMethodHandler.java
================================================
package reactivefeign.methodhandler;

import reactor.core.publisher.Flux;

public class FluxMethodHandler implements MethodHandler {

	private final MethodHandler methodHandler;

	public FluxMethodHandler(MethodHandler methodHandler) {
		this.methodHandler = methodHandler;
	}

	@Override
	@SuppressWarnings("unchecked")
	public Flux<Object> invoke(final Object[] argv) {
		try {
			return (Flux<Object>)methodHandler.invoke(argv);
		} catch (Throwable throwable) {
			return Flux.error(throwable);
		}
	}

}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/methodhandler/MethodHandler.java
================================================
package reactivefeign.methodhandler;

import feign.InvocationHandlerFactory;

public interface MethodHandler extends InvocationHandlerFactory.MethodHandler{ }


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/methodhandler/MethodHandlerFactory.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.methodhandler;

import feign.MethodMetadata;
import feign.Target;
import reactivefeign.methodhandler.MethodHandler;

import java.lang.reflect.Method;

public interface MethodHandlerFactory {

  MethodHandler create(final Target target, final MethodMetadata metadata);

  MethodHandler createDefault(Method method);
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/methodhandler/MonoMethodHandler.java
================================================
package reactivefeign.methodhandler;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class MonoMethodHandler implements MethodHandler {

	private final MethodHandler methodHandler;

	public MonoMethodHandler(MethodHandler methodHandler) {
		this.methodHandler = methodHandler;
	}

	@Override
	@SuppressWarnings("unchecked")
	public Mono<Object> invoke(final Object[] argv) {
		try {
			return (Mono<Object>)methodHandler.invoke(argv);
		} catch (Throwable throwable) {
			return Mono.error(throwable);
		}
	}
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/methodhandler/PublisherClientMethodHandler.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.methodhandler;

import feign.MethodMetadata;
import feign.Target;
import org.reactivestreams.Publisher;
import reactivefeign.client.ReactiveHttpClient;
import reactivefeign.client.ReactiveHttpRequest;
import reactivefeign.publisher.PublisherHttpClient;
import reactivefeign.utils.Pair;
import reactor.core.publisher.Mono;

import java.lang.reflect.Type;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static feign.Util.checkNotNull;
import static java.util.stream.Collectors.*;
import static reactivefeign.utils.FeignUtils.returnPublisherType;
import static reactivefeign.utils.MultiValueMapUtils.*;

/**
 * Method handler for asynchronous HTTP requests via {@link PublisherHttpClient}.
 *
 * Transforms method invocation into request that executed by {@link ReactiveHttpClient}.
 *
 * @author Sergii Karpenko
 */
public class PublisherClientMethodHandler implements MethodHandler {

  private final Target target;
  private final MethodMetadata methodMetadata;
  private final PublisherHttpClient publisherClient;
  private final Function<Map<String, ?>, String> pathExpander;
  private final Map<String, List<Function<Map<String, ?>, String>>> headerExpanders;
  private final Map<String, Collection<String>> queriesAll;
  private final Map<String, List<Function<Map<String, ?>, String>>> queryExpanders;

  public PublisherClientMethodHandler(Target target,
                                       MethodMetadata methodMetadata,
                                       PublisherHttpClient publisherClient) {
    this.target = checkNotNull(target, "target must be not null");
    this.methodMetadata = checkNotNull(methodMetadata,
        "methodMetadata must be not null");
    this.publisherClient = checkNotNull(publisherClient, "client must be not null");
    this.pathExpander = buildExpandFunction(methodMetadata.template().url());
    this.headerExpanders = buildExpanders(methodMetadata.template().headers());

    this.queriesAll = new HashMap<>(methodMetadata.template().queries());
    if (methodMetadata.formParams() != null) {
      methodMetadata.formParams()
          .forEach(param -> add(queriesAll, param, "{" + param + "}"));
    }
    this.queryExpanders = buildExpanders(queriesAll);
  }

  @Override
  @SuppressWarnings("unchecked")
  public Publisher<?> invoke(final Object[] argv) {

    final ReactiveHttpRequest request = buildRequest(argv);

    return publisherClient.executeRequest(request);
  }

  protected ReactiveHttpRequest buildRequest(Object[] argv) {

    Map<String, ?> substitutionsMap = methodMetadata.indexToName().entrySet().stream()
        .flatMap(e -> e.getValue().stream()
            .map(v -> new AbstractMap.SimpleImmutableEntry<>(e.getKey(), v)))
        .collect(Collectors.toMap(Map.Entry::getValue,
            entry -> argv[entry.getKey()]));

    try {
      String path = pathExpander.apply(substitutionsMap);
      Map<String, Collection<String>> queries = queries(argv, substitutionsMap);
      Map<String, List<String>> headers = headers(argv, substitutionsMap);

      URI uri = new URI(target.url() + path + queryLine(queries));

      return new ReactiveHttpRequest(methodMetadata.template().method(), uri, headers, body(argv));

    } catch (URISyntaxException e) {
      throw new RuntimeException(e);
    }
  }

  private String queryLine(Map<String, Collection<String>> queries) {
    if (queries.isEmpty()) {
      return "";
    }

    StringBuilder queryBuilder = new StringBuilder();
    for (Map.Entry<String, Collection<String>> query : queries.entrySet()) {
      String field = query.getKey();
      for (String value : query.getValue()) {
        queryBuilder.append('&');
        queryBuilder.append(field);
        if (value != null) {
          queryBuilder.append('=');
          if (!value.isEmpty()) {
            queryBuilder.append(value);
          }
        }
      }
    }
    queryBuilder.deleteCharAt(0);
    return queryBuilder.insert(0, '?').toString();
  }

  protected Map<String, Collection<String>> queries(Object[] argv,
                                                    Map<String, ?> substitutionsMap) {
    Map<String, Collection<String>> queries = new LinkedHashMap<>();

    // queries from template
    queriesAll.keySet()
        .forEach(queryName -> addAll(queries, queryName,
            queryExpanders.get(queryName).stream()
                .map(expander -> expander.apply(substitutionsMap))
                .collect(toList())));

    // queries from args
    if (methodMetadata.queryMapIndex() != null) {
      ((Map<String, ?>) argv[methodMetadata.queryMapIndex()])
          .forEach((key, value) -> {
            if (value instanceof Iterable) {
              ((Iterable<?>) value).forEach(element -> add(queries, key, element.toString()));
            } else {
              add(queries, key, value.toString());
            }
          });
    }

    return queries;
  }

  protected Map<String, List<String>> headers(Object[] argv, Map<String, ?> substitutionsMap) {

    Map<String, List<String>> headers = new LinkedHashMap<>();

    // headers from template
    methodMetadata.template().headers().keySet()
        .forEach(headerName -> addAllOrdered(headers, headerName,
            headerExpanders.get(headerName).stream()
                .map(expander -> expander.apply(substitutionsMap))
                .collect(toList())));

    // headers from args
    if (methodMetadata.headerMapIndex() != null) {
      ((Map<String, ?>) argv[methodMetadata.headerMapIndex()])
          .forEach((key, value) -> {
            if (value instanceof Iterable) {
              ((Iterable<?>) value)
                  .forEach(element -> addOrdered(headers, key, element.toString()));
            } else {
              addOrdered(headers, key, value.toString());
            }
          });
    }

    return headers;
  }

  protected Publisher<Object> body(Object[] argv) {
    if (methodMetadata.bodyIndex() != null) {
      return body(argv[methodMetadata.bodyIndex()]);
    } else {
      return Mono.empty();
    }
  }

  protected Publisher<Object> body(Object body) {
    if (body instanceof Publisher) {
      return (Publisher<Object>) body;
    } else {
      return Mono.just(body);
    }
  }

  private static Map<String, List<Function<Map<String, ?>, String>>> buildExpanders(
          Map<String, Collection<String>> templates) {
    Stream<Pair<String, String>> headersFlattened = templates.entrySet().stream()
        .flatMap(e -> e.getValue().stream()
            .map(v -> new Pair<>(e.getKey(), v)));
    return headersFlattened.collect(groupingBy(
        entry -> entry.left,
        mapping(entry -> buildExpandFunction(entry.right), toList())));
  }

  /**
   *
   * @param template
   * @return function that able to map substitutions map to actual value for specified template
   */
  private static final Pattern PATTERN = Pattern.compile("\\{([^}]+)\\}");

  private static Function<Map<String, ?>, String> buildExpandFunction(String template) {
    List<Function<Map<String, ?>, String>> chunks = new ArrayList<>();
    Matcher matcher = PATTERN.matcher(template);
    int previousMatchEnd = 0;
    while (matcher.find()) {
      String textChunk = template.substring(previousMatchEnd, matcher.start());
      if (textChunk.length() > 0) {
        chunks.add(data -> textChunk);
      }

      String substitute = matcher.group(1);
      chunks.add(data -> {
        Object substitution = data.get(substitute);
        if (substitution != null) {
          return substitution.toString();
        } else {
          return substitute;
        }
      });
      previousMatchEnd = matcher.end();
    }

    String textChunk = template.substring(previousMatchEnd, template.length());
    if (textChunk.length() > 0) {
      chunks.add(data -> textChunk);
    }

    return traceData -> chunks.stream().map(chunk -> chunk.apply(traceData))
        .collect(Collectors.joining());
  }

}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/methodhandler/ReactiveMethodHandlerFactory.java
================================================
package reactivefeign.methodhandler;

import feign.MethodMetadata;
import feign.Target;
import reactivefeign.publisher.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.lang.reflect.Method;
import java.lang.reflect.Type;

import static feign.Util.checkNotNull;
import static reactivefeign.utils.FeignUtils.returnPublisherType;

public class ReactiveMethodHandlerFactory implements MethodHandlerFactory {

	private final PublisherClientFactory publisherClientFactory;

	public ReactiveMethodHandlerFactory(final PublisherClientFactory publisherClientFactory) {
		this.publisherClientFactory = checkNotNull(publisherClientFactory, "client must not be null");
	}

	@Override
	public MethodHandler create(Target target, MethodMetadata metadata) {

		MethodHandler methodHandler = new PublisherClientMethodHandler(
				target, metadata, publisherClientFactory.apply(metadata));

		Type returnPublisherType = returnPublisherType(metadata);
		if(returnPublisherType == Mono.class){
			return new MonoMethodHandler(methodHandler);
		} else if(returnPublisherType == Flux.class) {
			return new FluxMethodHandler(methodHandler);
		} else {
			throw new IllegalArgumentException("Unknown returnPublisherType: " + returnPublisherType);
		}
	}

	@Override
	public MethodHandler createDefault(Method method) {
		MethodHandler defaultMethodHandler = new DefaultMethodHandler(method);

		if(method.getReturnType() == Mono.class){
			return new MonoMethodHandler(defaultMethodHandler);
		} else if(method.getReturnType() == Flux.class) {
			return new FluxMethodHandler(defaultMethodHandler);
		} else {
			throw new IllegalArgumentException("Unknown returnPublisherType: " + method.getReturnType());
		}
	}
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/publisher/FluxPublisherHttpClient.java
================================================
package reactivefeign.publisher;


import reactivefeign.client.ReactiveHttpClient;
import reactivefeign.client.ReactiveHttpRequest;
import reactivefeign.client.ReactiveHttpResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * Wraps {@link PublisherHttpClient}
 *
 * @author Sergii Karpenko
 */
public class FluxPublisherHttpClient implements PublisherHttpClient {

	private final ReactiveHttpClient reactiveHttpClient;

	public FluxPublisherHttpClient(ReactiveHttpClient reactiveHttpClient) {
		this.reactiveHttpClient = reactiveHttpClient;
	}

	@Override
	public Flux<?> executeRequest(ReactiveHttpRequest request) {
		Mono<ReactiveHttpResponse> response = reactiveHttpClient.executeRequest(request);
		return response.flatMapMany(ReactiveHttpResponse::body);
	}
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/publisher/FluxRetryPublisherHttpClient.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.publisher;

import feign.MethodMetadata;
import org.reactivestreams.Publisher;
import reactivefeign.client.ReactiveHttpRequest;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.function.Function;

/**
 * Wraps {@link PublisherHttpClient} with retry logic provided by retryFunction
 *
 * @author Sergii Karpenko
 */
public class FluxRetryPublisherHttpClient extends RetryPublisherHttpClient<FluxPublisherHttpClient> {

  public FluxRetryPublisherHttpClient(
          FluxPublisherHttpClient publisherClient, MethodMetadata methodMetadata,
          Function<Flux<Throwable>, Flux<Throwable>> retryFunction) {
    super(publisherClient, methodMetadata, retryFunction);
  }

  @Override
  public Publisher<?> executeRequest(ReactiveHttpRequest request) {
    Flux<?> response = publisherClient.executeRequest(request);
    return response.retryWhen(retryFunction).onErrorMap(outOfRetries());
  }
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/publisher/MonoPublisherHttpClient.java
================================================
package reactivefeign.publisher;


import org.reactivestreams.Publisher;
import reactivefeign.client.ReactiveHttpClient;
import reactivefeign.client.ReactiveHttpRequest;
import reactivefeign.client.ReactiveHttpResponse;
import reactor.core.publisher.Mono;

/**
 * Wraps {@link PublisherHttpClient}
 *
 * @author Sergii Karpenko
 */
public class MonoPublisherHttpClient implements PublisherHttpClient {

	private final ReactiveHttpClient reactiveHttpClient;

	public MonoPublisherHttpClient(ReactiveHttpClient reactiveHttpClient) {
		this.reactiveHttpClient = reactiveHttpClient;
	}

	@Override
	public Mono<?> executeRequest(ReactiveHttpRequest request) {
		Mono<ReactiveHttpResponse> response = reactiveHttpClient.executeRequest(request);
		return response.flatMap(resp -> Mono.from(resp.body()));
	}
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/publisher/MonoRetryPublisherHttpClient.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.publisher;

import feign.MethodMetadata;
import org.reactivestreams.Publisher;
import reactivefeign.client.ReactiveHttpRequest;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.function.Function;

/**
 * Wraps {@link PublisherHttpClient} with retry logic provided by retryFunction
 *
 * @author Sergii Karpenko
 */
public class MonoRetryPublisherHttpClient extends RetryPublisherHttpClient<MonoPublisherHttpClient> {

  public MonoRetryPublisherHttpClient(
          MonoPublisherHttpClient publisherClient, MethodMetadata methodMetadata,
          Function<Flux<Throwable>, Flux<Throwable>> retryFunction) {
    super(publisherClient, methodMetadata, retryFunction);
  }

  @Override
  public Publisher<?> executeRequest(ReactiveHttpRequest request) {
    Mono<?> response = publisherClient.executeRequest(request);
    return response.retryWhen(retryFunction).onErrorMap(outOfRetries());
  }
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/publisher/PublisherClientFactory.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.publisher;

import feign.MethodMetadata;

import java.util.function.Function;

/**
 * @author Sergii Karpenko
 */

public interface PublisherClientFactory extends Function<MethodMetadata, PublisherHttpClient> {
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/publisher/PublisherHttpClient.java
================================================
package reactivefeign.publisher;

import org.reactivestreams.Publisher;
import reactivefeign.client.ReactiveHttpRequest;

import java.lang.reflect.Type;

/**
 * @author Sergii Karpenko
 */
public interface PublisherHttpClient {

	Publisher<?> executeRequest(ReactiveHttpRequest request);
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/publisher/RetryPublisherHttpClient.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.publisher;

import feign.MethodMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;

import java.util.function.Function;

import static reactivefeign.utils.FeignUtils.methodTag;

/**
 * Wraps {@link PublisherHttpClient} with retry logic provided by retryFunction
 *
 * @author Sergii Karpenko
 */
abstract public class RetryPublisherHttpClient<P extends PublisherHttpClient> implements PublisherHttpClient {

  private static final Logger logger = LoggerFactory.getLogger(RetryPublisherHttpClient.class);

  private final String feignMethodTag;
  protected final P publisherClient;
  protected final Function<Flux<Throwable>, Flux<?>> retryFunction;

  protected RetryPublisherHttpClient(P publisherClient,
                                   MethodMetadata methodMetadata,
                                   Function<Flux<Throwable>, Flux<Throwable>> retryFunction) {
    this.publisherClient = publisherClient;
    this.feignMethodTag = methodTag(methodMetadata);
    this.retryFunction = wrapWithLog(retryFunction, feignMethodTag);
  }

  protected Function<Throwable, Throwable> outOfRetries() {
    return throwable -> {
      logger.debug("[{}]---> USED ALL RETRIES", feignMethodTag, throwable);
      return new OutOfRetriesException(throwable, feignMethodTag);
    };
  }

  protected static Function<Flux<Throwable>, Flux<?>> wrapWithLog(
          Function<Flux<Throwable>, Flux<Throwable>> retryFunction,
          String feignMethodTag) {
    return throwableFlux -> retryFunction.apply(throwableFlux)
            .doOnNext(throwable -> {
              if (logger.isDebugEnabled()) {
                logger.debug("[{}]---> RETRYING on error", feignMethodTag, throwable);
              }
            });
  }

  public static class OutOfRetriesException extends Exception {
    OutOfRetriesException(Throwable cause, String feignMethodTag) {
      super("All retries used for: " + feignMethodTag, cause);
    }
  }
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/utils/FeignUtils.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.utils;

import feign.MethodMetadata;
import org.reactivestreams.Publisher;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import static feign.Util.resolveLastTypeParameter;
import static java.util.Optional.ofNullable;

public class FeignUtils {

  public static String methodTag(MethodMetadata methodMetadata) {
    return methodMetadata.configKey().substring(0,
            methodMetadata.configKey().indexOf('('));
  }

  public static Class returnPublisherType(MethodMetadata methodMetadata) {
    final Type returnType = methodMetadata.returnType();
    return (Class)((ParameterizedType) returnType).getRawType();
  }

  public static Type returnActualType(MethodMetadata methodMetadata) {
    return resolveLastTypeParameter(methodMetadata.returnType(), returnPublisherType(methodMetadata));
  }

  public static Type bodyActualType(MethodMetadata methodMetadata) {
    return getBodyActualType(methodMetadata.bodyType());
  }

  public static Type getBodyActualType(Type bodyType) {
    return ofNullable(bodyType).map(type -> {
      if (type instanceof ParameterizedType) {
        Class<?> bodyClass = (Class<?>) ((ParameterizedType) type).getRawType();
        if (Publisher.class.isAssignableFrom(bodyClass)) {
          return resolveLastTypeParameter(bodyType, bodyClass);
        }
        else {
          return type;
        }
      }
      else {
        return type;
      }
    }).orElse(null);
  }

}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/utils/HttpUtils.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.utils;

import static reactivefeign.utils.HttpUtils.StatusCodeFamily.*;

public class HttpUtils {

  public static StatusCodeFamily familyOf(final int statusCode) {
    switch (statusCode / 100) {
      case 1:
        return INFORMATIONAL;
      case 2:
        return SUCCESSFUL;
      case 3:
        return REDIRECTION;
      case 4:
        return CLIENT_ERROR;
      case 5:
        return SERVER_ERROR;
      default:
        return OTHER;
    }
  }

  public enum StatusCodeFamily {
    INFORMATIONAL(false), SUCCESSFUL(false), REDIRECTION(false), CLIENT_ERROR(true), SERVER_ERROR(
        true), OTHER(false);

    private final boolean error;

    StatusCodeFamily(boolean error) {
      this.error = error;
    }

    public boolean isError() {
      return error;
    }
  }
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/utils/MultiValueMapUtils.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.utils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

public class MultiValueMapUtils {

  public static <K, V> void addAllOrdered(Map<K, List<V>> multiMap, K key, List<V> values) {
    multiMap.compute(key, (key_, values_) -> {
      List<V> valuesMerged = values_ != null ? values_ : new ArrayList<>(values.size());
      valuesMerged.addAll(values);
      return valuesMerged;
    });
  }

  public static <K, V> void addOrdered(Map<K, List<V>> multiMap, K key, V value) {
    multiMap.compute(key, (key_, values_) -> {
      List<V> valuesMerged = values_ != null ? values_ : new ArrayList<>(1);
      valuesMerged.add(value);
      return valuesMerged;
    });
  }

  public static <K, V> void addAll(Map<K, Collection<V>> multiMap, K key, Collection<V> values) {
    multiMap.compute(key, (key_, values_) -> {
      Collection<V> valuesMerged = values_ != null ? values_ : new ArrayList<>(values.size());
      valuesMerged.addAll(values);
      return valuesMerged;
    });
  }

  public static <K, V> void add(Map<K, Collection<V>> multiMap, K key, V value) {
    multiMap.compute(key, (key_, values_) -> {
      Collection<V> valuesMerged = values_ != null ? values_ : new ArrayList<>(1);
      valuesMerged.add(value);
      return valuesMerged;
    });
  }
}


================================================
FILE: feign-reactor-core/src/main/java/reactivefeign/utils/Pair.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign.utils;

public class Pair<L, R> {
  public final L left;
  public final R right;

  public Pair(L left, R right) {
    this.left = left;
    this.right = right;
  }
}


================================================
FILE: feign-reactor-core/src/test/java/reactivefeign/CompressionTest.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.github.tomakehurst.wiremock.common.Gzip;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import org.junit.ClassRule;
import org.junit.Test;
import reactivefeign.testcase.IcecreamServiceApi;
import reactivefeign.testcase.domain.Bill;
import reactivefeign.testcase.domain.IceCreamOrder;
import reactivefeign.testcase.domain.OrderGenerator;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static reactivefeign.TestUtils.equalsComparingFieldByFieldRecursively;

/**
 * Test the new capability of Reactive Feign client to support both Feign Request.Options
 * (regression) and the new ReactiveOptions configuration.
 *
 * @author Sergii Karpenko
 */

abstract public class CompressionTest {

  @ClassRule
  public static WireMockClassRule wireMockRule = new WireMockClassRule(
      wireMockConfig().dynamicPort());

  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options);

  @Test
  public void testCompression() throws JsonProcessingException {

    IceCreamOrder order = new OrderGenerator().generate(20);
    Bill billExpected = Bill.makeBill(order);

    wireMockRule.stubFor(post(urlEqualTo("/icecream/orders"))
        .withHeader("Accept-Encoding", containing("gzip"))
        .withRequestBody(equalTo(TestUtils.MAPPER.writeValueAsString(order)))
        .willReturn(aResponse().withStatus(200)
            .withHeader("Content-Type", "application/json")
            .withHeader("Content-Encoding", "gzip")
            .withBody(Gzip.gzip(TestUtils.MAPPER.writeValueAsString(billExpected)))));

    IcecreamServiceApi client = builder(
        new ReactiveOptions.Builder()
            .setTryUseCompression(true)
            .build())
                .target(IcecreamServiceApi.class, "http://localhost:" + wireMockRule.port());

    Mono<Bill> bill = client.makeOrder(order);
    StepVerifier.create(bill)
        .expectNextMatches(equalsComparingFieldByFieldRecursively(billExpected))
        .verifyComplete();
  }
}


================================================
FILE: feign-reactor-core/src/test/java/reactivefeign/ConnectionTimeoutTest.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

import org.hamcrest.Matchers;
import org.junit.*;
import org.junit.rules.ExpectedException;
import reactivefeign.testcase.IcecreamServiceApi;

import java.io.IOException;
import java.net.ConnectException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author Sergii Karpenko
 */
abstract public class ConnectionTimeoutTest {

  @Rule
  public ExpectedException expectedException = ExpectedException.none();

  private ServerSocket serverSocket;
  private Socket socket;
  private int port;

  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options);

  @Before
  public void before() throws IOException {
    // server socket with single element backlog queue (1) and dynamicaly allocated
    // port (0)
    serverSocket = new ServerSocket(0, 1);
    // just get the allocated port
    port = serverSocket.getLocalPort();
    // fill backlog queue by this request so consequent requests will be blocked
    socket = new Socket();
    socket.connect(serverSocket.getLocalSocketAddress());
  }

  @After
  public void after() throws IOException {
    // some cleanup
    if (serverSocket != null && !serverSocket.isClosed()) {
      serverSocket.close();
    }
  }

  // TODO investigate why doesn't work on codecov.io but works locally
  @Ignore
  @Test
  public void shouldFailOnConnectionTimeout() {

    expectedException.expectCause(

        Matchers.any(ConnectException.class));

    IcecreamServiceApi client = builder(
        new ReactiveOptions.Builder()
            .setConnectTimeoutMillis(300)
            .setReadTimeoutMillis(100)
            .build())
                .target(IcecreamServiceApi.class, "http://localhost:" + port);

    client.findOrder(1).block();
  }

}


================================================
FILE: feign-reactor-core/src/test/java/reactivefeign/ContractTest.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import reactivefeign.testcase.IcecreamServiceApi;
import reactivefeign.testcase.IcecreamServiceApiBroken;
import reactivefeign.testcase.IcecreamServiceApiBrokenByCopy;

import static org.hamcrest.Matchers.containsString;

/**
 * @author Sergii Karpenko
 */

abstract public class ContractTest {

  @Rule
  public ExpectedException expectedException = ExpectedException.none();

  abstract protected <T> ReactiveFeign.Builder<T> builder();

  @Test
  public void shouldFailOnBrokenContract() {

    expectedException.expect(IllegalArgumentException.class);
    expectedException.expectMessage(containsString("Broken Contract"));

    this.<IcecreamServiceApi>builder()
        .contract(targetType -> {
          throw new IllegalArgumentException("Broken Contract");
        })
        .target(IcecreamServiceApi.class, "http://localhost:8888");
  }

  @Test
  public void shouldFailIfNotReactiveContract() {

    expectedException.expect(IllegalArgumentException.class);
    expectedException.expectMessage(containsString("IcecreamServiceApiBroken#findOrderBlocking(int)"));

    this.<IcecreamServiceApiBroken>builder()
        .target(IcecreamServiceApiBroken.class, "http://localhost:8888");
  }

  @Test
  public void shouldFailIfMethodOperatesWithByteArray() {

    expectedException.expect(IllegalArgumentException.class);
    expectedException.expectMessage(containsString("IcecreamServiceApiBrokenByCopy#findOrderCopy(int)"));

    this.<IcecreamServiceApiBrokenByCopy>builder()
            .target(IcecreamServiceApiBrokenByCopy.class, "http://localhost:8888");
  }

}


================================================
FILE: feign-reactor-core/src/test/java/reactivefeign/DefaultMethodTest.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import feign.RequestLine;
import org.assertj.core.api.Assertions;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import reactivefeign.testcase.IcecreamServiceApi;
import reactivefeign.testcase.domain.IceCreamOrder;
import reactivefeign.testcase.domain.OrderGenerator;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static reactivefeign.TestUtils.equalsComparingFieldByFieldRecursively;

/**
 * @author Sergii Karpenko
 */
abstract public class DefaultMethodTest {

  @ClassRule
  public static WireMockClassRule wireMockRule = new WireMockClassRule(
      wireMockConfig().dynamicPort());

  @Before
  public void resetServers() {
    wireMockRule.resetAll();
  }

  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();

  abstract protected <API> ReactiveFeign.Builder<API> builder(Class<API> apiClass);

  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options);

  @Test
  public void shouldProcessDefaultMethodOnProxy() throws JsonProcessingException {
    IceCreamOrder orderGenerated = new OrderGenerator().generate(1);
    String orderStr = TestUtils.MAPPER.writeValueAsString(orderGenerated);

    wireMockRule.stubFor(get(urlEqualTo("/icecream/orders/1"))
        .withHeader("Accept", equalTo("application/json"))
        .willReturn(aResponse().withStatus(200)
            .withHeader("Content-Type", "application/json")
            .withBody(orderStr)));

    IcecreamServiceApi client = builder()
        .target(IcecreamServiceApi.class, "http://localhost:" + wireMockRule.port());

    StepVerifier.create(client.findFirstOrder())
        .expectNextMatches(equalsComparingFieldByFieldRecursively(orderGenerated))
        .verifyComplete();
  }

  @Test(expected = RuntimeException.class)
  public void shouldNotWrapException() {
    IceCreamOrder orderGenerated = new OrderGenerator().generate(1);

    IcecreamServiceApi client = builder()
        .target(IcecreamServiceApi.class, "http://localhost:" + wireMockRule.port());

    client.throwsException().onErrorReturn(
        throwable -> throwable.equals(IcecreamServiceApi.RUNTIME_EXCEPTION),
        orderGenerated).block();
  }

  @Test
  public void shouldOverrideEquals() {

    IcecreamServiceApi client = builder(
        new ReactiveOptions.Builder()
            .setConnectTimeoutMillis(300)
            .setReadTimeoutMillis(100).build())
                .target(IcecreamServiceApi.class,
                    "http://localhost:" + wireMockRule.port());

    IcecreamServiceApi clientWithSameTarget = builder()
        .target(IcecreamServiceApi.class, "http://localhost:" + wireMockRule.port());
    Assertions.assertThat(client).isEqualTo(clientWithSameTarget);

    IcecreamServiceApi clientWithOtherPort = builder()
        .target(IcecreamServiceApi.class, "http://localhost:" + (wireMockRule.port() + 1));
    Assertions.assertThat(client).isNotEqualTo(clientWithOtherPort);

    OtherApi clientWithOtherInterface = builder(OtherApi.class)
        .target(OtherApi.class, "http://localhost:" + wireMockRule.port());
    Assertions.assertThat(client).isNotEqualTo(clientWithOtherInterface);
  }

  interface OtherApi {
    @RequestLine("GET /icecream/flavors")
    Mono<String> method(String arg);
  }

  @Test
  public void shouldOverrideHashcode() {

    IcecreamServiceApi client = builder()
        .target(IcecreamServiceApi.class, "http://localhost:" + wireMockRule.port());

    IcecreamServiceApi otherClientWithSameTarget = builder()
        .target(IcecreamServiceApi.class, "http://localhost:" + wireMockRule.port());

    Assertions.assertThat(client.hashCode()).isEqualTo(otherClientWithSameTarget.hashCode());
  }

  @Test
  public void shouldOverrideToString() {

    IcecreamServiceApi client = builder()
        .target(IcecreamServiceApi.class, "http://localhost:" + wireMockRule.port());

    Assertions.assertThat(client.toString())
        .isEqualTo("HardCodedTarget(type=IcecreamServiceApi, "
            + "url=http://localhost:" + wireMockRule.port() + ")");
  }

}


================================================
FILE: feign-reactor-core/src/test/java/reactivefeign/LoggerTest.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.assertj.core.api.Condition;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import reactivefeign.client.LoggerReactiveHttpClient;
import reactivefeign.testcase.IcecreamServiceApi;
import reactivefeign.testcase.domain.Bill;
import reactivefeign.testcase.domain.IceCreamOrder;
import reactivefeign.testcase.domain.OrderGenerator;
import reactor.core.publisher.Mono;

import java.util.List;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;

/**
 * @author Sergii Karpenko
 */
abstract public class LoggerTest {

  public static final String LOGGER_NAME = LoggerReactiveHttpClient.class.getName();
  @ClassRule
  public static WireMockClassRule wireMockRule = new WireMockClassRule(
      wireMockConfig()
          .asynchronousResponseEnabled(true)
          .dynamicPort());

  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();

  protected Appender appender;

  @Test
  public void shouldLog() throws Exception {

    Level originalLevel = setLogLevel(Level.TRACE);

    IceCreamOrder order = new OrderGenerator().generate(20);
    Bill billExpected = Bill.makeBill(order);

    wireMockRule.stubFor(post(urlEqualTo("/icecream/orders"))
        .withRequestBody(equalTo(TestUtils.MAPPER.writeValueAsString(order)))
        .willReturn(aResponse().withStatus(200)
            .withHeader("Content-Type", "application/json")
            .withBody(TestUtils.MAPPER.writeValueAsString(billExpected))));

    IcecreamServiceApi client = builder()
        .target(IcecreamServiceApi.class,
            "http://localhost:" + wireMockRule.port());

    Mono<Bill> billMono = client.makeOrder(order);

    // no logs before subscription
    ArgumentCaptor<LogEvent> argumentCaptor = ArgumentCaptor.forClass(LogEvent.class);
    Mockito.verify(appender, never()).append(argumentCaptor.capture());

    billMono.block();

    Mockito.verify(appender, times(7)).append(argumentCaptor.capture());

    List<LogEvent> logEvents = argumentCaptor.getAllValues();
    assertLogEvent(logEvents, 0, Level.DEBUG,
        "[IcecreamServiceApi#makeOrder]--->POST http://localhost");
    assertLogEvent(logEvents, 1, Level.TRACE,
        "[IcecreamServiceApi#makeOrder] REQUEST HEADERS\n" +
            "Accept:[application/json]");
    assertLogEvent(logEvents, 2, Level.TRACE,
        "[IcecreamServiceApi#makeOrder] REQUEST BODY\n" +
            "IceCreamOrder{ id=20, balls=");
    assertLogEvent(logEvents, 3, Level.TRACE,
        "[IcecreamServiceApi#makeOrder] RESPONSE HEADERS",
            "Content-Type:application/json");
    assertLogEvent(logEvents, 4, Level.DEBUG,
        "[IcecreamServiceApi#makeOrder]<--- headers takes");
    assertLogEvent(logEvents, 5, Level.TRACE,
        "[IcecreamServiceApi#makeOrder] RESPONSE BODY\n" +
            "reactivefeign.testcase.domain.Bill");
    assertLogEvent(logEvents, 6, Level.DEBUG,
        "[IcecreamServiceApi#makeOrder]<--- body takes");

    setLogLevel(originalLevel);
  }

  private void assertLogEvent(List<LogEvent> events, int index, Level level, String message) {
    assertThat(events).element(index)
        .hasFieldOrPropertyWithValue("level", level)
        .extracting("message")
        .extractingResultOf("getFormattedMessage")
        .have(new Condition<>(o -> ((String) o).contains(message), "check message"));
  }

  private void assertLogEvent(List<LogEvent> events, int index, Level level, String message1, String message2) {
    assertThat(events).element(index)
            .hasFieldOrPropertyWithValue("level", level)
            .extracting("message")
            .extractingResultOf("getFormattedMessage")
            .have(new Condition<>(o -> ((String) o).contains(message1), "check message1"))
            .have(new Condition<>(o -> ((String) o).contains(message2), "check message2"));;
  }

  @Before
  public void before() {
    appender = Mockito.mock(Appender.class);
    when(appender.getName()).thenReturn("TestAppender");
    when(appender.isStarted()).thenReturn(true);
    getLoggerConfig().addAppender(appender, Level.ALL, null);
  }

  private static Level setLogLevel(Level logLevel) {
    LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
    Configuration configuration = loggerContext.getConfiguration();
    LoggerConfig loggerConfig = configuration.getLoggerConfig(LOGGER_NAME);
    Level previousLevel = loggerConfig.getLevel();
    loggerConfig.setLevel(logLevel);
    loggerContext.updateLoggers();
    return previousLevel;
  }

  private static LoggerConfig getLoggerConfig() {
    LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
    Configuration configuration = loggerContext.getConfiguration();
    configuration.addLogger(LOGGER_NAME, new LoggerConfig());
    return configuration.getLoggerConfig(LOGGER_NAME);
  }
}


================================================
FILE: feign-reactor-core/src/test/java/reactivefeign/NotFoundTest.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import org.apache.http.HttpStatus;
import org.junit.ClassRule;
import org.junit.Test;
import reactivefeign.testcase.IcecreamServiceApi;
import reactor.test.StepVerifier;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;

/**
 * @author Sergii Karpenko
 */
public abstract class NotFoundTest {

  @ClassRule
  public static WireMockClassRule wireMockRule = new WireMockClassRule(
      wireMockConfig().dynamicPort());

  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();

  @Test
  public void shouldReturnEmptyMono() {

    String orderUrl = "/icecream/orders/2";
    wireMockRule.stubFor(get(urlEqualTo(orderUrl))
        .withHeader("Accept", equalTo("application/json"))
        .willReturn(aResponse().withStatus(HttpStatus.SC_NOT_FOUND)));

    IcecreamServiceApi client = builder()
        .decode404()
        .target(IcecreamServiceApi.class, "http://localhost:" + wireMockRule.port());

    StepVerifier.create(client.findOrder(2))
        .expectNextCount(0)
        .verifyComplete();
  }
}


================================================
FILE: feign-reactor-core/src/test/java/reactivefeign/ReactivityTest.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import org.awaitility.Duration;
import org.junit.ClassRule;
import org.junit.Test;
import reactivefeign.testcase.IcecreamServiceApi;
import reactivefeign.testcase.domain.IceCreamOrder;
import reactivefeign.testcase.domain.OrderGenerator;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static org.awaitility.Awaitility.waitAtMost;

/**
 * @author Sergii Karpenko
 */
abstract public class ReactivityTest {

  public static final int DELAY_IN_MILLIS = 500;
  public static final int CALLS_NUMBER = 500;
  public static final int REACTIVE_GAIN_RATIO = 10;
  @ClassRule
  public static WireMockClassRule wireMockRule = new WireMockClassRule(
          wireMockConfig()
                  .asynchronousResponseEnabled(true)
                  .dynamicPort());

  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();

  @Test
  public void shouldRunReactively() throws JsonProcessingException {

    IceCreamOrder orderGenerated = new OrderGenerator().generate(1);
    String orderStr = TestUtils.MAPPER.writeValueAsString(orderGenerated);

    wireMockRule.stubFor(get(urlEqualTo("/icecream/orders/1"))
            .withHeader("Accept", equalTo("application/json"))
            .willReturn(aResponse().withStatus(200)
                    .withHeader("Content-Type", "application/json")
                    .withBody(orderStr)
                    .withFixedDelay(DELAY_IN_MILLIS)));

    IcecreamServiceApi client = builder()
            .target(IcecreamServiceApi.class,
                    "http://localhost:" + wireMockRule.port());

    AtomicInteger counter = new AtomicInteger();

    new Thread(() -> {
      for (int i = 0; i < CALLS_NUMBER; i++) {
        client.findFirstOrder()
                .doOnNext(order -> counter.incrementAndGet())
                .subscribe();
      }
    }).start();

    waitAtMost(new Duration(timeToCompleteReactively(), TimeUnit.MILLISECONDS))
            .until(() -> counter.get() == CALLS_NUMBER);
  }

  public static int timeToCompleteReactively() {
    return CALLS_NUMBER * DELAY_IN_MILLIS / REACTIVE_GAIN_RATIO;
  }
}


================================================
FILE: feign-reactor-core/src/test/java/reactivefeign/ReadTimeoutTest.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import org.junit.ClassRule;
import org.junit.Test;
import reactivefeign.client.ReadTimeoutException;
import reactivefeign.testcase.IcecreamServiceApi;
import reactor.test.StepVerifier;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;

/**
 * @author Sergii Karpenko
 */
abstract public class ReadTimeoutTest {

  @ClassRule
  public static WireMockClassRule wireMockRule = new WireMockClassRule(
      wireMockConfig().dynamicPort());

  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options);

  @Test
  public void shouldFailOnReadTimeout() {

    String orderUrl = "/icecream/orders/1";

    wireMockRule.stubFor(get(urlEqualTo(orderUrl))
        .withHeader("Accept", equalTo("application/json"))
        .willReturn(aResponse().withFixedDelay(200)));

    IcecreamServiceApi client = builder(
        new ReactiveOptions.Builder()
            .setConnectTimeoutMillis(300)
            .setReadTimeoutMillis(100)
            .build())
                .target(IcecreamServiceApi.class,
                    "http://localhost:" + wireMockRule.port());

    StepVerifier.create(client.findOrder(1))
        .expectError(ReadTimeoutException.class)
        .verify();
  }
}


================================================
FILE: feign-reactor-core/src/test/java/reactivefeign/RequestInterceptorTest.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import feign.FeignException;
import org.apache.http.HttpStatus;
import org.junit.ClassRule;
import org.junit.Test;
import reactivefeign.testcase.IcecreamServiceApi;
import reactivefeign.testcase.domain.IceCreamOrder;
import reactivefeign.testcase.domain.OrderGenerator;
import reactivefeign.utils.Pair;
import reactor.test.StepVerifier;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static java.util.Collections.singletonList;
import static reactivefeign.TestUtils.equalsComparingFieldByFieldRecursively;

/**
 * @author Sergii Karpenko
 */
abstract public class RequestInterceptorTest {

  @ClassRule
  public static WireMockClassRule wireMockRule = new WireMockClassRule(
      wireMockConfig().dynamicPort());

  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();

  @Test
  public void shouldInterceptRequestAndSetAuthHeader() throws JsonProcessingException {

    String orderUrl = "/icecream/orders/1";

    IceCreamOrder orderGenerated = new OrderGenerator().generate(1);
    String orderStr = TestUtils.MAPPER.writeValueAsString(orderGenerated);

    wireMockRule.stubFor(get(urlEqualTo(orderUrl))
        .withHeader("Accept", equalTo("application/json"))
        .willReturn(aResponse().withStatus(HttpStatus.SC_UNAUTHORIZED)))
        .setPriority(100);

    wireMockRule.stubFor(get(urlEqualTo(orderUrl))
        .withHeader("Accept", equalTo("application/json"))
        .withHeader("Authorization", equalTo("Bearer mytoken123"))
        .willReturn(aResponse().withStatus(200)
            .withHeader("Content-Type", "application/json")
            .withBody(orderStr)))
        .setPriority(1);

    IcecreamServiceApi clientWithoutAuth = builder()
        .target(IcecreamServiceApi.class, "http://localhost:" + wireMockRule.port());

    StepVerifier.create(clientWithoutAuth.findFirstOrder())
        .expectError(notAuthorizedException())
        .verify();

    IcecreamServiceApi clientWithAuth = builder()
        .addHeaders(singletonList(new Pair<>("Authorization", "Bearer mytoken123")))
        .target(IcecreamServiceApi.class,
            "http://localhost:" + wireMockRule.port());

    StepVerifier.create(clientWithAuth.findFirstOrder())
        .expectNextMatches(equalsComparingFieldByFieldRecursively(orderGenerated))
        .expectComplete();
  }

  protected Class notAuthorizedException() {
    return FeignException.class;
  }
}


================================================
FILE: feign-reactor-core/src/test/java/reactivefeign/RetryingTest.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import feign.RetryableException;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import reactivefeign.publisher.RetryPublisherHttpClient;
import reactivefeign.testcase.IcecreamServiceApi;
import reactivefeign.testcase.domain.IceCreamOrder;
import reactivefeign.testcase.domain.Mixin;
import reactivefeign.testcase.domain.OrderGenerator;
import reactor.test.StepVerifier;

import java.util.Arrays;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
import static org.apache.http.HttpHeaders.RETRY_AFTER;
import static org.apache.http.HttpStatus.SC_OK;
import static org.apache.http.HttpStatus.SC_SERVICE_UNAVAILABLE;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.isA;
import static reactivefeign.TestUtils.equalsComparingFieldByFieldRecursively;

/**
 * @author Sergii Karpenko
 */
public abstract class RetryingTest {

  @ClassRule
  public static WireMockClassRule wireMockRule = new WireMockClassRule(
      wireMockConfig().dynamicPort());

  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();

  @Before
  public void resetServers() {
    wireMockRule.resetAll();
  }

  @Test
  public void shouldSuccessOnRetriesMono() throws JsonProcessingException {

    IceCreamOrder orderGenerated = new OrderGenerator().generate(1);
    String orderStr = TestUtils.MAPPER.writeValueAsString(orderGenerated);

    mockResponseAfterSeveralAttempts(wireMockRule, 2, "testRetrying_success",
        "/icecream/orders/1",
        aResponse().withStatus(503).withHeader(RETRY_AFTER, "1"),
        aResponse().withStatus(200).withHeader("Content-Type", "application/json")
            .withBody(orderStr));

    IcecreamServiceApi client = builder()
        .retryWhen(ReactiveRetryers.retryWithBackoff(3, 0))
        .target(IcecreamServiceApi.class,
            "http://localhost:" + wireMockRule.port());

    StepVerifier.create(client.findOrder(1))
        .expectNextMatches(equalsComparingFieldByFieldRecursively(orderGenerated))
        .verifyComplete();
  }

  @Test
  public void shouldSuccessOnRetriesFlux() throws JsonProcessingException {

    String mixinsStr = TestUtils.MAPPER.writeValueAsString(Mixin.values());

    mockResponseAfterSeveralAttempts(wireMockRule, 2, "testRetrying_success",
        "/icecream/mixins",
        aResponse().withStatus(SC_SERVICE_UNAVAILABLE).withHeader(RETRY_AFTER,
            "1"),
        aResponse().withStatus(SC_OK)
            .withHeader("Content-Type", "application/json")
            .withBody(mixinsStr));

    IcecreamServiceApi client = builder()
        .retryWhen(ReactiveRetryers.retryWithBackoff(3, 0))
        .target(IcecreamServiceApi.class, "http://localhost:" + wireMockRule.port());

    StepVerifier.create(client.getAvailableMixins())
        .expectNextSequence(Arrays.asList(Mixin.values()))
        .verifyComplete();
  }

  @Test
  public void shouldSuccessOnRetriesWoRetryAfter() throws JsonProcessingException {

    IceCreamOrder orderGenerated = new OrderGenerator().generate(1);
    String orderStr = TestUtils.MAPPER.writeValueAsString(orderGenerated);

    mockResponseAfterSeveralAttempts(wireMockRule, 2, "testRetrying_success",
        "/icecream/orders/1", aResponse().withStatus(SC_SERVICE_UNAVAILABLE),
        aResponse().withStatus(SC_OK)
            .withHeader("Content-Type", "application/json")
            .withBody(orderStr));

    IcecreamServiceApi client = builder()
        .retryWhen(ReactiveRetryers.retryWithBackoff(3, 0))
        .target(IcecreamServiceApi.class, "http://localhost:" + wireMockRule.port());

    StepVerifier.create(client.findOrder(1))
        .expectNextMatches(equalsComparingFieldByFieldRecursively(orderGenerated))
        .verifyComplete();
  }

  private static void mockResponseAfterSeveralAttempts(WireMockClassRule rule,
                                                       int failedAttemptsNo,
                                                       String scenario,
                                                       String url,
                                                       ResponseDefinitionBuilder failResponse,
                                                       ResponseDefinitionBuilder response) {
    String state = STARTED;
    for (int attempt = 0; attempt < failedAttemptsNo; attempt++) {
      String nextState = "attempt" + attempt;
      rule.stubFor(
          get(urlEqualTo(url))
              .inScenario(scenario).whenScenarioStateIs(state)
              .willReturn(failResponse).willSetStateTo(nextState));

      state = nextState;
    }

    rule.stubFor(get(urlEqualTo(url))
        .inScenario(scenario)
        .whenScenarioStateIs(state).willReturn(response));
  }

  @Test
  public void shouldFailAsNoMoreRetries() {

    String orderUrl = "/icecream/orders/1";

    wireMockRule.stubFor(get(urlEqualTo(orderUrl))
        .withHeader("Accept", equalTo("application/json"))
        .willReturn(aResponse().withStatus(503).withHeader(RETRY_AFTER, "1")));

    IcecreamServiceApi client = builder()
        .retryWhen(ReactiveRetryers.retry(3))
        .target(IcecreamServiceApi.class, "http://localhost:" + wireMockRule.port());

    StepVerifier.create(client.findOrder(1))
        .expectErrorMatches(throwable -> throwable instanceof RetryPublisherHttpClient.OutOfRetriesException
            && hasProperty("cause", isA(RetryableException.class)).matches(throwable))
        .verify();
  }

  @Test
  public void shouldFailAsNoMoreRetriesWithBackoff() {

    String orderUrl = "/icecream/orders/1";

    wireMockRule.stubFor(get(urlEqualTo(orderUrl))
        .withHeader("Accept", equalTo("application/json"))
        .willReturn(aResponse().withStatus(503).withHeader(RETRY_AFTER, "1")));

    IcecreamServiceApi client = builder()
        .retryWhen(ReactiveRetryers.retryWithBackoff(7, 5))
        .target(IcecreamServiceApi.class, "http://localhost:" + wireMockRule.port());

    StepVerifier.create(client.findOrder(1))
        .expectErrorMatches(throwable -> throwable instanceof RetryPublisherHttpClient.OutOfRetriesException
                && hasProperty("cause", isA(RetryableException.class)).matches(throwable))
        .verify();
  }

}


================================================
FILE: feign-reactor-core/src/test/java/reactivefeign/SmokeTest.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import reactivefeign.testcase.IcecreamServiceApi;
import reactivefeign.testcase.domain.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import java.util.Map;
import java.util.stream.Collectors;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static java.util.Arrays.asList;
import static reactivefeign.TestUtils.equalsComparingFieldByFieldRecursively;

/**
 * @author Sergii Karpenko
 */

abstract public class SmokeTest {

  @ClassRule
  public static WireMockClassRule wireMockRule = new WireMockClassRule(
      wireMockConfig().dynamicPort());

  @Before
  public void resetServers() {
    wireMockRule.resetAll();
  }

  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();

  private IcecreamServiceApi client;

  private OrderGenerator generator = new OrderGenerator();
  private Map<Integer, IceCreamOrder> orders = generator.generateRange(10).stream()
      .collect(Collectors.toMap(IceCreamOrder::getId, o -> o));

  @Rule
  public ExpectedException expectedException = ExpectedException.none();

  @Before
  public void setUp() {
    String targetUrl = "http://localhost:" + wireMockRule.port();
    client = builder()
        .decode404()
        .target(IcecreamServiceApi.class, targetUrl);
  }

  @Test
  public void testSimpleGet_success() throws JsonProcessingException {

    wireMockRule.stubFor(get(urlEqualTo("/icecream/flavors"))
        .willReturn(aResponse().withStatus(200)
            .withHeader("Content-Type", "application/json")
            .withBody(TestUtils.MAPPER.writeValueAsString(Flavor.values()))));

    wireMockRule.stubFor(get(urlEqualTo("/icecream/mixins"))
        .willReturn(aResponse().withStatus(200)
            .withHeader("Content-Type", "application/json")
            .withBody(TestUtils.MAPPER.writeValueAsString(Mixin.values()))));

    Flux<Flavor> flavors = client.getAvailableFlavors();
    Flux<Mixin> mixins = client.getAvailableMixins();

    StepVerifier.create(flavors)
        .expectNextSequence(asList(Flavor.values()))
        .verifyComplete();
    StepVerifier.create(mixins)
        .expectNextSequence(asList(Mixin.values()))
        .verifyComplete();

  }

  @Test
  public void testFindOrder_success() throws JsonProcessingException {

    IceCreamOrder orderExpected = orders.get(1);
    wireMockRule.stubFor(get(urlEqualTo("/icecream/orders/1"))
        .willReturn(aResponse().withStatus(200)
            .withHeader("Content-Type", "application/json")
            .withBody(TestUtils.MAPPER.writeValueAsString(orderExpected))));

    Mono<IceCreamOrder> order = client.findOrder(1);

    StepVerifier.create(order)
        .expectNextMatches(equalsComparingFieldByFieldRecursively(orderExpected))
        .verifyComplete();
  }

  @Test
  public void testFindOrder_empty() {

    Mono<IceCreamOrder> orderEmpty = client.findOrder(123);

    StepVerifier.create(orderEmpty)
        .expectNextCount(0)
        .verifyComplete();
  }

  @Test
  public void testMakeOrder_success() throws JsonProcessingException {

    IceCreamOrder order = new OrderGenerator().generate(20);
    Bill billExpected = Bill.makeBill(order);

    wireMockRule.stubFor(post(urlEqualTo("/icecream/orders"))
        .withRequestBody(equalTo(TestUtils.MAPPER.writeValueAsString(order)))
        .willReturn(aResponse().withStatus(200)
            .withHeader("Content-Type", "application/json")
            .withBody(TestUtils.MAPPER.writeValueAsString(billExpected))));

    Mono<Bill> bill = client.makeOrder(order);
    StepVerifier.create(bill)
        .expectNextMatches(equalsComparingFieldByFieldRecursively(billExpected))
        .verifyComplete();
  }

  @Test
  public void testPayBill_success() throws JsonProcessingException {

    Bill bill = Bill.makeBill(new OrderGenerator().generate(30));

    wireMockRule.stubFor(post(urlEqualTo("/icecream/bills/pay"))
            .withRequestBody(equalTo(TestUtils.MAPPER.writeValueAsString(bill)))
            .willReturn(aResponse().withStatus(200)
                    .withHeader("Content-Type", "application/json")));

    Mono<Void> result = client.payBill(bill);
    StepVerifier.create(result)
        .expectNextCount(0)
        .verifyComplete();
  }
}


================================================
FILE: feign-reactor-core/src/test/java/reactivefeign/StatusHandlerTest.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
import feign.RetryableException;
import org.apache.http.HttpStatus;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import reactivefeign.testcase.IcecreamServiceApi;
import reactor.test.StepVerifier;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static reactivefeign.client.statushandler.CompositeStatusHandler.compose;
import static reactivefeign.client.statushandler.ReactiveStatusHandlers.throwOnStatus;

/**
 * @author Sergii Karpenko
 */
public abstract class StatusHandlerTest {

  @ClassRule
  public static WireMockClassRule wireMockRule = new WireMockClassRule(
      wireMockConfig().dynamicPort());

  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();

  @Before
  public void resetServers() {
    wireMockRule.resetAll();
  }

  @Test
  public void shouldThrowRetryException() {

    wireMockRule.stubFor(get(urlEqualTo("/icecream/orders/1"))
        .withHeader("Accept", equalTo("application/json"))
        .willReturn(aResponse().withStatus(HttpStatus.SC_SERVICE_UNAVAILABLE)));
    IcecreamServiceApi client = builder()
        .statusHandler(throwOnStatus(
            status -> status == HttpStatus.SC_SERVICE_UNAVAILABLE,
            (methodTag, response) -> new RetryableException("Should retry on next node", null)))
        .target(IcecreamServiceApi.class, "http://localhost:" + wireMockRule.port());

    StepVerifier.create(client.findFirstOrder())
        .expectError(RetryableException.class);
  }

  @Test
  public void shouldThrowOnStatusCode() {
    wireMockRule.stubFor(get(urlEqualTo("/icecream/orders/1"))
        .withHeader("Accept", equalTo("application/json"))
        .willReturn(aResponse().withStatus(HttpStatus.SC_SERVICE_UNAVAILABLE)));

    wireMockRule.stubFor(get(urlEqualTo("/icecream/orders/2"))
        .withHeader("Accept", equalTo("application/json"))
        .willReturn(aResponse().withStatus(HttpStatus.SC_UNAUTHORIZED)));


    IcecreamServiceApi client = builder()
        .statusHandler(compose(
            throwOnStatus(
                status -> status == HttpStatus.SC_SERVICE_UNAVAILABLE,
                (methodTag, response) -> new RetryableException("Should retry on next node", null)),
            throwOnStatus(
                status -> status == HttpStatus.SC_UNAUTHORIZED,
                (methodTag, response) -> new RuntimeException("Should login", null))))
        .target(IcecreamServiceApi.class, "http://localhost:" + wireMockRule.port());

    StepVerifier.create(client.findFirstOrder())
        .expectError(RetryableException.class)
        .verify();

    StepVerifier.create(client.findOrder(2))
        .expectError(RuntimeException.class)
        .verify();

  }
}


================================================
FILE: feign-reactor-core/src/test/java/reactivefeign/TestUtils.java
================================================
/**
 * Copyright 2018 The Feign Authors
 *
 * 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 reactivefeign;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import java.util.function.Predicate;

/**
 * Helper methods for tests.
 */
class TestUtils {
  static final ObjectMapper MAPPER;

  static {
    MAPPER = new ObjectMapper();
    MAPPER.registerModule(new JavaTimeModule());
  }

  public static <T> Predicate<T> equalsComparingFieldByFieldRecursively(T rhs) {
    return lhs -> {
      try {
        return MAPPER.writeValueAsString(lhs).equals(MAPPER.writeValueAsString(rhs));
      } catch (JsonProcessingException e) {
        throw new RuntimeException(e);
      }
    };
  }
}


================================================
FILE: feign-reactor-core/src/test/java/reactivefeign/allfeatures/AllFeaturesApi.java
================================================
/*
 * Copyright 2013-2015 the original author or authors.
 *
 * 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 reactivefeign.allfeatures;

import feign.*;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.ByteBuffer;
import java.util.Map;

import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE;
import static org.springframework.http.MediaType.APPLICATION_STREAM_JSON_VALUE;
import static org.springframework.http.MediaType.TEXT_EVENT_STREAM_VALUE;

@Headers({ "Accept: application/json" })
public interface AllFeaturesApi {

	@RequestLine("GET /mirrorParameters/{parameterInPathPlaceholder}?paramInUrl={paramInQueryPlaceholder}")
	Mono<Map<String, String>> mirrorParameters(
            @Param("parameterInPathPlaceholder") long paramInPath,
            @Param("paramInQueryPlaceholder") long paramInQuery,
            @QueryMap Map<String, String> paramMap);

	@RequestLine("GET /mirrorParametersNew?paramInUrl={paramInUrlPlaceholder}")
	Mono<Map<String, String>> mirrorParametersNew(
            @Param("paramInUrlPlaceholder") long paramInUrl,
            @Param("dynamicParam") long dynamicParam,
            @QueryMap Map<String, String> paramMap);

	@RequestLine("GET /mirrorHeaders")
	@Headers({ "Method-Header: {headerValue}" })
	Mono<Map<String, String>> mirrorHeaders(@Param("headerValue") long param,
                                            @HeaderMap Map<String, String> paramMap);

	@RequestLine("POST " + "/mirrorBody")
	Mono<String> mirrorBody(String body);

	@RequestLine("POST " + "/mirrorBodyMap")
	@Headers({ "Content-Type: application/json" })
	Mono<Map<String, String>> mirrorBodyMap(Map<String, String> body);

	@RequestLine("POST " + "/mirrorBodyReactive")
	@Headers({ "Content-Type: application/json" })
	Mono<String> mirrorBodyReactive(Publisher<String> body);

	@RequestLine("POST " + "/mirrorBodyMapReactive")
	@Headers({ "Content-Type: application/json" })
	Mono<Map<String, String>> mirrorBodyMapReactive(Publisher<Map<String, String>> body);

	@RequestLine("POST " + "/mirrorBodyStream")
	@Headers({ "Content-Type: "+APPLICATION_STREAM_JSON_VALUE,
			   "Accept: "+APPLICATION_STREAM_JSON_VALUE})
	Flux<TestObject> mirrorBodyStream(Publisher<TestObject> bodyStream);

	@RequestLine("POST " + "/mirrorIntegerBodyStream")
	@Headers({ "Content-Type: "+APPLICATION_STREAM_JSON_VALUE,
			"Accept: "+APPLICATION_STREAM_JSON_VALUE})
	Flux<Integer> mirrorIntegerBodyStream(Flux<Integer> body);

	@RequestLine("POST " + "/mirrorStringBodyStream")
	@Headers({ "Content-Type: "+TEXT_EVENT_STREAM_VALUE,
			"Accept: "+TEXT_EVENT_STREAM_VALUE})
	Flux<String> mirrorStringBodyStream(Flux<String> body);

	@RequestLine("GET /empty")
	@Headers({ "Method-Header: {headerValue}" })
	Mono<TestObject> empty();

	@RequestLine("POST " + "/mirrorBodyWithDelay")
	Mono<String> mirrorBodyWithDelay(String body);

	@RequestLine("POST " + "/mirrorStreamingBinaryBodyReactive")
	@Headers({ "Content-Type: "+APPLICATION_OCTET_STREAM_VALUE })
	Flux<ByteBuffer> mirrorStreamingBinaryBodyReactive(Publisher<ByteBuffer> body);

	default Mono<String> mirrorDefaultBody() {
		return mirrorBody("default");
	}

	class TestObject {

		public String payload;

		public TestObject() {
		}

		public TestObject(String payload) {
			this.payload = payload;
		}
	}

}


================================================
FILE: feign-reactor-core/src/test/java/reactivefeign/allfeatures/AllFeaturesController.java
================================================
/*
 * Copyright 2013-2015 the original author or authors.
 *
 * 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 reactivefeign.allfeatures;

import org.reactivestreams.Publisher;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.ByteBuffer;
import java.time.Duration;
import java.util.Map;

import static org.springframework.http.MediaType.APPLICATION_STREAM_JSON_VALUE;
import static reactor.core.publisher.Mono.just;

@RestController
public class AllFeaturesController implements AllFeaturesApi {

	@GetMapping(path = "/mirrorParameters/{paramInPath}")
	@Override
	public Mono<Map<String, String>> mirrorParameters(
			@PathVariable("paramInPath") long paramInPath,
			@RequestParam("paramInUrl") long paramInUrl,
			@RequestParam Map<String, String> paramMap) {
		paramMap.put("paramInPath", Long.toString(paramInPath));
		paramMap.put("paramInUrl", Long.toString(paramInUrl));
		return just(paramMap);
	}

	@GetMapping(path = "/mirrorParametersNew")
	@Override
	public Mono<Map<String, String>> mirrorParametersNew(
			@RequestParam("paramInUrl") long paramInUrl,
			@RequestParam("dynamicParam") long dynamicParam,
			@RequestParam Map<String, String> paramMap) {
		paramMap.put("paramInUrl", Long.toString(paramInUrl));
		paramMap.put("dynamicParam", Long.toString(dynamicParam));
		return just(paramMap);
	}

	@GetMapping(path = "/mirrorHeaders")
	@Override
	public Mono<Map<String, String>> mirrorHeaders(
			@RequestHeader("Method-Header") long param,
			@RequestHeader Map<String, String> headersMap) {
		return just(headersMap);
	}

	@PostMapping(path = "/mirrorBody")
	@Override
	public Mono<String> mirrorBody(@RequestBody String body) {
		return just(body);
	}

	@PostMapping(path = "/mirrorBodyMap")
	@Override
	public Mono<Map<String, String>> mirrorBodyMap(
			@RequestBody Map<String, String> body) {
		return just(body);
	}

	@PostMapping(path = "/mirrorBodyReactive")
	@Override
	public Mono<String> mirrorBodyReactive(@RequestBody Publisher<String> body) {
		return Mono.from(body);
	}

	@PostMapping(path = "/mirrorBodyMapReactive")
	@Override
	public Mono<Map<String, String>> mirrorBodyMapReactive(
			@RequestBody Publisher<Map<String, String>> body) {
		return Mono.from(body);
	}

	@PostMapping(path = "/mirrorBodyStream")
	@Override
	public Flux<TestObject> mirrorBodyStream(
			@RequestBody Publisher<TestObject> bodyStream) {
		return Flux.from(bodyStream);
	}

	@PostMapping(path = "/mirrorIntegerBodyStream")
	@Override
	public Flux<Integer> mirrorIntegerBodyStream(
			@RequestBody Flux<Integer> body){
		return body;
	}

	@PostMapping(path = "/mirrorStringBodyStream")
	@Override
	public Flux<String> mirrorStringBodyStream(
			@RequestBody  Flux<String> body){
		return body;
	}

	@PostMapping(path = "/mirrorBodyWithDelay")
	@Override
	public Mono<String> mirrorBodyWithDelay(@RequestBody String body) {
		return just(body).delayElement(Duration.ofMillis(500));
	}

	@Override
	public Mono<TestObject> empty() {
		return Mono.empty();
	}

	@PostMapping(path = "/mirrorStreamingBinaryBodyReactive")
	@Override
	public Flux<ByteBuffer> mirrorStreamingBinaryBodyReactive(@RequestBody Publisher<ByteBuffer> body) {
		return Flux.from(body);
	}

}


================================================
FILE: feign-reactor-core/src/test/java/reactivefeign/allfeatures/AllFeaturesTest.java
================================================
/*
 * Copyright 2013-2015 the original author or authors.
 *
 * 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 reactivefeign.allfeatures;

import org.awaitility.Duration;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import reactivefeign.ReactiveFeign;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import reactor.test.StepVerifier;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static java.nio.ByteBuffer.wrap;
import static org.assertj.core.api.Assertions.ass
Download .txt
gitextract_wskod754/

├── .gitignore
├── LICENSE
├── README.md
├── feign-reactor-cloud/
│   ├── pom.xml
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── reactivefeign/
│       │           └── cloud/
│       │               ├── CloudReactiveFeign.java
│       │               ├── methodhandler/
│       │               │   ├── HystrixMethodHandler.java
│       │               │   └── HystrixMethodHandlerFactory.java
│       │               └── publisher/
│       │                   └── RibbonPublisherClient.java
│       └── test/
│           └── java/
│               └── reactivefeign/
│                   └── cloud/
│                       ├── HystrixReactiveHttpClientTest.java
│                       └── LoadBalancingReactiveHttpClientTest.java
├── feign-reactor-core/
│   ├── pom.xml
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── reactivefeign/
│       │           ├── ReactiveContract.java
│       │           ├── ReactiveFeign.java
│       │           ├── ReactiveInvocationHandler.java
│       │           ├── ReactiveOptions.java
│       │           ├── ReactiveRetryPolicy.java
│       │           ├── ReactiveRetryers.java
│       │           ├── client/
│       │           │   ├── DelegatingReactiveHttpResponse.java
│       │           │   ├── InterceptorReactiveHttpClient.java
│       │           │   ├── LoggerReactiveHttpClient.java
│       │           │   ├── ReactiveHttpClient.java
│       │           │   ├── ReactiveHttpRequest.java
│       │           │   ├── ReactiveHttpRequestInterceptor.java
│       │           │   ├── ReactiveHttpResponse.java
│       │           │   ├── ReadTimeoutException.java
│       │           │   ├── ResponseMappers.java
│       │           │   ├── StatusHandlerReactiveHttpClient.java
│       │           │   └── statushandler/
│       │           │       ├── CompositeStatusHandler.java
│       │           │       ├── ReactiveStatusHandler.java
│       │           │       └── ReactiveStatusHandlers.java
│       │           ├── methodhandler/
│       │           │   ├── DefaultMethodHandler.java
│       │           │   ├── FluxMethodHandler.java
│       │           │   ├── MethodHandler.java
│       │           │   ├── MethodHandlerFactory.java
│       │           │   ├── MonoMethodHandler.java
│       │           │   ├── PublisherClientMethodHandler.java
│       │           │   └── ReactiveMethodHandlerFactory.java
│       │           ├── publisher/
│       │           │   ├── FluxPublisherHttpClient.java
│       │           │   ├── FluxRetryPublisherHttpClient.java
│       │           │   ├── MonoPublisherHttpClient.java
│       │           │   ├── MonoRetryPublisherHttpClient.java
│       │           │   ├── PublisherClientFactory.java
│       │           │   ├── PublisherHttpClient.java
│       │           │   └── RetryPublisherHttpClient.java
│       │           └── utils/
│       │               ├── FeignUtils.java
│       │               ├── HttpUtils.java
│       │               ├── MultiValueMapUtils.java
│       │               └── Pair.java
│       └── test/
│           └── java/
│               └── reactivefeign/
│                   ├── CompressionTest.java
│                   ├── ConnectionTimeoutTest.java
│                   ├── ContractTest.java
│                   ├── DefaultMethodTest.java
│                   ├── LoggerTest.java
│                   ├── NotFoundTest.java
│                   ├── ReactivityTest.java
│                   ├── ReadTimeoutTest.java
│                   ├── RequestInterceptorTest.java
│                   ├── RetryingTest.java
│                   ├── SmokeTest.java
│                   ├── StatusHandlerTest.java
│                   ├── TestUtils.java
│                   ├── allfeatures/
│                   │   ├── AllFeaturesApi.java
│                   │   ├── AllFeaturesController.java
│                   │   └── AllFeaturesTest.java
│                   ├── resttemplate/
│                   │   ├── CompressionTest.java
│                   │   ├── ConnectionTimeoutTest.java
│                   │   ├── ContractTest.java
│                   │   ├── DefaultMethodTest.java
│                   │   ├── LoggerTest.java
│                   │   ├── NotFoundTest.java
│                   │   ├── ReactivityTest.java
│                   │   ├── ReadTimeoutTest.java
│                   │   ├── RequestInterceptorTest.java
│                   │   ├── RetryingTest.java
│                   │   ├── SmokeTest.java
│                   │   ├── StatusHandlerTest.java
│                   │   └── client/
│                   │       ├── RestTemplateFakeReactiveFeign.java
│                   │       └── RestTemplateFakeReactiveHttpClient.java
│                   └── testcase/
│                       ├── IcecreamServiceApi.java
│                       ├── IcecreamServiceApiBroken.java
│                       ├── IcecreamServiceApiBrokenByCopy.java
│                       └── domain/
│                           ├── Bill.java
│                           ├── Flavor.java
│                           ├── IceCreamOrder.java
│                           ├── Mixin.java
│                           └── OrderGenerator.java
├── feign-reactor-jetty/
│   ├── pom.xml
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── reactivefeign/
│       │           └── jetty/
│       │               ├── JettyReactiveFeign.java
│       │               ├── client/
│       │               │   ├── JettyReactiveHttpClient.java
│       │               │   └── JettyReactiveHttpResponse.java
│       │               └── utils/
│       │                   └── ProxyPostProcessor.java
│       └── test/
│           ├── java/
│           │   └── reactivefeign/
│           │       └── jetty/
│           │           ├── CompressionTest.java
│           │           ├── ConnectionTimeoutTest.java
│           │           ├── ContractTest.java
│           │           ├── DefaultMethodTest.java
│           │           ├── LoggerTest.java
│           │           ├── NotFoundTest.java
│           │           ├── ReactivityTest.java
│           │           ├── ReadTimeoutTest.java
│           │           ├── RequestInterceptorTest.java
│           │           ├── RetryingTest.java
│           │           ├── SmokeTest.java
│           │           ├── StatusHandlerTest.java
│           │           └── allfeatures/
│           │               └── AllFeaturesTest.java
│           └── resources/
│               └── log4j2.xml
├── feign-reactor-rx2/
│   ├── pom.xml
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── reactivefeign/
│       │           └── rx2/
│       │               ├── Rx2Contract.java
│       │               ├── Rx2ReactiveFeign.java
│       │               ├── client/
│       │               │   └── statushandler/
│       │               │       ├── Rx2ReactiveStatusHandler.java
│       │               │       ├── Rx2StatusHandler.java
│       │               │       └── Rx2StatusHandlers.java
│       │               └── methodhandler/
│       │                   ├── Rx2MethodHandler.java
│       │                   ├── Rx2MethodHandlerFactory.java
│       │                   └── Rx2PublisherClientMethodHandler.java
│       └── test/
│           └── java/
│               └── reactivefeign/
│                   └── rx2/
│                       ├── ContractTest.java
│                       ├── DefaultMethodTest.java
│                       ├── LoggerTest.java
│                       ├── NotFoundTest.java
│                       ├── ReactivityTest.java
│                       ├── ReadTimeoutTest.java
│                       ├── RequestInterceptorTest.java
│                       ├── SmokeTest.java
│                       ├── StatusHandlerTest.java
│                       ├── TestUtils.java
│                       └── testcase/
│                           ├── IcecreamServiceApi.java
│                           ├── IcecreamServiceApiBroken.java
│                           └── domain/
│                               ├── Bill.java
│                               ├── Flavor.java
│                               ├── IceCreamOrder.java
│                               ├── Mixin.java
│                               └── OrderGenerator.java
├── feign-reactor-webclient/
│   ├── pom.xml
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── reactivefeign/
│       │           └── webclient/
│       │               ├── WebReactiveFeign.java
│       │               └── client/
│       │                   ├── WebReactiveHttpClient.java
│       │                   └── WebReactiveHttpResponse.java
│       └── test/
│           ├── java/
│           │   └── reactivefeign/
│           │       └── webclient/
│           │           ├── CompressionTest.java
│           │           ├── ConnectionTimeoutTest.java
│           │           ├── ContractTest.java
│           │           ├── DefaultMethodTest.java
│           │           ├── LoggerTest.java
│           │           ├── NotFoundTest.java
│           │           ├── ReactivityTest.java
│           │           ├── ReadTimeoutTest.java
│           │           ├── RequestInterceptorTest.java
│           │           ├── RetryingTest.java
│           │           ├── SmokeTest.java
│           │           ├── StatusHandlerTest.java
│           │           └── allfeatures/
│           │               ├── AllFeaturesTest.java
│           │               ├── WebClientFeaturesApi.java
│           │               ├── WebClientFeaturesController.java
│           │               └── WebClientFeaturesTest.java
│           └── resources/
│               └── log4j2.xml
├── pom.xml
└── settings.xml
Download .txt
SYMBOL INDEX (716 symbols across 142 files)

FILE: feign-reactor-cloud/src/main/java/reactivefeign/cloud/CloudReactiveFeign.java
  class CloudReactiveFeign (line 42) | public class CloudReactiveFeign extends ReactiveFeign {
    method CloudReactiveFeign (line 46) | private CloudReactiveFeign(ReactiveFeign.ParseHandlersByName targetToH...
    method builder (line 50) | public static <T> Builder<T> builder() {
    method builder (line 54) | public static <T> Builder<T> builder(WebClient webClient) {
    class Builder (line 58) | public static class Builder<T> extends WebReactiveFeign.Builder<T> {
      method Builder (line 65) | protected Builder() {
      method Builder (line 69) | protected Builder(WebClient webClient) {
      method disableHystrix (line 73) | public void disableHystrix() {
      method setHystrixCommandSetterFactory (line 77) | public Builder<T> setHystrixCommandSetterFactory(SetterFactory comma...
      method setFallback (line 82) | public Builder<T> setFallback(T fallback) {
      method setFallbackFactory (line 86) | public Builder<T> setFallbackFactory(Function<Throwable, ? extends T...
      method enableLoadBalancer (line 91) | public Builder<T> enableLoadBalancer(){
      method enableLoadBalancer (line 98) | public Builder<T> enableLoadBalancer(RetryHandler retryHandler){
      method setLoadBalancerCommandFactory (line 111) | public Builder<T> setLoadBalancerCommandFactory(
      method buildReactiveMethodHandlerFactory (line 117) | @Override
      method buildReactiveClientFactory (line 128) | @Override
      method extractServiceName (line 139) | private String extractServiceName(String url){
      method contract (line 147) | @Override
      method requestInterceptor (line 153) | @Override
      method decode404 (line 159) | @Override
      method statusHandler (line 165) | @Override
      method responseMapper (line 171) | @Override
      method retryWhen (line 178) | @Override
      method options (line 184) | @Override
    type SetterFactory (line 191) | public interface SetterFactory {
      method create (line 192) | HystrixObservableCommand.Setter create(Target<?> target, MethodMetad...
    class DefaultSetterFactory (line 195) | public static class DefaultSetterFactory implements SetterFactory {
      method create (line 196) | @Override

FILE: feign-reactor-cloud/src/main/java/reactivefeign/cloud/methodhandler/HystrixMethodHandler.java
  class HystrixMethodHandler (line 27) | public class HystrixMethodHandler implements MethodHandler {
    method HystrixMethodHandler (line 35) | HystrixMethodHandler(
    method invoke (line 56) | @Override
    method getFallbackValue (line 98) | protected Object getFallbackValue(Object target, Method method, Object...

FILE: feign-reactor-cloud/src/main/java/reactivefeign/cloud/methodhandler/HystrixMethodHandlerFactory.java
  class HystrixMethodHandlerFactory (line 15) | public class HystrixMethodHandlerFactory implements MethodHandlerFactory {
    method HystrixMethodHandlerFactory (line 21) | public HystrixMethodHandlerFactory(MethodHandlerFactory methodHandlerF...
    method create (line 29) | @Override
    method createDefault (line 38) | @Override

FILE: feign-reactor-cloud/src/main/java/reactivefeign/cloud/publisher/RibbonPublisherClient.java
  class RibbonPublisherClient (line 21) | public class RibbonPublisherClient implements PublisherHttpClient {
    method RibbonPublisherClient (line 27) | public RibbonPublisherClient(@Nullable LoadBalancerCommand<Object> loa...
    method executeRequest (line 35) | @Override
    method loadBalanceRequest (line 61) | protected ReactiveHttpRequest loadBalanceRequest(ReactiveHttpRequest r...

FILE: feign-reactor-cloud/src/test/java/reactivefeign/cloud/HystrixReactiveHttpClientTest.java
  class HystrixReactiveHttpClientTest (line 30) | public class HystrixReactiveHttpClientTest {
    method resetServers (line 46) | @Before
    method shouldFailAsNoFallback (line 53) | @Test
    method shouldNotFailDueToFallback (line 73) | @Test
    method shouldOpenCircuitBreakerAndCloseAfterSleepTime (line 92) | @Test
    method getSetterFactory (line 148) | CloudReactiveFeign.SetterFactory getSetterFactory(int testNo) {

FILE: feign-reactor-cloud/src/test/java/reactivefeign/cloud/LoadBalancingReactiveHttpClientTest.java
  class LoadBalancingReactiveHttpClientTest (line 35) | public class LoadBalancingReactiveHttpClientTest {
    method setupServersList (line 47) | @BeforeClass
    method resetServers (line 56) | @Before
    method shouldLoadBalanceRequests (line 62) | @Test
    method shouldFailAsPolicyWoRetries (line 84) | @Test
    method shouldRetryOnSameAndFail (line 100) | @Test
    method shouldRetryOnNextAndFail (line 116) | @Test
    method shouldRetryOnSameAndSuccess (line 131) | @Test
    method loadBalancingWithRetry (line 141) | private void loadBalancingWithRetry(int failedAttemptsNo, int retryOnS...
    method getSetterFactoryWithTimeoutDisabled (line 165) | private CloudReactiveFeign.SetterFactory getSetterFactoryWithTimeoutDi...
    method mockSuccess (line 178) | static void mockSuccess(WireMockClassRule server, String body) {
    method mockSuccessAfterSeveralAttempts (line 186) | static void mockSuccessAfterSeveralAttempts(WireMockClassRule server, ...
    type TestInterface (line 209) | interface TestInterface {
      method get (line 211) | @RequestLine("GET /")

FILE: feign-reactor-core/src/main/java/reactivefeign/ReactiveContract.java
  class ReactiveContract (line 34) | public class ReactiveContract implements Contract {
    method ReactiveContract (line 38) | public ReactiveContract(final Contract delegate) {
    method parseAndValidatateMetadata (line 42) | @Override
    method isReactorType (line 65) | private boolean isReactorType(final Type type) {

FILE: feign-reactor-core/src/main/java/reactivefeign/ReactiveFeign.java
  class ReactiveFeign (line 62) | public class ReactiveFeign {
    method ReactiveFeign (line 67) | protected ReactiveFeign(
    method newInstance (line 74) | @SuppressWarnings("unchecked")
    class Builder (line 105) | public abstract static class Builder<T> {
      method Builder (line 119) | protected Builder(){
      method options (line 123) | abstract public Builder<T> options(ReactiveOptions options);
      method clientFactory (line 125) | protected Builder<T> clientFactory(Function<MethodMetadata, Reactive...
      method contract (line 136) | public Builder<T> contract(final Contract contract) {
      method addHeaders (line 141) | public Builder<T> addHeaders(List<Pair<String, String>> headers) {
      method requestInterceptor (line 149) | public Builder<T> requestInterceptor(ReactiveHttpRequestInterceptor ...
      method decode404 (line 166) | public Builder<T> decode404() {
      method statusHandler (line 171) | public Builder<T> statusHandler(ReactiveStatusHandler statusHandler) {
      method responseMapper (line 182) | public Builder<T> responseMapper(BiFunction<MethodMetadata, Reactive...
      method retryWhen (line 187) | public Builder<T> retryWhen(Function<Flux<Throwable>, Flux<Throwable...
      method retryWhen (line 192) | public Builder<T> retryWhen(ReactiveRetryPolicy retryPolicy) {
      method target (line 203) | public T target(final Class<T> apiType, final String url) {
      method target (line 213) | public T target(final Target<T> target) {
      method build (line 218) | protected ReactiveFeign build() {
      method buildReactiveMethodHandlerFactory (line 224) | protected MethodHandlerFactory buildReactiveMethodHandlerFactory() {
      method buildReactiveClientFactory (line 228) | protected PublisherClientFactory buildReactiveClientFactory() {
      method retry (line 263) | protected PublisherHttpClient retry(
      method toPublisher (line 279) | protected PublisherHttpClient toPublisher(ReactiveHttpClient reactiv...
    class ParseHandlersByName (line 291) | public static final class ParseHandlersByName {
      method ParseHandlersByName (line 295) | ParseHandlersByName(final Contract contract, final MethodHandlerFact...
      method apply (line 300) | Map<String, MethodHandler> apply(final Target target) {

FILE: feign-reactor-core/src/main/java/reactivefeign/ReactiveInvocationHandler.java
  class ReactiveInvocationHandler (line 37) | public final class ReactiveInvocationHandler implements InvocationHandler {
    method ReactiveInvocationHandler (line 41) | private ReactiveInvocationHandler(final Target<?> target,
    method defineObjectMethodsHandlers (line 48) | private void defineObjectMethodsHandlers() {
    method invoke (line 66) | @Override
    method equals (line 71) | @Override
    method hashCode (line 80) | @Override
    method toString (line 85) | @Override
    class Factory (line 93) | public static final class Factory implements InvocationHandlerFactory {
      method create (line 95) | @Override

FILE: feign-reactor-core/src/main/java/reactivefeign/ReactiveOptions.java
  class ReactiveOptions (line 19) | public class ReactiveOptions {
    method ReactiveOptions (line 25) | private ReactiveOptions(Long connectTimeoutMillis, Long readTimeoutMil...
    method getConnectTimeoutMillis (line 32) | public Long getConnectTimeoutMillis() {
    method getReadTimeoutMillis (line 36) | public Long getReadTimeoutMillis() {
    method isTryUseCompression (line 40) | public Boolean isTryUseCompression() {
    method isEmpty (line 44) | public boolean isEmpty() {
    class Builder (line 49) | public static class Builder {
      method Builder (line 54) | public Builder() {}
      method setConnectTimeoutMillis (line 56) | public Builder setConnectTimeoutMillis(long connectTimeoutMillis) {
      method setReadTimeoutMillis (line 61) | public Builder setReadTimeoutMillis(long readTimeoutMillis) {
      method setTryUseCompression (line 66) | public Builder setTryUseCompression(boolean tryUseCompression) {
      method build (line 71) | public ReactiveOptions build() {

FILE: feign-reactor-core/src/main/java/reactivefeign/ReactiveRetryPolicy.java
  type ReactiveRetryPolicy (line 24) | public interface ReactiveRetryPolicy {
    method retryDelay (line 30) | long retryDelay(Throwable error, int attemptNo);
    method toRetryFunction (line 32) | default Function<Flux<Throwable>, Flux<Throwable>> toRetryFunction() {

FILE: feign-reactor-core/src/main/java/reactivefeign/ReactiveRetryers.java
  class ReactiveRetryers (line 23) | public class ReactiveRetryers {
    method retry (line 25) | public static ReactiveRetryPolicy retry(int maxRetries) {
    method retryWithBackoff (line 29) | public static ReactiveRetryPolicy retryWithBackoff(int maxRetries, lon...

FILE: feign-reactor-core/src/main/java/reactivefeign/client/DelegatingReactiveHttpResponse.java
  class DelegatingReactiveHttpResponse (line 24) | abstract public class DelegatingReactiveHttpResponse implements Reactive...
    method DelegatingReactiveHttpResponse (line 28) | protected DelegatingReactiveHttpResponse(ReactiveHttpResponse response) {
    method getResponse (line 32) | protected ReactiveHttpResponse getResponse() {
    method status (line 36) | @Override
    method headers (line 41) | @Override
    method bodyData (line 46) | @Override

FILE: feign-reactor-core/src/main/java/reactivefeign/client/InterceptorReactiveHttpClient.java
  class InterceptorReactiveHttpClient (line 21) | public class InterceptorReactiveHttpClient {
    method intercept (line 23) | public static ReactiveHttpClient intercept(ReactiveHttpClient reactive...

FILE: feign-reactor-core/src/main/java/reactivefeign/client/LoggerReactiveHttpClient.java
  class LoggerReactiveHttpClient (line 36) | public class LoggerReactiveHttpClient implements ReactiveHttpClient {
    method log (line 43) | public static ReactiveHttpClient log(ReactiveHttpClient reactiveClient...
    method LoggerReactiveHttpClient (line 47) | private LoggerReactiveHttpClient(ReactiveHttpClient reactiveClient,
    method executeRequest (line 53) | @Override
    method logRequest (line 72) | private ReactiveHttpRequest logRequest(
    method logResponseHeaders (line 104) | private void logResponseHeaders(String feignMethodTag,
    method logResponseBodyAndTime (line 121) | private void logResponseBodyAndTime(String feignMethodTag, Object resp...
    class LoggerReactiveHttpResponse (line 131) | private class LoggerReactiveHttpResponse extends DelegatingReactiveHtt...
      method LoggerReactiveHttpResponse (line 135) | private LoggerReactiveHttpResponse(ReactiveHttpResponse response, At...
      method body (line 140) | @Override
      method bodyData (line 152) | @Override
      method responseBodyLogger (line 159) | private Consumer<Object> responseBodyLogger(AtomicLong start) {
    method msg (line 165) | private static MessageSupplier msg(Supplier<?> supplier) {
    class MessageSupplier (line 169) | static class MessageSupplier {
      method MessageSupplier (line 172) | public MessageSupplier(Supplier<?> supplier) {
      method toString (line 176) | @Override

FILE: feign-reactor-core/src/main/java/reactivefeign/client/ReactiveHttpClient.java
  type ReactiveHttpClient (line 5) | public interface ReactiveHttpClient {
    method executeRequest (line 7) | Mono<ReactiveHttpResponse> executeRequest(ReactiveHttpRequest request);

FILE: feign-reactor-core/src/main/java/reactivefeign/client/ReactiveHttpRequest.java
  class ReactiveHttpRequest (line 29) | public final class ReactiveHttpRequest {
    method ReactiveHttpRequest (line 40) | public ReactiveHttpRequest(String method, URI uri,
    method ReactiveHttpRequest (line 48) | public ReactiveHttpRequest(ReactiveHttpRequest request, Publisher<Obje...
    method method (line 53) | public String method() {
    method uri (line 58) | public URI uri() {
    method headers (line 63) | public Map<String, List<String>> headers() {
    method body (line 70) | public Publisher<Object> body() {

FILE: feign-reactor-core/src/main/java/reactivefeign/client/ReactiveHttpRequestInterceptor.java
  type ReactiveHttpRequestInterceptor (line 24) | public interface ReactiveHttpRequestInterceptor

FILE: feign-reactor-core/src/main/java/reactivefeign/client/ReactiveHttpResponse.java
  type ReactiveHttpResponse (line 27) | public interface ReactiveHttpResponse {
    method status (line 29) | int status();
    method headers (line 31) | Map<String, List<String>> headers();
    method body (line 33) | Publisher<?> body();
    method bodyData (line 40) | Mono<byte[]> bodyData();

FILE: feign-reactor-core/src/main/java/reactivefeign/client/ReadTimeoutException.java
  class ReadTimeoutException (line 16) | public class ReadTimeoutException extends RuntimeException {
    method ReadTimeoutException (line 18) | public ReadTimeoutException(Throwable cause) {

FILE: feign-reactor-core/src/main/java/reactivefeign/client/ResponseMappers.java
  class ResponseMappers (line 28) | public class ResponseMappers {
    method ignore404 (line 30) | public static BiFunction<MethodMetadata, ReactiveHttpResponse, Reactiv...
    method mapResponse (line 49) | public static ReactiveHttpClient mapResponse(

FILE: feign-reactor-core/src/main/java/reactivefeign/client/StatusHandlerReactiveHttpClient.java
  class StatusHandlerReactiveHttpClient (line 30) | public class StatusHandlerReactiveHttpClient implements ReactiveHttpClie...
    method handleStatus (line 37) | public static ReactiveHttpClient handleStatus(
    method StatusHandlerReactiveHttpClient (line 44) | private StatusHandlerReactiveHttpClient(ReactiveHttpClient reactiveCli...
    method executeRequest (line 52) | @Override
    class ErrorReactiveHttpResponse (line 63) | private class ErrorReactiveHttpResponse extends DelegatingReactiveHttp...
      method ErrorReactiveHttpResponse (line 67) | protected ErrorReactiveHttpResponse(ReactiveHttpResponse response, M...
      method body (line 72) | @Override

FILE: feign-reactor-core/src/main/java/reactivefeign/client/statushandler/CompositeStatusHandler.java
  class CompositeStatusHandler (line 26) | public class CompositeStatusHandler implements ReactiveStatusHandler {
    method compose (line 30) | public static CompositeStatusHandler compose(ReactiveStatusHandler... ...
    method CompositeStatusHandler (line 34) | private CompositeStatusHandler(List<ReactiveStatusHandler> handlers) {
    method shouldHandle (line 38) | @Override
    method decode (line 43) | @Override

FILE: feign-reactor-core/src/main/java/reactivefeign/client/statushandler/ReactiveStatusHandler.java
  type ReactiveStatusHandler (line 22) | public interface ReactiveStatusHandler {
    method shouldHandle (line 24) | boolean shouldHandle(int status);
    method decode (line 26) | Mono<? extends Throwable> decode(String methodKey, ReactiveHttpRespons...

FILE: feign-reactor-core/src/main/java/reactivefeign/client/statushandler/ReactiveStatusHandlers.java
  class ReactiveStatusHandlers (line 29) | public class ReactiveStatusHandlers {
    method defaultFeign (line 31) | public static ReactiveStatusHandler defaultFeign(ErrorDecoder errorDec...
    method throwOnStatus (line 55) | public static ReactiveStatusHandler throwOnStatus(

FILE: feign-reactor-core/src/main/java/reactivefeign/methodhandler/DefaultMethodHandler.java
  class DefaultMethodHandler (line 25) | public final class DefaultMethodHandler implements MethodHandler {
    method DefaultMethodHandler (line 33) | public DefaultMethodHandler(Method defaultMethod) {
    method bindTo (line 51) | public void bindTo(Object proxy) {
    method invoke (line 63) | @Override

FILE: feign-reactor-core/src/main/java/reactivefeign/methodhandler/FluxMethodHandler.java
  class FluxMethodHandler (line 5) | public class FluxMethodHandler implements MethodHandler {
    method FluxMethodHandler (line 9) | public FluxMethodHandler(MethodHandler methodHandler) {
    method invoke (line 13) | @Override

FILE: feign-reactor-core/src/main/java/reactivefeign/methodhandler/MethodHandler.java
  type MethodHandler (line 5) | public interface MethodHandler extends InvocationHandlerFactory.MethodHa...

FILE: feign-reactor-core/src/main/java/reactivefeign/methodhandler/MethodHandlerFactory.java
  type MethodHandlerFactory (line 22) | public interface MethodHandlerFactory {
    method create (line 24) | MethodHandler create(final Target target, final MethodMetadata metadata);
    method createDefault (line 26) | MethodHandler createDefault(Method method);

FILE: feign-reactor-core/src/main/java/reactivefeign/methodhandler/MonoMethodHandler.java
  class MonoMethodHandler (line 6) | public class MonoMethodHandler implements MethodHandler {
    method MonoMethodHandler (line 10) | public MonoMethodHandler(MethodHandler methodHandler) {
    method invoke (line 14) | @Override

FILE: feign-reactor-core/src/main/java/reactivefeign/methodhandler/PublisherClientMethodHandler.java
  class PublisherClientMethodHandler (line 47) | public class PublisherClientMethodHandler implements MethodHandler {
    method PublisherClientMethodHandler (line 57) | public PublisherClientMethodHandler(Target target,
    method invoke (line 75) | @Override
    method buildRequest (line 84) | protected ReactiveHttpRequest buildRequest(Object[] argv) {
    method queryLine (line 106) | private String queryLine(Map<String, Collection<String>> queries) {
    method queries (line 129) | protected Map<String, Collection<String>> queries(Object[] argv,
    method headers (line 155) | protected Map<String, List<String>> headers(Object[] argv, Map<String,...
    method body (line 182) | protected Publisher<Object> body(Object[] argv) {
    method body (line 190) | protected Publisher<Object> body(Object body) {
    method buildExpanders (line 198) | private static Map<String, List<Function<Map<String, ?>, String>>> bui...
    method buildExpandFunction (line 215) | private static Function<Map<String, ?>, String> buildExpandFunction(St...

FILE: feign-reactor-core/src/main/java/reactivefeign/methodhandler/ReactiveMethodHandlerFactory.java
  class ReactiveMethodHandlerFactory (line 15) | public class ReactiveMethodHandlerFactory implements MethodHandlerFactory {
    method ReactiveMethodHandlerFactory (line 19) | public ReactiveMethodHandlerFactory(final PublisherClientFactory publi...
    method create (line 23) | @Override
    method createDefault (line 39) | @Override

FILE: feign-reactor-core/src/main/java/reactivefeign/publisher/FluxPublisherHttpClient.java
  class FluxPublisherHttpClient (line 15) | public class FluxPublisherHttpClient implements PublisherHttpClient {
    method FluxPublisherHttpClient (line 19) | public FluxPublisherHttpClient(ReactiveHttpClient reactiveHttpClient) {
    method executeRequest (line 23) | @Override

FILE: feign-reactor-core/src/main/java/reactivefeign/publisher/FluxRetryPublisherHttpClient.java
  class FluxRetryPublisherHttpClient (line 29) | public class FluxRetryPublisherHttpClient extends RetryPublisherHttpClie...
    method FluxRetryPublisherHttpClient (line 31) | public FluxRetryPublisherHttpClient(
    method executeRequest (line 37) | @Override

FILE: feign-reactor-core/src/main/java/reactivefeign/publisher/MonoPublisherHttpClient.java
  class MonoPublisherHttpClient (line 15) | public class MonoPublisherHttpClient implements PublisherHttpClient {
    method MonoPublisherHttpClient (line 19) | public MonoPublisherHttpClient(ReactiveHttpClient reactiveHttpClient) {
    method executeRequest (line 23) | @Override

FILE: feign-reactor-core/src/main/java/reactivefeign/publisher/MonoRetryPublisherHttpClient.java
  class MonoRetryPublisherHttpClient (line 29) | public class MonoRetryPublisherHttpClient extends RetryPublisherHttpClie...
    method MonoRetryPublisherHttpClient (line 31) | public MonoRetryPublisherHttpClient(
    method executeRequest (line 37) | @Override

FILE: feign-reactor-core/src/main/java/reactivefeign/publisher/PublisherClientFactory.java
  type PublisherClientFactory (line 24) | public interface PublisherClientFactory extends Function<MethodMetadata,...

FILE: feign-reactor-core/src/main/java/reactivefeign/publisher/PublisherHttpClient.java
  type PublisherHttpClient (line 11) | public interface PublisherHttpClient {
    method executeRequest (line 13) | Publisher<?> executeRequest(ReactiveHttpRequest request);

FILE: feign-reactor-core/src/main/java/reactivefeign/publisher/RetryPublisherHttpClient.java
  class RetryPublisherHttpClient (line 30) | abstract public class RetryPublisherHttpClient<P extends PublisherHttpCl...
    method RetryPublisherHttpClient (line 38) | protected RetryPublisherHttpClient(P publisherClient,
    method outOfRetries (line 46) | protected Function<Throwable, Throwable> outOfRetries() {
    method wrapWithLog (line 53) | protected static Function<Flux<Throwable>, Flux<?>> wrapWithLog(
    class OutOfRetriesException (line 64) | public static class OutOfRetriesException extends Exception {
      method OutOfRetriesException (line 65) | OutOfRetriesException(Throwable cause, String feignMethodTag) {

FILE: feign-reactor-core/src/main/java/reactivefeign/utils/FeignUtils.java
  class FeignUtils (line 25) | public class FeignUtils {
    method methodTag (line 27) | public static String methodTag(MethodMetadata methodMetadata) {
    method returnPublisherType (line 32) | public static Class returnPublisherType(MethodMetadata methodMetadata) {
    method returnActualType (line 37) | public static Type returnActualType(MethodMetadata methodMetadata) {
    method bodyActualType (line 41) | public static Type bodyActualType(MethodMetadata methodMetadata) {
    method getBodyActualType (line 45) | public static Type getBodyActualType(Type bodyType) {

FILE: feign-reactor-core/src/main/java/reactivefeign/utils/HttpUtils.java
  class HttpUtils (line 18) | public class HttpUtils {
    method familyOf (line 20) | public static StatusCodeFamily familyOf(final int statusCode) {
    type StatusCodeFamily (line 37) | public enum StatusCodeFamily {
      method StatusCodeFamily (line 43) | StatusCodeFamily(boolean error) {
      method isError (line 47) | public boolean isError() {

FILE: feign-reactor-core/src/main/java/reactivefeign/utils/MultiValueMapUtils.java
  class MultiValueMapUtils (line 21) | public class MultiValueMapUtils {
    method addAllOrdered (line 23) | public static <K, V> void addAllOrdered(Map<K, List<V>> multiMap, K ke...
    method addOrdered (line 31) | public static <K, V> void addOrdered(Map<K, List<V>> multiMap, K key, ...
    method addAll (line 39) | public static <K, V> void addAll(Map<K, Collection<V>> multiMap, K key...
    method add (line 47) | public static <K, V> void add(Map<K, Collection<V>> multiMap, K key, V...

FILE: feign-reactor-core/src/main/java/reactivefeign/utils/Pair.java
  class Pair (line 16) | public class Pair<L, R> {
    method Pair (line 20) | public Pair(L left, R right) {

FILE: feign-reactor-core/src/test/java/reactivefeign/CompressionTest.java
  class CompressionTest (line 39) | abstract public class CompressionTest {
    method builder (line 45) | abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder(R...
    method testCompression (line 47) | @Test

FILE: feign-reactor-core/src/test/java/reactivefeign/ConnectionTimeoutTest.java
  class ConnectionTimeoutTest (line 29) | abstract public class ConnectionTimeoutTest {
    method builder (line 38) | abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder(R...
    method before (line 40) | @Before
    method after (line 52) | @After
    method shouldFailOnConnectionTimeout (line 61) | @Ignore

FILE: feign-reactor-core/src/test/java/reactivefeign/ContractTest.java
  class ContractTest (line 29) | abstract public class ContractTest {
    method builder (line 34) | abstract protected <T> ReactiveFeign.Builder<T> builder();
    method shouldFailOnBrokenContract (line 36) | @Test
    method shouldFailIfNotReactiveContract (line 49) | @Test
    method shouldFailIfMethodOperatesWithByteArray (line 59) | @Test

FILE: feign-reactor-core/src/test/java/reactivefeign/DefaultMethodTest.java
  class DefaultMethodTest (line 37) | abstract public class DefaultMethodTest {
    method resetServers (line 43) | @Before
    method builder (line 48) | abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();
    method builder (line 50) | abstract protected <API> ReactiveFeign.Builder<API> builder(Class<API>...
    method builder (line 52) | abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder(R...
    method shouldProcessDefaultMethodOnProxy (line 54) | @Test
    method shouldNotWrapException (line 73) | @Test(expected = RuntimeException.class)
    method shouldOverrideEquals (line 85) | @Test
    type OtherApi (line 108) | interface OtherApi {
      method method (line 109) | @RequestLine("GET /icecream/flavors")
    method shouldOverrideHashcode (line 113) | @Test
    method shouldOverrideToString (line 125) | @Test

FILE: feign-reactor-core/src/test/java/reactivefeign/LoggerTest.java
  class LoggerTest (line 48) | abstract public class LoggerTest {
    method builder (line 57) | abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();
    method shouldLog (line 61) | @Test
    method assertLogEvent (line 112) | private void assertLogEvent(List<LogEvent> events, int index, Level le...
    method assertLogEvent (line 120) | private void assertLogEvent(List<LogEvent> events, int index, Level le...
    method before (line 129) | @Before
    method setLogLevel (line 137) | private static Level setLogLevel(Level logLevel) {
    method getLoggerConfig (line 147) | private static LoggerConfig getLoggerConfig() {

FILE: feign-reactor-core/src/test/java/reactivefeign/NotFoundTest.java
  class NotFoundTest (line 29) | public abstract class NotFoundTest {
    method builder (line 35) | abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();
    method shouldReturnEmptyMono (line 37) | @Test

FILE: feign-reactor-core/src/test/java/reactivefeign/ReactivityTest.java
  class ReactivityTest (line 35) | abstract public class ReactivityTest {
    method builder (line 46) | abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();
    method shouldRunReactively (line 48) | @Test
    method timeToCompleteReactively (line 79) | public static int timeToCompleteReactively() {

FILE: feign-reactor-core/src/test/java/reactivefeign/ReadTimeoutTest.java
  class ReadTimeoutTest (line 29) | abstract public class ReadTimeoutTest {
    method builder (line 35) | abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder(R...
    method shouldFailOnReadTimeout (line 37) | @Test

FILE: feign-reactor-core/src/test/java/reactivefeign/RequestInterceptorTest.java
  class RequestInterceptorTest (line 36) | abstract public class RequestInterceptorTest {
    method builder (line 42) | abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();
    method shouldInterceptRequestAndSetAuthHeader (line 44) | @Test
    method notAuthorizedException (line 82) | protected Class notAuthorizedException() {

FILE: feign-reactor-core/src/test/java/reactivefeign/RetryingTest.java
  class RetryingTest (line 45) | public abstract class RetryingTest {
    method builder (line 51) | abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();
    method resetServers (line 53) | @Before
    method shouldSuccessOnRetriesMono (line 58) | @Test
    method shouldSuccessOnRetriesFlux (line 80) | @Test
    method shouldSuccessOnRetriesWoRetryAfter (line 102) | @Test
    method mockResponseAfterSeveralAttempts (line 123) | private static void mockResponseAfterSeveralAttempts(WireMockClassRule...
    method shouldFailAsNoMoreRetries (line 145) | @Test
    method shouldFailAsNoMoreRetriesWithBackoff (line 164) | @Test

FILE: feign-reactor-core/src/test/java/reactivefeign/SmokeTest.java
  class SmokeTest (line 41) | abstract public class SmokeTest {
    method resetServers (line 47) | @Before
    method builder (line 52) | abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();
    method setUp (line 63) | @Before
    method testSimpleGet_success (line 71) | @Test
    method testFindOrder_success (line 96) | @Test
    method testFindOrder_empty (line 112) | @Test
    method testMakeOrder_success (line 122) | @Test
    method testPayBill_success (line 140) | @Test

FILE: feign-reactor-core/src/test/java/reactivefeign/StatusHandlerTest.java
  class StatusHandlerTest (line 33) | public abstract class StatusHandlerTest {
    method builder (line 39) | abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();
    method resetServers (line 41) | @Before
    method shouldThrowRetryException (line 46) | @Test
    method shouldThrowOnStatusCode (line 62) | @Test

FILE: feign-reactor-core/src/test/java/reactivefeign/TestUtils.java
  class TestUtils (line 25) | class TestUtils {
    method equalsComparingFieldByFieldRecursively (line 33) | public static <T> Predicate<T> equalsComparingFieldByFieldRecursively(...

FILE: feign-reactor-core/src/test/java/reactivefeign/allfeatures/AllFeaturesApi.java
  type AllFeaturesApi (line 31) | @Headers({ "Accept: application/json" })
    method mirrorParameters (line 34) | @RequestLine("GET /mirrorParameters/{parameterInPathPlaceholder}?param...
    method mirrorParametersNew (line 40) | @RequestLine("GET /mirrorParametersNew?paramInUrl={paramInUrlPlacehold...
    method mirrorHeaders (line 46) | @RequestLine("GET /mirrorHeaders")
    method mirrorBody (line 51) | @RequestLine("POST " + "/mirrorBody")
    method mirrorBodyMap (line 54) | @RequestLine("POST " + "/mirrorBodyMap")
    method mirrorBodyReactive (line 58) | @RequestLine("POST " + "/mirrorBodyReactive")
    method mirrorBodyMapReactive (line 62) | @RequestLine("POST " + "/mirrorBodyMapReactive")
    method mirrorBodyStream (line 66) | @RequestLine("POST " + "/mirrorBodyStream")
    method mirrorIntegerBodyStream (line 71) | @RequestLine("POST " + "/mirrorIntegerBodyStream")
    method mirrorStringBodyStream (line 76) | @RequestLine("POST " + "/mirrorStringBodyStream")
    method empty (line 81) | @RequestLine("GET /empty")
    method mirrorBodyWithDelay (line 85) | @RequestLine("POST " + "/mirrorBodyWithDelay")
    method mirrorStreamingBinaryBodyReactive (line 88) | @RequestLine("POST " + "/mirrorStreamingBinaryBodyReactive")
    method mirrorDefaultBody (line 92) | default Mono<String> mirrorDefaultBody() {
    class TestObject (line 96) | class TestObject {
      method TestObject (line 100) | public TestObject() {
      method TestObject (line 103) | public TestObject(String payload) {

FILE: feign-reactor-core/src/test/java/reactivefeign/allfeatures/AllFeaturesController.java
  class AllFeaturesController (line 32) | @RestController
    method mirrorParameters (line 35) | @GetMapping(path = "/mirrorParameters/{paramInPath}")
    method mirrorParametersNew (line 46) | @GetMapping(path = "/mirrorParametersNew")
    method mirrorHeaders (line 57) | @GetMapping(path = "/mirrorHeaders")
    method mirrorBody (line 65) | @PostMapping(path = "/mirrorBody")
    method mirrorBodyMap (line 71) | @PostMapping(path = "/mirrorBodyMap")
    method mirrorBodyReactive (line 78) | @PostMapping(path = "/mirrorBodyReactive")
    method mirrorBodyMapReactive (line 84) | @PostMapping(path = "/mirrorBodyMapReactive")
    method mirrorBodyStream (line 91) | @PostMapping(path = "/mirrorBodyStream")
    method mirrorIntegerBodyStream (line 98) | @PostMapping(path = "/mirrorIntegerBodyStream")
    method mirrorStringBodyStream (line 105) | @PostMapping(path = "/mirrorStringBodyStream")
    method mirrorBodyWithDelay (line 112) | @PostMapping(path = "/mirrorBodyWithDelay")
    method empty (line 118) | @Override
    method mirrorStreamingBinaryBodyReactive (line 123) | @PostMapping(path = "/mirrorStreamingBinaryBodyReactive")

FILE: feign-reactor-core/src/test/java/reactivefeign/allfeatures/AllFeaturesTest.java
  class AllFeaturesTest (line 68) | @RunWith(SpringRunner.class)
    method builder (line 84) | abstract protected ReactiveFeign.Builder<AllFeaturesApi> builder();
    method setUp (line 86) | @Before
    method shouldReturnAllPassedParameters (line 93) | @Test
    method shouldReturnAllPassedParametersNew (line 107) | @Test
    method shouldReturnAllPassedHeaders (line 122) | @Test
    method shouldReturnBody (line 137) | @Test
    method shouldReturnBodyMap (line 144) | @Test
    method shouldReturnBodyReactive (line 157) | @Test
    method shouldReturnBodyMapReactive (line 163) | @Test
    method shouldReturnFirstResultBeforeSecondSent (line 178) | @Test
    method shouldReturnEmpty (line 205) | @Test
    method shouldReturnDefaultBody (line 211) | @Test
    method shouldRunReactively (line 218) | @Test
    method shouldMirrorIntegerStreamBody (line 233) | @Test
    method shouldMirrorStringStreamBody (line 246) | @Test
    method shouldMirrorStreamingBinaryBodyReactive (line 258) | @Test
    method fromByteArray (line 290) | private static ByteBuffer fromByteArray(byte[] data){
    class TestConfiguration (line 294) | @Configuration
      method reactiveWebServerFactory (line 297) | @Bean

FILE: feign-reactor-core/src/test/java/reactivefeign/resttemplate/CompressionTest.java
  class CompressionTest (line 24) | public class CompressionTest extends reactivefeign.CompressionTest {
    method builder (line 26) | @Override

FILE: feign-reactor-core/src/test/java/reactivefeign/resttemplate/ConnectionTimeoutTest.java
  class ConnectionTimeoutTest (line 24) | public class ConnectionTimeoutTest extends reactivefeign.ConnectionTimeo...
    method builder (line 26) | @Override

FILE: feign-reactor-core/src/test/java/reactivefeign/resttemplate/ContractTest.java
  class ContractTest (line 22) | public class ContractTest extends reactivefeign.ContractTest {
    method builder (line 24) | @Override

FILE: feign-reactor-core/src/test/java/reactivefeign/resttemplate/DefaultMethodTest.java
  class DefaultMethodTest (line 24) | public class DefaultMethodTest extends reactivefeign.DefaultMethodTest {
    method builder (line 26) | @Override
    method builder (line 31) | @Override
    method builder (line 36) | @Override

FILE: feign-reactor-core/src/test/java/reactivefeign/resttemplate/LoggerTest.java
  class LoggerTest (line 23) | public class LoggerTest extends reactivefeign.LoggerTest {
    method builder (line 25) | @Override

FILE: feign-reactor-core/src/test/java/reactivefeign/resttemplate/NotFoundTest.java
  class NotFoundTest (line 23) | public class NotFoundTest extends reactivefeign.NotFoundTest {
    method builder (line 25) | @Override

FILE: feign-reactor-core/src/test/java/reactivefeign/resttemplate/ReactivityTest.java
  class ReactivityTest (line 24) | public class ReactivityTest extends reactivefeign.ReactivityTest {
    method builder (line 26) | @Override
    method shouldRunReactively (line 31) | @Test(expected = ConditionTimeoutException.class)

FILE: feign-reactor-core/src/test/java/reactivefeign/resttemplate/ReadTimeoutTest.java
  class ReadTimeoutTest (line 24) | public class ReadTimeoutTest extends reactivefeign.ReadTimeoutTest {
    method builder (line 26) | @Override

FILE: feign-reactor-core/src/test/java/reactivefeign/resttemplate/RequestInterceptorTest.java
  class RequestInterceptorTest (line 23) | public class RequestInterceptorTest extends reactivefeign.RequestInterce...
    method builder (line 25) | @Override

FILE: feign-reactor-core/src/test/java/reactivefeign/resttemplate/RetryingTest.java
  class RetryingTest (line 23) | public class RetryingTest extends reactivefeign.RetryingTest {
    method builder (line 25) | @Override

FILE: feign-reactor-core/src/test/java/reactivefeign/resttemplate/SmokeTest.java
  class SmokeTest (line 23) | public class SmokeTest extends reactivefeign.SmokeTest {
    method builder (line 25) | @Override

FILE: feign-reactor-core/src/test/java/reactivefeign/resttemplate/StatusHandlerTest.java
  class StatusHandlerTest (line 23) | public class StatusHandlerTest extends reactivefeign.StatusHandlerTest {
    method builder (line 25) | @Override

FILE: feign-reactor-core/src/test/java/reactivefeign/resttemplate/client/RestTemplateFakeReactiveFeign.java
  class RestTemplateFakeReactiveFeign (line 29) | public class RestTemplateFakeReactiveFeign {
    method builder (line 31) | public static <T> ReactiveFeign.Builder<T> builder() {

FILE: feign-reactor-core/src/test/java/reactivefeign/resttemplate/client/RestTemplateFakeReactiveHttpClient.java
  class RestTemplateFakeReactiveHttpClient (line 46) | public class RestTemplateFakeReactiveHttpClient implements ReactiveHttpC...
    method RestTemplateFakeReactiveHttpClient (line 53) | RestTemplateFakeReactiveHttpClient(MethodMetadata methodMetadata,
    method executeRequest (line 73) | @Override
    method responseType (line 105) | private ParameterizedTypeReference<Object> responseType(){
    class FakeReactiveHttpResponse (line 128) | private static class FakeReactiveHttpResponse implements ReactiveHttpR...
      method FakeReactiveHttpResponse (line 133) | private FakeReactiveHttpResponse(ResponseEntity response, Type retur...
      method status (line 138) | @Override
      method headers (line 143) | @Override
      method body (line 148) | @Override
      method bodyData (line 157) | @Override
    class ErrorReactiveHttpResponse (line 163) | private static class ErrorReactiveHttpResponse implements ReactiveHttp...
      method ErrorReactiveHttpResponse (line 167) | private ErrorReactiveHttpResponse(HttpStatusCodeException ex) {
      method status (line 171) | @Override
      method headers (line 176) | @Override
      method body (line 181) | @Override
      method bodyData (line 186) | @Override

FILE: feign-reactor-core/src/test/java/reactivefeign/testcase/IcecreamServiceApi.java
  type IcecreamServiceApi (line 31) | @Headers({"Accept: application/json"})
    method getAvailableFlavors (line 36) | @RequestLine("GET /icecream/flavors")
    method getAvailableMixins (line 39) | @RequestLine("GET /icecream/mixins")
    method makeOrder (line 42) | @RequestLine("POST /icecream/orders")
    method findOrder (line 46) | @RequestLine("GET /icecream/orders/{orderId}")
    method payBill (line 49) | @RequestLine("POST /icecream/bills/pay")
    method findFirstOrder (line 53) | default Mono<IceCreamOrder> findFirstOrder() {
    method throwsException (line 57) | default Mono<IceCreamOrder> throwsException() {

FILE: feign-reactor-core/src/test/java/reactivefeign/testcase/IcecreamServiceApiBroken.java
  type IcecreamServiceApiBroken (line 35) | public interface IcecreamServiceApiBroken {
    method findOrder (line 37) | @RequestLine("GET /icecream/orders/{orderId}")
    method findOrderBlocking (line 43) | @RequestLine("GET /icecream/orders/{orderId}")

FILE: feign-reactor-core/src/test/java/reactivefeign/testcase/IcecreamServiceApiBrokenByCopy.java
  type IcecreamServiceApiBrokenByCopy (line 36) | public interface IcecreamServiceApiBrokenByCopy {
    method findOrder (line 38) | @RequestLine("GET /icecream/orders/{orderId}")
    method findOrderCopy (line 44) | @RequestLine("GET /icecream/orders/{orderId}")

FILE: feign-reactor-core/src/test/java/reactivefeign/testcase/domain/Bill.java
  class Bill (line 22) | public class Bill {
    method Bill (line 36) | public Bill() {}
    method Bill (line 38) | public Bill(final Float price) {
    method getPrice (line 42) | public Float getPrice() {
    method setPrice (line 46) | public void setPrice(final Float price) {
    method makeBill (line 56) | public static Bill makeBill(final IceCreamOrder order) {

FILE: feign-reactor-core/src/test/java/reactivefeign/testcase/domain/Flavor.java
  type Flavor (line 19) | public enum Flavor {

FILE: feign-reactor-core/src/test/java/reactivefeign/testcase/domain/IceCreamOrder.java
  class IceCreamOrder (line 22) | public class IceCreamOrder {
    method IceCreamOrder (line 30) | IceCreamOrder() {}
    method IceCreamOrder (line 32) | IceCreamOrder(int id) {
    method IceCreamOrder (line 36) | IceCreamOrder(int id, final Instant orderTimestamp) {
    method addBall (line 43) | IceCreamOrder addBall(final Flavor ballFlavor) {
    method addMixin (line 51) | IceCreamOrder addMixin(final Mixin mixin) {
    method withOrderTimestamp (line 56) | IceCreamOrder withOrderTimestamp(final Instant orderTimestamp) {
    method getId (line 61) | public int getId() {
    method getBalls (line 65) | public Map<Flavor, Integer> getBalls() {
    method getMixins (line 69) | public Set<Mixin> getMixins() {
    method getOrderTimestamp (line 73) | public Instant getOrderTimestamp() {
    method toString (line 77) | @Override

FILE: feign-reactor-core/src/test/java/reactivefeign/testcase/domain/Mixin.java
  type Mixin (line 19) | public enum Mixin {

FILE: feign-reactor-core/src/test/java/reactivefeign/testcase/domain/OrderGenerator.java
  class OrderGenerator (line 27) | public class OrderGenerator {
    method generate (line 33) | public IceCreamOrder generate(int id) {
    method generateRange (line 47) | public Collection<IceCreamOrder> generateRange(int n) {
    method peekBallsNumber (line 60) | private int peekBallsNumber() {
    method peekMixinNumber (line 64) | private int peekMixinNumber() {
    method peekFlavor (line 68) | private Flavor peekFlavor() {
    method peekMixin (line 72) | private Mixin peekMixin() {

FILE: feign-reactor-jetty/src/main/java/reactivefeign/jetty/JettyReactiveFeign.java
  class JettyReactiveFeign (line 29) | public class JettyReactiveFeign {
    method builder (line 31) | public static <T> Builder<T> builder() {
    method builder (line 44) | public static <T> Builder<T> builder(HttpClient httpClient, JsonFactor...
    class Builder (line 48) | public static class Builder<T> extends ReactiveFeign.Builder<T> {
      method Builder (line 55) | protected Builder(HttpClient httpClient, JsonFactory jsonFactory, Ob...
      method options (line 61) | @Override
      method setHttpClient (line 73) | protected void setHttpClient(HttpClient httpClient, JsonFactory json...

FILE: feign-reactor-jetty/src/main/java/reactivefeign/jetty/client/JettyReactiveHttpClient.java
  class JettyReactiveHttpClient (line 57) | public class JettyReactiveHttpClient implements ReactiveHttpClient {
    method jettyClient (line 80) | public static JettyReactiveHttpClient jettyClient(
    method JettyReactiveHttpClient (line 97) | public JettyReactiveHttpClient(HttpClient httpClient,
    method setRequestTimeout (line 109) | public JettyReactiveHttpClient setRequestTimeout(long timeoutInMillis){
    method executeRequest (line 114) | @Override
    method setUpHeaders (line 147) | protected void setUpHeaders(ReactiveHttpRequest request, HttpFields ht...
    method provideBody (line 166) | protected ReactiveRequest.Content provideBody(ReactiveHttpRequest requ...
    method toByteBufferChunk (line 205) | protected ContentChunk toByteBufferChunk(Object data){
    method toByteArrayChunk (line 209) | protected ContentChunk toByteArrayChunk(Object data){
    method toCharSequenceChunk (line 213) | protected ContentChunk toCharSequenceChunk(Object data){
    method toJsonChunk (line 219) | protected ContentChunk toJsonChunk(Object data, boolean stream){
    method getClass (line 233) | public static Class getClass(Type type){

FILE: feign-reactor-jetty/src/main/java/reactivefeign/jetty/client/JettyReactiveHttpResponse.java
  class JettyReactiveHttpResponse (line 26) | class JettyReactiveHttpResponse implements ReactiveHttpResponse{
    method JettyReactiveHttpResponse (line 36) | JettyReactiveHttpResponse(Response clientResponse, Publisher<ContentCh...
    method status (line 47) | @Override
    method headers (line 52) | @Override
    method body (line 58) | @Override
    method getCharset (line 81) | private Charset getCharset() {
    method directContent (line 95) | private Flux<ByteBuffer> directContent() {
    method bodyData (line 99) | @Override
    method joinChunks (line 104) | private Mono<byte[]> joinChunks() {

FILE: feign-reactor-jetty/src/main/java/reactivefeign/jetty/utils/ProxyPostProcessor.java
  class ProxyPostProcessor (line 9) | public class ProxyPostProcessor<I> extends AbstractSingleProcessor<I, I>{
    method ProxyPostProcessor (line 14) | private ProxyPostProcessor(Publisher<I> publisher, BiConsumer<I, Throw...
    method onNext (line 19) | @Override
    method subscribe (line 29) | @Override
    method postProcess (line 35) | public static <I> Publisher<I> postProcess(Publisher<I> publisher, BiC...

FILE: feign-reactor-jetty/src/test/java/reactivefeign/jetty/CompressionTest.java
  class CompressionTest (line 23) | public class CompressionTest extends reactivefeign.CompressionTest {
    method builder (line 25) | @Override

FILE: feign-reactor-jetty/src/test/java/reactivefeign/jetty/ConnectionTimeoutTest.java
  class ConnectionTimeoutTest (line 23) | public class ConnectionTimeoutTest extends reactivefeign.ConnectionTimeo...
    method builder (line 25) | @Override

FILE: feign-reactor-jetty/src/test/java/reactivefeign/jetty/ContractTest.java
  class ContractTest (line 21) | public class ContractTest extends reactivefeign.ContractTest {
    method builder (line 23) | @Override

FILE: feign-reactor-jetty/src/test/java/reactivefeign/jetty/DefaultMethodTest.java
  class DefaultMethodTest (line 23) | public class DefaultMethodTest extends reactivefeign.DefaultMethodTest {
    method builder (line 25) | @Override
    method builder (line 30) | @Override
    method builder (line 35) | @Override

FILE: feign-reactor-jetty/src/test/java/reactivefeign/jetty/LoggerTest.java
  class LoggerTest (line 22) | public class LoggerTest extends reactivefeign.LoggerTest {
    method builder (line 24) | @Override

FILE: feign-reactor-jetty/src/test/java/reactivefeign/jetty/NotFoundTest.java
  class NotFoundTest (line 25) | public class NotFoundTest extends reactivefeign.NotFoundTest {
    method builder (line 27) | @Override

FILE: feign-reactor-jetty/src/test/java/reactivefeign/jetty/ReactivityTest.java
  class ReactivityTest (line 20) | public class ReactivityTest extends reactivefeign.ReactivityTest {
    method builder (line 22) | @Override
    method shouldRunReactively (line 27) | @Override

FILE: feign-reactor-jetty/src/test/java/reactivefeign/jetty/ReadTimeoutTest.java
  class ReadTimeoutTest (line 23) | public class ReadTimeoutTest extends reactivefeign.ReadTimeoutTest {
    method builder (line 25) | @Override

FILE: feign-reactor-jetty/src/test/java/reactivefeign/jetty/RequestInterceptorTest.java
  class RequestInterceptorTest (line 22) | public class RequestInterceptorTest extends reactivefeign.RequestInterce...
    method builder (line 24) | @Override
    method notAuthorizedException (line 29) | @Override

FILE: feign-reactor-jetty/src/test/java/reactivefeign/jetty/RetryingTest.java
  class RetryingTest (line 24) | public class RetryingTest extends reactivefeign.RetryingTest {
    method builder (line 26) | @Override
    method shouldFailAsNoMoreRetriesWithBackoff (line 31) | @Test

FILE: feign-reactor-jetty/src/test/java/reactivefeign/jetty/SmokeTest.java
  class SmokeTest (line 22) | public class SmokeTest extends reactivefeign.SmokeTest {
    method builder (line 24) | @Override

FILE: feign-reactor-jetty/src/test/java/reactivefeign/jetty/StatusHandlerTest.java
  class StatusHandlerTest (line 22) | public class StatusHandlerTest extends reactivefeign.StatusHandlerTest {
    method builder (line 24) | @Override

FILE: feign-reactor-jetty/src/test/java/reactivefeign/jetty/allfeatures/AllFeaturesTest.java
  class AllFeaturesTest (line 29) | @EnableAutoConfiguration(exclude = {org.springframework.boot.autoconfigu...
    method builder (line 32) | @Override

FILE: feign-reactor-rx2/src/main/java/reactivefeign/rx2/Rx2Contract.java
  class Rx2Contract (line 39) | public class Rx2Contract implements Contract {
    method Rx2Contract (line 46) | public Rx2Contract(final Contract delegate) {
    method parseAndValidatateMetadata (line 50) | @Override
    method isRx2Type (line 67) | private boolean isRx2Type(final Type type) {

FILE: feign-reactor-rx2/src/main/java/reactivefeign/rx2/Rx2ReactiveFeign.java
  class Rx2ReactiveFeign (line 39) | public class Rx2ReactiveFeign extends ReactiveFeign {
    method Rx2ReactiveFeign (line 41) | private Rx2ReactiveFeign(ReactiveFeign.ParseHandlersByName targetToHan...
    method builder (line 46) | public static <T> Builder<T> builder() {
    method builder (line 50) | public static <T> Builder<T> builder(WebClient webClient) {
    class Builder (line 54) | public static class Builder<T> extends WebReactiveFeign.Builder<T> {
      method Builder (line 58) | protected Builder() {
      method Builder (line 62) | protected Builder(WebClient webClient) {
      method setBackpressureStrategy (line 70) | public void setBackpressureStrategy(BackpressureStrategy backpressur...
      method buildReactiveMethodHandlerFactory (line 74) | @Override
      method contract (line 80) | @Override
      method addHeaders (line 86) | @Override
      method requestInterceptor (line 92) | @Override
      method decode404 (line 98) | @Override
      method statusHandler (line 104) | public Builder<T> statusHandler(Rx2StatusHandler statusHandler) {
      method retryWhen (line 109) | @Override
      method options (line 115) | @Override
      method toPublisher (line 121) | protected PublisherHttpClient toPublisher(ReactiveHttpClient reactiv...
      method setWebClient (line 132) | @Override
      method webClient (line 138) | public static WebReactiveHttpClient webClient(MethodMetadata methodM...
      method rx2ToReactor (line 153) | private static Class rx2ToReactor(Type type){

FILE: feign-reactor-rx2/src/main/java/reactivefeign/rx2/client/statushandler/Rx2ReactiveStatusHandler.java
  class Rx2ReactiveStatusHandler (line 9) | public class Rx2ReactiveStatusHandler implements ReactiveStatusHandler {
    method Rx2ReactiveStatusHandler (line 13) | public Rx2ReactiveStatusHandler(Rx2StatusHandler statusHandler) {
    method shouldHandle (line 17) | @Override
    method decode (line 22) | @Override

FILE: feign-reactor-rx2/src/main/java/reactivefeign/rx2/client/statushandler/Rx2StatusHandler.java
  type Rx2StatusHandler (line 22) | public interface Rx2StatusHandler {
    method shouldHandle (line 24) | boolean shouldHandle(int status);
    method decode (line 26) | Single<? extends Throwable> decode(String methodKey, ReactiveHttpRespo...

FILE: feign-reactor-rx2/src/main/java/reactivefeign/rx2/client/statushandler/Rx2StatusHandlers.java
  class Rx2StatusHandlers (line 22) | public class Rx2StatusHandlers {
    method throwOnStatus (line 25) | public static Rx2StatusHandler throwOnStatus(

FILE: feign-reactor-rx2/src/main/java/reactivefeign/rx2/methodhandler/Rx2MethodHandler.java
  class Rx2MethodHandler (line 15) | public class Rx2MethodHandler implements MethodHandler {
    method Rx2MethodHandler (line 20) | public Rx2MethodHandler(MethodHandler methodHandler, Type returnPublis...
    method invoke (line 25) | @Override

FILE: feign-reactor-rx2/src/main/java/reactivefeign/rx2/methodhandler/Rx2MethodHandlerFactory.java
  class Rx2MethodHandlerFactory (line 15) | public class Rx2MethodHandlerFactory implements MethodHandlerFactory {
    method Rx2MethodHandlerFactory (line 20) | public Rx2MethodHandlerFactory(PublisherClientFactory publisherClientF...
    method create (line 26) | @Override
    method createDefault (line 35) | @Override

FILE: feign-reactor-rx2/src/main/java/reactivefeign/rx2/methodhandler/Rx2PublisherClientMethodHandler.java
  class Rx2PublisherClientMethodHandler (line 13) | public class Rx2PublisherClientMethodHandler extends PublisherClientMeth...
    method Rx2PublisherClientMethodHandler (line 17) | public Rx2PublisherClientMethodHandler(
    method body (line 24) | @Override

FILE: feign-reactor-rx2/src/test/java/reactivefeign/rx2/ContractTest.java
  class ContractTest (line 29) | public class ContractTest {
    method builder (line 34) | protected <T> ReactiveFeign.Builder<T> builder(){
    method shouldFailOnBrokenContract (line 38) | @Test
    method shouldFailIfNotReactiveContract (line 51) | @Test

FILE: feign-reactor-rx2/src/test/java/reactivefeign/rx2/DefaultMethodTest.java
  class DefaultMethodTest (line 37) | public class DefaultMethodTest {
    method resetServers (line 43) | @Before
    method builder (line 48) | protected ReactiveFeign.Builder<IcecreamServiceApi> builder(){
    method builder (line 52) | protected <API> ReactiveFeign.Builder<API> builder(Class<API> apiClass){
    method builder (line 56) | protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOp...
    method shouldProcessDefaultMethodOnProxy (line 60) | @Test
    method shouldNotWrapException (line 82) | @Test(expected = RuntimeException.class)
    method shouldOverrideEquals (line 93) | @Test
    type OtherApi (line 116) | interface OtherApi {
      method method (line 117) | @RequestLine("GET /icecream/flavors")
    method shouldOverrideHashcode (line 121) | @Test
    method shouldOverrideToString (line 133) | @Test

FILE: feign-reactor-rx2/src/test/java/reactivefeign/rx2/LoggerTest.java
  class LoggerTest (line 49) | public class LoggerTest {
    method builder (line 58) | protected ReactiveFeign.Builder<IcecreamServiceApi> builder(){
    method shouldLog (line 64) | @Test
    method assertLogEvent (line 113) | private void assertLogEvent(List<LogEvent> events, int index, Level le...
    method before (line 121) | @Before
    method setLogLevel (line 129) | private static void setLogLevel(Level logLevel) {
    method getLoggerConfig (line 136) | private static LoggerConfig getLoggerConfig() {

FILE: feign-reactor-rx2/src/test/java/reactivefeign/rx2/NotFoundTest.java
  class NotFoundTest (line 29) | public class NotFoundTest {
    method builder (line 35) | protected ReactiveFeign.Builder<IcecreamServiceApi> builder(){
    method shouldReturnEmptyMono (line 39) | @Test

FILE: feign-reactor-rx2/src/test/java/reactivefeign/rx2/ReactivityTest.java
  class ReactivityTest (line 36) | public class ReactivityTest {
    method builder (line 47) | protected ReactiveFeign.Builder<IcecreamServiceApi> builder(){
    method shouldRunReactively (line 51) | @Test

FILE: feign-reactor-rx2/src/test/java/reactivefeign/rx2/ReadTimeoutTest.java
  class ReadTimeoutTest (line 30) | public class ReadTimeoutTest {
    method builder (line 36) | protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOp...
    method shouldFailOnReadTimeout (line 40) | @Test

FILE: feign-reactor-rx2/src/test/java/reactivefeign/rx2/RequestInterceptorTest.java
  class RequestInterceptorTest (line 36) | public class RequestInterceptorTest {
    method builder (line 42) | protected ReactiveFeign.Builder<IcecreamServiceApi> builder(){
    method shouldInterceptRequestAndSetAuthHeader (line 46) | @Test

FILE: feign-reactor-rx2/src/test/java/reactivefeign/rx2/SmokeTest.java
  class SmokeTest (line 41) | public class SmokeTest {
    method resetServers (line 47) | @Before
    method builder (line 52) | protected ReactiveFeign.Builder<IcecreamServiceApi> builder(){
    method setUp (line 65) | @Before
    method testSimpleGet_success (line 73) | @Test
    method testFindOrder_success (line 95) | @Test
    method testFindOrder_empty (line 111) | @Test
    method testMakeOrder_success (line 122) | @Test
    method testPayBill_success (line 139) | @Test

FILE: feign-reactor-rx2/src/test/java/reactivefeign/rx2/StatusHandlerTest.java
  class StatusHandlerTest (line 31) | public class StatusHandlerTest {
    method builder (line 37) | protected Rx2ReactiveFeign.Builder<IcecreamServiceApi> builder(){
    method resetServers (line 41) | @Before
    method shouldThrowRetryException (line 46) | @Test
    method shouldThrowOnStatusCode (line 63) | @Test

FILE: feign-reactor-rx2/src/test/java/reactivefeign/rx2/TestUtils.java
  class TestUtils (line 26) | class TestUtils {
    method equalsComparingFieldByFieldRecursively (line 34) | public static <T> Predicate<T> equalsComparingFieldByFieldRecursively(...
    method equalsComparingFieldByFieldRecursivelyRx (line 44) | public static <T> io.reactivex.functions.Predicate<T> equalsComparingF...

FILE: feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/IcecreamServiceApi.java
  type IcecreamServiceApi (line 33) | @Headers({"Accept: application/json"})
    method getAvailableFlavors (line 38) | @RequestLine("GET /icecream/flavors")
    method getAvailableMixins (line 41) | @RequestLine("GET /icecream/mixins")
    method makeOrder (line 44) | @RequestLine("POST /icecream/orders")
    method findOrder (line 48) | @RequestLine("GET /icecream/orders/{orderId}")
    method payBill (line 51) | @RequestLine("POST /icecream/bills/pay")
    method findFirstOrder (line 55) | default Maybe<IceCreamOrder> findFirstOrder() {
    method throwsException (line 59) | default Single<IceCreamOrder> throwsException() {

FILE: feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/IcecreamServiceApiBroken.java
  type IcecreamServiceApiBroken (line 37) | public interface IcecreamServiceApiBroken {
    method getAvailableFlavors (line 39) | @RequestLine("GET /icecream/flavors")
    method getAvailableMixins (line 42) | @RequestLine("GET /icecream/mixins")
    method findOrder (line 48) | @RequestLine("GET /icecream/orders/{orderId}")

FILE: feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/domain/Bill.java
  class Bill (line 22) | public class Bill {
    method Bill (line 36) | public Bill() {}
    method Bill (line 38) | public Bill(final Float price) {
    method getPrice (line 42) | public Float getPrice() {
    method setPrice (line 46) | public void setPrice(final Float price) {
    method makeBill (line 56) | public static Bill makeBill(final IceCreamOrder order) {

FILE: feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/domain/Flavor.java
  type Flavor (line 19) | public enum Flavor {

FILE: feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/domain/IceCreamOrder.java
  class IceCreamOrder (line 22) | public class IceCreamOrder {
    method IceCreamOrder (line 30) | IceCreamOrder() {}
    method IceCreamOrder (line 32) | IceCreamOrder(int id) {
    method IceCreamOrder (line 36) | IceCreamOrder(int id, final Instant orderTimestamp) {
    method addBall (line 43) | IceCreamOrder addBall(final Flavor ballFlavor) {
    method addMixin (line 51) | IceCreamOrder addMixin(final Mixin mixin) {
    method withOrderTimestamp (line 56) | IceCreamOrder withOrderTimestamp(final Instant orderTimestamp) {
    method getId (line 61) | public int getId() {
    method getBalls (line 65) | public Map<Flavor, Integer> getBalls() {
    method getMixins (line 69) | public Set<Mixin> getMixins() {
    method getOrderTimestamp (line 73) | public Instant getOrderTimestamp() {
    method toString (line 77) | @Override

FILE: feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/domain/Mixin.java
  type Mixin (line 19) | public enum Mixin {

FILE: feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/domain/OrderGenerator.java
  class OrderGenerator (line 27) | public class OrderGenerator {
    method generate (line 33) | public IceCreamOrder generate(int id) {
    method generateRange (line 47) | public Collection<IceCreamOrder> generateRange(int n) {
    method peekBallsNumber (line 60) | private int peekBallsNumber() {
    method peekMixinNumber (line 64) | private int peekMixinNumber() {
    method peekFlavor (line 68) | private Flavor peekFlavor() {
    method peekMixin (line 72) | private Mixin peekMixin() {

FILE: feign-reactor-webclient/src/main/java/reactivefeign/webclient/WebReactiveFeign.java
  class WebReactiveFeign (line 32) | public class WebReactiveFeign {
    method builder (line 37) | public static <T> Builder<T> builder() {
    method builder (line 41) | public static <T> Builder<T> builder(WebClient webClient) {
    class Builder (line 45) | public static class Builder<T> extends ReactiveFeign.Builder<T> {
      method Builder (line 49) | protected Builder() {
      method Builder (line 53) | protected Builder(WebClient webClient) {
      method options (line 61) | @Override
      method setWebClient (line 88) | protected void setWebClient(WebClient webClient){

FILE: feign-reactor-webclient/src/main/java/reactivefeign/webclient/client/WebReactiveHttpClient.java
  class WebReactiveHttpClient (line 44) | public class WebReactiveHttpClient implements ReactiveHttpClient {
    method webClient (line 51) | public static WebReactiveHttpClient webClient(MethodMetadata methodMet...
    method WebReactiveHttpClient (line 67) | public WebReactiveHttpClient(WebClient webClient,
    method executeRequest (line 76) | @Override
    method provideBody (line 88) | protected BodyInserter<?, ? super ClientHttpRequest> provideBody(React...
    method setUpHeaders (line 94) | protected void setUpHeaders(ReactiveHttpRequest request, HttpHeaders h...

FILE: feign-reactor-webclient/src/main/java/reactivefeign/webclient/client/WebReactiveHttpResponse.java
  class WebReactiveHttpResponse (line 15) | class WebReactiveHttpResponse implements ReactiveHttpResponse{
    method WebReactiveHttpResponse (line 21) | WebReactiveHttpResponse(ClientResponse clientResponse,
    method status (line 28) | @Override
    method headers (line 33) | @Override
    method body (line 38) | @Override
    method bodyData (line 49) | @Override

FILE: feign-reactor-webclient/src/test/java/reactivefeign/webclient/CompressionTest.java
  class CompressionTest (line 23) | public class CompressionTest extends reactivefeign.CompressionTest {
    method builder (line 25) | @Override

FILE: feign-reactor-webclient/src/test/java/reactivefeign/webclient/ConnectionTimeoutTest.java
  class ConnectionTimeoutTest (line 23) | public class ConnectionTimeoutTest extends reactivefeign.ConnectionTimeo...
    method builder (line 25) | @Override

FILE: feign-reactor-webclient/src/test/java/reactivefeign/webclient/ContractTest.java
  class ContractTest (line 21) | public class ContractTest extends reactivefeign.ContractTest {
    method builder (line 23) | @Override

FILE: feign-reactor-webclient/src/test/java/reactivefeign/webclient/DefaultMethodTest.java
  class DefaultMethodTest (line 23) | public class DefaultMethodTest extends reactivefeign.DefaultMethodTest {
    method builder (line 25) | @Override
    method builder (line 30) | @Override
    method builder (line 35) | @Override

FILE: feign-reactor-webclient/src/test/java/reactivefeign/webclient/LoggerTest.java
  class LoggerTest (line 22) | public class LoggerTest extends reactivefeign.LoggerTest {
    method builder (line 24) | @Override

FILE: feign-reactor-webclient/src/test/java/reactivefeign/webclient/NotFoundTest.java
  class NotFoundTest (line 22) | public class NotFoundTest extends reactivefeign.NotFoundTest {
    method builder (line 24) | @Override

FILE: feign-reactor-webclient/src/test/java/reactivefeign/webclient/ReactivityTest.java
  class ReactivityTest (line 20) | public class ReactivityTest extends reactivefeign.ReactivityTest {
    method builder (line 22) | @Override
    method shouldRunReactively (line 27) | @Override

FILE: feign-reactor-webclient/src/test/java/reactivefeign/webclient/ReadTimeoutTest.java
  class ReadTimeoutTest (line 23) | public class ReadTimeoutTest extends reactivefeign.ReadTimeoutTest {
    method builder (line 25) | @Override

FILE: feign-reactor-webclient/src/test/java/reactivefeign/webclient/RequestInterceptorTest.java
  class RequestInterceptorTest (line 22) | public class RequestInterceptorTest extends reactivefeign.RequestInterce...
    method builder (line 24) | @Override

FILE: feign-reactor-webclient/src/test/java/reactivefeign/webclient/RetryingTest.java
  class RetryingTest (line 22) | public class RetryingTest extends reactivefeign.RetryingTest {
    method builder (line 24) | @Override

FILE: feign-reactor-webclient/src/test/java/reactivefeign/webclient/SmokeTest.java
  class SmokeTest (line 22) | public class SmokeTest extends reactivefeign.SmokeTest {
    method builder (line 24) | @Override

FILE: feign-reactor-webclient/src/test/java/reactivefeign/webclient/StatusHandlerTest.java
  class StatusHandlerTest (line 22) | public class StatusHandlerTest extends reactivefeign.StatusHandlerTest {
    method builder (line 24) | @Override

FILE: feign-reactor-webclient/src/test/java/reactivefeign/webclient/allfeatures/AllFeaturesTest.java
  class AllFeaturesTest (line 36) | @EnableAutoConfiguration(exclude = {ReactiveSecurityAutoConfiguration.cl...
    method builder (line 39) | @Override
    method shouldReturnFirstResultBeforeSecondSent (line 45) | @Ignore
    method shouldMirrorStringStreamBody (line 52) | @Ignore

FILE: feign-reactor-webclient/src/test/java/reactivefeign/webclient/allfeatures/WebClientFeaturesApi.java
  type WebClientFeaturesApi (line 14) | public interface WebClientFeaturesApi {
    method mirrorStreamingBinaryBodyReactive (line 16) | @RequestLine("POST " + "/mirrorStreamingBinaryBodyReactive")
    method mirrorResourceReactiveWithZeroCopying (line 20) | @RequestLine("POST " + "/mirrorResourceReactiveWithZeroCopying")

FILE: feign-reactor-webclient/src/test/java/reactivefeign/webclient/allfeatures/WebClientFeaturesController.java
  class WebClientFeaturesController (line 13) | @RestController
    method mirrorStreamingBinaryBodyReactive (line 16) | @PostMapping(path = "/mirrorStreamingBinaryBodyReactive")
    method mirrorResourceReactiveWithZeroCopying (line 22) | @PostMapping(path = "/mirrorResourceReactiveWithZeroCopying")

FILE: feign-reactor-webclient/src/test/java/reactivefeign/webclient/allfeatures/WebClientFeaturesTest.java
  class WebClientFeaturesTest (line 25) | @RunWith(SpringRunner.class)
    method setUp (line 41) | @Before
    method shouldMirrorStreamingBinaryBodyReactive (line 48) | @Test
    method fromByteArray (line 62) | private static DataBuffer fromByteArray(byte[] data){
    method shouldMirrorResourceReactiveWithZeroCopying (line 66) | @Test
Condensed preview — 154 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (410K chars).
[
  {
    "path": ".gitignore",
    "chars": 249,
    "preview": "target/\n!.mvn/wrapper/maven-wrapper.jar\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBe"
  },
  {
    "path": "LICENSE",
    "chars": 11357,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "README.md",
    "chars": 5758,
    "preview": "Happy to announce that from now Java Reactive Feign client is officially backed by Playtika. All development will be con"
  },
  {
    "path": "feign-reactor-cloud/pom.xml",
    "chars": 2949,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2"
  },
  {
    "path": "feign-reactor-cloud/src/main/java/reactivefeign/cloud/CloudReactiveFeign.java",
    "chars": 7652,
    "preview": "package reactivefeign.cloud;\n\nimport com.netflix.client.ClientFactory;\nimport com.netflix.client.RetryHandler;\nimport co"
  },
  {
    "path": "feign-reactor-cloud/src/main/java/reactivefeign/cloud/methodhandler/HystrixMethodHandler.java",
    "chars": 4014,
    "preview": "package reactivefeign.cloud.methodhandler;\n\nimport com.netflix.hystrix.HystrixObservableCommand;\nimport feign.MethodMeta"
  },
  {
    "path": "feign-reactor-cloud/src/main/java/reactivefeign/cloud/methodhandler/HystrixMethodHandlerFactory.java",
    "chars": 1692,
    "preview": "package reactivefeign.cloud.methodhandler;\n\nimport feign.MethodMetadata;\nimport feign.Target;\nimport org.springframework"
  },
  {
    "path": "feign-reactor-cloud/src/main/java/reactivefeign/cloud/publisher/RibbonPublisherClient.java",
    "chars": 2684,
    "preview": "package reactivefeign.cloud.publisher;\n\nimport com.netflix.loadbalancer.Server;\nimport com.netflix.loadbalancer.reactive"
  },
  {
    "path": "feign-reactor-cloud/src/test/java/reactivefeign/cloud/HystrixReactiveHttpClientTest.java",
    "chars": 7075,
    "preview": "package reactivefeign.cloud;\n\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport com.netflix.hystrix"
  },
  {
    "path": "feign-reactor-cloud/src/test/java/reactivefeign/cloud/LoadBalancingReactiveHttpClientTest.java",
    "chars": 8003,
    "preview": "package reactivefeign.cloud;\n\nimport com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;\nimport com.github"
  },
  {
    "path": "feign-reactor-core/pom.xml",
    "chars": 4632,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n\n    Copyright 2018 The Feign Authors\n\n    Licensed under the Apache License"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/ReactiveContract.java",
    "chars": 2538,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/ReactiveFeign.java",
    "chars": 11954,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/ReactiveInvocationHandler.java",
    "chars": 3267,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/ReactiveOptions.java",
    "chars": 2214,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/ReactiveRetryPolicy.java",
    "chars": 1627,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/ReactiveRetryers.java",
    "chars": 1552,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/DelegatingReactiveHttpResponse.java",
    "chars": 1329,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/InterceptorReactiveHttpClient.java",
    "chars": 1030,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/LoggerReactiveHttpClient.java",
    "chars": 6131,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/ReactiveHttpClient.java",
    "chars": 182,
    "preview": "package reactivefeign.client;\n\nimport reactor.core.publisher.Mono;\n\npublic interface ReactiveHttpClient {\n\n\tMono<Reactiv"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/ReactiveHttpRequest.java",
    "chars": 2150,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/ReactiveHttpRequestInterceptor.java",
    "chars": 904,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/ReactiveHttpResponse.java",
    "chars": 1054,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/ReadTimeoutException.java",
    "chars": 758,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/ResponseMappers.java",
    "chars": 1889,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/StatusHandlerReactiveHttpClient.java",
    "chars": 2757,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/statushandler/CompositeStatusHandler.java",
    "chars": 1695,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/statushandler/ReactiveStatusHandler.java",
    "chars": 925,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/statushandler/ReactiveStatusHandlers.java",
    "chars": 2601,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/methodhandler/DefaultMethodHandler.java",
    "chars": 2656,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/methodhandler/FluxMethodHandler.java",
    "chars": 509,
    "preview": "package reactivefeign.methodhandler;\n\nimport reactor.core.publisher.Flux;\n\npublic class FluxMethodHandler implements Met"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/methodhandler/MethodHandler.java",
    "chars": 159,
    "preview": "package reactivefeign.methodhandler;\n\nimport feign.InvocationHandlerFactory;\n\npublic interface MethodHandler extends Inv"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/methodhandler/MethodHandlerFactory.java",
    "chars": 931,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/methodhandler/MonoMethodHandler.java",
    "chars": 544,
    "preview": "package reactivefeign.methodhandler;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\npublic cl"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/methodhandler/PublisherClientMethodHandler.java",
    "chars": 8744,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/methodhandler/ReactiveMethodHandlerFactory.java",
    "chars": 1732,
    "preview": "package reactivefeign.methodhandler;\n\nimport feign.MethodMetadata;\nimport feign.Target;\nimport reactivefeign.publisher.*"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/publisher/FluxPublisherHttpClient.java",
    "chars": 802,
    "preview": "package reactivefeign.publisher;\n\n\nimport reactivefeign.client.ReactiveHttpClient;\nimport reactivefeign.client.ReactiveH"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/publisher/FluxRetryPublisherHttpClient.java",
    "chars": 1551,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/publisher/MonoPublisherHttpClient.java",
    "chars": 804,
    "preview": "package reactivefeign.publisher;\n\n\nimport org.reactivestreams.Publisher;\nimport reactivefeign.client.ReactiveHttpClient;"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/publisher/MonoRetryPublisherHttpClient.java",
    "chars": 1551,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/publisher/PublisherClientFactory.java",
    "chars": 827,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/publisher/PublisherHttpClient.java",
    "chars": 290,
    "preview": "package reactivefeign.publisher;\n\nimport org.reactivestreams.Publisher;\nimport reactivefeign.client.ReactiveHttpRequest;"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/publisher/RetryPublisherHttpClient.java",
    "chars": 2590,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/utils/FeignUtils.java",
    "chars": 2067,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/utils/HttpUtils.java",
    "chars": 1402,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/utils/MultiValueMapUtils.java",
    "chars": 1941,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/utils/Pair.java",
    "chars": 781,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/CompressionTest.java",
    "chars": 2852,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/ConnectionTimeoutTest.java",
    "chars": 2359,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/ContractTest.java",
    "chars": 2283,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/DefaultMethodTest.java",
    "chars": 5022,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/LoggerTest.java",
    "chars": 6118,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/NotFoundTest.java",
    "chars": 1810,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/ReactivityTest.java",
    "chars": 3022,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/ReadTimeoutTest.java",
    "chars": 1998,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/RequestInterceptorTest.java",
    "chars": 3244,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/RetryingTest.java",
    "chars": 7201,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/SmokeTest.java",
    "chars": 5243,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/StatusHandlerTest.java",
    "chars": 3496,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/TestUtils.java",
    "chars": 1320,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/allfeatures/AllFeaturesApi.java",
    "chars": 3884,
    "preview": "/*\n * Copyright 2013-2015 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/allfeatures/AllFeaturesController.java",
    "chars": 3828,
    "preview": "/*\n * Copyright 2013-2015 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/allfeatures/AllFeaturesTest.java",
    "chars": 9327,
    "preview": "/*\n * Copyright 2013-2015 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/CompressionTest.java",
    "chars": 1127,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/ConnectionTimeoutTest.java",
    "chars": 1139,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/ContractTest.java",
    "chars": 960,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/DefaultMethodTest.java",
    "chars": 1411,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/LoggerTest.java",
    "chars": 1020,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/NotFoundTest.java",
    "chars": 1023,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/ReactivityTest.java",
    "chars": 1324,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/ReadTimeoutTest.java",
    "chars": 1127,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/RequestInterceptorTest.java",
    "chars": 1043,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/RetryingTest.java",
    "chars": 1023,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/SmokeTest.java",
    "chars": 1017,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/StatusHandlerTest.java",
    "chars": 1033,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/client/RestTemplateFakeReactiveFeign.java",
    "chars": 2240,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/client/RestTemplateFakeReactiveHttpClient.java",
    "chars": 6307,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/testcase/IcecreamServiceApi.java",
    "chars": 1867,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/testcase/IcecreamServiceApiBroken.java",
    "chars": 1537,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/testcase/IcecreamServiceApiBrokenByCopy.java",
    "chars": 1569,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/testcase/domain/Bill.java",
    "chars": 1757,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/testcase/domain/Flavor.java",
    "chars": 744,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/testcase/domain/IceCreamOrder.java",
    "chars": 2149,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/testcase/domain/Mixin.java",
    "chars": 750,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/testcase/domain/OrderGenerator.java",
    "chars": 2355,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-jetty/pom.xml",
    "chars": 3318,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2"
  },
  {
    "path": "feign-reactor-jetty/src/main/java/reactivefeign/jetty/JettyReactiveFeign.java",
    "chars": 3212,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-jetty/src/main/java/reactivefeign/jetty/client/JettyReactiveHttpClient.java",
    "chars": 8883,
    "preview": "/*\n * Copyright 2013-2015 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "feign-reactor-jetty/src/main/java/reactivefeign/jetty/client/JettyReactiveHttpResponse.java",
    "chars": 3667,
    "preview": "package reactivefeign.jetty.client;\n\nimport com.fasterxml.jackson.core.async_.JsonFactory;\nimport com.fasterxml.jackson."
  },
  {
    "path": "feign-reactor-jetty/src/main/java/reactivefeign/jetty/utils/ProxyPostProcessor.java",
    "chars": 1028,
    "preview": "package reactivefeign.jetty.utils;\n\nimport org.eclipse.jetty.reactive.client.internal.AbstractSingleProcessor;\nimport or"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/CompressionTest.java",
    "chars": 1037,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/ConnectionTimeoutTest.java",
    "chars": 1049,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/ContractTest.java",
    "chars": 870,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/DefaultMethodTest.java",
    "chars": 1299,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/LoggerTest.java",
    "chars": 930,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/NotFoundTest.java",
    "chars": 1068,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/ReactivityTest.java",
    "chars": 1080,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/ReadTimeoutTest.java",
    "chars": 1037,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/RequestInterceptorTest.java",
    "chars": 1080,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/RetryingTest.java",
    "chars": 1049,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/SmokeTest.java",
    "chars": 927,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/StatusHandlerTest.java",
    "chars": 943,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/allfeatures/AllFeaturesTest.java",
    "chars": 1444,
    "preview": "/*\n * Copyright 2013-2015 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "feign-reactor-jetty/src/test/resources/log4j2.xml",
    "chars": 396,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Configuration status=\"WARN\">\n    <Appenders>\n        <Console name=\"Console\" tar"
  },
  {
    "path": "feign-reactor-rx2/pom.xml",
    "chars": 2864,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2"
  },
  {
    "path": "feign-reactor-rx2/src/main/java/reactivefeign/rx2/Rx2Contract.java",
    "chars": 2302,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/main/java/reactivefeign/rx2/Rx2ReactiveFeign.java",
    "chars": 6153,
    "preview": "package reactivefeign.rx2;\n\nimport feign.Contract;\nimport feign.InvocationHandlerFactory;\nimport feign.MethodMetadata;\ni"
  },
  {
    "path": "feign-reactor-rx2/src/main/java/reactivefeign/rx2/client/statushandler/Rx2ReactiveStatusHandler.java",
    "chars": 776,
    "preview": "package reactivefeign.rx2.client.statushandler;\n\nimport reactivefeign.client.ReactiveHttpResponse;\nimport reactivefeign."
  },
  {
    "path": "feign-reactor-rx2/src/main/java/reactivefeign/rx2/client/statushandler/Rx2StatusHandler.java",
    "chars": 918,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/main/java/reactivefeign/rx2/client/statushandler/Rx2StatusHandlers.java",
    "chars": 1364,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/main/java/reactivefeign/rx2/methodhandler/Rx2MethodHandler.java",
    "chars": 1952,
    "preview": "package reactivefeign.rx2.methodhandler;\n\nimport io.reactivex.Flowable;\nimport io.reactivex.Maybe;\nimport io.reactivex.O"
  },
  {
    "path": "feign-reactor-rx2/src/main/java/reactivefeign/rx2/methodhandler/Rx2MethodHandlerFactory.java",
    "chars": 1449,
    "preview": "package reactivefeign.rx2.methodhandler;\n\nimport feign.MethodMetadata;\nimport feign.Target;\nimport io.reactivex.Backpres"
  },
  {
    "path": "feign-reactor-rx2/src/main/java/reactivefeign/rx2/methodhandler/Rx2PublisherClientMethodHandler.java",
    "chars": 1399,
    "preview": "package reactivefeign.rx2.methodhandler;\n\nimport feign.MethodMetadata;\nimport feign.Target;\nimport io.reactivex.*;\nimpor"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/ContractTest.java",
    "chars": 1910,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/DefaultMethodTest.java",
    "chars": 5209,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/LoggerTest.java",
    "chars": 5540,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/NotFoundTest.java",
    "chars": 1948,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/ReactivityTest.java",
    "chars": 2994,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/ReadTimeoutTest.java",
    "chars": 2161,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/RequestInterceptorTest.java",
    "chars": 3327,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/SmokeTest.java",
    "chars": 5341,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/StatusHandlerTest.java",
    "chars": 2948,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/TestUtils.java",
    "chars": 1652,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/IcecreamServiceApi.java",
    "chars": 1950,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/IcecreamServiceApiBroken.java",
    "chars": 1666,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/domain/Bill.java",
    "chars": 1761,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/domain/Flavor.java",
    "chars": 748,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/domain/IceCreamOrder.java",
    "chars": 2153,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/domain/Mixin.java",
    "chars": 754,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/domain/OrderGenerator.java",
    "chars": 2359,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-webclient/pom.xml",
    "chars": 2908,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2"
  },
  {
    "path": "feign-reactor-webclient/src/main/java/reactivefeign/webclient/WebReactiveFeign.java",
    "chars": 3435,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-webclient/src/main/java/reactivefeign/webclient/client/WebReactiveHttpClient.java",
    "chars": 3916,
    "preview": "/*\n * Copyright 2013-2015 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "feign-reactor-webclient/src/main/java/reactivefeign/webclient/client/WebReactiveHttpResponse.java",
    "chars": 1713,
    "preview": "package reactivefeign.webclient.client;\n\nimport org.reactivestreams.Publisher;\nimport org.springframework.core.Parameter"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/CompressionTest.java",
    "chars": 1039,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/ConnectionTimeoutTest.java",
    "chars": 1051,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/ContractTest.java",
    "chars": 872,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/DefaultMethodTest.java",
    "chars": 1297,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/LoggerTest.java",
    "chars": 932,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/NotFoundTest.java",
    "chars": 935,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/ReactivityTest.java",
    "chars": 1082,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/ReadTimeoutTest.java",
    "chars": 1039,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/RequestInterceptorTest.java",
    "chars": 955,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/RetryingTest.java",
    "chars": 935,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/SmokeTest.java",
    "chars": 929,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/StatusHandlerTest.java",
    "chars": 945,
    "preview": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may no"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/allfeatures/AllFeaturesTest.java",
    "chars": 2003,
    "preview": "/*\n * Copyright 2013-2015 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/allfeatures/WebClientFeaturesApi.java",
    "chars": 852,
    "preview": "package reactivefeign.webclient.allfeatures;\n\nimport feign.Headers;\nimport feign.RequestLine;\nimport org.reactivestreams"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/allfeatures/WebClientFeaturesController.java",
    "chars": 1111,
    "preview": "package reactivefeign.webclient.allfeatures;\n\nimport org.reactivestreams.Publisher;\nimport org.springframework.core.io.R"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/allfeatures/WebClientFeaturesTest.java",
    "chars": 2971,
    "preview": "package reactivefeign.webclient.allfeatures;\n\n\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nim"
  },
  {
    "path": "feign-reactor-webclient/src/test/resources/log4j2.xml",
    "chars": 395,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Configuration status=\"WARN\">\n    <Appenders>\n        <Console name=\"Console\" tar"
  },
  {
    "path": "pom.xml",
    "chars": 19654,
    "preview": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocat"
  },
  {
    "path": "settings.xml",
    "chars": 504,
    "preview": "<settings xmlns=\"http://maven.apache.org/SETTINGS/1.0.0\"\n          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
  }
]

About this extraction

This page contains the full source code of the kptfh/feign-reactive GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 154 files (370.5 KB), approximately 88.7k tokens, and a symbol index with 716 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!