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]
|
||||
--profile-file, -f "~/.gotty" Path to profile file [$GOTTY_PROFILE_FILE]
|
||||
--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.
|
||||
|
|
17
app/app.go
17
app/app.go
|
@ -32,14 +32,15 @@ type App struct {
|
|||
}
|
||||
|
||||
type Options struct {
|
||||
Address string
|
||||
Port string
|
||||
PermitWrite bool
|
||||
Credential string
|
||||
RandomUrl bool
|
||||
ProfileFile string
|
||||
TitleFormat string
|
||||
Command []string
|
||||
Address string
|
||||
Port string
|
||||
PermitWrite bool
|
||||
Credential string
|
||||
RandomUrl bool
|
||||
ProfileFile string
|
||||
TitleFormat string
|
||||
AutoReconnect int
|
||||
Command []string
|
||||
}
|
||||
|
||||
const DefaultProfileFilePath = "~/.gotty"
|
||||
|
|
|
@ -28,9 +28,10 @@ const (
|
|||
)
|
||||
|
||||
const (
|
||||
Output = '0'
|
||||
SetWindowTitle = '1'
|
||||
SetPreferences = '2'
|
||||
Output = '0'
|
||||
SetWindowTitle = '1'
|
||||
SetPreferences = '2'
|
||||
SetAutoReconnect = '3'
|
||||
)
|
||||
|
||||
type argResizeTerminal struct {
|
||||
|
@ -124,6 +125,17 @@ func (context *clientContext) sendInitialize() error {
|
|||
writer.Write(prefs)
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ func (fi bindataFileInfo) Sys() interface{} {
|
|||
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) {
|
||||
return bindataRead(
|
||||
|
@ -86,7 +86,7 @@ func staticGottyJs() (*asset, error) {
|
|||
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}
|
||||
return a, nil
|
||||
}
|
||||
|
|
7
main.go
7
main.go
|
@ -55,6 +55,12 @@ func main() {
|
|||
Usage: "Title format of browser window",
|
||||
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) {
|
||||
if len(c.Args()) == 0 {
|
||||
|
@ -72,6 +78,7 @@ func main() {
|
|||
c.Bool("random-url"),
|
||||
c.String("profile-file"),
|
||||
c.String("title-format"),
|
||||
c.Int("auto-reconnect"),
|
||||
c.Args(),
|
||||
},
|
||||
)
|
||||
|
|
|
@ -2,62 +2,84 @@
|
|||
var httpsEnabled = window.location.protocol == "https:";
|
||||
var url = (httpsEnabled ? 'wss://' : 'ws://') + window.location.host + window.location.pathname + 'ws';
|
||||
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) {
|
||||
hterm.defaultStorage = new lib.Storage.Local();
|
||||
hterm.defaultStorage.clear();
|
||||
var term;
|
||||
|
||||
term = new hterm.Terminal();
|
||||
ws.onopen = function(event) {
|
||||
hterm.defaultStorage = new lib.Storage.Local();
|
||||
hterm.defaultStorage.clear();
|
||||
|
||||
term.onTerminalReady = function() {
|
||||
var io = term.io.push();
|
||||
term = new hterm.Terminal();
|
||||
|
||||
io.onVTKeystroke = function(str) {
|
||||
ws.send("0" + str);
|
||||
};
|
||||
term.onTerminalReady = function() {
|
||||
var io = term.io.push();
|
||||
|
||||
io.sendString = io.onVTKeystroke;
|
||||
io.onVTKeystroke = function(str) {
|
||||
ws.send("0" + str);
|
||||
};
|
||||
|
||||
io.onTerminalResize = function(columns, rows) {
|
||||
ws.send(
|
||||
"1" + JSON.stringify(
|
||||
{
|
||||
columns: columns,
|
||||
rows: rows,
|
||||
}
|
||||
io.sendString = io.onVTKeystroke;
|
||||
|
||||
io.onTerminalResize = function(columns, rows) {
|
||||
ws.send(
|
||||
"1" + JSON.stringify(
|
||||
{
|
||||
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) {
|
||||
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;
|
||||
ws.onclose = function(event) {
|
||||
if (term) {
|
||||
term.uninstallKeyboard();
|
||||
term.io.showOverlay("Connection Closed", null);
|
||||
}
|
||||
tryReconnect();
|
||||
}
|
||||
|
||||
ws.onerror = function(error) {
|
||||
tryReconnect();
|
||||
}
|
||||
}
|
||||
|
||||
ws.onclose = function(event) {
|
||||
term.io.showOverlay("Connection Closed", null);
|
||||
term.uninstallKeyboard();
|
||||
openWs();
|
||||
|
||||
var tryReconnect = function() {
|
||||
if (autoReconnect >= 0) {
|
||||
setTimeout(openWs, autoReconnect * 1000);
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
|
Loading…
Reference in New Issue