• Stars
    star
    137
  • Rank 266,121 (Top 6 %)
  • Language
    Go
  • License
    MIT License
  • Created over 10 years ago
  • Updated almost 7 years ago

Reviews

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

Repository Details

Caching reverse proxy for testing written in Go

chameleon

Gitter

Build Status Coveralls License

What is chameleon?

chameleon is a caching reverse proxy.

chameleon supports recording and replaying requests with the ability to customize how responses are stored.

Why is chameleon useful?

  • Proxy rate-limited APIs for local development
  • Create reliable test environments
  • Test services in places you normally couldn't due to firewalling, etc (CI servers being common)
  • Improve speed of tests by never leaving your local network
  • Inspect recorded APIs responses for exploratory testing
  • Stub out unimplemented API endpoints during development

What can't I do with chameleon?

  • Have tests that exercise a given service right now as results are cached
  • Total control on how things are cached, frequency, rate-limiting, etc (pull requests are welcome, though!)

How to get chameleon?

chameleon has no runtime dependencies. You can download a prebuilt binary for your platform.

If you have Go installed, you may go get github.com/nickpresta/chameleon to download it to your $GOPATH directory.

How to use chameleon

  • Check out the example directory for a small app that uses chameleon to create reliable tests with a custom hasher.

To run chameleon, you can:

chameleon -data ./httpbin -url http://httpbin.org -verbose

The directory httpbin must already exist before running.

See chameleon -help for more information.

Specifying custom hash

There may be a reason in your tests to manually create responses - perhaps the backing service doesn't exist yet, or in test mode a service behaves differently than production. When this is the case, you can create custom responses and signal to chameleon the hash you want to use for a given request.

Set the chameleon-request-hash header with a unique hash value (which is a valid filename) and chameleon will look for that hash in the spec.json file and for all subsequent requests.

This allows you to not only have total control over the response for a given request but also makes your test code easier to reason about -- it is clear where the "special case" response is coming from.

Getting the hash for a given request

All responses from chameleon will have a chameleon-request-hash header set which is the hash used for that request. This header is present even if you did not set it on the incoming request.

Preseeding the cache

If you want to configure the cache at runtime without having to depend on an external service, you may preseed the cache via HTTP. This is particularly useful for mocking out services which don't yet exist.

To preseed a request, issue a JSON POST request to chameleon at the _seed endpoint with the following payload:

Field Description
Request Request is the request payload including a URL, Method and Body
Response Response is the response to be cached and sent back for a given request

Request

Field Description
Body Body is the content for the request. May be empty where body doesn't make sense (e.g. GET requests)
Method Method is the HTTP method used to match the incoming request. Case insensitive, supports arbitrary methods
URL URL is the absolute or relative URL to match in requests. Only the path and querystring are used

Response

Field Description
Body Body is the content for the request. May be empty where body doesn't make sense (e.g. GET requests)
Headers Headers is a map of headers in the format of string key to string value
StatusCode StatusCode is the HTTP status code of the response

Repeated, duplicate requests to preseed the cache will be discarded and the cache unaffected.

Successful new preseed requests will return an HTTP 201 CREATED on success or HTTP 500 INTERNAL SERVER ERROR. Duplicate preseed requests will return an HTTP 200 OK on success or HTTP 500 INTERNAL SERVER ERROR on failure.

Here is an example of preseeding the cache with a JSON response for a GET request for /foobar.

import requests

preseed = json.dumps({
    'Request': {
        'Body': '',
        'URL': '/foobar',
        'Method': 'GET',
    },
    'Response': {
        'Body': '{"key": "value"}',
        'Headers': {
            'Content-Type': 'application/json',
            'Other-Header': 'something-else',
        },
        'StatusCode': 200,
    },
})

response = requests.post('http://localhost:6005/_seed', data=preseed)
if response.status_code in (200, 201):
    # Created, or duplicate
else:
    # Error, print it out
    print(response.content)

# Continue tests as normal
# Making requests to `/foobar` will return `{"key": "value"}`
# without hitting the proxied service

Check out the example directory to see preseeding in action.

How chameleon caches responses

