File: rack_compatible.rb

package info (click to toggle)
ruby-faraday-middleware 1.2.0-4
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 376 kB
  • sloc: ruby: 2,388; sh: 15; makefile: 6
file content (93 lines) | stat: -rw-r--r-- 2,795 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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
# frozen_string_literal: true

require 'stringio'

module FaradayMiddleware
  # Wraps a handler originally written for Rack for Faraday compatibility.
  #
  # Experimental. Only handles changes in request headers.
  class RackCompatible
    def initialize(app, rack_handler, *args)
      # tiny middleware that decomposes a Faraday::Response to standard Rack
      # array: [status, headers, body]
      compatible_app = lambda do |rack_env|
        env = restore_env(rack_env)
        response = app.call(env)
        [response.status, response.headers, Array(response.body)]
      end
      @rack = rack_handler.new(compatible_app, *args)
    end

    def call(env)
      rack_env = prepare_env(env)
      rack_response = @rack.call(rack_env)
      finalize_response(env, rack_response)
    end

    NON_PREFIXED_HEADERS = %w[CONTENT_LENGTH CONTENT_TYPE].freeze

    # faraday to rack-compatible
    def prepare_env(faraday_env)
      env = headers_to_rack(faraday_env)

      url = faraday_env[:url]
      env['rack.url_scheme'] = url.scheme
      env['PATH_INFO'] = url.path
      env['SERVER_PORT'] = if url.respond_to?(:inferred_port)
                             url.inferred_port
                           else
                             url.port
                           end
      env['QUERY_STRING'] = url.query
      env['REQUEST_METHOD'] = faraday_env[:method].to_s.upcase

      env['rack.errors'] ||= StringIO.new
      env['faraday'] = faraday_env

      env
    end

    def headers_to_rack(env)
      rack_env = {}
      env[:request_headers].each do |name, value|
        name = name.upcase.tr('-', '_')
        name = "HTTP_#{name}" unless NON_PREFIXED_HEADERS.include? name
        rack_env[name] = value
      end
      rack_env
    end

    # rack to faraday-compatible
    def restore_env(rack_env)
      env = rack_env.fetch('faraday')
      headers = env[:request_headers]
      headers.clear

      rack_env.each do |name, value|
        next unless name.is_a?(String) && value.is_a?(String)

        if NON_PREFIXED_HEADERS.include?(name) || name.start_with?('HTTP_')
          name = name.sub(/^HTTP_/, '').downcase.tr('_', '-')
          headers[name] = value
        end
      end

      env[:method] = rack_env['REQUEST_METHOD'].downcase.to_sym
      env[:rack_errors] = rack_env['rack.errors']
      env
    end

    def finalize_response(env, rack_response)
      status, headers, body = rack_response
      body = body.inject { |str, part| str << part }
      headers = Faraday::Utils::Headers.new(headers) unless headers.is_a?(Faraday::Utils::Headers)

      env.update status: status.to_i,
                 body: body,
                 response_headers: headers

      env[:response] ||= Faraday::Response.new(env)
      env[:response]
    end
  end
end