I seem unable to terminate a websocket connection from the server side of the connection using the recommended approach of calling terminate() on the ServerBinding.
I have included code below that establishes a connection, terminates it from the server side, waits for termination, then sends messages from the client. All messages are successfully handled by the server after termination.
How do I terminate a web-socket connection from the server side?
public class AkkaWebSocketServerTerminateTest {
private static final Logger LOGGER = LoggerFactory.getLogger(AkkaWebSocketServerTerminateTest.class);
#Test
public void clientConnectsServer_ServerGracefullyTerminatesConnection() throws ExecutionException, InterruptedException, TimeoutException {
ActorSystem system = ActorSystem.create();
Materializer materializer = ActorMaterializer.create(system);
Http http = Http.get(system);
/* SERVER */
Flow<Message, Message, CompletionStage<Done>> serverSideHandlerFlow = Flow
.of(Message.class)
.via(WebsocketLayer.messageToStringFlow())
.map(s -> {
LOGGER.debug("handling {}", s);
return "handled " + s;
})
.map(s -> (Message) TextMessage.create(s))
.alsoToMat(Sink.ignore(), Keep.right());
CompletionStage<ServerBinding> serverBindingCompletionStage = http.bindAndHandleSync(
httpRequest -> WebSocket.handleWebSocketRequestWith(httpRequest, serverSideHandlerFlow),
ConnectHttp.toHost("localhost", 9999),
materializer);
ServerBinding serverBinding = serverBindingCompletionStage.toCompletableFuture().get(3, TimeUnit.SECONDS);// wait for binding
/* CLIENT */
Sink<Message, CompletionStage<Done>> sink
= Flow.of(Message.class)
.via(WebsocketLayer.messageToStringFlow())
.toMat(Sink.foreach(s -> LOGGER.debug("client received message: '{}'", s)), Keep.right());
CompletableFuture<SourceQueueWithComplete<String>> futureClientSideSourceQueue = new CompletableFuture<>();
Source<Message, SourceQueueWithComplete<String>> source
= Source.<String>queue(0, OverflowStrategy.backpressure())
.alsoToMat(Sink.foreach(s -> LOGGER.debug("client sending '{}'", s)), Keep.left())
.map(s -> (Message) TextMessage.create(s))
.mapMaterializedValue(sourceQueue -> {
futureClientSideSourceQueue.complete(sourceQueue);
return sourceQueue;
});
Flow<Message, Message, CompletionStage<Done>> clientFlow = Flow.fromSinkAndSourceCoupledMat(sink, source, Keep.left());
WebSocketRequest webSocketRequest
= WebSocketRequest.create("ws://localhost:9999");
Pair<CompletionStage<WebSocketUpgradeResponse>, CompletionStage<Done>> clientPair
= http.singleWebSocketRequest(webSocketRequest,
clientFlow,
materializer);
CompletionStage<WebSocketUpgradeResponse> clientSideUpgradeResponse = clientPair.first();
CompletionStage<Done> clientSideConnected = clientSideUpgradeResponse.thenApply(upgrade -> {
if (upgrade.response().status().equals(StatusCodes.SWITCHING_PROTOCOLS)) {
return Done.getInstance();
} else {
throw new RuntimeException("Connection failed: " + upgrade.response().status());
}
});
CompletionStage<Done> clientSideClosed = clientPair.second();
clientSideConnected.thenAccept(done -> {
LOGGER.debug("Client connected");
LOGGER.debug("Terminating all connections with a 1 second hard deadline");
CompletionStage<HttpTerminated> onceAllConnectionsTerminated
= serverBinding.terminate(Duration.ofSeconds(1));
serverBinding.whenTerminated().thenAccept(terminated -> {
LOGGER.debug("whenTerminated() -> terminated");
});
onceAllConnectionsTerminated.toCompletableFuture()
.thenAccept(terminated -> {
LOGGER.debug("All connections terminated.");
try {
LOGGER.debug("Waiting 5 seconds before sending messages from the client to the terminated server.");
Thread.sleep(5000); // wait 5 seconds
SourceQueueWithComplete<String> queue = futureClientSideSourceQueue.get();
queue.offer("message 1");
queue.offer("message 2");
queue.offer("message 3");
queue.offer("message 4");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
});
try {
LOGGER.debug("Waiting 15 seconds for client connection to close.");
clientSideClosed
.thenAccept(done -> LOGGER.debug("Client connection closed"))
.toCompletableFuture()
.get(15, TimeUnit.SECONDS); // wait for connection to close
} catch (InterruptedException e) {
LOGGER.error("Interrupted waiting for client connection to close", e);
} catch (TimeoutException e) {
LOGGER.error("Timeout waiting for client connection to close", e);
}
}
}
I get the following output:
[DEBUG] [10/25/2018 12:14:18.763] [main] [EventStream(akka://default)] logger log1-Logging$DefaultLogger started
[DEBUG] [10/25/2018 12:14:18.780] [main] [EventStream(akka://default)] Default Loggers started
[DEBUG] [10/25/2018 12:14:19.297] [main] [AkkaSSLConfig(akka://default)] Initializing AkkaSSLConfig extension...
[DEBUG] [10/25/2018 12:14:19.306] [main] [AkkaSSLConfig(akka://default)] buildHostnameVerifier: created hostname verifier: com.typesafe.sslconfig.ssl.DefaultHostnameVerifier#31c7528f
[DEBUG] [10/25/2018 12:14:20.343] [default-akka.actor.default-dispatcher-6]
[akka://default/system/IO-TCP/selectors/$a/0] Successfully bound to /127.0.0.1:9999
2018-10-25 12:14:20 DEBUG AkkaWebSocketServerTerminateTest:130 - Waiting 15 seconds for client connection to close.
[DEBUG] [10/25/2018 12:14:20.479] [default-akka.actor.default-dispatcher-4]
[akka://default/system/IO-TCP/selectors/$a/1] Resolving localhost before connecting
[DEBUG] [10/25/2018 12:14:20.496] [default-akka.actor.default-dispatcher-6]
[akka://default/system/IO-DNS] Resolution request for localhost from Actor[akka://default/system/IO-TCP/selectors/$a/1#1366663132]
[DEBUG] [10/25/2018 12:14:20.536] [default-akka.actor.default-dispatcher-6]
[akka://default/system/IO-TCP/selectors/$a/1] Attempting connection to [localhost/127.0.0.1:9999]
[DEBUG] [10/25/2018 12:14:20.537] [default-akka.actor.default-dispatcher-4]
[akka://default/system/IO-TCP/selectors/$a/0] New connection accepted
[DEBUG] [10/25/2018 12:14:20.538] [default-akka.actor.default-dispatcher-6]
[akka://default/system/IO-TCP/selectors/$a/1] Connection established to [localhost:9999]
2018-10-25 12:14:20 DEBUG AkkaWebSocketServerTerminateTest:104 - Client connected
2018-10-25 12:14:20 DEBUG AkkaWebSocketServerTerminateTest:105 - Terminating all connections with a 1 second hard deadline
[DEBUG] [10/25/2018 12:14:20.642] [default-akka.actor.default-dispatcher-13]
[akka://default/system/IO-TCP/selectors/$a/0] Unbinding endpoint /127.0.0.1:9999
[DEBUG] [10/25/2018 12:14:20.643] [default-akka.actor.default-dispatcher-13]
[akka://default/system/IO-TCP/selectors/$a/0] Unbound endpoint /127.0.0.1:9999, stopping listener
2018-10-25 12:14:20 DEBUG AkkaWebSocketServerTerminateTest:108 - whenTerminated() -> terminated
2018-10-25 12:14:20 DEBUG AkkaWebSocketServerTerminateTest:112 - All connections terminated.
2018-10-25 12:14:20 DEBUG AkkaWebSocketServerTerminateTest:114 - Waiting 5 seconds before sending messages from the client to the terminated server.
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:76 - client sending 'message 1'
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:76 - client sending 'message 2'
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:76 - client sending 'message 3'
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:76 - client sending 'message 4'
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:53 - handling message 1
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:53 - handling message 2
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:70 - client received message: 'handled message 1'
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:53 - handling message 3
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:53 - handling message 4
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:70 - client received message: 'handled message 2'
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:70 - client received message: 'handled message 3'
2018-10-25 12:14:25 DEBUG AkkaWebSocketServerTerminateTest:70 - client received message: 'handled message 4'
2018-10-25 12:14:35 ERROR AkkaWebSocketServerTerminateTest:138 - Timeout waiting for client connection to close
I think explicit termination is not possible but you can set akka.http.server.idle-timeout according to note from their documentation
Inactive WebSocket connections will be dropped according to the idle-timeout settings. In case you need to keep inactive connections alive, you can either tweak your idle-timeout or inject ‘keep-alive’ messages regularly.
Related
I am looking at upgrading to the latest aws sdk for embedded-c and can`t implement the openssl version as my device only supports openssl-1.0.2
Looking at using the mbedtls as transport protocol on top of pkcs11, there as some differences which I am probably missing...
This is part of the code we have used, which also is developed by the aws team for embedded-c:
#define ALPN_PROTOCOL_NAME "\x0ex-amzn-mqtt-ca"
const char * alpn[] = { ALPN_PROTOCOL_NAME, NULL };
MbedtlsPkcs11Status_t tlsStatus = MBEDTLS_PKCS11_SUCCESS;
MbedtlsPkcs11Credentials_t tlsCredentials = { 0 };
const char * alpn[] = { ALPN_PROTOCOL_NAME, NULL };
/* Set the pParams member of the network context with desired transport. */
pNetworkContext->pParams = &tlsContext;
/* Initialize credentials for establishing TLS session. */
tlsCredentials.pRootCaPath = rootCA;
tlsCredentials.pClientCertLabel = clientCRT;
tlsCredentials.pPrivateKeyLabel = clientKey;
pkcs11ret = xInitializePkcs11Session( &tlsCredentials.p11Session );
if( pkcs11ret != CKR_OK )
{
LogError( ( "Failed to initialize PKCS #11." ) );
}
/* AWS IoT requires devices to send the Server Name Indication (SNI)
* extension to the Transport Layer Security (TLS) protocol and provide
* the complete endpoint address in the host_name field. Details about
* SNI for AWS IoT can be found in the link below.
* https://docs.aws.amazon.com/iot/latest/developerguide/transport-security.html
*/
tlsCredentials.disableSni = false;
uint16_t port = AWS_IOT_MQTT_PORT;
if( port == 443 )
{
/* Pass the ALPN protocol name depending on the port being used.
* Please see more details about the ALPN protocol for AWS IoT MQTT endpoint
* in the link below.
* https://aws.amazon.com/blogs/iot/mqtt-with-tls-client-authentication-on-port-443-
why-it-is-useful-and-how-it-works/
*/
tlsCredentials.pAlpnProtos = alpn;
}
bool status = false;
if( pkcs11ret != CKR_OK )
{
LE_ERROR( "Failed to initialize PKCS #11.");
status = false;
}
else
{
printf( "===SUCCESSFULLY INITIALIZED PKCS #11.===");
// Insert the claim credentials into the PKCS #11 module
status = loadClaimCredentials( tlsCredentials.p11Session,
clientCRT,
pkcs11configLABEL_CLAIM_CERTIFICATE,
clientKey,
pkcs11configLABEL_CLAIM_PRIVATE_KEY );
if( status == false )
{
printf( "Failed to provision PKCS #11 with claim credentials." );
}
else
printf( "===SUCCESSFULLY PROVISIONED PKCS #11.===");
}
/* Initialize reconnect attempts and interval */
BackoffAlgorithm_InitializeParams( &reconnectParams,
CONNECTION_RETRY_BACKOFF_BASE_MS,
CONNECTION_RETRY_MAX_BACKOFF_DELAY_MS,
CONNECTION_RETRY_MAX_ATTEMPTS );
do
{
/* Establish a TLS session with the MQTT broker. This example connects
* to the MQTT broker as specified in BROKER_ENDPOINT and BROKER_PORT at
* the top of this file. */
LE_INFO("Establishing a TLS session to %.*s:%d.",
BROKER_ENDPOINT_LENGTH,
BROKER_ENDPOINT,
AWS_IOT_MQTT_PORT );
tlsStatus = Mbedtls_Pkcs11_Connect( pNetworkContext,
BROKER_ENDPOINT,
port,
&tlsCredentials,
TRANSPORT_SEND_RECV_TIMEOUT_MS );
}
This will always fails due to invalid certificates, but I am using those already and they are all valid, so I am thinking this has something to do with the way we load the certificates in the handler "tlsCredentials.p11Session" and I am probably missing something and not loading everything?
This is the log I get:
mqttClientAws.c connectToServerWithBackoffRetries() 654 | ===SUCCESSFULLY INITIALIZED PKCS #11.===
mqttClientAws[5879] | [DEBUG] [PKCS11] [core_pkcs11_mbedtls.c:483] Successfully found object class attribute.^M
mqttClientAws[5879] | [INFO] [PKCS11] [core_pkcs11_mbedtls.c:2823] Creating a 0x3 type object.^M
mqttClientAws[5879] | [DEBUG] [PKCS11] [core_pkcs11_mbedtls.c:2057] Successfully found the key type in the template.^M
mqttClientAws[5879] | [DEBUG] [PKCS11] [core_pkcs11_mbedtls.c:2086] Successfully found the label in the template.^M
mqttClientAws[5879] | [DEBUG] [PKCS11] [core_pkcs11_mbedtls.c:1259] Key was private type.^M
mqttClientAws[5879] | [DEBUG] [PKCS11] [core_pkcs11_mbedtls.c:1268] Received RSA key type.^M
mqttClientAws[5879] | [DEBUG] [PKCS11] [core_pkcs11_mbedtls.c:1288] Allocating a 1200 bytes sized buffer to write the key to.^M
mqttClientAws[5879] | [DEBUG] [PKCS11] [core_pkcs11_pal_utils.c:123] Converted Device Priv TLS Key to corePKCS11_Key.dat^M
mqttClientAws[5879] | Successfully wrote 1192 to corePKCS11_Key.dat[DEBUG] [PKCS11] [core_pkcs11_mbedtls.c:3352] Search parameters other than label are ignored.^M
mqttClientAws[5879] | [DEBUG] [PKCS11] [core_pkcs11_mbedtls.c:3446] Could not find the object handle in the list. Trying to search PKCS #11 PAL for object.^M
mqttClientAws[5879] | [DEBUG] [PKCS11] [core_pkcs11_pal_utils.c:123] Converted Device Cert to corePKCS11_Certificate.dat^M
mqttClientAws[5879] | [INFO] [PKCS11] [core_pkcs11_pal.c:63] Could not open corePKCS11_Certificate.dat for reading.^M
mqttClientAws[5879] | [ERROR] [PKCS11] [core_pkcs11.c:370] xFindObjectWithLabelAndClass ERROR CK_INVALID_HANDLE^M
mqttClientAws[5879] | [INFO] [FLEET_PROVISIONING_DEMO] [pkcs11_operations.c:770] Writing certificate into label "Device Cert".^M
mqttClientAws[5879] | [DEBUG] [PKCS11] [core_pkcs11_mbedtls.c:483] Successfully found object class attribute.^M
mqttClientAws[5879] | [INFO] [PKCS11] [core_pkcs11_mbedtls.c:2823] Creating a 0x1 type object.^M
mqttClientAws[5879] | [DEBUG] [PKCS11] [core_pkcs11_pal_utils.c:123] Converted Device Cert to corePKCS11_Certificate.dat^M
mqttClientAws[5879]/mqttClientAwsComponent T=main | mqttClientAws.c connectToServerWithBackoffRetries() 668 | ===SUCCESSFULLY PROVISIONED PKCS #11.===
mqttClientAws.c connectToServerWithBackoffRetries() 686 | Establishing a TLS session to axxxxxx-ats.iot.ap-southeast-2.amazonaws.com:443.
mqttClientAws[5879] | Successfully wrote 861 to corePKCS11_Certificate.dat[DEBUG] [PKCS11] [core_pkcs11_mbedtls.c:3352] Search parameters other than label are ignored.^M
mqttClientAws[5879] | [DEBUG] [PKCS11] [core_pkcs11_mbedtls.c:3352] Search parameters other than label are ignored.^M
mqttClientAws[5879] | [ERROR] [PKCS11] [core_pkcs11.c:370] xFindObjectWithLabelAndClass ERROR CK_INVALID_HANDLE^M
mqttClientAws[5879] | [ERROR] [Transport_MbedTLS_PKCS11] [mbedtls_pkcs11_posix.c:625] Function returned ERROR.^M
mqttClientAws[5879] | [ERROR] [Transport_MbedTLS_PKCS11] [mbedtls_pkcs11_posix.c:400] Failed to setup key handling by PKCS #11.^M
mqttClientAws[5879] | [WARN] [DEMO] [mqttClientAws.c:724] Connection to the broker failed. Retrying connection after 270 ms backoff.^M
Very much appreciated any help on this.
So the answer and also a commit to the aws sdk, when using the mbdetls the alpn should be indicated as follow: (remove the \x0e character:
#define ALPN_PROTOCOL_NAME "\x-amzn-mqtt-ca"
While the \x0e is necessary when using openssl.
#Component
class TestRoute(
context: CamelContext,
) : EndpointRouteBuilder() {
val streamName: String = "news-ticker-stream"
val logger = LoggerFactory.getLogger(TestRoute::class.java)
val camel: CamelReactiveStreamsService = CamelReactiveStreams.get(context)
var count = 0L
val subscriber: Subscriber<String> =
camel.streamSubscriber(streamName, String::class.java)
override fun configure() {
from("timer://foo?fixedRate=true&period=30000")
.process {
count++
logger.info("Start emitting data for the $count time")
Flux.fromIterable(
listOf(
"APPLE", "MANGO", "PINEAPPLE"
)
)
.doOnComplete {
logger.info("All the data are emitted from the flux for the $count time")
}
.subscribe(
subscriber
)
}
from(reactiveStreams(streamName))
.to("file:outbox")
}
}
2022-07-07 13:01:44.626 INFO 50988 --- [1 - timer://foo] c.e.reactivecameltutorial.TestRoute : Start emitting data for the 1 time
2022-07-07 13:01:44.640 INFO 50988 --- [1 - timer://foo] c.e.reactivecameltutorial.TestRoute : All the data are emitted from the flux for the 1 time
2022-07-07 13:01:44.646 INFO 50988 --- [1 - timer://foo] a.c.c.r.s.ReactiveStreamsCamelSubscriber : Reactive stream 'news-ticker-stream' completed
2022-07-07 13:02:14.616 INFO 50988 --- [1 - timer://foo] c.e.reactivecameltutorial.TestRoute : Start emitting data for the 2 time
2022-07-07 13:02:44.610 INFO 50988 --- [1 - timer://foo] c.e.reactivecameltutorial.TestRoute : Start emitting data for the 3 time
2022-07-07 13:02:44.611 WARN 50988 --- [1 - timer://foo] a.c.c.r.s.ReactiveStreamsCamelSubscriber : There is another active subscription: cancelled
The reactive stream are not getting completed when running for more than 1 times. So, as you can see in the logs the log message which I have added doOnComplete is only coming for the first time when timer route was triggered. When the timer route is triggered for the second time then there is no completion message. I tried to put the break point in the ReactiveStreamsCamelSubscriber, and found that for the 1st time the flow is going into the onNext() and onComplete() methods but the flow is not going into these method when the timer ran for 2nd time. I am not able to understand why this scenario is playing out?
I try to send a connection message to a server but I get a response from the server with the status 400 (Bad request).
I understand from RFC that the ":scheme" and ":path" pseudo-header fields MUST be omitted, so I put in the header just :method and :authority
nghttp2_nv hdrs[] = {
MAKE_NV2(":method", "CONNECT"),
MAKE_NV(":authority", authority, authoritylen),
};
nghttp2_submit_request(session, NULL, hdrs, ARRLEN(hdrs), NULL, NULL);
What I did wrong?
I am using Rest API and when I use Rest.put(), it throws an error. It does not even reach the server at all. I don't know where I am going wrong.
Here is my code:
Response<String> res = Rest.put(URLLinks.getMainBackend() + "items")
.body(reqJson.toString())
.jsonContent()
.bearer(initForm.data.getString("jwt"))
.header("token", initForm.data.getString("token"))
.pathParam("id", item.getId())
.onErrorCodeJSON((errorData) -> {
if (errorData.getResponseCode() == 404) {
Dialog dlg = rich.Dialog("Not found!");
Button yes = new Button("Close");
yes.addActionListener(ev -> {
dlg.dispose();
});
dlg.add(new SpanLabel("//////!"));
dlg.add(FlowLayout.encloseRight(yes));
dlg.showPacked(BorderLayout.CENTER, true);
}else if (errorData.getResponseCode() == 402){
}
})
.getAsString();
It throws an error:
java.io.IOException: Stream closed
[Network Thread] 0:0:12,855 - Codename One revisions: 4afb54f6a5cecd2b6fbee170262d5c3c8d9431f9
[Network Thread] 0:0:12,855 - Exception: java.io.IOException - Stream closed
[Network Thread] 0:0:12,858 - Exception during JSON parsing at row: 1 column: 1 buffer:
[EDT] 0:0:12,874 - Exception: java.lang.NullPointerException - null
at com.codename1.io.BufferedInputStream.getInIfOpen(BufferedInputStream.java:140)
at com.codename1.io.BufferedInputStream.read1(BufferedInputStream.java:338)
at com.codename1.io.BufferedInputStream.read(BufferedInputStream.java:445)
at java.base/sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at java.base/sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at java.base/sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
at java.base/java.io.InputStreamReader.read(InputStreamReader.java:185)
at com.codename1.io.JSONParser$ReaderClass.read(JSONParser.java:124)
at com.codename1.io.JSONParser.parse(JSONParser.java:188)
at com.codename1.io.JSONParser.parseJSON(JSONParser.java:475)
at com.codename1.io.rest.RequestBuilder$Connection.readUnzipedResponse(RequestBuilder.java:683)
at com.codename1.io.gzip.GZConnectionRequest.readResponse(GZConnectionRequest.java:67)
at com.codename1.io.ConnectionRequest.performOperation(ConnectionRequest.java:809)
at com.codename1.io.NetworkManager$NetworkThread.run(NetworkManager.java:282)
at com.codename1.impl.CodenameOneThread.run(CodenameOneThread.java:176)
java.lang.NullPointerException
at java.base/java.lang.String.<init>(String.java:537)
at com.codename1.io.rest.RequestBuilder.getAsString(RequestBuilder.java:361)
at com.falcontechnology.items.NewItem.backendItem(NewItem.java:425)
at com.falcontechnology.items.NewItem.lambda$itemFields$7(NewItem.java:296)
at com.codename1.ui.util.EventDispatcher.fireActionEvent(EventDispatcher.java:349)
at com.codename1.ui.Button.fireActionEvent(Button.java:570)
at com.codename1.ui.Button.released(Button.java:604)
at com.codename1.ui.Button.pointerReleased(Button.java:708)
at com.codename1.ui.Form.pointerReleased(Form.java:3356)
at com.codename1.ui.Component.pointerReleased(Component.java:4552)
at com.codename1.ui.Display.handleEvent(Display.java:2080)
at com.codename1.ui.Display.edtLoopImpl(Display.java:1052)
at com.codename1.ui.Display.mainEDTLoop(Display.java:970)
at com.codename1.ui.RunnableWrapper.run(RunnableWrapper.java:120)
at com.codename1.impl.CodenameOneThread.run(CodenameOneThread.java:176)
The null pointer on line NewItem.java:425 points at the line where there is .getAsString().
What could i be doing wrong?
EDIT
I have updated my project libraries which solved the null pointer exception on .getAsString() but it still leaves another error which is:
java.io.IOException: Stream closed
[Network Thread] 0:0:54,316 - Exception: java.io.IOException - Stream closed
[Network Thread] 0:0:54,344 - Exception during JSON parsing at row: 1 column: 1 buffer:
at com.codename1.io.BufferedInputStream.getInIfOpen(BufferedInputStream.java:140)
at com.codename1.io.BufferedInputStream.read1(BufferedInputStream.java:338)
at com.codename1.io.BufferedInputStream.read(BufferedInputStream.java:445)
at java.base/sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at java.base/sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at java.base/sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
at java.base/java.io.InputStreamReader.read(InputStreamReader.java:185)
at com.codename1.io.JSONParser$ReaderClass.read(JSONParser.java:191)
at com.codename1.io.JSONParser.parse(JSONParser.java:278)
at com.codename1.io.JSONParser.parseJSON(JSONParser.java:568)
at com.codename1.io.rest.RequestBuilder$Connection.readUnzipedResponse(RequestBuilder.java:785)
at com.codename1.io.gzip.GZConnectionRequest.readResponse(GZConnectionRequest.java:67)
at com.codename1.io.ConnectionRequest.performOperationComplete(ConnectionRequest.java:1002)
at com.codename1.io.NetworkManager$NetworkThread.run(NetworkManager.java:340)
at com.codename1.impl.CodenameOneThread.run(CodenameOneThread.java:176)
Apparently when you use .pathparam(), you also have to add the parameter in the url. Hence I adjusted my code to:
Response<String> res = Rest.put(URLLinks.getMainBackend() + "items"+item.getId())
.body(reqJson.toString())
.jsonContent()
.bearer(initForm.data.getString("jwt"))
.header("token", initForm.data.getString("token"))
.pathParam("id", item.getId())
.onErrorCodeJSON((errorData) -> {
if (errorData.getResponseCode() == 404) {
Dialog dlg = rich.Dialog("Not found!");
Button yes = new Button("Close");
yes.addActionListener(ev -> {
dlg.dispose();
});
dlg.add(new SpanLabel("//////!"));
dlg.add(FlowLayout.encloseRight(yes));
dlg.showPacked(BorderLayout.CENTER, true);
}else if (errorData.getResponseCode() == 402){
}
})
.getAsString();
#!/usr/bin/env node
var WebSocketServer = require('websocket').server;
var http = require('http');
var server = http.createServer(function(request, response) {
console.log((new Date()) + ' Received request for ' + request.url);
response.writeHead(404);
response.end();
});
server.listen(8080, function() {
console.log((new Date()) + ' Server is listening on port 8080');
});
wsServer = new WebSocketServer({
httpServer: server,
// You should not use autoAcceptConnections for production
// applications, as it defeats all standard cross-origin protection
// facilities built into the protocol and the browser. You should
// *always* verify the connection's origin and decide whether or not
// to accept it.
autoAcceptConnections: false
});
function originIsAllowed(origin) {
// put logic here to detect whether the specified origin is allowed.
return true;
}
var jConnections = new Array();
wsServer.on('request', function(request) {
if (!originIsAllowed(request.origin)) {
// Make sure we only accept requests from an allowed origin
request.reject();
console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
return;
}
var connection = request.accept('echo-protocol', request.origin);
var jConnectionIdentity = new Date();
jConnections.push(new Array(connection,jConnectionIdentity) );
//console.log("datestamp"+jConnections[0][1]);
console.log((new Date()) + ' Connection accepted.');
connection.on('message', function(message) {
if (message.type === 'utf8') {
console.log('Received Message: ' + message.utf8Data);
//connection.sendUTF(message.utf8Data);
for(var i=0;i<jConnections.length;i++)
{
var jConnection = jConnections[i][0];
jConnection.sendUTF(message.utf8Data);
}
}
else if (message.type === 'binary') {
console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
//connection.sendBytes(message.binaryData);
for(var i=0;i<jConnections.length;i++)
{
var jConnection = jConnections[i][0];
jConnection.sendBytes(message.binaryData);
}
}
});
connection.on('close', function(reasonCode, description) {
for(var i=0;i<jConnections.length;i++)
{
if(jConnections[i][1] == jConnectionIdentity)
{
delete jConnections[i];
console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
console.log(jConnections.length);
}
}
});
});
If I open 3 client connections and send a message from each client and then close the third client I am seeing that the output of "console.log(jConnections.length);" is still three. Upon closing the third client and sending a message from client two a crash of NodeJS occurs. My console output looks like this:
C:\Program Files\nodejs>node.exe workingexample3.js
Mon May 20 2013 16:30:14 GMT-0700 (Pacific Daylight Time) Server is listening on port 8080
Mon May 20 2013 16:30:18 GMT-0700 (Pacific Daylight Time) Connection accepted.
Mon May 20 2013 16:30:20 GMT-0700 (Pacific Daylight Time) Connection accepted.
Mon May 20 2013 16:30:22 GMT-0700 (Pacific Daylight Time) Connection accepted.
Received Message: client1
Received Message: client2
Received Message: client3
Mon May 20 2013 16:30:43 GMT-0700 (Pacific Daylight Time) Peer 127.0.0.1 disconnected.
3
Received Message: client2
C:\Program Files\nodejs\workingexample3.js:48
var jConnection = jConnections[i][0];
^
TypeError: Cannot read property '0' of undefined
at WebSocketConnection.<anonymous> (C:\Program Files\nodejs\workingexample3.js:48:38)
at WebSocketConnection.EventEmitter.emit (events.js:95:17)
at WebSocketConnection.processFrame (C:\Program Files\nodejs\node_modules\websocket\lib\WebSocketConnection.js:403:26)
at WebSocketConnection.handleSocketData (C:\Program Files\nodejs\node_modules\websocket\lib\WebSocketConnection.js:247:14)
at Socket.EventEmitter.emit (events.js:95:17)
at Socket.<anonymous> (_stream_readable.js:736:14)
at Socket.EventEmitter.emit (events.js:92:17)
at emitReadable_ (_stream_readable.js:408:10)
at emitReadable (_stream_readable.js:404:5)
at readableAddChunk (_stream_readable.js:165:9)
C:\Program Files\nodejs>
I was hoping that upon the delete statement the value of jConnections.length would be decreasing by one but that does not seem to be happening. Since all three of my clients are connecting from localhost 127.0.0.1 I had to come up with a plan to identify each connection by something other than the remoteAdress property. That's why I am pushing a Date in addition to the connection object. I am hoping to id each connection by the Date it was created on.
1) After you call delete your array looks like this -
[[1, 1], [2, 2], undefined]
The length is still 3 :)