CC-Tweaked/projects/core/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketHandle.java

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

136 lines
5.2 KiB
Java
Raw Normal View History

// SPDX-FileCopyrightText: 2019 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.core.apis.http.websocket;
Replace getMethodNames/callMethod with annotations (#447) When creating a peripheral or custom Lua object, one must implement two methods: - getMethodNames(): String[] - Returns the name of the methods - callMethod(int, ...): Object[] - Invokes the method using an index in the above array. This has a couple of problems: - It's somewhat unwieldy to use - you need to keep track of array indices, which leads to ugly code. - Functions which yield (for instance, those which run on the main thread) are blocking. This means we need to spawn new threads for each CC-side yield. We replace this system with a few changes: - @LuaFunction annotation: One may annotate a public instance method with this annotation. This then exposes a peripheral/lua object method. Furthermore, this method can accept and return a variety of types, which often makes functions cleaner (e.g. can return an int rather than an Object[], and specify and int argument rather than Object[]). - MethodResult: Instead of returning an Object[] and having blocking yields, functions return a MethodResult. This either contains an immediate return, or an instruction to yield with some continuation to resume with. MethodResult is then interpreted by the Lua runtime (i.e. Cobalt), rather than our weird bodgey hacks before. This means we no longer spawn new threads when yielding within CC. - Methods accept IArguments instead of a raw Object array. This has a few benefits: - Consistent argument handling - people no longer need to use ArgumentHelper (as it doesn't exist!), or even be aware of its existence - you're rather forced into using it. - More efficient code in some cases. We provide a Cobalt-specific implementation of IArguments, which avoids the boxing/unboxing when handling numbers and binary strings.
2020-05-15 12:21:16 +00:00
import dan200.computercraft.api.lua.*;
import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.apis.http.options.Options;
Always use raw bytes in file handles Historically CC has supported two modes when working with file handles (and HTTP requests): - Text mode, which reads/write using UTF-8. - Binary mode, which reads/writes the raw bytes. However, this can be confusing at times. CC/Lua doesn't actually support unicode, so any characters beyond the 0.255 range were replaced with '?'. This meant that most of the time you were better off just using binary mode. This commit unifies text and binary mode - we now /always/ read the raw bytes of the file, rather than converting to/from UTF-8. Binary mode now only specifies whether handle.read() returns a number (and .write(123) writes a byte rather than coercing to a string). - Refactor the entire handle hierarchy. We now have an AbstractMount base class, which has the concrete implementation of all methods. The public-facing classes then re-export these methods by annotating them with @LuaFunction. These implementations are based on the Binary{Readable,Writable}Handle classes. The Encoded{..}Handle versions are now entirely removed. - As we no longer need to use BufferedReader/BufferedWriter, we can remove quite a lot of logic in Filesystem to handle wrapping closeable objects. - Add a new WritableMount.openFile method, which generalises openForWrite/openForAppend to accept OpenOptions. This allows us to support update mode (r+, w+) in fs.open. - fs.open now uses the new handle types, and supports update (r+, w+) mode. - http.request now uses the new readable handle type. We no longer encode the request body to UTF-8, nor decode the response from UTF-8. - Websockets now return text frame's contents directly, rather than converting it from UTF-8. Sending text frames now attempts to treat the passed string as UTF-8, rather than treating it as latin1.
2023-11-08 19:37:10 +00:00
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Objects;
Replace getMethodNames/callMethod with annotations (#447) When creating a peripheral or custom Lua object, one must implement two methods: - getMethodNames(): String[] - Returns the name of the methods - callMethod(int, ...): Object[] - Invokes the method using an index in the above array. This has a couple of problems: - It's somewhat unwieldy to use - you need to keep track of array indices, which leads to ugly code. - Functions which yield (for instance, those which run on the main thread) are blocking. This means we need to spawn new threads for each CC-side yield. We replace this system with a few changes: - @LuaFunction annotation: One may annotate a public instance method with this annotation. This then exposes a peripheral/lua object method. Furthermore, this method can accept and return a variety of types, which often makes functions cleaner (e.g. can return an int rather than an Object[], and specify and int argument rather than Object[]). - MethodResult: Instead of returning an Object[] and having blocking yields, functions return a MethodResult. This either contains an immediate return, or an instruction to yield with some continuation to resume with. MethodResult is then interpreted by the Lua runtime (i.e. Cobalt), rather than our weird bodgey hacks before. This means we no longer spawn new threads when yielding within CC. - Methods accept IArguments instead of a raw Object array. This has a few benefits: - Consistent argument handling - people no longer need to use ArgumentHelper (as it doesn't exist!), or even be aware of its existence - you're rather forced into using it. - More efficient code in some cases. We provide a Cobalt-specific implementation of IArguments, which avoids the boxing/unboxing when handling numbers and binary strings.
2020-05-15 12:21:16 +00:00
import java.util.Optional;
Replace getMethodNames/callMethod with annotations (#447) When creating a peripheral or custom Lua object, one must implement two methods: - getMethodNames(): String[] - Returns the name of the methods - callMethod(int, ...): Object[] - Invokes the method using an index in the above array. This has a couple of problems: - It's somewhat unwieldy to use - you need to keep track of array indices, which leads to ugly code. - Functions which yield (for instance, those which run on the main thread) are blocking. This means we need to spawn new threads for each CC-side yield. We replace this system with a few changes: - @LuaFunction annotation: One may annotate a public instance method with this annotation. This then exposes a peripheral/lua object method. Furthermore, this method can accept and return a variety of types, which often makes functions cleaner (e.g. can return an int rather than an Object[], and specify and int argument rather than Object[]). - MethodResult: Instead of returning an Object[] and having blocking yields, functions return a MethodResult. This either contains an immediate return, or an instruction to yield with some continuation to resume with. MethodResult is then interpreted by the Lua runtime (i.e. Cobalt), rather than our weird bodgey hacks before. This means we no longer spawn new threads when yielding within CC. - Methods accept IArguments instead of a raw Object array. This has a few benefits: - Consistent argument handling - people no longer need to use ArgumentHelper (as it doesn't exist!), or even be aware of its existence - you're rather forced into using it. - More efficient code in some cases. We provide a Cobalt-specific implementation of IArguments, which avoids the boxing/unboxing when handling numbers and binary strings.
2020-05-15 12:21:16 +00:00
import static dan200.computercraft.api.lua.LuaValues.checkFinite;
import static dan200.computercraft.core.apis.IAPIEnvironment.TIMER_EVENT;
import static dan200.computercraft.core.apis.http.websocket.WebsocketClient.CLOSE_EVENT;
import static dan200.computercraft.core.apis.http.websocket.WebsocketClient.MESSAGE_EVENT;
Generate documentation stubs from Javadocs illuaminate does not handle Java files, for obvious reasons. In order to get around that, we have a series of stub files within /doc/stub which mirrored the Java ones. While this works, it has a few problems: - The link to source code does not work - it just links to the stub file. - There's no guarantee that documentation remains consistent with the Java code. This change found several methods which were incorrectly documented beforehand. We now replace this with a custom Java doclet[1], which extracts doc comments from @LuaFunction annotated methods and generates stub-files from them. These also contain a @source annotation, which allows us to correctly link them back to the original Java code. There's some issues with this which have yet to be fixed. However, I don't think any of them are major blockers right now: - The custom doclet relies on Java 9 - I think it's /technically/ possible to do this on Java 8, but the API is significantly uglier. This means that we need to run javadoc on a separate JVM. This is possible, and it works locally and on CI, but is definitely not a nice approach. - illuaminate now requires the doc stubs to be generated in order for the linter to pass, which does make running the linter locally much harder (especially given the above bullet point). We could notionally include the generated stubs (or at least a cut down version of them) in the repo, but I'm not 100% sure about that. [1]: https://docs.oracle.com/javase/9/docs/api/jdk/javadoc/doclet/package-summary.html
2020-07-03 12:31:26 +00:00
/**
* A websocket, which can be used to send and receive messages with a web server.
Generate documentation stubs from Javadocs illuaminate does not handle Java files, for obvious reasons. In order to get around that, we have a series of stub files within /doc/stub which mirrored the Java ones. While this works, it has a few problems: - The link to source code does not work - it just links to the stub file. - There's no guarantee that documentation remains consistent with the Java code. This change found several methods which were incorrectly documented beforehand. We now replace this with a custom Java doclet[1], which extracts doc comments from @LuaFunction annotated methods and generates stub-files from them. These also contain a @source annotation, which allows us to correctly link them back to the original Java code. There's some issues with this which have yet to be fixed. However, I don't think any of them are major blockers right now: - The custom doclet relies on Java 9 - I think it's /technically/ possible to do this on Java 8, but the API is significantly uglier. This means that we need to run javadoc on a separate JVM. This is possible, and it works locally and on CI, but is definitely not a nice approach. - illuaminate now requires the doc stubs to be generated in order for the linter to pass, which does make running the linter locally much harder (especially given the above bullet point). We could notionally include the generated stubs (or at least a cut down version of them) in the repo, but I'm not 100% sure about that. [1]: https://docs.oracle.com/javase/9/docs/api/jdk/javadoc/doclet/package-summary.html
2020-07-03 12:31:26 +00:00
*
* @cc.module http.Websocket
* @see dan200.computercraft.core.apis.HTTPAPI#websocket On how to open a websocket.
*/
public class WebsocketHandle {
private static final ThreadLocal<CharsetDecoder> DECODER = ThreadLocal.withInitial(() -> StandardCharsets.UTF_8.newDecoder().onMalformedInput(CodingErrorAction.REPLACE));
Always use raw bytes in file handles Historically CC has supported two modes when working with file handles (and HTTP requests): - Text mode, which reads/write using UTF-8. - Binary mode, which reads/writes the raw bytes. However, this can be confusing at times. CC/Lua doesn't actually support unicode, so any characters beyond the 0.255 range were replaced with '?'. This meant that most of the time you were better off just using binary mode. This commit unifies text and binary mode - we now /always/ read the raw bytes of the file, rather than converting to/from UTF-8. Binary mode now only specifies whether handle.read() returns a number (and .write(123) writes a byte rather than coercing to a string). - Refactor the entire handle hierarchy. We now have an AbstractMount base class, which has the concrete implementation of all methods. The public-facing classes then re-export these methods by annotating them with @LuaFunction. These implementations are based on the Binary{Readable,Writable}Handle classes. The Encoded{..}Handle versions are now entirely removed. - As we no longer need to use BufferedReader/BufferedWriter, we can remove quite a lot of logic in Filesystem to handle wrapping closeable objects. - Add a new WritableMount.openFile method, which generalises openForWrite/openForAppend to accept OpenOptions. This allows us to support update mode (r+, w+) in fs.open. - fs.open now uses the new handle types, and supports update (r+, w+) mode. - http.request now uses the new readable handle type. We no longer encode the request body to UTF-8, nor decode the response from UTF-8. - Websockets now return text frame's contents directly, rather than converting it from UTF-8. Sending text frames now attempts to treat the passed string as UTF-8, rather than treating it as latin1.
2023-11-08 19:37:10 +00:00
private final IAPIEnvironment environment;
private final String address;
private final WebsocketClient websocket;
private final Options options;
public WebsocketHandle(IAPIEnvironment environment, String address, WebsocketClient websocket, Options options) {
this.environment = environment;
this.address = address;
this.websocket = websocket;
this.options = options;
}
Generate documentation stubs from Javadocs illuaminate does not handle Java files, for obvious reasons. In order to get around that, we have a series of stub files within /doc/stub which mirrored the Java ones. While this works, it has a few problems: - The link to source code does not work - it just links to the stub file. - There's no guarantee that documentation remains consistent with the Java code. This change found several methods which were incorrectly documented beforehand. We now replace this with a custom Java doclet[1], which extracts doc comments from @LuaFunction annotated methods and generates stub-files from them. These also contain a @source annotation, which allows us to correctly link them back to the original Java code. There's some issues with this which have yet to be fixed. However, I don't think any of them are major blockers right now: - The custom doclet relies on Java 9 - I think it's /technically/ possible to do this on Java 8, but the API is significantly uglier. This means that we need to run javadoc on a separate JVM. This is possible, and it works locally and on CI, but is definitely not a nice approach. - illuaminate now requires the doc stubs to be generated in order for the linter to pass, which does make running the linter locally much harder (especially given the above bullet point). We could notionally include the generated stubs (or at least a cut down version of them) in the repo, but I'm not 100% sure about that. [1]: https://docs.oracle.com/javase/9/docs/api/jdk/javadoc/doclet/package-summary.html
2020-07-03 12:31:26 +00:00
/**
* Wait for a message from the server.
*
* @param timeout The number of seconds to wait if no message is received.
* @return The result of receiving.
* @throws LuaException If the websocket has been closed.
* @cc.treturn [1] string The received message.
* @cc.treturn boolean If this was a binary message.
* @cc.treturn [2] nil If the websocket was closed while waiting, or if we timed out.
* @cc.changed 1.80pr1.13 Added return value indicating whether the message was binary.
* @cc.changed 1.87.0 Added timeout argument.
Generate documentation stubs from Javadocs illuaminate does not handle Java files, for obvious reasons. In order to get around that, we have a series of stub files within /doc/stub which mirrored the Java ones. While this works, it has a few problems: - The link to source code does not work - it just links to the stub file. - There's no guarantee that documentation remains consistent with the Java code. This change found several methods which were incorrectly documented beforehand. We now replace this with a custom Java doclet[1], which extracts doc comments from @LuaFunction annotated methods and generates stub-files from them. These also contain a @source annotation, which allows us to correctly link them back to the original Java code. There's some issues with this which have yet to be fixed. However, I don't think any of them are major blockers right now: - The custom doclet relies on Java 9 - I think it's /technically/ possible to do this on Java 8, but the API is significantly uglier. This means that we need to run javadoc on a separate JVM. This is possible, and it works locally and on CI, but is definitely not a nice approach. - illuaminate now requires the doc stubs to be generated in order for the linter to pass, which does make running the linter locally much harder (especially given the above bullet point). We could notionally include the generated stubs (or at least a cut down version of them) in the repo, but I'm not 100% sure about that. [1]: https://docs.oracle.com/javase/9/docs/api/jdk/javadoc/doclet/package-summary.html
2020-07-03 12:31:26 +00:00
*/
Replace getMethodNames/callMethod with annotations (#447) When creating a peripheral or custom Lua object, one must implement two methods: - getMethodNames(): String[] - Returns the name of the methods - callMethod(int, ...): Object[] - Invokes the method using an index in the above array. This has a couple of problems: - It's somewhat unwieldy to use - you need to keep track of array indices, which leads to ugly code. - Functions which yield (for instance, those which run on the main thread) are blocking. This means we need to spawn new threads for each CC-side yield. We replace this system with a few changes: - @LuaFunction annotation: One may annotate a public instance method with this annotation. This then exposes a peripheral/lua object method. Furthermore, this method can accept and return a variety of types, which often makes functions cleaner (e.g. can return an int rather than an Object[], and specify and int argument rather than Object[]). - MethodResult: Instead of returning an Object[] and having blocking yields, functions return a MethodResult. This either contains an immediate return, or an instruction to yield with some continuation to resume with. MethodResult is then interpreted by the Lua runtime (i.e. Cobalt), rather than our weird bodgey hacks before. This means we no longer spawn new threads when yielding within CC. - Methods accept IArguments instead of a raw Object array. This has a few benefits: - Consistent argument handling - people no longer need to use ArgumentHelper (as it doesn't exist!), or even be aware of its existence - you're rather forced into using it. - More efficient code in some cases. We provide a Cobalt-specific implementation of IArguments, which avoids the boxing/unboxing when handling numbers and binary strings.
2020-05-15 12:21:16 +00:00
@LuaFunction
Generate documentation stubs from Javadocs illuaminate does not handle Java files, for obvious reasons. In order to get around that, we have a series of stub files within /doc/stub which mirrored the Java ones. While this works, it has a few problems: - The link to source code does not work - it just links to the stub file. - There's no guarantee that documentation remains consistent with the Java code. This change found several methods which were incorrectly documented beforehand. We now replace this with a custom Java doclet[1], which extracts doc comments from @LuaFunction annotated methods and generates stub-files from them. These also contain a @source annotation, which allows us to correctly link them back to the original Java code. There's some issues with this which have yet to be fixed. However, I don't think any of them are major blockers right now: - The custom doclet relies on Java 9 - I think it's /technically/ possible to do this on Java 8, but the API is significantly uglier. This means that we need to run javadoc on a separate JVM. This is possible, and it works locally and on CI, but is definitely not a nice approach. - illuaminate now requires the doc stubs to be generated in order for the linter to pass, which does make running the linter locally much harder (especially given the above bullet point). We could notionally include the generated stubs (or at least a cut down version of them) in the repo, but I'm not 100% sure about that. [1]: https://docs.oracle.com/javase/9/docs/api/jdk/javadoc/doclet/package-summary.html
2020-07-03 12:31:26 +00:00
public final MethodResult receive(Optional<Double> timeout) throws LuaException {
Replace getMethodNames/callMethod with annotations (#447) When creating a peripheral or custom Lua object, one must implement two methods: - getMethodNames(): String[] - Returns the name of the methods - callMethod(int, ...): Object[] - Invokes the method using an index in the above array. This has a couple of problems: - It's somewhat unwieldy to use - you need to keep track of array indices, which leads to ugly code. - Functions which yield (for instance, those which run on the main thread) are blocking. This means we need to spawn new threads for each CC-side yield. We replace this system with a few changes: - @LuaFunction annotation: One may annotate a public instance method with this annotation. This then exposes a peripheral/lua object method. Furthermore, this method can accept and return a variety of types, which often makes functions cleaner (e.g. can return an int rather than an Object[], and specify and int argument rather than Object[]). - MethodResult: Instead of returning an Object[] and having blocking yields, functions return a MethodResult. This either contains an immediate return, or an instruction to yield with some continuation to resume with. MethodResult is then interpreted by the Lua runtime (i.e. Cobalt), rather than our weird bodgey hacks before. This means we no longer spawn new threads when yielding within CC. - Methods accept IArguments instead of a raw Object array. This has a few benefits: - Consistent argument handling - people no longer need to use ArgumentHelper (as it doesn't exist!), or even be aware of its existence - you're rather forced into using it. - More efficient code in some cases. We provide a Cobalt-specific implementation of IArguments, which avoids the boxing/unboxing when handling numbers and binary strings.
2020-05-15 12:21:16 +00:00
checkOpen();
var timeoutId = timeout.isPresent()
? environment.startTimer(Math.round(checkFinite(0, timeout.get()) / 0.05))
Replace getMethodNames/callMethod with annotations (#447) When creating a peripheral or custom Lua object, one must implement two methods: - getMethodNames(): String[] - Returns the name of the methods - callMethod(int, ...): Object[] - Invokes the method using an index in the above array. This has a couple of problems: - It's somewhat unwieldy to use - you need to keep track of array indices, which leads to ugly code. - Functions which yield (for instance, those which run on the main thread) are blocking. This means we need to spawn new threads for each CC-side yield. We replace this system with a few changes: - @LuaFunction annotation: One may annotate a public instance method with this annotation. This then exposes a peripheral/lua object method. Furthermore, this method can accept and return a variety of types, which often makes functions cleaner (e.g. can return an int rather than an Object[], and specify and int argument rather than Object[]). - MethodResult: Instead of returning an Object[] and having blocking yields, functions return a MethodResult. This either contains an immediate return, or an instruction to yield with some continuation to resume with. MethodResult is then interpreted by the Lua runtime (i.e. Cobalt), rather than our weird bodgey hacks before. This means we no longer spawn new threads when yielding within CC. - Methods accept IArguments instead of a raw Object array. This has a few benefits: - Consistent argument handling - people no longer need to use ArgumentHelper (as it doesn't exist!), or even be aware of its existence - you're rather forced into using it. - More efficient code in some cases. We provide a Cobalt-specific implementation of IArguments, which avoids the boxing/unboxing when handling numbers and binary strings.
2020-05-15 12:21:16 +00:00
: -1;
return new ReceiveCallback(timeoutId).pull;
}
Generate documentation stubs from Javadocs illuaminate does not handle Java files, for obvious reasons. In order to get around that, we have a series of stub files within /doc/stub which mirrored the Java ones. While this works, it has a few problems: - The link to source code does not work - it just links to the stub file. - There's no guarantee that documentation remains consistent with the Java code. This change found several methods which were incorrectly documented beforehand. We now replace this with a custom Java doclet[1], which extracts doc comments from @LuaFunction annotated methods and generates stub-files from them. These also contain a @source annotation, which allows us to correctly link them back to the original Java code. There's some issues with this which have yet to be fixed. However, I don't think any of them are major blockers right now: - The custom doclet relies on Java 9 - I think it's /technically/ possible to do this on Java 8, but the API is significantly uglier. This means that we need to run javadoc on a separate JVM. This is possible, and it works locally and on CI, but is definitely not a nice approach. - illuaminate now requires the doc stubs to be generated in order for the linter to pass, which does make running the linter locally much harder (especially given the above bullet point). We could notionally include the generated stubs (or at least a cut down version of them) in the repo, but I'm not 100% sure about that. [1]: https://docs.oracle.com/javase/9/docs/api/jdk/javadoc/doclet/package-summary.html
2020-07-03 12:31:26 +00:00
/**
* Send a websocket message to the connected server.
*
* @param message The message to send.
* @param binary Whether this message should be treated as a
* @throws LuaException If the message is too large.
* @throws LuaException If the websocket has been closed.
* @cc.changed 1.81.0 Added argument for binary mode.
Generate documentation stubs from Javadocs illuaminate does not handle Java files, for obvious reasons. In order to get around that, we have a series of stub files within /doc/stub which mirrored the Java ones. While this works, it has a few problems: - The link to source code does not work - it just links to the stub file. - There's no guarantee that documentation remains consistent with the Java code. This change found several methods which were incorrectly documented beforehand. We now replace this with a custom Java doclet[1], which extracts doc comments from @LuaFunction annotated methods and generates stub-files from them. These also contain a @source annotation, which allows us to correctly link them back to the original Java code. There's some issues with this which have yet to be fixed. However, I don't think any of them are major blockers right now: - The custom doclet relies on Java 9 - I think it's /technically/ possible to do this on Java 8, but the API is significantly uglier. This means that we need to run javadoc on a separate JVM. This is possible, and it works locally and on CI, but is definitely not a nice approach. - illuaminate now requires the doc stubs to be generated in order for the linter to pass, which does make running the linter locally much harder (especially given the above bullet point). We could notionally include the generated stubs (or at least a cut down version of them) in the repo, but I'm not 100% sure about that. [1]: https://docs.oracle.com/javase/9/docs/api/jdk/javadoc/doclet/package-summary.html
2020-07-03 12:31:26 +00:00
*/
Replace getMethodNames/callMethod with annotations (#447) When creating a peripheral or custom Lua object, one must implement two methods: - getMethodNames(): String[] - Returns the name of the methods - callMethod(int, ...): Object[] - Invokes the method using an index in the above array. This has a couple of problems: - It's somewhat unwieldy to use - you need to keep track of array indices, which leads to ugly code. - Functions which yield (for instance, those which run on the main thread) are blocking. This means we need to spawn new threads for each CC-side yield. We replace this system with a few changes: - @LuaFunction annotation: One may annotate a public instance method with this annotation. This then exposes a peripheral/lua object method. Furthermore, this method can accept and return a variety of types, which often makes functions cleaner (e.g. can return an int rather than an Object[], and specify and int argument rather than Object[]). - MethodResult: Instead of returning an Object[] and having blocking yields, functions return a MethodResult. This either contains an immediate return, or an instruction to yield with some continuation to resume with. MethodResult is then interpreted by the Lua runtime (i.e. Cobalt), rather than our weird bodgey hacks before. This means we no longer spawn new threads when yielding within CC. - Methods accept IArguments instead of a raw Object array. This has a few benefits: - Consistent argument handling - people no longer need to use ArgumentHelper (as it doesn't exist!), or even be aware of its existence - you're rather forced into using it. - More efficient code in some cases. We provide a Cobalt-specific implementation of IArguments, which avoids the boxing/unboxing when handling numbers and binary strings.
2020-05-15 12:21:16 +00:00
@LuaFunction
Always use raw bytes in file handles Historically CC has supported two modes when working with file handles (and HTTP requests): - Text mode, which reads/write using UTF-8. - Binary mode, which reads/writes the raw bytes. However, this can be confusing at times. CC/Lua doesn't actually support unicode, so any characters beyond the 0.255 range were replaced with '?'. This meant that most of the time you were better off just using binary mode. This commit unifies text and binary mode - we now /always/ read the raw bytes of the file, rather than converting to/from UTF-8. Binary mode now only specifies whether handle.read() returns a number (and .write(123) writes a byte rather than coercing to a string). - Refactor the entire handle hierarchy. We now have an AbstractMount base class, which has the concrete implementation of all methods. The public-facing classes then re-export these methods by annotating them with @LuaFunction. These implementations are based on the Binary{Readable,Writable}Handle classes. The Encoded{..}Handle versions are now entirely removed. - As we no longer need to use BufferedReader/BufferedWriter, we can remove quite a lot of logic in Filesystem to handle wrapping closeable objects. - Add a new WritableMount.openFile method, which generalises openForWrite/openForAppend to accept OpenOptions. This allows us to support update mode (r+, w+) in fs.open. - fs.open now uses the new handle types, and supports update (r+, w+) mode. - http.request now uses the new readable handle type. We no longer encode the request body to UTF-8, nor decode the response from UTF-8. - Websockets now return text frame's contents directly, rather than converting it from UTF-8. Sending text frames now attempts to treat the passed string as UTF-8, rather than treating it as latin1.
2023-11-08 19:37:10 +00:00
public final void send(Coerced<ByteBuffer> message, Optional<Boolean> binary) throws LuaException {
Replace getMethodNames/callMethod with annotations (#447) When creating a peripheral or custom Lua object, one must implement two methods: - getMethodNames(): String[] - Returns the name of the methods - callMethod(int, ...): Object[] - Invokes the method using an index in the above array. This has a couple of problems: - It's somewhat unwieldy to use - you need to keep track of array indices, which leads to ugly code. - Functions which yield (for instance, those which run on the main thread) are blocking. This means we need to spawn new threads for each CC-side yield. We replace this system with a few changes: - @LuaFunction annotation: One may annotate a public instance method with this annotation. This then exposes a peripheral/lua object method. Furthermore, this method can accept and return a variety of types, which often makes functions cleaner (e.g. can return an int rather than an Object[], and specify and int argument rather than Object[]). - MethodResult: Instead of returning an Object[] and having blocking yields, functions return a MethodResult. This either contains an immediate return, or an instruction to yield with some continuation to resume with. MethodResult is then interpreted by the Lua runtime (i.e. Cobalt), rather than our weird bodgey hacks before. This means we no longer spawn new threads when yielding within CC. - Methods accept IArguments instead of a raw Object array. This has a few benefits: - Consistent argument handling - people no longer need to use ArgumentHelper (as it doesn't exist!), or even be aware of its existence - you're rather forced into using it. - More efficient code in some cases. We provide a Cobalt-specific implementation of IArguments, which avoids the boxing/unboxing when handling numbers and binary strings.
2020-05-15 12:21:16 +00:00
checkOpen();
var text = message.value();
Always use raw bytes in file handles Historically CC has supported two modes when working with file handles (and HTTP requests): - Text mode, which reads/write using UTF-8. - Binary mode, which reads/writes the raw bytes. However, this can be confusing at times. CC/Lua doesn't actually support unicode, so any characters beyond the 0.255 range were replaced with '?'. This meant that most of the time you were better off just using binary mode. This commit unifies text and binary mode - we now /always/ read the raw bytes of the file, rather than converting to/from UTF-8. Binary mode now only specifies whether handle.read() returns a number (and .write(123) writes a byte rather than coercing to a string). - Refactor the entire handle hierarchy. We now have an AbstractMount base class, which has the concrete implementation of all methods. The public-facing classes then re-export these methods by annotating them with @LuaFunction. These implementations are based on the Binary{Readable,Writable}Handle classes. The Encoded{..}Handle versions are now entirely removed. - As we no longer need to use BufferedReader/BufferedWriter, we can remove quite a lot of logic in Filesystem to handle wrapping closeable objects. - Add a new WritableMount.openFile method, which generalises openForWrite/openForAppend to accept OpenOptions. This allows us to support update mode (r+, w+) in fs.open. - fs.open now uses the new handle types, and supports update (r+, w+) mode. - http.request now uses the new readable handle type. We no longer encode the request body to UTF-8, nor decode the response from UTF-8. - Websockets now return text frame's contents directly, rather than converting it from UTF-8. Sending text frames now attempts to treat the passed string as UTF-8, rather than treating it as latin1.
2023-11-08 19:37:10 +00:00
if (options.websocketMessage() != 0 && text.remaining() > options.websocketMessage()) {
Replace getMethodNames/callMethod with annotations (#447) When creating a peripheral or custom Lua object, one must implement two methods: - getMethodNames(): String[] - Returns the name of the methods - callMethod(int, ...): Object[] - Invokes the method using an index in the above array. This has a couple of problems: - It's somewhat unwieldy to use - you need to keep track of array indices, which leads to ugly code. - Functions which yield (for instance, those which run on the main thread) are blocking. This means we need to spawn new threads for each CC-side yield. We replace this system with a few changes: - @LuaFunction annotation: One may annotate a public instance method with this annotation. This then exposes a peripheral/lua object method. Furthermore, this method can accept and return a variety of types, which often makes functions cleaner (e.g. can return an int rather than an Object[], and specify and int argument rather than Object[]). - MethodResult: Instead of returning an Object[] and having blocking yields, functions return a MethodResult. This either contains an immediate return, or an instruction to yield with some continuation to resume with. MethodResult is then interpreted by the Lua runtime (i.e. Cobalt), rather than our weird bodgey hacks before. This means we no longer spawn new threads when yielding within CC. - Methods accept IArguments instead of a raw Object array. This has a few benefits: - Consistent argument handling - people no longer need to use ArgumentHelper (as it doesn't exist!), or even be aware of its existence - you're rather forced into using it. - More efficient code in some cases. We provide a Cobalt-specific implementation of IArguments, which avoids the boxing/unboxing when handling numbers and binary strings.
2020-05-15 12:21:16 +00:00
throw new LuaException("Message is too large");
}
if (binary.orElse(false)) {
Always use raw bytes in file handles Historically CC has supported two modes when working with file handles (and HTTP requests): - Text mode, which reads/write using UTF-8. - Binary mode, which reads/writes the raw bytes. However, this can be confusing at times. CC/Lua doesn't actually support unicode, so any characters beyond the 0.255 range were replaced with '?'. This meant that most of the time you were better off just using binary mode. This commit unifies text and binary mode - we now /always/ read the raw bytes of the file, rather than converting to/from UTF-8. Binary mode now only specifies whether handle.read() returns a number (and .write(123) writes a byte rather than coercing to a string). - Refactor the entire handle hierarchy. We now have an AbstractMount base class, which has the concrete implementation of all methods. The public-facing classes then re-export these methods by annotating them with @LuaFunction. These implementations are based on the Binary{Readable,Writable}Handle classes. The Encoded{..}Handle versions are now entirely removed. - As we no longer need to use BufferedReader/BufferedWriter, we can remove quite a lot of logic in Filesystem to handle wrapping closeable objects. - Add a new WritableMount.openFile method, which generalises openForWrite/openForAppend to accept OpenOptions. This allows us to support update mode (r+, w+) in fs.open. - fs.open now uses the new handle types, and supports update (r+, w+) mode. - http.request now uses the new readable handle type. We no longer encode the request body to UTF-8, nor decode the response from UTF-8. - Websockets now return text frame's contents directly, rather than converting it from UTF-8. Sending text frames now attempts to treat the passed string as UTF-8, rather than treating it as latin1.
2023-11-08 19:37:10 +00:00
websocket.sendBinary(text);
} else {
Always use raw bytes in file handles Historically CC has supported two modes when working with file handles (and HTTP requests): - Text mode, which reads/write using UTF-8. - Binary mode, which reads/writes the raw bytes. However, this can be confusing at times. CC/Lua doesn't actually support unicode, so any characters beyond the 0.255 range were replaced with '?'. This meant that most of the time you were better off just using binary mode. This commit unifies text and binary mode - we now /always/ read the raw bytes of the file, rather than converting to/from UTF-8. Binary mode now only specifies whether handle.read() returns a number (and .write(123) writes a byte rather than coercing to a string). - Refactor the entire handle hierarchy. We now have an AbstractMount base class, which has the concrete implementation of all methods. The public-facing classes then re-export these methods by annotating them with @LuaFunction. These implementations are based on the Binary{Readable,Writable}Handle classes. The Encoded{..}Handle versions are now entirely removed. - As we no longer need to use BufferedReader/BufferedWriter, we can remove quite a lot of logic in Filesystem to handle wrapping closeable objects. - Add a new WritableMount.openFile method, which generalises openForWrite/openForAppend to accept OpenOptions. This allows us to support update mode (r+, w+) in fs.open. - fs.open now uses the new handle types, and supports update (r+, w+) mode. - http.request now uses the new readable handle type. We no longer encode the request body to UTF-8, nor decode the response from UTF-8. - Websockets now return text frame's contents directly, rather than converting it from UTF-8. Sending text frames now attempts to treat the passed string as UTF-8, rather than treating it as latin1.
2023-11-08 19:37:10 +00:00
try {
websocket.sendText(DECODER.get().decode(text).toString());
Always use raw bytes in file handles Historically CC has supported two modes when working with file handles (and HTTP requests): - Text mode, which reads/write using UTF-8. - Binary mode, which reads/writes the raw bytes. However, this can be confusing at times. CC/Lua doesn't actually support unicode, so any characters beyond the 0.255 range were replaced with '?'. This meant that most of the time you were better off just using binary mode. This commit unifies text and binary mode - we now /always/ read the raw bytes of the file, rather than converting to/from UTF-8. Binary mode now only specifies whether handle.read() returns a number (and .write(123) writes a byte rather than coercing to a string). - Refactor the entire handle hierarchy. We now have an AbstractMount base class, which has the concrete implementation of all methods. The public-facing classes then re-export these methods by annotating them with @LuaFunction. These implementations are based on the Binary{Readable,Writable}Handle classes. The Encoded{..}Handle versions are now entirely removed. - As we no longer need to use BufferedReader/BufferedWriter, we can remove quite a lot of logic in Filesystem to handle wrapping closeable objects. - Add a new WritableMount.openFile method, which generalises openForWrite/openForAppend to accept OpenOptions. This allows us to support update mode (r+, w+) in fs.open. - fs.open now uses the new handle types, and supports update (r+, w+) mode. - http.request now uses the new readable handle type. We no longer encode the request body to UTF-8, nor decode the response from UTF-8. - Websockets now return text frame's contents directly, rather than converting it from UTF-8. Sending text frames now attempts to treat the passed string as UTF-8, rather than treating it as latin1.
2023-11-08 19:37:10 +00:00
} catch (CharacterCodingException e) {
// This shouldn't happen, but worth mentioning.
throw new LuaException("Message is not valid UTF8");
}
}
}
Generate documentation stubs from Javadocs illuaminate does not handle Java files, for obvious reasons. In order to get around that, we have a series of stub files within /doc/stub which mirrored the Java ones. While this works, it has a few problems: - The link to source code does not work - it just links to the stub file. - There's no guarantee that documentation remains consistent with the Java code. This change found several methods which were incorrectly documented beforehand. We now replace this with a custom Java doclet[1], which extracts doc comments from @LuaFunction annotated methods and generates stub-files from them. These also contain a @source annotation, which allows us to correctly link them back to the original Java code. There's some issues with this which have yet to be fixed. However, I don't think any of them are major blockers right now: - The custom doclet relies on Java 9 - I think it's /technically/ possible to do this on Java 8, but the API is significantly uglier. This means that we need to run javadoc on a separate JVM. This is possible, and it works locally and on CI, but is definitely not a nice approach. - illuaminate now requires the doc stubs to be generated in order for the linter to pass, which does make running the linter locally much harder (especially given the above bullet point). We could notionally include the generated stubs (or at least a cut down version of them) in the repo, but I'm not 100% sure about that. [1]: https://docs.oracle.com/javase/9/docs/api/jdk/javadoc/doclet/package-summary.html
2020-07-03 12:31:26 +00:00
/**
* Close this websocket. This will terminate the connection, meaning messages can no longer be sent or received
* along it.
*/
@LuaFunction
public final void close() {
Replace getMethodNames/callMethod with annotations (#447) When creating a peripheral or custom Lua object, one must implement two methods: - getMethodNames(): String[] - Returns the name of the methods - callMethod(int, ...): Object[] - Invokes the method using an index in the above array. This has a couple of problems: - It's somewhat unwieldy to use - you need to keep track of array indices, which leads to ugly code. - Functions which yield (for instance, those which run on the main thread) are blocking. This means we need to spawn new threads for each CC-side yield. We replace this system with a few changes: - @LuaFunction annotation: One may annotate a public instance method with this annotation. This then exposes a peripheral/lua object method. Furthermore, this method can accept and return a variety of types, which often makes functions cleaner (e.g. can return an int rather than an Object[], and specify and int argument rather than Object[]). - MethodResult: Instead of returning an Object[] and having blocking yields, functions return a MethodResult. This either contains an immediate return, or an instruction to yield with some continuation to resume with. MethodResult is then interpreted by the Lua runtime (i.e. Cobalt), rather than our weird bodgey hacks before. This means we no longer spawn new threads when yielding within CC. - Methods accept IArguments instead of a raw Object array. This has a few benefits: - Consistent argument handling - people no longer need to use ArgumentHelper (as it doesn't exist!), or even be aware of its existence - you're rather forced into using it. - More efficient code in some cases. We provide a Cobalt-specific implementation of IArguments, which avoids the boxing/unboxing when handling numbers and binary strings.
2020-05-15 12:21:16 +00:00
websocket.close();
}
private void checkOpen() throws LuaException {
if (websocket.isClosed()) throw new LuaException("attempt to use a closed file");
}
Replace getMethodNames/callMethod with annotations (#447) When creating a peripheral or custom Lua object, one must implement two methods: - getMethodNames(): String[] - Returns the name of the methods - callMethod(int, ...): Object[] - Invokes the method using an index in the above array. This has a couple of problems: - It's somewhat unwieldy to use - you need to keep track of array indices, which leads to ugly code. - Functions which yield (for instance, those which run on the main thread) are blocking. This means we need to spawn new threads for each CC-side yield. We replace this system with a few changes: - @LuaFunction annotation: One may annotate a public instance method with this annotation. This then exposes a peripheral/lua object method. Furthermore, this method can accept and return a variety of types, which often makes functions cleaner (e.g. can return an int rather than an Object[], and specify and int argument rather than Object[]). - MethodResult: Instead of returning an Object[] and having blocking yields, functions return a MethodResult. This either contains an immediate return, or an instruction to yield with some continuation to resume with. MethodResult is then interpreted by the Lua runtime (i.e. Cobalt), rather than our weird bodgey hacks before. This means we no longer spawn new threads when yielding within CC. - Methods accept IArguments instead of a raw Object array. This has a few benefits: - Consistent argument handling - people no longer need to use ArgumentHelper (as it doesn't exist!), or even be aware of its existence - you're rather forced into using it. - More efficient code in some cases. We provide a Cobalt-specific implementation of IArguments, which avoids the boxing/unboxing when handling numbers and binary strings.
2020-05-15 12:21:16 +00:00
private final class ReceiveCallback implements ILuaCallback {
final MethodResult pull = MethodResult.pullEvent(null, this);
private final int timeoutId;
ReceiveCallback(int timeoutId) {
this.timeoutId = timeoutId;
}
@Override
public MethodResult resume(Object[] event) {
if (event.length >= 3 && Objects.equals(event[0], MESSAGE_EVENT) && Objects.equals(event[1], address)) {
Replace getMethodNames/callMethod with annotations (#447) When creating a peripheral or custom Lua object, one must implement two methods: - getMethodNames(): String[] - Returns the name of the methods - callMethod(int, ...): Object[] - Invokes the method using an index in the above array. This has a couple of problems: - It's somewhat unwieldy to use - you need to keep track of array indices, which leads to ugly code. - Functions which yield (for instance, those which run on the main thread) are blocking. This means we need to spawn new threads for each CC-side yield. We replace this system with a few changes: - @LuaFunction annotation: One may annotate a public instance method with this annotation. This then exposes a peripheral/lua object method. Furthermore, this method can accept and return a variety of types, which often makes functions cleaner (e.g. can return an int rather than an Object[], and specify and int argument rather than Object[]). - MethodResult: Instead of returning an Object[] and having blocking yields, functions return a MethodResult. This either contains an immediate return, or an instruction to yield with some continuation to resume with. MethodResult is then interpreted by the Lua runtime (i.e. Cobalt), rather than our weird bodgey hacks before. This means we no longer spawn new threads when yielding within CC. - Methods accept IArguments instead of a raw Object array. This has a few benefits: - Consistent argument handling - people no longer need to use ArgumentHelper (as it doesn't exist!), or even be aware of its existence - you're rather forced into using it. - More efficient code in some cases. We provide a Cobalt-specific implementation of IArguments, which avoids the boxing/unboxing when handling numbers and binary strings.
2020-05-15 12:21:16 +00:00
return MethodResult.of(Arrays.copyOfRange(event, 2, event.length));
} else if (event.length >= 2 && Objects.equals(event[0], CLOSE_EVENT) && Objects.equals(event[1], address) && websocket.isClosed()) {
Replace getMethodNames/callMethod with annotations (#447) When creating a peripheral or custom Lua object, one must implement two methods: - getMethodNames(): String[] - Returns the name of the methods - callMethod(int, ...): Object[] - Invokes the method using an index in the above array. This has a couple of problems: - It's somewhat unwieldy to use - you need to keep track of array indices, which leads to ugly code. - Functions which yield (for instance, those which run on the main thread) are blocking. This means we need to spawn new threads for each CC-side yield. We replace this system with a few changes: - @LuaFunction annotation: One may annotate a public instance method with this annotation. This then exposes a peripheral/lua object method. Furthermore, this method can accept and return a variety of types, which often makes functions cleaner (e.g. can return an int rather than an Object[], and specify and int argument rather than Object[]). - MethodResult: Instead of returning an Object[] and having blocking yields, functions return a MethodResult. This either contains an immediate return, or an instruction to yield with some continuation to resume with. MethodResult is then interpreted by the Lua runtime (i.e. Cobalt), rather than our weird bodgey hacks before. This means we no longer spawn new threads when yielding within CC. - Methods accept IArguments instead of a raw Object array. This has a few benefits: - Consistent argument handling - people no longer need to use ArgumentHelper (as it doesn't exist!), or even be aware of its existence - you're rather forced into using it. - More efficient code in some cases. We provide a Cobalt-specific implementation of IArguments, which avoids the boxing/unboxing when handling numbers and binary strings.
2020-05-15 12:21:16 +00:00
// If the socket is closed abort.
return MethodResult.of();
} else if (event.length >= 2 && timeoutId != -1 && Objects.equals(event[0], TIMER_EVENT)
2021-11-28 15:58:30 +00:00
&& event[1] instanceof Number id && id.intValue() == timeoutId) {
Replace getMethodNames/callMethod with annotations (#447) When creating a peripheral or custom Lua object, one must implement two methods: - getMethodNames(): String[] - Returns the name of the methods - callMethod(int, ...): Object[] - Invokes the method using an index in the above array. This has a couple of problems: - It's somewhat unwieldy to use - you need to keep track of array indices, which leads to ugly code. - Functions which yield (for instance, those which run on the main thread) are blocking. This means we need to spawn new threads for each CC-side yield. We replace this system with a few changes: - @LuaFunction annotation: One may annotate a public instance method with this annotation. This then exposes a peripheral/lua object method. Furthermore, this method can accept and return a variety of types, which often makes functions cleaner (e.g. can return an int rather than an Object[], and specify and int argument rather than Object[]). - MethodResult: Instead of returning an Object[] and having blocking yields, functions return a MethodResult. This either contains an immediate return, or an instruction to yield with some continuation to resume with. MethodResult is then interpreted by the Lua runtime (i.e. Cobalt), rather than our weird bodgey hacks before. This means we no longer spawn new threads when yielding within CC. - Methods accept IArguments instead of a raw Object array. This has a few benefits: - Consistent argument handling - people no longer need to use ArgumentHelper (as it doesn't exist!), or even be aware of its existence - you're rather forced into using it. - More efficient code in some cases. We provide a Cobalt-specific implementation of IArguments, which avoids the boxing/unboxing when handling numbers and binary strings.
2020-05-15 12:21:16 +00:00
// If we received a matching timer event then abort.
return MethodResult.of();
}
return pull;
}
}
}