@@ -4,15 +4,18 @@ module SerpApi
44 # Client for SerpApi.com
55 #
66 class Client
7- include Errors
87
98 # Backend service URL
109 BACKEND = 'serpapi.com' . freeze
1110
1211 # HTTP timeout requests
1312 attr_reader :timeout ,
1413 # Query parameters
15- :params
14+ :params ,
15+ # HTTP persistent
16+ :persistent ,
17+ # HTTP.rb client
18+ :socket
1619
1720 # Constructor
1821 #
@@ -30,21 +33,32 @@ class Client
3033 # key can be either a symbol or a string.
3134 #
3235 # @param [Hash] params default for the search
33- def initialize ( params = { } , _adapter = :net_http )
34- # set default read timeout
35- @timeout = params [ :timeout ] || params [ 'timeout' ] || 120
36+ def initialize ( params = { } )
37+ # store client HTTP request timeout
38+ @timeout = params [ :timeout ] || 120
3639 @timeout . freeze
3740
41+ # enable HTTP persistent mode
42+ @persistent = params [ :persistent ] || true
43+ @persistent . freeze
44+
3845 # delete this client only configuration keys
39- params . delete ( 'timeout' ) if params . key? 'timeout'
40- params . delete ( :timeout ) if params . key? :timeout
46+ %i( timeout persistent ) . each do |option |
47+ params . delete ( option ) if params . key? ( option )
48+ end
4149
42- # set default params safely in memory
50+ # set default serpapi related parameters
4351 @params = params . clone || { }
52+
53+ # track ruby library as a client for statistic purpose
54+ @params [ :source ] = 'serpapi-ruby:' << SerpApi ::VERSION
55+
4456 @params . freeze
4557
46- # setup connection socket
47- @socket = Faraday . new ( url : "https://#{ BACKEND } " )
58+ # create connection socket
59+ if persistent?
60+ @socket = HTTP . persistent ( "https://#{ BACKEND } " )
61+ end
4862 end
4963
5064 # perform a search using SerpApi.com
@@ -83,7 +97,7 @@ def location(params = {})
8397 # @param [Symbol] format :json or :html (default: json, optional)
8498 # @return [String|Hash] raw html or JSON / Hash
8599 def search_archive ( search_id , format = :json )
86- raise SerpApiException , 'format must be json or html' unless [ :json , :html ] . include? ( format )
100+ raise SerpApiError , 'format must be json or html' unless [ :json , :html ] . include? ( format )
87101
88102 get ( "/searches/#{ search_id } .#{ format } " , format )
89103 end
@@ -105,46 +119,60 @@ def api_key
105119 @params [ :api_key ]
106120 end
107121
122+ # close open connection
123+ def close
124+ @socket . close if @socket
125+ end
126+
108127 private
109128
110129 # @return [Hash] query parameter
111130 def query ( params )
112131 # merge default params with custom params
113132 q = @params . merge ( params )
114133
115- # set ruby client
116- q [ :source ] = 'serpapi-ruby:' << SerpApi ::VERSION
117-
118134 # delete empty key/value
119135 q . compact
120136 end
121137
138+ # @return [Boolean] HTTP session persistent enabled
139+ def persistent?
140+ persistent
141+ end
142+
122143 # get HTTP query formatted results
123144 #
124145 # @param [String] endpoint HTTP service uri
125146 # @param [Symbol] decoder type :json or :html
126147 # @param [Hash] params custom search inputs
127148 # @param [Boolean] symbolize_names if true, convert JSON keys to symbols
128- # @return decoded payload as JSON / Hash or String
149+ # @return decoded response as JSON / Hash or String
129150 def get ( endpoint , decoder = :json , params = { } , symbolize_names = true )
130- payload = @socket . get ( endpoint ) do |req |
131- req . params = query ( params )
132- req . options . timeout = timeout
151+ # execute get via open socket
152+ if persistent?
153+ response = @socket . get ( endpoint , params : query ( params ) )
154+ else
155+ response = HTTP . timeout ( timeout ) . get ( "https://#{ BACKEND } #{ endpoint } " , params : query ( params ) )
133156 end
134- # read http response
135- data = payload . body
136- # decode payload using JSON native parser
137- if decoder == :json
138- data = JSON . parse ( data , symbolize_names : symbolize_names )
139- if data . instance_of? ( Hash ) && data . key? ( 'error' )
140- raise SerpApiException , "get failed with error: #{ data [ 'error' ] } from url: #{ endpoint } , params: #{ params } , decoder: #{ decoder } , http status: #{ payload . status } "
157+
158+ # decode response using JSON native parser
159+ case decoder
160+ when :json
161+ # read http response
162+ data = JSON . parse ( response . body , symbolize_names : symbolize_names )
163+ if data . instance_of? ( Hash ) && data . key? ( :error )
164+ raise SerpApiError , "HTTP request failed with error: #{ data [ :error ] } from url: https://#{ BACKEND } #{ endpoint } , params: #{ params } , decoder: #{ decoder } , response status: #{ response . status } "
165+ elsif response . status != 200
166+ raise SerpApiError , "HTTP request failed with response status: #{ response . status } reponse: #{ data } on get url: https://#{ BACKEND } #{ endpoint } , params: #{ params } , decoder: #{ decoder } "
141167 end
142- raise SerpApiException , "get failed with response status: #{ payload . status } reponse: #{ data } on get url: #{ endpoint } , params: #{ params } , decoder: #{ decoder } " if payload . status != 200
168+
169+ # discard response body
170+ response . flush if persistent?
171+
172+ return data
173+ else
174+ return response . body
143175 end
144- # return raw HTML
145- data
146- rescue Faraday ::Error => e
147- raise SerpApiException , "fail: get url: #{ endpoint } caused by #{ e . class } : #{ e . message } (params: #{ params } , decoder: #{ decoder } )"
148176 end
149177 end
150178end
0 commit comments