Support auto reconnection
A new option `--auto-reconnect` which takes seconds to reconnect is added.
This commit is contained in:
parent
4df9ac8059
commit
acacba6f03
|
@ -56,7 +56,7 @@ By default, gotty starts a web server at port 8080. Open the URL on your web bro
|
||||||
--random-url, -r Add a random string to the URL [$GOTTY_RANDOM_URL]
|
--random-url, -r Add a random string to the URL [$GOTTY_RANDOM_URL]
|
||||||
--profile-file, -f "~/.gotty" Path to profile file [$GOTTY_PROFILE_FILE]
|
--profile-file, -f "~/.gotty" Path to profile file [$GOTTY_PROFILE_FILE]
|
||||||
--title-format "GoTTY - {{ .Command }} ({{ .Hostname }})" Title format of browser window [$GOTTY_TITLE_FORMAT]
|
--title-format "GoTTY - {{ .Command }} ({{ .Hostname }})" Title format of browser window [$GOTTY_TITLE_FORMAT]
|
||||||
--version, -v print the version
|
--auto-reconnect "-1" Seconds to automatically reconnect to the server when the connection is closed (default: disabled) [$GOTTY_AUTO_RECONNECT]
|
||||||
```
|
```
|
||||||
|
|
||||||
By default, gotty doesn't allow clients to send any keystrokes or commands except terminal window resizing. When you want to permit clients to write input to the PTY, add the `-w` option. However, accepting input from remote clients is dangerous for most commands. Make sure that only trusted clients can connect to your gotty server when you activate this option. If you need interaction with the PTY, consider starting gotty with tmux or GNU Screen and run your main command on it.
|
By default, gotty doesn't allow clients to send any keystrokes or commands except terminal window resizing. When you want to permit clients to write input to the PTY, add the `-w` option. However, accepting input from remote clients is dangerous for most commands. Make sure that only trusted clients can connect to your gotty server when you activate this option. If you need interaction with the PTY, consider starting gotty with tmux or GNU Screen and run your main command on it.
|
||||||
|
|
17
app/app.go
17
app/app.go
|
@ -32,14 +32,15 @@ type App struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Address string
|
Address string
|
||||||
Port string
|
Port string
|
||||||
PermitWrite bool
|
PermitWrite bool
|
||||||
Credential string
|
Credential string
|
||||||
RandomUrl bool
|
RandomUrl bool
|
||||||
ProfileFile string
|
ProfileFile string
|
||||||
TitleFormat string
|
TitleFormat string
|
||||||
Command []string
|
AutoReconnect int
|
||||||
|
Command []string
|
||||||
}
|
}
|
||||||
|
|
||||||
const DefaultProfileFilePath = "~/.gotty"
|
const DefaultProfileFilePath = "~/.gotty"
|
||||||
|
|
|
@ -28,9 +28,10 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Output = '0'
|
Output = '0'
|
||||||
SetWindowTitle = '1'
|
SetWindowTitle = '1'
|
||||||
SetPreferences = '2'
|
SetPreferences = '2'
|
||||||
|
SetAutoReconnect = '3'
|
||||||
)
|
)
|
||||||
|
|
||||||
type argResizeTerminal struct {
|
type argResizeTerminal struct {
|
||||||
|
@ -124,6 +125,17 @@ func (context *clientContext) sendInitialize() error {
|
||||||
writer.Write(prefs)
|
writer.Write(prefs)
|
||||||
writer.Close()
|
writer.Close()
|
||||||
|
|
||||||
|
if context.app.options.AutoReconnect >= 0 {
|
||||||
|
autoReconnect, _ := json.Marshal(context.app.options.AutoReconnect)
|
||||||
|
writer, err = context.connection.NextWriter(websocket.TextMessage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
writer.Write([]byte{SetAutoReconnect})
|
||||||
|
writer.Write(autoReconnect)
|
||||||
|
writer.Close()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ func (fi bindataFileInfo) Sys() interface{} {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var _staticGottyJs = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x8c\x54\xc1\x8e\x9b\x30\x10\xbd\xe7\x2b\x2c\x2e\x31\x6a\xe4\xdd\xf4\xd0\x43\xa2\x55\x0f\xab\xed\xa1\xad\xba\x55\x93\x76\x0f\xab\x3d\x18\x33\x09\x6e\x1c\x1b\xd9\x66\x51\x5a\xf1\xef\x1d\x13\x48\x08\x61\x51\xe7\x80\x60\xfc\xde\xf3\xf3\xcc\x18\xba\x29\xb4\xf0\xd2\x68\x1a\x93\xbf\x13\x82\xf1\xca\x2d\xc9\xbc\xcf\xdd\x83\xe6\x89\x82\x94\xdc\x91\x52\xea\xd4\x94\x4c\x19\xc1\x03\x94\xe5\xd6\x78\x23\x8c\x22\x77\x77\x24\xaa\xb1\x8b\x68\x79\x22\x17\x16\x17\x08\xbd\xd0\xf8\x48\xa6\xa5\x73\x8b\x9b\x9b\x29\x59\x84\xd7\xf0\x16\x93\x77\x57\xca\x99\x71\x7e\x20\x9d\x73\x9f\x69\xbe\x07\x5c\x42\xf2\xf4\xbc\x57\xeb\xc4\xe1\x8e\xcf\xd1\xd6\x78\x7f\x88\x5e\xce\xcb\x65\xc8\x6b\x28\xc9\x13\x24\x2b\x23\x76\xe0\x29\xba\x9b\x9d\x69\xf1\x72\x72\x02\x7b\xb0\xfb\xe6\xb3\x74\xcc\x68\x93\x83\x46\xfa\xa9\x40\xf0\x0a\xda\xb7\x55\x0a\x91\x05\x06\x4b\x61\xc3\x0b\xe5\x57\xde\x58\xbe\x85\x66\x3f\x25\x13\xd6\x64\xd8\x57\x3c\x85\xa2\xf1\x72\x94\xc7\x84\x02\x6e\x69\xeb\x27\x44\x40\x35\x72\x47\xc6\x1a\x1f\x52\x1f\xb5\x2e\x50\x68\xb6\x5d\xfb\x01\x3c\x3d\x74\x5d\x77\x0d\xb7\x27\x95\x06\x11\x35\x51\x1a\x96\x17\x2e\xbb\x50\x0c\x81\x79\xa3\x7f\xad\xbf\xc0\xc1\x79\x6b\x76\xd0\x55\xc4\x4c\x5f\xb4\xa9\x99\x03\x9d\xd2\xe8\x36\xc2\x36\x05\xd0\xf2\x02\x53\x5d\x6f\x11\xf0\x2b\x6f\xa5\xde\xa2\x7e\x7f\xcb\x21\x47\xe7\x53\x3a\xf9\xe7\xc2\x14\x36\xb3\xd8\x6b\x37\x23\xd6\x94\x6e\xcc\xde\xd5\x42\x88\x68\x1e\x3c\x7f\x5e\x3d\x7e\x63\xae\xf6\x23\x37\x87\x61\x64\x88\x6b\xf1\x6e\x34\x4e\x16\xed\xcb\x6c\x14\x1d\xec\x2e\xea\xe7\xdb\xb8\x6a\x70\x25\xbe\xca\xc6\xa3\xf5\x3e\xf6\x5b\x3b\xcf\x95\xc2\x22\x27\x86\xdb\xb4\x3b\x95\x55\x7f\xa8\x52\x10\x38\x9a\x1e\x68\x6a\x44\xb1\xc7\xd9\x67\x89\x49\x0f\x0d\xa3\xea\xde\x94\x3d\x38\x77\x9c\xfd\xb7\x2f\x4b\xca\x3d\x47\x40\x9d\x67\xe1\x83\x39\x25\x05\xd0\x79\xc7\x82\x2b\xa5\x17\x19\x3d\x63\x9e\x6f\x5f\xba\x1a\x82\x3b\x20\xd3\xdb\xe9\x62\xe0\x60\x86\x95\x56\x7a\xf8\xb9\xfe\x34\xff\x40\x03\xb7\x37\x7e\x89\x05\xbe\x5b\xf6\xa4\xe6\x43\x52\x0e\xfc\x53\xfd\xff\x59\x4b\xaf\xe0\xbf\xb5\xde\xf7\xb4\x72\x0b\x1b\xb0\xa0\x05\x84\x9f\x50\x3d\x5b\x39\xb7\x6e\x50\xf0\x31\xf9\x0d\xc2\xb3\x1d\xce\x3e\xed\xf0\x62\xb6\x31\xf6\x81\x63\x49\x4e\x75\x45\xc8\xd0\x74\xd7\xc6\xb7\xe0\xbf\x23\xd9\xd1\x38\x9c\x21\x40\x67\x5d\x17\xcf\x98\x78\xe9\x5f\xca\xf1\x93\x1d\x27\xaf\xea\xb4\x5a\x28\xe3\xc6\x1b\xdd\xb6\xc3\x65\xa6\x7c\x7c\x05\xab\xf8\x81\x46\xf7\x46\x6b\xa8\x09\xe4\x3e\x28\xa4\xd1\x8c\xe8\x42\xa9\xce\xf6\x35\xaf\xd0\x6f\x4c\x68\x35\xa9\x62\x1a\x4f\xfe\x05\x00\x00\xff\xff\x0d\xc1\x26\x9b\xaf\x06\x00\x00")
|
var _staticGottyJs = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x94\x55\xc1\x6e\x1a\x31\x10\xbd\xe7\x2b\xac\xbd\xe0\x6d\xa9\x03\xad\xd4\x03\x88\xf6\x10\xa5\x87\xb6\x6a\xaa\x40\x9b\x43\x94\x83\xf1\x0e\xe0\x62\x6c\x64\x7b\xb3\xa2\x15\xff\xde\xf1\xb2\xc0\xb2\x78\x83\xe2\x03\x5a\x3c\x6f\xde\xcc\x1b\xcf\xd8\x74\x96\x6b\xe1\xa5\xd1\x34\x25\xff\xae\x08\xae\x67\x6e\xc9\xc2\xfb\xb5\xbb\xd5\x7c\xaa\x20\x23\x23\x52\x48\x9d\x99\x82\x29\x23\x78\x80\xb2\xb5\x35\xde\x08\xa3\xc8\x68\x44\x92\x12\x3b\x48\x86\x07\xe7\xdc\xa2\x81\xd0\x13\x8e\xcf\xa4\x53\x38\x37\xb8\xbe\xee\x90\x41\xf8\x0c\x5f\x29\x79\x7b\xc6\xbc\x30\xce\x47\xb6\xd7\xdc\x2f\x34\x5f\x01\x9a\xd0\xb9\x73\x8c\xb5\xcf\xc4\x61\xc4\xc7\x64\x6e\xbc\xdf\x24\x4f\x47\x33\xcf\xbd\xb9\x07\x61\xb4\x06\xe1\x11\xf2\xae\x3f\xbc\x3a\x18\xcd\x1a\xf4\x43\x70\x3c\x2b\xc1\x1e\x51\x04\xab\x86\x82\x3c\xc0\x74\x6c\xc4\x12\x3c\x45\x71\xdd\x63\xd4\xb4\xa2\xdb\x3b\x78\xb0\xab\xda\x56\xe1\x98\xd1\x21\x4c\x3d\x08\x3c\x83\xf6\xf5\x48\x61\x2d\x82\x27\xcb\x60\xc6\x73\xe5\xc7\xde\x58\x3e\x87\x2a\xb6\x92\x53\x56\xed\xb0\xef\x58\x10\x45\xd3\xe1\x45\x5f\x26\x14\x70\x4b\xeb\xf9\x85\x15\x90\x15\xed\xce\x6b\x82\x3f\x52\xef\x38\xcf\x90\x98\xfc\xde\x7e\x0f\x3c\xdb\xb4\x95\xaa\x5e\x01\x69\x10\x55\x3a\x4b\xc3\xd6\xb9\x5b\x9c\x31\x87\x85\x36\xa3\x7f\x4f\xbe\xc1\xc6\x79\x6b\x96\x50\x67\xc6\x9d\x18\x79\x55\x4f\x07\x3a\xa3\x49\x2f\xc1\x4e\x08\xc0\xe1\x19\x6e\x1b\x0f\x17\xfc\xc6\xde\x4a\x3d\xc7\x58\xcd\xf0\x6d\x19\x1e\xd5\x3b\xf9\xf7\x24\x49\x3c\xfc\x7c\xa5\x5d\x97\x58\x53\xb8\x4b\xe9\x46\x8d\x61\x25\xfd\xa0\xe3\xeb\xf8\xee\x07\x73\x65\x6e\x72\xb6\x69\x47\x87\x15\x0f\x54\x5f\x55\x66\x83\xfd\x47\xf7\xa2\x47\x90\x30\x28\x7f\x5f\xc6\x6e\x5b\xad\x69\xd4\x72\xbe\x1b\x3b\x9b\x5d\xaf\x68\xe7\xb9\x52\x78\x20\x53\xc3\x6d\xd6\xec\xf0\xa6\x5f\xd5\xf0\x02\x5b\xdd\x03\xcd\x8c\xc8\x57\x38\x53\x6c\x6a\xb2\x4d\xcd\x73\xdb\x9c\xc4\x15\x38\xb7\x9b\xab\x97\x87\x31\xe3\x9e\x23\xa8\xb4\xb1\xf0\x87\x39\x25\x05\xd0\x7e\x23\x2d\x57\x48\x2f\x16\xf4\x88\x7b\xec\x3d\x35\xb9\x04\x77\x40\x3a\xbd\xce\xa0\x45\xb8\x61\x85\x95\x1e\x7e\x4d\xbe\xf4\x3f\xd2\xc0\x11\x69\xe9\xa9\x05\xbe\x1c\x46\x68\xfb\x6d\xb4\x0e\xfc\x43\x79\x85\x4e\xa4\x57\xf0\x6a\xde\xf7\x11\xde\xb5\x85\x19\x58\xd0\x02\xc2\xa5\x58\xf6\xec\x9a\x5b\xd7\x4a\x7e\x37\xfd\x83\x77\x2e\x5b\xe2\x8c\xd1\x9a\x6f\xca\x66\xc6\xde\x72\x2c\xdb\xe1\x0c\x10\xd2\x36\x41\xa5\x98\x39\xf8\x9f\x48\xe0\x68\x1a\x74\x05\x78\xb7\x9e\xcd\x23\x6e\x3c\xc5\x2e\x82\xd7\x28\xfe\x10\x51\xdc\x7c\x3c\x2e\x6b\x8e\xd0\x1f\x87\x66\xdb\xe8\x46\xa1\x8c\xbb\xdc\x8b\x72\x46\x68\xa8\x42\xac\x42\x65\x75\x72\x7d\x61\x78\x0e\xc8\x70\x0b\x2e\x4c\x71\xf7\x0c\x56\xf1\x0d\x4d\x6e\x76\xca\x30\x34\xb9\x09\xb9\x64\x49\x97\xe8\x5c\xa9\xe6\xf0\x9d\x8e\x9e\xdd\x1c\x6a\x52\x8f\xd4\x54\x07\xd6\x1a\x7b\xa2\x2e\x6c\x34\x55\xb4\xb3\xd5\x38\x77\xef\xf4\xe1\x1d\x29\xdf\xd9\x9a\x5f\xdb\xab\x14\x2a\x77\x7a\x84\x9f\x46\xa4\xd7\xcc\x00\x3b\x6a\x22\x57\x60\x72\x4f\x77\x71\xba\x8d\x73\x7f\x43\xfa\xbd\x5e\x2f\x92\xdb\x36\xa5\xe9\xd5\xff\x00\x00\x00\xff\xff\x1e\x3d\x0c\xe8\x3f\x09\x00\x00")
|
||||||
|
|
||||||
func staticGottyJsBytes() ([]byte, error) {
|
func staticGottyJsBytes() ([]byte, error) {
|
||||||
return bindataRead(
|
return bindataRead(
|
||||||
|
@ -86,7 +86,7 @@ func staticGottyJs() (*asset, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "static/gotty.js", size: 1711, mode: os.FileMode(436), modTime: time.Unix(1440336972, 0)}
|
info := bindataFileInfo{name: "static/gotty.js", size: 2367, mode: os.FileMode(436), modTime: time.Unix(1440367388, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
7
main.go
7
main.go
|
@ -55,6 +55,12 @@ func main() {
|
||||||
Usage: "Title format of browser window",
|
Usage: "Title format of browser window",
|
||||||
EnvVar: "GOTTY_TITLE_FORMAT",
|
EnvVar: "GOTTY_TITLE_FORMAT",
|
||||||
},
|
},
|
||||||
|
cli.IntFlag{
|
||||||
|
Name: "auto-reconnect",
|
||||||
|
Value: -1,
|
||||||
|
Usage: "Seconds to automatically reconnect to the server when the connection is closed (default: disabled)",
|
||||||
|
EnvVar: "GOTTY_AUTO_RECONNECT",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
cmd.Action = func(c *cli.Context) {
|
cmd.Action = func(c *cli.Context) {
|
||||||
if len(c.Args()) == 0 {
|
if len(c.Args()) == 0 {
|
||||||
|
@ -72,6 +78,7 @@ func main() {
|
||||||
c.Bool("random-url"),
|
c.Bool("random-url"),
|
||||||
c.String("profile-file"),
|
c.String("profile-file"),
|
||||||
c.String("title-format"),
|
c.String("title-format"),
|
||||||
|
c.Int("auto-reconnect"),
|
||||||
c.Args(),
|
c.Args(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,62 +2,84 @@
|
||||||
var httpsEnabled = window.location.protocol == "https:";
|
var httpsEnabled = window.location.protocol == "https:";
|
||||||
var url = (httpsEnabled ? 'wss://' : 'ws://') + window.location.host + window.location.pathname + 'ws';
|
var url = (httpsEnabled ? 'wss://' : 'ws://') + window.location.host + window.location.pathname + 'ws';
|
||||||
var protocols = ["gotty"];
|
var protocols = ["gotty"];
|
||||||
var ws = new WebSocket(url, protocols);
|
var autoReconnect = -1;
|
||||||
|
|
||||||
var term;
|
var openWs = function() {
|
||||||
|
var ws = new WebSocket(url, protocols);
|
||||||
|
|
||||||
ws.onopen = function(event) {
|
var term;
|
||||||
hterm.defaultStorage = new lib.Storage.Local();
|
|
||||||
hterm.defaultStorage.clear();
|
|
||||||
|
|
||||||
term = new hterm.Terminal();
|
ws.onopen = function(event) {
|
||||||
|
hterm.defaultStorage = new lib.Storage.Local();
|
||||||
|
hterm.defaultStorage.clear();
|
||||||
|
|
||||||
term.onTerminalReady = function() {
|
term = new hterm.Terminal();
|
||||||
var io = term.io.push();
|
|
||||||
|
|
||||||
io.onVTKeystroke = function(str) {
|
term.onTerminalReady = function() {
|
||||||
ws.send("0" + str);
|
var io = term.io.push();
|
||||||
};
|
|
||||||
|
|
||||||
io.sendString = io.onVTKeystroke;
|
io.onVTKeystroke = function(str) {
|
||||||
|
ws.send("0" + str);
|
||||||
|
};
|
||||||
|
|
||||||
io.onTerminalResize = function(columns, rows) {
|
io.sendString = io.onVTKeystroke;
|
||||||
ws.send(
|
|
||||||
"1" + JSON.stringify(
|
io.onTerminalResize = function(columns, rows) {
|
||||||
{
|
ws.send(
|
||||||
columns: columns,
|
"1" + JSON.stringify(
|
||||||
rows: rows,
|
{
|
||||||
}
|
columns: columns,
|
||||||
|
rows: rows,
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
};
|
||||||
|
|
||||||
|
term.installKeyboard();
|
||||||
};
|
};
|
||||||
|
|
||||||
term.installKeyboard();
|
term.decorate(document.body);
|
||||||
};
|
};
|
||||||
|
|
||||||
term.decorate(document.body);
|
ws.onmessage = function(event) {
|
||||||
};
|
data = event.data.slice(1);
|
||||||
|
switch(event.data[0]) {
|
||||||
|
case '0':
|
||||||
|
term.io.writeUTF16(data);
|
||||||
|
break;
|
||||||
|
case '1':
|
||||||
|
term.setWindowTitle(data);
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
preferences = JSON.parse(data);
|
||||||
|
Object.keys(preferences).forEach(function(key) {
|
||||||
|
term.getPrefs().set(key, preferences[key]);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
autoReconnect = JSON.parse(data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ws.onmessage = function(event) {
|
ws.onclose = function(event) {
|
||||||
data = event.data.slice(1);
|
if (term) {
|
||||||
switch(event.data[0]) {
|
term.uninstallKeyboard();
|
||||||
case '0':
|
term.io.showOverlay("Connection Closed", null);
|
||||||
term.io.writeUTF16(data);
|
}
|
||||||
break;
|
tryReconnect();
|
||||||
case '1':
|
}
|
||||||
term.setWindowTitle(data);
|
|
||||||
break;
|
ws.onerror = function(error) {
|
||||||
case '2':
|
tryReconnect();
|
||||||
preferences = JSON.parse(data);
|
|
||||||
Object.keys(preferences).forEach(function(key) {
|
|
||||||
term.getPrefs().set(key, preferences[key]);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ws.onclose = function(event) {
|
openWs();
|
||||||
term.io.showOverlay("Connection Closed", null);
|
|
||||||
term.uninstallKeyboard();
|
var tryReconnect = function() {
|
||||||
|
if (autoReconnect >= 0) {
|
||||||
|
setTimeout(openWs, autoReconnect * 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|
Loading…
Reference in New Issue