• Stars
    star
    246
  • Rank 164,726 (Top 4 %)
  • Language
    C#
  • License
    Apache License 2.0
  • Created over 7 years ago
  • Updated almost 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A .NET library for intercepting server-side HTTP requests

HttpClient Interception

A .NET Standard library for intercepting server-side HTTP dependencies.

NuGet version Build status

codecov OpenSSF Scorecard

Introduction

This library provides functionality for intercepting HTTP requests made using the HttpClient class in code targeting .NET Standard 2.0 (and later), and .NET Framework 4.7.2.

The primary use-case is for providing stub responses for use in tests for applications, such as an ASP.NET Core application, to drive your functional test scenarios.

The library is based around an implementation of DelegatingHandler, which can either be used directly as an implementation of HttpMessageHandler, or can be provided to instances of HttpClient. This also allows it to be registered via Dependency Injection to make it available for use in code under test without the application itself requiring any references to JustEat.HttpClientInterception or any custom abstractions of HttpClient.

This design means that no HTTP server needs to be hosted to proxy traffic to/from, so does not consume any additional system resources, such as needing to bind a port for HTTP traffic, making it lightweight to use.

Installation

To install the library from NuGet using the .NET SDK run:

dotnet add package JustEat.HttpClientInterception

Basic Examples

Request Interception

Fluent API

Below is a minimal example of intercepting an HTTP GET request to an API for a JSON resource to return a custom response using the fluent API:

// Arrange
var options = new HttpClientInterceptorOptions();
var builder = new HttpRequestInterceptionBuilder();

builder
    .Requests()
    .ForGet()
    .ForHttps()
    .ForHost("public.je-apis.com")
    .ForPath("terms")
    .Responds()
    .WithJsonContent(new { Id = 1, Link = "https://www.just-eat.co.uk/privacy-policy" })
    .RegisterWith(options);

using var client = options.CreateHttpClient();

// Act
// The value of json will be: {"Id":1, "Link":"https://www.just-eat.co.uk/privacy-policy"}
string json = await client.GetStringAsync("https://public.je-apis.com/terms");

snippet source | anchor

HttpRequestInterceptionBuilder objects are mutable, so properties can be freely changed once a particular setup has been registered with an instance of HttpClientInterceptorOptions as the state is captured at the point of registration. This allows multiple responses and paths to be configured from a single HttpRequestInterceptionBuilder instance where multiple registrations against a common hostname.

HTTP Bundle Files

HTTP requests to intercept can also be configured in an "HTTP bundle" file, which can be used to store the HTTP requests to intercept and their corresponding responses as JSON.

This functionality is analogous to our Shock pod for iOS.

JSON

Below is an example bundle file, which can return content in formats such as a string, JSON and base64-encoded data.

The full JSON schema for HTTP bundle files can be found here.

{
  "$schema": "https://raw.githubusercontent.com/justeattakeaway/httpclient-interception/main/src/HttpClientInterception/Bundles/http-request-bundle-schema.json",
  "id": "my-bundle",
  "comment": "A bundle of HTTP requests",
  "items": [
    {
      "id": "home",
      "comment": "Returns the home page",
      "uri": "https://www.just-eat.co.uk",
      "contentString": "<html><head><title>Just Eat</title></head></html>"
    },
    {
      "id": "terms",
      "comment": "Returns the Ts & Cs",
      "uri": "https://public.je-apis.com/terms",
      "contentFormat": "json",
      "contentJson": {
        "Id": 1,
        "Link": "https://www.just-eat.co.uk/privacy-policy"
      }
    }
  ]
}

snippet source | anchor

Code
// using JustEat.HttpClientInterception;

var options = new HttpClientInterceptorOptions().RegisterBundle("my-bundle.json");

var client = options.CreateHttpClient();

// The value of html will be "<html><head><title>Just Eat</title></head></html>"
var html = await client.GetStringAsync("https://www.just-eat.co.uk");

// The value of json will be "{\"Id\":1,\"Link\":\"https://www.just-eat.co.uk/privacy-policy\"}"
var json = await client.GetStringAsync("https://public.je-apis.com/terms");

Further examples of using HTTP bundles can be found in the tests, such as for changing the response code, the HTTP method, and matching to HTTP requests based on the request headers.

Fault Injection

Below is a minimal example of intercepting a request to inject an HTTP fault:

var options = new HttpClientInterceptorOptions();

var builder = new HttpRequestInterceptionBuilder()
    .Requests()
    .ForHost("public.je-apis.com")
    .WithStatus(HttpStatusCode.InternalServerError)
    .RegisterWith(options);

var client = options.CreateHttpClient();

// Throws an HttpRequestException
await Assert.ThrowsAsync<HttpRequestException>(
    () => client.GetStringAsync("http://public.je-apis.com"));

snippet source | anchor

Registering Request Interception When Using IHttpClientFactory

If you are using IHttpClientFactory to register HttpClient for Dependency Injection in a .NET Core 3.1 application (or later), you can implement a custom IHttpMessageHandlerBuilderFilter to register during test setup, which makes an instance of HttpClientInterceptorOptions available to inject an HTTP handler.

