Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

error 403 with ein over proxy #76

Open
sam-s opened this issue Nov 24, 2020 · 14 comments
Open

error 403 with ein over proxy #76

sam-s opened this issue Nov 24, 2020 · 14 comments

Comments

@sam-s
Copy link

sam-s commented Nov 24, 2020

according to @dickmao in tkf/emacs-request#193 (comment),
the bug described in tkf/emacs-request#193 and millejoh/emacs-ipython-notebook#754 is coming from websocket.
Please let me know if I could be of any assistance in debugging.
thank you.

@ahyatt
Copy link
Owner

ahyatt commented Nov 28, 2020

This isn't really possible for me to debug, AFAICT. The websocket library offers a way to send headers. Those headers can be authentication headers. Websockets aren't going to cause authentication problems itself - that's the server's doing. It could be that the necessary info isn't coming over via websockets to the server. I can think of a few reasons for this.

  1. ein isn't sending the authentication information via the header. By tracing websocket-open you should be able to see the headers sent to the websocket library. Maybe ein has no way to do this, maybe it does, but you are doing it incorrectly. I'm not sure.
  2. websocket isn't correctly sending the header information ein provides. You can trace websocket-create-headers to verify that the headers are being created correctly, or perhaps try and snoop on the network traffic to see what is going on.
  3. You mentioned in another bug there's a proxy involved - it is possible that the websocket library and the proxy server have some incompatibility.
  4. It's possible that whatever you are connecting to expects some other form of authentication that websocket cannot provide. If it should be providing that according to the websocket spec, that's a bug, and something that I can fix.

Can you look at the above possibilities and let me know?

@sam-s
Copy link
Author

sam-s commented Nov 30, 2020

1

emacs -Q -f package-initialize --eval "(setq debug-on-error t)" --eval '(load "websocket")' --eval "(trace-function 'websocket-open)" -f ein:notebooklist-login

======================================================================
1 -> (websocket-open "wss://data-science.k8s.region-001.p-use-1.braze.com:443/api/kernels/8ceaf574-4657-41d5-92be-6bbe80796629/channels?session_id=e2eb6bf9-2566-47b6-ba5a-10470dee7a00" :on-open #[128 #2="\302\300\303\301�\"\"\207" [#[514 "\302�!�>\204��\303\304\305�D\"\210\211\306H\211\203e�\302�!	>\204%�\303\304\307�D\"\210\211\306H\211\203Q�\310�!\203?�\311\312�\"\210�\203?���!\210\313\314\315\316\317\320\321�	!\322\"\323$\"\202`�\313\324\315\316\317\320\321�	!\325\"\323$\"\262�\202t�\313\324\315\316\317\320\321��!\326\"\323$\"\207" [cl-struct-websocket-tags cl-struct-ein:$websocket-tags type-of signal wrong-type-argument websocket 2 ein:$websocket ein:kernel-live-p run-hook-with-args ein:on-kernel-connect-functions ein:log-wrapper verbose make-byte-code 0 "\302\303\304\300!	>\204��\305\306\307\300D\"\210\300\310H\"\207" vconcat vector [cl-struct-websocket-tags format "WS opened: %s" type-of signal wrong-type-argument websocket 10] 6 error [cl-struct-websocket-tags format #1="ein:start-single-websocket: on-open no client data for %s." type-of signal wrong-type-argument websocket 10] [cl-struct-websocket-tags format #1# type-of signal wrong-type-argument websocket 10]] 12 "

(fn CB WS)"] (#[128 #2# [#[771 "� \210\300\301\302\303\304\305\306��!\307\"\310$\"\207" [ein:log-wrapper info make-byte-code 0 "\301\302\300\"\207" vconcat vector [format "Notebook %s is ready"] 3] 11 "

(fn CALLBACK0* NAME* KERNEL)"] (#[128 #2# [#[1285 "� \210r\303�!q\210\304 \210)\211\204$�r\303�!q\210\305\306 !\210\307\310\306 !!\210)�\204\313�\311\312��!	>\204:�\313\314\315��D\"\210�\316H\317\"\204\313�\312�!	>\204R�\313\314\315��D\"\210�\320H\211\203\312�\312��!	>\204j�\313\314\315��D\"\210�\211\316\321\312�
!	>\204��\313\314\315�D\"\210�	\316H\317\312��!
>\204\225�\313\314\322�	D\"\210��\323H\312��!
>\204\252�\313\314\322�
D\"\210��\324H\311�\325\"\203\272�\262�\202\301�\321�\325�#\266\202#I\266�\326��!\210\210�\203\324����\"\210�\211\203\364�\327\330!\211\203\363�r\211q\210\331\332\333\")\211\203\362�\334\335�\"\210\210\210\210\336\312��!	>\204��\313\314\315��D\"\210�\323H!\211\205��\337\340\341�\"!\207" [noninteractive cl-struct-ein:$notebook-tags cl-struct-ein:$kernelspec-tags ein:notebook-buffer ein:worksheet-focus-cell pm-select-buffer pm-innermost-span pop-to-buffer pm-span-buffer plist-member type-of signal wrong-type-argument ein:$notebook 9 :kernelspec 6 plist-put ein:$kernelspec 1 4 :name ein:notebook-save-notebook get-buffer "*Warnings*" thing-at-point line t message "%s" ein:notebooklist-get-buffer ein:notebooklist-reload buffer-local-value ein:%notebooklist%] 18 "

(fn NOTEBOOK* CREATED CALLBACK* PENDING-CLEAR* NO-POP*)"] (#8=#s(ein:$notebook #3="https://data-science.k8s.region-001.p-use-1.braze.com" nil #4="Untitled.ipynb" #7=#s(ein:$kernel #3# #4# nil #10=#s(ein:events #s(hash-table size 65 test eq rehash-size 1.5 rehash-threshold 0.8125 data (maybe_reset_undo\.Worksheet ((#[514 "\300�!\207" [ein:worksheet--unshift-undo-list] 4 "

