├── shortcuts.rb ├── without_uri.rb ├── timeout.rb ├── basic_auth.rb ├── normal.rb ├── asynchronous.rb ├── logging_and_debugging.rb ├── custom_verb.rb ├── post_form.rb ├── cookies.rb ├── methods_and_rest.rb ├── headers.rb ├── response.rb ├── uri.rb ├── LICENSE.md ├── README.md ├── proxy.rb ├── file_upload_html_style.rb └── ssl_and_https.rb /shortcuts.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /without_uri.rb: -------------------------------------------------------------------------------- 1 | # You don't have to use URI.parse 2 | require "net/http" 3 | 4 | http = Net::HTTP.new("google.com", 80) 5 | response = http.request(Net::HTTP::Get.new("/foo/bar")) -------------------------------------------------------------------------------- /timeout.rb: -------------------------------------------------------------------------------- 1 | require "net/http" 2 | require "uri" 3 | 4 | uri = URI.parse("http://google.com/") 5 | 6 | http = Net::HTTP.new(uri.host, uri.port) 7 | http.open_timeout = 3 # in seconds 8 | http.read_timeout = 3 # in seconds 9 | http.request(Net::HTTP::Get.new(uri.request_uri)) -------------------------------------------------------------------------------- /basic_auth.rb: -------------------------------------------------------------------------------- 1 | require "net/http" 2 | require "uri" 3 | 4 | uri = URI.parse("http://google.com/") 5 | 6 | http = Net::HTTP.new(uri.host, uri.port) 7 | request = Net::HTTP::Get.new(uri.request_uri) 8 | request.basic_auth("username", "password") 9 | response = http.request(request) -------------------------------------------------------------------------------- /normal.rb: -------------------------------------------------------------------------------- 1 | require "net/http" 2 | require "uri" 3 | 4 | uri = URI.parse("http://google.com/") 5 | 6 | # Shortcut 7 | response = Net::HTTP.get_response(uri) 8 | 9 | # Will print response.body 10 | Net::HTTP.get_print(uri) 11 | 12 | # Full 13 | http = Net::HTTP.new(uri.host, uri.port) 14 | response = http.request(Net::HTTP::Get.new(uri.request_uri)) -------------------------------------------------------------------------------- /asynchronous.rb: -------------------------------------------------------------------------------- 1 | # All the APIs in Net::HTTP are synchronous. 2 | # We have to use threads. 3 | 4 | require "net/http" 5 | require "uri" 6 | 7 | Thread.new do 8 | # Do normal Net::HTTP stuff here. 9 | uri = URI.parse("http://google.com/") 10 | http = Net::HTTP.new(uri.host, uri.port) 11 | response = http.request(Net::HTTP::Get.new(uri.request_uri)) 12 | end.join 13 | 14 | -------------------------------------------------------------------------------- /logging_and_debugging.rb: -------------------------------------------------------------------------------- 1 | require "net/http" 2 | require "uri" 3 | 4 | uri = URI.parse("http://google.com/") 5 | http = Net::HTTP.new(uri.host, uri.port) 6 | 7 | http.set_debug_output($stdout) 8 | # or 9 | http.set_debug_output($stderr) 10 | # or 11 | require "logger" 12 | http.set_debug_output(Logger.new("/path/to/my.log")) 13 | 14 | response = http.request(Net::HTTP::Get.new(uri.request_uri)) -------------------------------------------------------------------------------- /custom_verb.rb: -------------------------------------------------------------------------------- 1 | require "net/http" 2 | 3 | # Varnish uses a custom PURGE verb. A simple subclass is all it takes for 4 | # Net::HTTP to send requests with this method. 5 | 6 | class Purge < Net::HTTPRequest 7 | METHOD = "PURGE" 8 | REQUEST_HAS_BODY = false 9 | RESPONSE_HAS_BODY = false 10 | end 11 | 12 | http = Net::HTTP.new("localhost", "80") 13 | response = http.request(Purge.new("/")) 14 | -------------------------------------------------------------------------------- /post_form.rb: -------------------------------------------------------------------------------- 1 | require "net/http" 2 | require "uri" 3 | 4 | uri = URI.parse("http://example.com/search") 5 | 6 | # Shortcut 7 | response = Net::HTTP.post_form(uri, {"q" => "My query", "per_page" => "50"}) 8 | 9 | # Full control 10 | http = Net::HTTP.new(uri.host, uri.port) 11 | 12 | request = Net::HTTP::Post.new(uri.request_uri) 13 | request.set_form_data({"q" => "My query", "per_page" => "50"}) 14 | 15 | # Tweak headers, removing this will default to application/x-www-form-urlencoded 16 | request["Content-Type"] = "application/json" 17 | 18 | response = http.request(request) -------------------------------------------------------------------------------- /cookies.rb: -------------------------------------------------------------------------------- 1 | require "net/http" 2 | require "uri" 3 | 4 | uri = URI.parse("http://translate.google.com/") 5 | http = Net::HTTP.new(uri.host, uri.port) 6 | 7 | # make first call to get cookies 8 | request = Net::HTTP::Get.new(uri.request_uri) 9 | 10 | response = http.request(request) 11 | 12 | # save cookies 13 | cookies = response.response['set-cookie'] 14 | 15 | 16 | # make second call 17 | request = Net::HTTP::Get.new('/#auto|en|Pardon') 18 | 19 | # add previously stored cookies 20 | request['Cookie'] = cookies 21 | 22 | response = http.request(request) 23 | 24 | cookies = response.response['set-cookie'] # => nil 25 | 26 | -------------------------------------------------------------------------------- /methods_and_rest.rb: -------------------------------------------------------------------------------- 1 | # Basic REST. 2 | # Most REST APIs will set semantic values in response.body and response.code. 3 | require "net/http" 4 | 5 | http = Net::HTTP.new("api.restsite.com") 6 | 7 | request = Net::HTTP::Post.new("/users") 8 | request.set_form_data({"users[login]" => "quentin"}) 9 | # or 10 | request.body = '{"username":"quentin"}' 11 | response = http.request(request) # Use nokogiri, hpricot, etc to parse response.body. 12 | 13 | 14 | request = Net::HTTP::Get.new("/users/1") 15 | response = http.request(request) # As with POST, the data is in response.body. 16 | 17 | 18 | request = Net::HTTP::Put.new("/users/1") 19 | request.set_form_data({"users[login]" => "changed"}) 20 | response = http.request(request) 21 | 22 | 23 | request = Net::HTTP::Delete.new("/users/1") 24 | response = http.request(request) 25 | -------------------------------------------------------------------------------- /headers.rb: -------------------------------------------------------------------------------- 1 | require "net/http" 2 | require "uri" 3 | 4 | uri = URI.parse("http://google.com/") 5 | http = Net::HTTP.new(uri.host, uri.port) 6 | 7 | request = Net::HTTP::Get.new(uri.request_uri) 8 | request["User-Agent"] = "My Ruby Script" 9 | request["Accept"] = "*/*" 10 | 11 | response = http.request(request) 12 | 13 | # Get specific header 14 | response["content-type"] 15 | # => "text/html; charset=UTF-8" 16 | 17 | # Iterate all response headers. 18 | response.each_header do |key, value| 19 | p "#{key} => #{value}" 20 | end 21 | # => "location => http://www.google.com/" 22 | # => "content-type => text/html; charset=UTF-8" 23 | # ... 24 | 25 | # Alternatively, reach into private APIs. 26 | p response.instance_variable_get("@header") 27 | # => {"location"=>["http://www.google.com/"], "content-type"=>["text/html; charset=UTF-8"], ...} 28 | -------------------------------------------------------------------------------- /response.rb: -------------------------------------------------------------------------------- 1 | require "net/http" 2 | require "uri" 3 | 4 | uri = URI.parse("http://google.com/") 5 | 6 | http = Net::HTTP.new(uri.host, uri.port) 7 | request = Net::HTTP::Get.new(uri.request_uri) 8 | 9 | response = http.request(request) 10 | 11 | response.code # => 301 12 | response.body # => The body (HTML, XML, blob, whatever) 13 | # Headers are lowercased 14 | response["cache-control"] # => public, max-age=2592000 15 | 16 | # Listing all headers 17 | response.each_header { |h| do_something(h, response[h]) } # => location = http://www.google.com/ 18 | # => content-type = text/html; charset=UTF-8 19 | # => cache-control = public, max-age=2592000 20 | # etc... 21 | -------------------------------------------------------------------------------- /uri.rb: -------------------------------------------------------------------------------- 1 | require "uri" 2 | 3 | uri = URI.parse("http://mysite.com/some_api") 4 | uri = URI.parse("https://mysite.com/thing?foo=bar") 5 | 6 | # URI will also guess the correct port 7 | URI.parse("http://foo.com").port # => 80 8 | URI.parse("https://foo.com/").port # => 443 9 | 10 | # Full reference 11 | uri = URI.parse("http://foo.com/this/is/everything?query=params") 12 | # p (uri.methods - Object.methods).sort 13 | p uri.scheme # => "http" 14 | p uri.host # => "foo.com" 15 | p uri.port # => 80 16 | p uri.request_uri # => "/this/is/everything?query=params" 17 | p uri.path # => "/this/is/everything" 18 | p uri.query # => "query=params" 19 | 20 | # There are setters as well 21 | uri.port = 8080 22 | uri.host = "google.com" 23 | uri.scheme = "ftp" 24 | p uri.to_s 25 | # => "ftp://google.com:8080/this/is/everything?query=param" -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2019 August Lilleaas 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ruby `Net::HTTP` cheat sheet 2 | 3 | A bunch of examples of various use cases, implemented with Ruby's `Net::HTTP` library. 4 | 5 | # Alternatives to `Net::HTTP` 6 | 7 | This cheat sheet was created many years ago, when invoking `Net::HTTP` directly was common. These days, there are better alternatives around, with much nicer APIs. 8 | 9 | Compare multipart file uploads with `Net::HTTP`: 10 | 11 | ```ruby 12 | BOUNDARY = "AaB03x" 13 | 14 | uri = URI.parse("http://something.com/uploads") 15 | file = "/path/to/your/testfile.txt" 16 | 17 | post_body = [] 18 | post_body << "--#{BOUNDARY}\r\n" 19 | post_body << "Content-Disposition: form-data; name=\"datafile\"; filename=\"#{File.basename(file)}\"\r\n" 20 | post_body << "Content-Type: text/plain\r\n" 21 | post_body << "\r\n" 22 | post_body << File.read(file) 23 | post_body << "\r\n--#{BOUNDARY}--\r\n" 24 | 25 | http = Net::HTTP.new(uri.host, uri.port) 26 | request = Net::HTTP::Post.new(uri.request_uri) 27 | request.body = post_body.join 28 | request["Content-Type"] = "multipart/form-data, boundary=#{BOUNDARY}" 29 | 30 | http.request(request) 31 | ``` 32 | 33 | And file uploads with RestClient - just a single line, and no shoddy manual string concatenation: 34 | 35 | ```ruby 36 | RestClient.post '/data', :myfile => File.new("/path/to/image.jpg", 'rb') 37 | ``` 38 | 39 | Check out RestClient! https://github.com/rest-client/rest-client 40 | -------------------------------------------------------------------------------- /proxy.rb: -------------------------------------------------------------------------------- 1 | require 'net/http' 2 | require 'uri' 3 | 4 | uri = URI.parse('http://google.com') 5 | 6 | # Net::HTTP will automatically create a proxy from the http_proxy environment variable if it is present. 7 | ENV['http_proxy'] # => "http://myproxy.com:8080" 8 | 9 | http = Net::HTTP.new(uri.host, uri.port) 10 | 11 | # This request uses proxy. 12 | request = Net::HTTP::Get.new(uri.request_uri) 13 | response = http.request(request) 14 | 15 | # But it does not work without a Net::HTTP object. 16 | # This request doest not use proxy. 17 | response = Net::HTTP.get_response(uri) 18 | 19 | 20 | # You can pass proxy address to Net::HTTP constructor too. 21 | proxy_uri = URI.parse('http://myproxy.com:8080') 22 | 23 | http = Net::HTTP.new(uri.host, uri.port, proxy_uri.host, proxy_uri.port) 24 | 25 | request = Net::HTTP::Get.new(uri.request_uri) 26 | response = http.request(request) 27 | 28 | 29 | # If you are using an authenticated proxy, use Net::HTTP.start method. 30 | Net::HTTP.start(uri.host, uri.port, proxy_uri.host, proxy_uri.port, 'proxy_user', 'proxy_pass') do |http| 31 | request = Net::HTTP::Get.new(uri.request_uri) 32 | response = http.request(request) 33 | end 34 | 35 | # If you want to reuse Net::HTTP instance, don't forget to finish HTTP connection. 36 | http = Net::HTTP.start(uri.host, uri.port, proxy_uri.host, proxy_uri.port, 'proxy_user', 'proxy_pass').start 37 | 38 | request = Net::HTTP::Get.new(uri.request_uri) 39 | response = http.request(request) 40 | 41 | # Finish HTTP connection. 42 | http.finish if http.started? 43 | -------------------------------------------------------------------------------- /file_upload_html_style.rb: -------------------------------------------------------------------------------- 1 | require "net/http" 2 | require "uri" 3 | 4 | # Token used to terminate the file in the post body. Make sure it is not 5 | # present in the file you're uploading. 6 | BOUNDARY = "AaB03x" 7 | 8 | uri = URI.parse("http://something.com/uploads") 9 | file = "/path/to/your/testfile.txt" 10 | 11 | post_body = [] 12 | post_body << "--#{BOUNDARY}\r\n" 13 | post_body << "Content-Disposition: form-data; name=\"datafile\"; filename=\"#{File.basename(file)}\"\r\n" 14 | post_body << "Content-Type: text/plain\r\n" 15 | post_body << "\r\n" 16 | post_body << File.read(file) 17 | post_body << "\r\n--#{BOUNDARY}--\r\n" 18 | 19 | http = Net::HTTP.new(uri.host, uri.port) 20 | request = Net::HTTP::Post.new(uri.request_uri) 21 | request.body = post_body.join 22 | request["Content-Type"] = "multipart/form-data, boundary=#{BOUNDARY}" 23 | 24 | http.request(request) 25 | 26 | # Alternative method, using Nick Sieger's multipart-post gem 27 | require "rubygems" 28 | require "net/http/post/multipart" 29 | 30 | request = Net::HTTP::Post::Multipart.new uri.request_uri, "file" => UploadIO.new(file, "application/octet-stream") 31 | http = Net::HTTP.new(uri.host, uri.port) 32 | http.request(request) 33 | 34 | # Another alternative, using Rack 1.3 + 35 | require 'rack' 36 | uri = URI.parse("http://something.com/uploads") 37 | http = Net::HTTP.new(uri.host, uri.port) 38 | request = Net::HTTP::Post.new(uri.request_uri) 39 | 40 | request.body = Rack::Multipart::Generator.new( 41 | "form_text_field" => "random text here", 42 | "file" => Rack::Multipart::UploadedFile.new(path_to_file, file_mime_type) 43 | ).dump 44 | request.content_type = "multipart/form-data, boundary=#{Rack::Multipart::MULTIPART_BOUNDARY}" 45 | 46 | http.request(request) 47 | 48 | http.start do |connection| 49 | response = retrying_request(connection, request) 50 | end 51 | -------------------------------------------------------------------------------- /ssl_and_https.rb: -------------------------------------------------------------------------------- 1 | require "net/https" 2 | require "uri" 3 | 4 | # A regular-ish https request. 5 | # 6 | # ssltest7.bbtest.net is Thawte's SSL test site. Net::HTTP will use the CA 7 | # certificates installed on your system by default, which most likely includes 8 | # the Thawte cert that signed ssltest7.bbtest.net. 9 | http = Net::HTTP.new("ssltest7.bbtest.net", 443) 10 | http.use_ssl = true 11 | http.verify_mode = OpenSSL::SSL::VERIFY_PEER 12 | 13 | response = http.request(Net::HTTP::Get.new("/")) 14 | response.body 15 | response.status 16 | # .. do normal Net::HTTP response stuff here (see separate cheat sheet entry) 17 | 18 | # You can specify custom CA certs. If your production system only connects to 19 | # one particular server, you should specify these, and bundle them with your 20 | # app, so that you don't depend OS level pre-installed certificates in the 21 | # production environment. 22 | http = Net::HTTP.new("verysecure.com", 443) 23 | http.use_ssl = true 24 | http.verify_mode = OpenSSL::SSL::VERIFY_PEER 25 | 26 | store = OpenSSL::X509::Store.new 27 | store.set_default_paths # Optional method that will auto-include the system CAs. 28 | store.add_cert(OpenSSL::X509::Certificate.new(File.read("/path/to/ca1.crt"))) 29 | store.add_cert(OpenSSL::X509::Certificate.new(File.read("/path/to/ca2.crt"))) 30 | store.add_file("/path/to/ca3.crt") # Alternative syntax for adding certs. 31 | http.cert_store = store 32 | 33 | response = http.request(Net::HTTP::Get.new("/")) 34 | 35 | 36 | # Client certificate example. Some servers use this to authorize the connecting 37 | # client, i.e. you. The server you connect to gets the certificate you specify, 38 | # and they can use it to check who signed the certificate, and use the 39 | # certificate fingerprint to identify exactly which certificate you're using. 40 | http = Net::HTTP.new("verysecure.com", 443) 41 | http.use_ssl = true 42 | http.verify_mode = OpenSSL::SSL::VERIFY_PEER 43 | http.key = OpenSSL::PKey::RSA.new(File.read("/path/to/client.key"), "optional passphrase argument") 44 | http.cert = OpenSSL::X509::Certificate.new(File.read("/path/to/client.crt")) 45 | 46 | response = http.request(Net::HTTP::Get.new("/")) 47 | 48 | 49 | # You can also skip verification. This is almost certainly a bad idea, read more 50 | # here: 51 | # http://www.rubyinside.com/how-to-cure-nethttps-risky-default-https-behavior-4010.html 52 | http.verify_mode = OpenSSL::SSL::VERIFY_NONE 53 | --------------------------------------------------------------------------------