A working example of this approach can be found in the sample application.

/// <summary>
/// A class that registers an intercepting HTTP message handler at the end of
/// the message handler pipeline when an <see cref="HttpClient"/> is created.
/// </summary>
public sealed class HttpClientInterceptionFilter : IHttpMessageHandlerBuilderFilter
{
    private readonly HttpClientInterceptorOptions _options;

    public HttpClientInterceptionFilter(HttpClientInterceptorOptions options)
    {
        _options = options;
    }

    /// <inheritdoc/>
    public Action<HttpMessageHandlerBuilder> Configure(Action<HttpMessageHandlerBuilder> next)
    {
        return (builder) =>
        {
            // Run any actions the application has configured for itself
            next(builder);

            // Add the interceptor as the last message handler
            builder.AdditionalHandlers.Add(_options.CreateHttpMessageHandler());
        };
    }
}

snippet source | anchor

Setting Up HttpClient for Dependency Injection Manually

Below is an example of setting up IServiceCollection to register HttpClient for Dependency Injection in a manner that allows tests to use HttpClientInterceptorOptions to intercept HTTP requests. Similar approaches can be used with other IoC containers.

You may wish to consider registering HttpClient as a singleton, rather than as transient, if you do not use properties such as BaseAddress on instances of HttpClient. This allows the same instance to be used throughout the application, which improves performance and resource utilisation under heavy server load. If using a singleton instance, ensure that you manage the lifetime of your message handlers appropriately so they are not disposed of incorrectly and update the registration for your HttpClient instance appropriately.

services.AddTransient(
    (serviceProvider) =>
    {
        // Create a handler that makes actual HTTP calls
        HttpMessageHandler handler = new HttpClientHandler();

        // Have any delegating handlers been registered?
        var handlers = serviceProvider
            .GetServices<DelegatingHandler>()
            .ToList();

        if (handlers.Count > 0)
        {
            // Attach the initial handler to the first delegating handler
            DelegatingHandler previous = handlers.First();
            previous.InnerHandler = handler;

            // Chain any remaining handlers to each other
            foreach (DelegatingHandler next in handlers.Skip(1))
            {
                next.InnerHandler = previous;
                previous = next;
            }

            // Replace the initial handler with the last delegating handler
            handler = previous;
        }

        // Create the HttpClient using the inner HttpMessageHandler
        return new HttpClient(handler);
    });

Then in the test project register HttpClientInterceptorOptions to provide an implementation of DelegatingHandler. If using a singleton for HttpClient as described above, update the registration for the tests appropriately so that the message handler is shared.

var options = new HttpClientInterceptorOptions();

var server = new WebHostBuilder()
    .UseStartup<Startup>()
    .ConfigureServices(
        (services) => services.AddTransient((_) => options.CreateHttpMessageHandler()))
    .Build();

server.Start();

Further Examples

Further examples of using the library can be found by following the links below:

  1. Example tests
  2. Sample application with tests
  3. This library's own tests

Benchmarks

Generated with the Benchmarks project using BenchmarkDotNet using commit c31abf3 on 15/11/2022.

BenchmarkDotNet=v0.13.2, OS=Windows 11 (10.0.22000.1098/21H2)
Intel Core i9-9980HK CPU 2.40GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK=7.0.100
  [Host]     : .NET 7.0.0 (7.0.22.51805), X64 RyuJIT AVX2
  DefaultJob : .NET 7.0.0 (7.0.22.51805), X64 RyuJIT AVX2

Method Mean Error StdDev Gen0 Allocated
GetBytes 3.102 μs 0.0780 μs 0.2264 μs 0.3128 2648 B
GetHtml 3.675 μs 0.0735 μs 0.2143 μs 0.3700 3104 B
GetJson 5.175 μs 0.1223 μs 0.3470 μs 0.3433 2904 B
GetStream 36.359 μs 0.7168 μs 1.3981 μs 0.3662 3312 B

Feedback

Any feedback or issues can be added to the issues for this project in GitHub.

Repository

The repository is hosted in GitHub: https://github.com/justeattakeaway/httpclient-interception.git

Building and Testing

Compiling the library yourself requires Git and the .NET SDK to be installed (version 7.0.100 or later).

To build and test the library locally from a terminal/command-line, run one of the following set of commands:

git clone https://github.com/justeattakeaway/httpclient-interception.git
cd httpclient-interception
./build.ps1

License

This project is licensed under the Apache 2.0 license.

More Repositories

1

ScrollingStackViewController

A view controller that uses root views of child view controllers as views in a UIStackView.
Swift
646
star
2

JustLog

JustLog brings logging on iOS to the next level. It supports console, file and remote Logstash logging via TCP socket with no effort. Support for logz.io available.
Swift
518
star
3

JustTweak

JustTweak is a feature flagging framework for iOS apps.
Swift
203
star
4

JustPersist

JustPersist is the easiest and safest way to do persistence on iOS with Core Data support out of the box. It also allows you to migrate to any other persistence framework with minimal effort.
Swift
165
star
5

NavigationEngineDemo

A scalable and robust solution to navigation, deep linking and universal links on iOS 🚀
Swift
125
star
6

Shock

A HTTP mocking framework written in Swift.
Swift
101
star
7

JustPeek

JustPeek is an iOS Library that adds support for Force Touch-like Peek and Pop interactions on devices that do not natively support this kind of interaction.
Shell
68
star
8

Topshelf.Nancy

Nancy endpoint for the Topshelf service host providing additional support around URL reservations. — Edit
C#
65
star
9

ZendeskApiClient

C# Client for working with the Zendesk API
C#
64
star
10

JustEat.RecruitmentTest

The recruitment test to apply for an engineering role at Just Eat
Scala
62
star
11

AutomationTools

iOS UI testing framework and guidelines
Swift
52
star
12

NLog.StructuredLogging.Json

Structured logging for NLog using Json (formerly known as JsonFields)
C#
51
star
13

JustTrack

The Just Eat solution to better manage the analytics tracking and improve the relationship with your BI team.
Swift
42
star
14

JustFakeIt

HTTP server faking library
C#
31
star
15

jubako

A small API to help display rich content in a RecyclerView such as a wall of carousels
Kotlin
30
star
16

JustSaying

A light-weight message bus on top of AWS services (SNS and SQS).
C#
30
star
17

JustBehave

A BDD-influenced C# testing library cooked up by Just Eat
C#
29
star
18

PRAssigner

Swift AWS Lambda to automatically assign engineers to pull requests with a Slack integration
Swift
29
star
19

ApplePayJSSample

A sample implementation of Apple Pay JS using ASP.NET Core
C#
23
star
20

kongverge

A desired state configuration tool for Kong
C#
23
star
21

ProcessManager

C#
21
star
22

Android.Samples.Deeplinks

Sample app demonstrating an effective and unit-testable way to handle deep links in Android
Java
20
star
23

JustEat.StatsD

Our library for publishing metrics to statsd
C#
19
star
24

iOS.ErrorUtilities

Just Eat collection of iOS error-related utilities
Swift
19
star
25

JustSupport

JavaScript
18
star
26

AwsWatchman

Because unmonitored infrastructure will bite you
C#
17
star
27

fozzie-components

JavaScript
13
star
28

kubernetes-windows-aws-ovs

Kubernetes on windows in aws using ovn
HCL
13
star
29

applepayjs-polyfill

A polyfill for the Apple Pay JS for use in non-supported browsers
JavaScript
12
star
30

fozzie

Web UI Base Library
SCSS
12
star
31

LocalDynamoDb

C#
11
star
32

JE.IdentityServer.Security

Recaptcha for OpenIdConnect
C#
7
star
33

JustEat.Recruitment.UI

UI Test for interview candidates
7
star
34

OpenRastaSwagger

Swagger / Swagger-UI implementation for OpenRasta
JavaScript
7
star
35

ts-jsonschema-builder

Fluent TypeScript JSON Schema builder.
TypeScript
6
star
36

JustEat.InfoSecRecruitmentTest

C#
4
star
37

gulp-build-fozzie

Gulp build tasks for use across Fozzie modules
JavaScript
4
star
38

JustEat.CWA.RecruitmentTest

Consumer Web Applications recruitment test for roles at JUST EAT
4
star
39

AngularJSWebAPI.Experiment

HTML
3
star
40

JE.EmbeddedChecks

Micro-framework for embedding checks inside applications, so they tell you when they're healthy and how.
C#
3
star
41

JE.TurningPages

C#
3
star
42

JE.ApiValidation

API validation helpers - A standard error response contract and FluentValidation support in .NET web frameworks
C#
3
star
43

JE.ApiExceptions

C#
2
star
44

f-footer

Common footer component for Just Eat websites
SCSS
2
star
45

JustEat.EntryLevel.RecruitmentTest

Recruitment Test for Entry Level Engineers
2
star
46

global-component-library

Global Component Library and Documentation for sharing across UK and International
CSS
2
star
47

f-templates

Locate, compile, and serve HTML from templates.
JavaScript
1
star
48

f-dom

Fozzie JS DOM queries library.
JavaScript
1
star
49

f-toggle

Fozzie vanilla JS toggle library.
JavaScript
1
star
50

PowerShellDSCUtils

Just Eat PowerShell DSC Utilities
PowerShell
1
star
51

f-header

Common header component for Just Eat websites
JavaScript
1
star
52

f-recruit-message

Adds a recruitment message to the browser console
JavaScript
1
star
53

ruby_bootcamp

Code samples for ruby bootcamp show & tells
Ruby
1
star
54

f-serviceworker

JavaScript
1
star
55

mickeydb

Android sqlite db super tool, for migrations and data access code generation
Java
1
star
56

f-copy-assets

NPM package to copy assets from node_modules to a specified directory
JavaScript
1
star
57

JustEat.PublicApi.TestApp

JavaScript
1
star
58

browserslist-config-fozzie

Just Eat's Browserslist Config used in UI packages
JavaScript
1
star
59

openrasta-hosting-owin

OWIN host for OpenRasta
C#
1
star