chameleon makes a hash for a given request URI, request method and request body and uses that to cache content. What that means:

  • a request of GET /foo/ will be cached differently than GET /bar/
  • a request of GET /foo/5 will be cached differently than GET /foo/6
  • a request of DELETE /foo/5 will be cached differently than DELETE /foo/6
  • a request of POST /foo with a body of {"hi":"hello} will be cached differently than a request of POST /foo with a body of {"spam":"eggs"}. To ignore the request body, set a header of chameleon-no-hash-body to any value. This will instruct chameleon to ignore the body as part of the hash.

Writing custom hasher

You can specify a custom hasher, which could be any program in any language, to determine what makes a request unique.

chameleon will communicate with this program via STDIN/STDOUT and feed the hasher a serialized Request (see below). You are then responsible for returning data to chameleon to be used for that given request (which will be hashed).

This feature is especially useful if you have to cache content based on the body of a request (XML payload, specific keys in JSON payload, etc).

See the example hasher for a sample hasher that emulates the default hasher.

Structure of Request

Below is an example Request serialized to JSON.

{
    "BodyBase64":"eyJmb28iOiAiYmFyIn0=",
    "ContentLength":14,
    "Headers":{
        "Accept":[
            "application/json"
        ],
        "Accept-Encoding":[
            "gzip, deflate"
        ],
        "Authorization":[
            "Basic dXNlcjpwYXNzd29yZA=="
        ],
        "Connection":[
            "keep-alive"
        ],
        "Content-Length":[
            "14"
        ],
        "Content-Type":[
            "application/json; charset=utf-8"
        ],
        "User-Agent":[
            "HTTPie/0.7.2"
        ]
    },
    "Method":"POST",
    "URL":{
        "Host":"httpbin.org",
        "Path":"/post",
        "RawQuery":"q=search+term%23home",
        "Scheme":"https"
    }
}
Field Description
BodyBase64 Body is the request's body, base64 encoded
ContentLength ContentLength records the length of the associated content after being base64 decoded
Headers Headers is a map of request lines to value lists. HTTP defines that header names are case-insensitive. Header names have been canonicalized, making the first character and any characters following a hyphen uppercase and the rest lowercase
Method Method specifies the HTTP method (GET, POST, PUT, etc.)
URL URL is an object containing Host, the HTTP Host in the form of 'host' or 'host:port', Path, the request path including trailing slash, RawQuery, encoded query string values without '?', and Scheme, the URL scheme 'http', 'https'

Getting help

Please open an issue for any bugs encountered, features requests, or general troubleshooting.

Authors

Nick Presta (@NickPresta)

Thanks to @mdibernardo for the inspiration.

License

Please see LICENSE

More Repositories

1

GoURLShortener

A URL shortener using http://is.gd/ and the Go programming language (http://golang.org/)
Go
22
star
2

scribe

A Go web server that generates PDFs via WebKit.
Go
9
star
3

go-wall

A Go implementation of the Coderwall API (http://coderwall.com/api)
Go
8
star
4

StackTray

A Python/Qt application utilizing the new StackOverflow API.
Python
7
star
5

panitizer

Go library for scrubbing strings of PAN (Personal Account Number) data.
Go
6
star
6

CV-Builder

A Django app that lets you create a CV, as a professor, quickly and easily.
Python
3
star
7

nurblizer

Go Implementation of Nurblizer - http://adambard.com/blog/PHP-ruby-python-clojure-webapps-by-example/
Go
3
star
8

MyGuelph

A student-made application for the University of Guelph.
Java
3
star
9

gowave

Go client library for accessing the Wave API
Go
2
star
10

dotfiles

My dotfiles.
Vim Script
2
star
11

gonotify

Gonotify is an HTTP/WebSocket notification server
Go
2
star
12

gogithub

A Go implement of the Github API v3 (http://developer.github.com/v3/)
Go
2
star
13

shorter

A URL Shortener written in Go #golang
Go
2
star
14

phantomjs-render-html

Render HTML using PhantomJS into a PDF/GIF/JPEG/PNG.
CoffeeScript
2
star
15

guelphdev-iphone-app

Objective-C
2
star
16

pyTasker

A collaborative task manager (task list) written in Python and PyQt4.
Python
2
star
17

guelphdev-api-service

JavaScript
2
star
18

java-client

A Java client that interacts with the GuelphDev API.
Java
1
star
19

CIS-4500---ACM-Prep

Coursework for CIS 4500 - a special topics course that focuses on solving ACM-style competition puzzles.
C++
1
star
20

gontacts

Google Contacts OAuth2 Site
Go
1
star
21

At-Guelph

JavaScript
1
star
22

MyFido

An Android 4.0+ application for Fido subscribers to check their usage and billing details.
Java
1
star
23

pyuoguelph

Python bindings for manipulating uoguelph.ca stuff
Python
1
star
24

wave-js-todomvc

Wave's TodoMVC for an internal JavaScript workshop
JavaScript
1
star
25

gamestream.in

View video game streams quickly and easily.
JavaScript
1
star
26

gototalks

GoTO (http://golangto.com/) Talks
Go
1
star
27

Steam-Now-Playing

Google App Engine project for extracting player details from the Steam Community page.
Python
1
star
28

My-Caffeine-Counter

Tracks my daily intake of caffeine
Python
1
star
29

boat-ws

A pirate boat game utilizing web sockets (and more!)
Python
1
star
30

Parallel-Waldo-Image-Search

2D Template/Pattern Matching project using Go.
Go
1
star
31

nickpresta.ca

HTML
1
star
32

Ce-Faci

Small Userscript to Ce Faci
JavaScript
1
star
33

go-wave-workshop

Something that can be `go get`'d to bootstrap our Wave workshop
Go
1
star
34

wave-polymer-components

A Polymer component for the Wave Business Switcher.
1
star
35

cardgame

Go
1
star