lib/rhack/frame.rb in rhack-1.3.1 vs lib/rhack/frame.rb in rhack-1.3.3
- old
+ new
@@ -88,21 +88,40 @@
@use_cache = use if use.in [true, false]
end
def inspect
sssize = @ss.size
- "<#Frame @ #{@ss.untargeted ? 'no target' : @loc.root}: #{sssize} #{sssize == 1 ? 'scout' : 'scouts'}#{', static'+(' => '+@static.protocol if @static.is(Hash)) if @static}, cookies #{@ss[0].cookieProc ? 'on' : 'off'}>"
+ "<#Frame @ #{@ss.untargeted ? 'no target' : @loc.root}: #{sssize} #{sssize == 1 ? 'scout' : 'scouts'}#{', static'+(' => '+@static.protocol if @static.is(Hash)) if @static}, cookies #{@ss[0].cookie_enabled ? 'on' : 'off'}>"
end
# All opts going in one hash.
# Opts for Frame:
- # :wait, :proc_result, :save_result, :zip, :thread_safe, :result, :stream, :raw, :xhr, :content_type
- # Opts passed to Page:
+ # :wait, :sync, :thread_safe, :raw, :proc_result, :save_result, :zip, :result, :stream
+ # ... processed and passed to Scout:
+ # :xhr, :content_type, :auth
+ # Opts passed to result:
# :xml, :html, :json, :hash, :eval, :load_scripts
- # Opts for Scout:
+ # Opts passed to Scout:
# :headers, :redir, :relvl
- # TODO: describe options
+ #
+ # @ :result : враппер результата исполнения; по умолчанию Page, для Client — если определён — Result; при асинхронном вызове будет возвращён незамедлительно
+ # @ &callback : в него будет передан инстанс result, а его результат будет записан в result#res (по умолчанию это ссылка на себя)
+ # @ :thread_safe : не использовать луп исполнения Curl::Multi#perform, а вызывать #perform прямо в этом треде; если установлен, то невозможно прерывание исполнения клавиатурой (продолжит работать, выполняя колбеки, в фоне), и невозможно задавать больше параллельных реквестов, чем разрешено параллельных соединений (просто застрянет)
+ # @ :sync : остановить (Thread#kill) perform-loop после исполнения всех запросов; подразумевает wait=true; при вызове одиночного реквеста подразумевает thread_safe=true
+ # @ :wait : ждать исполнения всех реквестов
+ # @ :save_result: возвращает #res для каждого инстанса result вместо самого инстанса; если не задан :proc_result, то подразумевает wait=true
+ # @ :proc_result: Proc, в который будет передан result#res, если задан также &callback; служит для создания вложенных блоков для клиентов; если =nil, то подразумевает wait=true
+ # @ :raw : сохраняем *только* тело ответа, без хедеров, без отладочной инфы в #res
+ # @ :raw + :sync : подразумевает save_result=true
+ # @ :xhr, :content_type, :auth : формируют хедеры X-Requested-With, Content-Type, Authorization для передачи в Scout
+ # @ :xhr : boolean
+ # @ :content_type : symbol<extension> | raw string
+ # @ :auth : "<username>:<password>"
+ #
+ # @ :zip, :stream и все опции для result : deprecated
+ #
+ # TODO: Семантически разделить синхронное и асинхронное выполнение запросов (не важно, серии или отдельных), с учётом, что асинхронность по сути своей перегружена и требуется, например, в очередях сообщений, но не в синхронных контроллерах Rails
def exec *args, &callback
many, order, orders, with_opts = interpret_request *args
L.log({:many => many, :order => order, :orders => orders, :with_opts => with_opts})
if !Johnson::Enabled and with_opts[:eval]
@@ -111,12 +130,19 @@
end
# JS Runtime is not thread-safe and must be created in curl thread
# if we aren't said explicitly about the opposite
Johnson::Runtime.set_browser_for_curl with_opts
- if many then exec_many orders, with_opts, &callback
- else exec_one order, with_opts, &callback end
+ if many
+ result = exec_many orders, with_opts, &callback
+ else
+ result = exec_one order, with_opts, &callback
+ end
+ if with_opts[:sync]
+ Curl.stop
+ end
+ result
end
alias :get :exec
alias :run :get
def interpret_request(*args)
@@ -216,12 +242,18 @@
opts[:wait] = true if !:wait.in(opts) and
:proc_result.in(opts) ? !opts[:proc_result] : opts[:save_result]
opts[:eval] = false if opts[:json] or opts[:hash] or opts[:raw]
opts[:load_scripts] = self if opts[:load_scripts]
- opts[:stream] = true if opts[:raw]
+ opts[:save_result] = true if opts[:wait] and opts[:raw]
+ if orders
+ opts[:thread_safe] = false if @ss.size < orders.size
+ else
+ opts[:thread_safe] = true if opts[:sync]
+ end
+
(opts[:headers] ||= {})['X-Requested-With'] = 'XMLHttpRequest' if opts[:xhr]
if opts[:content_type]
if opts[:content_type].is Symbol
if mime_type = MIME::Types.of(opts[:content_type])[0]
(opts[:headers] ||= {})['Content-Type'] = mime_type.content_type
@@ -231,10 +263,13 @@
end
else
(opts[:headers] ||= {})['Content-Type'] = opts[:content_type]
end
end
+ if opts[:auth]
+ (opts[:headers] ||= {})['Authorization'] = "Basic #{Base64.encode64(opts[:auth])}".chop
+ end
[many, order, orders, opts]
end
private
@@ -329,25 +364,25 @@
# must result in Page (default) or it's subclass
page = opts[:result].new
# if no spare scouts can be found, squad simply waits for first callbacks to complete
s = @ss.next
s.http.on_failure {|curl, error|
- if s.process_failure(*error)
+ s.process_failure(*error) {
# curl itself has decided not to retry a request
if opts[:raw]
page.res = s.error
elsif page.process(curl, opts)
run_callbacks! page, opts, &callback
# nothing to do here if process returns nil or false
end
- end
+ }
}
s.send(*(order << opts)) {|curl|
# there is a problem with storing html on disk
if order[0] == :loadGet and @write_to
# sometimes (about 2% for 100-threads-dling) when this string is calling
# no matter what +curl.res.body+ has contained here
- RMTools.rw @write_to+'/'+order[-2].sub(/^[a-z]+:\/\//, ''), curl.res.body.xml_to_utf
+ RMTools.rw @write_to+'/'+order[-2].sub(/^\w+:\/\//, ''), curl.res.body.xml_to_utf
end
if opts[:raw]
page.res = block_given? ? yield(curl) : curl.body_str
# here +curl.res.body+ becomes empty
# curl.res.body.+xml_to_utf+ -- maybe this is problem?
\ No newline at end of file