2012-05-07 14:41:15 +02:00
;;; ein-websocket.el --- Wrapper of websocket.el
;; Copyright (C) 2012- Takafumi Arakaki
2012-07-01 20:18:05 +02:00
;; Author: Takafumi Arakaki <aka.tkf at gmail.com>
2012-05-07 14:41:15 +02:00
;; This file is NOT part of GNU Emacs.
;; ein-websocket.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-websocket.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-websocket.el. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;
;;; Code:
( eval-when-compile ( require 'cl ) )
( require 'websocket )
2012-08-28 15:26:32 +02:00
( require 'ein-core )
2015-06-29 07:07:53 -05:00
( require 'url-cookie )
2016-12-14 12:32:26 -06:00
( require 'request )
2012-05-07 14:41:15 +02:00
( defstruct ein:$websocket
2012-05-13 16:13:52 +02:00
" A wrapper object of `websocket' .
` ein:$websocket-ws ' : an instance returned by ` websocket-open '
` ein:$websocket-onmessage ' : function called with ( PACKET &rest ARGS ) '
` ein:$websocket-onclose ' : function called with ( WEBSOCKET &rest ARGS ) '
` ein:$websocket-onopen ' : function called with ( &rest ARGS ) '
` ein:$websocket-onmessage-args ' : optional arguments for onmessage callback '
` ein:$websocket-onclose-args ' : optional arguments for onclose callback '
` ein:$websocket-onopen-args ' : optional arguments for onopen callback '
` ein:$websocket-closed-by-client ' : t/nil '
"
2012-05-07 14:41:15 +02:00
ws
2012-05-13 16:13:52 +02:00
onmessage
onmessage-args
onclose
onclose-args
onopen
onopen-args
closed-by-client )
2012-05-07 14:41:15 +02:00
2016-12-14 12:32:26 -06:00
;; Fix issues reading cookies in request when using curl backend
( defun fix-request-netscape-cookie-parse ( next-method )
" Parse Netscape/Mozilla cookie format. "
( goto-char ( point-min ) )
( let ( ( tsv-re ( concat " ^ \\ = "
( cl-loop repeat 6 concat " \\ ([^ \t \n ]+ \\ ) \t " )
" \\ (.* \\ ) " ) )
cookies )
( forward-line 3 ) ;; Skip header (first three lines)
( while
( and
( cond
( ( re-search-forward " ^ \\ =$ " nil t ) )
( ( re-search-forward tsv-re )
( push ( cl-loop for i from 1 to 7 collect ( match-string i ) )
cookies )
t ) )
( = ( forward-line 1 ) 0 )
( not ( = ( point ) ( point-max ) ) ) ) )
( setq cookies ( nreverse cookies ) )
( cl-loop for ( domain flag path secure expiration name value ) in cookies
collect ( list domain
( equal flag " TRUE " )
path
( equal secure " TRUE " )
( string-to-number expiration )
name
value ) ) ) )
( advice-add 'request--netscape-cookie-parse :around #' fix-request-netscape-cookie-parse )
2016-12-13 17:47:23 -06:00
;; This seems redundant, but websocket does not work otherwise.
2015-06-29 07:07:53 -05:00
( defun ein:websocket--prepare-cookies ( url )
( let* ( ( parsed-url ( url-generic-parse-url url ) )
( host-port ( if ( url-port-if-non-default parsed-url )
( format " %s:%s " ( url-host parsed-url ) ( url-port parsed-url ) )
( url-host parsed-url ) ) )
2016-04-08 21:22:14 -05:00
( securep ( string-match " ^wss:// " url ) )
2016-12-13 17:47:23 -06:00
( http-only-cookies ( request-cookie-alist ( concat " #HttpOnly_ " ( url-host ( url-generic-parse-url url ) ) ) " / " securep ) ) ;; Current version of Jupyter store cookies as HttpOnly)
2016-04-08 21:22:14 -05:00
( cookies ( request-cookie-alist ( url-host ( url-generic-parse-url url ) ) " / " securep ) ) )
2016-12-13 17:47:23 -06:00
( when ( or cookies http-only-cookies )
2015-08-26 20:45:09 -05:00
( ein:log 'debug " Storing cookies in prep for opening websocket (%s) " cookies )
2016-12-13 17:47:23 -06:00
( dolist ( c ( append cookies http-only-cookies ) )
2016-04-08 21:22:14 -05:00
( url-cookie-store ( car c ) ( cdr c ) nil host-port ( car ( url-path-and-query parsed-url ) ) securep ) ) ) ) )
2015-06-29 07:07:53 -05:00
2012-05-07 14:41:15 +02:00
2014-04-14 12:48:55 -05:00
;; Issues opening websockets in IPython 2.0, think it is related to
;; http://stackoverflow.com/questions/22202182/error-on-websocket-when-try-to-use-ipython-notebook-in-emacs
2012-05-09 08:15:05 +02:00
( defun ein:websocket ( url &optional onmessage onclose onopen
onmessage-args onclose-args onopen-args )
2015-06-29 07:07:53 -05:00
( ein:websocket--prepare-cookies url )
( let* ( ( websocket ( make-ein:$websocket
2016-04-08 21:22:14 -05:00
:onmessage onmessage
:onclose onclose
:onopen onopen
:onmessage-args onmessage-args
:onclose-args onclose-args
:onopen-args onopen-args ) )
( ws ( websocket-open
url
:on-open
( lambda ( ws )
( let ( ( websocket ( websocket-client-data ws ) ) )
( ein:aif ( ein:$websocket-onopen websocket )
( apply it ( ein:$websocket-onopen-args websocket ) ) ) ) )
:on-message
( lambda ( ws frame )
( let ( ( websocket ( websocket-client-data ws ) )
( packet ( websocket-frame-payload frame ) ) )
( ein:aif ( ein:$websocket-onmessage websocket )
( when packet
( apply it packet
( ein:$websocket-onmessage-args websocket ) ) ) ) ) )
:on-close
( lambda ( ws )
( let ( ( websocket ( websocket-client-data ws ) ) )
( ein:aif ( ein:$websocket-onclose websocket )
( apply it websocket
2016-11-05 17:49:52 -05:00
( ein:$websocket-onclose-args websocket ) ) ) ) )
:on-error
( lambda ( ws action err )
( ein:log 'error " Error %s on websocket %s action %s. " err ws action ) ) ) ) )
2012-06-01 22:11:34 +02:00
( setf ( websocket-client-data ws ) websocket )
( setf ( ein:$websocket-ws websocket ) ws )
2012-05-07 14:41:15 +02:00
websocket ) )
2012-05-13 16:23:57 +02:00
( defun ein:websocket-open-p ( websocket )
2012-06-01 22:56:09 +02:00
( eql ( websocket-ready-state ( ein:$websocket-ws websocket ) ) 'open ) )
2012-05-13 16:23:57 +02:00
2012-05-07 14:41:15 +02:00
( defun ein:websocket-send ( websocket text )
2014-10-22 21:35:20 -05:00
;; (ein:log 'info "WS: Sent message %s" text)
2012-06-01 22:11:34 +02:00
( websocket-send-text ( ein:$websocket-ws websocket ) text ) )
2012-05-07 14:41:15 +02:00
( defun ein:websocket-close ( websocket )
2012-05-09 08:54:02 +02:00
( setf ( ein:$websocket-closed-by-client websocket ) t )
2012-05-07 14:41:15 +02:00
( websocket-close ( ein:$websocket-ws websocket ) ) )
2015-01-14 17:07:28 -06:00
( defun ein:websocket-send-shell-channel ( kernel msg )
( cond ( ( = ( ein:$kernel-api-version kernel ) 2 )
( ein:websocket-send
( ein:$kernel-shell-channel kernel )
( json-encode msg ) ) )
2016-11-05 17:49:52 -05:00
( ( >= ( ein:$kernel-api-version kernel ) 3 )
2015-01-14 17:07:28 -06:00
( ein:websocket-send
( ein:$kernel-channels kernel )
( json-encode ( plist-put msg :channel " shell " ) ) ) ) ) )
2015-03-31 16:55:14 -05:00
( defun ein:websocket-send-stdin-channel ( kernel msg )
( cond ( ( = ( ein:$kernel-api-version kernel ) 2 )
( ein:log 'warn " Stdin messages only supported with IPython 3. " ) )
2016-11-05 17:49:52 -05:00
( ( >= ( ein:$kernel-api-version kernel ) 3 )
2015-03-31 16:55:14 -05:00
( ein:websocket-send
( ein:$kernel-channels kernel )
( json-encode ( plist-put msg :channel " stdin " ) ) ) ) ) )
2012-05-07 14:41:15 +02:00
( provide 'ein-websocket )
;;; ein-websocket.el ends here