(fn IGNORE CELL)"])) set_next_input\.Worksheet ((ein:worksheet--set-next-input)) set_dirty\.Worksheet ((ein:worksheet--set-dirty)) open_with_text\.Pager ((ein:pager--open-with-text . #9="*ein:pager https://data-science.k8s.region-001.p-use-1.braze.com/Untitled.ipynb*")) status_idle\.Kernel ((ein:notification--callback #5=#s(ein:notification-status nil nil ((status_idle\.Kernel) (status_busy\.Kernel . "Kernel busy...") (status_restarting\.Kernel . "Kernel restarting...") (status_restarted\.Kernel . "Kernel restarted") (status_dead\.Kernel . "Kernel requires restart \\<ein:notebook-mode-map>\\[ein:notebook-restart-session-command-km]") (status_reconnecting\.Kernel . "Kernel reconnecting...") (status_reconnected\.Kernel . "Kernel reconnected") (status_disconnected\.Kernel . "Kernel requires reconnect \\<ein:notebook-mode-map>\\[ein:notebook-reconnect-session-command-km]"))) . status_idle\.Kernel)) status_busy\.Kernel ((ein:notification--callback #5# . status_busy\.Kernel)) status_restarting\.Kernel ((ein:notification--callback #5# . status_restarting\.Kernel)) status_restarted\.Kernel ((ein:notification--callback #5# . status_restarted\.Kernel)) status_dead\.Kernel ((ein:notification--callback #5# . status_dead\.Kernel)) status_reconnecting\.Kernel ((ein:notification--callback #5# . status_reconnecting\.Kernel)) status_reconnected\.Kernel ((ein:notification--callback #5# . status_reconnected\.Kernel)) status_disconnected\.Kernel ((ein:notification--callback #5# . status_disconnected\.Kernel)) notebook_saving\.Notebook ((ein:notification--callback #6=#s(ein:notification-status nil nil ((notebook_saving\.Notebook . "Saving notebook...") (notebook_saved\.Notebook . "Notebook saved") (notebook_save_failed\.Notebook . "Failed saving notebook!"))) . notebook_saving\.Notebook)) notebook_saved\.Notebook ((ein:notification--fadeout-callback #6# "Notebook is saved" notebook_saved\.Notebook nil) (ein:notification--callback #6# . notebook_saved\.Notebook)) notebook_save_failed\.Notebook ((ein:notification--callback #6# . notebook_save_failed\.Notebook)) execution_count\.Kernel ((ein:notification--set-execution-count . #s(ein:notification #<buffer *ein: https://data-science.k8s.region-001.p-use-1.braze.com/Untitled.ipynb*> #s(ein:notification-tab #[0 "\302�!	>\204��\303\304\305�D\"\210�\306H\207" [ein:%notebook% cl-struct-ein:$notebook-tags type-of signal wrong-type-argument ein:$notebook 14] 4] #[0 "�\207" [ein:%worksheet%] 1]) "y" #6# #5#)))))) 6 "e2eb6bf9-2566-47b6-ba5a-10470dee7a00" "8ceaf574-4657-41d5-92be-6bbe80796629" nil nil nil "/api/kernels" "/api/kernels/8ceaf574-4657-41d5-92be-6bbe80796629" "wss://data-science.k8s.region-001.p-use-1.braze.com:443" nil "username" #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) nil nil) #s(ein:kernelinfo #7# (ein:notebook-buffer-list . #8#) unbound unbound) nil #9# nil nil #20="Untitled.ipynb" 4 4 #10# (#s(ein:worksheet 4 #[128 #2# [ein:$notebook-notebook-path (#8#) apply append] 6 #19="

(fn &rest ARGS2)"] nil nil #13=#s(ewoc #<buffer *ein: https://data-science.k8s.region-001.p-use-1.braze.com/Untitled.ipynb*> ein:worksheet-pp #11=[#18=[#17=[#15=[#12=[#14=[#11# #12# #s(ein:$node (cell prompt) #16=#s(ein:codecell "code" nil #13# (:prompt #14# :input #12# :output nil :footer #15#) (:prompt :input :output :footer) unbound nil nil #10# "55a29e09-5cd7-474e-818f-a6b1732f5b95" nil #7# unbound nil nil) nil) #<marker at 2 in *ein: https://data-science.k8s.region-001.p-use-1.braze.com/Untitled.ipynb*>] #15# #s(ein:$node (cell input) #16# nil) #<marker at 9 in *ein: https://data-science.k8s.region-001.p-use-1.braze.com/Untitled.ipynb*>] #17# #s(ein:$node (cell footer) #16# nil) #<marker at 11 in *ein: https://data-science.k8s.region-001.p-use-1.braze.com/Untitled.ipynb*>] #18# "" #<marker at 12 in *ein: https://data-science.k8s.region-001.p-use-1.braze.com/Untitled.ipynb*>] #11# DL-LIST #<marker at 12 in *ein: https://data-science.k8s.region-001.p-use-1.braze.com/Untitled.ipynb*>] #14# #("
" 0 1 (front-sticky t read-only t)) #<marker at 1 in *ein: https://data-science.k8s.region-001.p-use-1.braze.com/Untitled.ipynb*>] #17# #18# #12# insert) #7# t nil #10#)) nil 6) t nil #[128 #2# [#[385 "\301��\"\207" [*ein:notebook--pending-query* remhash] 5 "

