Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Working with HTTP in Ruby: Tips, Tricks, and Te...
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Andrey Deryabin
November 23, 2017
Technology
860
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
45
Микросервисы. Мифы и суровая реальность
aderyabin
0
73
Microservices in Gett
aderyabin
1
390
Rove
aderyabin
0
150
Manage environment with Vagrant
aderyabin
0
87
Other Decks in Technology
See All in Technology
AI駆動開発が変える、大規模開発の前提 ーHuman in the Loop から Human on the Loop へ / AIE2026
visional_engineering_and_design
24
13k
【Gen-AX】20260530開催_JJUG CCC 2026 Spring
genax
0
430
[モダンアプリ勉強会]今更聞けないGit/GitHub入門
tsukuboshi
0
290
新アーキテクチャ「TiDB X」解説とDedicated比較 TiDB Cloud Premiumのゲーム運用活用を検証
staffrecruiter
0
120
Chart.js が簡単に使えるようになっていたので OGP 画像生成に使った話
kamekyame
0
170
AWSシリコン最前線 〜AI時代のチップ選択を読み解く〜
htokoyo
1
170
先取りMaven4 ~16年ぶりのメジャーアップデート、その進化とは?~
ogiwarat
0
150
「気づいたら仕事が終わっている」バクラクAIエージェント本番運用の裏側 / layerx-bakuraku-aie2026
yuya4
19
11k
はじめてのDatadog
kairim0
0
290
noUncheckedIndexedAccess、3時間、1万円。 / noUncheckedIndexedAccess, 3 Hours, 10,000 JPY.
kaonavi
1
310
TypeScript Compiler APIとPHP-Parserを活用し、TypeScriptとPHPで型を共有する
shuta13
0
360
DevOps Agentで始めるAWS運用 〜フロンティアエージェントが変える運用の現場〜
nyankotaro
1
280
Featured
See All Featured
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
190
My Coaching Mixtape
mlcsv
0
140
Test your architecture with Archunit
thirion
1
2.3k
Thoughts on Productivity
jonyablonski
76
5.2k
Building a Modern Day E-commerce SEO Strategy
aleyda
45
9.1k
Bridging the Design Gap: How Collaborative Modelling removes blockers to flow between stakeholders and teams @FastFlow conf
baasie
0
580
The Spectacular Lies of Maps
axbom
PRO
1
790
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
200
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
140
Technical Leadership for Architectural Decision Making
baasie
3
400
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
520
Product Roadmaps are Hard
iamctodd
PRO
55
12k
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]