├── .circleci └── config.yml ├── .gitignore ├── LICENSE ├── README.md ├── go.mod ├── go.sum ├── makefile ├── viewb.go └── viewb_test.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Golang CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-go/ for more details 4 | version: 2 5 | jobs: 6 | build: 7 | docker: 8 | # specify the version 9 | - image: circleci/golang:1.11 10 | 11 | # Specify service dependencies here if necessary 12 | # CircleCI maintains a library of pre-built images 13 | # documented at https://circleci.com/docs/2.0/circleci-images/ 14 | # - image: circleci/postgres:9.4 15 | 16 | #### TEMPLATE_NOTE: go expects specific checkout path representing url 17 | #### expecting it in the form of 18 | #### /go/src/github.com/circleci/go-tool 19 | #### /go/src/bitbucket.org/circleci/go-tool 20 | working_directory: /go/src/github.com/kurehajime/viewb 21 | steps: 22 | - checkout 23 | 24 | # specify any bash command here prefixed with `run: ` 25 | - run: go get -v -t -d ./... 26 | - run: go test -v ./... -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | bin/ 3 | 4 | *.zip 5 | 6 | viewb 7 | 8 | .vscode/tasks.json 9 | .DS_Store 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 kurehajime 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # viewb 2 | 3 | [![CircleCI](https://circleci.com/gh/kurehajime/viewb.svg?style=svg)](https://circleci.com/gh/kurehajime/viewb) 4 | 5 | Convert the command to a web server 6 | 7 | ![viewb](https://cloud.githubusercontent.com/assets/4569916/9249386/589c2126-41ff-11e5-9e6f-b12daa6aadf0.png) 8 | 9 | ## Usage 10 | 11 | ``` 12 | $ viewb 13 | ``` 14 | 15 | Option: 16 | -p=\: Port(default:8080) 17 | -h: Prints the first N lines.if minus value then prints the last N lines. 18 | -o: Open web browser 19 | -user=\: Basic Authentication user name 20 | -pass=\: Basic Authentication password 21 | -e=\: input encoding 22 | 23 | 24 | ## How to install 25 | 26 | [Download](https://github.com/kurehajime/viewb/releases) 27 | 28 | or 29 | 30 | Build yourself (Go lang). 31 | 32 | ``` 33 | go install github.com/kurehajime/viewb@latest 34 | ``` 35 | 36 | 37 | ## Example 38 | 39 | ##### Example 1 :Command to web server 40 | 41 | ```sh 42 | $ viewb -p 8080 ls -la 43 | 44 | http://localhost:8080 45 | Stop: Ctrl+C 46 | ``` 47 | 48 | Open in browser http://localhost:8080 49 | 50 | ``` 51 | total 32 52 | drwxr-xr-x 6 user staff 204 8 6 20:19 . 53 | drwx------+ 11 user staff 374 8 6 20:17 .. 54 | -rw-r--r--@ 1 user staff 6148 8 6 20:19 .DS_Store 55 | -rw-r--r-- 1 user staff 5 8 6 20:18 Untitled-1.txt 56 | -rw-r--r-- 1 user staff 1557 8 6 20:19 Untitled-2.txt 57 | drwxr-xr-x 2 user staff 68 8 6 20:19 test 58 | ``` 59 | 60 | ##### Example 2 :Script to web server 61 | 62 | ```sh 63 | $ viewb ./HelloworldAndPingOne.sh 64 | 65 | http://localhost:8080 66 | Stop: Ctrl+C 67 | ``` 68 | 69 | Open in browser http://localhost:8080 70 | 71 | ``` 72 | Hello World! 73 | PING 8.8.8.8 (8.8.8.8): 56 data bytes 74 | 64 bytes from 8.8.8.8: icmp_seq=0 ttl=54 time=60.380 ms 75 | 76 | --- 8.8.8.8 ping statistics --- 77 | 1 packets transmitted, 1 packets received, 0.0% packet loss 78 | round-trip min/avg/max/stddev = 60.380/60.380/60.380/0.000 ms 79 | ``` 80 | 81 | ##### Example 3 :Basic Authentication 82 | 83 | ```sh 84 | $ viewb -user laputa -pass balse echo booomb! 85 | 86 | http://localhost:8080 87 | Stop: Ctrl+C 88 | ``` 89 | 90 | Open in browser http://localhost:8080 91 | And login. 92 | 93 | ##### Example 5 :open browser 94 | 95 | ```sh 96 | $ viewb -o echo yah! 97 | 98 | http://localhost:8080 99 | Stop: Ctrl+C 100 | ``` 101 | 102 | Open automatically. 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kurehajime/viewb 2 | 3 | require ( 4 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d 5 | golang.org/x/text v0.3.0 6 | ) 7 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I= 2 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 3 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 4 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 5 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | out: 2 | go get github.com/mitchellh/gox 3 | go test 4 | rm -rf ./_obj 5 | gox -output "_obj/{{.OS}}_{{.Arch}}/{{.Dir}}" ./... 6 | mv ./_obj/darwin_386 ./_obj/macos_386 7 | mv ./_obj/darwin_amd64 ./_obj/macos_amd64 8 | -find ./_obj \! -name "*.zip" -type d -exec zip -r -j {}.zip {} \; -exec rm -R -d {} \; -------------------------------------------------------------------------------- /viewb.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "net/http" 8 | "os" 9 | "os/exec" 10 | "runtime" 11 | "strconv" 12 | "strings" 13 | "time" 14 | 15 | "golang.org/x/net/html/charset" 16 | "golang.org/x/text/transform" 17 | ) 18 | 19 | var ( 20 | port int 21 | head int 22 | user string 23 | pass string 24 | com string 25 | open bool 26 | encode string 27 | ) 28 | 29 | func main() { 30 | //parse args 31 | flag.IntVar(&port, "p", 8080, "Port /default:8080") 32 | flag.IntVar(&head, "h", 0, "Prints the first N lines.If minus value then prints the last N lines.") 33 | flag.BoolVar(&open, "o", false, "Open web browser") 34 | flag.StringVar(&user, "user", "", "User (BASIC AUTH)") 35 | flag.StringVar(&pass, "pass", "", "Pass (BASIC AUTH)") 36 | flag.StringVar(&encode, "e", "utf-8", "Input encoding") 37 | flag.Usage = func() { 38 | fmt.Printf("Convert the command to a web server \n\n") 39 | flag.PrintDefaults() 40 | } 41 | flag.Parse() 42 | 43 | if len(flag.Args()) == 0 { 44 | flag.Usage() 45 | return 46 | } 47 | 48 | com = strings.Join(flag.Args(), " ") 49 | url := "http://localhost" + ":" + strconv.Itoa(port) 50 | 51 | //open web browser 52 | go func() { 53 | if open == true { 54 | time.Sleep(500 * time.Millisecond) 55 | switch runtime.GOOS { 56 | case "linux": 57 | exec.Command("xdg-open", url).Start() 58 | case "windows": 59 | exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() 60 | case "darwin": 61 | exec.Command("open", url).Start() 62 | } 63 | } 64 | }() 65 | 66 | //start server 67 | fmt.Println(url) 68 | fmt.Println("Stop: Ctrl+C") 69 | http.HandleFunc("/", handler) 70 | err := http.ListenAndServe(":"+strconv.Itoa(port), nil) 71 | if err != nil { 72 | fmt.Fprintln(os.Stderr, err.Error()) 73 | os.Exit(1) 74 | } 75 | } 76 | 77 | //handler: command result 78 | func handler(w http.ResponseWriter, r *http.Request) { 79 | w.Header().Set("Content-Type", "text/plain; charset=UTF-8") 80 | if auth(r) == false { 81 | w.Header().Set("WWW-Authenticate", `Basic realm="MY REALM"`) 82 | w.WriteHeader(401) 83 | w.Write([]byte("401 Unauthorized\n")) 84 | return 85 | } 86 | 87 | if strings.Contains(r.URL.Path, "favicon.ico") { 88 | w.WriteHeader(404) 89 | return 90 | } 91 | 92 | text, err := transEnc(cmd(com), encode) 93 | if err != nil { 94 | fmt.Fprintln(os.Stderr, err.Error()) 95 | } 96 | arr := strings.Split(text, "\n") 97 | if head > 0 && len(arr) > head { 98 | fmt.Fprint(w, strings.Join(arr[:head], "\n")) 99 | } else if head < 0 && len(arr) > -1*head { 100 | fmt.Fprint(w, strings.Join(arr[len(arr)+head-1:], "\n")) 101 | } else { 102 | fmt.Fprint(w, text) 103 | } 104 | } 105 | 106 | //exec command 107 | func cmd(commandString string) string { 108 | var command string 109 | var op string 110 | if runtime.GOOS == "windows" { 111 | command = "cmd" 112 | op = "/c" 113 | } else { 114 | command = "/bin/sh" 115 | op = "-c" 116 | } 117 | out, err := exec.Command(command, op, commandString).Output() 118 | if err != nil { 119 | return string(err.Error()) 120 | } 121 | return string(out) 122 | } 123 | 124 | //basic auth 125 | func auth(r *http.Request) bool { 126 | if user == "" || pass == "" { 127 | return true 128 | } 129 | _user, _pass, ok := r.BasicAuth() 130 | if ok == false { 131 | return false 132 | } 133 | return _user == user && _pass == pass 134 | } 135 | 136 | //trans encoding -> qiita.com/nobuhito/items/ff782f64e32f7ed95e43 137 | func transEnc(text string, encode string) (string, error) { 138 | body := []byte(text) 139 | var f []byte 140 | 141 | encodings := []string{"sjis", "utf-8"} 142 | if encode != "" { 143 | encodings = append([]string{encode}, encodings...) 144 | } 145 | for _, enc := range encodings { 146 | if enc != "" { 147 | ee, _ := charset.Lookup(enc) 148 | if ee == nil { 149 | continue 150 | } 151 | var buf bytes.Buffer 152 | ic := transform.NewWriter(&buf, ee.NewDecoder()) 153 | _, err := ic.Write(body) 154 | if err != nil { 155 | continue 156 | } 157 | err = ic.Close() 158 | if err != nil { 159 | continue 160 | } 161 | f = buf.Bytes() 162 | break 163 | } 164 | } 165 | return string(f), nil 166 | } 167 | -------------------------------------------------------------------------------- /viewb_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | func TestHandler(t *testing.T) { 11 | user = "user" 12 | pass = "pass" 13 | 14 | ts := httptest.NewServer(http.HandlerFunc(handler)) 15 | defer ts.Close() 16 | 17 | //Basic auth failure pattern 18 | res, err := http.Get(ts.URL) 19 | if err != nil { 20 | t.Error("unexpected") 21 | return 22 | } 23 | 24 | //Basic auth working ? 25 | if res.StatusCode == 200 { 26 | t.Error("Basic auth not working") 27 | return 28 | } else if res.StatusCode != 401 { 29 | t.Error("Status code error:" + string(res.StatusCode)) 30 | } 31 | 32 | //Basic auth success pattern 33 | res, err = http.Get(strings.Replace(ts.URL, "http://", "http://"+user+":"+pass+"@", 1)) 34 | if err != nil { 35 | t.Error("unexpected") 36 | return 37 | } 38 | 39 | //STATUS OK? 40 | if res.StatusCode != 200 { 41 | t.Error("Status code error:" + string(res.StatusCode)) 42 | return 43 | } 44 | } 45 | 46 | func TestCmd(t *testing.T) { 47 | //Command OK? 48 | ans := "HelloWorld" 49 | res := cmd("echo HelloWorld") 50 | if strings.Trim(ans, " \r\n") != strings.Trim(res, " \r\n") { 51 | t.Error("cmd Error:", ans, res) 52 | return 53 | } 54 | } 55 | --------------------------------------------------------------------------------