(fn PENDING-KEY* &rest ARGS)"] ((#3# . #4#)) apply append] 6 #19#] nil) apply append] 6 #19#] #20#) apply append] 6 #19#]) apply append] 6 #19#] :on-message #[128 #2# [ein:kernel--handle-websocket-reply (#7#) apply append] 6 #19#] :on-close #[257 "\302�!�>\204��\303\304\305�D\"\210\211\306H\211\203l�\302�!	>\204%�\303\304\307�D\"\210\211\306H\211\203X�\302�!	>\204;�\303\304\307�D\"\210�\310H?\205g�\311\312\313\314\315\316\317�	!\320\"\321$\"\210\322�!\202g�\311\323\313\314\315\316\317�	!\324\"\321$\"\262�\202{�\311\323\313\314\315\316\317��!\325\"\321$\"\207" [cl-struct-websocket-tags cl-struct-ein:$websocket-tags type-of signal wrong-type-argument websocket 2 ein:$websocket 3 ein:log-wrapper verbose make-byte-code 0 "\302\303\304\300!	>\204��\305\306\307\300D\"\210\300\310H\"\207" vconcat vector [cl-struct-websocket-tags format "WS closed unexpectedly: %s" type-of signal wrong-type-argument websocket 10] 6 ein:kernel-disconnect error [cl-struct-websocket-tags format #21="ein:start-single-websocket: on-close no client data for %s." type-of signal wrong-type-argument websocket 10] [cl-struct-websocket-tags format #21# type-of signal wrong-type-argument websocket 10]] 11 "

(fn WS)"] :on-error #[771 "\300\301\302\303\304\305\306�	�	�	#\307\"\310$\"\207" [ein:log-wrapper info make-byte-code 0 "\304\305\302\301\306\300!�>\204��\307\310\311\300D\"\210\300\312H$\207" vconcat vector [cl-struct-websocket-tags format "WS action [%s] %s (%s)" type-of signal wrong-type-argument websocket 10] 8] 13 "

(fn WS ACTION ERR)"])
1 <- websocket-open: #s(websocket connecting nil #[128 #2="\302\300\303\301�\"\"\207" [#[514 "\302�!�>\204��\303\304\305�D\"\210\211\306H\211\203e�\302�!	>\204%�\303\304\307�D\"\210\211\306H\211\203Q�\310�!\203?�\311\312�\"\210�\203?���!\210\313\314\315\316\317\320\321�	!\322\"\323$\"\202`�\313\324\315\316\317\320\321�	!\325\"\323$\"\262�\202t�\313\324\315\316\317\320\321��!\326\"\323$\"\207" [cl-struct-websocket-tags cl-struct-ein:$websocket-tags type-of signal wrong-type-argument websocket 2 ein:$websocket ein:kernel-live-p run-hook-with-args ein:on-kernel-connect-functions ein:log-wrapper verbose make-byte-code 0 "\302\303\304\300!	>\204��\305\306\307\300D\"\210\300\310H\"\207" vconcat vector [cl-struct-websocket-tags format "WS opened: %s" type-of signal wrong-type-argument websocket 10] 6 error [cl-struct-websocket-tags format #1="ein:start-single-websocket: on-open no client data for %s." type-of signal wrong-type-argument websocket 10] [cl-struct-websocket-tags format #1# type-of signal wrong-type-argument websocket 10]] 12 "

(fn CB WS)"] (#[128 #2# [#[771 "� \210\300\301\302\303\304\305\306��!\307\"\310$\"\207" [ein:log-wrapper info make-byte-code 0 "\301\302\300\"\207" vconcat vector [format "Notebook %s is ready"] 3] 11 "

(fn CALLBACK0* NAME* KERNEL)"] (#[128 #2# [#[1285 "� \210r\303�!q\210\304 \210)\211\204$�r\303�!q\210\305\306 !\210\307\310\306 !!\210)�\204\313�\311\312��!	>\204:�\313\314\315��D\"\210�\316H\317\"\204\313�\312�!	>\204R�\313\314\315��D\"\210�\320H\211\203\312�\312��!	>\204j�\313\314\315��D\"\210�\211\316\321\312�
!	>\204��\313\314\315�D\"\210�	\316H\317\312��!
>\204\225�\313\314\322�	D\"\210��\323H\312��!
>\204\252�\313\314\322�
D\"\210��\324H\311�\325\"\203\272�\262�\202\301�\321�\325�#\266\202#I\266�\326��!\210\210�\203\324����\"\210�\211\203\364�\327\330!\211\203\363�r\211q\210\331\332\333\")\211\203\362�\334\335�\"\210\210\210\210\336\312��!	>\204��\313\314\315��D\"\210�\323H!\211\205��\337\340\341�\"!\207" [noninteractive cl-struct-ein:$notebook-tags cl-struct-ein:$kernelspec-tags ein:notebook-buffer ein:worksheet-focus-cell pm-select-buffer pm-innermost-span pop-to-buffer pm-span-buffer plist-member type-of signal wrong-type-argument ein:$notebook 9 :kernelspec 6 plist-put ein:$kernelspec 1 4 :name ein:notebook-save-notebook get-buffer "*Warnings*" thing-at-point line t message "%s" ein:notebooklist-get-buffer ein:notebooklist-reload buffer-local-value ein:%notebooklist%] 18 "

(fn NOTEBOOK* CREATED CALLBACK* PENDING-CLEAR* NO-POP*)"] (#8=#s(ein:$notebook #3="https://data-science.k8s.region-001.p-use-1.braze.com" nil #4="Untitled.ipynb" #7=#s(ein:$kernel #3# #4# nil #10=#s(ein:events #s(hash-table size 65 test eq rehash-size 1.5 rehash-threshold 0.8125 data (maybe_reset_undo\.Worksheet ((#[514 "\300�!\207" [ein:worksheet--unshift-undo-list] 4 "

