VCRProxy:在Capybara内用录像机录制PhantomJS ajax电话

时间:2012-10-23 21:06:46

标签: ruby ajax https phantomjs vcr

我已经在这个领域做了一些研究,但没有找到任何解决方案。我有一个网站,在那里对facebook进行异步ajax调用(使用JSONP)。我正在使用VCR在Ruby端录制我的所有HTTP请求,所以我认为将这个功能用于AJAX调用也很酷。

所以我玩了一下,并想出了一个代理尝试。我正在使用PhantomJS作为Capybara内部集成的无头浏览器和恶作剧者。 Poltergeist现在配置为使用这样的代理:

  Capybara.register_driver :poltergeist_vcr do |app|
    options = {
      :phantomjs_options => [
        "--proxy=127.0.0.1:9100",
        "--proxy-type=http",
        "--ignore-ssl-errors=yes",
        "--web-security=no"
      ],
      :inspector => true
    }
    Capybara::Poltergeist::Driver.new(app, options)
  end
  Capybara.javascript_driver = :poltergeist_vcr

出于测试目的,我编写了一个基于WEbrick的代理服务器,它集成了VCR:

require 'io/wait'
require 'webrick'
require 'webrick/httpproxy'

require 'rubygems'
require 'vcr'

module WEBrick
  class VCRProxyServer < HTTPProxyServer
    def service(*args)
      VCR.use_cassette('proxied') { super(*args) }
    end
  end
end

VCR.configure do |c|
  c.stub_with :webmock
  c.cassette_library_dir = '.'
  c.default_cassette_options = { :record => :new_episodes }
  c.ignore_localhost = true
end

IP   = '127.0.0.1'
PORT = 9100

reader, writer = IO.pipe

@pid = fork do
  reader.close
  $stderr = writer
  server = WEBrick::VCRProxyServer.new(:BindAddress => IP, :Port => PORT)
  trap('INT') { server.shutdown }
  server.start
end

raise 'VCR Proxy did not start in 10 seconds' unless reader.wait(10)

这适用于每个localhost调用,并且它们可以很好地记录。 HTML,JS和CSS文件由VCR记录。然后我启用了c.ignore_localhost = true选项,导致它(在我看来)无法记录localhost调用。

然后我再试一次,但我必须弄清楚,没有记录在页面上进行的AJAX调用。更糟糕的是,他们不再在测试中工作了。

所以说到这一点,我的问题是:为什么所有对localhost上的JS文件的调用都被记录下来,而JSONP调用外部资源却没有?它不能是jsonP的东西,因为它是一个“正常”的ajax请求。或者phantomjs中是否存在错误,AJAX调用未被代理?如果是这样,我们怎么能解决这个问题?

如果它正在运行,我想在

中集成启动和停止程序

-------更新-------

我做了一些研究并得出以下观点:代理在HTTPS调用和通过HTTPS调用的二进制数据方面存在一些问题。

我启动了服务器,并进行了一些卷曲调用:

curl --proxy 127.0.0.1:9100 http://d3jgo56a5b0my0.cloudfront.net/images/v7/application/stories_view/icons/bug.png

此通话应按原样录制。代理的请求和响应输出是

GET http://d3jgo56a5b0my0.cloudfront.net/images/v7/application/stories_view/icons/bug.png HTTP/1.1
User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8r zlib/1.2.5
Host: d3jgo56a5b0my0.cloudfront.net
Accept: */*
Proxy-Connection: Keep-Alive

HTTP/1.1 200 OK 
Server: WEBrick/1.3.1 (Ruby/1.9.3/2012-10-12)
Date: Tue, 20 Nov 2012 10:13:10 GMT
Content-Length: 0
Connection: Keep-Alive

但是这个调用没有被记录,HTTPS必然存在一些问题:

curl --proxy 127.0.0.1:9100 https://d3jgo56a5b0my0.cloudfront.net/images/v7/application/stories_view/icons/bug.png

标题输出为:

CONNECT d3jgo56a5b0my0.cloudfront.net:443 HTTP/1.1
Host: d3jgo56a5b0my0.cloudfront.net:443
User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8r zlib/1.2.5
Proxy-Connection: Keep-Alive

HTTP/1.1 200 OK 
Server: WEBrick/1.3.1 (Ruby/1.9.3/2012-10-12)
Date: Tue, 20 Nov 2012 10:15:48 GMT
Content-Length: 0
Connection: close

所以,我认为代理可能无法处理HTTPS,但它可以(只要我在cURL调用后获得控制台上的输出)。然后我想,也许VCR无法模拟HTTPS请求。但是当我不在代理中使用它时,VCR使用这个脚本来模拟HTTPS请求:

require 'vcr'

VCR.configure do |c|
  c.hook_into :webmock
  c.cassette_library_dir = 'cassettes'
end

uri = URI("https://d3jgo56a5b0my0.cloudfront.net/images/v7/application/stories_view/icons/bug.png")

VCR.use_cassette('https', :record => :new_episodes) do
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  response = http.request_get(uri.path)
  puts response.body
end

那么问题是什么? VCR处理HTTPS,代理处理HTTPS。他们为什么不一起玩?

2 个答案:

答案 0 :(得分:7)

所以我做了一些研究,现在我有一个非常基本的工作VCR代理服务器示例,它将HTTPS调用作为MITM代理服务器处理(如果您在客户端中停用安全检查)。如果有人能够做出贡献并帮助我实现这一目标,我将非常高兴。

这是github回购:https://github.com/23tux/vcr_proxy

答案 1 :(得分:1)

Puffing Billy是一个非常好的工具。您需要指定要绕过哪些域,以及需要存根的URL。这也是一个有点棘手的存根https网址。您需要将https网址存根为 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); image = (ImageView) findViewById(R.id.uploadedImage); uploadB = (Button) findViewById(R.id.uploadButton); uploadB.setOnClickListener(handler); Log.v("On create", "CREATED"); } View.OnClickListener handler = new View.OnClickListener() { @Override public void onClick(View view) { Log.v("HIT", "HIT"); UploadImage(); Next(); } }; public void Next() { Log.v("BITMAP OF IMAGE", newImage.toString()); image.setImageBitmap(newImage); } public void UploadImage() { @SuppressLint("StaticFieldLeak") AsyncTask<Void, Void, Bitmap> k = new AsyncTask<Void, Void, Bitmap>() { ProgressDialog loading; @Override protected void onPreExecute() { super.onPreExecute(); loading = ProgressDialog.show(MainActivity.this, "Uploading Image", "Please wait..."); } @Override protected void onPostExecute(Bitmap s) { loading.dismiss(); // Toast.makeText(MainActivity.this,s , Toast.LENGTH_LONG).show(); newImage = s; } @Override protected Bitmap doInBackground(Void... bitmaps) { Bitmap imageIcon = null; HandleImageRequest hir = new HandleImageRequest(); result = hir.ImageHttpRequest(UPLOAD_URL, tag); try { HttpURLConnection conn = (HttpURLConnection) new java.net.URL(result).openConnection(); conn.setDoInput(true); conn.connect(); InputStream input = conn.getInputStream(); imageIcon = BitmapFactory.decodeStream(input); } catch (IOException e) { e.printStackTrace(); } return imageIcon; } }; if (Build.VERSION.SDK_INT >= 11/*HONEYCOMB*/) { k.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } else { k.execute(); } }