diff --git a/lib/rage/request.rb b/lib/rage/request.rb index a2f8ad24..34b6f8f6 100644 --- a/lib/rage/request.rb +++ b/lib/rage/request.rb @@ -37,6 +37,45 @@ def fresh?(etag:, last_modified:) ) end + # Returns the full URL of the request. + # @example + # request.url # => "https://example.com/users?show_archived=true" + def url + scheme = @env["rack.url_scheme"] + host = @env["SERVER_NAME"] + port = @env["SERVER_PORT"] + path = @env["PATH_INFO"] + query_string = @env["QUERY_STRING"] + + port_part = (scheme == "http" && port == "80") || (scheme == "https" && port == "443") ? "" : ":#{port}" + query_part = query_string.empty? ? "" : "?#{query_string}" + + "#{scheme}://#{host}#{port_part}#{path}#{query_part}" + end + + # Returns the path of the request. + # @example + # request.path # => "/users" + def path + @env["PATH_INFO"] + end + + # Returns the full path including the query string. + # @example + # request.fullpath # => "/users?show_archived=true" + def fullpath + path = @env["PATH_INFO"] + query_string = @env["QUERY_STRING"] + query_string.empty? ? path : "#{path}?#{query_string}" + end + + # Returns the user agent of the request. + # @example + # request.user_agent # => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" + def user_agent + headers["HTTP_USER_AGENT"] + end + private def if_none_match diff --git a/spec/rage/request_spec.rb b/spec/rage/request_spec.rb new file mode 100644 index 00000000..a48d76d4 --- /dev/null +++ b/spec/rage/request_spec.rb @@ -0,0 +1,68 @@ +require "rack" +require "time" + +RSpec.describe Rage::Request do + let(:env) do + { + "rack.upgrade?" => nil, + "rack.upgrade" => nil, + "rack.version" => [1, 3], + "SCRIPT_NAME" => "", + "rack.url_scheme" => "http", + "HTTP_VERSION" => "HTTP/1.1", + "PATH_INFO" => "/users", + "QUERY_STRING" => "show_archived=true", + "REMOTE_ADDR" => "::1", + "REQUEST_METHOD" => "GET", + "SERVER_NAME" => "localhost", + "SERVER_PROTOCOL" => "HTTP/1.1", + "IODINE_REQUEST_ID" => "QUERY_STRING", + "IODINE_HAS_BODY" => false, + "SERVER_PORT" => "3000", + "HTTP_HOST" => "localhost:3000", + "HTTP_CONNECTION" => "keep-alive", + "HTTP_USER_AGENT" => "Mozilla/5.0 (Macintosh; ...", + "HTTP_ACCEPT" => "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", + "HTTP_ACCEPT_ENCODING" => "gzip, deflate, br, zstd", + "HTTP_ACCEPT_LANGUAGE" => "en-GB,en-US;q=0.9,en;q=0.8", + "HTTP_COOKIE" => "test_cookie=1" + } + end + + subject(:request) { described_class.new(env) } + + it "returns the full URL" do + expect(request.url).to eq("http://localhost:3000/users?show_archived=true") + end + + it "returns the path" do + expect(request.path).to eq("/users") + end + + it "returns the full path with query string" do + expect(request.fullpath).to eq("/users?show_archived=true") + end + + it "returns the user agent" do + expect(request.user_agent).to eq("Mozilla/5.0 (Macintosh; ...") + end + + it "handles default ports in URL" do + env["SERVER_PORT"] = "80" + expect(request.url).to eq("http://localhost/users?show_archived=true") + + env["rack.url_scheme"] = "https" + env["SERVER_PORT"] = "443" + expect(request.url).to eq("https://localhost/users?show_archived=true") + end + + it "handles empty query string in full path" do + env["QUERY_STRING"] = "" + expect(request.fullpath).to eq("/users") + end + + it "handles missing user agent header" do + env.delete("HTTP_USER_AGENT") + expect(request.user_agent).to be_nil + end +end