(fn IGNORE CELL)"])) set_next_input\.Worksheet ((ein:worksheet--set-next-input)) set_dirty\.Worksheet ((ein:worksheet--set-dirty)) open_with_text\.Pager ((ein:pager--open-with-text . #9="*ein:pager https://data-science.k8s.region-001.p-use-1.braze.com/Untitled.ipynb*")) status_idle\.Kernel ((ein:notification--callback #5=#s(ein:notification-status nil nil ((status_idle\.Kernel) (status_busy\.Kernel . "Kernel busy...") (status_restarting\.Kernel . "Kernel restarting...") (status_restarted\.Kernel . "Kernel restarted") (status_dead\.Kernel . "Kernel requires restart \\<ein:notebook-mode-map>\\[ein:notebook-restart-session-command-km]") (status_reconnecting\.Kernel . "Kernel reconnecting...") (status_reconnected\.Kernel . "Kernel reconnected") (status_disconnected\.Kernel . "Kernel requires reconnect \\<ein:notebook-mode-map>\\[ein:notebook-reconnect-session-command-km]"))) . status_idle\.Kernel)) status_busy\.Kernel ((ein:notification--callback #5# . status_busy\.Kernel)) status_restarting\.Kernel ((ein:notification--callback #5# . status_restarting\.Kernel)) status_restarted\.Kernel ((ein:notification--callback #5# . status_restarted\.Kernel)) status_dead\.Kernel ((ein:notification--callback #5# . status_dead\.Kernel)) status_reconnecting\.Kernel ((ein:notification--callback #5# . status_reconnecting\.Kernel)) status_reconnected\.Kernel ((ein:notification--callback #5# . status_reconnected\.Kernel)) status_disconnected\.Kernel ((ein:notification--callback #5# . status_disconnected\.Kernel)) notebook_saving\.Notebook ((ein:notification--callback #6=#s(ein:notification-status nil nil ((notebook_saving\.Notebook . "Saving notebook...") (notebook_saved\.Notebook . "Notebook saved") (notebook_save_failed\.Notebook . "Failed saving notebook!"))) . notebook_saving\.Notebook)) notebook_saved\.Notebook ((ein:notification--fadeout-callback #6# "Notebook is saved" notebook_saved\.Notebook nil) (ein:notification--callback #6# . notebook_saved\.Notebook)) notebook_save_failed\.Notebook ((ein:notification--callback #6# . notebook_save_failed\.Notebook)) execution_count\.Kernel ((ein:notification--set-execution-count . #s(ein:notification #<buffer *ein: https://data-science.k8s.region-001.p-use-1.braze.com/Untitled.ipynb*> #s(ein:notification-tab #[0 "\302�!	>\204��\303\304\305�D\"\210�\306H\207" [ein:%notebook% cl-struct-ein:$notebook-tags type-of signal wrong-type-argument ein:$notebook 14] 4] #[0 "�\207" [ein:%worksheet%] 1]) "y" #6# #5#)))))) 6 "e2eb6bf9-2566-47b6-ba5a-10470dee7a00" "8ceaf574-4657-41d5-92be-6bbe80796629" nil nil nil "/api/kernels" "/api/kernels/8ceaf574-4657-41d5-92be-6bbe80796629" "wss://data-science.k8s.region-001.p-use-1.braze.com:443" nil "username" #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8125 data ()) nil nil) #s(ein:kernelinfo #7# (ein:notebook-buffer-list . #8#) unbound unbound) nil #9# nil nil #20="Untitled.ipynb" 4 4 #10# (#s(ein:worksheet 4 #[128 #2# [ein:$notebook-notebook-path (#8#) apply append] 6 #19="

(fn &rest ARGS2)"] nil nil #13=#s(ewoc #<buffer *ein: https://data-science.k8s.region-001.p-use-1.braze.com/Untitled.ipynb*> ein:worksheet-pp #11=[#18=[#17=[#15=[#12=[#14=[#11# #12# #s(ein:$node (cell prompt) #16=#s(ein:codecell "code" nil #13# (:prompt #14# :input #12# :output nil :footer #15#) (:prompt :input :output :footer) unbound nil nil #10# "55a29e09-5cd7-474e-818f-a6b1732f5b95" nil #7# unbound nil nil) nil) #<marker at 2 in *ein: https://data-science.k8s.region-001.p-use-1.braze.com/Untitled.ipynb*>] #15# #s(ein:$node (cell input) #16# nil) #<marker at 9 in *ein: https://data-science.k8s.region-001.p-use-1.braze.com/Untitled.ipynb*>] #17# #s(ein:$node (cell footer) #16# nil) #<marker at 11 in *ein: https://data-science.k8s.region-001.p-use-1.braze.com/Untitled.ipynb*>] #18# "" #<marker at 12 in *ein: https://data-science.k8s.region-001.p-use-1.braze.com/Untitled.ipynb*>] #11# DL-LIST #<marker at 12 in *ein: https://data-science.k8s.region-001.p-use-1.braze.com/Untitled.ipynb*>] #14# #("
" 0 1 (front-sticky t read-only t)) #<marker at 1 in *ein: https://data-science.k8s.region-001.p-use-1.braze.com/Untitled.ipynb*>] #17# #18# #12# insert) #7# t nil #10#)) nil 6) t nil #[128 #2# [#[385 "\301��\"\207" [*ein:notebook--pending-query* remhash] 5 "

(fn PENDING-KEY* &rest ARGS)"] ((#3# . #4#)) apply append] 6 #19#] nil) apply append] 6 #19#] #20#) apply append] 6 #19#]) apply append] 6 #19#] #[128 #2# [ein:kernel--handle-websocket-reply (#7#) apply append] 6 #19#] #[257 "\302�!�>\204��\303\304\305�D\"\210\211\306H\211\203l�\302�!	>\204%�\303\304\307�D\"\210\211\306H\211\203X�\302�!	>\204;�\303\304\307�D\"\210�\310H?\205g�\311\312\313\314\315\316\317�	!\320\"\321$\"\210\322�!\202g�\311\323\313\314\315\316\317�	!\324\"\321$\"\262�\202{�\311\323\313\314\315\316\317��!\325\"\321$\"\207" [cl-struct-websocket-tags cl-struct-ein:$websocket-tags type-of signal wrong-type-argument websocket 2 ein:$websocket 3 ein:log-wrapper verbose make-byte-code 0 "\302\303\304\300!	>\204��\305\306\307\300D\"\210\300\310H\"\207" vconcat vector [cl-struct-websocket-tags format "WS closed unexpectedly: %s" type-of signal wrong-type-argument websocket 10] 6 ein:kernel-disconnect error [cl-struct-websocket-tags format #21="ein:start-single-websocket: on-close no client data for %s." type-of signal wrong-type-argument websocket 10] [cl-struct-websocket-tags format #21# type-of signal wrong-type-argument websocket 10]] 11 "

(fn WS)"] #[771 "\300\301\302\303\304\305\306�	�	�	#\307\"\310$\"\207" [ein:log-wrapper info make-byte-code 0 "\304\305\302\301\306\300!�>\204��\307\310\311\300D\"\210\300\312H$\207" vconcat vector [cl-struct-websocket-tags format "WS action [%s] %s (%s)" type-of signal wrong-type-argument websocket 10] 8] 13 "

(fn WS ACTION ERR)"] nil nil nil "wss://data-science.k8s.region-001.p-use-1.braze.com:443/api/kernels/8ceaf574-4657-41d5-92be-6bbe80796629/channels?session_id=e2eb6bf9-2566-47b6-ba5a-10470dee7a00" nil nil #<process websocket to wss://data-science.k8s.region-001.p-use-1.braze.com:443/api/kernels/8ceaf574-4657-41d5-92be-6bbe80796629/channels?session_id=e2eb6bf9-2566-47b6-ba5a-10470dee7a00> nil "1s3dgffFQc7QPp5x5MglKLlCo4w=" nil)

*backtrace*

Debugger entered--Lisp error: (websocket-closed #s(websocket-frame :opcode pong :payload "2e9fb87e0d8ef1ea96893aa6906147a371bb\" type=\"text/c..." :length nil :completep t))
  signal(websocket-closed (#s(websocket-frame :opcode pong :payload "2e9fb87e0d8ef1ea96893aa6906147a371bb\" type=\"text/c..." :length nil :completep t)))
  websocket-send(#s(websocket :ready-state open :client-data #s(ein:$websocket :ws #1 :kernel #s(ein:$kernel :url-or-port "https://data-science.k8s.region-001.p-use-1.braze...." :path "Untitled.ipynb" :kernelspec nil :events #<ein:events ein:events-1fe0b3d2cd14> :api-version 6 :session-id "e21e701d-c904-4ddd-9e97-fb1e906c51eb" :kernel-id "17a98983-0ab0-489a-80e9-8e5498fbb74d" :shell-channel nil :iopub-channel nil :websocket nil :base-url "/api/kernels" :kernel-url "/api/kernels/17a98983-0ab0-489a-80e9-8e5498fbb74d" :ws-url "wss://data-science.k8s.region-001.p-use-1.braze.co..." :stdin-activep nil :username "username" :msg-callbacks #<hash-table equal 0/65 0x1fe0b3d2de6f> :oinfo-cache #<hash-table equal 0/65 0x1fe0b3d2ced7> :after-start-hook nil :after-execute-hook nil) :closed-by-client t) :on-open #f(compiled-function (&rest args2) #<bytecode -0x1906404012e5f044>) :on-message #f(compiled-function (&rest args2) #<bytecode -0x1dc37bb17bb11c3c>) :on-close #f(compiled-function (ws) #<bytecode -0x1bee7e17009a5545>) :on-error #f(compiled-function (ws action err) #<bytecode -0x1569342114fce244>) :negotiated-protocols nil :negotiated-extensions nil :server-p nil :url "wss://data-science.k8s.region-001.p-use-1.braze.co..." :protocols nil :extensions nil :conn #<process websocket to wss://data-science.k8s.region-001.p-use-1.braze.com:443/api/kernels/17a98983-0ab0-489a-80e9-8e5498fbb74d/channels?session_id=e21e701d-c904-4ddd-9e97-fb1e906c51eb> :server-conn nil :accept-string "4ob9LNuARAZl8K8BWULanLIkEOQ=" :inflight-input "\n              exports: \"typ") #s(websocket-frame :opcode pong :payload "2e9fb87e0d8ef1ea96893aa6906147a371bb\" type=\"text/c..." :length nil :completep t))
  #f(compiled-function () #<bytecode -0xd195b131c2109b5>)()
  websocket-process-input-on-open-ws(#s(websocket :ready-state open :client-data #s(ein:$websocket :ws #1 :kernel #s(ein:$kernel :url-or-port "https://data-science.k8s.region-001.p-use-1.braze...." :path "Untitled.ipynb" :kernelspec nil :events #<ein:events ein:events-1fe0b3d2cd14> :api-version 6 :session-id "e21e701d-c904-4ddd-9e97-fb1e906c51eb" :kernel-id "17a98983-0ab0-489a-80e9-8e5498fbb74d" :shell-channel nil :iopub-channel nil :websocket nil :base-url "/api/kernels" :kernel-url "/api/kernels/17a98983-0ab0-489a-80e9-8e5498fbb74d" :ws-url "wss://data-science.k8s.region-001.p-use-1.braze.co..." :stdin-activep nil :username "username" :msg-callbacks #<hash-table equal 0/65 0x1fe0b3d2de6f> :oinfo-cache #<hash-table equal 0/65 0x1fe0b3d2ced7> :after-start-hook nil :after-execute-hook nil) :closed-by-client t) :on-open #f(compiled-function (&rest args2) #<bytecode -0x1906404012e5f044>) :on-message #f(compiled-function (&rest args2) #<bytecode -0x1dc37bb17bb11c3c>) :on-close #f(compiled-function (ws) #<bytecode -0x1bee7e17009a5545>) :on-error #f(compiled-function (ws action err) #<bytecode -0x1569342114fce244>) :negotiated-protocols nil :negotiated-extensions nil :server-p nil :url "wss://data-science.k8s.region-001.p-use-1.braze.co..." :protocols nil :extensions nil :conn #<process websocket to wss://data-science.k8s.region-001.p-use-1.braze.com:443/api/kernels/17a98983-0ab0-489a-80e9-8e5498fbb74d/channels?session_id=e21e701d-c904-4ddd-9e97-fb1e906c51eb> :server-conn nil :accept-string "4ob9LNuARAZl8K8BWULanLIkEOQ=" :inflight-input "\n              exports: \"typ") "<!DOCTYPE HTML>\n<html>\n\n<head>\n    <meta charset=\"...")
  websocket-outer-filter(#s(websocket :ready-state open :client-data #s(ein:$websocket :ws #1 :kernel #s(ein:$kernel :url-or-port "https://data-science.k8s.region-001.p-use-1.braze...." :path "Untitled.ipynb" :kernelspec nil :events #<ein:events ein:events-1fe0b3d2cd14> :api-version 6 :session-id "e21e701d-c904-4ddd-9e97-fb1e906c51eb" :kernel-id "17a98983-0ab0-489a-80e9-8e5498fbb74d" :shell-channel nil :iopub-channel nil :websocket nil :base-url "/api/kernels" :kernel-url "/api/kernels/17a98983-0ab0-489a-80e9-8e5498fbb74d" :ws-url "wss://data-science.k8s.region-001.p-use-1.braze.co..." :stdin-activep nil :username "username" :msg-callbacks #<hash-table equal 0/65 0x1fe0b3d2de6f> :oinfo-cache #<hash-table equal 0/65 0x1fe0b3d2ced7> :after-start-hook nil :after-execute-hook nil) :closed-by-client t) :on-open #f(compiled-function (&rest args2) #<bytecode -0x1906404012e5f044>) :on-message #f(compiled-function (&rest args2) #<bytecode -0x1dc37bb17bb11c3c>) :on-close #f(compiled-function (ws) #<bytecode -0x1bee7e17009a5545>) :on-error #f(compiled-function (ws action err) #<bytecode -0x1569342114fce244>) :negotiated-protocols nil :negotiated-extensions nil :server-p nil :url "wss://data-science.k8s.region-001.p-use-1.braze.co..." :protocols nil :extensions nil :conn #<process websocket to wss://data-science.k8s.region-001.p-use-1.braze.com:443/api/kernels/17a98983-0ab0-489a-80e9-8e5498fbb74d/channels?session_id=e21e701d-c904-4ddd-9e97-fb1e906c51eb> :server-conn nil :accept-string "4ob9LNuARAZl8K8BWULanLIkEOQ=" :inflight-input "\n              exports: \"typ") "HTTP/1.1 403 Forbidden\15\nDate: Mon, 30 Nov 2020 21:...")
  #f(compiled-function (process output) #<bytecode 0x26c7c2aa9f33e22>)(#<process websocket to wss://data-science.k8s.region-001.p-use-1.braze.com:443/api/kernels/17a98983-0ab0-489a-80e9-8e5498fbb74d/channels?session_id=e21e701d-c904-4ddd-9e97-fb1e906c51eb> "HTTP/1.1 403 Forbidden\15\nDate: Mon, 30 Nov 2020 21:...")

@sam-s
Copy link
Author

sam-s commented Nov 30, 2020

2

emacs -Q -f package-initialize --eval "(setq debug-on-error t)" --eval '(load "websocket")' --eval "(trace-function 'websocket-create-aders)" -f ein:notebooklist-login

======================================================================
1 -> (websocket-create-headers "wss://data-science.k8s.region-001.p-use-1.braze.com:443/api/kernels/17a98983-0ab0-489a-80e9-8e5498fbb74d/channels?session_id=e21e701d-c904-4ddd-9e97-fb1e906c51eb" "RzHInp8N4aAm6DgI/S3YZw==" nil nil nil)
1 <- websocket-create-headers: "Host: data-science.k8s.region-001.p-use-1.braze.com:443
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: RzHInp8N4aAm6DgI/S3YZw==
Sec-WebSocket-Version: 13
Cookie: _xsrf=2|046f02b3|01ff161c84d0637bae97d257f35e12c4|1605223410; username-data-science-k8s-region-001-p-use-1-braze-com=\"2|1:0|10:1605223426|54:username-data-science-k8s-region-001-p-use-1-braze-com|44:ZmI0ZjRmODFhYzQzNDBjY2ExNjYxNWVhNTQ1MWViNDM=|eb556dd2974c320ff9fa751d25f74f1a56d970911394b4ba320c42a7e4b7f408\"

"

@sam-s
Copy link
Author

sam-s commented Nov 30, 2020

4

I can connect to the jupyter server using chrome; here are the headers:

image

@ahyatt
Copy link
Owner

ahyatt commented Dec 2, 2020

Thanks for the response. What is interesting is that you have cookies for this site already in emacs. Do you know where those come from? Can you connect to the notebook in emacs with eww?

If this is an internal server for your organization, is there any chance you can ask what authentication they are expecting? Or if they have logs of when you access with websockets, to see what the problem is on the server side.

There's a few things here that could be interesting - what you can try to do is to add some of these headers to the websocket-open call in ein (I don't know if ein provides a good mechanism for you to do that, but if not you could perhaps use advise the websocket-open call to do this). You can try the various headers you see here, but to me the interesting one is upgrade-insecure-requests.

@sam-s
Copy link
Author

sam-s commented Dec 2, 2020

Thanks for the response.

Thank you!

What is interesting is that you have cookies for this site already in emacs. Do you know where those come from?

Yes, they are written by Emacs/curl.
When I remove the local file ~/.config/emacs/request/curl-cookie-jar, ein:bnotebooklist-login asks me for the token (stored on the server in ~/.local/share/jupyter/runtime/nbserver-*.json and, after I enter it, never asks me again until the jupyter server is restarted (with the new token).
Please see millejoh/emacs-ipython-notebook#754 (comment) et al.

@sam-s
Copy link
Author

sam-s commented Dec 2, 2020

Can you connect to the notebook in emacs with eww?

Sort of...
I can "get in" using https://data-science.k8s.region-001.p-use-1.braze.com/tree/?token=XXXXX but the page does not show the actual list of notebooks because Jupyter Notebook requires JavaScript (and Emacs/eww does not support JavaScript for ideological reasons).
When I try to access https://data-science.k8s.region-001.p-use-1.braze.com/notebooks/Untiled.ipynb directly, I get the same JavaScript message.

@sam-s
Copy link
Author

sam-s commented Dec 2, 2020

If this is an internal server for your organization, is there any chance you can ask what authentication they are expecting?

this is the standard jupyter server:

jupyter notebook --port=8080 --ip=0.0.0.0 --allow-root \
     --NotebookApp.iopub_data_rate_limit=1.0e10 \
     --NotebookApp.iopub_msg_rate_limit=1.0e10

with token authentication

Or if they have logs of when you access with websockets, to see what the problem is on the server side.

here is the relevant part of the jupyter server log:

[W 12:15:34.907 NotebookApp] Couldn't authenticate WebSocket connection
[W 12:15:34.908 NotebookApp] 403 GET /api/kernels/17a98983-0ab0-489a-80e9-8e5498fbb74d/channels?session_id=e21e701d-c904-4ddd-9e97-fb1e906c51eb (100.108.90.236) 2.21ms referer=None

@sam-s
Copy link
Author

sam-s commented Dec 2, 2020

There's a few things here that could be interesting - what you can try to do is to add some of these headers to the websocket-open call in ein
You can try the various headers you see here, but to me the interesting one is upgrade-insecure-requests.

trace:

======================================================================
1 -> (websocket-create-headers "wss://data-science.k8s.region-001.p-use-1.braze.com:443/api/kernels/17a98983-0ab0-489a-80e9-8e5498fbb74d/channels?session_id=e21e701d-c904-4ddd-9e97-fb1e906c51eb" "aG5nwE6D7O9QzEDc4nlDkQ==" nil nil (("authority" . "data-science.k8s.region-001.p-use-1.braze.com") (":scheme" . "https") ("cache-control" . "max-age=0") ("dnt" . 1) ("sec-fetch-dest" . "document") ("sec-fetch-mode" . "navigate") ("sec-fetch-site" . "same-origin") ("sec-fetch-user" . "?1") ("sec-gpc" . 1) ("upgrade-insecure-requests" . 1) ("user-agent" . "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36")))
1 <- websocket-create-headers: "Host: data-science.k8s.region-001.p-use-1.braze.com:443
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: aG5nwE6D7O9QzEDc4nlDkQ==
Sec-WebSocket-Version: 13
Cookie: username-data-science-k8s-region-001-p-use-1-braze-com=\"2|1:0|10:1605223426|54:username-data-science-k8s-region-001-p-use-1-braze-com|44:ZmI0ZjRmODFhYzQzNDBjY2ExNjYxNWVhNTQ1MWViNDM=|eb556dd2974c320ff9fa751d25f74f1a56d970911394b4ba320c42a7e4b7f408\"; _xsrf=2|046f02b3|01ff161c84d0637bae97d257f35e12c4|1605223410
authority: data-science.k8s.region-001.p-use-1.braze.com
:scheme: https
cache-control: max-age=0
dnt: 1
sec-fetch-dest: document
sec-fetch-mode: navigate
sec-fetch-site: same-origin
sec-fetch-user: ?1
sec-gpc: 1
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36

"

same error

Debugger entered--Lisp error: (websocket-closed #s(websocket-frame :opcode pong :payload "2e9fb87e0d8ef1ea96893aa6906147a371bb\" type=\"text/c..." :length nil :completep t))
  signal(websocket-closed (#s(websocket-frame :opcode pong :payload "2e9fb87e0d8ef1ea96893aa6906147a371bb\" type=\"text/c..." :length nil :completep t)))
  websocket-send(#s(websocket :ready-state open :client-data #s(ein:$websocket :ws #1 :kernel #s(ein:$kernel :url-or-port "https://data-science.k8s.region-001.p-use-1.braze...." :path "Untitled.ipynb" :kernelspec #s(ein:$kernelspec :name "python3" :display-name "Python 3" :resources (:logo-32x32 "/kernelspecs/python3/logo-32x32.png" :logo-64x64 "/kernelspecs/python3/logo-64x64.png") :spec (:argv ["python" "-m" "ipykernel_launcher" "-f" "{connection_file}"] :env nil :display_name "Python 3" :language "python" :interrupt_mode "signal" :metadata nil) :language "python") :events #<ein:events ein:events-1ff3202ce158> :api-version 6 :session-id "e21e701d-c904-4ddd-9e97-fb1e906c51eb" :kernel-id "17a98983-0ab0-489a-80e9-8e5498fbb74d" :shell-channel nil :iopub-channel nil :websocket nil :base-url "/api/kernels" :kernel-url "/api/kernels/17a98983-0ab0-489a-80e9-8e5498fbb74d" :ws-url "wss://data-science.k8s.region-001.p-use-1.braze.co..." :stdin-activep nil :username "username" :msg-callbacks #<hash-table equal 0/65 0x1ff3202f45bf> :oinfo-cache #<hash-table equal 0/65 0x1ff3202ce2c1> :after-start-hook nil :after-execute-hook nil) :closed-by-client t) :on-open #f(compiled-function (&rest args2) #<bytecode -0x1906404012e5f044>) :on-message #f(compiled-function (&rest args2) #<bytecode -0x1d1c10bdafb11c3c>) :on-close #f(compiled-function (ws) #<bytecode -0x1e79d20f5597d566>) :on-error #f(compiled-function (ws action err) #<bytecode -0xeed6b6cdd78e285>) :negotiated-protocols nil :negotiated-extensions nil :server-p nil :url "wss://data-science.k8s.region-001.p-use-1.braze.co..." :protocols nil :extensions nil :conn #<process websocket to wss://data-science.k8s.region-001.p-use-1.braze.com:443/api/kernels/17a98983-0ab0-489a-80e9-8e5498fbb74d/channels?session_id=e21e701d-c904-4ddd-9e97-fb1e906c51eb> :server-conn nil :accept-string "tXY7Pbmme0ql+CG+B4vndlSPXc4=" :inflight-input "\n              exports: \"typ") #s(websocket-frame :opcode pong :payload "2e9fb87e0d8ef1ea96893aa6906147a371bb\" type=\"text/c..." :length nil :completep t))
  #f(compiled-function () #<bytecode -0xeaa51175c919b25>)()
  websocket-process-input-on-open-ws(#s(websocket :ready-state open :client-data #s(ein:$websocket :ws #1 :kernel #s(ein:$kernel :url-or-port "https://data-science.k8s.region-001.p-use-1.braze...." :path "Untitled.ipynb" :kernelspec #s(ein:$kernelspec :name "python3" :display-name "Python 3" :resources (:logo-32x32 "/kernelspecs/python3/logo-32x32.png" :logo-64x64 "/kernelspecs/python3/logo-64x64.png") :spec (:argv ["python" "-m" "ipykernel_launcher" "-f" "{connection_file}"] :env nil :display_name "Python 3" :language "python" :interrupt_mode "signal" :metadata nil) :language "python") :events #<ein:events ein:events-1ff3202ce158> :api-version 6 :session-id "e21e701d-c904-4ddd-9e97-fb1e906c51eb" :kernel-id "17a98983-0ab0-489a-80e9-8e5498fbb74d" :shell-channel nil :iopub-channel nil :websocket nil :base-url "/api/kernels" :kernel-url "/api/kernels/17a98983-0ab0-489a-80e9-8e5498fbb74d" :ws-url "wss://data-science.k8s.region-001.p-use-1.braze.co..." :stdin-activep nil :username "username" :msg-callbacks #<hash-table equal 0/65 0x1ff3202f45bf> :oinfo-cache #<hash-table equal 0/65 0x1ff3202ce2c1> :after-start-hook nil :after-execute-hook nil) :closed-by-client t) :on-open #f(compiled-function (&rest args2) #<bytecode -0x1906404012e5f044>) :on-message #f(compiled-function (&rest args2) #<bytecode -0x1d1c10bdafb11c3c>) :on-close #f(compiled-function (ws) #<bytecode -0x1e79d20f5597d566>) :on-error #f(compiled-function (ws action err) #<bytecode -0xeed6b6cdd78e285>) :negotiated-protocols nil :negotiated-extensions nil :server-p nil :url "wss://data-science.k8s.region-001.p-use-1.braze.co..." :protocols nil :extensions nil :conn #<process websocket to wss://data-science.k8s.region-001.p-use-1.braze.com:443/api/kernels/17a98983-0ab0-489a-80e9-8e5498fbb74d/channels?session_id=e21e701d-c904-4ddd-9e97-fb1e906c51eb> :server-conn nil :accept-string "tXY7Pbmme0ql+CG+B4vndlSPXc4=" :inflight-input "\n              exports: \"typ") "<!DOCTYPE HTML>\n<html>\n\n<head>\n    <meta charset=\"...")
  websocket-outer-filter(#s(websocket :ready-state open :client-data #s(ein:$websocket :ws #1 :kernel #s(ein:$kernel :url-or-port "https://data-science.k8s.region-001.p-use-1.braze...." :path "Untitled.ipynb" :kernelspec #s(ein:$kernelspec :name "python3" :display-name "Python 3" :resources (:logo-32x32 "/kernelspecs/python3/logo-32x32.png" :logo-64x64 "/kernelspecs/python3/logo-64x64.png") :spec (:argv ["python" "-m" "ipykernel_launcher" "-f" "{connection_file}"] :env nil :display_name "Python 3" :language "python" :interrupt_mode "signal" :metadata nil) :language "python") :events #<ein:events ein:events-1ff3202ce158> :api-version 6 :session-id "e21e701d-c904-4ddd-9e97-fb1e906c51eb" :kernel-id "17a98983-0ab0-489a-80e9-8e5498fbb74d" :shell-channel nil :iopub-channel nil :websocket nil :base-url "/api/kernels" :kernel-url "/api/kernels/17a98983-0ab0-489a-80e9-8e5498fbb74d" :ws-url "wss://data-science.k8s.region-001.p-use-1.braze.co..." :stdin-activep nil :username "username" :msg-callbacks #<hash-table equal 0/65 0x1ff3202f45bf> :oinfo-cache #<hash-table equal 0/65 0x1ff3202ce2c1> :after-start-hook nil :after-execute-hook nil) :closed-by-client t) :on-open #f(compiled-function (&rest args2) #<bytecode -0x1906404012e5f044>) :on-message #f(compiled-function (&rest args2) #<bytecode -0x1d1c10bdafb11c3c>) :on-close #f(compiled-function (ws) #<bytecode -0x1e79d20f5597d566>) :on-error #f(compiled-function (ws action err) #<bytecode -0xeed6b6cdd78e285>) :negotiated-protocols nil :negotiated-extensions nil :server-p nil :url "wss://data-science.k8s.region-001.p-use-1.braze.co..." :protocols nil :extensions nil :conn #<process websocket to wss://data-science.k8s.region-001.p-use-1.braze.com:443/api/kernels/17a98983-0ab0-489a-80e9-8e5498fbb74d/channels?session_id=e21e701d-c904-4ddd-9e97-fb1e906c51eb> :server-conn nil :accept-string "tXY7Pbmme0ql+CG+B4vndlSPXc4=" :inflight-input "\n              exports: \"typ") "HTTP/1.1 403 Forbidden\15\nDate: Wed, 02 Dec 2020 18:...")
  #f(compiled-function (process output) #<bytecode 0x26f5d943965ae22>)(#<process websocket to wss://data-science.k8s.region-001.p-use-1.braze.com:443/api/kernels/17a98983-0ab0-489a-80e9-8e5498fbb74d/channels?session_id=e21e701d-c904-4ddd-9e97-fb1e906c51eb> "HTTP/1.1 403 Forbidden\15\nDate: Wed, 02 Dec 2020 18:...")

@ahyatt
Copy link
Owner

ahyatt commented Dec 6, 2020

Interesting, thanks for sharing. Let's summarize - and let me know if anything I say is wrong.

  1. You get a 403 error with ein over a proxy.
  2. Most people using ein don't get this error.
  3. The websocket headers are probably fine.
  4. Your jupyter notebook isn't unusual.

My guess is that it has to be a proxy issue - that seems to be the only difference from normal. Is there any relevant info on the proxy you are using you can share? I don't know much about proxies, but I do know that websocket doesn't have any special support for them.

Also, have you seen jupyter/notebook#3389? It seems very similar to your issue.

@sam-s
Copy link
Author

sam-s commented Dec 6, 2020

Also, have you seen jupyter/notebook#3389? It seems very similar to your issue.

could be - my jupyter works with chrome & firefox but not with ein.

@sam-s
Copy link
Author

sam-s commented Dec 9, 2020

There is a glimpse of hope - when I append &token=XXX to the url that I pass to websocket-open, everything works just fine.

@cw1427
Copy link

cw1427 commented Dec 25, 2020

Same error on ubuntu 18.04 jupyter 6.1.6

`
[W 14:57:11.070 NotebookApp] 403 GET /api/kernels/c1e81a22-109e-48b8-89ac-9920bbad65ea/channels?session_id=b5c8bfa87979493d8c461f34c7839215 (127.0.0.1) 5.540000ms referer=None
[W 14:57:21.969 NotebookApp] Couldn't authenticate WebSocket connection
[W 14:57:21.972 NotebookApp] 403 GET /api/kernels/c1e81a22-109e-48b8-89ac-9920bbad65ea/channels?session_id=b5c8bfa87979493d8c461f34c7839215 (127.0.0.1) 4.770000ms referer=None

`

@ahyatt
Copy link
Owner

ahyatt commented Dec 27, 2020

@sam-s Interesting that the token works. I wonder if this means that ein should be setting it. The websocket library is agnostic about such things. It seems like, from the headers you have provided, that ein is asking for it but not passing it along to the websocket library. Does that sound plausible to you?

@cw1427 Can you also try to manually append the token in the same way described above to see if that solves your problem? Is there anything unusual about your jupyter installation?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants