Class: Parse::Client
- Inherits:
-
Object
- Object
- Parse::Client
- Includes:
- API::Aggregate, API::Analytics, API::Batch, API::CloudFunctions, API::Config, API::Files, API::Hooks, API::Objects, API::Push, API::Schema, API::Server, API::Sessions, API::Users
- Defined in:
- lib/parse/client.rb
Overview
This class is the core and low level API for the Parse SDK REST interface that is used by the other components. It can manage multiple sessions, which means you can have multiple client instances pointing to different Parse Applications at the same time. It handles sending raw requests as well as providing Request/Response objects for all API handlers. The connection engine is Faraday, which means it is open to add any additional middleware for features you'd like to implement.
Defined Under Namespace
Modules: Connectable Classes: DuplicateValueError, ResponseError
Constant Summary collapse
- USER_AGENT_HEADER =
The user agent header key.
"User-Agent".freeze
- USER_AGENT_VERSION =
The value for the User-Agent header.
"Parse-Stack v#{Parse::Stack::VERSION}".freeze
- DEFAULT_RETRIES =
The default retry count
2- RETRY_DELAY =
The wait time in seconds between retries
1.5- LIVE_QUERY_LOOPBACK_HOSTS =
Hosts considered "loopback" for the cleartext-ws:// guard in #configure_live_query. Mirrors LiveQuery::Client::LOOPBACK_HOSTS so the explicit-URL path and the derived-URL path agree on what counts as local.
%w[localhost 127.0.0.1 ::1 [::1] 0.0.0.0].freeze
Constants included from API::Server
API::Server::DEPRECATED_SERVER_VERSION_BELOW
Constants included from API::Hooks
Class Attribute Summary collapse
-
.clients ⇒ Object
readonly
Returns the value of attribute clients.
Instance Attribute Summary collapse
-
#api_key ⇒ String
readonly
The Parse API key to be sent in every API request.
-
#application_id ⇒ String
(also: #app_id)
readonly
The Parse application identifier to be sent in every API request.
-
#cache ⇒ Moneta::Transformer, Moneta::Expires
The underlying cache store for caching API requests.
-
#master_key ⇒ String
readonly
The Parse master key for this application, which when set, will be sent in every API request.
-
#retry_limit ⇒ Integer
If set, returns the current retry count for this instance.
-
#server_url ⇒ String
readonly
The Parse server url that will be receiving these API requests.
Attributes included from API::Server
Attributes included from API::Config
Class Method Summary collapse
-
.client(conn = :default) ⇒ Parse::Client
Returns or create a new Parse::Client connection for the given connection name.
-
.client?(conn = :default) ⇒ Boolean
True if a Parse::Client has been configured.
-
.setup(opts = {}) { ... } ⇒ Client
Setup the a new client with the appropriate Parse app keys, middleware and options.
Instance Method Summary collapse
-
#clear_cache! ⇒ Object
Clear the client cache.
-
#configure_live_query(opts) ⇒ Object
private
Configure LiveQuery with the given options.
-
#delete(uri, body = nil, headers = {}) ⇒ Parse::Response
Send a DELETE request.
-
#get(uri, query = nil, headers = {}) ⇒ Parse::Response
Send a GET request.
-
#initialize(opts = {}) ⇒ Client
constructor
Create a new client connected to the Parse Server REST API endpoint.
-
#post(uri, body = nil, headers = {}) ⇒ Parse::Response
Send a POST request.
-
#put(uri, body = nil, headers = {}) ⇒ Parse::Response
Send a PUT request.
-
#request(method, uri = nil, body: nil, query: nil, headers: nil, opts: {}) ⇒ Parse::Response
Send a REST API request to the server.
-
#send_request(req) ⇒ Parse::Response
Send a Request object.
-
#url_prefix ⇒ String
The url prefix of the Parse Server url.
- #validate_live_query_url!(url, allow_insecure:) ⇒ Object private
- #warn_about_unknown_live_query_keys!(live_query_opts) ⇒ Object private
Methods included from API::Users
#create_user, #current_user, #delete_user, #fetch_user, #find_users, #login, #login_with_mfa, #logout, #request_password_reset, #set_service_auth_data, #signup, #update_user
Methods included from API::Sessions
Methods included from API::Server
#server_health, #server_info!, #server_version
Methods included from API::Schema
#create_schema, #schema, #schemas, #update_schema
Methods included from API::Push
Methods included from API::Objects
#create_object, #delete_object, #fetch_object, #find_objects, #update_object, #uri_path
Methods included from API::Hooks
#create_function, #create_trigger, #delete_function, #delete_trigger, #fetch_function, #fetch_trigger, #functions, #triggers, #update_function, #update_trigger
Methods included from API::Files
Methods included from API::Config
#config!, #config_entries, #update_config
Methods included from API::CloudFunctions
#call_function, #call_function_with_session, #trigger_job, #trigger_job_with_session
Methods included from API::Batch
Methods included from API::Aggregate
#aggregate_objects, #aggregate_pipeline, #aggregate_uri_path
Methods included from API::Analytics
Constructor Details
#initialize(opts = {}) ⇒ Client
Create a new client connected to the Parse Server REST API endpoint.
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 |
# File 'lib/parse/client.rb', line 446 def initialize(opts = {}) @server_url = opts[:server_url] || ENV["PARSE_SERVER_URL"] || Parse::Protocol::SERVER_URL @application_id = opts[:application_id] || opts[:app_id] || ENV["PARSE_SERVER_APPLICATION_ID"] || ENV["PARSE_APP_ID"] @api_key = opts[:api_key] || opts[:rest_api_key] || ENV["PARSE_SERVER_REST_API_KEY"] || ENV["PARSE_API_KEY"] @master_key = opts[:master_key] || ENV["PARSE_SERVER_MASTER_KEY"] || ENV["PARSE_MASTER_KEY"] @require_https = opts.fetch(:require_https, ENV["PARSE_REQUIRE_HTTPS"] == "true") @allow_faraday_proxy = opts.fetch(:allow_faraday_proxy, false) # Security check for HTTP usage (except localhost/127.0.0.1 for development) if @server_url&.start_with?("http://") && !@server_url.match?(%r{^http://(localhost|127\.0\.0\.1)(:|/)}) if @require_https raise ArgumentError, "[Parse::Client] HTTPS required but server URL uses HTTP: #{@server_url}. " \ "Set require_https: false or use an HTTPS URL." else warn "[Parse::Client] SECURITY WARNING: Using HTTP instead of HTTPS for Parse server. " \ "This exposes credentials and data to network interception. " \ "Use HTTPS in production: #{@server_url}" end end # Determine the HTTP adapter to use # Priority: explicit :adapter > :connection_pooling setting > default (pooling enabled) # Falls back to default adapter if net_http_persistent is not available if opts[:adapter] # User explicitly specified an adapter, use it directly adapter = opts[:adapter] = {} elsif opts[:connection_pooling] == false # User explicitly disabled connection pooling adapter = Faraday.default_adapter = {} elsif opts[:connection_pooling].is_a?(Hash) # User provided connection pooling with custom options if NET_HTTP_PERSISTENT_AVAILABLE adapter = :net_http_persistent = opts[:connection_pooling] else adapter = Faraday.default_adapter = {} end else # Default: use persistent connections for better performance (if available) if NET_HTTP_PERSISTENT_AVAILABLE adapter = :net_http_persistent = {} else adapter = Faraday.default_adapter = {} end end opts[:expires] ||= 3 if @server_url.nil? || @application_id.nil? || (@api_key.nil? && @master_key.nil?) raise Parse::Error::ConnectionError, "Please call Parse.setup(server_url:, application_id:, api_key:) to setup a client" end @server_url += "/" unless @server_url.ends_with?("/") # Resolve timeouts. Defaults guard the calling thread against an # unresponsive Parse Server (slowloris, hung dyno) which would # otherwise tie up Puma/Sidekiq workers indefinitely. open_timeout = opts.fetch(:open_timeout, (ENV["PARSE_OPEN_TIMEOUT"] || 5).to_i) read_timeout = opts.fetch(:timeout, (ENV["PARSE_TIMEOUT"] || 30).to_i) #Configure Faraday opts[:faraday] ||= {} # Guard against silent TLS downgrade or attacker-controlled proxy via # opts[:faraday]. The require_https check earlier only inspects the URL # scheme; without this guard a caller passing # faraday: { ssl: { verify: false }, proxy: "http://attacker" } # would neuter TLS verification on an HTTPS connection. validate_faraday_opts!(opts[:faraday]) opts[:faraday].merge!(:url => @server_url) @conn = Faraday.new(opts[:faraday]) do |conn| # Apply timeouts before any user-supplied middleware sees a request. conn..timeout = read_timeout if read_timeout > 0 conn..open_timeout = open_timeout if open_timeout > 0 #conn.request :json # Configure logging if enabled if opts[:logging].present? # Configure the new structured logging middleware Parse::Middleware::Logging.enabled = true Parse::Middleware::Logging.logger = opts[:logger] if opts[:logger] case opts[:logging] when :debug Parse::Middleware::Logging.log_level = :debug Parse::Middleware::BodyBuilder.logging = true when :warn Parse::Middleware::Logging.log_level = :warn else Parse::Middleware::Logging.log_level = :info end end # This middleware handles sending the proper authentication headers to Parse # on each request. # this is the required authentication middleware. Should be the first thing # so that other middlewares have access to the env that is being set by # this middleware. First added is first to brocess. conn.use Parse::Middleware::Authentication, application_id: @application_id, master_key: @master_key, api_key: @api_key # Request/response logging middleware (configured via Parse.logging_enabled) conn.use Parse::Middleware::Logging # Performance profiling middleware (configured via Parse.profiling_enabled) conn.use Parse::Middleware::Profiling # This middleware turns the result from Parse into a Parse::Response object # and making sure request that are going out, follow the proper MIME format. # We place it after the Authentication middleware in case we need to use then # authentication information when building request and responses. conn.use Parse::Middleware::BodyBuilder if opts[:cache].present? if opts[:expires].to_i <= 0 warn "[Parse::Client] Cache store provided but :expires is not set or is 0. " \ "Caching will be disabled. Set :expires to enable caching (e.g., expires: 10)." else # advanced: provide a REDIS url, we'll configure a Moneta Redis store. if opts[:cache].is_a?(String) && opts[:cache].starts_with?("redis://") begin opts[:cache] = Moneta.new(:Redis, url: opts[:cache]) rescue LoadError puts "[Parse::Middleware::Caching] Did you forget to load the redis gem (Gemfile)?" raise end end unless [:key?, :[], :delete, :store].all? { |method| opts[:cache].respond_to?(method) } raise ArgumentError, "Parse::Client option :cache needs to be a type of Moneta store" end # If the caller passed a `Parse::Cache::Redis` wrapper, let its # built-in namespace flow through automatically. An explicit # `cache_namespace:` still wins so callers can override. if defined?(Parse::Cache::Redis) && opts[:cache].is_a?(Parse::Cache::Redis) opts[:cache_namespace] ||= opts[:cache].namespace end self.cache = opts[:cache] conn.use Parse::Middleware::Caching, self.cache, { expires: opts[:expires].to_i, # Optional `cache_namespace:` prefixes every key so two Parse # apps sharing one Redis don't collide on `mk:/classes/Song/abc`. # Explicit only — we do NOT auto-derive from app_id to keep # existing single-app deployments backward-compatible. namespace: opts[:cache_namespace], } # Inform about opt-in cache behavior unless Parse.default_query_cache warn "[Parse::Client] Caching middleware enabled (expires: #{opts[:expires]}s). " \ "Queries do NOT use cache by default. Use `cache: true` on queries to opt-in, " \ "or set `Parse.default_query_cache = true` for opt-out behavior." end end end yield(conn) if block_given? # Configure the adapter with optional settings # For net_http_persistent: # - pool_size must be passed as an adapter argument (constructor param, no setter) # - idle_timeout and keep_alive have setters and are configured in the block if .any? # Extract constructor arguments for the adapter adapter_args = {} adapter_args[:pool_size] = [:pool_size] if [:pool_size] conn.adapter adapter, **adapter_args do |http| http.idle_timeout = [:idle_timeout] if [:idle_timeout] http.keep_alive = [:keep_alive] if [:keep_alive] end else conn.adapter adapter end end # Faraday's constructor may still synthesise a ProxyOptions from # HTTPS_PROXY/HTTP_PROXY env vars regardless of the `proxy: nil` # we pass in opts. Clear the proxy on the connection itself to be # sure no env-derived MITM survives. @conn.proxy = nil if !@allow_faraday_proxy && @conn.respond_to?(:proxy=) Parse::Client.clients[:default] ||= self # Configure LiveQuery if URL provided configure_live_query(opts) end |
Class Attribute Details
.clients ⇒ Object (readonly)
Returns the value of attribute clients.
298 299 300 |
# File 'lib/parse/client.rb', line 298 def clients @clients end |
Instance Attribute Details
#api_key ⇒ String (readonly)
The Parse API key to be sent in every API request.
286 |
# File 'lib/parse/client.rb', line 286 attr_accessor :cache |
#application_id ⇒ String (readonly) Also known as: app_id
The Parse application identifier to be sent in every API request.
286 |
# File 'lib/parse/client.rb', line 286 attr_accessor :cache |
#cache ⇒ Moneta::Transformer, Moneta::Expires
The underlying cache store for caching API requests.
286 287 288 |
# File 'lib/parse/client.rb', line 286 def cache @cache end |
#master_key ⇒ String (readonly)
The Parse master key for this application, which when set, will be sent in every API request. (There is a way to prevent this on a per request basis.)
286 |
# File 'lib/parse/client.rb', line 286 attr_accessor :cache |
#retry_limit ⇒ Integer
If set, returns the current retry count for this instance. Otherwise, returns DEFAULT_RETRIES. Set to 0 to disable retry mechanism.
286 |
# File 'lib/parse/client.rb', line 286 attr_accessor :cache |
#server_url ⇒ String (readonly)
The Parse server url that will be receiving these API requests. By default this will be Protocol::SERVER_URL.
286 |
# File 'lib/parse/client.rb', line 286 attr_accessor :cache |
Class Method Details
.client(conn = :default) ⇒ Parse::Client
Returns or create a new Parse::Client connection for the given connection name.
310 311 312 |
# File 'lib/parse/client.rb', line 310 def client(conn = :default) @clients[conn] ||= self.new end |
.client?(conn = :default) ⇒ Boolean
Returns true if a Parse::Client has been configured.
302 303 304 |
# File 'lib/parse/client.rb', line 302 def client?(conn = :default) @clients[conn].present? end |
.setup(opts = {}) { ... } ⇒ Client
Setup the a new client with the appropriate Parse app keys, middleware and options.
329 330 331 |
# File 'lib/parse/client.rb', line 329 def setup(opts = {}, &block) @clients[:default] = self.new(opts, &block) end |
Instance Method Details
#clear_cache! ⇒ Object
Clear the client cache
783 784 785 |
# File 'lib/parse/client.rb', line 783 def clear_cache! self.cache.clear if self.cache.present? end |
#configure_live_query(opts) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Configure LiveQuery with the given options
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 |
# File 'lib/parse/client.rb', line 691 def configure_live_query(opts) live_query_url = opts[:live_query_url] || ENV["PARSE_LIVE_QUERY_URL"] return unless live_query_url || opts[:live_query] require_relative "live_query" live_query_opts = opts[:live_query].is_a?(Hash) ? opts[:live_query] : {} resolved_url = live_query_url || live_query_opts[:url] # Refuse explicit `ws://` against a non-loopback host unless # `allow_insecure: true` is also passed in `live_query:`. The # downstream `derive_websocket_url` path already enforces this for # URLs derived from a Parse Server `http://` URL, but an explicit # `live_query: { url: "ws://prod-host" }` or # `live_query_url: "ws://prod-host"` bypassed it — the master key # and any session token would ride the connect frame in cleartext. validate_live_query_url!(resolved_url, allow_insecure: live_query_opts[:allow_insecure]) # Warn (don't raise) on `live_query: { ... }` keys that are not # `Parse::LiveQuery::Configuration` setters. The block form would # otherwise silently swallow typos like # `live_query: { ssl_min_versoin: :TLSv1_3 }` and leave TLS at the # default, losing the operator's intent. The pre-fix kwargs form # raised `ArgumentError` here; this restores the surface without # making it a hard failure for unknown-but-harmless keys. warn_about_unknown_live_query_keys!(live_query_opts) Parse::LiveQuery.configure do |config| config.application_id = @application_id if @application_id config.client_key = @api_key if @api_key config.master_key = @master_key if @master_key # Apply hash-form options first so the resolved URL (which honors # top-level `live_query_url:` over `live_query: { url: }`) wins. # Without this, the loop would re-write `config.url` from the # hash and silently invert the documented precedence. live_query_opts.each do |key, value| next if key == :url setter = "#{key}=" config.public_send(setter, value) if config.respond_to?(setter) end config.url = resolved_url if resolved_url end end |
#delete(uri, body = nil, headers = {}) ⇒ Parse::Response
Send a DELETE request.
1040 1041 1042 |
# File 'lib/parse/client.rb', line 1040 def delete(uri, body = nil, headers = {}) request :delete, uri, body: body, headers: headers end |
#get(uri, query = nil, headers = {}) ⇒ Parse::Response
Send a GET request.
1013 1014 1015 |
# File 'lib/parse/client.rb', line 1013 def get(uri, query = nil, headers = {}) request :get, uri, query: query, headers: headers end |
#post(uri, body = nil, headers = {}) ⇒ Parse::Response
Send a POST request.
1022 1023 1024 |
# File 'lib/parse/client.rb', line 1022 def post(uri, body = nil, headers = {}) request :post, uri, body: body, headers: headers end |
#put(uri, body = nil, headers = {}) ⇒ Parse::Response
Send a PUT request.
1031 1032 1033 |
# File 'lib/parse/client.rb', line 1031 def put(uri, body = nil, headers = {}) request :put, uri, body: body, headers: headers end |
#request(method, uri = nil, body: nil, query: nil, headers: nil, opts: {}) ⇒ Parse::Response
Send a REST API request to the server. This is the low-level API used for all requests to the Parse server with the provided options. Every request sent to Parse through the client goes through the configured set of middleware that can be modified by applying different headers or specific options. This method supports retrying requests a few times when a ServiceUnavailableError is raised.
834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 |
# File 'lib/parse/client.rb', line 834 def request(method, uri = nil, body: nil, query: nil, headers: nil, opts: {}) # Pre-declare locals referenced inside rescue blocks so CodeQL's # uninitialized-variable analysis is satisfied even if an exception # raises before the natural assignment site. response = nil _retry_count = nil _retry_delay = nil _request = nil # Kwarg-absorption guard. The `**opts` splat in API helper methods # (lib/parse/api/*.rb) absorbs a caller-passed `opts: { ... }` # keyword as a key named `:opts` rather than as the request options # hash itself. The auth context (session_token, use_master_key) # buried under :opts then never reaches the request — the call # silently goes out anonymous (or master, if one is configured), # which is a permission-downgrade footgun. Fail loudly here so the # bug surfaces in dev/test instead of in production. if opts.is_a?(Hash) && opts[:opts].is_a?(Hash) raise ArgumentError, "Parse::Client#request received nested `opts: { opts: { ... } }` — " \ "pass session_token: / use_master_key: directly as keywords, " \ "not wrapped in an `opts:` hash. " \ "Bad: Parse.client.create_object('X', body, opts: { session_token: t }) " \ "Good: Parse.client.create_object('X', body, session_token: t, use_master_key: false)" end _retry_count ||= self.retry_limit if opts[:retry] == false _retry_count = 0 elsif opts[:retry].to_i > 0 _retry_count = opts[:retry] end headers ||= {} # if the first argument is a Parse::Request object, then construct it _request = nil if method.is_a?(Request) _request = method method = _request.method uri ||= _request.path query ||= _request.query body ||= _request.body headers.merge! _request.headers else _request = Parse::Request.new(method, uri, body: body, headers: headers, opts: opts) end # http method method = method.downcase.to_sym # set the User-Agent headers[USER_AGENT_HEADER] = USER_AGENT_VERSION if opts[:cache] == false headers[Parse::Middleware::Caching::CACHE_CONTROL] = "no-cache" elsif opts[:cache] == :write_only # Write-only mode: skip reading from cache, but still write to cache # Useful for fetch!/reload! which want fresh data but should update cache headers[Parse::Middleware::Caching::CACHE_WRITE_ONLY] = "true" elsif opts[:cache].is_a?(Numeric) # specify the cache duration of this request headers[Parse::Middleware::Caching::CACHE_EXPIRES_DURATION] = opts[:cache].to_s end # Resolve the auth context in three layers: # 1. explicit per-call `use_master_key:` and `session_token:` # 2. ambient session set by `Parse.with_session { ... }` (fiber-local) # 3. process-wide `Parse.client_mode` flag — when true, master key is # never sent unless the caller explicitly passed `use_master_key: true` explicit_master = opts.key?(:use_master_key) if opts[:use_master_key] == false headers[Parse::Middleware::Authentication::DISABLE_MASTER_KEY] = "true" elsif Parse.client_mode && opts[:use_master_key] != true # client mode defaults master key OFF unless explicitly opted in headers[Parse::Middleware::Authentication::DISABLE_MASTER_KEY] = "true" end token = opts[:session_token] # When no explicit token was passed AND the caller didn't ask to send # the master key, fall through to the fiber-local ambient set by # `Parse.with_session`. Explicit `use_master_key: true` is treated as # a deliberate admin call and skips the ambient — otherwise an # `admin.do_thing(use_master_key: true)` nested inside a # `with_session(user)` block would silently downgrade. if token.nil? && !(explicit_master && opts[:use_master_key] == true) ambient = Parse.current_session_token token = ambient if ambient.is_a?(String) && !ambient.empty? end if token.present? token = token.session_token if token.respond_to?(:session_token) headers[Parse::Middleware::Authentication::DISABLE_MASTER_KEY] = "true" headers[Parse::Protocol::SESSION_TOKEN] = token end #if it is a :get request, then use query params, otherwise body. params = (method == :get ? query : body) || {} # if the path does not start with the '/1/' prefix, then add it to be nice. # actually send the request and return the body response_env = @conn.send(method, uri, params, headers) response = response_env.body response.request = _request case response.http_status when 401, 403 Parse::Client._safe_warn("AuthenticationError", response) raise Parse::Error::AuthenticationError, response when 400, 408 if response.code == Parse::Response::ERROR_TIMEOUT || response.code == 143 #"net/http: timeout awaiting response headers" Parse::Client._safe_warn("TimeoutError", response) raise Parse::Error::TimeoutError, response end when 404 unless response.object_not_found? Parse::Client._safe_warn("ConnectionError", response) raise Parse::Error::ConnectionError, response end when 405, 406 Parse::Client._safe_warn("ProtocolError", response) raise Parse::Error::ProtocolError, response when 429 # Request over the throttle limit Parse::Client._safe_warn("RequestLimitExceededError", response) raise Parse::Error::RequestLimitExceededError, response when 500, 503 Parse::Client._safe_warn("ServiceUnavailableError", response) raise Parse::Error::ServiceUnavailableError, response end if response.error? if response.code <= Parse::Response::ERROR_SERVICE_UNAVAILABLE Parse::Client._safe_warn("ServiceUnavailableError", response) raise Parse::Error::ServiceUnavailableError, response elsif response.code <= 100 Parse::Client._safe_warn("ServerError", response) raise Parse::Error::ServerError, response elsif response.code == Parse::Response::ERROR_EXCEEDED_BURST_LIMIT Parse::Client._safe_warn("RequestLimitExceededError", response) raise Parse::Error::RequestLimitExceededError, response elsif response.code == 209 # Error 209: invalid session token Parse::Client._safe_warn("InvalidSessionTokenError", response) raise Parse::Error::InvalidSessionTokenError, response end end response rescue Parse::Error::RequestLimitExceededError, Parse::Error::ServiceUnavailableError => e if _retry_count > 0 warn "[Parse:Retry] Retries remaining #{_retry_count} : #{response.request}" _retry_count -= 1 # Use Retry-After header if available, otherwise use exponential backoff retry_after = response.retry_after if response.respond_to?(:retry_after) if retry_after && retry_after > 0 _retry_delay = retry_after warn "[Parse:Retry] Using Retry-After header: #{_retry_delay}s" else # Deterministic exponential backoff with +/-25% jitter. Never zero — # zero-wait retries amplify DoS against upstream and stampede on 429. backoff_delay = RETRY_DELAY * (self.retry_limit - _retry_count) _retry_delay = backoff_delay * (0.75 + rand * 0.5) end sleep _retry_delay if _retry_delay > 0 retry end raise rescue Faraday::ClientError, Net::OpenTimeout => e if _retry_count > 0 warn "[Parse:Retry] Retries remaining #{_retry_count} : #{_request}" _retry_count -= 1 backoff_delay = RETRY_DELAY * (self.retry_limit - _retry_count) _retry_delay = backoff_delay * (0.75 + rand * 0.5) sleep _retry_delay if _retry_delay > 0 retry end raise Parse::Error::ConnectionError, "#{_request} : #{e.class} - #{e.}" end |
#send_request(req) ⇒ Parse::Response
Send a Request object.
1048 1049 1050 1051 |
# File 'lib/parse/client.rb', line 1048 def send_request(req) #Parse::Request object raise ArgumentError, "Object not of Parse::Request type." unless req.is_a?(Parse::Request) request req.method, req.path, req.body, req.headers end |
#url_prefix ⇒ String
Returns the url prefix of the Parse Server url.
778 779 780 |
# File 'lib/parse/client.rb', line 778 def url_prefix @conn.url_prefix end |
#validate_live_query_url!(url, allow_insecure:) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
739 740 741 742 743 744 745 746 747 748 749 750 751 752 |
# File 'lib/parse/client.rb', line 739 def validate_live_query_url!(url, allow_insecure:) return unless url.is_a?(String) && url.start_with?("ws://") host = URI.parse(url).host.to_s rescue "" return if LIVE_QUERY_LOOPBACK_HOSTS.include?(host) return if allow_insecure raise ArgumentError, "[Parse::Client] Refusing explicit insecure LiveQuery URL #{url.inspect}. " \ "The connect frame carries the master key and any session token in " \ "plaintext on this socket. Use wss:// for routable hosts, or pass " \ "`live_query: { allow_insecure: true }` to opt into cleartext for " \ "local development on a non-loopback address." end |
#warn_about_unknown_live_query_keys!(live_query_opts) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
755 756 757 758 759 760 761 762 763 764 765 766 767 |
# File 'lib/parse/client.rb', line 755 def warn_about_unknown_live_query_keys!(live_query_opts) return unless live_query_opts.is_a?(Hash) && live_query_opts.any? probe = Parse::LiveQuery::Configuration.new unknown = live_query_opts.keys.reject { |k| probe.respond_to?("#{k}=") } return if unknown.empty? warn "[Parse::Client] Ignoring unknown live_query option(s): " \ "#{unknown.inspect}. Valid keys are Parse::LiveQuery::Configuration " \ "setters (url, application_id, client_key, master_key, ping_interval, " \ "pong_timeout, allow_insecure, ssl_min_version, ssl_max_version, " \ "logging_enabled, log_level, ...). Check for typos." end |