Authenticate WS connection using token

Safari doesn't support basic authentication for websocket sessions.
This commit introduces a token-based authentication only for websocket
connection.
The token is shared by all clients and that might be not secure. However,
basic authentication itself is insecure and the credential is already
shared by clients, so don't mind.
This commit is contained in:
Iwasaki Yudai 2015-08-31 15:54:34 +09:00
parent e7e607b3d7
commit 0e81c484a9
4 changed files with 47 additions and 34 deletions

View File

@ -29,8 +29,9 @@ type App struct {
command []string command []string
options *Options options *Options
upgrader *websocket.Upgrader upgrader *websocket.Upgrader
server *manners.GracefulServer server *manners.GracefulServer
authToken string
titleTemplate *template.Template titleTemplate *template.Template
} }
@ -88,19 +89,13 @@ func New(command []string, options *Options) (*App, error) {
WriteBufferSize: 1024, WriteBufferSize: 1024,
Subprotocols: []string{"gotty"}, Subprotocols: []string{"gotty"},
}, },
authToken: generateRandomString(20),
titleTemplate: titleTemplate, titleTemplate: titleTemplate,
}, nil }, nil
} }
func ApplyConfigFile(options *Options, configFilePath string) error { func ApplyConfigFile(options *Options, filePath string) error {
if err := applyConfigFile(options, configFilePath); err != nil {
return err
}
return nil
}
func applyConfigFile(options *Options, filePath string) error {
filePath = ExpandHomeDir(filePath) filePath = ExpandHomeDir(filePath)
if _, err := os.Stat(filePath); os.IsNotExist(err) { if _, err := os.Stat(filePath); os.IsNotExist(err) {
return err return err
@ -146,19 +141,15 @@ func applyConfigFile(options *Options, filePath string) error {
return nil return nil
} }
func ExpandHomeDir(path string) string {
if path[0:2] == "~/" {
return os.Getenv("HOME") + path[1:]
} else {
return path
}
}
func (app *App) Run() error { func (app *App) Run() error {
if app.options.PermitWrite { if app.options.PermitWrite {
log.Printf("Permitting clients to write input to the PTY.") log.Printf("Permitting clients to write input to the PTY.")
} }
if app.options.Once {
log.Printf("Once option is provided, accepting only one client")
}
path := "" path := ""
if app.options.EnableRandomUrl { if app.options.EnableRandomUrl {
path += "/" + generateRandomString(app.options.RandomUrlLength) path += "/" + generateRandomString(app.options.RandomUrlLength)
@ -167,31 +158,23 @@ func (app *App) Run() error {
endpoint := net.JoinHostPort(app.options.Address, app.options.Port) endpoint := net.JoinHostPort(app.options.Address, app.options.Port)
wsHandler := http.HandlerFunc(app.handleWS) wsHandler := http.HandlerFunc(app.handleWS)
customIndexHandler := http.HandlerFunc(app.handleCustomIndex)
authTokenHandler := http.HandlerFunc(app.handleAuthToken)
staticHandler := http.FileServer( staticHandler := http.FileServer(
&assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "static"}, &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "static"},
) )
if app.options.Once {
log.Printf("Once option is provided, accepting only one client")
}
var siteMux = http.NewServeMux() var siteMux = http.NewServeMux()
if app.options.IndexFile != "" { if app.options.IndexFile != "" {
log.Printf("Using index file at " + app.options.IndexFile) log.Printf("Using index file at " + app.options.IndexFile)
indexHandler := http.HandlerFunc( siteMux.Handle(path+"/", customIndexHandler)
func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, ExpandHomeDir(app.options.IndexFile))
},
)
siteMux.Handle(path+"/", indexHandler)
} else { } else {
siteMux.Handle(path+"/", http.StripPrefix(path+"/", staticHandler)) siteMux.Handle(path+"/", http.StripPrefix(path+"/", staticHandler))
} }
siteMux.Handle(path+"/auth_token.js", authTokenHandler)
siteMux.Handle(path+"/js/", http.StripPrefix(path+"/", staticHandler)) siteMux.Handle(path+"/js/", http.StripPrefix(path+"/", staticHandler))
siteMux.Handle(path+"/favicon.png", http.StripPrefix(path+"/", staticHandler)) siteMux.Handle(path+"/favicon.png", http.StripPrefix(path+"/", staticHandler))
siteMux.Handle(path+"/ws", wsHandler)
siteHandler := http.Handler(siteMux) siteHandler := http.Handler(siteMux)
@ -200,6 +183,11 @@ func (app *App) Run() error {
siteHandler = wrapBasicAuth(siteHandler, app.options.Credential) siteHandler = wrapBasicAuth(siteHandler, app.options.Credential)
} }
wsMux := http.NewServeMux()
wsMux.Handle("/", siteHandler)
wsMux.Handle(path+"/ws", wsHandler)
siteHandler = (http.Handler(wsMux))
siteHandler = wrapLogger(siteHandler) siteHandler = wrapLogger(siteHandler)
scheme := "http" scheme := "http"
@ -264,6 +252,12 @@ func (app *App) handleWS(w http.ResponseWriter, r *http.Request) {
return return
} }
_, initMessage, err := conn.ReadMessage()
if err != nil || string(initMessage) != app.authToken {
log.Print("Failed to authenticate websocket connection")
return
}
cmd := exec.Command(app.command[0], app.command[1:]...) cmd := exec.Command(app.command[0], app.command[1:]...)
ptyIo, err := pty.Start(cmd) ptyIo, err := pty.Start(cmd)
if err != nil { if err != nil {
@ -283,6 +277,14 @@ func (app *App) handleWS(w http.ResponseWriter, r *http.Request) {
context.goHandleClient() context.goHandleClient()
} }
func (app *App) handleCustomIndex(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, ExpandHomeDir(app.options.IndexFile))
}
func (app *App) handleAuthToken(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("var gotty_auth_token = '" + app.authToken + "';"))
}
func (app *App) Exit() (firstCall bool) { func (app *App) Exit() (firstCall bool) {
if app.server != nil { if app.server != nil {
log.Printf("Received Exit command, waiting for all clients to close sessions...") log.Printf("Received Exit command, waiting for all clients to close sessions...")
@ -355,3 +357,11 @@ func listAddresses() (addresses []string) {
return return
} }
func ExpandHomeDir(path string) string {
if path[0:2] == "~/" {
return os.Getenv("HOME") + path[1:]
} else {
return path
}
}

View File

@ -91,7 +91,7 @@ func staticFaviconPng() (*asset, error) {
return a, nil return a, nil
} }
var _staticIndexHtml = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x74\x90\xc1\x52\xc4\x20\x0c\x86\xef\x3e\x45\xc4\xf1\xe6\x94\x7a\xdd\xd2\x5e\x7d\x81\xbd\x78\x64\x4b\xb6\x64\xa5\xc0\x40\x5c\xed\x38\xbe\xbb\xb0\xd8\x93\xe3\x89\x3f\xf9\xff\x49\x3e\xa2\xee\x4d\x98\x79\x8b\x08\x96\x57\x37\xdd\xa9\xf6\x00\x28\x8b\xda\x54\x51\x24\x13\x3b\x9c\x5e\xc2\xf1\xf8\xaa\x64\x2b\x9a\x91\x79\x2b\xfa\x14\xcc\xf6\x04\x0f\x8c\x69\x25\xaf\x1d\x7c\xc5\x90\x89\x29\xf8\x03\xe8\x53\x0e\xee\x9d\x71\x00\x8b\xb4\x58\x3e\xc0\x73\xdf\x3f\x0e\xf0\x41\x86\xed\x5e\xac\x3a\x2d\x54\xc2\x7d\xfc\x1c\xbe\x95\x6c\x43\xdb\x02\x47\xfe\x0d\x12\xba\x51\xd0\x1c\xbc\x80\x4a\x5a\xf4\xaa\x17\x94\xd1\x2f\x02\x6c\xc2\xf3\x28\xce\xfa\x5a\xfd\xae\xb6\x6e\xf0\x72\xa7\x57\x15\xee\x77\x98\xa1\x2b\x90\x19\xc5\x0e\x2a\x26\x25\x4b\x6f\xff\xcb\x9c\x28\x32\xe4\x34\x8f\xa2\x93\x97\x2c\x6d\xcd\x75\x97\x5c\x63\xcd\xfc\x2f\xb9\x04\xe6\xed\x4f\x52\xc9\xb6\xbb\xc0\xdc\x6e\xfa\x13\x00\x00\xff\xff\x96\x39\xb4\x53\x6b\x01\x00\x00") var _staticIndexHtml = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x84\x90\xc1\x52\xc3\x20\x10\x86\xef\x3e\xc5\x8a\xe3\xcd\x09\xf1\xda\x90\x5c\x7d\x81\x5e\x3c\x39\x34\x6c\xc3\xb6\x04\x32\xb0\xad\x66\x1c\xdf\x5d\x28\xe6\xa4\x33\x9e\xf8\x77\xf7\x9b\xe5\x03\x75\x6f\xc2\xc8\xeb\x82\x60\x79\x76\xc3\x9d\xaa\x07\x80\xb2\xa8\x4d\x09\x39\x32\xb1\xc3\xe1\x25\xec\xf7\xaf\x4a\xd6\xa2\x0e\x12\xaf\x39\x1f\x82\x59\x9f\xe0\x81\x31\xce\xe4\xb5\x83\xcf\x25\x24\x62\x0a\x7e\x07\xfa\x90\x82\xbb\x30\x76\x60\x91\x26\xcb\x3b\x78\x6e\xdb\xc7\x0e\xde\xc9\xb0\xdd\x8a\x59\xc7\x89\x32\xdc\x2e\x1f\xdd\x97\x92\x75\x69\xbd\xc0\x91\x3f\x43\x44\xd7\x0b\x1a\x83\x17\x50\x4c\x73\x9e\xf5\x84\x72\xf1\x93\x00\x1b\xf1\xd8\x8b\xa3\xbe\x96\x79\x53\x5a\x37\x79\xb9\xd9\xab\x22\xf7\xb3\xcc\xd0\x15\xc8\xf4\x62\x13\x15\x83\x92\xb9\xb7\xbd\x65\x8c\xb4\x30\xa4\x38\xf6\xa2\x91\xa7\x24\x6d\xe1\x9a\x53\x2a\x58\x1d\xfe\x49\xea\x0b\xdb\x37\x0e\x67\xf4\xff\xb3\x79\xeb\x14\x98\xd7\x5f\xa4\x92\xd5\x33\x8b\xdf\xfe\xff\x3b\x00\x00\xff\xff\x35\xaf\x3e\x6a\x97\x01\x00\x00")
func staticIndexHtmlBytes() ([]byte, error) { func staticIndexHtmlBytes() ([]byte, error) {
return bindataRead( return bindataRead(
@ -106,12 +106,12 @@ func staticIndexHtml() (*asset, error) {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "static/index.html", size: 363, mode: os.FileMode(436), modTime: time.Unix(1440919621, 0)} info := bindataFileInfo{name: "static/index.html", size: 407, mode: os.FileMode(436), modTime: time.Unix(1441003850, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
var _staticJsGottyJs = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x94\x56\x5f\x6f\xe2\x38\x10\x7f\xef\xa7\xb0\xf2\x82\x73\x47\x5d\xb8\x93\xee\x01\xc4\x9d\x74\x55\x57\xda\x3f\xda\xae\x0a\xbb\x7d\xa8\xfa\x60\x92\x01\xb2\x18\x1b\xd9\x4e\x23\x76\xc5\x77\xdf\x99\x10\x20\x04\xa7\x6c\xfd\x10\x19\xcf\xcc\x6f\xfe\x78\x7e\x63\xf8\x2c\xd7\x89\xcf\x8c\xe6\x31\xfb\x79\xc5\x70\xbd\x48\xcb\x16\xde\xaf\xdd\x9d\x96\x53\x05\x29\x1b\xb1\x22\xd3\xa9\x29\x84\x32\x89\x24\x55\xb1\xb6\xc6\x9b\xc4\x28\x36\x1a\xb1\xa8\xd4\x1d\x44\xc3\x83\x71\x6e\x51\xc0\xf8\x09\xc6\x7f\xac\x53\x38\x37\xb8\xb9\xe9\xb0\x01\x6d\x69\x17\xb3\x3f\xcf\x90\x17\xc6\xf9\xc0\xf1\x5a\xfa\x85\x96\x2b\x40\x11\x1a\x77\x8e\xbe\xf6\x91\x38\xf4\xf8\x14\xcd\x8d\xf7\x9b\xe8\xf9\x28\x96\xb9\x37\x0f\x90\x18\xad\x21\xf1\xa8\x72\xdd\x1f\x5e\x1d\x84\x66\x0d\xfa\x91\x0c\xcf\x4a\xb0\xd7\x28\x48\xaa\xa1\x60\x8f\x30\x1d\x9b\x64\x09\x9e\x63\x72\xdd\xa3\xd7\xb8\x82\xdb\x1b\x78\xb0\xab\xda\x51\xe1\x84\xd1\xe4\xa6\xee\x04\x5e\x40\xfb\xba\x27\x5a\x0b\xb2\x14\x29\xcc\x64\xae\xfc\xd8\x1b\x2b\xe7\x50\xf9\x56\xd9\x54\x54\x27\xe2\x13\x16\x44\xf1\x78\x78\xd1\x56\x24\x0a\xa4\xe5\xf5\xf8\x68\x91\x66\x05\xbb\xb3\x9a\xe0\x27\xd3\x3b\xcc\x33\x4d\x31\x07\xff\xc5\xc2\xcc\xf1\x58\x38\xcc\x3d\x72\xa0\xd3\x6b\xd0\x89\x49\x33\x3d\x8f\xba\x2c\xb2\xb2\x88\x82\x96\x46\xef\x91\x1f\x40\xa6\x9b\xb6\x22\xd7\x6b\x97\x19\xd4\x2a\x8d\x33\x23\xd6\xb9\x5b\x9c\xc5\x44\x0b\x65\x46\x7f\x9b\x7c\x84\x8d\xf3\xd6\x2c\xa1\x8e\x8c\x27\x21\xf0\xea\x26\x28\x78\x1e\xf5\x22\xec\x21\x52\x1c\x9e\xe9\x6d\xc3\xee\xc8\x6e\xec\x2d\x66\x8c\xbe\x9a\xee\xdb\x22\x3c\x66\xef\xb2\x1f\x27\x41\x62\xdb\xe4\x2b\xed\xba\xcc\x9a\xc2\x5d\x0a\x37\x28\xa4\x15\xf5\x29\x8f\x0f\xe3\xfb\xcf\xc2\x95\xb1\x65\xb3\x4d\xbb\x36\xad\xb0\xa3\xfa\xaa\x22\x1b\xec\x37\xdd\x8b\x16\x94\xc2\xa0\xfc\xbe\xae\xbb\x6d\x95\xc6\x41\xc9\xf9\x69\xe8\x6e\x76\xbd\xa2\x9d\x97\x4a\xe1\x85\x4c\x8d\xb4\x69\x93\x1b\x4d\xbb\x8a\x2a\x09\x92\xc4\x03\x4f\x4d\x92\xaf\x90\x8d\xd4\xe8\x77\x0a\x68\xfb\xff\xe6\x3d\x76\x89\xaf\xae\x2f\x8a\x6b\x78\xdb\x26\xb3\x57\xe0\xdc\x8e\xa7\xaf\x93\x3b\x95\x5e\xa2\x52\x29\x13\xf4\x43\x38\x95\x25\xc0\xfb\x8d\x60\x5d\x91\xf9\x64\xc1\x8f\x7a\x4f\xbd\xe7\x26\x56\x22\x1d\xb0\x4e\xaf\x33\x68\x29\x87\x11\x85\xcd\x3c\x7c\x9d\xbc\xeb\xff\xc3\x09\x23\xd0\xe8\x53\x0b\x72\x39\x0c\xc0\xf6\xdb\x60\x91\xfd\x8f\xe5\x48\x9e\x64\x5e\xc1\x9b\x71\xff\x0a\xe0\xae\x71\xb0\x80\xc5\x61\x02\x34\x64\xcb\x4e\x5e\x4b\xeb\x5a\xc1\xef\xa7\xdf\x71\x86\x8b\x25\x32\x8f\xd7\x6c\x63\x31\x33\xf6\x4e\x62\xd9\x0e\x77\x80\x2a\x6d\xbc\xc2\x97\xc0\x19\x05\xf8\xb0\xcc\x79\x34\x06\xef\x89\xd5\xc4\x24\xb4\xc1\x6f\x34\x28\x7f\xd4\x63\x7b\x42\xc9\x73\x20\x9c\x43\x69\x1a\x33\x12\xd5\xbb\xbf\x63\xbf\x7d\x4b\xfd\xfe\x0e\xd4\xaf\xf9\xb4\x5d\xae\x60\x00\xfe\x48\xcc\x6d\xa3\xb7\x13\x65\xdc\xe5\xce\xce\x66\x8c\x53\x15\x42\xf5\x2e\xab\x93\xeb\x0b\x04\x3d\x68\xd2\xa4\x5d\x98\xe2\xfe\x05\xac\x92\x1b\x1e\xdd\xee\x32\x43\xd7\xec\x96\x62\x49\xf1\xbd\xd1\xb9\x52\x4d\x82\x9f\xd2\xdb\x6e\x0e\x35\xa9\x7b\x6a\x66\x07\xd6\x1a\x7b\x92\x1d\x1d\x34\xb3\x68\x47\xab\x61\xee\xfe\x45\x1c\xde\xaa\xf2\x5f\x40\xcd\xae\xed\xe5\xa3\xca\x9d\x5e\xe1\xbf\x23\xd6\x6b\x46\x80\x1d\x35\xc9\x56\x60\x72\xcf\x77\x7e\xba\x8d\x7b\xff\x83\xf5\x7b\xbd\x5e\x20\xb6\x6d\xcc\xe3\xab\x5f\x01\x00\x00\xff\xff\x42\x24\x4b\x87\xdd\x09\x00\x00") var _staticJsGottyJs = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x94\x56\xdd\x8e\xda\x3a\x10\xbe\xdf\xa7\xb0\x72\x83\x73\x0e\x9b\x85\x73\xa4\x5e\x80\x68\xa5\xae\xb6\x52\x7f\xd4\xad\x16\xda\xbd\x58\xad\x56\x26\x19\x48\x8a\xb1\x91\x3d\xd9\x88\x56\xbc\x7b\xc7\x21\x40\x08\xce\xd2\xf5\x45\x64\x3c\xdf\x7c\xf3\xe3\x99\x31\x7c\x96\xab\x18\x33\xad\x78\xc8\x7e\x5f\x30\x5a\xcf\xc2\xb0\x14\x71\x65\x6f\x94\x98\x4a\x48\xd8\x88\x15\x99\x4a\x74\x11\x49\x1d\x0b\x07\x8d\x56\x46\xa3\x8e\xb5\x64\xa3\x11\x0b\x4a\xec\x20\x18\xee\x95\x73\x43\x02\xc6\x8f\x38\xde\xb1\x4e\x61\xed\xe0\xea\xaa\xc3\x06\x6e\xeb\x76\x21\xfb\xf7\x84\x39\xd5\x16\x3d\xc7\x2b\x81\xa9\x12\x4b\x20\x11\x29\x77\x0e\xb6\x76\x9e\x58\xb2\xf8\x10\xcc\x35\xe2\x3a\x78\x3c\x88\x45\x8e\xfa\x0e\x62\xad\x14\xc4\x48\x90\xcb\xfe\xf0\x62\x2f\xd4\x2b\x50\xf7\x4e\xf1\x24\x05\x3b\x44\xe1\xa4\x0a\x0a\x76\x0f\xd3\xb1\x8e\x17\x80\x9c\x82\xeb\x1e\xac\x86\x15\xdd\x4e\x01\xc1\x2c\x6b\x47\x85\x8d\xb4\x72\x66\xea\x46\xe0\x19\x14\xd6\x2d\x55\x48\x0b\x2a\xe1\x65\x04\x4f\xe4\x76\xfa\x84\x7a\x01\xaa\x6e\xc0\xad\xd4\x59\x88\x12\x98\x89\x5c\xe2\x18\xb5\x11\x73\xa8\x7c\x94\xd9\x34\xaa\x4e\xa2\x2f\x94\x38\xc9\xc3\xe1\x59\xdd\x28\x96\x20\x0c\x6f\x9a\x71\xc8\x8a\x76\xab\x35\xa1\x4f\xa6\xb6\x9c\x27\xc8\x68\x0e\xf8\xcd\xc0\xcc\xf2\x90\xa2\x40\x1e\xb8\x50\x2e\x41\xc5\x3a\xc9\xd4\x3c\xe8\xb2\xc0\x88\x22\xf0\x6a\x6a\xb5\x63\xbe\x03\x91\xac\xdb\x2e\xa3\x9e\xe3\x4c\x13\xaa\x54\xce\x74\xb4\xca\x6d\x7a\xe2\x93\x5b\x24\xd3\xea\xc7\xe4\x33\xac\x2d\x1a\xca\x64\x9d\x99\x4e\x7c\xe4\xf5\x7b\x08\x7a\x01\xd5\x9a\x03\x0e\x4f\x70\x1b\xbf\x39\xa7\x37\x46\x43\x11\x93\xad\xa6\xf9\x36\x0f\x0f\xd1\xdb\xec\xd7\x91\x93\x54\x5e\xf9\x52\xd9\x2e\x33\xba\xb0\xe7\xdc\xf5\x0a\xdd\x0a\xfa\x2e\x8e\x4f\xe3\xdb\xaf\x91\x2d\x7d\xcb\x66\xeb\x76\xb4\x5b\x7e\x43\xf5\x55\x79\x36\xd8\x6d\xba\x67\x35\x5c\x08\x83\xf2\xfb\x32\x76\xd3\x2a\x0d\xbd\x92\xd3\x53\xdf\xdd\x6c\x6b\x45\x59\x14\x52\xd2\x85\x4c\xb5\x30\x49\xb3\x37\x9a\x7a\x55\xab\xc4\xd4\x24\x08\x3c\xd1\x71\xbe\xa4\xae\x75\x85\x7e\x23\xc1\x6d\xdf\xaf\x3f\x52\x95\x60\x75\x7d\x41\x58\xe3\xdb\x34\x27\xc0\x12\xac\xdd\xf6\xe9\xcb\x43\x20\x11\x28\x08\x54\xca\x22\xf7\x23\xb2\x32\x8b\x81\xf7\x1b\xce\xda\x22\xc3\x38\xe5\x07\xdc\x43\xef\xb1\xc9\x15\x0b\x0b\xac\xd3\xeb\x0c\x5a\xd2\xa1\xa3\xc2\x64\x08\xdf\x27\x1f\xfa\x6f\xb8\xe3\xf0\x14\xfa\xd4\x80\x58\x0c\x3d\xb4\xfd\x36\x5a\xea\xfe\xfb\x72\x74\x4f\x32\x94\xf0\x6a\xde\xff\x3c\xbc\x2b\x1a\x2c\x60\x68\x98\x80\x1b\xc6\x65\x25\xaf\x84\xb1\xad\xe4\xb7\xd3\x9f\x34\xeb\xa3\x05\x75\x1e\xaf\xe9\x86\xd1\x4c\x9b\x1b\x41\x69\xdb\xdf\x01\x41\xda\xfa\x8a\x5e\x0c\xab\x25\xd0\x03\x34\xe7\xc1\x18\x10\x5d\x57\xbb\x4e\x22\x1d\xfa\x06\x83\xf2\x47\xdd\xb7\x07\x92\x3c\x7a\xdc\xd9\xa7\xa6\x31\x23\x09\xde\xfd\x1b\xfd\xcd\x6b\xf2\xf7\xbf\x27\x7f\xcd\x27\xf0\x7c\x06\x3d\xf4\x87\xc6\xdc\x34\x6a\x3b\x96\xda\x9e\xaf\xec\x6c\xc6\xb8\xcb\x82\x2f\xdf\x65\x76\x72\x75\xa6\x41\xf7\x48\x37\x69\x53\x5d\xdc\x3e\x83\x91\x62\xcd\x83\xeb\x6d\x64\x64\x9a\x5d\x3b\x5f\x12\x7a\x6f\x54\x2e\x65\xb3\xc1\x8f\xdb\xdb\xac\xf7\x39\xa9\x5b\x6a\x46\x07\xc6\x68\x73\x14\x9d\x3b\x68\x46\xd1\xce\x56\xe3\xdc\xfe\xdb\xd8\xbf\x55\xe5\xbf\x85\x9a\x5e\xdb\xcb\xe7\x32\x77\x7c\x85\x6f\x47\xac\xd7\xf4\x80\x2a\x6a\x92\x2d\x41\xe7\xc8\xb7\x76\xba\x8d\x7b\xff\x87\xf5\x7b\xbd\x9e\xc7\xb7\x4d\xc8\xc3\x8b\x3f\x01\x00\x00\xff\xff\x4f\xa7\xa4\xe9\x05\x0a\x00\x00")
func staticJsGottyJsBytes() ([]byte, error) { func staticJsGottyJsBytes() ([]byte, error) {
return bindataRead( return bindataRead(
@ -126,7 +126,7 @@ func staticJsGottyJs() (*asset, error) {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "static/js/gotty.js", size: 2525, mode: os.FileMode(436), modTime: time.Unix(1440919623, 0)} info := bindataFileInfo{name: "static/js/gotty.js", size: 2565, mode: os.FileMode(436), modTime: time.Unix(1441003850, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }

View File

@ -10,6 +10,8 @@
var term; var term;
ws.onopen = function(event) { ws.onopen = function(event) {
ws.send(gotty_auth_token);
hterm.defaultStorage = new lib.Storage.Local(); hterm.defaultStorage = new lib.Storage.Local();
hterm.defaultStorage.clear(); hterm.defaultStorage.clear();

View File

@ -8,6 +8,7 @@
<body> <body>
<div id="terminal"></div> <div id="terminal"></div>
<script src="./js/hterm.js"></script> <script src="./js/hterm.js"></script>
<script src="./auth_token.js"></script>
<script src="./js/gotty.js"></script> <script src="./js/gotty.js"></script>
</body> </body>
</html> </html>