Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

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

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

Already on GitHub? Sign in to your account

v3 architecture API suggestions #2585

Closed
mholt opened this issue Apr 12, 2023 · 4 comments
Closed

v3 architecture API suggestions #2585

mholt opened this issue Apr 12, 2023 · 4 comments
Labels
Enhancement New feature or request

Comments

@mholt
Copy link
Contributor

mholt commented Apr 12, 2023

Is your feature request related to a problem? Please describe.

I know there has been some talk about a server/hybrid mode (#1652 for example), and brainstorms about using Caddy as the server to benefit from its plugin ecosystem. After reviewing my (excellent) experience with Wails, I think I have some concrete suggestions/requests for Wails v3 related to how we expose functions/methods to the outside.

Currently, Wails exposes pure-Go functionality to the frontend (JS) through method bindings which it generates at compile-time. (In v3 this still applies, although I believe it is done with static analysis rather than runtime hacks.)

This is great but has some limitations/difficulties:

  • Method bindings are only available to the JS frontend. It can't be easily scripted or automated locally or remotely through other tools like an HTTP API or CLI.
  • It lacks the ability to cancel long-running function calls. We have to implement our own cancellation by making all function calls return some sort of handle or ID and then use some other communication to convey the result, and implement another endpoint to cancel. Lots of messy state to deal with. Cancellation is important for SPAs that may be doing something, then if the user clicks away, the long-running request should cancel. (The browser would normally do this for a regular site / non-SPA, but since there's no page change it doesn't do it for you.)

On that first point: I have an increasing need to be able to use the app in ways other than through the native webview frontend:

  • Users want to script or automate parts of the program through either an HTTP API or a CLI -- with the same functionality that the webview has, so I don't want to duplicate code or set up a bunch of boilerplate if possible.
  • I'm finding that there are times when the native webview just doesn't work, unfortunately. It's not Wails problems per-se, just bugs in the webview. For example, on Mac, the webkit webview often crashes when displaying a video. (Haven't been able to do this reliably. But Safari crashes too.) On Linux, we get obscure GPU errors like src/nv_gbm.c:99: GBM-DRV error (nv_gbm_bo_create): DRM_IOCTL_NVIDIA_GEM_ALLOC_NVKMS_MEMORY failed (ret=-1) and the app is unusable / window is blank -- someone else actually reported this exact same error just hours after I first encountered it, after I reinstalled my OS and chose Arch for the first time; I suspect it has to do with the latest NVIDIA driver and its interop with either Wayland, the 6.2 kernel, or webkit2gtk, but either way, the app is unusable in its current state even though it's not Wails' fault.

Describe the solution you'd like

I'd like to propose -- and discuss -- the possibility of approaching method bindings in a new way for v3:

  • Methods that are bound should accept a context as the first parameter (maybe optionally, but methods without it can't be canceled).

  • Implement cancellation: expose context creation and cancellation to the frontend. I envision an endpoint on the Wails runtime that returns an object from which bound methods can be called, like const ctx = runtime.NewContext(); -- and then you can call ctx.Foo(...) to invoke bound methods. If ctx.cancel() is called, then the context passed to all the outstanding method calls on the Go side is canceled and the context state that is stored server-side can be cleaned up. Maybe a ctx.reset() method can be used to cancel the context and replace it with a new one to make SPA page transitions easy without leaking memory. (Sure, we could just have a singleton pattern for the context, but allowing multiple contexts can be useful when a page has multiple things going on.)

  • Continue to perform static analysis at compile-time to generate functions that can be called by JS frontends. 👍 However, these should be cancelable (see previous point). I'm not sure how the IPC works, but I imagine it involves a socket connection that can be closed, for example?

  • Generate thin HTTP routes over the bound methods. For example, a method defined as Foo(ctx context.Context, a string, b int) could have an HTTP route of POST /foo where the payload is ["a", 15] for example. In other words, the payload is a JSON array where each element is a method argument. (Maybe methods with a single argument can elide the [ ] for convenience.) These are naturally cancelable as net/http implements context cancellation. We'd probably want to implement some basic access controls like Host and Origin header enforcement (CORS, and to mitigate DNS rebinding attacks) to prevent rogue web pages running on the user's computer being able to access the server. This generation can be done automatically with reflection when the application initializes.

  • Generate a thin CLI for bound methods. I've done this before with a Go program that registered HTTP routes, it was really nifty: basically the CLI derives the name of the subcommand from the path of the registered HTTP route (for example, /api/new-account had a command line subcommand of new-account) and then the arguments formed the payload, for example a JSON struct would look like: myapp new-account --email [email protected] --password foobar -- then some clever Go code generates the equivalent JSON payload and HTTP request, and sends it off to the currently-running, potentially-headless process.

With 3 ways to access the API instead of just 1, it will be possible to:

  • Continue running Wails apps with their native webview, when it works
  • Automate/script Wails apps with HTTP clients and CLI tools
  • Remotely administer Wails apps (with proper access controls)
  • Fall back to running the Wails app in a browser if the native webview isn't working or isn't preferred
  • Quickly cancel long-running function calls to free up system resources on-demand

Describe alternatives you've considered

One last thought, regarding using Caddy as the HTTP server: I agree this is a cool idea, as it would allow the HTTP endpoint to be greatly extended so that Wails apps could become much more versatile in both production and end-user environments.

The way it usually works with Caddy is you'd embed your app into Caddy (not the other way around). This is because Caddy does all the configuration management for you, etc. So when you run the program, you actually run Caddy, which then runs apps. You can extend Caddy's CLI to add subcommands, and rename the main command from Caddy to something else if needed, but the basic core of Caddy would remain. I don't know if that's desirable, but that's usually how things work.

There is one possible alternative that I have not explored in depth yet, and that is using the caddyhttp package independently of using the entire Caddy core and CLI. You'd probably have to still use functions from the caddy/v2 package to load HTTP modules and such, but I don't know how much work would be needed to flip around the architecture so that you can truly embed Caddy into your app (instead embedding your app into Caddy). Anyone with a little time want to explore this?

Additional context

Thanks for Wails, it continues to be great. I hope some of these suggestions can make it in. I'm willing to help iron out technical details.

@mholt mholt added the Enhancement New feature or request label Apr 12, 2023
@tmclane
Copy link
Member

tmclane commented Apr 12, 2023

Should this be moved to the actual discussion area?

@mholt
Copy link
Contributor Author

mholt commented Apr 14, 2023

Either way. I can never tell when to use issues or discussions. Typically in my own projects I use issues for development items.

@leaanthony
Copy link
Member

GitHub for project management is.....fun....

@leaanthony
Copy link
Member

@mholt - Been reviewing this. Thanks for the awesome suggestions. I'd love to use Caddy as the http server but I guess changing the architecture of it is far too hardcore 😱 Some of what's been suggested here has already been done in slightly different forms so none of the above is unfeasible. The CLI generation might be a stretch but having a generic CLI that loads up a config to build up the CLI options would be realistic. Thanks again! 👍

@wailsapp wailsapp locked and limited conversation to collaborators Jul 9, 2023
@leaanthony leaanthony converted this issue into discussion #2765 Jul 9, 2023

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
Enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants