Skip to content

gareththackeray/httpclient-interception

 
 

Repository files navigation

HttpClient Interception

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

NuGet version

Linux Windows
Build Status Build status Build status codecov
Build History Build history Build history

Introduction

This library provides functionality for intercepting HTTP requests made using the HttpClient class in code targeting .NET Standard 1.3 and later and .NET Framework 4.6.1 and later.

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

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

// using JustEat.HttpClientInterception;

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

var options = new HttpClientInterceptorOptions()
    .Register(builder);

var client = options.CreateHttpClient();

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

Fault Injection

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

// using JustEat.HttpClientInterception;

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

var options = new HttpClientInterceptorOptions()
    .Register(builder);

var client = options.CreateHttpClient();

// Throws an HttpRequestException
await client.GetStringAsync("http://public.je-apis.com");

Setting Up HttpClient for Dependency Injection

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.

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 a442f1d on 23/09/2017.

BenchmarkDotNet=v0.10.9, OS=Windows 10 Redstone 2 (10.0.15063)
Processor=Intel Core i7-6500U CPU 2.50GHz (Skylake), ProcessorCount=4
Frequency=2531249 Hz, Resolution=395.0619 ns, Timer=TSC
.NET Core SDK=2.0.0
  [Host]     : .NET Core 2.0.0 (Framework 4.6.00001.0), 64bit RyuJIT
  DefaultJob : .NET Core 2.0.0 (Framework 4.6.00001.0), 64bit RyuJIT
Method Mean Error StdDev
GetBytes 3.604 μs 0.0639 μs 0.0567 μs
GetHtml 4.674 μs 0.0881 μs 0.0781 μs
GetJson 12.295 μs 0.2015 μs 0.1683 μs
GetStream 99.040 μs 1.9611 μs 3.0531 μs
Refit 33.651 μs 0.5165 μs 0.4831 μs

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/justeat/httpclient-interception.git

Building and Testing

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

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

Linux/OS X

git clone https://github.com/justeat/httpclient-interception.git
cd httpclient-interception
./build.sh  --restore-packages

Windows

git clone https://github.com/justeat/httpclient-interception.git
cd httpclient-interception
.\Build.ps1 -RestorePackages

License

This project is licensed under the Apache 2.0 license.

Packages

No packages published

Languages

  • C# 95.7%
  • PowerShell 3.3%
  • Shell 1.0%