require"forwardable"require"base64"require"time"require"http/errors"require"http/headers"require"http/request/caching"require"http/request/writer"require"http/version"require"http/uri"moduleHTTPclassRequestextendForwardableincludeHTTP::Headers::Mixin# The method given was not understoodclassUnsupportedMethodError<RequestError;end# The scheme of given URI was not understoodclassUnsupportedSchemeError<RequestError;end# Default User-Agent header valueUSER_AGENT="http.rb/#{HTTP::VERSION}".freeze# RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1METHODS=[:options,:get,:head,:post,:put,:delete,:trace,:connect]# RFC 2518: HTTP Extensions for Distributed Authoring -- WEBDAVMETHODS.concat[:propfind,:proppatch,:mkcol,:copy,:move,:lock,:unlock]# RFC 3648: WebDAV Ordered Collections ProtocolMETHODS.concat[:orderpatch]# RFC 3744: WebDAV Access Control ProtocolMETHODS.concat[:acl]# draft-dusseault-http-patch: PATCH Method for HTTPMETHODS.concat[:patch]# draft-reschke-webdav-search: WebDAV SearchMETHODS.concat[:search]# Allowed schemesSCHEMES=[:http,:https,:ws,:wss]# Default ports of supported schemesPORTS={:http=>80,:https=>443,:ws=>80,:wss=>443}# Method is given as a lowercase symbol e.g. :get, :postattr_reader:verb# Scheme is normalized to be a lowercase symbol e.g. :http, :httpsattr_reader:scheme# "Request URI" as per RFC 2616# http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.htmlattr_reader:uriattr_reader:proxy,:body,:version# :nodoc:definitialize(verb,uri,headers={},proxy={},body=nil,version="1.1")# rubocop:disable ParameterLists@verb=verb.to_s.downcase.to_sym@uri=HTTP::URI.parse(uri).normalize@scheme=@uri.scheme&&@uri.scheme.to_s.downcase.to_symfail(UnsupportedMethodError,"unknown method: #{verb}")unlessMETHODS.include?(@verb)fail(UnsupportedSchemeError,"unknown scheme: #{scheme}")unlessSCHEMES.include?(@scheme)@proxy=proxy@body=body@version=version@headers=HTTP::Headers.coerce(headers||{})@headers["Host"]||=default_host_header_value@headers["User-Agent"]||=USER_AGENTend# Returns new Request with updated uridefredirect(uri,verb=@verb)req=self.class.new(verb,@uri.join(uri),headers,proxy,body,version)req["Host"]=req.uri.hostreqend# Stream the request to a socketdefstream(socket)include_proxy_authorization_headerifusing_authenticated_proxy?&&!@uri.https?Request::Writer.new(socket,body,headers,headline).streamend# Is this request using a proxy?defusing_proxy?proxy&&proxy.keys.size>=2end# Is this request using an authenticated proxy?defusing_authenticated_proxy?proxy&&proxy.keys.size==4end# Compute and add the Proxy-Authorization headerdefinclude_proxy_authorization_headerheaders["Proxy-Authorization"]=proxy_authorization_headerenddefproxy_authorization_headerdigest=Base64.strict_encode64("#{proxy[:proxy_username]}:#{proxy[:proxy_password]}")"Basic #{digest}"end# Setup tunnel through proxy for SSL requestdefconnect_using_proxy(socket)Request::Writer.new(socket,nil,proxy_connect_headers,proxy_connect_header).connect_through_proxyend# Compute HTTP request header for direct or proxy requestdefheadlinerequest_uri=using_proxy??uri:uri.omit(:scheme,:authority)"#{verb.to_s.upcase}#{request_uri} HTTP/#{version}"end# @deprecated Will be removed in 1.0.0alias_method:request_header,:headline# Compute HTTP request header SSL proxy connectiondefproxy_connect_header"CONNECT #{host}:#{port} HTTP/#{version}"end# Headers to send with proxy connect requestdefproxy_connect_headersconnect_headers=HTTP::Headers.coerce("Host"=>headers["Host"],"User-Agent"=>headers["User-Agent"])connect_headers["Proxy-Authorization"]=proxy_authorization_headerifusing_authenticated_proxy?connect_headersend# Host for tcp socketdefsocket_hostusing_proxy??proxy[:proxy_address]:hostend# Port for tcp socketdefsocket_portusing_proxy??proxy[:proxy_port]:portend# @return [HTTP::Request::Caching]defcachingCaching.newselfendprivate# @!attribute [r] host# @return [String]def_delegator:@uri,:host# @!attribute [r] port# @return [Fixnum]defport@uri.port||@uri.default_portend# @return [String] Default host (with port if needed) header value.defdefault_host_header_valuePORTS[@scheme]!=port?"#{host}:#{port}":hostendendend