Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: a caching HTTP proxy for integration tests #1364

Merged
merged 1 commit into from
May 31, 2018

Conversation

bajtos
Copy link
Member

@bajtos bajtos commented May 25, 2018

After spending several days searching for viable options allowing us to write an integration test against a slow REST service (see #1347), I figured out it will be faster to implement our own caching proxy.

Welcome @loopback/http-caching-proxy.

This proxy makes it easy to record snap-hots of HTTP(S) communication and use this snapshot when running the tests. What's even more important, these snapshots are automatically updated whenever the backing service changes the API.

Why a caching proxy

Testing applications connecting to backend REST/SOAP services can be difficult:
The backend service may be slow, apply rate limiting, etc. Integration tests
become too slow in such case, which makes test-first development impractical.

This can be addressed by setting up a snapshot-based mock server or using
a caching HTTP client, but both of these solutions come with severe
disadvantages:

  • When using a snapshot-based mock server, we must ensure that snapshots
    are up-to-date with the actual backend implementation.

  • Caching at HTTP-client side requires non-trivial changes of the application
    code. We would have to update all our HTTP-based connectors to support client-side caching.

A filesystem-backed caching HTTP proxy offers a neat solution that combines
caching and snapshots:

  • The first request is forwarded to the actual backend and the response
    is stored as a snapshot.
  • Subsequent requests are served by the proxy using the cached snaphost.
  • Snapshot older than a configured time are discarded and the first next
    request will fetch the real response from the backend.

Basic use (as described in README)

Import the module at the top of your test file.

import {HttpCachingProxy} from '@loopback/http-caching-proxy';

Create a proxy instance during test-suite setup (typically in Mocha's before hook):

const proxy = new HttpCachingProxy({
  // directory where to store recorded snapshots
  cachePath: path.resolve(__dirname, '.proxy-cache'),
});
await proxy.start();

In your tests, configure the client library to use the caching proxy. Below is an example configuration for request:

request = request.defaults({
  proxy: proxy.url,
  // Disable tunneling of HTTPS requests - this is required for HTTPS!
  tunnel: false
});

Finally, stop the proxy when the test suite is done (typically in Mocha's after hook):

await proxy.stop();

Checklist

  • npm test passes on your machine
  • New tests added or existing tests modified to cover all changes
  • Code conforms with the style guide
  • API Documentation in code was updated
  • Documentation in /docs/site was updated
  • Affected artifact templates in packages/cli were updated
  • Affected example projects in examples/* were updated

@bajtos bajtos self-assigned this May 25, 2018
@bajtos bajtos requested a review from raymondfeng May 25, 2018 13:39
@bajtos
Copy link
Member Author

bajtos commented May 25, 2018

Oh, I just realized that my proxy is not expiring old cache entries. I'll add that functionality next week.

@bajtos bajtos force-pushed the feat/caching-http-proxy branch 2 times, most recently from 29a9550 to 6d8a2af Compare May 29, 2018 08:30
Copy link
Contributor

@b-admike b-admike left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! I did a first round of review and it LGTM. I will have another look again and try it out too.

@raymondfeng
Copy link
Contributor

raymondfeng commented May 29, 2018

@bajtos
Copy link
Member Author

bajtos commented May 30, 2018

https://github.com/nodejitsu/node-http-proxy

I was looking into this module. My understanding is that node-http-proxy does not provide a generic HTTP proxy capable of proxying requests to any backend. Instead, it works as a reverse-proxy for a single backend host (hostname:port). I find that limitation impractical for integration tests, because each tested service would need a different proxy configuration.

https://github.com/alibaba/anyproxy

Nice find! I did not come across this module in my search. AFAICT, they work as a regular proxy and the API is flexible enough to support caching:

http://anyproxy.io/en/#beforesendrequest

Any of these return values are valid

  • do nothing
  • (etc.)
  • give response to the client, not sending request any longe

BUT:

The reason why I am reluctant to use anyproxy is the unnecessary complexity and dependency size.

My current implementation is about 200 lines of production code with 6 dependencies (~1.9MiB). It should be easy for any developer to understand how the cache works and troubleshoot possible issues.

Compare that with anyproxy, which has 29 dependencies (10.3MiB), over 2800 lines of code and a bunch of features like browser-based GUI that we don't really need. I am also not particularly happy about their decision to use generator functions for flow control. While generator functions do have advantages over promise-based async functions, I think that ship has sailed long time ago and now it's better to use standard async/await.

Thoughts?

@bajtos bajtos mentioned this pull request May 30, 2018
6 tasks
@raymondfeng
Copy link
Contributor

@bajtos Fair enough. Two suggestions:

  1. Rename the module to http-caching-proxy
  2. Add a disclaimer to make it clear the module is for integration tests only, not intended for production.

@bajtos
Copy link
Member Author

bajtos commented May 31, 2018

Rename the module to http-caching-proxy

Done. I have also renamed CachedProxy to HttpCachedProxy for consistency.

Add a disclaimer to make it clear the module is for integration tests only, not intended for production.

I already have a bold disclaimer in README, I added it to package.json description field and MONOREPO.md too.

@bajtos bajtos force-pushed the feat/caching-http-proxy branch 4 times, most recently from c366d23 to 9ae77dd Compare May 31, 2018 08:49
Implement an HTTP proxy with aggressive cache persisted on file system.
This proxy is intended for integration tests to reduce the time needed
to access real backends.
@bajtos bajtos force-pushed the feat/caching-http-proxy branch from 9ae77dd to 9488dfc Compare May 31, 2018 10:12
@bajtos bajtos merged commit 7d8345c into master May 31, 2018
@bajtos bajtos deleted the feat/caching-http-proxy branch May 31, 2018 12:59
@bajtos bajtos removed the review label May 31, 2018
@bajtos bajtos added this to the June Milestone milestone Jun 28, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants