Project

cap_proxy

0.0
No commit activity in last 3 years
No release in over 3 years
HTTP proxy with the ability to capture requests and generate a fake response. It is *not* intended to use in production environments, but only in integration tests.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 10.3.0
>= 0

Runtime

 Project Readme

CapProxy

Build Status

HTTP proxy with the ability to capture requests and generate a fake response. It is not intended to use in production environments, but only in integration tests.

It is tested in Ruby MRI (1.9, 2.0, 2.1) and Rubinius. Check out the Travis page to see the current status on every platform. We plan to support JRuby when some concurrency issues are managed.

Usage

Installation

Install the gem with

$ gem install cap_proxy

Or add it to your Gemfile

group :test do
  gem "cap_proxy"
end

Finally, load it into your application

require "cap_proxy/server"

Create a proxy instance

The main class to use the proxy is CapProxy::Server. You have to indicate the address where the proxy will be listening, and the address of the HTTP server which receives every non-captured request.

When the proxy is initialized, you have to call to the #run! method to start the server. This method has to be invoked when EventMachine is running.

proxy = CapProxy::Server.new("localhost:1234", "localhost:5678")

EM.run do
  proxy.run!
end

You can use a logger with the proxy:

logger = Logger.new(STDOUT)
proxy = CapProxy::Server.new("localhost:1234", "localhost:5678", logger)

EM.run do
  proxy.run!
end

Now, every connection to http://localhost:1234 will be forwarded to http://localhost:5678.

Testing

For your convenience, if you are using RSpec you can use CapProxy::TestWrapper to wrap every example.

require "cap_proxy/testkit"

# ...

around :each do |example|
  CapProxy::TestWrapper.run(example, "localhost:4001", "localhost:3000") do |proxy|
    @proxy = proxy
  end
end

CapProxy::TestWrapper will initialize EventMachine, creates a proxy running in it, and launch the example in a different thread. When the example is finished, the EventMachine is stopped.

Capturing and manipulating requests

With #capture you can capture any request, and generate a dummy response for it. You have to define a filter, and a block to generate the response.

@proxy.capture(method: "get", path: "/users") do |client, request|
  client.respond 404, {}, "Dummy response"
end

Details about how to capture requests, and to generate a response for it, are in the section Capturing requests.

Capturing requests

The first step is to define a filter. If a request matches multiple filters, it will be managed by the oldest one. If no filter matches the request, it will be forwarded to the default HTTP server.

Defining a filter

The easiest way to define a filter is with a hash, which can contain any of the following items:

  • :method
  • :path
  • :headers

:method can accept any string to represent a HTTP method ("get" and "GET" are equivalent).

@proxy.capture(method: "get") { ... }

:path can be defined with a string (full URI has to match), or with a regular expression (matched with the =~ operator).

@proxy.capture(path: "/users") { ... }

@proxy.capture(method: "post", path: %r{/users/\d+}) { ... }

:headers expects a hash with the headers to be matched. The value of every header can be a string or a regular expression.

@proxy.capture(path: "/", headers: { "content-type" => /json/ }) do
  ...
end

Custom filters

If you need more control to filter the request, you can create your own filter

class MyFilter < CapProxy::Filter

  def apply?(request)
    # Conditions
  end

end

The #apply? method receives an instance of Http::Parser, from the http_parser.rb gem. You can use methods like http_method, request_url or headers to query info about the request.

If #apply? returns false or nil, the filter will skip this request.

To use your filter, create an instance of it:

@proxy.capture(MyFilter.new) { ... }

Generating responses

The block of the capture method is invoked with two arguments: the first one is an instance of CapProxy::Client. The last one is the instance of HTTP::Parser.

The simplest way to generate a response is calling to client.respond(status, headers, body).

@proxy.capture(path: "/users", method: "post") do |client, request|
  client.respond 201, {"Content-Type" => "application/json"}, %q[{"id": 123}]
end

Chunked responses

You can generate a response in multiple chunks.

First, you have to call to client.chunks_start(status, headers), to initialize the chunked response. Then, for every chunk, call to client.chunks_send(data). Finally, finish the connection with client.chunks_finish

@proxy.capture(path: "/chunks") do |client, request|
  client.chunks_start 200, "content-type" => "text/plain"
  EM.add_timer(0.4) { client.chunks_send("Cap") }
  EM.add_timer(0.8) { client.chunks_send("Proxy\n") }
  EM.add_timer(1.2) { client.chunks_finish }
end

Example

require "cap_proxy/server"
require "cap_proxy/testkit"

describe MyApp do

  around :each do |test|
    CapProxy::TestWrapper.run(test, "localhost:4001", "localhost:3000") do |proxy|
      @proxy = proxy
    end
  end

  it "should do it" do
    @proxy.capture(method: "get", path: "/users") do |client, request|
      request.request_url.should include("foo_id")
      client.respond 404, {}, "Dummy response"
    end
  end
end