Module: Descope::Mixins::HTTP
Overview
HTTP-related methods
Constant Summary collapse
- DEFAULT_RETRIES =
3- MAX_ALLOWED_RETRIES =
10- MAX_REQUEST_RETRY_JITTER =
250- MAX_REQUEST_RETRY_DELAY =
1000- MIN_REQUEST_RETRY_DELAY =
250- BASE_DELAY =
100
Constants included from Common
Common::COOKIE_DATA_NAME, Common::DEFAULT_BASE_URL, Common::DEFAULT_JWT_VALIDATION_LEEWAY, Common::DEFAULT_TIMEOUT_SECONDS, Common::PHONE_REGEX, Common::REDIRECT_LOCATION_COOKIE_NAME, Common::REFRESH_SESSION_COOKIE_NAME, Common::REFRESH_SESSION_TOKEN_NAME, Common::SESSION_COOKIE_NAME, Common::SESSION_TOKEN_NAME
Instance Attribute Summary collapse
-
#base_uri ⇒ Object
Returns the value of attribute base_uri.
-
#headers ⇒ Object
Returns the value of attribute headers.
-
#retry_count ⇒ Object
Returns the value of attribute retry_count.
-
#timeout ⇒ Object
Returns the value of attribute timeout.
Instance Method Summary collapse
- #add_headers(h = {}) ⇒ Object
- #call(method, url, timeout, headers, body = nil) ⇒ Object
- #encode_uri(uri) ⇒ Object
- #parse_cookie_value(cookie_header, cookie_name) ⇒ Object
- #request(method, uri, body = {}, extra_headers = {}) ⇒ Object
- #request_with_retry(method, uri, body = {}, extra_headers = {}, pswd = nil) ⇒ Object
- #retry_options ⇒ Object
- #safe_parse_json(body, cookies: {}, headers: {}) ⇒ Object
- #url(path) ⇒ Object
Methods included from Common
#deep_copy, #get_method_string
Instance Attribute Details
#base_uri ⇒ Object
Returns the value of attribute base_uri.
12 13 14 |
# File 'lib/descope/mixins/http.rb', line 12 def base_uri @base_uri end |
#headers ⇒ Object
Returns the value of attribute headers.
12 13 14 |
# File 'lib/descope/mixins/http.rb', line 12 def headers @headers end |
#retry_count ⇒ Object
Returns the value of attribute retry_count.
12 13 14 |
# File 'lib/descope/mixins/http.rb', line 12 def retry_count @retry_count end |
#timeout ⇒ Object
Returns the value of attribute timeout.
12 13 14 |
# File 'lib/descope/mixins/http.rb', line 12 def timeout @timeout end |
Instance Method Details
#add_headers(h = {}) ⇒ Object
114 115 116 117 118 119 |
# File 'lib/descope/mixins/http.rb', line 114 def add_headers(h = {}) raise ArgumentError, 'Headers must be an object which responds to #to_hash' unless h.respond_to?(:to_hash) @headers ||= {} @headers.merge!(h.to_hash) end |
#call(method, url, timeout, headers, body = nil) ⇒ Object
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/descope/mixins/http.rb', line 165 def call(method, url, timeout, headers, body = nil) RestClient::Request.execute( method: method, url: url, timeout: timeout, headers: headers, payload: body ) rescue RestClient::Exception => e case e when RestClient::RequestTimeout raise Descope::RequestTimeout.new(e.) else return e.response end end |
#encode_uri(uri) ⇒ Object
104 105 106 107 108 |
# File 'lib/descope/mixins/http.rb', line 104 def encode_uri(uri) encoded_uri = base_uri ? Addressable::URI.parse(uri).normalize : Addressable::URI.escape(uri) @logger.debug "will call #{url(encoded_uri)}" url(encoded_uri) end |
#parse_cookie_value(cookie_header, cookie_name) ⇒ Object
96 97 98 99 100 101 102 |
# File 'lib/descope/mixins/http.rb', line 96 def (, ) # Extract cookie value from Set-Cookie header # Format: "cookieName=cookieValue; attribute1=value1; attribute2=value2" # Only match valid cookie value characters (RFC 6265: exclude whitespace, semicolon, comma) match = .match(/#{Regexp.escape()}=([^;]+)/) match ? match[1].strip : nil end |
#request(method, uri, body = {}, extra_headers = {}) ⇒ Object
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/descope/mixins/http.rb', line 127 def request(method, uri, body = {}, extra_headers = {}) # @headers is getting the authorization header merged in initializer.rb headers_debug = @headers.dup if headers_debug['Authorization'] headers_debug['Authorization'] = headers_debug['Authorization'].gsub(/(.{10})\z/, '***********') end @logger.debug "base url: #{@base_uri}" @logger.debug "request method: #{method}, uri: #{uri}, body: #{body}, extra_headers: #{extra_headers}, headers: #{headers_debug}" result = case method when :get get_headers = @headers.merge({ params: body }).merge(extra_headers) call(:get, encode_uri(uri), timeout, get_headers) when :delete delete_headers = @headers.merge({ params: body }) call(:delete, encode_uri(uri), timeout, delete_headers) else call(method, encode_uri(uri), timeout, @headers, body.to_json) end raise Descope::Unsupported.new('No response from server', code: 400) unless result.respond_to?(:code) @logger.info("API Request: [#{method}] #{uri} - Response Code: #{result.code}") case result.code when 200...226 then safe_parse_json(result.body, cookies: result., headers: result.headers) when 400 then raise Descope::BadRequest.new(result.body, code: result.code, headers: result.headers) when 401 then raise Descope::Unauthorized.new(result.body, code: result.code, headers: result.headers) when 403 then raise Descope::AccessDenied.new(result.body, code: result.code, headers: result.headers) when 404 then raise Descope::NotFound.new(result.body, code: result.code, headers: result.headers) when 405 then raise Descope::MethodNotAllowed.new(result.body, code: result.code, headers: result.headers) when 429 then raise Descope::RateLimitException.new(result.body, code: result.code, headers: result.headers) when 500 then raise Descope::ServerError.new(result.body, code: result.code, headers: result.headers) else raise Descope::Unsupported.new(result.body, code: result.code, headers: result.headers) end end |
#request_with_retry(method, uri, body = {}, extra_headers = {}, pswd = nil) ⇒ Object
121 122 123 124 125 |
# File 'lib/descope/mixins/http.rb', line 121 def request_with_retry(method, uri, body = {}, extra_headers = {}, pswd = nil) Retryable.retryable() do request(method, uri, body, extra_headers) end end |
#retry_options ⇒ Object
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/descope/mixins/http.rb', line 31 def sleep_timer = lambda do |attempt| wait = BASE_DELAY * (2**attempt - 1) # Exponential delay with each subsequent request attempt. wait += rand(wait + 1..wait + MAX_REQUEST_RETRY_JITTER) # Add jitter to the delay window. wait = [MAX_REQUEST_RETRY_DELAY, wait].min # Cap delay at MAX_REQUEST_RETRY_DELAY. wait = [MIN_REQUEST_RETRY_DELAY, wait].max # Ensure delay is no less than MIN_REQUEST_RETRY_DELAY. wait / 1000.to_f.round(2) # convert ms to seconds end tries = 1 + [Integer(retry_count || DEFAULT_RETRIES), MAX_ALLOWED_RETRIES].min # Cap retries at MAX_ALLOWED_RETRIES { tries: tries, sleep: sleep_timer, on: Descope::RateLimitException } end |
#safe_parse_json(body, cookies: {}, headers: {}) ⇒ Object
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/descope/mixins/http.rb', line 49 def safe_parse_json(body, cookies: {}, headers: {}) @logger.debug "response => #{JSON.parse(body.to_s)}" res = JSON.parse(body.to_s) # Handle DS and DSR cookies in response. # First check RestClient's cookies (works for same-domain cookies) = {} if .key?(SESSION_COOKIE_NAME) [SESSION_COOKIE_NAME] = [SESSION_COOKIE_NAME] end if .key?(REFRESH_SESSION_COOKIE_NAME) [REFRESH_SESSION_COOKIE_NAME] = [REFRESH_SESSION_COOKIE_NAME] end # If no cookies found via RestClient, parse Set-Cookie headers directly # This handles custom domain cookies that RestClient filters out if .empty? && headers.respond_to?(:[]) = headers[:set_cookie] || headers['set-cookie'] || headers['Set-Cookie'] || [] = [] unless .is_a?(Array) .each do || next unless .is_a?(String) # Parse DS cookie (session token) if .include?("#{SESSION_COOKIE_NAME}=") = (, SESSION_COOKIE_NAME) [SESSION_COOKIE_NAME] = if end # Parse DSR cookie (refresh token) if .include?("#{REFRESH_SESSION_COOKIE_NAME}=") = (, REFRESH_SESSION_COOKIE_NAME) [REFRESH_SESSION_COOKIE_NAME] = if end end end # Add extracted cookies to response if any were found unless .empty? res['cookies'] = end res rescue JSON::ParserError body end |
#url(path) ⇒ Object
110 111 112 |
# File 'lib/descope/mixins/http.rb', line 110 def url(path) "#{@base_uri}#{path}" end |