Class: Async::HTTP::Client

Inherits:
Protocol::HTTP::Methods
  • Object
show all
Includes:
Proxy::Client
Defined in:
lib/async/http/client.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Proxy::Client

#proxied_client, #proxied_endpoint, #proxy

Constructor Details

#initialize(endpoint, protocol: endpoint.protocol, scheme: endpoint.scheme, authority: endpoint.authority, retries: DEFAULT_RETRIES, **options) ⇒ Client

Provides a robust interface to a server.

  • If there are no connections, it will create one.

  • If there are already connections, it will reuse it.

  • If a request fails, it will retry it up to N times if it was idempotent.

The client object will never become unusable. It internally manages persistent connections (or non-persistent connections if that’s required).

Parameters:

  • endpoint (Endpoint)

    the endpoint to connnect to.

  • protocol (Protocol::HTTP1 | Protocol::HTTP2 | Protocol::HTTPS) (defaults to: endpoint.protocol)

    the protocol to use.

  • scheme (String) (defaults to: endpoint.scheme)

    The default scheme to set to requests.

  • authority (String) (defaults to: endpoint.authority)

    The default authority to set to requests.



30
31
32
33
34
35
36
37
38
39
# File 'lib/async/http/client.rb', line 30

def initialize(endpoint, protocol: endpoint.protocol, scheme: endpoint.scheme, authority: endpoint.authority, retries: DEFAULT_RETRIES, **options)
  @endpoint = endpoint
  @protocol = protocol
  
  @retries = retries
  @pool = make_pool(**options)
  
  @scheme = scheme
  @authority = authority
end

Instance Attribute Details

#authorityObject (readonly)

Returns the value of attribute authority.



62
63
64
# File 'lib/async/http/client.rb', line 62

def authority
  @authority
end

#endpointObject (readonly)

Returns the value of attribute endpoint.



55
56
57
# File 'lib/async/http/client.rb', line 55

def endpoint
  @endpoint
end

#poolObject (readonly)

Returns the value of attribute pool.



59
60
61
# File 'lib/async/http/client.rb', line 59

def pool
  @pool
end

#protocolObject (readonly)

Returns the value of attribute protocol.



56
57
58
# File 'lib/async/http/client.rb', line 56

def protocol
  @protocol
end

#retriesObject (readonly)

Returns the value of attribute retries.



58
59
60
# File 'lib/async/http/client.rb', line 58

def retries
  @retries
end

#schemeObject (readonly)

Returns the value of attribute scheme.



61
62
63
# File 'lib/async/http/client.rb', line 61

def scheme
  @scheme
end

Class Method Details

.open(*arguments, **options, &block) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
# File 'lib/async/http/client.rb', line 68

def self.open(*arguments, **options, &block)
  client = self.new(*arguments, **options)
  
  return client unless block_given?
  
  begin
    yield client
  ensure
    client.close
  end
end

Instance Method Details

#as_jsonObject



41
42
43
44
45
46
47
48
49
# File 'lib/async/http/client.rb', line 41

def as_json(...)
  {
    endpoint: @endpoint.to_s,
    protocol: @protocol,
    retries: @retries,
    scheme: @scheme,
    authority: @authority,
  }
end

#call(request) ⇒ Object



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
# File 'lib/async/http/client.rb', line 88

def call(request)
  request.scheme ||= self.scheme
  request.authority ||= self.authority
  
  attempt = 0
  
  # We may retry the request if it is possible to do so. https://tools.ietf.org/html/draft-nottingham-httpbis-retry-01 is a good guide for how retrying requests should work.
  begin
    attempt += 1
    
    # As we cache pool, it's possible these pool go bad (e.g. closed by remote host). In this case, we need to try again. It's up to the caller to impose a timeout on this. If this is the last attempt, we force a new connection.
    connection = @pool.acquire
    
    response = make_response(request, connection, attempt)
    
    # This signals that the ensure block below should not try to release the connection, because it's bound into the response which will be returned:
    connection = nil
    return response
  rescue Protocol::RequestFailed
    # This is a specific case where the entire request wasn't sent before a failure occurred. So, we can even resend non-idempotent requests.
    if connection
      @pool.release(connection)
      connection = nil
    end
    
    if attempt < @retries
      retry
    else
      raise
    end
  rescue SocketError, IOError, EOFError, Errno::ECONNRESET, Errno::EPIPE
    if connection
      @pool.release(connection)
      connection = nil
    end
    
    if request.idempotent? and attempt < @retries
      retry
    else
      raise
    end
  ensure
    if connection
      @pool.release(connection)
    end
  end
end

#closeObject



80
81
82
83
84
85
86
# File 'lib/async/http/client.rb', line 80

def close
  @pool.wait_until_free do
    Console.warn(self) {"Waiting for #{@protocol} pool to drain: #{@pool}"}
  end
  
  @pool.close
end

#inspectObject



136
137
138
# File 'lib/async/http/client.rb', line 136

def inspect
  "#<#{self.class} authority=#{@authority.inspect}>"
end

#secure?Boolean

Returns:

  • (Boolean)


64
65
66
# File 'lib/async/http/client.rb', line 64

def secure?
  @endpoint.secure?
end

#to_jsonObject



51
52
53
# File 'lib/async/http/client.rb', line 51

def to_json(...)
  as_json.to_json(...)
end