remove gotty-client ... again
this is still broken on solaris
This commit is contained in:
parent
5b90087cd4
commit
065e159464
|
@ -1,22 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Manfred Touron
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
# Project-specific variables
|
|
||||||
BINARIES ?= gotty-client
|
|
||||||
GOTTY_URL := http://localhost:8081/
|
|
||||||
VERSION := $(shell cat .goxc.json | jq -c .PackageVersion | sed 's/"//g')
|
|
||||||
|
|
||||||
CONVEY_PORT ?= 9042
|
|
||||||
|
|
||||||
|
|
||||||
# Common variables
|
|
||||||
SOURCES := $(shell find . -type f -name "*.go")
|
|
||||||
COMMANDS := $(shell go list ./... | grep -v /vendor/ | grep /cmd/)
|
|
||||||
PACKAGES := $(shell go list ./... | grep -v /vendor/ | grep -v /cmd/)
|
|
||||||
GOENV ?= GO15VENDOREXPERIMENT=1
|
|
||||||
GO ?= $(GOENV) go
|
|
||||||
GODEP ?= $(GOENV) godep
|
|
||||||
USER ?= $(shell whoami)
|
|
||||||
|
|
||||||
|
|
||||||
all: build
|
|
||||||
|
|
||||||
|
|
||||||
.PHONY: build
|
|
||||||
build: $(BINARIES)
|
|
||||||
|
|
||||||
|
|
||||||
.PHONY: install
|
|
||||||
install:
|
|
||||||
$(GO) install ./cmd/gotty-client
|
|
||||||
|
|
||||||
|
|
||||||
$(BINARIES): $(SOURCES)
|
|
||||||
$(GO) build -o $@ ./cmd/$@
|
|
||||||
|
|
||||||
|
|
||||||
.PHONY: test
|
|
||||||
test:
|
|
||||||
$(GO) get -t .
|
|
||||||
$(GO) test -v .
|
|
||||||
|
|
||||||
|
|
||||||
.PHONY: godep-save
|
|
||||||
godep-save:
|
|
||||||
$(GODEP) save $(PACKAGES) $(COMMANDS)
|
|
||||||
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
clean:
|
|
||||||
rm -f $(BINARIES)
|
|
||||||
|
|
||||||
|
|
||||||
.PHONY: re
|
|
||||||
re: clean all
|
|
||||||
|
|
||||||
|
|
||||||
.PHONY: convey
|
|
||||||
convey:
|
|
||||||
$(GO) get github.com/smartystreets/goconvey
|
|
||||||
goconvey -cover -port=$(CONVEY_PORT) -workDir="$(realpath .)" -depth=1
|
|
||||||
|
|
||||||
|
|
||||||
.PHONY: cover
|
|
||||||
cover: profile.out
|
|
||||||
|
|
||||||
|
|
||||||
profile.out: $(SOURCES)
|
|
||||||
rm -f $@
|
|
||||||
$(GO) test -covermode=count -coverpkg=. -coverprofile=$@ .
|
|
||||||
|
|
||||||
|
|
||||||
.PHONY: docker-build
|
|
||||||
docker-build:
|
|
||||||
go get github.com/laher/goxc
|
|
||||||
rm -rf contrib/docker/linux_386
|
|
||||||
for binary in $(BINARIES); do \
|
|
||||||
goxc -bc="linux,386" -d . -pv contrib/docker -n $$binary xc; \
|
|
||||||
mv contrib/docker/linux_386/$$binary contrib/docker/entrypoint; \
|
|
||||||
docker build -t $(USER)/$$binary contrib/docker; \
|
|
||||||
docker run -it --rm $(USER)/$$binary || true; \
|
|
||||||
docker inspect --type=image --format="{{ .Id }}" moul/$$binary || true; \
|
|
||||||
echo "Now you can run 'docker push $(USER)/$$binary'"; \
|
|
||||||
done
|
|
|
@ -1,201 +0,0 @@
|
||||||
# gotty-client
|
|
||||||
:wrench: Terminal client for [GoTTY](https://github.com/yudai/gotty).
|
|
||||||
|
|
||||||
![](https://raw.githubusercontent.com/moul/gotty-client/master/resources/gotty-client.png)
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/moul/gotty-client.svg?branch=master)](https://travis-ci.org/moul/gotty-client)
|
|
||||||
[![GoDoc](https://godoc.org/github.com/moul/gotty-client?status.svg)](https://godoc.org/github.com/moul/gotty-client)
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
┌─────────────────┐
|
|
||||||
┌──────▶│ /bin/bash │
|
|
||||||
│ └─────────────────┘
|
|
||||||
┌──────────────┐ ┌──────────┐
|
|
||||||
│ │ │ Gotty │
|
|
||||||
┌───────┐ ┌──▶│ Browser │───────┐ │ │
|
|
||||||
│ │ │ │ │ │ │ │
|
|
||||||
│ │ │ └──────────────┘ │ │ │ ┌─────────────────┐
|
|
||||||
│ Bob │───┤ websockets─▶│ │─▶│ emacs /var/www │
|
|
||||||
│ │ │ ╔═ ══ ══ ══ ══ ╗ │ │ │ └─────────────────┘
|
|
||||||
│ │ │ ║ ║ │ │ │
|
|
||||||
└───────┘ └──▶║ gotty-client ───────┘ │ │
|
|
||||||
║ │ │
|
|
||||||
╚═ ══ ══ ══ ══ ╝ └──────────┘
|
|
||||||
│ ┌─────────────────┐
|
|
||||||
└──────▶│ tmux attach │
|
|
||||||
└─────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
Server side ([GoTTY](https://github.com/yudai/gotty))
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ gotty -p 9191 sh -c 'while true; do date; sleep 1; done'
|
|
||||||
2015/08/24 18:54:31 Server is starting with command: sh -c while true; do date; sleep 1; done
|
|
||||||
2015/08/24 18:54:31 URL: http://[::1]:9191/
|
|
||||||
2015/08/24 18:54:34 GET /ws
|
|
||||||
2015/08/24 18:54:34 New client connected: 127.0.0.1:61811
|
|
||||||
2015/08/24 18:54:34 Command is running for client 127.0.0.1:61811 with PID 64834
|
|
||||||
2015/08/24 18:54:39 Command exited for: 127.0.0.1:61811
|
|
||||||
2015/08/24 18:54:39 Connection closed: 127.0.0.1:61811
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
**Client side**
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ gotty-client http://localhost:9191/
|
|
||||||
INFO[0000] New title: GoTTY - sh -c while true; do date; sleep 1; done (jean-michel-van-damme.local)
|
|
||||||
WARN[0000] Unhandled protocol message: json pref: 2{}
|
|
||||||
Mon Aug 24 18:54:34 CEST 2015
|
|
||||||
Mon Aug 24 18:54:35 CEST 2015
|
|
||||||
Mon Aug 24 18:54:36 CEST 2015
|
|
||||||
Mon Aug 24 18:54:37 CEST 2015
|
|
||||||
Mon Aug 24 18:54:38 CEST 2015
|
|
||||||
^C
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ gotty-client -h
|
|
||||||
NAME:
|
|
||||||
gotty-client - GoTTY client for your terminal
|
|
||||||
|
|
||||||
USAGE:
|
|
||||||
gotty-client [global options] command [command options] GOTTY_URL
|
|
||||||
|
|
||||||
VERSION:
|
|
||||||
1.3.0+
|
|
||||||
|
|
||||||
AUTHOR(S):
|
|
||||||
Manfred Touron <https://github.com/moul/gotty-client>
|
|
||||||
|
|
||||||
COMMANDS:
|
|
||||||
help, h Shows a list of commands or help for one command
|
|
||||||
|
|
||||||
GLOBAL OPTIONS:
|
|
||||||
--debug, -D Enable debug mode [$GOTTY_CLIENT_DEBUG]
|
|
||||||
--help, -h show help
|
|
||||||
--version, -v print the version
|
|
||||||
```
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
Install latest version using Golang (recommended)
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ go get github.com/moul/gotty-client/cmd/gotty-client
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Install latest version using Homebrew (Mac OS X)
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ brew install https://raw.githubusercontent.com/moul/gotty-client/master/contrib/homebrew/gotty-client.rb --HEAD
|
|
||||||
```
|
|
||||||
|
|
||||||
or the latest released version
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ brew install https://raw.githubusercontent.com/moul/gotty-client/master/contrib/homebrew/gotty-client.rb
|
|
||||||
```
|
|
||||||
|
|
||||||
## Changelog
|
|
||||||
|
|
||||||
### master (unreleased)
|
|
||||||
|
|
||||||
* No entry
|
|
||||||
|
|
||||||
[full commits list](https://github.com/moul/gotty-client/compare/v1.6.1...master)
|
|
||||||
|
|
||||||
### [v1.6.1](https://github.com/moul/gotty-client/releases/tag/v1.6.1) (2017-01-19)
|
|
||||||
|
|
||||||
* Do not exit on EOF ([#45](https://github.com/moul/gotty-client/pull/45)) ([@gurjeet](https://github.com/gurjeet))
|
|
||||||
|
|
||||||
[full commits list](https://github.com/moul/gotty-client/compare/v1.6.0...v1.6.1)
|
|
||||||
|
|
||||||
### [v1.6.0](https://github.com/moul/gotty-client/releases/tag/v1.6.0) (2016-05-23)
|
|
||||||
|
|
||||||
* Support of `--use-proxy-from-env` (Add Proxy support) ([#36](https://github.com/moul/gotty-client/pull/36)) ([@byung2](https://github.com/byung2))
|
|
||||||
* Add debug mode ([#18](https://github.com/moul/gotty-client/issues/18))
|
|
||||||
* Fix argument passing ([#16](https://github.com/moul/gotty-client/issues/16))
|
|
||||||
* Remove warnings + golang fixes and refactoring ([@QuentinPerez](https://github.com/QuentinPerez))
|
|
||||||
|
|
||||||
[full commits list](https://github.com/moul/gotty-client/compare/v1.5.0...v1.6.0)
|
|
||||||
|
|
||||||
### [v1.5.0](https://github.com/moul/gotty-client/releases/tag/v1.5.0) (2016-02-18)
|
|
||||||
|
|
||||||
* Add autocomplete support ([#19](https://github.com/moul/gotty-client/issues/19))
|
|
||||||
* Switch from `Party` to `Godep`
|
|
||||||
* Fix terminal data being interpreted as format string ([#34](https://github.com/moul/gotty-client/pull/34)) ([@mickael9](https://github.com/mickael9))
|
|
||||||
|
|
||||||
[full commits list](https://github.com/moul/gotty-client/compare/v1.4.0...v1.5.0)
|
|
||||||
|
|
||||||
### [v1.4.0](https://github.com/moul/gotty-client/releases/tag/v1.4.0) (2015-12-09)
|
|
||||||
|
|
||||||
* Remove solaris,plan9,nacl for `.goxc.json`
|
|
||||||
* Add an error if the go version is lower than 1.5
|
|
||||||
* Flexible parsing of the input URL
|
|
||||||
* Add tests
|
|
||||||
* Support of `--skip-tls-verify`
|
|
||||||
|
|
||||||
[full commits list](https://github.com/moul/gotty-client/compare/v1.3.0...v1.4.0)
|
|
||||||
|
|
||||||
### [v1.3.0](https://github.com/moul/gotty-client/releases/tag/v1.3.0) (2015-10-27)
|
|
||||||
|
|
||||||
* Fix `connected` state when using `Connect()` + `Loop()` methods
|
|
||||||
* Add `ExitLoop` which allow to exit from `Loop` function
|
|
||||||
|
|
||||||
[full commits list](https://github.com/moul/gotty-client/compare/v1.2.0...v1.3.0)
|
|
||||||
|
|
||||||
### [v1.2.0](https://github.com/moul/gotty-client/releases/tag/v1.2.0) (2015-10-23)
|
|
||||||
|
|
||||||
* Removed an annoying warning when exiting connection ([#22](https://github.com/moul/gotty-client/issues/22)) ([@QuentinPerez](https://github.com/QuentinPerez))
|
|
||||||
* Add the ability to configure alternative stdout ([#21](https://github.com/moul/gotty-client/issues/21)) ([@QuentinPerez](https://github.com/QuentinPerez))
|
|
||||||
* Refactored the goroutine system with select, improve speed and stability ([@QuentinPerez](https://github.com/QuentinPerez))
|
|
||||||
* Add debug mode (`--debug`/`-D`) ([#18](https://github.com/moul/gotty-client/issues/18))
|
|
||||||
* Improve error message when connecting by checking HTTP status code
|
|
||||||
* Fix arguments passing ([#16](https://github.com/moul/gotty-client/issues/16))
|
|
||||||
* Dropped support for golang<1.5
|
|
||||||
* Small fixes
|
|
||||||
|
|
||||||
[full commits list](https://github.com/moul/gotty-client/compare/v1.1.0...v1.2.0)
|
|
||||||
|
|
||||||
### [v1.1.0](https://github.com/moul/gotty-client/releases/tag/v1.1.0) (2015-10-10)
|
|
||||||
|
|
||||||
* Handling arguments + using mutexes (thanks to [@QuentinPerez](https://github.com/QuentinPerez))
|
|
||||||
* Add logo ([#9](https://github.com/moul/gotty-client/issues/9))
|
|
||||||
* Using codegansta/cli for CLI parsing ([#3](https://github.com/moul/gotty-client/issues/3))
|
|
||||||
* Fix panic when running on older GoTTY server ([#13](https://github.com/moul/gotty-client/issues/13))
|
|
||||||
* Add 'homebrew support' ([#1](https://github.com/moul/gotty-client/issues/1))
|
|
||||||
* Add Changelog ([#5](https://github.com/moul/gotty-client/issues/5))
|
|
||||||
* Add GOXC configuration to build binaries for multiple architectures ([#2](https://github.com/moul/gotty-client/issues/2))
|
|
||||||
|
|
||||||
[full commits list](https://github.com/moul/gotty-client/compare/v1.0.1...v1.1.0)
|
|
||||||
|
|
||||||
### [v1.0.1](https://github.com/moul/gotty-client/releases/tag/v1.0.1) (2015-09-27)
|
|
||||||
|
|
||||||
* Using party to manage dependencies
|
|
||||||
|
|
||||||
[full commits list](https://github.com/moul/gotty-client/compare/v1.0.0...v1.0.1)
|
|
||||||
|
|
||||||
### [v1.0.0](https://github.com/moul/gotty-client/releases/tag/v1.0.0) (2015-09-27)
|
|
||||||
|
|
||||||
Compatible with [GoTTY](https://github.com/yudai/gotty) version: [v0.0.10](https://github.com/yudai/gotty/releases/tag/v0.0.10)
|
|
||||||
|
|
||||||
#### Features
|
|
||||||
|
|
||||||
* Support **basic-auth**
|
|
||||||
* Support **terminal-(re)size**
|
|
||||||
* Support **write**
|
|
||||||
* Support **title**
|
|
||||||
* Support **custom URI**
|
|
||||||
|
|
||||||
[full commits list](https://github.com/moul/gotty-client/compare/cf0c1146c7ce20fe0bd65764c13253bc575cd43a...v1.0.0)
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MIT
|
|
|
@ -1,34 +0,0 @@
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package gottyclient
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func notifySignalSIGWINCH(c chan<- os.Signal) {
|
|
||||||
signal.Notify(c, syscall.SIGWINCH)
|
|
||||||
}
|
|
||||||
|
|
||||||
func resetSignalSIGWINCH() {
|
|
||||||
signal.Reset(syscall.SIGWINCH)
|
|
||||||
}
|
|
||||||
|
|
||||||
func syscallTIOCGWINSZ() ([]byte, error) {
|
|
||||||
ws := winsize{}
|
|
||||||
|
|
||||||
syscall.Syscall(syscall.SYS_IOCTL,
|
|
||||||
uintptr(0), uintptr(syscall.TIOCGWINSZ),
|
|
||||||
uintptr(unsafe.Pointer(&ws)))
|
|
||||||
|
|
||||||
b, err := json.Marshal(ws)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("json.Marshal error: %v", err)
|
|
||||||
}
|
|
||||||
return b, err
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
package gottyclient
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
func notifySignalSIGWINCH(c chan<- os.Signal) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func resetSignalSIGWINCH() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func syscallTIOCGWINSZ() ([]byte, error) {
|
|
||||||
return nil, errors.New("SIGWINCH isn't supported on this ARCH")
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
hash: 5ba4ef945563e8e85097f2699126b1577f9c667fdbbcdd71604e553ab7dd2a03
|
|
||||||
updated: 2017-02-04T23:03:32.03375088+01:00
|
|
||||||
imports:
|
|
||||||
- name: github.com/codegangsta/cli
|
|
||||||
version: 2ae9042f5bcbaf15b01229f8395ba8e72e01bded
|
|
||||||
- name: github.com/creack/goselect
|
|
||||||
version: 40085cf5fd629ccd88dc328895f1f137d09a1de4
|
|
||||||
- name: github.com/gopherjs/gopherjs
|
|
||||||
version: db27c7c470d7404b6b201f82d6fd4821260bd13e
|
|
||||||
subpackages:
|
|
||||||
- js
|
|
||||||
- name: github.com/gorilla/websocket
|
|
||||||
version: 1f512fc3f05332ba7117626cdfb4e07474e58e60
|
|
||||||
- name: github.com/jtolds/gls
|
|
||||||
version: 8ddce2a84170772b95dd5d576c48d517b22cac63
|
|
||||||
- name: github.com/Sirupsen/logrus
|
|
||||||
version: cd7d1bbe41066b6c1f19780f895901052150a575
|
|
||||||
- name: github.com/smartystreets/assertions
|
|
||||||
version: 40711f7748186bbf9c99977cd89f21ce1a229447
|
|
||||||
subpackages:
|
|
||||||
- internal/go-render/render
|
|
||||||
- internal/oglematchers
|
|
||||||
- name: github.com/smartystreets/goconvey
|
|
||||||
version: d4c757aa9afd1e2fc1832aaab209b5794eb336e1
|
|
||||||
subpackages:
|
|
||||||
- convey
|
|
||||||
- convey/gotest
|
|
||||||
- convey/reporting
|
|
||||||
- name: golang.org/x/crypto
|
|
||||||
version: 5bcd134fee4dd1475da17714aac19c0aa0142e2f
|
|
||||||
subpackages:
|
|
||||||
- ssh/terminal
|
|
||||||
- name: golang.org/x/sys
|
|
||||||
version: d4feaf1a7e61e1d9e79e6c4e76c6349e9cab0a03
|
|
||||||
subpackages:
|
|
||||||
- unix
|
|
||||||
testImports: []
|
|
|
@ -1,35 +0,0 @@
|
||||||
package: github.com/moul/gotty-client
|
|
||||||
import:
|
|
||||||
- package: github.com/Sirupsen/logrus
|
|
||||||
version: cd7d1bbe41066b6c1f19780f895901052150a575
|
|
||||||
- package: github.com/codegangsta/cli
|
|
||||||
version: 2ae9042f5bcbaf15b01229f8395ba8e72e01bded
|
|
||||||
- package: github.com/creack/goselect
|
|
||||||
version: 40085cf5fd629ccd88dc328895f1f137d09a1de4
|
|
||||||
- package: github.com/gopherjs/gopherjs
|
|
||||||
version: db27c7c470d7404b6b201f82d6fd4821260bd13e
|
|
||||||
subpackages:
|
|
||||||
- js
|
|
||||||
- package: github.com/gorilla/websocket
|
|
||||||
version: 1f512fc3f05332ba7117626cdfb4e07474e58e60
|
|
||||||
- package: github.com/jtolds/gls
|
|
||||||
version: 8ddce2a84170772b95dd5d576c48d517b22cac63
|
|
||||||
- package: github.com/smartystreets/assertions
|
|
||||||
version: 40711f7748186bbf9c99977cd89f21ce1a229447
|
|
||||||
subpackages:
|
|
||||||
- internal/go-render/render
|
|
||||||
- internal/oglematchers
|
|
||||||
- package: github.com/smartystreets/goconvey
|
|
||||||
version: d4c757aa9afd1e2fc1832aaab209b5794eb336e1
|
|
||||||
subpackages:
|
|
||||||
- convey
|
|
||||||
- convey/gotest
|
|
||||||
- convey/reporting
|
|
||||||
- package: golang.org/x/crypto
|
|
||||||
version: 5bcd134fee4dd1475da17714aac19c0aa0142e2f
|
|
||||||
subpackages:
|
|
||||||
- ssh/terminal
|
|
||||||
- package: golang.org/x/sys
|
|
||||||
version: d4feaf1a7e61e1d9e79e6c4e76c6349e9cab0a03
|
|
||||||
subpackages:
|
|
||||||
- unix
|
|
|
@ -1,469 +0,0 @@
|
||||||
package gottyclient
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/creack/goselect"
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetAuthTokenURL transforms a GoTTY http URL to its AuthToken file URL
|
|
||||||
func GetAuthTokenURL(httpURL string) (*url.URL, *http.Header, error) {
|
|
||||||
header := http.Header{}
|
|
||||||
target, err := url.Parse(httpURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
target.Path = strings.TrimLeft(target.Path+"auth_token.js", "/")
|
|
||||||
|
|
||||||
if target.User != nil {
|
|
||||||
header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(target.User.String())))
|
|
||||||
target.User = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return target, &header, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetURLQuery returns url.query
|
|
||||||
func GetURLQuery(rawurl string) (url.Values, error) {
|
|
||||||
target, err := url.Parse(rawurl)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return target.Query(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetWebsocketURL transforms a GoTTY http URL to its WebSocket URL
|
|
||||||
func GetWebsocketURL(httpURL string) (*url.URL, *http.Header, error) {
|
|
||||||
header := http.Header{}
|
|
||||||
target, err := url.Parse(httpURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if target.Scheme == "https" {
|
|
||||||
target.Scheme = "wss"
|
|
||||||
} else {
|
|
||||||
target.Scheme = "ws"
|
|
||||||
}
|
|
||||||
|
|
||||||
target.Path = strings.TrimLeft(target.Path+"ws", "/")
|
|
||||||
|
|
||||||
if target.User != nil {
|
|
||||||
header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(target.User.String())))
|
|
||||||
target.User = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return target, &header, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Client struct {
|
|
||||||
Dialer *websocket.Dialer
|
|
||||||
Conn *websocket.Conn
|
|
||||||
URL string
|
|
||||||
WriteMutex *sync.Mutex
|
|
||||||
Output io.Writer
|
|
||||||
poison chan bool
|
|
||||||
SkipTLSVerify bool
|
|
||||||
UseProxyFromEnv bool
|
|
||||||
Connected bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type querySingleType struct {
|
|
||||||
AuthToken string `json:"AuthToken"`
|
|
||||||
Arguments string `json:"Arguments"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) write(data []byte) error {
|
|
||||||
c.WriteMutex.Lock()
|
|
||||||
defer c.WriteMutex.Unlock()
|
|
||||||
return c.Conn.WriteMessage(websocket.TextMessage, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAuthToken retrieves an Auth Token from dynamic auth_token.js file
|
|
||||||
func (c *Client) GetAuthToken() (string, error) {
|
|
||||||
target, header, err := GetAuthTokenURL(c.URL)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.Debugf("Fetching auth token auth-token: %q", target.String())
|
|
||||||
req, err := http.NewRequest("GET", target.String(), nil)
|
|
||||||
req.Header = *header
|
|
||||||
tr := &http.Transport{}
|
|
||||||
if c.SkipTLSVerify {
|
|
||||||
conf := &tls.Config{InsecureSkipVerify: true}
|
|
||||||
tr.TLSClientConfig = conf
|
|
||||||
}
|
|
||||||
if c.UseProxyFromEnv {
|
|
||||||
tr.Proxy = http.ProxyFromEnvironment
|
|
||||||
}
|
|
||||||
client := &http.Client{Transport: tr}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch resp.StatusCode {
|
|
||||||
case 200:
|
|
||||||
// Everything is OK
|
|
||||||
default:
|
|
||||||
return "", fmt.Errorf("unknown status code: %d (%s)", resp.StatusCode, http.StatusText(resp.StatusCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
re := regexp.MustCompile("var gotty_auth_token = '(.*)'")
|
|
||||||
output := re.FindStringSubmatch(string(body))
|
|
||||||
if len(output) == 0 {
|
|
||||||
return "", fmt.Errorf("Cannot fetch GoTTY auth-token, please upgrade your GoTTY server.")
|
|
||||||
}
|
|
||||||
|
|
||||||
return output[1], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect tries to dial a websocket server
|
|
||||||
func (c *Client) Connect() error {
|
|
||||||
// Retrieve AuthToken
|
|
||||||
authToken, err := c.GetAuthToken()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logrus.Debugf("Auth-token: %q", authToken)
|
|
||||||
|
|
||||||
// Open WebSocket connection
|
|
||||||
target, header, err := GetWebsocketURL(c.URL)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logrus.Debugf("Connecting to websocket: %q", target.String())
|
|
||||||
if c.SkipTLSVerify {
|
|
||||||
c.Dialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
|
||||||
}
|
|
||||||
if c.UseProxyFromEnv {
|
|
||||||
c.Dialer.Proxy = http.ProxyFromEnvironment
|
|
||||||
}
|
|
||||||
conn, _, err := c.Dialer.Dial(target.String(), *header)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.Conn = conn
|
|
||||||
c.Connected = true
|
|
||||||
|
|
||||||
// Pass arguments and auth-token
|
|
||||||
query, err := GetURLQuery(c.URL)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
querySingle := querySingleType{
|
|
||||||
Arguments: "?" + query.Encode(),
|
|
||||||
AuthToken: authToken,
|
|
||||||
}
|
|
||||||
json, err := json.Marshal(querySingle)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("Failed to parse init message %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Send Json
|
|
||||||
logrus.Debugf("Sending arguments and auth-token")
|
|
||||||
err = c.write(json)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
go c.pingLoop()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) pingLoop() {
|
|
||||||
for {
|
|
||||||
logrus.Debugf("Sending ping")
|
|
||||||
c.write([]byte("1"))
|
|
||||||
time.Sleep(30 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close will nicely close the dialer
|
|
||||||
func (c *Client) Close() {
|
|
||||||
c.Conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitLoop will kill all goroutines launched by c.Loop()
|
|
||||||
// ExitLoop() -> wait Loop() -> Close()
|
|
||||||
func (c *Client) ExitLoop() {
|
|
||||||
fname := "ExitLoop"
|
|
||||||
openPoison(fname, c.poison)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop will look indefinitely for new messages
|
|
||||||
func (c *Client) Loop() error {
|
|
||||||
if !c.Connected {
|
|
||||||
err := c.Connect()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wg := &sync.WaitGroup{}
|
|
||||||
|
|
||||||
wg.Add(1)
|
|
||||||
go c.termsizeLoop(wg)
|
|
||||||
|
|
||||||
wg.Add(1)
|
|
||||||
go c.readLoop(wg)
|
|
||||||
|
|
||||||
wg.Add(1)
|
|
||||||
go c.writeLoop(wg)
|
|
||||||
|
|
||||||
/* Wait for all of the above goroutines to finish */
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
logrus.Debug("Client.Loop() exiting")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type winsize struct {
|
|
||||||
Rows uint16 `json:"rows"`
|
|
||||||
Columns uint16 `json:"columns"`
|
|
||||||
// unused
|
|
||||||
x uint16
|
|
||||||
y uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
type posionReason int
|
|
||||||
|
|
||||||
const (
|
|
||||||
committedSuicide = iota
|
|
||||||
killed
|
|
||||||
)
|
|
||||||
|
|
||||||
func openPoison(fname string, poison chan bool) posionReason {
|
|
||||||
logrus.Debug(fname + " suicide")
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The close() may raise panic if multiple goroutines commit suicide at the
|
|
||||||
* same time. Prevent that panic from bubbling up.
|
|
||||||
*/
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
logrus.Debug("Prevented panic() of simultaneous suicides", r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
/* Signal others to die */
|
|
||||||
close(poison)
|
|
||||||
return committedSuicide
|
|
||||||
}
|
|
||||||
|
|
||||||
func die(fname string, poison chan bool) posionReason {
|
|
||||||
logrus.Debug(fname + " died")
|
|
||||||
|
|
||||||
wasOpen := <-poison
|
|
||||||
if wasOpen {
|
|
||||||
logrus.Error("ERROR: The channel was open when it wasn't suppoed to be")
|
|
||||||
}
|
|
||||||
|
|
||||||
return killed
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) termsizeLoop(wg *sync.WaitGroup) posionReason {
|
|
||||||
|
|
||||||
defer wg.Done()
|
|
||||||
fname := "termsizeLoop"
|
|
||||||
|
|
||||||
ch := make(chan os.Signal, 1)
|
|
||||||
notifySignalSIGWINCH(ch)
|
|
||||||
defer resetSignalSIGWINCH()
|
|
||||||
|
|
||||||
for {
|
|
||||||
if b, err := syscallTIOCGWINSZ(); err != nil {
|
|
||||||
logrus.Warn(err)
|
|
||||||
} else {
|
|
||||||
if err = c.write(append([]byte("2"), b...)); err != nil {
|
|
||||||
logrus.Warnf("ws.WriteMessage failed: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-c.poison:
|
|
||||||
/* Somebody poisoned the well; die */
|
|
||||||
return die(fname, c.poison)
|
|
||||||
case <-ch:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type exposeFd interface {
|
|
||||||
Fd() uintptr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) writeLoop(wg *sync.WaitGroup) posionReason {
|
|
||||||
|
|
||||||
defer wg.Done()
|
|
||||||
fname := "writeLoop"
|
|
||||||
|
|
||||||
buff := make([]byte, 128)
|
|
||||||
oldState, err := terminal.MakeRaw(0)
|
|
||||||
if err == nil {
|
|
||||||
defer terminal.Restore(0, oldState)
|
|
||||||
}
|
|
||||||
|
|
||||||
rdfs := &goselect.FDSet{}
|
|
||||||
reader := io.Reader(os.Stdin)
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-c.poison:
|
|
||||||
/* Somebody poisoned the well; die */
|
|
||||||
return die(fname, c.poison)
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
rdfs.Zero()
|
|
||||||
rdfs.Set(reader.(exposeFd).Fd())
|
|
||||||
err := goselect.Select(1, rdfs, nil, nil, 50*time.Millisecond)
|
|
||||||
if err != nil {
|
|
||||||
return openPoison(fname, c.poison)
|
|
||||||
}
|
|
||||||
if rdfs.IsSet(reader.(exposeFd).Fd()) {
|
|
||||||
size, err := reader.Read(buff)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
// Send EOF to GoTTY
|
|
||||||
|
|
||||||
// Send 'Input' marker, as defined in GoTTY::client_context.go,
|
|
||||||
// followed by EOT (a translation of Ctrl-D for terminals)
|
|
||||||
err = c.write(append([]byte("0"), byte(4)))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return openPoison(fname, c.poison)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
return openPoison(fname, c.poison)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if size <= 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
data := buff[:size]
|
|
||||||
err = c.write(append([]byte("0"), data...))
|
|
||||||
if err != nil {
|
|
||||||
return openPoison(fname, c.poison)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) readLoop(wg *sync.WaitGroup) posionReason {
|
|
||||||
|
|
||||||
defer wg.Done()
|
|
||||||
fname := "readLoop"
|
|
||||||
|
|
||||||
type MessageNonBlocking struct {
|
|
||||||
Data []byte
|
|
||||||
Err error
|
|
||||||
}
|
|
||||||
msgChan := make(chan MessageNonBlocking)
|
|
||||||
|
|
||||||
for {
|
|
||||||
go func() {
|
|
||||||
_, data, err := c.Conn.ReadMessage()
|
|
||||||
msgChan <- MessageNonBlocking{Data: data, Err: err}
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-c.poison:
|
|
||||||
/* Somebody poisoned the well; die */
|
|
||||||
return die(fname, c.poison)
|
|
||||||
case msg := <-msgChan:
|
|
||||||
if msg.Err != nil {
|
|
||||||
|
|
||||||
if _, ok := msg.Err.(*websocket.CloseError); !ok {
|
|
||||||
logrus.Warnf("c.Conn.ReadMessage: %v", msg.Err)
|
|
||||||
}
|
|
||||||
return openPoison(fname, c.poison)
|
|
||||||
}
|
|
||||||
if len(msg.Data) == 0 {
|
|
||||||
|
|
||||||
logrus.Warnf("An error has occured")
|
|
||||||
return openPoison(fname, c.poison)
|
|
||||||
}
|
|
||||||
switch msg.Data[0] {
|
|
||||||
case '0': // data
|
|
||||||
buf, err := base64.StdEncoding.DecodeString(string(msg.Data[1:]))
|
|
||||||
if err != nil {
|
|
||||||
logrus.Warnf("Invalid base64 content: %q", msg.Data[1:])
|
|
||||||
break
|
|
||||||
}
|
|
||||||
c.Output.Write(buf)
|
|
||||||
case '1': // pong
|
|
||||||
case '2': // new title
|
|
||||||
newTitle := string(msg.Data[1:])
|
|
||||||
fmt.Fprintf(c.Output, "\033]0;%s\007", newTitle)
|
|
||||||
case '3': // json prefs
|
|
||||||
logrus.Debugf("Unhandled protocol message: json pref: %s", string(msg.Data[1:]))
|
|
||||||
case '4': // autoreconnect
|
|
||||||
logrus.Debugf("Unhandled protocol message: autoreconnect: %s", string(msg.Data))
|
|
||||||
default:
|
|
||||||
logrus.Warnf("Unhandled protocol message: %s", string(msg.Data))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetOutput changes the output stream
|
|
||||||
func (c *Client) SetOutput(w io.Writer) {
|
|
||||||
c.Output = w
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseURL parses an URL which may be incomplete and tries to standardize it
|
|
||||||
func ParseURL(input string) (string, error) {
|
|
||||||
parsed, err := url.Parse(input)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
switch parsed.Scheme {
|
|
||||||
case "http", "https":
|
|
||||||
// everything is ok
|
|
||||||
default:
|
|
||||||
return ParseURL(fmt.Sprintf("http://%s", input))
|
|
||||||
}
|
|
||||||
return parsed.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewClient returns a GoTTY client object
|
|
||||||
func NewClient(inputURL string) (*Client, error) {
|
|
||||||
url, err := ParseURL(inputURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &Client{
|
|
||||||
Dialer: &websocket.Dialer{},
|
|
||||||
URL: url,
|
|
||||||
WriteMutex: &sync.Mutex{},
|
|
||||||
Output: os.Stdout,
|
|
||||||
poison: make(chan bool),
|
|
||||||
}, nil
|
|
||||||
}
|
|
|
@ -2445,12 +2445,6 @@
|
||||||
"revision": "609b752a95effbbef26d134ac18ed6f57e01b98e",
|
"revision": "609b752a95effbbef26d134ac18ed6f57e01b98e",
|
||||||
"revisionTime": "2016-02-22T16:21:17Z"
|
"revisionTime": "2016-02-22T16:21:17Z"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"checksumSHA1": "WcXDSYIAP73RAvy22iD57nE/peI=",
|
|
||||||
"path": "github.com/moul/gotty-client",
|
|
||||||
"revision": "99224eea3278d662fce9124bb2bf6c2bb39f5160",
|
|
||||||
"revisionTime": "2017-02-05T09:54:39Z"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"checksumSHA1": "/iig5lYSPCL3C8J7e4nTAevYNDE=",
|
"checksumSHA1": "/iig5lYSPCL3C8J7e4nTAevYNDE=",
|
||||||
"comment": "v0.2.0-8-g841842b",
|
"comment": "v0.2.0-8-g841842b",
|
||||||
|
|
Loading…
Reference in New Issue