nonopolarity
nonopolarity

Reputation: 150986

How to use standard library to parse URL params in Ruby?

I am using the following code with "uri" and "CGI" to parse the params of the URL:

require 'socket'
require 'uri'
require 'CGI'

server = TCPServer.new 8888

while session = server.accept
  request = session.gets

  p "request", request

  url = "http://somewebsite.com" + request.sub("GET ", "").sub(" HTTP/1.1", "").gsub(/(\r|\n)/, "")
  uri = URI(url)
  params = CGI.parse(uri.query)

  p "params", params

  session.print "HTTP/1.1 200\r\n" # 1
  session.print "Content-Type: text/html\r\n" # 2
  session.print "\r\n" # 3

  session.print "Hello world! The time is #{Time.now}" #4

  session.close
end

I had to "make up" a full URL by adding the http://somewebsite.com to the path, and use uri and CGI functions to do it. If the browser uses http://localhost:8888/?a=123&b=hello then it works well. But if the browser tried to access http://localhost:8888/favicon.ico or http://localhost:8888 then it broke right away, saying cannot split. (failing at CGI.parse(uri.query))

I can change the line to

params = uri.query ? CGI.parse(uri.query) : nil

but the whole thing seems a bit hacky, needing to make up a URL and then CGI.parse would break if query doesn't exist. Is there actually a better way to use standard library to do it? (should something else be used instead of uri and CGI?)

(Using standard library has the advantage of automatically handling the cases for %20 and multiple params as in http://localhost:8888/?a=123&b=hello%20world&b=good, giving

{"a"=>["123"], "b"=>["hello world", "good"]}

as the result.)

Upvotes: 1

Views: 743

Answers (2)

max
max

Reputation: 101891

You could also just use Rack if you don't want to learn about reinventing the CGI wheel. Rack is not technically part of the standard library but is as close as you get.

# Gemfile.rb
gem 'rack'

run $ bundle install.

# application.rb
class Application
  # This is the main entry point for Rack
  # @param env [Hash]
  # @return [Array] status, headers, body
  # @see https://www.rubydoc.info/gems/rack/Rack/Request
  # @see https://www.rubydoc.info/gems/rack/Rack/Response
  def self.call(env)
    request = Rack::Request.new(env)
    Rack::Response.new(
      "Hello " + request.params["name"] || "World",
      200,
      { "Content-Type" => "text/plain" }
    ).finish
  end
end

request.params is a hash that contains query string parameters and parameters from the request body for POST requests.

# config.ru
require 'rack'
require_relative 'application'
run Application

Run $ rackup to start the server.

Upvotes: 1

lacostenycoder
lacostenycoder

Reputation: 11206

Why do you need to make up anything? You don't need a full URI to use CGI.parse. Something like this should work:

require 'socket'
require 'CGI'

server = TCPServer.new 8888

while session = server.accept
  request = session.gets

  method, full_path = request.split(' ')
  path, params = full_path.split('?')

  params = CGI.parse(params.gsub('?','')) if params

  session.print "HTTP/1.1 200\r\n" # 1
  session.print "Content-Type: text/html\r\n" # 2
  session.print "\r\n" # 3
  session.print "Hello world! The time is #{Time.now}" #4
  session.print "\nparams: #{params}"

  p "params:", params

  session.close
end

Upvotes: 1

Related Questions