PY-14605 Support remote single-user ipnb servers

TODO: Get ipnb file path correctly as a relative path from working directory
This commit is contained in:
Valentina Kiryushkina
2017-01-31 23:42:29 +03:00
parent 9f7faec974
commit b46db2148c
4 changed files with 400 additions and 174 deletions
@@ -51,6 +51,8 @@ import org.jetbrains.plugins.ipnb.protocol.IpnbConnectionV3;
import javax.swing.event.HyperlinkEvent;
import java.io.IOException;
import java.net.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -114,17 +116,21 @@ public final class IpnbConnectionManager implements ProjectComponent {
@NotNull final IpnbFileEditor fileEditor,
@NotNull final String path,
@NotNull final String url) {
if (myToken != null) return startConnection(codePanel, path, url, false);
final Module module = ProjectFileIndex.SERVICE.getInstance(myProject).getModuleForFile(fileEditor.getVirtualFile());
if (module != null) {
final Sdk sdk = PythonSdkType.findPythonSdk(module);
if (sdk != null) {
final List<PyPackage> packages = PyPackageManager.getInstance(sdk).getPackages();
if (packages != null) {
final PyPackage notebookPackage = PyPackageUtil.findPackage(packages, "notebook");
if (notebookPackage != null && VersionComparatorUtil.compare(notebookPackage.getVersion(), "4.3.0") >= 0) {
myToken = askForToken(url);
if (myToken == null) return false;
final IpnbSettings ipnbSettings = IpnbSettings.getInstance(myProject);
final boolean isRemote = !ipnbSettings.getUsername().isEmpty() && !ipnbSettings.getPassword().isEmpty();
if (!isRemote) {
if (myToken != null) return startConnection(codePanel, path, url, false);
final Module module = ProjectFileIndex.SERVICE.getInstance(myProject).getModuleForFile(fileEditor.getVirtualFile());
if (module != null) {
final Sdk sdk = PythonSdkType.findPythonSdk(module);
if (sdk != null) {
final List<PyPackage> packages = PyPackageManager.getInstance(sdk).getPackages();
if (packages != null) {
final PyPackage notebookPackage = PyPackageUtil.findPackage(packages, "notebook");
if (notebookPackage != null && VersionComparatorUtil.compare(notebookPackage.getVersion(), "4.3.0") >= 0) {
myToken = askForToken(url);
if (myToken == null) return false;
}
}
}
}
@@ -271,10 +277,30 @@ public final class IpnbConnectionManager implements ProjectComponent {
private IpnbConnection getConnection(@Nullable final IpnbCodePanel codePanel, @NotNull final String urlString,
@NotNull final IpnbConnectionListenerBase listener)
throws IOException, URISyntaxException {
if (codePanel != null && !IpnbParser.isIpythonNewFormat(codePanel.getFileEditor().getVirtualFile())) {
return new IpnbConnection(urlString, listener, myToken);
if (codePanel != null) {
final VirtualFile file = codePanel.getFileEditor().getVirtualFile();
String pathToFile = getRelativePathToFile(file);
if (pathToFile != null) {
if (!IpnbParser.isIpythonNewFormat(file)) {
return new IpnbConnection(urlString, listener, myToken, myProject, pathToFile);
}
return new IpnbConnectionV3(urlString, listener, myToken, myProject, pathToFile);
}
}
return new IpnbConnectionV3(urlString, listener, myToken);
throw new IOException("Code panel is null");
}
@Nullable
private String getRelativePathToFile(VirtualFile file) {
final String workingDirectory = IpnbSettings.getInstance(myProject).getWorkingDirectory();
final String realWorkingDir = workingDirectory.isEmpty() ? myProject.getBasePath() : workingDirectory;
if (realWorkingDir != null) {
final Path basePath = Paths.get(realWorkingDir);
final Path filePath = Paths.get(file.getPath());
return basePath.relativize(filePath).toString();
}
return null;
}
public void interruptKernel(@NotNull final String filePath) {
@@ -310,125 +336,131 @@ public final class IpnbConnectionManager implements ProjectComponent {
}
public boolean startIpythonServer(@NotNull final String initUrl, @NotNull final IpnbFileEditor fileEditor) {
final Module module = ProjectFileIndex.SERVICE.getInstance(myProject).getModuleForFile(fileEditor.getVirtualFile());
if (module == null) return false;
final Sdk sdk = PythonSdkType.findPythonSdk(module);
if (sdk == null) {
showWarning(fileEditor, "Please check Python Interpreter in Settings->Python Interpreter", null);
return false;
}
final List<PyPackage> packages = PyPackageManager.getInstance(sdk).getPackages();
final PyPackage ipythonPackage = packages != null ? PyPackageUtil.findPackage(packages, "ipython") : null;
final PyPackage jupyterPackage = packages != null ? PyPackageUtil.findPackage(packages, "jupyter") : null;
if (ipythonPackage == null && jupyterPackage == null) {
showWarning(fileEditor, "Add Jupyter to the interpreter of the current project.", null);
return false;
}
final String username = IpnbSettings.getInstance(myProject).getUsername();
final String password = IpnbSettings.getInstance(myProject).getPassword();
final boolean isRemote = !username.isEmpty() && !password.isEmpty();
if (!isRemote) {
final Module module = ProjectFileIndex.SERVICE.getInstance(myProject).getModuleForFile(fileEditor.getVirtualFile());
if (module == null) return false;
final Sdk sdk = PythonSdkType.findPythonSdk(module);
if (sdk == null) {
showWarning(fileEditor, "Please check Python Interpreter in Settings->Python Interpreter", null);
return false;
}
final List<PyPackage> packages = PyPackageManager.getInstance(sdk).getPackages();
final PyPackage ipythonPackage = packages != null ? PyPackageUtil.findPackage(packages, "ipython") : null;
final PyPackage jupyterPackage = packages != null ? PyPackageUtil.findPackage(packages, "jupyter") : null;
if (ipythonPackage == null && jupyterPackage == null) {
showWarning(fileEditor, "Add Jupyter to the interpreter of the current project.", null);
return false;
}
String url = showDialogUrl(initUrl);
if (url == null) return false;
final IpnbSettings ipnbSettings = IpnbSettings.getInstance(myProject);
ipnbSettings.setURL(url);
String url = showDialogUrl(initUrl);
if (url == null) return false;
final IpnbSettings ipnbSettings = IpnbSettings.getInstance(myProject);
ipnbSettings.setURL(url);
final Pair<String, String> hostPort = getHostPortFromUrl(url);
if (hostPort == null) {
showWarning(fileEditor, "Please, check Jupyter Notebook URL in <a href=\"\">Settings->Tools->Jupyter Notebook</a>",
new IpnbSettingsAdapter());
return false;
}
final String homePath = sdk.getHomePath();
if (homePath == null) {
showWarning(fileEditor, "Python Sdk is invalid, please check Python Interpreter in Settings->Python Interpreter", null);
return false;
}
Map<String, String> env = null;
final ArrayList<String> parameters = Lists.newArrayList(homePath);
String ipython = findJupyterRunner(homePath);
if (ipython == null) {
ipython = findIPythonRunner(homePath);
final Pair<String, String> hostPort = getHostPortFromUrl(url);
if (hostPort == null) {
showWarning(fileEditor, "Please, check Jupyter Notebook URL in <a href=\"\">Settings->Tools->Jupyter Notebook</a>",
new IpnbSettingsAdapter());
return false;
}
final String homePath = sdk.getHomePath();
if (homePath == null) {
showWarning(fileEditor, "Python Sdk is invalid, please check Python Interpreter in Settings->Python Interpreter", null);
return false;
}
Map<String, String> env = null;
final ArrayList<String> parameters = Lists.newArrayList(homePath);
String ipython = findJupyterRunner(homePath);
if (ipython == null) {
ipython = PythonHelper.LOAD_ENTRY_POINT.asParamString();
env = ImmutableMap.of("PYCHARM_EP_DIST", "ipython", "PYCHARM_EP_NAME", "ipython");
ipython = findIPythonRunner(homePath);
if (ipython == null) {
ipython = PythonHelper.LOAD_ENTRY_POINT.asParamString();
env = ImmutableMap.of("PYCHARM_EP_DIST", "ipython", "PYCHARM_EP_NAME", "ipython");
}
parameters.add(ipython);
parameters.add("notebook");
}
parameters.add(ipython);
parameters.add("notebook");
}
else {
parameters.add(ipython);
}
parameters.add("--no-browser");
else {
parameters.add(ipython);
}
parameters.add("--no-browser");
if (hostPort.getFirst() != null) {
parameters.add("--ip");
parameters.add(hostPort.getFirst());
}
if (hostPort.getSecond() != null) {
parameters.add("--port");
parameters.add(hostPort.getSecond());
}
final String arguments = ipnbSettings.getArguments();
if (!StringUtil.isEmptyOrSpaces(arguments)) {
parameters.addAll(StringUtil.split(arguments, " "));
}
if (hostPort.getFirst() != null) {
parameters.add("--ip");
parameters.add(hostPort.getFirst());
}
if (hostPort.getSecond() != null) {
parameters.add("--port");
parameters.add(hostPort.getSecond());
}
final String arguments = ipnbSettings.getArguments();
if (!StringUtil.isEmptyOrSpaces(arguments)) {
parameters.addAll(StringUtil.split(arguments, " "));
}
final String directory = ipnbSettings.getWorkingDirectory();
final String baseDir = !StringUtil.isEmptyOrSpaces(directory) ? directory :
ModuleRootManager.getInstance(module).getContentRoots()[0].getCanonicalPath();
final GeneralCommandLine commandLine = new GeneralCommandLine(parameters).withWorkDirectory(baseDir);
if (env != null) {
commandLine.withEnvironment(env);
}
final String directory = ipnbSettings.getWorkingDirectory();
final String baseDir = !StringUtil.isEmptyOrSpaces(directory) ? directory :
ModuleRootManager.getInstance(module).getContentRoots()[0].getCanonicalPath();
final GeneralCommandLine commandLine = new GeneralCommandLine(parameters).withWorkDirectory(baseDir);
if (env != null) {
commandLine.withEnvironment(env);
}
try {
final boolean[] serverStarted = {false};
final KillableColoredProcessHandler processHandler = new KillableColoredProcessHandler(commandLine) {
@Override
protected void doDestroyProcess() {
super.doDestroyProcess();
myKernels.clear();
myToken = null;
UnixProcessManager.sendSigIntToProcessTree(getProcess());
}
try {
final boolean[] serverStarted = {false};
final KillableColoredProcessHandler processHandler = new KillableColoredProcessHandler(commandLine) {
@Override
protected void doDestroyProcess() {
super.doDestroyProcess();
myKernels.clear();
myToken = null;
UnixProcessManager.sendSigIntToProcessTree(getProcess());
}
@Override
public void coloredTextAvailable(@NotNull @NonNls String text, @NotNull Key attributes) {
super.coloredTextAvailable(text, attributes);
if (text.toLowerCase().contains("active kernels")) {
serverStarted[0] = true;
@Override
public void coloredTextAvailable(@NotNull @NonNls String text, @NotNull Key attributes) {
super.coloredTextAvailable(text, attributes);
if (text.toLowerCase().contains("active kernels")) {
serverStarted[0] = true;
}
final String token = "?token=";
if (text.toLowerCase().contains(token)) {
myToken = text.substring(text.indexOf(token) + token.length()).trim();
}
}
final String token = "?token=";
if (text.toLowerCase().contains(token)) {
myToken = text.substring(text.indexOf(token) + token.length()).trim();
@Override
public boolean isSilentlyDestroyOnClose() {
return true;
}
}
@Override
public boolean isSilentlyDestroyOnClose() {
return true;
};
processHandler.setShouldDestroyProcessRecursively(true);
GuiUtils.invokeLaterIfNeeded(() -> new RunContentExecutor(myProject, processHandler)
.withTitle("Jupyter Notebook")
.withStop(() -> {
myKernels.clear();
processHandler.destroyProcess();
UnixProcessManager.sendSigIntToProcessTree(processHandler.getProcess());
}, () -> !processHandler.isProcessTerminated())
.withRerun(() -> startIpythonServer(url, fileEditor))
.withHelpId("reference.manage.py")
.withFilter(new UrlFilter())
.run(), ModalityState.defaultModalityState());
int countAttempt = 0;
while (!serverStarted[0] && countAttempt < MAX_ATTEMPTS) {
countAttempt += 1;
TimeoutUtil.sleep(1000);
}
};
processHandler.setShouldDestroyProcessRecursively(true);
GuiUtils.invokeLaterIfNeeded(() -> new RunContentExecutor(myProject, processHandler)
.withTitle("Jupyter Notebook")
.withStop(() -> {
myKernels.clear();
processHandler.destroyProcess();
UnixProcessManager.sendSigIntToProcessTree(processHandler.getProcess());
}, () -> !processHandler.isProcessTerminated())
.withRerun(() -> startIpythonServer(url, fileEditor))
.withHelpId("reference.manage.py")
.withFilter(new UrlFilter())
.run(), ModalityState.defaultModalityState());
int countAttempt = 0;
while (!serverStarted[0] && countAttempt < MAX_ATTEMPTS) {
countAttempt += 1;
TimeoutUtil.sleep(1000);
return true;
}
catch (ExecutionException e) {
return false;
}
return true;
}
catch (ExecutionException e) {
return false;
}
return true;
}
@Nullable
@@ -2,7 +2,12 @@ package org.jetbrains.plugins.ipnb.protocol;
import com.google.common.collect.Lists;
import com.google.gson.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.net.HTTPMethod;
import com.intellij.util.net.ssl.CertificateManager;
import com.jetbrains.python.psi.PyUtil;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft;
import org.java_websocket.drafts.Draft_17;
@@ -10,28 +15,36 @@ import org.java_websocket.handshake.ClientHandshakeBuilder;
import org.java_websocket.handshake.ServerHandshake;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.ipnb.configuration.IpnbSettings;
import org.jetbrains.plugins.ipnb.format.cells.output.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import java.io.*;
import java.net.*;
import java.util.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* @author vlan
*
* To be removed
*/
* <p>
* To be removed
*/
public class IpnbConnection {
private static final Logger LOG = Logger.getInstance(IpnbConnection.class);
protected static final String API_URL = "/api";
protected static final String KERNELS_URL = API_URL + "/kernels";
protected static final String HTTP_POST = "POST";
// TODO: Serialize cookies for the authentication message
protected static final String authMessage = "{\"header\":{\"msg_id\":\"\", \"msg_type\":\"connect_request\"}, \"parent_header\":\"\", \"metadata\":{}," +
"\"channel\":\"shell\" }";
public static final String HTTP_DELETE = "DELETE";
public static final String AUTHENTICATION_NEEDED = "Authentication needed";
private static final String LOGIN_URL = "/login";
@NotNull protected final URI myURI;
@NotNull protected final String myKernelId;
@@ -50,20 +63,140 @@ public class IpnbConnection {
private IpnbOutputCell myOutput;
private int myExecCount;
private String myXsrf;
private HashMap<String, String> myHeaders = new HashMap<>();
public IpnbConnection(@NotNull String uri, @NotNull IpnbConnectionListener listener,
@Nullable final String token) throws IOException, URISyntaxException {
@Nullable final String token, @NotNull Project project, @NotNull String pathToFile) throws IOException, URISyntaxException {
myURI = new URI(uri);
myListener = listener;
myToken = token;
mySessionId = UUID.randomUUID().toString();
initXSRF();
myKernelId = startKernel();
HttpsURLConnection.setDefaultSSLSocketFactory(CertificateManager.getInstance().getSslContext().getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession session) {
return myURI.getHost().equals(s);
}
});
final boolean needed = isLoginNeeded();
if (needed) {
final IpnbSettings ipnbSettings = IpnbSettings.getInstance(project);
final List<HttpCookie> cookies = login(ipnbSettings.getUsername(), ipnbSettings.getPassword());
final String cookieString =
cookies.stream().map(cookie -> cookie.getName() + "=" + cookie.getValue()).collect(Collectors.joining(";"));
myHeaders.put("Cookie", cookieString);
myKernelId = getExistingKernelForSession(pathToFile);
}
else {
initXSRF();
if (myToken != null) {
myHeaders.put("Authorization", "token " + myToken);
}
myKernelId = startKernel();
}
initializeClients();
}
private List<HttpCookie> login(@NotNull String username, @NotNull String password) throws IOException {
CookieManager cookieManager = new CookieManager();
CookieHandler.setDefault(cookieManager);
String urlParameters = "username=" + username + "&" + "password=" + password;
byte[] postData = urlParameters.getBytes(StandardCharsets.UTF_8);
final HttpsURLConnection connection = (HttpsURLConnection)new URL(myURI + LOGIN_URL).openConnection();
connection.setUseCaches(false);
connection.setRequestMethod(HTTPMethod.POST.name());
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Length", Integer.toString(postData.length));
connection.setDoOutput(true);
final OutputStream outputStream = connection.getOutputStream();
try (DataOutputStream wr = new DataOutputStream(outputStream)) {
wr.write(postData);
wr.flush();
}
connection.connect();
final int code = connection.getResponseCode();
if (code == HttpURLConnection.HTTP_OK) {
return cookieManager.getCookieStore().getCookies();
}
else {
throw new IOException("Unable to login: " + code + " " + connection.getResponseMessage());
}
}
private String getDefaultKernelName() {
try {
final String response = httpRequest(myURI + "/api/kernelspecs", HTTPMethod.GET.name());
final JsonObject kernelSpecs = PyUtil.as(new JsonParser().parse(response), JsonObject.class);
if (kernelSpecs != null && kernelSpecs.has("default")) {
return kernelSpecs.get("default").getAsString();
}
else {
LOG.warn("Got wrong kernel specs: " + response);
}
}
catch (IOException e) {
LOG.warn(e.getMessage());
}
return "";
}
private String getExistingKernelForSession(@NotNull String pathToFile) {
try {
final URLConnection connection = new URL(myURI + "/api/sessions").openConnection();
if (connection instanceof HttpsURLConnection) {
final String kernelName = getDefaultKernelName();
if (kernelName.isEmpty()) return "";
final SessionWrapper sessionWrapper = new SessionWrapper(kernelName, pathToFile);
final Gson gsonBuilder = new GsonBuilder().serializeNulls().create();
final byte[] postData = gsonBuilder.toJson(sessionWrapper).getBytes(StandardCharsets.UTF_8);
final HttpsURLConnection httpsConnection = (HttpsURLConnection)configureConnection((HttpURLConnection)connection,
HTTPMethod.POST.name());
httpsConnection.setRequestProperty("Content-Type", "application/json");
httpsConnection.setRequestProperty("Content-Length", Integer.toString(postData.length));
httpsConnection.setRequestProperty("Referer", myURI + "/notebooks/" + pathToFile);
httpsConnection.setUseCaches(false);
httpsConnection.setDoOutput(true);
final OutputStream outputStream = connection.getOutputStream();
try (DataOutputStream wr = new DataOutputStream(outputStream)) {
wr.write(postData);
wr.flush();
}
connection.connect();
final String response = getResponse(httpsConnection);
final SessionWrapper wrapper = gsonBuilder.fromJson(response, SessionWrapper.class);
return wrapper.kernel.id;
}
else {
throw new UnsupportedOperationException("Only HTTP URLs are supported");
}
}
catch (IOException e) {
LOG.warn(e);
}
return "";
}
private boolean isLoginNeeded() throws IOException {
final HttpsURLConnection connection = PyUtil.as(new URL(myURI.toString() + "/tree").openConnection(), HttpsURLConnection.class);
if (connection != null) {
connection.setInstanceFollowRedirects(false);
connection.connect();
if (connection.getResponseCode() == HttpURLConnection.HTTP_MOVED_TEMP) {
final String location = connection.getHeaderField("Location");
connection.disconnect();
if (location != null && location.startsWith("/login")) return true;
}
}
return false;
}
private void initXSRF() {
try {
CookieManager cookieManager = new CookieManager();
@@ -84,7 +217,7 @@ public class IpnbConnection {
protected void initializeClients() throws URISyntaxException {
final Draft draft = new Draft17WithOrigin();
myShellClient = new WebSocketClient(getShellURI(), draft) {
myShellClient = new WebSocketClient(getShellURI(), draft, myHeaders, 0) {
@Override
public void onOpen(@NotNull ServerHandshake handshakeData) {
send(authMessage);
@@ -148,22 +281,22 @@ public class IpnbConnection {
@NotNull
private String startKernel() throws IOException {
final String s = httpRequest(myURI + KERNELS_URL, HTTP_POST);
final String s = httpRequest(myURI + KERNELS_URL, HTTPMethod.POST.name());
final Gson gson = new Gson();
final Kernel kernel = gson.fromJson(s, Kernel.class);
return kernel.getId();
}
protected void shutdownKernel() throws IOException {
httpRequest(myURI + KERNELS_URL + "/" + myKernelId, HTTP_DELETE);
httpRequest(myURI + KERNELS_URL + "/" + myKernelId, HTTPMethod.DELETE.name());
}
public void interrupt() throws IOException {
httpRequest(myURI + KERNELS_URL + "/" + myKernelId + "/interrupt", HTTP_POST);
httpRequest(myURI + KERNELS_URL + "/" + myKernelId + "/interrupt", HTTPMethod.POST.name());
}
public void reload() throws IOException {
httpRequest(myURI + KERNELS_URL + "/" + myKernelId + "/restart", HTTP_POST);
httpRequest(myURI + KERNELS_URL + "/" + myKernelId + "/restart", HTTPMethod.POST.name());
}
@NotNull
@@ -178,44 +311,62 @@ public class IpnbConnection {
@NotNull
protected String getWebSocketURIBase() {
return "ws://" + myURI.getAuthority() + KERNELS_URL + "/" + myKernelId;
final String scheme = myURI.getScheme();
String prefix = scheme.equals("http") ? "ws://" : "wss://";
return prefix + myURI.getAuthority() + KERNELS_URL + "/" + myKernelId;
}
@NotNull
private String httpRequest(@NotNull String url, @NotNull String method) throws IOException {
final URLConnection urlConnection = new URL(url).openConnection();
if (urlConnection instanceof HttpURLConnection) {
final HttpURLConnection connection = (HttpURLConnection)urlConnection;
connection.setRequestMethod(method);
connection.setReadTimeout(60000);
if (!StringUtil.isEmptyOrSpaces(myToken)) {
connection.setRequestProperty("Authorization", "token " + myToken);
}
else if (!StringUtil.isEmptyOrSpaces(myXsrf)) {
connection.setRequestProperty("X-XSRFToken", myXsrf);
}
if (connection.getResponseCode() == HttpURLConnection.HTTP_FORBIDDEN) {
final HttpURLConnection connection = configureConnection((HttpURLConnection)urlConnection, method);
final int code = connection.getResponseCode();
if (code == HttpURLConnection.HTTP_FORBIDDEN) {
throw new IOException(AUTHENTICATION_NEEDED);
}
final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"));
try {
final StringBuilder builder = new StringBuilder();
char[] buffer = new char[4096];
int n;
while ((n = reader.read(buffer)) != -1) {
builder.append(buffer, 0, n);
}
return builder.toString();
}
finally {
reader.close();
}
return getResponse(connection);
}
else {
throw new UnsupportedOperationException("Only HTTP URLs are supported");
}
}
@NotNull
private static String getResponse(HttpURLConnection connection) throws IOException {
final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"));
try {
final StringBuilder builder = new StringBuilder();
char[] buffer = new char[4096];
int n;
while ((n = reader.read(buffer)) != -1) {
builder.append(buffer, 0, n);
}
return builder.toString();
}
finally {
reader.close();
}
}
@NotNull
private HttpURLConnection configureConnection(HttpURLConnection urlConnection, @NotNull String method) throws ProtocolException {
urlConnection.setRequestMethod(method);
urlConnection.setReadTimeout(60000);
if (!StringUtil.isEmptyOrSpaces(myToken)) {
urlConnection.setRequestProperty("Authorization", "token " + myToken);
}
else if (!StringUtil.isEmptyOrSpaces(myXsrf)) {
urlConnection.setRequestProperty("X-XSRFToken", myXsrf);
}
else if (!myHeaders.isEmpty()) {
for (Map.Entry<String, String> entry : myHeaders.entrySet()) {
urlConnection.setRequestProperty(entry.getKey(), entry.getValue());
}
}
return urlConnection;
}
@NotNull
public Message createExecuteRequest(String code, String messageId) {
final JsonObject content = new JsonObject();
@@ -471,8 +622,20 @@ public class IpnbConnection {
protected class IpnbWebSocketClient extends WebSocketClient {
protected IpnbWebSocketClient(@NotNull final URI serverUri, @NotNull final Draft draft) {
super(serverUri, draft, myToken != null ? Collections.singletonMap("Authorization", "token " + myToken) :
Collections.emptyMap(), 5000);
super(serverUri, draft, myHeaders, 10000);
configureSsl(serverUri);
}
private void configureSsl(@NotNull URI serverUri) {
if (serverUri.getScheme().equals("wss")) {
final SSLContext sslContext = CertificateManager.getInstance().getSslContext();
try {
this.setSocket(sslContext.getSocketFactory().createSocket());
}
catch (IOException e) {
LOG.warn(e.getMessage());
}
}
}
@Override
@@ -529,12 +692,12 @@ public class IpnbConnection {
@Override
public void onClose(int code, String reason, boolean remote) {
LOG.info("IPNB WebSocket was closed: code " + code + " reason: " + reason);
}
@Override
public void onError(Exception ex) {
LOG.error(ex);
}
}
@@ -545,4 +708,31 @@ public class IpnbConnection {
public int getExecCount() {
return myExecCount;
}
private static class SessionWrapper {
NotebookWrapper notebook;
KernelWrapper kernel;
public SessionWrapper(String interpreterName, String filePath) {
kernel = new KernelWrapper(interpreterName);
notebook = new NotebookWrapper(filePath);
}
}
private static class KernelWrapper {
String id;
String name;
public KernelWrapper(String name) {
this.name = name;
}
}
private static class NotebookWrapper {
String path;
public NotebookWrapper(String path) {
this.path = path;
}
}
}
@@ -1,6 +1,7 @@
package org.jetbrains.plugins.ipnb.protocol;
import com.google.gson.Gson;
import com.intellij.openapi.project.Project;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft;
import org.jetbrains.annotations.NotNull;
@@ -15,8 +16,11 @@ public class IpnbConnectionV3 extends IpnbConnection {
private WebSocketClient myChannelsClient;
private Thread myChannelsThread;
public IpnbConnectionV3(@NotNull String uri, @NotNull IpnbConnectionListener listener, @Nullable final String token) throws IOException, URISyntaxException {
super(uri, listener, token);
public IpnbConnectionV3(@NotNull String uri,
@NotNull IpnbConnectionListener listener,
@Nullable final String token,
@NotNull Project project, String pathToFile) throws IOException, URISyntaxException {
super(uri, listener, token, project, pathToFile);
}
@Override
@@ -1,3 +1,4 @@
import com.intellij.openapi.project.DefaultProjectFactory;
import com.intellij.openapi.util.Ref;
import junit.framework.TestCase;
import org.jetbrains.annotations.NotNull;
@@ -11,12 +12,11 @@ import java.net.URISyntaxException;
import java.util.List;
/**
*
* * Message Spec
* http://ipython.org/ipython-doc/dev/development/messaging.html
*
* http://ipython.org/ipython-doc/dev/development/messaging.html
* <p>
* * Notebook REST API
* https://github.com/ipython/ipython/wiki/IPEP-16%3A-Notebook-multi-directory-dashboard-and-URL-mapping
* https://github.com/ipython/ipython/wiki/IPEP-16%3A-Notebook-multi-directory-dashboard-and-URL-mapping
*
* @author vlan
*/
@@ -33,7 +33,7 @@ public class WebSocketConnectionTest extends TestCase {
assertTrue(connection.getKernelId().length() > 0);
connection.shutdown();
}
}, null);
}, null, DefaultProjectFactory.getInstance().getDefaultProject(), "");
connection.close();
}
@@ -60,7 +60,7 @@ public class WebSocketConnectionTest extends TestCase {
connection.shutdown();
}
}
}, null);
}, null, DefaultProjectFactory.getInstance().getDefaultProject(), "");
connection.close();
assertTrue(evaluated.get());
}
@@ -94,7 +94,7 @@ public class WebSocketConnectionTest extends TestCase {
connection.shutdown();
}
}
}, null);
}, null, DefaultProjectFactory.getInstance().getDefaultProject(), "");
connection.close();
assertTrue(evaluated.get());
}