As we reevaluate how to best support and maintain Staging Ref in the future, we encourage development teams using this environment to highlight their use cases in the following issue: https://gitlab.com/gitlab-com/gl-infra/software-delivery/framework/software-delivery-framework-issue-tracker/-/issues/36.

Skip to content
Snippets Groups Projects
Commit d03e022b authored by Jacob Vosmaer's avatar Jacob Vosmaer
Browse files

WIP Use excon for HTTP requests

parent 3fe9cea0
No related branches found
No related tags found
No related merge requests found
Showing
with 1752 additions and 94 deletions
Loading
Loading
@@ -10,10 +10,10 @@ require_relative '../lib/gitlab_net'
print "Check GitLab API access: "
begin
resp = GitlabNet.new.check
if resp.code == "200"
if resp.status == 200
print 'OK'
else
abort "FAILED. code: #{resp.code}"
abort "FAILED. code: #{resp.status}"
end
rescue GitlabNet::ApiUnreachableError
abort "FAILED: Failed to connect to internal API"
Loading
Loading
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), 'vendor/excon/lib')))
require 'excon'
Loading
Loading
@@ -7,7 +7,8 @@ require_relative 'gitlab_logger'
require_relative 'gitlab_access'
require_relative 'gitlab_redis'
require_relative 'gitlab_lfs_authentication'
require_relative 'httpunix'
require_relative 'gitlab_excon'
class GitlabNet
class ApiUnreachableError < StandardError; end
Loading
Loading
@@ -35,7 +36,7 @@ class GitlabNet
url = "#{host}/allowed"
resp = post(url, params)
if resp.code == '200'
if resp.status == 200
GitAccessStatus.create_from_json(resp.body)
else
GitAccessStatus.new(false, 'API is not accessible', nil)
Loading
Loading
@@ -56,7 +57,7 @@ class GitlabNet
resp = post("#{host}/lfs_authenticate", params)
if resp.code == '200'
if resp.status == 200
GitlabLfsAuthentication.build_from_json(resp.body)
end
end
Loading
Loading
@@ -79,7 +80,7 @@ class GitlabNet
def authorized_key(key)
resp = get("#{host}/authorized_keys?key=#{URI.escape(key, '+/=')}")
JSON.parse(resp.body) if resp.code == "200"
JSON.parse(resp.body) if resp.code == 200
rescue
nil
end
Loading
Loading
@@ -88,7 +89,7 @@ class GitlabNet
key_id = key.gsub('key-', '')
resp = post("#{host}/two_factor_recovery_codes", key_id: key_id)
JSON.parse(resp.body) if resp.code == '200'
JSON.parse(resp.body) if resp.status == 200
rescue
{}
end
Loading
Loading
@@ -131,48 +132,42 @@ class GitlabNet
"#{config.gitlab_url}/api/v3/internal"
end
def http_client_for(uri, options={})
if uri.is_a?(URI::HTTPUNIX)
http = Net::HTTPUNIX.new(uri.hostname)
else
http = Net::HTTP.new(uri.host, uri.port)
end
http.read_timeout = options[:read_timeout] || read_timeout
if uri.is_a?(URI::HTTPS)
http.use_ssl = true
http.cert_store = cert_store
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if config.http_settings['self_signed_cert']
end
http
end
def http_request_for(method, uri, params = {})
request_klass = method == :get ? Net::HTTP::Get : Net::HTTP::Post
request = request_klass.new(uri.request_uri)
def http_client_for(url)
excon_options = {}
user = config.http_settings['user']
password = config.http_settings['password']
request.basic_auth(user, password) if user && password
request.set_form_data(params.merge(secret_token: secret_token))
if user && password
excon_options[:user] = user
excon_options[:password] = password
end
request
uri = URI.parse(url)
if uri.scheme == 'http+unix'
excon_options[:socket] = URI.unescape(uri.host)
url.sub!('http+', '')
url.sub!(uri.host, '/')
else
excon_options[:ssl_verify_peer] = !config.http_settings['self_signed_cert']
excon_options[:ssl_cert_store] = cert_store
end
Excon.new(url, excon_options)
end
def request(method, url, params = {}, options={})
$logger.debug "Performing #{method.to_s.upcase} #{url}"
uri = URI.parse(url)
http = http_client_for(uri, options)
request = http_request_for(method, uri, params)
http = http_client_for(url)
request_options = {
method: method,
read_timeout: options[:read_timeout] || read_timeout,
body: URI.encode_www_form(params.merge(secret_token: secret_token)),
headers: { "Content-Type" => "application/x-www-form-urlencoded" }
}
begin
start_time = Time.new
response = http.start { http.request(request) }
response = http.request(request_options)
rescue => e
$logger.warn "Failed to connect to internal API <#{method.to_s.upcase} #{url}>: #{e.inspect}"
raise ApiUnreachableError
Loading
Loading
@@ -182,10 +177,10 @@ class GitlabNet
end
end
if response.code == "200"
$logger.debug "Received response #{response.code} => <#{response.body}>."
if response.status == 200
$logger.debug "Received response #{response.status} => <#{response.body}>."
else
$logger.error "API call <#{method.to_s.upcase} #{url}> failed: #{response.code} => <#{response.body}>."
$logger.error "API call <#{method.to_s.upcase} #{url}> failed: #{response.status} => <#{response.body}>."
end
response
Loading
Loading
# support for http+unix://... connection scheme
#
# The URI scheme has the same structure as the similar one for python requests. See:
# http://fixall.online/theres-no-need-to-reinvent-the-wheelhttpsgithubcommsabramorequests-unixsocketurl/241810/
# https://github.com/msabramo/requests-unixsocket
require 'uri'
require 'net/http'
module URI
class HTTPUNIX < HTTP
def hostname
# decode %XX from path to file
v = self.host
URI.decode(v)
end
# port is not allowed in URI
DEFAULT_PORT = nil
def set_port(v)
return v unless v
raise InvalidURIError, "http+unix:// cannot contain port"
end
end
@@schemes['HTTP+UNIX'] = HTTPUNIX
end
# Based on:
# - http://stackoverflow.com/questions/15637226/ruby-1-9-3-simple-get-request-to-unicorn-through-socket
# - Net::HTTP::connect
module Net
class HTTPUNIX < HTTP
def initialize(socketpath, port=nil)
super(socketpath, port)
@port = nil # HTTP will set it to default - override back -> set DEFAULT_PORT
end
# override to prevent ":<port>" being appended to HTTP_HOST
def addr_port
address
end
def connect
D "opening connection to #{address} ..."
s = UNIXSocket.new(address)
D "opened"
@socket = BufferedIO.new(s)
@socket.read_timeout = @read_timeout
@socket.continue_timeout = @continue_timeout
@socket.debug_output = @debug_output
on_connect
end
end
end
## Getting Involved
New contributors are always welcome, when it doubt please ask questions. We strive to be an open and welcoming community. Please be nice to one another.
### Coding
* Pick a task:
* Offer feedback on open [pull requests](https://github.com/excon/excon/pulls).
* Review open [issues](https://github.com/excon/excon/issues) for things to help on.
* [Create an issue](https://github.com/excon/excon/issues/new) to start a discussion on additions or features.
* Fork the project, add your changes and tests to cover them in a topic branch.
* Commit your changes and rebase against `excon/excon` to ensure everything is up to date.
* [Submit a pull request](https://github.com/excon/excon/compare/).
### Non-Coding
* Work for [twitter](http://twitter.com)? I'd love to reclaim the unused [@excon](http://twitter.com/excon) account!
* Offer feedback on open [issues](https://github.com/excon/excon/issues).
* Write and help edit [documentation](https://github.com/excon/excon.github.com).
* Translate [documentation](https://github.com/excon/excon.github.com) in to other languages.
* Organize or volunteer at events.
* [Donate](https://www.gittip.com/geemus/)!
* Discuss other ideas for contribution with [geemus](mailto:geemus+excon@gmail.com).
* Aaron Stone <aaron@serendipity.cx>
* Adam Esterline <adam@esterlines.com>
* Alexander Sandström <alexander@skovik.com>
* Andrew Katz <andrew.katz@outright.com>
* Andy Delcambre <adelcambre@gmail.com>
* Anshul Khandelwal <anshul@anshulkhandelwal.com>
* Ash Wilson <smashwilson@gmail.com>
* Ben Burkert <ben@benburkert.com>
* Benedikt Böhm <bb@xnull.de>
* Bo Jeanes <me@bjeanes.com>
* Brandur <brandur@mutelight.org>
* Brian D. Burns <iosctr@gmail.com>
* Brian Hartsock <brian.hartsock@gmail.com>
* Bryan Paxton <starbelly@pobox.com>
* Caio Chassot <dev@caiochassot.com>
* Caius Durling <dev@caius.name>
* Carl Hörberg <carl.hoerberg@gmail.com>
* Carl Hörberg <carl.hoerberg@gmail.com>
* Carlos Sanchez <csanchez@maestrodev.com>
* Casper Thomsen <ct@clearhaus.com>
* Chris Hanks <christopher.m.hanks@gmail.com>
* Claudio Poli <masterkain@gmail.com>
* Damien Mathieu <damien@heroku.com>
* Dan Hensgen <dan@methodhead.com>
* Dan Peterson <dpiddy@gmail.com>
* Dan Prince <dprince@redhat.com>
* Dane Harrigan <dane.harrigan@gmail.com>
* Dave Myron <therealdave.myron@gmail.com>
* Dave Newton <davelnewton@gmail.com>
* David Biehl <dbiehl@ncmedical.com>
* David Biehl <lazylodr@gmail.com>
* Dimitrij Denissenko <dimitrij@blacksquaremedia.com>
* Dominik Richter <dominik.richter@gmail.com>
* Doug McInnes <doug@dougmcinnes.com>
* Eugene Howe <eugene@xtreme-computers.net>
* Evan Phoenix <evan@fallingsnow.net>
* Fabian Wiesel <fabian.wiesel@sap.com>
* Federico Ravasio <ravasio.federico@gmail.com>
* Glenn Pratt <glennpratt@gmail.com>
* Graeme Nelson <graeme.nelson@gmail.com>
* Guillaume Balaine <igosuki@gmail.com>
* Hakan Ensari <hakan.ensari@papercavalier.com>
* Ian Neubert <ian@ianneubert.com>
* Jacob Atzen <jacob@incremental.dk>
* James Cox <james@imaj.es>
* James Watling <watling.james@gmail.com>
* Jean Mertz <jean@mertz.fm>
* Jeremy Hinegardner <jeremy@copiousfreetime.org>
* Jesse Kempf <jesse.kempf@opower.com>
* Joe Rafaniello <jrafanie@redhat.com>
* John Keiser <jkeiser@opscode.com>
* John Leach <john@brightbox.co.uk>
* Jonas Pfenniger <jonas@pfenniger.name>
* Jonathan Dance <github@wuputah.com>
* Jonathan Dance <jd@wuputah.com>
* Jonathan Roes <jroes@jroes.net>
* Joshua B. Smith <jbsmith@us.ibm.com>
* Joshua Gross <joshua@surfeasy.com>
* Joshua Mckinney <joshmckin@gmail.com>
* Joshua Napoli <jnapoli@swipely-napoli.home>
* Joshua Napoli <jnapoli@swipely-napoli.local>
* Kelly Mahan <kmahan@kmahan.com>
* Kensuke Nagae <kyanny@gmail.com>
* Konstantin Shabanov <etehtsea@gmail.com>
* Kyle Rames <kyle.rames@rackspace.com>
* Lewis Marshall <lewis@lmars.net>
* Lincoln Stoll <me@lstoll.net>
* Louis Sobel <sobel@mit.edu>
* Mahemoff <michael@mahemoff.com>
* Mathias Meyer <meyer@paperplanes.de>
* Matt Gauger <matt.gauger@gmail.com>
* Matt Sanders <matt@modal.org>
* Matt Sanders <matt@polycot.com>
* Matt Snyder <snyder2112@me.com>
* Matt Todd <chiology@gmail.com>
* Max Lincoln <max@devopsy.com>
* Michael Brodhead <mkb@engineyard.com>
* Michael Hale <mike@hales.ws>
* Michael Rowe <mrowe@mojain.com>
* Michael Rykov <mrykov@gmail.com>
* Mike Heffner <mikeh@fesnel.com>
* Myron Marston <myron.marston@gmail.com>
* Nathan Long <nathan.long@tma1.com>
* Nathan Sutton <nate@zencoder.com>
* Nick Osborn <nick.osborn@digital.cabinet-office.gov.uk>
* Nicolas Sanguinetti <contacto@nicolassanguinetti.info>
* Paul Gideon Dann <pdgiddie@gmail.com>
* Pavel <pavel.evst@gmail.com>
* Peter Meier <peter.meier@immerda.ch>
* Peter Weldon <peter.weldon@null.net>
* Peter Weldon <peter@lautus.net>
* Phil Ross <phil.ross@gmail.com>
* Richard Ramsden <richard@rramsden.ca>
* Ruslan Korolev <rs3@fastmail.com>
* Ruslan Korolev <rs41@gmx.com>
* Ruslan Kyrychuk <ruslan.kyrychuk@gmail.com>
* Ryan Bigg <radarlistener@fastmail.fm>
* Ryan Mohr <ryan.mohr@gmail.com>
* Sam Withrow <sam.withrow@curiousnation.org>
* Scott Gonyea <me@aitrus.org>
* Scott Gonyea <me@sgonyea.com>
* Scott Walkinshaw <scott.walkinshaw@gmail.com>
* Sean Cribbs <seancribbs@gmail.com>
* Sergio Rubio <rubiojr@frameos.org>
* Shai Rosenfeld <shaiguitar@gmail.com>
* Stefan Merettig <stefan-merettig@nuriaproject.org>
* Stephen Chu <github@stephenchu.com>
* Swanand Pagnis <swanandp@users.noreply.github.com>
* Terry Howe <terrylhowe@gmail.com>
* Thom Mahoney & Josh Lane <tmahoney@engineyard.com>
* Thom May <thom@digital-science.com>
* Tim Carey-Smith <tim@spork.in>
* Todd Lunter <tlunter@gmail.com>
* Tom Maher <tmaher@heroku.com>
* Tom Maher <tmaher@tursom.org>
* Trym Skaar <trym@tryms.no>
* Tuomas Silen <tuomas.silen@nodeta.fi>
* Victor Costan <costan@gmail.com>
* Viven <vivien.schilis@gmail.com>
* Wesley Beary <geemus+github@gmail.com>
* Wesley Beary <geemus@engineyard.com>
* Wesley Beary <geemus@gmail.com>
* Wesley Beary <wbeary@engineyard.com>
* Wesley Beary <wesley@heroku.com>
* Zach Anker <zanker@squareup.com>
* chrisrhoden <carhoden@gmail.com>
* dickeyxxx <jeff@dickeyxxx.com>
* geemus (Wesley Beary) <wbeary@engineyard.com>
* geemus <geemus@gmail.com>
* ggoodale <ggoodale@gmail.com>
* marios <marios@redhat.com>
* mkb <mkb@black-ice.org>
* phiggins <pete@peterhiggins.org>
* rin_ne <rinrin.ne@gmail.com>
* rinrinne <rinrin.ne@gmail.com>
* rkyrychuk <ruslan.kyrychuk@gmail.com>
* sshaw <skye.shaw@gmail.com>
* starbelly <starbelly@pobox.com>
* twrodriguez <tw.rodriguez@gmail.com>
* zimbatm <zimbatm@zimbatm.com>
\ No newline at end of file
source "http://rubygems.org"
gemspec
gem 'jruby-openssl', '~> 0.9', :platform => :jruby
gem 'unicorn', :platforms => [:mri, :rbx], :groups => [:development, :test]
gem 'rubysl', '~> 2.0', :platform => :rbx
gem 'rack', '~> 1.6'
# group :benchmark do
# gem 'em-http-request'
# gem 'httparty'
# gem 'rest-client'
# gem 'tach'
# gem 'typhoeus'
# gem 'sinatra'
# gem 'streamly_ffi'
# gem 'curb'
# end
PATH
remote: .
specs:
excon (0.54.0)
GEM
remote: http://rubygems.org/
specs:
activesupport (3.2.6)
i18n (~> 0.6)
multi_json (~> 1.0)
backports (3.6.4)
chronic (0.6.7)
delorean (2.0.0)
chronic
diff-lcs (1.2.5)
eventmachine (1.0.4)
eventmachine (1.0.4-java)
ffi2-generators (0.1.1)
formatador (0.2.3)
i18n (0.6.0)
jruby-openssl (0.9.17-java)
json (1.8.2)
json (1.8.2-java)
kgio (2.9.2)
minitest (4.7.5)
multi_json (1.3.6)
open4 (1.3.0)
puma (3.6.0)
puma (3.6.0-java)
rack (1.6.0)
rack-protection (1.2.0)
rack
rack-test (0.6.3)
rack (>= 1.0)
raindrops (0.13.0)
rake (0.9.2.2)
rdoc (3.12)
json (~> 1.4)
rspec (3.5.0)
rspec-core (~> 3.5.0)
rspec-expectations (~> 3.5.0)
rspec-mocks (~> 3.5.0)
rspec-core (3.5.0)
rspec-support (~> 3.5.0)
rspec-expectations (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-mocks (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-support (3.5.0)
rubysl (2.0.14)
rubysl-abbrev (~> 2.0)
rubysl-base64 (~> 2.0)
rubysl-benchmark (~> 2.0)
rubysl-bigdecimal (~> 2.0)
rubysl-cgi (~> 2.0)
rubysl-cgi-session (~> 2.0)
rubysl-cmath (~> 2.0)
rubysl-complex (~> 2.0)
rubysl-continuation (~> 2.0)
rubysl-coverage (~> 2.0)
rubysl-csv (~> 2.0)
rubysl-curses (~> 2.0)
rubysl-date (~> 2.0)
rubysl-delegate (~> 2.0)
rubysl-digest (~> 2.0)
rubysl-drb (~> 2.0)
rubysl-e2mmap (~> 2.0)
rubysl-english (~> 2.0)
rubysl-enumerator (~> 2.0)
rubysl-erb (~> 2.0)
rubysl-etc (~> 2.0)
rubysl-expect (~> 2.0)
rubysl-fcntl (~> 2.0)
rubysl-fiber (~> 2.0)
rubysl-fileutils (~> 2.0)
rubysl-find (~> 2.0)
rubysl-forwardable (~> 2.0)
rubysl-getoptlong (~> 2.0)
rubysl-gserver (~> 2.0)
rubysl-io-console (~> 2.0)
rubysl-io-nonblock (~> 2.0)
rubysl-io-wait (~> 2.0)
rubysl-ipaddr (~> 2.0)
rubysl-irb (~> 2.0)
rubysl-logger (~> 2.0)
rubysl-mathn (~> 2.0)
rubysl-matrix (~> 2.0)
rubysl-mkmf (~> 2.0)
rubysl-monitor (~> 2.0)
rubysl-mutex_m (~> 2.0)
rubysl-net-ftp (~> 2.0)
rubysl-net-http (~> 2.0)
rubysl-net-imap (~> 2.0)
rubysl-net-pop (~> 2.0)
rubysl-net-protocol (~> 2.0)
rubysl-net-smtp (~> 2.0)
rubysl-net-telnet (~> 2.0)
rubysl-nkf (~> 2.0)
rubysl-observer (~> 2.0)
rubysl-open-uri (~> 2.0)
rubysl-open3 (~> 2.0)
rubysl-openssl (~> 2.0)
rubysl-optparse (~> 2.0)
rubysl-ostruct (~> 2.0)
rubysl-pathname (~> 2.0)
rubysl-prettyprint (~> 2.0)
rubysl-prime (~> 2.0)
rubysl-profile (~> 2.0)
rubysl-profiler (~> 2.0)
rubysl-pstore (~> 2.0)
rubysl-pty (~> 2.0)
rubysl-rational (~> 2.0)
rubysl-readline (~> 2.0)
rubysl-resolv (~> 2.0)
rubysl-rexml (~> 2.0)
rubysl-rinda (~> 2.0)
rubysl-rss (~> 2.0)
rubysl-scanf (~> 2.0)
rubysl-securerandom (~> 2.0)
rubysl-set (~> 2.0)
rubysl-shellwords (~> 2.0)
rubysl-singleton (~> 2.0)
rubysl-socket (~> 2.0)
rubysl-stringio (~> 2.0)
rubysl-strscan (~> 2.0)
rubysl-sync (~> 2.0)
rubysl-syslog (~> 2.0)
rubysl-tempfile (~> 2.0)
rubysl-test-unit (~> 2.0)
rubysl-thread (~> 2.0)
rubysl-thwait (~> 2.0)
rubysl-time (~> 2.0)
rubysl-timeout (~> 2.0)
rubysl-tmpdir (~> 2.0)
rubysl-tsort (~> 2.0)
rubysl-un (~> 2.0)
rubysl-uri (~> 2.0)
rubysl-weakref (~> 2.0)
rubysl-webrick (~> 2.0)
rubysl-xmlrpc (~> 2.0)
rubysl-yaml (~> 2.0)
rubysl-zlib (~> 2.0)
rubysl-abbrev (2.0.4)
rubysl-base64 (2.0.0)
rubysl-benchmark (2.0.1)
rubysl-bigdecimal (2.0.2)
rubysl-cgi (2.0.1)
rubysl-cgi-session (2.0.1)
rubysl-cmath (2.0.0)
rubysl-complex (2.0.0)
rubysl-continuation (2.0.0)
rubysl-coverage (2.0.3)
rubysl-csv (2.0.2)
rubysl-english (~> 2.0)
rubysl-curses (2.0.0)
rubysl-date (2.0.6)
rubysl-delegate (2.0.1)
rubysl-digest (2.0.3)
rubysl-drb (2.0.1)
rubysl-e2mmap (2.0.0)
rubysl-english (2.0.0)
rubysl-enumerator (2.0.0)
rubysl-erb (2.0.1)
rubysl-etc (2.0.3)
ffi2-generators (~> 0.1)
rubysl-expect (2.0.0)
rubysl-fcntl (2.0.4)
ffi2-generators (~> 0.1)
rubysl-fiber (2.0.0)
rubysl-fileutils (2.0.3)
rubysl-find (2.0.1)
rubysl-forwardable (2.0.1)
rubysl-getoptlong (2.0.0)
rubysl-gserver (2.0.0)
rubysl-socket (~> 2.0)
rubysl-thread (~> 2.0)
rubysl-io-console (2.0.0)
rubysl-io-nonblock (2.0.0)
rubysl-io-wait (2.0.0)
rubysl-ipaddr (2.0.0)
rubysl-irb (2.0.4)
rubysl-e2mmap (~> 2.0)
rubysl-mathn (~> 2.0)
rubysl-readline (~> 2.0)
rubysl-thread (~> 2.0)
rubysl-logger (2.0.0)
rubysl-mathn (2.0.0)
rubysl-matrix (2.1.0)
rubysl-e2mmap (~> 2.0)
rubysl-mkmf (2.0.1)
rubysl-fileutils (~> 2.0)
rubysl-shellwords (~> 2.0)
rubysl-monitor (2.0.0)
rubysl-mutex_m (2.0.0)
rubysl-net-ftp (2.0.1)
rubysl-net-http (2.0.4)
rubysl-cgi (~> 2.0)
rubysl-erb (~> 2.0)
rubysl-singleton (~> 2.0)
rubysl-net-imap (2.0.1)
rubysl-net-pop (2.0.1)
rubysl-net-protocol (2.0.1)
rubysl-net-smtp (2.0.1)
rubysl-net-telnet (2.0.0)
rubysl-nkf (2.0.1)
rubysl-observer (2.0.0)
rubysl-open-uri (2.0.0)
rubysl-open3 (2.0.0)
rubysl-openssl (2.0.4)
rubysl-optparse (2.0.1)
rubysl-shellwords (~> 2.0)
rubysl-ostruct (2.0.4)
rubysl-pathname (2.0.0)
rubysl-prettyprint (2.0.2)
rubysl-prime (2.0.0)
rubysl-profile (2.0.0)
rubysl-profiler (2.0.1)
rubysl-pstore (2.0.0)
rubysl-pty (2.0.2)
rubysl-rational (2.0.1)
rubysl-readline (2.0.2)
rubysl-resolv (2.0.0)
rubysl-rexml (2.0.2)
rubysl-rinda (2.0.0)
rubysl-rss (2.0.0)
rubysl-scanf (2.0.0)
rubysl-securerandom (2.0.0)
rubysl-set (2.0.1)
rubysl-shellwords (2.0.0)
rubysl-singleton (2.0.0)
rubysl-socket (2.0.1)
rubysl-stringio (2.0.0)
rubysl-strscan (2.0.0)
rubysl-sync (2.0.0)
rubysl-syslog (2.0.1)
ffi2-generators (~> 0.1)
rubysl-tempfile (2.0.1)
rubysl-test-unit (2.0.1)
minitest (~> 4.7)
rubysl-thread (2.0.2)
rubysl-thwait (2.0.0)
rubysl-time (2.0.3)
rubysl-timeout (2.0.0)
rubysl-tmpdir (2.0.0)
rubysl-tsort (2.0.1)
rubysl-un (2.0.0)
rubysl-fileutils (~> 2.0)
rubysl-optparse (~> 2.0)
rubysl-uri (2.0.0)
rubysl-weakref (2.0.0)
rubysl-webrick (2.0.0)
rubysl-xmlrpc (2.0.0)
rubysl-yaml (2.0.3)
rubysl-zlib (2.0.1)
shindo (0.3.4)
formatador (>= 0.1.1)
sinatra (1.3.2)
rack (~> 1.3, >= 1.3.6)
rack-protection (~> 1.2)
tilt (~> 1.3, >= 1.3.3)
sinatra-contrib (1.3.2)
backports (>= 2.0)
eventmachine
rack-protection
rack-test
sinatra (~> 1.3.0)
tilt (~> 1.3)
tilt (1.3.3)
unicorn (4.8.3)
kgio (~> 2.6)
rack
raindrops (~> 0.7)
PLATFORMS
java
ruby
DEPENDENCIES
activesupport
delorean
eventmachine (>= 1.0.4)
excon!
jruby-openssl (~> 0.9)
json (>= 1.8.2)
open4
puma
rack (~> 1.6)
rake
rdoc
rspec (>= 3.5.0)
rubysl (~> 2.0)
shindo
sinatra
sinatra-contrib
unicorn
BUNDLED WITH
1.13.1
The MIT License (MIT)
Copyright (c) 2009-2015 [CONTRIBUTORS.md](https://github.com/excon/excon/blob/master/CONTRIBUTORS.md)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# excon
Usable, fast, simple Ruby HTTP 1.1
Excon was designed to be simple, fast and performant. It works great as a general HTTP(s) client and is particularly well suited to usage in API clients.
[![Build Status](https://secure.travis-ci.org/excon/excon.svg)](http://travis-ci.org/excon/excon)
[![Dependency Status](https://gemnasium.com/geemus/excon.svg)](https://gemnasium.com/geemus/excon)
[![Gem Version](https://badge.fury.io/rb/excon.svg)](http://badge.fury.io/rb/excon)
[![Gittip](http://img.shields.io/gittip/geemus.svg)](https://www.gittip.com/geemus/)
* [Getting Started](#getting-started)
* [Options](#options)
* [Chunked Requests](#chunked-requests)
* [Pipelining Requests](#pipelining-requests)
* [Streaming Responses](#streaming-responses)
* [Proxy Support](#proxy-support)
* [Reusable ports](#reusable-ports)
* [Unix Socket Support](#unix-socket-support)
* [Stubs](#stubs)
* [Instrumentation](#instrumentation)
* [HTTPS client certificate](#https-client-certificate)
* [HTTPS/SSL Issues](#httpsssl-issues)
* [Getting Help](#getting-help)
* [Contributing](#contributing)
* [Plugins and Middlewares](#plugins-and-middlewares)
* [License](#license)
## Getting Started
Install the gem.
```
$ sudo gem install excon
```
Require with rubygems.
```ruby
require 'rubygems'
require 'excon'
```
The easiest way to get started is by using one-off requests. Supported one-off request methods are `connect`, `delete`, `get`, `head`, `options`, `post`, `put`, and `trace`. Requests return a response object which has `body`, `headers`, `remote_ip` and `status` attributes.
```ruby
response = Excon.get('http://geemus.com')
response.body # => "..."
response.headers # => {...}
response.remote_ip # => "..."
response.status # => 200
```
For API clients or other ongoing usage, reuse a connection across multiple requests to share options and improve performance.
```ruby
connection = Excon.new('http://geemus.com')
get_response = connection.get
post_response = connection.post(:path => '/foo')
delete_response = connection.delete(:path => '/bar')
```
By default, each connection is non-persistent. This means that each request made against a connection behaves like a
one-off request. Each request will establish a socket connection to the server, then close the socket once the request
is complete.
To use a persistent connection, use the `:persistent` option:
```ruby
connection = Excon.new('http://geemus.com', :persistent => true)
```
The initial request will establish a socket connection to the server and leave the socket open. Subsequent requests
will reuse that socket. You may call `Connection#reset` at any time to close the underlying socket, and the next request
will establish a new socket connection.
You may also control persistence on a per-request basis by setting the `:persistent` option for each request.
```ruby
connection = Excon.new('http://geemus.com') # non-persistent by default
connection.get # socket established, then closed
connection.get(:persistent => true) # socket established, left open
connection.get(:persistent => true) # socket reused
connection.get # socket reused, then closed
connection = Excon.new('http://geemus.com', :persistent => true)
connection.get # socket established, left open
connection.get(:persistent => false) # socket reused, then closed
connection.get(:persistent => false) # socket established, then closed
connection.get # socket established, left open
connection.get # socket reused
```
Note that sending a request with `:persistent => false` to close the socket will also send `Connection: close` to inform
the server the connection is no longer needed. `Connection#reset` will simply close our end of the socket.
## Options
Both one-off and persistent connections support many other options. The final options for a request are built up by starting with `Excon.defaults`, then merging in options from the connection and finally merging in any request options. In this way you have plenty of options on where and how to set options and can easily setup connections or defaults to match common options for a particular endpoint.
Here are a few common examples:
```ruby
# Output debug info, similar to ENV['EXCON_DEBUG']
connection = Excon.new('http://geemus.com/', :debug_request => true, :debug_response => true)
# Custom headers
Excon.get('http://geemus.com', :headers => {'Authorization' => 'Basic 0123456789ABCDEF'})
connection.get(:headers => {'Authorization' => 'Basic 0123456789ABCDEF'})
# Changing query strings
connection = Excon.new('http://geemus.com/')
connection.get(:query => {:foo => 'bar'})
# POST body encoded with application/x-www-form-urlencoded
Excon.post('http://geemus.com',
:body => 'language=ruby&class=fog',
:headers => { "Content-Type" => "application/x-www-form-urlencoded" })
# same again, but using URI to build the body of parameters
Excon.post('http://geemus.com',
:body => URI.encode_www_form(:language => 'ruby', :class => 'fog'),
:headers => { "Content-Type" => "application/x-www-form-urlencoded" })
# request takes a method option, accepting either a symbol or string
connection.request(:method => :get)
connection.request(:method => 'GET')
# expect one or more status codes, or raise an error
connection.request(:expects => [200, 201], :method => :get)
# this request can be repeated safely, so retry on errors up to 3 times
connection.request(:idempotent => true)
# this request can be repeated safely, retry up to 6 times
connection.request(:idempotent => true, :retry_limit => 6)
# set longer read_timeout (default is 60 seconds)
connection.request(:read_timeout => 360)
# set longer write_timeout (default is 60 seconds)
connection.request(:write_timeout => 360)
# Enable the socket option TCP_NODELAY on the underlying socket.
#
# This can improve response time when sending frequent short
# requests in time-sensitive scenarios.
#
connection = Excon.new('http://geemus.com/', :tcp_nodelay => true)
# set longer connect_timeout (default is 60 seconds)
connection = Excon.new('http://geemus.com/', :connect_timeout => 360)
# opt-out of nonblocking operations for performance and/or as a workaround
connection = Excon.new('http://geemus.com/', :nonblock => false)
# use basic authentication by supplying credentials in the URL or as parameters
connection = Excon.new('http://username:password@secure.geemus.com')
connection = Excon.new('http://secure.geemus.com',
:user => 'username', :password => 'password')
# use custom uri parser
require 'addressable/uri'
connection = Excon.new('http://geemus.com/', uri_parser: Addressable::URI)
```
Compared to web browsers and other http client libraries, e.g. curl, Excon is a bit more low-level and doesn't assume much by default. If you are seeing different results compared to other clients, the following options might help:
```ruby
# opt-in to omitting port from http:80 and https:443
connection = Excon.new('http://geemus.com/', :omit_default_port => true)
# accept gzip encoding
connection = Excon.new('http://geemus.com/', :headers => { "Accept" => "gzip" })
# turn off peer verification (less secure)
Excon.defaults[:ssl_verify_peer] = false
connection = Excon.new('https://...')
```
## Chunked Requests
You can make `Transfer-Encoding: chunked` requests by passing a block that will deliver chunks, delivering an empty chunk to signal completion.
```ruby
file = File.open('data')
chunker = lambda do
# Excon.defaults[:chunk_size] defaults to 1048576, ie 1MB
# to_s will convert the nil received after everything is read to the final empty chunk
file.read(Excon.defaults[:chunk_size]).to_s
end
Excon.post('http://geemus.com', :request_block => chunker)
file.close
```
Iterating in this way allows you to have more granular control over writes and to write things where you can not calculate the overall length up front.
## Pipelining Requests
You can make use of HTTP pipelining to improve performance. Instead of the normal request/response cycle, pipelining sends a series of requests and then receives a series of responses. You can take advantage of this using the `requests` method, which takes an array of params where each is a hash like request would receive and returns an array of responses.
```ruby
connection = Excon.new('http://geemus.com/')
connection.requests([{:method => :get}, {:method => :get}])
```
By default, each call to `requests` will use a separate persistent socket connection. To make multiple `requests` calls
using a single persistent connection, set `:persistent => true` when establishing the connection.
## Streaming Responses
You can stream responses by passing a block that will receive each chunk.
```ruby
streamer = lambda do |chunk, remaining_bytes, total_bytes|
puts chunk
puts "Remaining: #{remaining_bytes.to_f / total_bytes}%"
end
Excon.get('http://geemus.com', :response_block => streamer)
```
Iterating over each chunk will allow you to do work on the response incrementally without buffering the entire response first. For very large responses this can lead to significant memory savings.
## Proxy Support
You can specify a proxy URL that Excon will use with both HTTP and HTTPS connections:
```ruby
connection = Excon.new('http://geemus.com', :proxy => 'http://my.proxy:3128')
connection.request(:method => 'GET')
Excon.get('http://geemus.com', :proxy => 'http://my.proxy:3128')
```
The proxy URL must be fully specified, including scheme (e.g. "http://") and port.
Proxy support must be set when establishing a connection object and cannot be overridden in individual requests.
NOTE: Excon will use the environment variables `http_proxy` and `https_proxy` if they are present. If these variables are set they will take precedence over a :proxy option specified in code. If "https_proxy" is not set, the value of "http_proxy" will be used for both HTTP and HTTPS connections.
## Reusable ports
For advanced cases where you'd like to reuse the local port assigned to the excon socket in another socket, use the `:reuseaddr` option.
```ruby
connection = Excon.new('http://geemus.com', :reuseaddr => true)
connection.get
s = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
s.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
if defined?(Socket::SO_REUSEPORT)
s.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEPORT, true)
end
s.bind(Socket.pack_sockaddr_in(connection.local_port, connection.local_address))
s.connect(Socket.pack_sockaddr_in(80, '1.2.3.4'))
puts s.read
s.close
```
## Unix Socket Support
The Unix socket will work for one-off requests and multiuse connections. A Unix socket path must be provided separate from the resource path.
```ruby
connection = Excon.new('unix:///', :socket => '/tmp/unicorn.sock')
connection.request(:method => :get, :path => '/ping')
Excon.get('unix:///ping', :socket => '/tmp/unicorn.sock')
```
NOTE: Proxies will be ignored when using a Unix socket, since a Unix socket has to be local.
## Stubs
You can stub out requests for testing purposes by enabling mock mode on a connection.
```ruby
connection = Excon.new('http://example.com', :mock => true)
```
Or by enabling mock mode for a request.
```ruby
connection.request(:method => :get, :path => 'example', :mock => true)
```
Add stubs by providing the request attributes to match and response attributes to return. Response params can be specified as either a hash or block which will yield with the request params.
```ruby
Excon.stub({}, {:body => 'body', :status => 200})
Excon.stub({}, lambda {|request_params| {:body => request_params[:body], :status => 200}})
```
Omitted attributes are assumed to match, so this stub will match *any* request and return an Excon::Response with a body of 'body' and status of 200. You can add whatever stubs you might like this way and they will be checked against in the order they were added, if none of them match then excon will raise an `Excon::Errors::StubNotFound` error to let you know.
If you want to allow unstubbed requests without raising `StubNotFound`, set the `allow_unstubbed_requests` option either globally or per request.
```ruby
connection = Excon.new('http://example.com', :mock => true, :allow_unstubbed_requests => true)
```
To remove a previously defined stub, or all stubs:
```ruby
Excon.unstub({}) # remove first/oldest stub matching {}
Excon.stubs.clear # remove all stubs
```
For example, if using RSpec for your test suite you can clear stubs after running each example:
```ruby
config.after(:each) do
Excon.stubs.clear
end
```
You can also modify `Excon.defaults` to set a stub for all requests, so for a test suite you might do this:
```ruby
# Mock by default and stub any request as success
config.before(:all) do
Excon.defaults[:mock] = true
Excon.stub({}, {:body => 'Fallback', :status => 200})
# Add your own stubs here or in specific tests...
end
```
By default stubs are shared globally, to make stubs unique to each thread, use `Excon.defaults[:stubs] = :local`.
## Instrumentation
Excon calls can be timed using the [ActiveSupport::Notifications](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) API.
```ruby
connection = Excon.new(
'http://geemus.com',
:instrumentor => ActiveSupport::Notifications
)
```
Excon will then instrument each request, retry, and error. The corresponding events are named `excon.request`, `excon.retry`, and `excon.error` respectively.
```ruby
ActiveSupport::Notifications.subscribe(/excon/) do |*args|
puts "Excon did stuff!"
end
```
If you prefer to label each event with a namespace other than "excon", you may specify
an alternate name in the constructor:
```ruby
connection = Excon.new(
'http://geemus.com',
:instrumentor => ActiveSupport::Notifications,
:instrumentor_name => 'my_app'
)
```
Note: Excon's ActiveSupport::Notifications implementation has the following event format: `<namespace>.<event>` which is the opposite of the Rails' implementation.
ActiveSupport provides a [subscriber](http://api.rubyonrails.org/classes/ActiveSupport/Subscriber.html) interface which lets you attach a subscriber to a namespace. Due to the incompability above, you won't be able to attach a subscriber to the "excon" namespace out of the box.
If you want this functionality, you can use a simple adapter such as this one:
```ruby
class ExconToRailsInstrumentor
def self.instrument(name, datum, &block)
namespace, *event = name.split(".")
rails_name = [event, namespace].flatten.join(".")
ActiveSupport::Notifications.instrument(rails_name, datum, &block)
end
end
```
If you don't want to add ActiveSupport to your application, simply define a class which implements the same `#instrument` method like so:
```ruby
class SimpleInstrumentor
class << self
attr_accessor :events
def instrument(name, params = {}, &block)
puts "#{name} just happened."
yield if block_given?
end
end
end
```
The #instrument method will be called for each HTTP request, response, retry, and error.
For debugging purposes you can also use `Excon::StandardInstrumentor` to output all events to stderr. This can also be specified by setting the `EXCON_DEBUG` ENV var.
See [the documentation for ActiveSupport::Notifications](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) for more detail on using the subscription interface. See excon's [instrumentation_test.rb](https://github.com/excon/excon/blob/master/tests/middlewares/instrumentation_tests.rb) for more examples of instrumenting excon.
## HTTPS client certificate
You can supply a client side certificate if the server requires it for authentication:
```ruby
connection = Excon.new('https://example.com',
client_cert: 'mycert.pem',
client_key: 'mycert.key',
client_key_pass: 'my pass phrase')
```
`client_key_pass` is optional.
If you already have loaded the certificate and key into memory, then pass it through like:
```ruby
client_cert_data = File.load 'mycert.pem'
client_key_data = File.load 'mycert.key'
connection = Excon.new('https://example.com',
client_cert_data: client_cert_data,
client_key_data: client_key_data)
```
This can be useful if your program has already loaded the assets through
another mechanism (E.g. a remote API call to a secure K:V system like Vault).
## HTTPS/SSL Issues
By default excon will try to verify peer certificates when using HTTPS. Unfortunately on some operating systems the defaults will not work. This will likely manifest itself as something like `Excon::Errors::CertificateError: SSL_connect returned=1 ...`
If you have the misfortune of running into this problem you have a couple options. If you have certificates but they aren't being auto-discovered, you can specify the path to your certificates:
```ruby
Excon.defaults[:ssl_ca_path] = '/path/to/certs'
```
Failing that, you can turn off peer verification (less secure):
```ruby
Excon.defaults[:ssl_verify_peer] = false
```
Either of these should allow you to work around the socket error and continue with your work.
## Getting Help
* Ask specific questions on [Stack Overflow](http://stackoverflow.com/questions/tagged/excon).
* Report bugs and discuss potential features in [Github issues](https://github.com/excon/excon/issues).
## Contributing
Please refer to [CONTRIBUTING.md](https://github.com/excon/excon/blob/master/CONTRIBUTING.md).
# Plugins and Middlewares
Using Excon's [Middleware system][middleware], you can easily extend Excon's
functionality with your own. The following plugins extend Excon in their own
way:
* [excon-addressable](https://github.com/JeanMertz/excon-addressable)
Set [addressable](https://github.com/sporkmonger/addressable) as the default
URI parser, and add support for [URI templating][templating].
* [excon-hypermedia](https://github.com/JeanMertz/excon-hypermedia)
Teaches Excon to talk with [HyperMedia APIs][hypermedia]. Allowing you to use
all of Excon's functionality, while traversing APIs in an easy and
self-discovering way.
## License
Please refer to [LICENSE.md](https://github.com/excon/excon/blob/master/LICENSE.md).
[middleware]: lib/excon/middlewares/base.rb
[hypermedia]: https://en.wikipedia.org/wiki/HATEOAS
[templating]: https://www.rfc-editor.org/rfc/rfc6570.txt
require 'rubygems'
require 'rake'
require 'date'
include Rake::DSL
#############################################################################
#
# Helper functions
#
#############################################################################
def name
@name ||= Dir['*.gemspec'].first.split('.').first
end
def version
line = File.read("lib/#{name}/constants.rb")[/^\s*VERSION\s*=\s*.*/]
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
end
def date
Date.today.to_s
end
def rubyforge_project
name
end
def gemspec_file
"#{name}.gemspec"
end
def gem_file
"#{name}-#{version}.gem"
end
def replace_header(head, header_name)
head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
end
#############################################################################
#
# Standard tasks
#
#############################################################################
require 'shindo/rake'
require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:spec, :format) do |t, args|
format = args[:format] || 'doc'
t.rspec_opts = ["-c", "-f #{format}", "-r ./spec/spec_helper.rb"]
t.pattern = 'spec/**/*_spec.rb'
end
Shindo::Rake.new
task :default => [ :tests, :test]
task :test => :spec
require 'rdoc/task'
Rake::RDocTask.new do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "#{name} #{version}"
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('lib/**/*.rb')
end
desc "Open an irb session preloaded with this library"
task :console do
sh "irb -rubygems -r ./lib/#{name}.rb"
end
#############################################################################
#
# Custom tasks (add your own tasks here)
#
#############################################################################
#############################################################################
#
# Packaging tasks
#
#############################################################################
task :release => [:update_certs, :build] do
unless `git branch` =~ /^\* master$/
puts "You must be on the master branch to release!"
exit!
end
sh "gem install pkg/#{name}-#{version}.gem"
sh "git commit --allow-empty -a -m 'Release #{version}'"
sh "git tag v#{version}"
sh "git push origin master"
sh "git push origin v#{version}"
sh "gem push pkg/#{name}-#{version}.gem"
end
task :build => :gemspec do
sh "mkdir -p pkg"
sh "gem build #{gemspec_file}"
sh "mv #{gem_file} pkg"
end
task :gemspec => :validate do
# read spec file and split out manifest section
spec = File.read(gemspec_file)
head, manifest, tail = spec.split(" # = MANIFEST =\n")
# replace name version and date
replace_header(head, :name)
replace_header(head, :version)
replace_header(head, :date)
#comment this out if your rubyforge_project has a different name
replace_header(head, :rubyforge_project)
# determine file list from git ls-files
files = `git ls-files`.
split("\n").
sort.
reject { |file| file =~ /^\./ }.
reject { |file| file =~ /^(rdoc|pkg)/ }.
map { |file| " #{file}" }.
join("\n")
# piece file back together and write
manifest = " s.files = %w[\n#{files}\n ]\n"
spec = [head, manifest, tail].join(" # = MANIFEST =\n")
File.open(gemspec_file, 'w') { |io| io.write(spec) }
puts "Updated #{gemspec_file}"
end
task :validate do
libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
unless libfiles.empty?
puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
exit!
end
unless Dir['VERSION*'].empty?
puts "A `VERSION` file at root level violates Gem best practices."
exit!
end
end
desc "update bundled certs"
task :update_certs do
require File.join(File.dirname(__FILE__), 'lib', 'excon')
File.open(File.join(File.dirname(__FILE__), 'data', 'cacert.pem'), 'w') do |file|
data = Excon.get("https://curl.haxx.se/ca/cacert.pem").body
file.write(data)
end
end
desc "check ssl settings"
task :hows_my_ssl do
require File.join(File.dirname(__FILE__), 'lib', 'excon')
data = Excon.get("https://www.howsmyssl.com/a/check").body
puts data
end
require 'rubygems'
require 'tach'
class Concatenator
def initialize(string)
@string = string
end
def call(data)
@string << data
end
end
string = "0123456789ABCDEF"
Tach.meter(100_000) do
tach('class') do
s = ""
obj = Concatenator.new(s)
10.times { obj.call(string) }
end
tach('lambda') do
s = ""
obj = lambda {|data| s << data }
10.times { obj.call(string) }
end
end
# ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-linux]
#
# +--------+----------+
# | tach | total |
# +--------+----------+
# | class | 1.450284 |
# +--------+----------+
# | lambda | 2.506496 |
# +--------+----------+
# ruby 1.8.7 (2010-12-23 patchlevel 330) [x86_64-linux]
#
# +--------+----------+
# | tach | total |
# +--------+----------+
# | class | 1.373917 |
# +--------+----------+
# | lambda | 2.589384 |
# +--------+----------+
require 'rubygems'
require 'tach'
Tach.meter(1_000_000) do
tach('concat') do
path = 'path'
path = '/' << path
end
tach('insert') do
path = 'path'
path.insert(0, '/')
end
end
# +--------+----------+
# | tach | total |
# +--------+----------+
# | insert | 0.974036 |
# +--------+----------+
# | concat | 0.998904 |
# +--------+----------+
\ No newline at end of file
require 'rubygems'
require 'tach'
key = 'Content-Length'
value = '100'
Tach.meter(1_000) do
tach('concat') do
temp = ''
temp << key << ': ' << value << "\r\n"
end
tach('interpolate') do
"#{key}: #{value}\r\n"
end
end
# +-------------+----------+
# | tach | total |
# +-------------+----------+
# | interpolate | 0.000404 |
# +-------------+----------+
# | concat | 0.000564 |
# +-------------+----------+
require 'rubygems'
require 'tach'
CR_LF = "\r\n"
Tach.meter(1_000_000) do
tach('constant') do
'' << CR_LF
end
tach('string') do
'' << "\r\n"
end
end
# +----------+----------+
# | tach | total |
# +----------+----------+
# | constant | 0.819885 |
# +----------+----------+
# | string | 0.893602 |
# +----------+----------+
\ No newline at end of file
# Copied from my benchmark_hell repo: github.com/sgonyea/benchmark_hell
require 'benchmark'
iters = 1000000
comp = "hello"
hello = "HelLo"
puts 'String#downcase == vs. String#casecmp'
Benchmark.bmbm do |x|
x.report('String#downcase1') do
iters.times.each do
hello.downcase == comp
end
end
x.report('String#downcase2') do
iters.times.each do
"HelLo".downcase == "hello"
end
end
x.report('String#downcase3') do
iters.times.each do
var = "HelLo"
var.downcase!
var == "hello"
end
end
x.report('casecmp1') do
iters.times.each do
hello.casecmp(comp).zero?
end
end
x.report('casecmp1-1') do
iters.times.each do
hello.casecmp(comp) == 0
end
end
x.report('casecmp2') do
iters.times.each do
"HelLo".casecmp(comp).zero?
end
end
x.report('casecmp2-1') do
iters.times.each do
"HelLo".casecmp(comp) == 0
end
end
end
=begin
rvm exec bash -c 'echo && echo $RUBY_VERSION && echo && ruby downcase-eq-eq_vs_casecmp.rb'
jruby-1.5.6
String#downcase == vs. String#casecmp
Rehearsal ----------------------------------------------------
String#downcase1 0.461000 0.000000 0.461000 ( 0.387000)
String#downcase2 0.269000 0.000000 0.269000 ( 0.269000)
String#downcase3 0.224000 0.000000 0.224000 ( 0.224000)
casecmp1 0.157000 0.000000 0.157000 ( 0.157000)
casecmp1-1 0.153000 0.000000 0.153000 ( 0.153000)
casecmp2 0.163000 0.000000 0.163000 ( 0.163000)
casecmp2-1 0.163000 0.000000 0.163000 ( 0.163000)
------------------------------------------- total: 1.590000sec
user system total real
String#downcase1 0.190000 0.000000 0.190000 ( 0.191000)
String#downcase2 0.225000 0.000000 0.225000 ( 0.225000)
String#downcase3 0.190000 0.000000 0.190000 ( 0.190000)
casecmp1 0.125000 0.000000 0.125000 ( 0.125000)
casecmp1-1 0.127000 0.000000 0.127000 ( 0.127000)
casecmp2 0.144000 0.000000 0.144000 ( 0.144000)
casecmp2-1 0.147000 0.000000 0.147000 ( 0.147000)
macruby-0.7.1
String#downcase == vs. String#casecmp
Rehearsal ----------------------------------------------------
String#downcase1 2.340000 0.040000 2.380000 ( 1.765141)
String#downcase2 5.510000 0.100000 5.610000 ( 3.893249)
String#downcase3 4.200000 0.080000 4.280000 ( 3.031621)
casecmp1 0.270000 0.000000 0.270000 ( 0.267613)
casecmp1-1 0.190000 0.000000 0.190000 ( 0.188848)
casecmp2 1.450000 0.020000 1.470000 ( 1.027956)
casecmp2-1 1.380000 0.030000 1.410000 ( 0.951474)
------------------------------------------ total: 15.610000sec
user system total real
String#downcase1 2.350000 0.040000 2.390000 ( 1.774292)
String#downcase2 5.890000 0.120000 6.010000 ( 4.214038)
String#downcase3 4.530000 0.090000 4.620000 ( 3.286059)
casecmp1 0.270000 0.000000 0.270000 ( 0.271119)
casecmp1-1 0.190000 0.000000 0.190000 ( 0.189462)
casecmp2 1.540000 0.030000 1.570000 ( 1.104751)
casecmp2-1 1.440000 0.030000 1.470000 ( 0.999689)
rbx-head
String#downcase == vs. String#casecmp
Rehearsal ----------------------------------------------------
String#downcase1 0.702746 0.005229 0.707975 ( 0.621969)
String#downcase2 0.701429 0.001617 0.703046 ( 0.691833)
String#downcase3 1.042835 0.002952 1.045787 ( 0.953992)
casecmp1 0.654571 0.002239 0.656810 ( 0.480158)
casecmp1-1 0.484706 0.001105 0.485811 ( 0.398601)
casecmp2 0.564140 0.001579 0.565719 ( 0.545332)
casecmp2-1 0.554889 0.001153 0.556042 ( 0.539569)
------------------------------------------- total: 4.721190sec
user system total real
String#downcase1 0.491199 0.001081 0.492280 ( 0.493727)
String#downcase2 0.631059 0.001018 0.632077 ( 0.629885)
String#downcase3 0.968867 0.002504 0.971371 ( 0.976734)
casecmp1 0.364496 0.000434 0.364930 ( 0.365262)
casecmp1-1 0.373140 0.000562 0.373702 ( 0.374136)
casecmp2 0.487644 0.001057 0.488701 ( 0.490302)
casecmp2-1 0.469868 0.001178 0.471046 ( 0.472220)
ruby-1.8.7-p330
String#downcase == vs. String#casecmp
Rehearsal ----------------------------------------------------
String#downcase1 0.780000 0.000000 0.780000 ( 0.783979)
String#downcase2 0.950000 0.000000 0.950000 ( 0.954109)
String#downcase3 0.960000 0.000000 0.960000 ( 0.960554)
casecmp1 0.440000 0.000000 0.440000 ( 0.442546)
casecmp1-1 0.490000 0.000000 0.490000 ( 0.487795)
casecmp2 0.530000 0.000000 0.530000 ( 0.535819)
casecmp2-1 0.570000 0.000000 0.570000 ( 0.574653)
------------------------------------------- total: 4.720000sec
user system total real
String#downcase1 0.780000 0.000000 0.780000 ( 0.780692)
String#downcase2 0.980000 0.010000 0.990000 ( 0.982925)
String#downcase3 0.960000 0.000000 0.960000 ( 0.961501)
casecmp1 0.440000 0.000000 0.440000 ( 0.444528)
casecmp1-1 0.490000 0.000000 0.490000 ( 0.487437)
casecmp2 0.540000 0.000000 0.540000 ( 0.537686)
casecmp2-1 0.570000 0.000000 0.570000 ( 0.574253)
ruby-1.9.2-p136
String#downcase == vs. String#casecmp
Rehearsal ----------------------------------------------------
String#downcase1 0.750000 0.000000 0.750000 ( 0.750523)
String#downcase2 1.190000 0.000000 1.190000 ( 1.193346)
String#downcase3 1.030000 0.010000 1.040000 ( 1.036435)
casecmp1 0.640000 0.000000 0.640000 ( 0.640327)
casecmp1-1 0.480000 0.000000 0.480000 ( 0.484709) # With all this crap running, some flukes pop out
casecmp2 0.820000 0.000000 0.820000 ( 0.822223)
casecmp2-1 0.660000 0.000000 0.660000 ( 0.664190)
------------------------------------------- total: 5.580000sec
user system total real
String#downcase1 0.760000 0.000000 0.760000 ( 0.759816)
String#downcase2 1.150000 0.010000 1.160000 ( 1.150792)
String#downcase3 1.000000 0.000000 1.000000 ( 1.005549)
casecmp1 0.650000 0.000000 0.650000 ( 0.644021)
casecmp1-1 0.490000 0.000000 0.490000 ( 0.494456)
casecmp2 0.820000 0.000000 0.820000 ( 0.817689)
casecmp2-1 0.680000 0.000000 0.680000 ( 0.685121)
=end
require 'rubygems' if RUBY_VERSION < '1.9'
require 'bundler'
Bundler.require(:default)
Bundler.require(:benchmark)
require 'sinatra/base'
require File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib', 'excon')
module Excon
class Server < Sinatra::Base
def self.run
Rack::Handler::WEBrick.run(
Excon::Server.new,
:Port => 9292,
:AccessLog => [],
:Logger => WEBrick::Log.new(nil, WEBrick::Log::ERROR)
)
end
get '/data/:amount' do |amount|
'x' * amount.to_i
end
end
end
def with_server(&block)
pid = Process.fork do
Excon::Server.run
end
loop do
sleep(1)
begin
Excon.get('http://localhost:9292/api/foo')
break
rescue
end
end
yield
ensure
Process.kill(9, pid)
end
require 'tach'
size = 10_000
path = '/data/' << size.to_s
url = 'http://localhost:9292' << path
times = 1_000
with_server do
Tach.meter(times) do
tach('Excon') do
Excon.get(url).body
end
excon = Excon.new(url)
tach('Excon (persistent)') do
excon.request(:method => 'get').body
end
end
end
require 'rubygems' if RUBY_VERSION < '1.9'
require 'bundler'
Bundler.require(:default)
Bundler.require(:benchmark)
require 'sinatra/base'
require File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib', 'excon')
module Excon
class Server < Sinatra::Base
def self.run
Rack::Handler::WEBrick.run(
Excon::Server.new,
:Port => 9292,
:AccessLog => [],
:Logger => WEBrick::Log.new(nil, WEBrick::Log::ERROR)
)
end
get '/data/:amount' do |amount|
'x' * amount.to_i
end
end
end
def with_server(&block)
pid = Process.fork do
Excon::Server.run
end
loop do
sleep(1)
begin
Excon.get('http://localhost:9292/api/foo')
break
rescue
end
end
yield
ensure
Process.kill(9, pid)
end
require 'em-http-request'
require 'httparty'
require 'net/http'
require 'open-uri'
require 'rest_client'
require 'tach'
require 'typhoeus'
size = 10_000
path = '/data/' << size.to_s
url = 'http://localhost:9292' << path
times = 1_000
with_server do
Tach.meter(times) do
tach('curb (persistent)') do |n|
curb = Curl::Easy.new
n.times do
curb.url = url
curb.http_get
curb.body_str
end
end
tach('em-http-request') do |n|
EventMachine.run {
count = 0
n.times do
http = EventMachine::HttpRequest.new(url).get
http.callback {
http.response
count += 1
EM.stop if count == n
}
http.errback {
http.response
count += 1
EM.stop if count == n
}
end
}
end
tach('Excon') do
Excon.get(url).body
end
excon = Excon.new(url)
tach('Excon (persistent)') do
excon.request(:method => 'get').body
end
tach('HTTParty') do
HTTParty.get(url).body
end
tach('Net::HTTP') do
# Net::HTTP.get('localhost', path, 9292)
Net::HTTP.start('localhost', 9292) {|http| http.get(path).body }
end
Net::HTTP.start('localhost', 9292) do |http|
tach('Net::HTTP (persistent)') do
http.get(path).body
end
end
tach('open-uri') do
open(url).read
end
tach('RestClient') do
RestClient.get(url)
end
streamly = StreamlyFFI::Connection.new
tach('StreamlyFFI (persistent)') do
streamly.get(url)
end
tach('Typhoeus') do
Typhoeus::Request.get(url).body
end
end
end
# +--------------------------+----------+
# | tach | total |
# +--------------------------+----------+
# | Excon (persistent) | 1.529095 |
# +--------------------------+----------+
# | curb (persistent) | 1.740387 |
# +--------------------------+----------+
# | Typhoeus | 1.876236 |
# +--------------------------+----------+
# | Excon | 2.001858 |
# +--------------------------+----------+
# | StreamlyFFI (persistent) | 2.200701 |
# +--------------------------+----------+
# | Net::HTTP | 2.395704 |
# +--------------------------+----------+
# | Net::HTTP (persistent) | 2.418099 |
# +--------------------------+----------+
# | HTTParty | 2.659317 |
# +--------------------------+----------+
# | RestClient | 2.958159 |
# +--------------------------+----------+
# | open-uri | 2.987051 |
# +--------------------------+----------+
# | em-http-request | 4.123798 |
# +--------------------------+----------+
require 'rubygems'
require 'tach'
data = ["some", "var", "goes", "in", :here, 0]
Tach.meter(1_000_000) do
tach('for') do
for element in data
element == nil
end
end
tach('each') do
data.each do |element|
element == nil
end
end
end
# ruby 1.8.7 (2009-06-12 patchlevel 174) [universal-darwin10.0]
#
# +------+----------+
# | tach | total |
# +------+----------+
# | for | 2.958672 |
# +------+----------+
# | each | 2.983550 |
# +------+----------+
#
require 'rubygems'
require 'tach'
data = {"some" => "var", "goes" => "in", :here => 0}
Tach.meter(1_000_000) do
tach('for') do
for key, values in data
key == values
end
end
tach('each') do
data.each do |key, values|
key == values
end
end
end
# ruby 1.8.7 (2009-06-12 patchlevel 174) [universal-darwin10.0]
#
# +------+----------+
# | tach | total |
# +------+----------+
# | each | 2.748909 |
# +------+----------+
# | for | 2.949512 |
# +------+----------+
#
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment