2019-11-30 08:46:49 +01:00
;;; ein-query.el --- jQuery like interface on to of url-retrieve -*- lexical-binding: t -*-
;; Copyright (C) 2012- Takafumi Arakaki
;; Author: Takafumi Arakaki <aka.tkf at gmail.com>
;; This file is NOT part of GNU Emacs.
;; ein-query.el is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; ein-query.el is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with ein-query.el. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;
;;; Code:
( require 'request )
( require 'url )
( require 'ein-core )
( require 'ein-log )
;;; Utils
( defun ein:safe-funcall-packed ( packed &rest args )
( when packed
( ein:log-ignore-errors ( apply #' ein:funcall-packed packed args ) ) ) )
;;; Variables
( defcustom ein:query-timeout
( if ( eq request-backend 'url-retrieve ) 1000 nil )
" Default query timeout for HTTP access in millisecond.
Setting this to ` nil ' means no timeout.
If you have ` ` curl ` ` command line program, it is automatically set to
` nil ' as ` ` curl ` ` is reliable than ` url-retrieve ' therefore no need for
a workaround ( see below ) .
If you do the same operation before the timeout, old operation
will NO LONGER be canceled ( as it the cookie jar gets clobbered when curl
aborts ) . Instead you will see Race! in debug messages.
. . note:: This value exists because it looks like ` url-retrieve '
occasionally fails to finish \(start? ) querying. Timeout is
used to let user notice that their operation is not finished.
It also prevent opening a lot of useless process buffers.
You will see them when closing Emacs if there is no timeout.
If you know how to fix the problem with ` url-retrieve ', please
let me know or send pull request at github!
\(Related bug report in Emacs bug tracker:
http://debbugs.gnu.org/cgi/bugreport.cgi?bug=11469 ) "
:type ' ( choice ( integer :tag " Timeout [ms] " 5000 )
( const :tag " No timeout " nil ) )
:group 'ein )
;;; Functions
( defvar ein:query-running-process-table ( make-hash-table :test 'equal ) )
( defvar ein:query-xsrf-cache ( make-hash-table :test 'equal )
" Hack: remember the last xsrf token by host in case we catch cookie jar in transition. The proper fix is to sempahore between competing curl processes. " )
( defun ein:query-prepare-header ( url settings &optional securep )
" Ensure that REST calls to the jupyter server have the correct _xsrf argument. "
( let* ( ( host ( url-host ( url-generic-parse-url url ) ) )
( cookies ( request-cookie-alist host " / " securep ) )
( xsrf ( or ( cdr ( assoc-string " _xsrf " cookies ) )
( gethash host ein:query-xsrf-cache ) ) ) )
( ein:log 'debug " EIN:QUERY-PREPARE-HEADER: Found xsrf: %s " xsrf )
( setq settings ( plist-put settings :headers
( append ( plist-get settings :headers )
( list ( cons " User-Agent " " Mozilla/5.0 " ) ) ) ) )
( when xsrf
( setq settings ( plist-put settings :headers
( append ( plist-get settings :headers )
( list ( cons " X-XSRFTOKEN " xsrf ) ) ) ) )
( setf ( gethash host ein:query-xsrf-cache ) xsrf ) )
( setq settings ( plist-put settings :encoding 'binary ) )
settings ) )
( defcustom ein:max-simultaneous-queries 100
" Limit number of simultaneous queries to Jupyter server.
If too many calls to ` request ' are made at once Emacs may
complaint and raise a 'Too Many Files ' exception. By setting this
variable to a reasonable value you can avoid this situation. "
:group 'ein
:type 'integer )
( let ( ( checked-curl-version nil ) )
( defun ein:warn-on-curl-version ( )
( let ( ( curl ( executable-find request-curl ) ) )
( unless checked-curl-version
( setq checked-curl-version t )
( with-temp-buffer
( call-process curl nil t nil " --version " )
( goto-char ( point-min ) )
( when ( search-forward " mingw32 " nil t )
( warn " The current version of curl (%s) may not work with ein. We recommend you install the latest, official version from the curl website: https://curl.haxx.se " ( buffer-string ) ) ) ) ) ) ) )
( defsubst ein:query-enforce-curl ( )
( ein:warn-on-curl-version )
( unless ( eq request-backend 'curl )
( ein:display-warning
( format " request-backend: %s unsupported " request-backend ) )
( if ( executable-find request-curl )
( setq request-backend 'curl )
( ein:display-warning
( format " The %s program was not found " request-curl ) :error ) ) ) )
( cl-defun ein:query-singleton-ajax ( key url &rest settings
&key ( timeout ein:query-timeout ) &allow-other-keys )
" Do not cancel the old process if there is a process associated with
KEY, then call ` request ' with URL and SETTINGS. KEY is compared by
` equal '. "
( ein:query-enforce-curl )
( with-local-quit
( when timeout
( setq settings ( plist-put settings :timeout ( / timeout 1000.0 ) ) ) )
( cl-loop do ( ein:query-running-process-table )
for running = ( hash-table-count ein:query-running-process-table )
until ( < running ein:max-simultaneous-queries )
do ( ein:log 'warn " ein:query-singleton-ajax: %d running processes "
running )
do ( sleep-for 3 ) )
( ein:aif ( gethash key ein:query-running-process-table )
( unless ( request-response-done-p it )
( ein:log 'debug " Race! %s %s " key ( request-response-data it ) ) ) )
( let ( ( response ( apply #' request ( url-encode-url url )
( ein:query-prepare-header url settings ) ) ) )
( puthash key response ein:query-running-process-table )
response ) ) )
( defun ein:query-running-process-table ( )
" Keep track of unfinished curl requests. "
( maphash
( lambda ( key buffer )
( when ( request-response-done-p buffer )
( remhash key ein:query-running-process-table ) ) )
ein:query-running-process-table ) )
( defun ein:get-response-redirect ( response )
" Determine if the query has been redirected, and if so return then URL the request was redirected to. "
( let ( ( url ( url-generic-parse-url ( format " %s " ( request-response-url response ) ) ) ) )
( format " %s://%s:%s "
( url-type url )
( url-host url )
2019-12-20 13:38:19 +01:00
( url-port url ) ) ) )
2019-11-30 08:46:49 +01:00
;;; Cookie
( defalias 'ein:query-get-cookie 'request-cookie-string )
( provide 'ein-query )
;;; ein-query.el ends here