diff --git a/lib/ably/modules/ably.rb b/lib/ably/modules/ably.rb index 0cb5ef9a..0c277617 100644 --- a/lib/ably/modules/ably.rb +++ b/lib/ably/modules/ably.rb @@ -11,12 +11,12 @@ module Ably FALLBACK_DOMAIN = 'ably-realtime.com'.freeze FALLBACK_IDS = %w(a b c d e).freeze - # Default production fallbacks a.ably-realtime.com ... e.ably-realtime.com - FALLBACK_HOSTS = FALLBACK_IDS.map { |host| "#{host}.#{FALLBACK_DOMAIN}".freeze }.freeze + # Default production fallbacks main.a.fallback.ably-realtime.com ... main.e.fallback.ably-realtime.com + FALLBACK_HOSTS = FALLBACK_IDS.map { |host| "main.#{host}.fallback.#{FALLBACK_DOMAIN}".freeze }.freeze - # Custom environment default fallbacks {ENV}-a-fallback.ably-realtime.com ... {ENV}-a-fallback.ably-realtime.com + # Custom environment default fallbacks {ENV}.a.fallback.ably-realtime.com ... {ENV}.e.fallback.ably-realtime.com CUSTOM_ENVIRONMENT_FALLBACKS_SUFFIXES = FALLBACK_IDS.map do |host| - "-#{host}-fallback.#{FALLBACK_DOMAIN}".freeze + ".#{host}.fallback.#{FALLBACK_DOMAIN}".freeze end.freeze INTERNET_CHECK = { diff --git a/lib/ably/realtime/client.rb b/lib/ably/realtime/client.rb index b35aaa94..4b498be7 100644 --- a/lib/ably/realtime/client.rb +++ b/lib/ably/realtime/client.rb @@ -73,7 +73,7 @@ class Client def_delegators :auth, :client_id, :auth_options def_delegators :@rest_client, :encoders def_delegators :@rest_client, :use_tls?, :protocol, :protocol_binary? - def_delegators :@rest_client, :environment, :custom_host, :custom_port, :custom_tls_port + def_delegators :@rest_client, :endpoint, :custom_host, :custom_port, :custom_tls_port def_delegators :@rest_client, :log_level def_delegators :@rest_client, :options @@ -289,10 +289,24 @@ def publish(channel_name, name, data = nil, attributes = {}, &success_block) end end - # @!attribute [r] endpoint + # @!attribute [r] hostname + # @return [String] The primary hostname to connect to Ably + def hostname + if endpoint.include?('.') || endpoint.include?('::') || endpoint == 'localhost' + return endpoint + end + + if endpoint.start_with?('nonprod:') + "#{endpoint.gsub('nonprod:', '')}.realtime.#{root_domain}" + else + "#{endpoint}.realtime.#{root_domain}" + end + end + + # @!attribute [r] uri # @return [URI::Generic] Default Ably Realtime endpoint used for all requests - def endpoint - endpoint_for_host(custom_realtime_host || [environment, DOMAIN].compact.join('-')) + def uri + uri_for_host(custom_realtime_host || hostname) end # (see Ably::Rest::Client#register_encoder) @@ -341,7 +355,8 @@ def device end private - def endpoint_for_host(host) + + def uri_for_host(host) port = if use_tls? custom_tls_port else diff --git a/lib/ably/realtime/connection.rb b/lib/ably/realtime/connection.rb index 6295ef09..82ddb599 100644 --- a/lib/ably/realtime/connection.rb +++ b/lib/ably/realtime/connection.rb @@ -173,7 +173,7 @@ def initialize(client, options) @state = STATE(state_machine.current_state) @manager = ConnectionManager.new(self) - @current_host = client.endpoint.host + @current_host = client.uri.host reset_client_msg_serial end @@ -396,12 +396,12 @@ def determine_host @current_host = if internet_is_up_result client.fallback_endpoint.host else - client.endpoint.host + client.uri.host end yield current_host end else - @current_host = client.endpoint.host + @current_host = client.uri.host yield current_host end end @@ -496,8 +496,8 @@ def create_websocket_transport end end - url = URI(client.endpoint).tap do |endpoint| - endpoint.query = URI.encode_www_form(url_params) + url = URI(client.uri).tap do |uri| + uri.query = URI.encode_www_form(url_params) end determine_host do |host| diff --git a/lib/ably/rest/client.rb b/lib/ably/rest/client.rb index 3dbda2df..d32ad69e 100644 --- a/lib/ably/rest/client.rb +++ b/lib/ably/rest/client.rb @@ -43,9 +43,9 @@ class Client def_delegators :auth, :client_id, :auth_options - # Custom environment to use such as 'sandbox' when testing the client library against an alternate Ably environment + # The hostname used to connect to Ably # @return [String] - attr_reader :environment + attr_reader :endpoint # The protocol configured for this client, either binary `:msgpack` or text based `:json` # @return [Symbol] @@ -135,7 +135,8 @@ class Client # @option options [String] :token Token string or {Models::TokenDetails} used to authenticate requests # @option options [String] :token_details {Models::TokenDetails} used to authenticate requests # @option options [Boolean] :use_token_auth Will force Basic Auth if set to false, and Token auth if set to true - # @option options [String] :environment Specify 'sandbox' when testing the client library against an alternate Ably environment + # @option options [String] :endpoint Specify a routing policy or fully-qualified domain name to connect to Ably + # @option options [String] :environment Specify 'sandbox' when testing the client library against an alternate Ably environment (deprecated) # @option options [Symbol] :protocol (:msgpack) Protocol used to communicate with Ably, :json and :msgpack currently supported # @option options [Boolean] :use_binary_protocol (true) When true will use the MessagePack binary protocol, when false it will use JSON encoding. This option will overide :protocol option # @option options [Logger::Severity,Symbol] :log_level (Logger::WARN) Log level for the standard Logger that outputs to STDOUT. Can be set to :fatal (Logger::FATAL), :error (Logger::ERROR), :warn (Logger::WARN), :info (Logger::INFO), :debug (Logger::DEBUG) or :none @@ -188,8 +189,6 @@ def initialize(options) @agent = options.delete(:agent) || Ably::AGENT @realtime_client = options.delete(:realtime_client) @tls = options.delete_with_default(:tls, true) - @environment = options.delete(:environment) # nil is production - @environment = nil if [:production, 'production'].include?(@environment) @protocol = options.delete(:protocol) || :msgpack @debug_http = options.delete(:debug_http) @log_level = options.delete(:log_level) || ::Logger::WARN @@ -203,9 +202,14 @@ def initialize(options) @max_frame_size = options.delete(:max_frame_size) || MAX_FRAME_SIZE @idempotent_rest_publishing = options.delete_with_default(:idempotent_rest_publishing, true) + @environment = options.delete(:environment) # nil is production + @environment = nil if [:production, 'production'].include?(@environment) + @endpoint = @environment || options.delete_with_default(:endpoint, 'main') + if options[:fallback_hosts_use_default] && options[:fallback_hosts] raise ArgumentError, "fallback_hosts_use_default cannot be set to try when fallback_hosts is also provided" end + @fallback_hosts = case when options.delete(:fallback_hosts_use_default) Ably::FALLBACK_HOSTS @@ -213,8 +217,12 @@ def initialize(options) options_fallback_hosts when custom_host || options[:realtime_host] || custom_port || custom_tls_port [] - when environment - CUSTOM_ENVIRONMENT_FALLBACKS_SUFFIXES.map { |host| "#{environment}#{host}" } + when endpoint + if endpoint.start_with?('nonprod:') + CUSTOM_ENVIRONMENT_FALLBACKS_SUFFIXES.map { |host| "#{endpoint.gsub('nonprod:', '')}.#{host}" } + else + CUSTOM_ENVIRONMENT_FALLBACKS_SUFFIXES.map { |host| "#{endpoint}.#{host}" } + end else Ably::FALLBACK_HOSTS end @@ -426,10 +434,24 @@ def push @push ||= Push.new(self) end - # @!attribute [r] endpoint + # @!attribute [r] hostname + # @return [String] The primary hostname to connect to Ably + def hostname + if endpoint.include?('.') || endpoint.include?('::') || endpoint == 'localhost' + return endpoint + end + + if endpoint.start_with?('nonprod:') + "#{endpoint.gsub('nonprod:', '')}.realtime.#{root_domain}" + else + "#{endpoint}.realtime.#{root_domain}" + end + end + + # @!attribute [r] uri # @return [URI::Generic] Default Ably REST endpoint used for all requests - def endpoint - endpoint_for_host(custom_host || [@environment, DOMAIN].compact.join('-')) + def uri + uri_for_host(custom_host || hostname) end # @!attribute [r] logger @@ -480,7 +502,7 @@ def connection(options = {}) if options[:use_fallback] fallback_connection else - @connection ||= Faraday.new(endpoint.to_s, connection_options) + @connection ||= Faraday.new(uri.to_s, connection_options) end end @@ -493,7 +515,7 @@ def connection(options = {}) # @api private def fallback_connection unless defined?(@fallback_connections) && @fallback_connections - @fallback_connections = fallback_hosts.shuffle.map { |host| Faraday.new(endpoint_for_host(host).to_s, connection_options) } + @fallback_connections = fallback_hosts.shuffle.map { |host| Faraday.new(uri_for_host(host).to_s, connection_options) } end @fallback_index ||= 0 @@ -653,7 +675,7 @@ def reauthorize_on_authorization_failure end end - def endpoint_for_host(host) + def uri_for_host(host) port = if use_tls? custom_tls_port else @@ -671,6 +693,14 @@ def endpoint_for_host(host) URI::Generic.build(options) end + def root_domain + if endpoint.start_with?('nonprod:') + 'ably-nonprod.net' + else + 'ably.net' + end + end + # Return a Hash of connection options to initiate the Faraday::Connection with # # @return [Hash] diff --git a/spec/acceptance/realtime/client_spec.rb b/spec/acceptance/realtime/client_spec.rb index b392d118..ce26dd30 100644 --- a/spec/acceptance/realtime/client_spec.rb +++ b/spec/acceptance/realtime/client_spec.rb @@ -234,7 +234,7 @@ context '#request (#RSC19*)' do let(:client_options) { default_options.merge(key: api_key) } let(:device_id) { random_str } - let(:endpoint) { subject.rest_client.endpoint } + let(:uri) { subject.rest_client.uri } context 'get' do it 'returns an HttpPaginatedResponse object' do @@ -287,7 +287,7 @@ context 'post', :webmock do before do - stub_request(:delete, "#{endpoint}/push/deviceRegistrations/#{device_id}/resetUpdateToken"). + stub_request(:delete, "#{uri}/push/deviceRegistrations/#{device_id}/resetUpdateToken"). to_return(status: 200, body: '{}', headers: { 'Content-Type' => 'application/json' }) end @@ -301,7 +301,7 @@ context 'delete', :webmock do before do - stub_request(:delete, "#{endpoint}/push/channelSubscriptions?deviceId=#{device_id}"). + stub_request(:delete, "#{uri}/push/channelSubscriptions?deviceId=#{device_id}"). to_return(status: 200, body: '{}', headers: { 'Content-Type' => 'application/json' }) end @@ -317,7 +317,7 @@ let(:body_params) { { 'metadata' => { 'key' => 'value' } } } before do - stub_request(:patch, "#{endpoint}/push/deviceRegistrations/#{device_id}") + stub_request(:patch, "#{uri}/push/deviceRegistrations/#{device_id}") .with(body: serialize_body(body_params, protocol)) .to_return(status: 200, body: '{}', headers: { 'Content-Type' => 'application/json' }) end @@ -341,7 +341,7 @@ end before do - stub_request(:put, "#{endpoint}/push/deviceRegistrations/#{device_id}") + stub_request(:put, "#{uri}/push/deviceRegistrations/#{device_id}") .with(body: serialize_body(body_params, protocol)) .to_return(status: 200, body: '{}', headers: { 'Content-Type' => 'application/json' }) end diff --git a/spec/acceptance/realtime/message_spec.rb b/spec/acceptance/realtime/message_spec.rb index b218a8e1..63c8aa35 100644 --- a/spec/acceptance/realtime/message_spec.rb +++ b/spec/acceptance/realtime/message_spec.rb @@ -775,7 +775,7 @@ def publish_and_check_extras(extras) EventMachine.add_timer(0.0001) do connection.transition_state_machine :suspended stub_const 'Ably::FALLBACK_HOSTS', [] - allow(client).to receive(:endpoint).and_return(URI::Generic.build(scheme: 'wss', host: 'does.not.exist.com')) + allow(client).to receive(:uri).and_return(URI::Generic.build(scheme: 'wss', host: 'does.not.exist.com')) end end end diff --git a/spec/acceptance/realtime/push_admin_spec.rb b/spec/acceptance/realtime/push_admin_spec.rb index e9dfba92..1be57968 100644 --- a/spec/acceptance/realtime/push_admin_spec.rb +++ b/spec/acceptance/realtime/push_admin_spec.rb @@ -102,7 +102,7 @@ end let!(:publish_stub) do - stub_request(:post, "#{client.rest_client.endpoint}/push/publish"). + stub_request(:post, "#{client.rest_client.uri}/push/publish"). with do |request| expect(deserialize_body(request.body, protocol)['recipient']['camelCase']['secondLevelCamelCase']).to eql('val') expect(deserialize_body(request.body, protocol)['recipient']).to_not have_key('camel_case') @@ -135,7 +135,7 @@ 'transportType' => 'ablyChannel', 'channel' => channel, 'ablyKey' => api_key, - 'ablyUrl' => client.rest_client.endpoint.to_s + 'ablyUrl' => client.rest_client.uri.to_s } end let(:notification_payload) do diff --git a/spec/acceptance/rest/auth_spec.rb b/spec/acceptance/rest/auth_spec.rb index ef07af9f..6b34e8f1 100644 --- a/spec/acceptance/rest/auth_spec.rb +++ b/spec/acceptance/rest/auth_spec.rb @@ -61,7 +61,7 @@ def request_body_includes(request, protocol, key, val) end it 'creates a TokenRequest automatically and sends it to Ably to obtain a token', webmock: true do - token_request_stub = stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken"). + token_request_stub = stub_request(:post, "#{client.uri}/keys/#{key_name}/requestToken"). to_return(status: 201, body: serialize_body({}, protocol), headers: { 'Content-Type' => content_type }) expect(auth).to receive(:create_token_request).and_call_original auth.request_token @@ -90,7 +90,7 @@ def coerce_if_time_value(field_name, value, params = {}) let(:token_response) { {} } let!(:request_token_stub) do - stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken"). + stub_request(:post, "#{client.uri}/keys/#{key_name}/requestToken"). with do |request| request_body_includes(request, protocol, token_param, coerce_if_time_value(token_param, random, multiply: 1000)) end.to_return( @@ -121,7 +121,7 @@ def coerce_if_time_value(field_name, value, params = {}) let(:token_response) { {} } let!(:request_token_stub) do - stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken"). + stub_request(:post, "#{client.uri}/keys/#{key_name}/requestToken"). with do |request| request_body_includes(request, protocol, 'mac', mac) end.to_return( @@ -151,7 +151,7 @@ def coerce_if_time_value(field_name, value, params = {}) let(:token_response) { {} } let!(:request_token_stub) do - stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken"). + stub_request(:post, "#{client.uri}/keys/#{key_name}/requestToken"). with do |request| request_body_includes(request, protocol, 'mac', mac) end.to_return( @@ -293,7 +293,7 @@ def coerce_if_time_value(field_name, value, params = {}) let(:auth_url_content_type) { 'application/json' } let!(:request_token_stub) do - stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken"). + stub_request(:post, "#{client.uri}/keys/#{key_name}/requestToken"). with do |request| request_body_includes(request, protocol, 'key_name', key_name) end.to_return( diff --git a/spec/acceptance/rest/base_spec.rb b/spec/acceptance/rest/base_spec.rb index 0e046458..4a8ff749 100644 --- a/spec/acceptance/rest/base_spec.rb +++ b/spec/acceptance/rest/base_spec.rb @@ -14,7 +14,7 @@ let(:body_value) { [as_since_epoch(now)] } before do - stub_request(:get, "#{client.endpoint}/time"). + stub_request(:get, "#{client.uri}/time"). with(:headers => { 'Accept' => mime }). to_return(:status => 200, :body => request_body, :headers => { 'Content-Type' => mime }) end @@ -87,7 +87,7 @@ let(:error_response) { '{ "error": { "statusCode": 500, "code": 50000, "message": "Internal error" } }' } before do - (client.fallback_hosts.map { |host| "https://#{host}" } + [client.endpoint]).each do |host| + (client.fallback_hosts.map { |host| "https://#{host}" } + [client.uri]).each do |host| stub_request(:get, "#{host}/time") .to_return(:status => 500, :body => error_response, :headers => { 'Content-Type' => 'application/json' }) end @@ -100,7 +100,7 @@ describe '500 server error without a valid JSON response body', :webmock do before do - (client.fallback_hosts.map { |host| "https://#{host}" } + [client.endpoint]).each do |host| + (client.fallback_hosts.map { |host| "https://#{host}" } + [client.uri]).each do |host| stub_request(:get, "#{host}/time"). to_return(:status => 500, :headers => { 'Content-Type' => 'application/json' }) end @@ -121,7 +121,7 @@ @token_requests = 0 @publish_attempts = 0 - stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").to_return do + stub_request(:post, "#{client.uri}/keys/#{key_name}/requestToken").to_return do @token_requests += 1 { :body => public_send("token_#{@token_requests}").merge(expires: (Time.now.to_i + 60) * 1000).to_json, @@ -129,7 +129,7 @@ } end - stub_request(:post, "#{client.endpoint}/channels/#{channel}/publish").to_return do + stub_request(:post, "#{client.uri}/channels/#{channel}/publish").to_return do @publish_attempts += 1 if [1, 3].include?(@publish_attempts) { status: 201, :body => '[]', :headers => { 'Content-Type' => 'application/json' } } diff --git a/spec/acceptance/rest/channel_spec.rb b/spec/acceptance/rest/channel_spec.rb index 37a4e174..36d15eab 100644 --- a/spec/acceptance/rest/channel_spec.rb +++ b/spec/acceptance/rest/channel_spec.rb @@ -364,12 +364,12 @@ context 'with a non ASCII channel name' do let(:channel_name) { 'foo:¡€≤`☃' } let(:channel_name_encoded) { 'foo%3A%C2%A1%E2%82%AC%E2%89%A4%60%E2%98%83' } - let(:endpoint) { client.endpoint } + let(:uri) { client.uri } let(:channel) { client.channels.get(channel_name) } context 'stubbed', :webmock do let!(:get_stub) { - stub_request(:post, "#{endpoint}/channels/#{channel_name_encoded}/publish"). + stub_request(:post, "#{uri}/channels/#{channel_name_encoded}/publish"). to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' }) } @@ -534,8 +534,8 @@ describe '#history option' do let(:channel_name) { "persisted:#{random_str(4)}" } let(:channel) { client.channel(channel_name) } - let(:endpoint) do - client.endpoint + let(:uri) do + client.uri end let(:default_history_options) do { @@ -549,7 +549,7 @@ let!(:history_stub) { query_params = default_history_options .merge(option => milliseconds).map { |k, v| "#{k}=#{v}" }.join('&') - stub_request(:get, "#{endpoint}/channels/#{URI.encode_www_form_component(channel_name)}/messages?#{query_params}"). + stub_request(:get, "#{uri}/channels/#{URI.encode_www_form_component(channel_name)}/messages?#{query_params}"). to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' }) } diff --git a/spec/acceptance/rest/client_spec.rb b/spec/acceptance/rest/client_spec.rb index e1619c6e..8d9d38de 100644 --- a/spec/acceptance/rest/client_spec.rb +++ b/spec/acceptance/rest/client_spec.rb @@ -301,12 +301,12 @@ def encode64(text) context 'configured' do let(:client_options) { default_options.merge(key: api_key, environment: 'production') } - it 'should make connection attempts to a.ably-realtime.com, b.ably-realtime.com, c.ably-realtime.com, d.ably-realtime.com, e.ably-realtime.com (#RSC15a)' do + it 'should make connection attempts to main.a.fallback.ably-realtime.com, main.b.fallback.ably-realtime.com, main.c.fallback.ably-realtime.com, main.d.fallback.ably-realtime.com, main.e.fallback.ably-realtime.com (#RSC15a)' do hosts = [] 5.times do hosts << client.fallback_connection.host end - expect(hosts).to match_array(%w(a.ably-realtime.com b.ably-realtime.com c.ably-realtime.com d.ably-realtime.com e.ably-realtime.com)) + expect(hosts).to match_array(%w(main.a.fallback.ably-realtime.com main.b.fallback.ably-realtime.com main.c.fallback.ably-realtime.com main.d.fallback.ably-realtime.com main.e.fallback.ably-realtime.com)) end end @@ -327,12 +327,12 @@ def encode64(text) context 'and no custom fallback hosts are provided' do let(:client_options) { default_options.merge(environment: 'sandbox', key: api_key) } - it 'should make connection attempts to sandbox-a-fallback.ably-realtime.com, sandbox-b-fallback.ably-realtime.com, sandbox-c-fallback.ably-realtime.com, sandbox-d-fallback.ably-realtime.com, sandbox-e-fallback.ably-realtime.com (#RSC15a)' do + it 'should make connection attempts to sandbox.a.fallback.ably-realtime.com, sandbox.b.fallback.ably-realtime.com, sandbox.c.fallback.ably-realtime.com, sandbox.d.fallback.ably-realtime.com, sandbox.e.fallback.ably-realtime.com (#RSC15a)' do hosts = [] 5.times do hosts << client.fallback_connection.host end - expect(hosts).to match_array(%w(a b c d e).map { |id| "sandbox-#{id}-fallback.ably-realtime.com" }) + expect(hosts).to match_array(%w(a b c d e).map { |id| "sandbox.#{id}.fallback.ably-realtime.com" }) end end end @@ -554,8 +554,8 @@ def encode64(text) end context 'using a local web-server', webmock: false do - let(:primary_host) { 'local-rest.ably.io' } - let(:fallbacks) { ['local.ably.io', 'localhost'] } + let(:primary_host) { 'local.realtime.ably.net' } + let(:fallbacks) { ['local.ably.net', 'localhost'] } let(:port) { rand(10000) + 2000 } let(:channel_name) { 'foo' } let(:request_timeout) { 3 } @@ -838,7 +838,7 @@ def encode64(text) context 'when environment is not production and server returns a 50x error' do let(:env) { 'custom-env' } - let(:default_fallbacks) { %w(a b c d e).map { |id| "#{env}-#{id}-fallback.ably-realtime.com" } } + let(:default_fallbacks) { %w(a b c d e).map { |id| "#{env}.#{id}.fallback.ably-realtime.com" } } let(:custom_hosts) { %w(A.foo.com B.foo.com) } let(:max_retry_count) { 2 } let(:max_retry_duration) { 0.5 } @@ -1086,7 +1086,7 @@ def encode64(text) let(:client_options) { default_options.merge(key: api_key, agent: agent) } let!(:publish_message_stub) do - stub_request(:post, "#{client.endpoint}/channels/foo/publish"). + stub_request(:post, "#{client.uri}/channels/foo/publish"). with(headers: { 'X-Ably-Version' => Ably::PROTOCOL_VERSION, 'Ably-Agent' => agent || Ably::AGENT @@ -1113,7 +1113,7 @@ def encode64(text) context '#request (#RSC19*, #TO3l9)' do let(:client_options) { default_options.merge(key: api_key) } let(:device_id) { random_str } - let(:endpoint) { client.endpoint } + let(:uri) { client.uri } context 'get' do it 'returns an HttpPaginatedResponse object' do @@ -1156,7 +1156,7 @@ def encode64(text) context 'post', :webmock do before do - stub_request(:delete, "#{endpoint}/push/deviceRegistrations/#{device_id}/resetUpdateToken"). + stub_request(:delete, "#{uri}/push/deviceRegistrations/#{device_id}/resetUpdateToken"). to_return(status: 200, body: '{}', headers: { 'Content-Type' => 'application/json' }) end @@ -1168,14 +1168,14 @@ def encode64(text) it 'raises an exception once body size in bytes exceeded' do expect { - client.request(:post, endpoint, {}, { content: 'x' * Ably::Rest::Client::MAX_FRAME_SIZE }) + client.request(:post, uri, {}, { content: 'x' * Ably::Rest::Client::MAX_FRAME_SIZE }) }.to raise_error(Ably::Exceptions::MaxFrameSizeExceeded) end end context 'delete', :webmock do before do - stub_request(:delete, "#{endpoint}/push/channelSubscriptions?deviceId=#{device_id}"). + stub_request(:delete, "#{uri}/push/channelSubscriptions?deviceId=#{device_id}"). to_return(status: 200, body: '{}', headers: { 'Content-Type' => 'application/json' }) end @@ -1190,7 +1190,7 @@ def encode64(text) let(:body_params) { { 'metadata' => { 'key' => 'value' } } } before do - stub_request(:patch, "#{endpoint}/push/deviceRegistrations/#{device_id}") + stub_request(:patch, "#{uri}/push/deviceRegistrations/#{device_id}") .with(body: serialize_body(body_params, protocol)) .to_return(status: 200, body: '{}', headers: { 'Content-Type' => 'application/json' }) end @@ -1203,7 +1203,7 @@ def encode64(text) it 'raises an exception once body size in bytes exceeded' do expect { - client.request(:patch, endpoint, {}, { content: 'x' * Ably::Rest::Client::MAX_FRAME_SIZE }) + client.request(:patch, uri, {}, { content: 'x' * Ably::Rest::Client::MAX_FRAME_SIZE }) }.to raise_error(Ably::Exceptions::MaxFrameSizeExceeded) end end @@ -1219,7 +1219,7 @@ def encode64(text) end before do - stub_request(:put, "#{endpoint}/push/deviceRegistrations/#{device_id}") + stub_request(:put, "#{uri}/push/deviceRegistrations/#{device_id}") .with(body: serialize_body(body_params, protocol)) .to_return(status: 200, body: '{}', headers: { 'Content-Type' => 'application/json' }) end @@ -1232,7 +1232,7 @@ def encode64(text) it 'raises an exception once body size in bytes exceeded' do expect { - client.request(:put, endpoint, {}, { content: 'x' * Ably::Rest::Client::MAX_FRAME_SIZE }) + client.request(:put, uri, {}, { content: 'x' * Ably::Rest::Client::MAX_FRAME_SIZE }) }.to raise_error(Ably::Exceptions::MaxFrameSizeExceeded) end end @@ -1246,7 +1246,7 @@ def encode64(text) before do @request_id = nil - stub_request(:get, Addressable::Template.new("#{client.endpoint}/time{?request_id}")).with do |request| + stub_request(:get, Addressable::Template.new("#{client.uri}/time{?request_id}")).with do |request| @request_id = request.uri.query_values['request_id'] end.to_return do raise Faraday::TimeoutError.new('timeout error message') @@ -1268,7 +1268,7 @@ def encode64(text) context 'with mocks to inspect the params', :webmock do before do - stub_request(:post, Addressable::Template.new("#{client.endpoint}/channels/#{channel_name}/publish{?request_id}")). + stub_request(:post, Addressable::Template.new("#{client.uri}/channels/#{channel_name}/publish{?request_id}")). with do |request| @request_id = request.uri.query_values['request_id'] end.to_return(:status => 200, :body => [], :headers => { 'Content-Type' => 'application/json' }) @@ -1312,7 +1312,7 @@ def encode64(text) before do @request_id = nil - hosts = Ably::FALLBACK_HOSTS + ['rest.ably.io'] + hosts = Ably::FALLBACK_HOSTS + ['main.realtime.ably.net'] hosts.each do |host| stub_request(:get, Addressable::Template.new("https://#{host.downcase}/time{?request_id}")).with do |request| @request_id = request.uri.query_values['request_id'] diff --git a/spec/acceptance/rest/presence_spec.rb b/spec/acceptance/rest/presence_spec.rb index b0c61c8e..bd76f460 100644 --- a/spec/acceptance/rest/presence_spec.rb +++ b/spec/acceptance/rest/presence_spec.rb @@ -68,12 +68,12 @@ limit: 100 } end - let(:endpoint) do - client.endpoint + let(:uri) do + client.uri end let!(:get_stub) { query_params = query_options.map { |k, v| "#{k}=#{v}" }.join('&') - stub_request(:get, "#{endpoint}/channels/#{URI.encode_www_form_component(channel_name)}/presence?#{query_params}"). + stub_request(:get, "#{uri}/channels/#{URI.encode_www_form_component(channel_name)}/presence?#{query_params}"). to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' }) } let(:channel_name) { random_str } @@ -115,12 +115,12 @@ context 'with a non ASCII channel name' do let(:channel_name) { 'foo:¡€≤`☃' } let(:channel_name_encoded) { 'foo%3A%C2%A1%E2%82%AC%E2%89%A4%60%E2%98%83' } - let(:endpoint) { client.endpoint } + let(:uri) { client.uri } let(:channel) { client.channels.get(channel_name) } context 'stubbed', :webmock do let!(:get_stub) { - stub_request(:get, "#{endpoint}/channels/#{channel_name_encoded}/presence?limit=100"). + stub_request(:get, "#{uri}/channels/#{channel_name_encoded}/presence?limit=100"). to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' }) } @@ -197,8 +197,8 @@ let(:presence) { client.channel(channel_name).presence } let(:user) { 'appid.keyuid' } let(:secret) { random_str(8) } - let(:endpoint) do - client.endpoint + let(:uri) do + client.uri end let(:client) do Ably::Rest::Client.new(key: "#{user}:#{secret}") @@ -213,7 +213,7 @@ context 'limit options', :webmock do let!(:history_stub) { query_params = history_options.map { |k, v| "#{k}=#{v}" }.join('&') - stub_request(:get, "#{endpoint}/channels/#{URI.encode_www_form_component(channel_name)}/presence/history?#{query_params}"). + stub_request(:get, "#{uri}/channels/#{URI.encode_www_form_component(channel_name)}/presence/history?#{query_params}"). to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' }) } @@ -253,7 +253,7 @@ } let!(:history_stub) { query_params = history_options.map { |k, v| "#{k}=#{v}" }.join('&') - stub_request(:get, "#{endpoint}/channels/#{URI.encode_www_form_component(channel_name)}/presence/history?#{query_params}"). + stub_request(:get, "#{uri}/channels/#{URI.encode_www_form_component(channel_name)}/presence/history?#{query_params}"). to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' }) } @@ -322,8 +322,8 @@ def message(client_id, messages) describe 'decoding permutations using mocked #history', :webmock do let(:user) { 'appid.keyuid' } let(:secret) { random_str(8) } - let(:endpoint) do - client.endpoint + let(:uri) do + client.uri end let(:client) do Ably::Rest::Client.new(client_options.merge(key: "#{user}:#{secret}")) @@ -357,7 +357,7 @@ def message(client_id, messages) context '#get' do let!(:get_stub) { - stub_request(:get, "#{endpoint}/channels/#{URI.encode_www_form_component(channel_name)}/presence?limit=100"). + stub_request(:get, "#{uri}/channels/#{URI.encode_www_form_component(channel_name)}/presence?limit=100"). to_return(:body => serialized_encoded_message, :headers => { 'Content-Type' => content_type }) } @@ -374,7 +374,7 @@ def message(client_id, messages) context '#history' do let!(:history_stub) { - stub_request(:get, "#{endpoint}/channels/#{URI.encode_www_form_component(channel_name)}/presence/history?direction=backwards&limit=100"). + stub_request(:get, "#{uri}/channels/#{URI.encode_www_form_component(channel_name)}/presence/history?direction=backwards&limit=100"). to_return(:body => serialized_encoded_message, :headers => { 'Content-Type' => content_type }) } @@ -404,7 +404,7 @@ def message(client_id, messages) context '#get' do let(:client_options) { default_options.merge(log_level: :fatal) } let!(:get_stub) { - stub_request(:get, "#{endpoint}/channels/#{URI.encode_www_form_component(channel_name)}/presence?limit=100"). + stub_request(:get, "#{uri}/channels/#{URI.encode_www_form_component(channel_name)}/presence?limit=100"). to_return(:body => serialized_encoded_message_with_invalid_encoding, :headers => { 'Content-Type' => content_type }) } let(:presence_message) { presence.get.items.first } @@ -428,7 +428,7 @@ def message(client_id, messages) context '#history' do let(:client_options) { default_options.merge(log_level: :fatal) } let!(:history_stub) { - stub_request(:get, "#{endpoint}/channels/#{URI.encode_www_form_component(channel_name)}/presence/history?direction=backwards&limit=100"). + stub_request(:get, "#{uri}/channels/#{URI.encode_www_form_component(channel_name)}/presence/history?direction=backwards&limit=100"). to_return(:body => serialized_encoded_message_with_invalid_encoding, :headers => { 'Content-Type' => content_type }) } let(:presence_message) { presence.history.items.first } diff --git a/spec/acceptance/rest/push_admin_spec.rb b/spec/acceptance/rest/push_admin_spec.rb index a5e1433f..36824fa4 100644 --- a/spec/acceptance/rest/push_admin_spec.rb +++ b/spec/acceptance/rest/push_admin_spec.rb @@ -90,7 +90,7 @@ end let!(:publish_stub) do - stub_request(:post, "#{client.endpoint}/push/publish"). + stub_request(:post, "#{client.uri}/push/publish"). with do |request| expect(deserialize_body(request.body, protocol)['recipient']['camelCase']['secondLevelCamelCase']).to eql('val') expect(deserialize_body(request.body, protocol)['recipient']).to_not have_key('camel_case') @@ -119,7 +119,7 @@ 'transportType' => 'ablyChannel', 'channel' => channel, 'ablyKey' => api_key, - 'ablyUrl' => client.endpoint.to_s + 'ablyUrl' => client.uri.to_s } end let(:notification_payload) do diff --git a/spec/shared/client_initializer_behaviour.rb b/spec/shared/client_initializer_behaviour.rb index 84ff2df4..9d0d66a8 100644 --- a/spec/shared/client_initializer_behaviour.rb +++ b/spec/shared/client_initializer_behaviour.rb @@ -1,14 +1,6 @@ # encoding: utf-8 shared_examples 'a client initializer' do - def subdomain - if rest? - 'rest' - else - 'realtime' - end - end - def protocol if rest? 'http' @@ -154,38 +146,38 @@ def rest? end end - context 'endpoint' do + context 'uri' do before do allow_any_instance_of(subject.class).to receive(:auto_connect).and_return(false) end it 'defaults to production' do - expect(subject.endpoint.to_s).to eql("#{protocol}s://#{subdomain}.ably.io") + expect(subject.uri.to_s).to eql("#{protocol}s://main.realtime.ably.net") end context 'with environment option' do let(:client_options) { default_options.merge(environment: 'sandbox', auto_connect: false) } - it 'uses an alternate endpoint' do - expect(subject.endpoint.to_s).to eql("#{protocol}s://sandbox-#{subdomain}.ably.io") + it 'uses an alternate uri' do + expect(subject.uri.to_s).to eql("#{protocol}s://sandbox.realtime.ably.net") end end context 'with rest_host option' do let(:client_options) { default_options.merge(rest_host: 'custom-rest.host.com', auto_connect: false) } - it 'uses an alternate endpoint for REST clients' do + it 'uses an alternate uri for REST clients' do skip 'does not apply as testing a Realtime client' unless rest? - expect(subject.endpoint.to_s).to eql("#{protocol}s://custom-rest.host.com") + expect(subject.uri.to_s).to eql("#{protocol}s://custom-rest.host.com") end end context 'with realtime_host option' do let(:client_options) { default_options.merge(realtime_host: 'custom-realtime.host.com', auto_connect: false) } - it 'uses an alternate endpoint for Realtime clients' do + it 'uses an alternate uri for Realtime clients' do skip 'does not apply as testing a REST client' if rest? - expect(subject.endpoint.to_s).to eql("#{protocol}s://custom-realtime.host.com") + expect(subject.uri.to_s).to eql("#{protocol}s://custom-realtime.host.com") end end @@ -193,7 +185,7 @@ def rest? let(:client_options) { default_options.merge(port: 999, tls: false, auto_connect: false) } it 'uses the custom port for non-TLS requests' do - expect(subject.endpoint.to_s).to include(":999") + expect(subject.uri.to_s).to include(":999") end end @@ -201,7 +193,7 @@ def rest? let(:client_options) { default_options.merge(tls_port: 666, tls: true, auto_connect: false) } it 'uses the custom port for TLS requests' do - expect(subject.endpoint.to_s).to include(":666") + expect(subject.uri.to_s).to include(":666") end end end @@ -219,7 +211,7 @@ def rest? end it 'uses HTTP' do - expect(subject.endpoint.to_s).to eql("#{protocol}://#{subdomain}.ably.io") + expect(subject.uri.to_s).to eql("#{protocol}://main.realtime.ably.net") end end @@ -270,7 +262,7 @@ def rest? context 'when set without custom fallback hosts configured' do let(:environment) { 'foo' } let(:client_options) { default_options.merge(environment: environment) } - let(:default_fallbacks) { %w(a b c d e).map { |id| "#{environment}-#{id}-fallback.ably-realtime.com" } } + let(:default_fallbacks) { %w(a b c d e).map { |id| "#{environment}.#{id}.fallback.ably-realtime.com" } } it 'sets the environment attribute' do expect(subject.environment).to eql(environment) @@ -298,7 +290,7 @@ def rest? context 'when set with fallback_hosts_use_default' do let(:environment) { 'foo' } let(:custom_fallbacks) { %w(a b c).map { |id| "#{environment}-#{id}.foo.com" } } - let(:default_production_fallbacks) { %w(a b c d e).map { |id| "#{id}.ably-realtime.com" } } + let(:default_production_fallbacks) { %w(a b c d e).map { |id| "main.#{id}.fallback.ably-realtime.com" } } let(:client_options) { default_options.merge(environment: environment, fallback_hosts_use_default: true) } it 'sets the environment attribute' do diff --git a/spec/support/test_app.rb b/spec/support/test_app.rb index 2e7fc394..e1bf93ae 100644 --- a/spec/support/test_app.rb +++ b/spec/support/test_app.rb @@ -57,7 +57,7 @@ def restricted_api_key def delete return unless TestApp.instance_variable_get('@singleton__instance__') - url = "#{sandbox_client.endpoint}/apps/#{app_id}" + url = "#{sandbox_client.uri}/apps/#{app_id}" basic_auth = Base64.urlsafe_encode64(api_key).chomp headers = { "Authorization" => "Basic #{basic_auth}" } @@ -66,11 +66,11 @@ def delete end def environment - ENV['ABLY_ENV'] || 'sandbox' + ENV['ABLY_ENV'] || 'nonprod:sandbox' end def create_test_app - url = "#{sandbox_client.endpoint}/apps" + url = "#{sandbox_client.uri}/apps" headers = { 'Accept' => 'application/json', @@ -86,7 +86,7 @@ def create_test_app end def host - sandbox_client.endpoint.host + sandbox_client.uri.host end def realtime_host @@ -94,12 +94,13 @@ def realtime_host end def create_test_stats(stats) - client = Ably::Rest::Client.new(key: api_key, environment: environment) + client = Ably::Rest::Client.new(key: api_key, endpoint: environment) response = client.post('/stats', stats) raise "Could not create stats fixtures. Ably responded with status #{response.status}\n#{response.body}" unless (200..299).include?(response.status) end private + def sandbox_client @sandbox_client ||= Ably::Rest::Client.new(key: 'app.key:secret', tls: true, environment: environment) end diff --git a/spec/unit/models/http_paginated_result_spec.rb b/spec/unit/models/http_paginated_result_spec.rb index 1c4ac199..cb8ef1bc 100644 --- a/spec/unit/models/http_paginated_result_spec.rb +++ b/spec/unit/models/http_paginated_result_spec.rb @@ -23,7 +23,7 @@ status: status }) end - let(:base_url) { 'http://rest.ably.io/channels/channel_name' } + let(:base_url) { 'http://main.realtime.ably.net/channels/channel_name' } let(:full_url) { "#{base_url}/whatever?param=exists" } let(:paginated_result_options) { Hash.new } let(:first_paged_request) { paginated_result_class.new(http_response, full_url, client, paginated_result_options) } @@ -193,7 +193,7 @@ end context 'with paged http response' do - let(:base_url) { 'http://rest.ably.io/channels/channel_name' } + let(:base_url) { 'http://main.realtime.ably.net/channels/channel_name' } let(:full_url) { "#{base_url}/messages" } let(:headers) do { diff --git a/spec/unit/realtime/connection_spec.rb b/spec/unit/realtime/connection_spec.rb index 9c227af0..08e66733 100644 --- a/spec/unit/realtime/connection_spec.rb +++ b/spec/unit/realtime/connection_spec.rb @@ -2,7 +2,7 @@ require 'shared/protocol_msgbus_behaviour' describe Ably::Realtime::Connection do - let(:client) { instance_double('Ably::Realtime::Client', logger: double('logger').as_null_object, recover: nil, endpoint: double('endpoint', host: 'realtime.ably.io')) } + let(:client) { instance_double('Ably::Realtime::Client', logger: double('logger').as_null_object, recover: nil, uri: double('uri', host: 'main.realtime.ably.net')) } subject do Ably::Realtime::Connection.new(client, {}).tap do |connection|