Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Working with HTTP in Ruby: Tips, Tricks, and Te...
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Andrey Deryabin
November 23, 2017
Technology
980
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Working with HTTP in Ruby: Tips, Tricks, and Techniques.
Slides from "Fall Saint P Ruby Meetup" meetup (23.11.2017)
Andrey Deryabin
November 23, 2017
More Decks by Andrey Deryabin
See All by Andrey Deryabin
Working with HTTP in Ruby: Tips, Tricks, and Techniques.
aderyabin
0
48
Микросервисы. Мифы и суровая реальность
aderyabin
0
77
Microservices in Gett
aderyabin
1
400
Rove
aderyabin
0
150
Manage environment with Vagrant
aderyabin
0
100
Other Decks in Technology
See All in Technology
高負荷プロダクション環境におけるAWS Lambdaのリアル 〜スケールとコストを左右する実行ライフサイクルの技術仕様〜
maimyyym
2
850
2026-09-26 Platform Engineering Kaigi 2026 インフラとアプリの境界線と委譲の設計 / Drawing the Infra and App Line
masasuzu
0
570
AI Made Us Faster at Solving the Wrong Problems
marceloancelmo
0
130
キャリアLT今日までそして明日から
kentapapa
1
140
全社共通データ基盤をつくる。ソニーのDatabricks活用とデータガバナンス設計の裏側
sony
0
290
OpenClawでAzure DevOpsのWiki更新を自動化する - クラウドAIだけでは届かない場所へ
yutakaosada
0
140
組み立てて楽しむ AWS Blocks 入門
kmiya84377
0
130
[2026 Oracle Technical Deep Dive] AI時代のデータベース基盤をどう選ぶ? Exadata Database Serviceの選択肢と使い分け (2026年9月17日開催)
oracle4engineer
PRO
0
180
ビジネスを止めない技術的負債の返済のための戦略とその手法 - 技術的負債と向き合う / Complexity and Simplicity
soudai
PRO
2
410
LLM機能を自作して分かるSnowflake Cortex AIの強み
nayuts
0
180
形式手法を使って仕様をコーディングしよう
mikanichinose
0
160
AWS FinOps Agent 結局何が得意なの?
siromi
0
230
Featured
See All Featured
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
590
Statistics for Hackers
jakevdp
799
230k
Six Lessons from altMBA
skipperchong
29
4.5k
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.8k
Design of three-dimensional binary manipulators for pick-and-place task avoiding obstacles (IECON2024)
konakalab
0
610
Crafting Experiences
bethany
1
360
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
Future Trends and Review - Lecture 12 - Web Technologies (1019888BNR)
signer
PRO
0
3.8k
Building AI with AI
inesmontani
PRO
1
1.3k
[SF Ruby Conf 2025] Rails X
palkan
3
1.4k
Prompt Engineering for Job Search
mfonobong
0
470
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
270
Transcript
HTTP in Ruby: Tips, Tricks and Techniques Andrey Deryabin
Andrey Deryabin
None
None
EVIL MARTIANS
EVIL MARTIANS
Промедление смерти подобно
HTTP Hypertext Transfer Protocol
History – HTTP/1.1 (v1) - 1999 (RFCs 2616, 7230, 7231,
7232, 7233, 7234 and 7235) – HTTP/2.0 -2015 (RFC 7540)
HTTP 1.1 – most popular protocol in WEB – a
stateless – request => response
HTTP/2 – Binary framing parsing and encoding – Query multiplexing
– Headers Compression – Connection and stream management – And more and more
curl -v https: //google.com > GET / HTTP/1.1 > Host:
google.com > User-Agent: curl/7.51.0 > Accept: */* > < HTTP/1.1 302 Found < Cache-Control: private < Content-Type: text/html; charset=UTF-8 < Location: https: // www.google.es/?gfe_rd=cr&dcr=0&ei=XIINWr2XIuis8wfFipPwDQ < Content-Length: 269 < <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8"> <TITLE>302 Moved </TITLE> </HEAD><BODY> <H1>302 Moved </H1> The document has moved <A HREF="https: // www.google.es/? gfe_rd=cr&dcr=0&ei=XIINWr2XIuis8wfFipPwDQ">here </A>. </BODY> </HTML> Method Resource Protocol Request Response Body Headers
What is about ?
How many Ruby HTTP clients do you know?
Ruby HTTP Clients Net::HTTP RestClient Faraday Ethcon Curb Typhoeus HTTP.rb
HTTPClient HTTParty Patron EM-HTTP-Request Excon
Identical
Ruby HTTP Clients – Support GET/POST/PUT/DELETE methods – Follow redirect
– Proxy – HTTP Auth – Compression – etc
What are the differences?
Engine?
Ruby HTTP Clients Faraday Ethcon Curb Typhoeus Patron Libcurl EM-HTTP-Request
Excon Event machine HTTParty Net::HTTP RestClient HTTP.rb HTTPClient TCPSocket Net::HTTP
DSL?
What else?
Feature (Bugs)!!!
Net::HTTP # request uri = URI.parse('http: //localhost:4567/json') http = Net
::HTTP.new(uri.host, uri.port) request = Net ::HTTP ::Post.new(uri.request_uri, 'Content-Type' => 'text/json') request.body = { ‘first_name' => 'Leo', ‘last_name' => 'Messi' }.to_json http.request(request) # response [201, { "Сontent-length" => "7", 'Connection' => "Barca" }, "Created"]
Net::HTTP { "content-type" =>"text/html;charset=utf-8", "connection" =>"Barca, close", "x-xss-protection" =>"1; mode=block",
"x-content-type-options" =>"nosniff", "x-frame-options" =>"SAMEORIGIN", "content-length" =>"7" } WHAT?
Net::HTTP # [201, { "Сontent-length" => "7", 'Connection' => "Barca"
}, "Created"] { "content-type" =>"text/html;charset=utf-8", "connection" =>"Barca, close", "x-xss-protection" =>"1; mode=block", "x-content-type-options" =>"nosniff", "x-frame-options" =>"SAMEORIGIN", "content-length" =>"7" } WHAT?
RFC 2616 - "Hypertext Transfer Protocol -- HTTP/1.1", Section 4.2,
"Message Headers" Each header field consists of a name followed by a colon (":") and the field value. Field names are case- insensitive
HTTPClient Documented? NO!
Typhoeus Typhoeus ::Request.new('localhost:4567/?lang=ruby&author=matz', method: :get).run
Typhoeus # request headers {"User-Agent" =>"Typhoeus - https: //github.com/typhoeus/typhoeus”} WHAT?
Typhoeus Default User-Agent?
Typhoeus
Typhoeus
Fail story 1
Groupon
# Simplifed ERB <%= Ipgeobase.lookup(current_ip).city %>
# https: //github.com/mokevnin/ipgeobase/blob/master/lib/ipgeobase.rb require 'uri' require 'open-uri' module Ipgeobase URL
= 'http: //ipgeobase.ru:7020/geo' autoload 'IpMetaData', 'ipgeobase/ip_meta_data' def self.lookup(ip, params = {}) uri = URI.parse(URL) uri.query = URI.encode_ www_form :ip => ip resp = open(uri, params).read() IpMetaData.parse(resp.to_s) end end
Use timeouts
Fail story 2
Useless box
TopSecretProjectMlApi ::AdaptiveFeed .perform({id:"15234", date: “2017-11-23"})
What is it? API call? What JSON do you send?
Epic thinking face
Control HTTP traffic
Command line tools
Tcpdump
GUI applications
Charles (Web debug proxy application)
Does it developer friendly?
puts "no"
Requirements – Log all requests – Analyse requests – Understand
what is under the hood
gem install sniffer
Sniffer supports most popular HTTP clients: Net::HTTP HTTP HTTPClient Patron
Curb Ethan Typhoeus
Disabled by default
Sniffer.enable!
Easy configuration
Sniffer.config do logger: Logger.new($stdout), severity: Logger ::Severity ::DEBUG, # HTTP
options to log log: { request_url: true, request_headers: true, request_body: true, request_method: true, response_status: true, response_headers: true, response_body: true, timing: true }, store: true, # save requests/responses to Sniffer.data enabled: false # Sniffer disabled by default end
Time to play
pry(main)> Sniffer.enable!; pry(main)> client = Elasticsearch ::Client.new; pry(main)> client.search q:
'Saint P Ruby'; D, [2017-11-21T21:13:25.120892 #87793] DEBUG -- : {"port": 9200,"host":"localhost","query":"/_search? q=Saint+P+Ruby","rq_content_type":"application/ json","rq_user_agent":"Faraday v0.9.2","rq_accept_encoding":"gzip;q=1.0,deflate;q=0.6,identity;q=0 .3","rq_accept":"*/ *","rq_connection":"close","method":"GET","request_body":"","status ":200,"rs_content_type":"application/json; charset=UTF-8","rs_content_length":"124","timing": 0.49408299988135695,"response_body":"{\"took\": 224,\"timed_out\":false,\"_shards\":{\"total\":5,\"successful\": 5,\"failed\":0},\"hits\":{\"total\":0,\"max_score\":null,\"hits\": []}}"} Example
{ "port":9200, "host":"localhost", "query":"/_search?q=Saint+P+Ruby", "rq_content_type":"application/json", "rq_user_agent":"Faraday v0.9.2", "rq_accept_encoding":"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "rq_accept":"*/*", "rq_connection":"close",
"method":"GET", "request_body":"", "status":200, "rs_content_type":"application/json; charset=UTF-8", "rs_content_length":"124", "timing":0.49408299988135695, "response_body":"{\"took\":224,\"timed_out\":false,\"_shards\":{\"total\": 5,\"successful\":5,\"failed\":0},\"hits\":{\"total\":0,\"max_score\":null, \"hits\":[]}}" } Logging
Sniffer.data.first.request => #<Sniffer ::DataItem ::Request:0x00007fbe880cd700 @body="", @headers= {"content-type" =>"application/json", "user-agent"
=>"Faraday v0.11.0", "accept- encoding" =>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "accept" =>"*/*", "connection" =>"close"}, @host="localhost", @method="GET", @port=9200, @query="/_search?q=Saint+P+Ruby"> Analyze request
Sniffer.data.first.request.headers => {"content-type" =>"application/json", "user-agent" =>"Faraday v0.9.2", "accept-encoding" =>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "accept"
=>"*/*", “connection" =>"close"} Sniffer.data.first.request.method => "GET" Analyze request
Sniffer.data.first.response => #<Sniffer ::DataItem ::Response:0x00007fbe86078658 @body= "{\"took\":989,\"timed_out\":false,\"_shards\":{\"total\": 5,\"successful\":5,\"failed\":0},\"hits\":{\"total\": 0,\"max_score\":null,\"hits\":[]}}", @headers=
{"content-type" =>"application/json; charset=UTF-8", "content-length" =>"124"}, @status=200, @timing=1.6817439999431372> Analyze response
Sniffer.data.first.response.body => “{\"took\":224,\"timed_out\":false,\"_shards\":{\"total\": 5,\"successful\":5,\"failed\":0},\"hits\":{\"total\": 0,\"max_score\":null,\"hits\":[]}}" Sniffer.data.first.response.headers => {"content-type" =>"application/json; charset=UTF-8",
“content- length" =>"124"} Sniffer.data.first.response.timing => 0.49408299988135695 Analyze response
Log Management
require 'elasticsearch' require 'sniffer' Sniffer.config.logger = Logger.new('sniffer.log') Sniffer.enable! client =
Elasticsearch ::Client.new; client.cluster.health client.search q: 'Saint P Ruby'; sudo remote_syslog -p 300 \n -d logs.papertrailapp.com \n sniffer.log
None
require 'elasticsearch' require 'sniffer' require 'le' Sniffer.config.logger = Le.new('<token>') Sniffer.enable!
client = Elasticsearch ::Client.new; client.cluster.health client.search q: 'Saint P Ruby';
None
Resume
Read a source code
Write open source
Use Sniffer!
None
Thanks! aderyabin @aderyabin @evilmartians evilmartians.com
[email protected]