blob: eaf023b15dc7b955777d244166385b4a068717e4 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2016 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
******************************************************************************/
package org.eclipse.lsp4j.jsonrpc.test;
import static org.eclipse.lsp4j.jsonrpc.json.MessageConstants.CONTENT_LENGTH_HEADER;
import static org.eclipse.lsp4j.jsonrpc.json.MessageConstants.CRLF;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.lsp4j.jsonrpc.CompletableFutures;
import org.eclipse.lsp4j.jsonrpc.Launcher;
import org.eclipse.lsp4j.jsonrpc.RemoteEndpoint;
import org.eclipse.lsp4j.jsonrpc.ResponseErrorException;
import org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint;
import org.eclipse.lsp4j.jsonrpc.services.JsonNotification;
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
import org.junit.Assert;
import org.junit.Test;
public class IntegrationTest {
private static final long TIMEOUT = 2000;
public static class MyParam {
public MyParam() {}
public MyParam(@NonNull String string) {
this.value = string;
}
@NonNull
private String value;
@NonNull
public String getValue() {
return value;
}
public void setValue(@NonNull String value) {
this.value = value;
}
private MyParam nested;
public MyParam getNested() {
return nested;
}
public void setNested(MyParam nested) {
this.nested = nested;
}
private Either<String, Integer> either;
public Either<String, Integer> getEither() {
return either;
}
public void setEither(Either<String, Integer> either) {
this.either = either;
}
}
public static interface MyServer {
@JsonRequest
CompletableFuture<MyParam> askServer(MyParam param);
}
public static class MyServerImpl implements MyServer {
@Override
public CompletableFuture<MyParam> askServer(MyParam param) {
return CompletableFuture.completedFuture(param);
}
};
public static interface MyClient {
@JsonRequest
CompletableFuture<MyParam> askClient(MyParam param);
}
public static class MyClientImpl implements MyClient {
@Override
public CompletableFuture<MyParam> askClient(MyParam param) {
return CompletableFuture.completedFuture(param);
}
};
@Test
public void testBothDirectionRequests() throws Exception {
// create client side
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream();
PipedInputStream in2 = new PipedInputStream();
PipedOutputStream out2 = new PipedOutputStream();
in.connect(out2);
out.connect(in2);
MyClient client = new MyClientImpl();
Launcher<MyServer> clientSideLauncher = Launcher.createLauncher(client, MyServer.class, in, out);
// create server side
MyServer server = new MyServerImpl();
Launcher<MyClient> serverSideLauncher = Launcher.createLauncher(server, MyClient.class, in2, out2);
clientSideLauncher.startListening();
serverSideLauncher.startListening();
CompletableFuture<MyParam> fooFuture = clientSideLauncher.getRemoteProxy().askServer(new MyParam("FOO"));
CompletableFuture<MyParam> barFuture = serverSideLauncher.getRemoteProxy().askClient(new MyParam("BAR"));
Assert.assertEquals("FOO", fooFuture.get(TIMEOUT, TimeUnit.MILLISECONDS).value);
Assert.assertEquals("BAR", barFuture.get(TIMEOUT, TimeUnit.MILLISECONDS).value);
}
@Test
public void testResponse1() throws Exception {
// create client message
String requestMessage = "{\"jsonrpc\": \"2.0\",\n"
+ "\"id\": \"42\",\n"
+ "\"method\": \"askServer\",\n"
+ "\"params\": { \"value\": \"bar\" }\n"
+ "}";
String clientMessage = getHeader(requestMessage.getBytes().length) + requestMessage;
// create server side
ByteArrayInputStream in = new ByteArrayInputStream(clientMessage.getBytes());
ByteArrayOutputStream out = new ByteArrayOutputStream();
MyServer server = new MyServerImpl();
Launcher<MyClient> serverSideLauncher = Launcher.createLauncher(server, MyClient.class, in, out);
serverSideLauncher.startListening().get(TIMEOUT, TimeUnit.MILLISECONDS);
Assert.assertEquals("Content-Length: 52" + CRLF + CRLF
+ "{\"jsonrpc\":\"2.0\",\"id\":\"42\",\"result\":{\"value\":\"bar\"}}",
out.toString());
}
@Test
public void testResponse2() throws Exception {
// create client message
String requestMessage = "{\"jsonrpc\": \"2.0\",\n"
+ "\"id\": 42,\n"
+ "\"method\": \"askServer\",\n"
+ "\"params\": { \"value\": \"bar\" }\n"
+ "}";
String clientMessage = getHeader(requestMessage.getBytes().length) + requestMessage;
// create server side
ByteArrayInputStream in = new ByteArrayInputStream(clientMessage.getBytes());
ByteArrayOutputStream out = new ByteArrayOutputStream();
MyServer server = new MyServerImpl();
Launcher<MyClient> serverSideLauncher = Launcher.createLauncher(server, MyClient.class, in, out);
serverSideLauncher.startListening().get(TIMEOUT, TimeUnit.MILLISECONDS);
Assert.assertEquals("Content-Length: 50" + CRLF + CRLF
+ "{\"jsonrpc\":\"2.0\",\"id\":42,\"result\":{\"value\":\"bar\"}}",
out.toString());
}
@Test
public void testEither() throws Exception {
// create client message
String requestMessage = "{\"jsonrpc\": \"2.0\",\n"
+ "\"id\": 42,\n"
+ "\"method\": \"askServer\",\n"
+ "\"params\": { \"either\": \"bar\", \"value\": \"foo\" }\n"
+ "}";
String clientMessage = getHeader(requestMessage.getBytes().length) + requestMessage;
// create server side
ByteArrayInputStream in = new ByteArrayInputStream(clientMessage.getBytes());
ByteArrayOutputStream out = new ByteArrayOutputStream();
MyServer server = new MyServerImpl();
Launcher<MyClient> serverSideLauncher = Launcher.createLauncher(server, MyClient.class, in, out);
serverSideLauncher.startListening().get(TIMEOUT, TimeUnit.MILLISECONDS);
Assert.assertEquals("Content-Length: 65" + CRLF + CRLF
+ "{\"jsonrpc\":\"2.0\",\"id\":42,\"result\":{\"value\":\"foo\",\"either\":\"bar\"}}",
out.toString());
}
@Test
public void testEitherNull() throws Exception {
// create client message
String requestMessage = "{\"jsonrpc\": \"2.0\",\n"
+ "\"id\": 42,\n"
+ "\"method\": \"askServer\",\n"
+ "\"params\": { \"either\": null, \"value\": \"foo\" }\n"
+ "}";
String clientMessage = getHeader(requestMessage.getBytes().length) + requestMessage;
// create server side
ByteArrayInputStream in = new ByteArrayInputStream(clientMessage.getBytes());
ByteArrayOutputStream out = new ByteArrayOutputStream();
MyServer server = new MyServerImpl();
Launcher<MyClient> serverSideLauncher = Launcher.createLauncher(server, MyClient.class, in, out);
serverSideLauncher.startListening().get(TIMEOUT, TimeUnit.MILLISECONDS);
Assert.assertEquals("Content-Length: 50" + CRLF + CRLF
+ "{\"jsonrpc\":\"2.0\",\"id\":42,\"result\":{\"value\":\"foo\"}}",
out.toString());
}
@Test
public void testCancellation() throws Exception {
// create client side
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream();
PipedInputStream in2 = new PipedInputStream();
PipedOutputStream out2 = new PipedOutputStream();
in.connect(out2);
out.connect(in2);
boolean[] inComputeAsync = new boolean[1];
boolean[] cancellationHappened = new boolean[1];
MyClient client = new MyClient() {
@Override
public CompletableFuture<MyParam> askClient(MyParam param) {
return CompletableFutures.computeAsync(cancelToken -> {
try {
long startTime = System.currentTimeMillis();
inComputeAsync[0] = true;
do {
cancelToken.checkCanceled();
Thread.sleep(50);
} while (System.currentTimeMillis() - startTime < TIMEOUT);
} catch (CancellationException e) {
cancellationHappened[0] = true;
} catch (InterruptedException e) {
Assert.fail("Thread was interrupted unexpectedly.");
}
return param;
});
}
};
Launcher<MyServer> clientSideLauncher = Launcher.createLauncher(client, MyServer.class, in, out);
// create server side
MyServer server = new MyServerImpl();
Launcher<MyClient> serverSideLauncher = Launcher.createLauncher(server, MyClient.class, in2, out2);
clientSideLauncher.startListening();
serverSideLauncher.startListening();
CompletableFuture<MyParam> future = serverSideLauncher.getRemoteProxy().askClient(new MyParam("FOO"));
long startTime = System.currentTimeMillis();
while (!inComputeAsync[0]) {
Thread.sleep(50);
if (System.currentTimeMillis() - startTime > TIMEOUT)
Assert.fail("Timeout waiting for client to start computing.");
}
future.cancel(true);
startTime = System.currentTimeMillis();
while (!cancellationHappened[0]) {
Thread.sleep(50);
if (System.currentTimeMillis() - startTime > TIMEOUT)
Assert.fail("Timeout waiting for confirmation of cancellation.");
}
try {
future.get(TIMEOUT, TimeUnit.MILLISECONDS);
Assert.fail("Expected cancellation.");
} catch (CancellationException e) {
}
}
@Test
public void testCancellationResponse() throws Exception {
// create client messages
String requestMessage = "{\"jsonrpc\": \"2.0\",\n"
+ "\"id\": \"1\",\n"
+ "\"method\": \"askServer\",\n"
+ "\"params\": { \"value\": \"bar\" }\n"
+ "}";
String cancellationMessage = "{\"jsonrpc\": \"2.0\",\n"
+ "\"method\": \"$/cancelRequest\",\n"
+ "\"params\": { \"id\": 1 }\n"
+ "}";
String clientMessages = getHeader(requestMessage.getBytes().length) + requestMessage
+ getHeader(cancellationMessage.getBytes().length) + cancellationMessage;
// create server side
ByteArrayInputStream in = new ByteArrayInputStream(clientMessages.getBytes());
ByteArrayOutputStream out = new ByteArrayOutputStream();
MyServer server = new MyServer() {
@Override
public CompletableFuture<MyParam> askServer(MyParam param) {
return CompletableFutures.computeAsync(cancelToken -> {
try {
long startTime = System.currentTimeMillis();
do {
cancelToken.checkCanceled();
Thread.sleep(50);
} while (System.currentTimeMillis() - startTime < TIMEOUT);
} catch (InterruptedException e) {
Assert.fail("Thread was interrupted unexpectedly.");
}
return param;
});
}
};
Launcher<MyClient> serverSideLauncher = Launcher.createLauncher(server, MyClient.class, in, out);
serverSideLauncher.startListening().get(TIMEOUT, TimeUnit.MILLISECONDS);
Assert.assertEquals("Content-Length: 132" + CRLF + CRLF
+ "{\"jsonrpc\":\"2.0\",\"id\":\"1\",\"error\":{\"code\":-32800,\"message\":\"The request (id: 1, method: \\u0027askServer\\u0027) has been cancelled\"}}",
out.toString());
}
@Test
public void testVersatility() throws Exception {
Logger.getLogger(RemoteEndpoint.class.getName()).setLevel(Level.OFF);
// create client side
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream();
PipedInputStream in2 = new PipedInputStream();
PipedOutputStream out2 = new PipedOutputStream();
// See https://github.com/eclipse/lsp4j/issues/510 for full details.
// Make sure that the thread that writes to the PipedOutputStream stays alive
// until the read from the PipedInputStream. Using a cached thread pool
// does not 100% guarantee that, but increases the probability that the
// selected thread will exist for the lifetime of the test.
ExecutorService executor = Executors.newCachedThreadPool();
in.connect(out2);
out.connect(in2);
MyClient client = new MyClient() {
private int tries = 0;
@Override
public CompletableFuture<MyParam> askClient(MyParam param) {
if (tries == 0) {
tries++;
throw new UnsupportedOperationException();
}
return CompletableFutures.computeAsync(executor, cancelToken -> {
if (tries++ == 1)
throw new UnsupportedOperationException();
return param;
});
}
};
Launcher<MyServer> clientSideLauncher = Launcher.createLauncher(client, MyServer.class, in, out);
// create server side
MyServer server = new MyServerImpl();
Launcher<MyClient> serverSideLauncher = Launcher.createLauncher(server, MyClient.class, in2, out2);
clientSideLauncher.startListening();
serverSideLauncher.startListening();
CompletableFuture<MyParam> errorFuture1 = serverSideLauncher.getRemoteProxy().askClient(new MyParam("FOO"));
try {
System.out.println(errorFuture1.get());
Assert.fail();
} catch (ExecutionException e) {
Assert.assertNotNull(((ResponseErrorException)e.getCause()).getResponseError().getMessage());
}
CompletableFuture<MyParam> errorFuture2 = serverSideLauncher.getRemoteProxy().askClient(new MyParam("FOO"));
try {
errorFuture2.get();
Assert.fail();
} catch (ExecutionException e) {
Assert.assertNotNull(((ResponseErrorException)e.getCause()).getResponseError().getMessage());
}
CompletableFuture<MyParam> goodFuture = serverSideLauncher.getRemoteProxy().askClient(new MyParam("FOO"));
Assert.assertEquals("FOO", goodFuture.get(TIMEOUT, TimeUnit.MILLISECONDS).value);
}
@Test
public void testUnknownMessages() throws Exception {
// intercept log messages
LogMessageAccumulator logMessages = new LogMessageAccumulator();
try {
logMessages.registerTo(GenericEndpoint.class);
// create client messages
String clientMessage1 = "{\"jsonrpc\": \"2.0\",\n"
+ "\"method\": \"foo1\",\n"
+ "\"params\": \"bar\"\n"
+ "}";
String clientMessage2 = "{\"jsonrpc\": \"2.0\",\n"
+ "\"id\": \"1\",\n"
+ "\"method\": \"foo2\",\n"
+ "\"params\": \"bar\"\n"
+ "}";
String clientMessages = getHeader(clientMessage1.getBytes().length) + clientMessage1
+ getHeader(clientMessage2.getBytes().length) + clientMessage2;
// create server side
ByteArrayInputStream in = new ByteArrayInputStream(clientMessages.getBytes());
ByteArrayOutputStream out = new ByteArrayOutputStream();
MyServer server = new MyServerImpl();
Launcher<MyClient> serverSideLauncher = Launcher.createLauncher(server, MyClient.class, in, out);
serverSideLauncher.startListening().get(TIMEOUT, TimeUnit.MILLISECONDS);
logMessages.await(Level.WARNING, "Unsupported notification method: foo1");
logMessages.await(Level.WARNING, "Unsupported request method: foo2");
Assert.assertEquals("Content-Length: 95" + CRLF + CRLF
+ "{\"jsonrpc\":\"2.0\",\"id\":\"1\",\"error\":{\"code\":-32601,\"message\":\"Unsupported request method: foo2\"}}",
out.toString());
} finally {
logMessages.unregister();
}
}
@Test
public void testUnknownOptionalMessages() throws Exception {
// intercept log messages
LogMessageAccumulator logMessages = new LogMessageAccumulator();
try {
logMessages.registerTo(GenericEndpoint.class);
// create client messages
String clientMessage1 = "{\"jsonrpc\": \"2.0\",\n"
+ "\"method\": \"$/foo1\",\n"
+ "\"params\": \"bar\"\n"
+ "}";
String clientMessage2 = "{\"jsonrpc\": \"2.0\",\n"
+ "\"id\": \"1\",\n"
+ "\"method\": \"$/foo2\",\n"
+ "\"params\": \"bar\"\n"
+ "}";
String clientMessages = getHeader(clientMessage1.getBytes().length) + clientMessage1
+ getHeader(clientMessage2.getBytes().length) + clientMessage2;
// create server side
ByteArrayInputStream in = new ByteArrayInputStream(clientMessages.getBytes());
ByteArrayOutputStream out = new ByteArrayOutputStream();
MyServer server = new MyServerImpl();
Launcher<MyClient> serverSideLauncher = Launcher.createLauncher(server, MyClient.class, in, out);
serverSideLauncher.startListening().get(TIMEOUT, TimeUnit.MILLISECONDS);
logMessages.await(Level.INFO, "Unsupported notification method: $/foo1");
logMessages.await(Level.INFO, "Unsupported request method: $/foo2");
Assert.assertEquals("Content-Length: 40" + CRLF + CRLF
+ "{\"jsonrpc\":\"2.0\",\"id\":\"1\",\"result\":null}",
out.toString());
} finally {
logMessages.unregister();
}
}
public static interface UnexpectedParamsTestServer {
@JsonNotification
void myNotification();
}
@Test
public void testUnexpectedParams() throws Exception {
// intercept log messages
LogMessageAccumulator logMessages = new LogMessageAccumulator();
try {
logMessages.registerTo(GenericEndpoint.class);
// create client messages
String notificationMessage = "{\"jsonrpc\": \"2.0\",\n"
+ "\"method\": \"myNotification\",\n"
+ "\"params\": { \"value\": \"foo\" }\n"
+ "}";
String clientMessages = getHeader(notificationMessage.getBytes().length) + notificationMessage;
// create server side
ByteArrayInputStream in = new ByteArrayInputStream(clientMessages.getBytes());
ByteArrayOutputStream out = new ByteArrayOutputStream();
UnexpectedParamsTestServer server = new UnexpectedParamsTestServer() {
public void myNotification() {
}
};
Launcher<MyClient> serverSideLauncher = Launcher.createLauncher(server, MyClient.class, in, out);
serverSideLauncher.startListening();
logMessages.await(Level.WARNING, "Unexpected params '{\"value\":\"foo\"}' for "
+ "'public abstract void org.eclipse.lsp4j.jsonrpc.test.IntegrationTest$UnexpectedParamsTestServer.myNotification()' is ignored");
} finally {
logMessages.unregister();
}
}
@Test
public void testMalformedJson1() throws Exception {
String requestMessage1 = "{\"jsonrpc\": \"2.0\",\n"
+ "\"id\": \"1\",\n"
+ "\"method\": \"askServer\",\n"
+ "\"params\": { \"value\": }\n"
+ "}";
String requestMessage2 = "{\"jsonrpc\": \"2.0\",\n"
+ "\"id\": \"2\",\n"
+ "\"method\": \"askServer\",\n"
+ "\"params\": { \"value\": \"bar\" }\n"
+ "}";
String clientMessages = getHeader(requestMessage1.getBytes().length) + requestMessage1
+ getHeader(requestMessage2.getBytes().length) + requestMessage2;
ByteArrayInputStream in = new ByteArrayInputStream(clientMessages.getBytes());
ByteArrayOutputStream out = new ByteArrayOutputStream();
MyServer server = new MyServerImpl();
Launcher<MyClient> serverSideLauncher = Launcher.createLauncher(server, MyClient.class, in, out);
serverSideLauncher.startListening().get(TIMEOUT, TimeUnit.MILLISECONDS);
Assert.assertEquals("Content-Length: 214" + CRLF + CRLF
+ "{\"jsonrpc\":\"2.0\",\"id\":\"1\",\"error\":{\"code\":-32700,\"message\":\"Message could not be parsed.\","
+ "\"data\":{\"message\":\"com.google.gson.stream.MalformedJsonException: Expected value at line 4 column 22 path $.params.value\"}}}"
+ "Content-Length: 51" + CRLF + CRLF
+ "{\"jsonrpc\":\"2.0\",\"id\":\"2\",\"result\":{\"value\":\"bar\"}}",
out.toString());
}
@Test
public void testMalformedJson2() throws Exception {
// intercept log messages
LogMessageAccumulator logMessages = new LogMessageAccumulator();
try {
logMessages.registerTo(StreamMessageProducer.class);
String requestMessage1 = "{\"jsonrpc\": \"2.0\",\n"
+ "\"params\": { \"value\": }\n"
+ "\"id\": \"1\",\n"
+ "\"method\":\"askServer\",\n"
+ "}";
String requestMessage2 = "{\"jsonrpc\": \"2.0\",\n"
+ "\"id\": \"2\",\n"
+ "\"method\": \"askServer\",\n"
+ "\"params\": { \"value\": \"bar\" }\n"
+ "}";
String clientMessages = getHeader(requestMessage1.getBytes().length) + requestMessage1
+ getHeader(requestMessage2.getBytes().length) + requestMessage2;
ByteArrayInputStream in = new ByteArrayInputStream(clientMessages.getBytes());
ByteArrayOutputStream out = new ByteArrayOutputStream();
MyServer server = new MyServerImpl();
Launcher<MyClient> serverSideLauncher = Launcher.createLauncher(server, MyClient.class, in, out);
serverSideLauncher.startListening().get(TIMEOUT, TimeUnit.MILLISECONDS);
logMessages.await(Level.SEVERE, "com.google.gson.stream.MalformedJsonException: Expected value at line 2 column 22 path $.params.value");
Assert.assertEquals("Content-Length: 51" + CRLF + CRLF
+ "{\"jsonrpc\":\"2.0\",\"id\":\"2\",\"result\":{\"value\":\"bar\"}}",
out.toString());
} finally {
logMessages.unregister();
}
}
@Test
public void testMalformedJson3() throws Exception {
String requestMessage1 = "{\"jsonrpc\": \"2.0\",\n"
+ "\"id\": \"1\",\n"
+ "\"method\": \"askServer\",\n"
+ "\"params\": { \"value\": \"bar\" }\n"
+ "]";
String requestMessage2 = "{\"jsonrpc\": \"2.0\",\n"
+ "\"id\": \"2\",\n"
+ "\"method\": \"askServer\",\n"
+ "\"params\": { \"value\": \"bar\" }\n"
+ "}";
String clientMessages = getHeader(requestMessage1.getBytes().length) + requestMessage1
+ getHeader(requestMessage2.getBytes().length) + requestMessage2;
ByteArrayInputStream in = new ByteArrayInputStream(clientMessages.getBytes());
ByteArrayOutputStream out = new ByteArrayOutputStream();
MyServer server = new MyServerImpl();
Launcher<MyClient> serverSideLauncher = Launcher.createLauncher(server, MyClient.class, in, out);
serverSideLauncher.startListening().get(TIMEOUT, TimeUnit.MILLISECONDS);
Assert.assertEquals("Content-Length: 165" + CRLF + CRLF
+ "{\"jsonrpc\":\"2.0\",\"id\":\"1\",\"error\":{\"code\":-32700,\"message\":\"Message could not be parsed.\","
+ "\"data\":{\"message\":\"Unterminated object at line 5 column 2 path $.params\"}}}"
+ "Content-Length: 51" + CRLF + CRLF
+ "{\"jsonrpc\":\"2.0\",\"id\":\"2\",\"result\":{\"value\":\"bar\"}}",
out.toString());
}
@Test
public void testMalformedJson4() throws Exception {
String requestMessage1 = "{\"jsonrpc\": \"2.0\",\n"
+ "\"id\": \"1\",\n"
+ "\"method\": \"askServer\",\n"
+ "\"params\": { \"value\": \"bar\" }\n"
+ "}}";
String requestMessage2 = "{\"jsonrpc\":\"2.0\",\n"
+ "\"id\":\"2\",\n"
+ "\"method\":\"askServer\",\n"
+ "\"params\": { \"value\": \"bar\" }\n"
+ "}";
String clientMessages = getHeader(requestMessage1.getBytes().length) + requestMessage1
+ getHeader(requestMessage2.getBytes().length) + requestMessage2;
ByteArrayInputStream in = new ByteArrayInputStream(clientMessages.getBytes());
ByteArrayOutputStream out = new ByteArrayOutputStream();
MyServer server = new MyServerImpl();
Launcher<MyClient> serverSideLauncher = Launcher.createLauncher(server, MyClient.class, in, out);
serverSideLauncher.startListening().get(TIMEOUT, TimeUnit.MILLISECONDS);
Assert.assertEquals("Content-Length: 195" + CRLF + CRLF
+ "{\"jsonrpc\":\"2.0\",\"id\":\"1\",\"error\":{\"code\":-32700,\"message\":\"Message could not be parsed.\","
+ "\"data\":{\"message\":\"Use JsonReader.setLenient(true) to accept malformed JSON at line 5 column 3 path $\"}}}"
+ "Content-Length: 51" + CRLF + CRLF
+ "{\"jsonrpc\":\"2.0\",\"id\":\"2\",\"result\":{\"value\":\"bar\"}}",
out.toString());
}
@Test
public void testValidationIssue1() throws Exception {
String requestMessage1 = "{\"jsonrpc\": \"2.0\",\n"
+ "\"id\": \"1\",\n"
+ "\"method\": \"askServer\",\n"
+ "\"params\": { \"value\": null }\n"
+ "}";
String requestMessage2 = "{\"jsonrpc\": \"2.0\",\n"
+ "\"id\": \"2\",\n"
+ "\"method\": \"askServer\",\n"
+ "\"params\": { \"value\": \"bar\" }\n"
+ "}";
String clientMessages = getHeader(requestMessage1.getBytes().length) + requestMessage1
+ getHeader(requestMessage2.getBytes().length) + requestMessage2;
ByteArrayInputStream in = new ByteArrayInputStream(clientMessages.getBytes());
ByteArrayOutputStream out = new ByteArrayOutputStream();
MyServer server = new MyServerImpl();
Launcher<MyClient> serverSideLauncher = Launcher.createLauncher(server, MyClient.class, in, out, true, null);
serverSideLauncher.startListening().get(TIMEOUT, TimeUnit.MILLISECONDS);
Assert.assertEquals("Content-Length: 157" + CRLF + CRLF
+ "{\"jsonrpc\":\"2.0\",\"id\":\"1\",\"error\":{\"code\":-32602,\"message\":\"The accessor \\u0027MyParam.getValue()\\u0027 must return a non-null value. Path: $.params.value\"}}"
+ "Content-Length: 51" + CRLF + CRLF
+ "{\"jsonrpc\":\"2.0\",\"id\":\"2\",\"result\":{\"value\":\"bar\"}}",
out.toString());
}
@Test
public void testValidationIssue2() throws Exception {
String requestMessage1 = "{\"jsonrpc\": \"2.0\",\n"
+ "\"id\": \"1\",\n"
+ "\"method\": \"askServer\",\n"
+ "\"params\": { \"value\": null, \"nested\": { \"value\": null } }\n"
+ "}";
String clientMessages = getHeader(requestMessage1.getBytes().length) + requestMessage1;
ByteArrayInputStream in = new ByteArrayInputStream(clientMessages.getBytes());
ByteArrayOutputStream out = new ByteArrayOutputStream();
MyServer server = new MyServerImpl();
Launcher<MyClient> serverSideLauncher = Launcher.createLauncher(server, MyClient.class, in, out, true, null);
serverSideLauncher.startListening().get(TIMEOUT, TimeUnit.MILLISECONDS);
Assert.assertEquals("Content-Length: 379" + CRLF + CRLF
+ "{\"jsonrpc\":\"2.0\",\"id\":\"1\",\"error\":{\"code\":-32600,\"message\":\"Multiple issues were found in \\u0027askServer\\u0027 request.\","
+ "\"data\":["
+ "{\"text\":\"The accessor \\u0027MyParam.getValue()\\u0027 must return a non-null value. Path: $.params.nested.value\",\"code\":-32602},"
+ "{\"text\":\"The accessor \\u0027MyParam.getValue()\\u0027 must return a non-null value. Path: $.params.value\",\"code\":-32602}"
+ "]}}",
out.toString());
}
protected String getHeader(int contentLength) {
StringBuilder headerBuilder = new StringBuilder();
headerBuilder.append(CONTENT_LENGTH_HEADER).append(": ").append(contentLength).append(CRLF);
headerBuilder.append(CRLF);
return headerBuilder.toString();
}
}