File: response.rb

package info (click to toggle)
ruby-webmock 3.25.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,172 kB
  • sloc: ruby: 12,829; makefile: 6
file content (161 lines) | stat: -rw-r--r-- 4,251 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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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
# frozen_string_literal: true

require "pathname"

module WebMock

  class ResponseFactory
    def self.response_for(options)
      if options.respond_to?(:call)
        WebMock::DynamicResponse.new(options)
      else
        WebMock::Response.new(options)
      end
    end
  end

  class Response
    def initialize(options = {})
      case options
      when IO, StringIO
        self.options = read_raw_response(options)
      when String
        self.options = read_raw_response(StringIO.new(options))
      else
        self.options = options
      end
    end

    def headers
      @headers
    end

    def headers=(headers)
      @headers = headers
      if @headers && !@headers.is_a?(Proc)
        @headers = Util::Headers.normalize_headers(@headers)
      end
    end

    def body
      @body || String.new("")
    end

    def body=(body)
      @body = body
      assert_valid_body!
      stringify_body!
    end

    def status
      @status || [200, ""]
    end

    def status=(status)
      @status = status.is_a?(Integer) ? [status, ""] : status
    end

    def exception
      @exception
    end

    def exception=(exception)
      @exception = case exception
      when String then StandardError.new(exception)
      when Class then exception.new('Exception from WebMock')
      when Exception then exception
      end
    end

    def raise_error_if_any
      raise @exception if @exception
    end

    def should_timeout
      @should_timeout == true
    end

    def options=(options)
      options = WebMock::Util::HashKeysStringifier.stringify_keys!(options)
      HashValidator.new(options).validate_keys('headers', 'status', 'body', 'exception', 'should_timeout')
      self.headers = options['headers']
      self.status = options['status']
      self.body = options['body']
      self.exception = options['exception']
      @should_timeout = options['should_timeout']
    end

    def evaluate(request_signature)
      self.body = @body.call(request_signature) if @body.is_a?(Proc)
      self.headers = @headers.call(request_signature) if @headers.is_a?(Proc)
      self.status = @status.call(request_signature) if @status.is_a?(Proc)
      @should_timeout = @should_timeout.call(request_signature) if @should_timeout.is_a?(Proc)
      @exception = @exception.call(request_signature) if @exception.is_a?(Proc)
      self
    end

    def ==(other)
      self.body == other.body &&
        self.headers === other.headers &&
        self.status == other.status &&
        self.exception == other.exception &&
        self.should_timeout == other.should_timeout
    end

    private

    def stringify_body!
      if @body.is_a?(IO) || @body.is_a?(Pathname)
        io = @body
        @body = io.read
        io.close if io.respond_to?(:close)
      end
    end

    def assert_valid_body!
      valid_types = [Proc, IO, Pathname, String, Array]
      return if @body.nil?
      return if valid_types.any? { |c| @body.is_a?(c) }

      if @body.is_a?(Hash)
        raise InvalidBody, "must be one of: #{valid_types}, but you've used a #{@body.class}. " \
          "Please convert it by calling .to_json .to_xml, or otherwise convert it to a string."
      else
        raise InvalidBody, "must be one of: #{valid_types}. '#{@body.class}' given."
      end
    end

    def read_raw_response(io)
      socket = ::Net::BufferedIO.new(io)
      response = ::Net::HTTPResponse.read_new(socket)
      transfer_encoding = response.delete('transfer-encoding') #chunks were already read by curl
      response.reading_body(socket, true) {}

      options = {}
      options[:headers] = {}
      response.each_header {|name, value| options[:headers][name] = value}
      options[:headers]['transfer-encoding'] = transfer_encoding if transfer_encoding
      options[:body] = response.read_body
      options[:status] = [response.code.to_i, response.message]
      options
    ensure
      socket.close
    end

    InvalidBody = Class.new(StandardError)

  end

  class DynamicResponse < Response
    attr_accessor :responder

    def initialize(responder)
      @responder = responder
    end

    def evaluate(request_signature)
      options = @responder.call(request_signature)
      Response.new(options)
    end
  end
end