It is possible to bind a golang channel into a template - go

I have the go templates (upload.tmpl.html) like this :
<html>
<body>
<div class="container">
<ul>
<li>current fileName : {{ .fileName}} </li>
</ul>
</body>
</html>
an handler uploadHandler.go with
func UploadHandler(c *gin.Context) {
file, header, err := c.Request.FormFile("file-upload")
if err != nil {
log.Fatal("Erreur dans la récupération de fichier")
}
//...
fileName := make(chan string)
go ReadCsvFile(bytes, fileName)
go func() {
for {
log.Info(<-fileName)
}
}()
c.HTML(http.StatusOK, "upload.tmpl.html", gin.H{
"fileName": <-fileName,
})
}
and the ReadCsvFile() method like that :
func ReadCsvFile(bytesCSV []byte, fileName chan string) {
r := bytes.NewReader(bytesCSV)
reader := csv.NewReader(r)
reader.Comma = ';'
records, err := reader.ReadAll()
if err != nil {
fmt.Println("Error:", err)
return
}
db, _ := databaseApp.OpenDatabase()
defer db.Close()
for _, record := range records {
fileName <- record[0]
product := &em.Product{
Name: record[0],
//...
}
db.Create(product)
}
fileName <- "done"
}
I try to display the current fileName of each line in the template, but it is possible to bind the channel into the template like this ? Because in this way the page does not load anymore.

Use Websockets. Here are some examples:
HTML/JavaScript:
<script>
var ws= new WebSocket("ws://yoursite.com");
ws.onmessage = function (event) {
console.log(event.data);
// $('#your-element').html(event.data);
}
</script>
Go Websockets:
func websocketSenderHandler(conn *websocket.Conn){
for {
msg := <- globalChannel
conn.WriteMessage(websocket.TextMessage, msg)
}
}
More Websockets in Go: golang.org/x/net/websocket
Other Example: https://github.com/golang-samples/websocket

Related

Golang Chromedp: pdf file download without saving in server

How to chromedp pdf download without saving in server?
Below code is working for generating pdf file and saving in server side. But I want to download pdf file without saving in server side.
func PDFInvoice(c *gin.Context) {
session := sessions.Default(c)
id := c.Params.ByName("id")
token := session.Get("login_session").(string)
// create context
ctx, cancel := chromedp.NewContext(context.Background())
defer cancel()
// capture pdf
var buf []byte
url := "http://localhost:8080/invoice/" + id + "/" + token
if err := chromedp.Run(ctx, printToPDF(url, &buf)); err != nil {
log.Fatal(err)
}
buff := new(bytes.Buffer)
if _, err := buff.WriteTo(c.Writer); err != nil {
panic(err)
}
if err := os.WriteFile("sample.pdf", buf, 0o644); err != nil {
log.Fatal(err)
}
//ioutil.WriteFile("sample.pdf", buf, 0644)
c.JSON(200, id+" "+token)
}
// print a specific pdf page.
func printToPDF(urlstr string, res *[]byte) chromedp.Tasks {
return chromedp.Tasks{
chromedp.Navigate(urlstr),
chromedp.ActionFunc(func(ctx context.Context) error {
buf, _, err := page.PrintToPDF().WithPrintBackground(false).Do(ctx)
if err != nil {
return err
}
*res = buf
return nil
}),
}
}
You can write the bytes to http.ResponseWriter directly. See the demo below:
package main
import (
"context"
"log"
"net/http"
"sync"
"github.com/chromedp/cdproto/page"
"github.com/chromedp/chromedp"
)
func main() {
http.Handle("/pdf", http.HandlerFunc(servePDF))
log.Fatal(http.ListenAndServe(":8080", http.DefaultServeMux))
}
func servePDF(w http.ResponseWriter, r *http.Request) {
buf, err := createPDF()
if err != nil {
log.Fatalln(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/pdf")
w.Write(buf)
}
func createPDF() ([]byte, error) {
ctx, cancel := newTabContext()
defer cancel()
html := `<html>
<body>
<div>text</div>
<img src="https://pkg.go.dev/static/shared/gopher/package-search-700x300.jpeg"/>
<img src="https://go.dev/images/gophers/motorcycle.svg"/>
<img src="https://go.dev/images/go_google_case_study_carousel.png" />
</body>
</html>`
var buf []byte
if err := chromedp.Run(ctx,
chromedp.Navigate("about:blank"),
// set the page content and wait until the page is loaded (including its resources).
chromedp.ActionFunc(func(ctx context.Context) error {
lctx, cancel := context.WithCancel(ctx)
defer cancel()
var wg sync.WaitGroup
wg.Add(1)
chromedp.ListenTarget(lctx, func(ev interface{}) {
if _, ok := ev.(*page.EventLoadEventFired); ok {
// It's a good habit to remove the event listener if we don't need it anymore.
cancel()
wg.Done()
}
})
frameTree, err := page.GetFrameTree().Do(ctx)
if err != nil {
return err
}
if err := page.SetDocumentContent(frameTree.Frame.ID, html).Do(ctx); err != nil {
return err
}
wg.Wait()
return nil
}),
chromedp.ActionFunc(func(ctx context.Context) error {
var err error
buf, _, err = page.PrintToPDF().WithPrintBackground(false).Do(ctx)
if err != nil {
return err
}
return nil
}),
); err != nil {
return nil, err
}
return buf, nil
}
var (
browserCtx context.Context
once sync.Once
)
// newTabContext creates a tab context with the global browser context as its parent context.
//
// When tasks is run with the returned context, a new tab will be created in the browser.
func newTabContext() (context.Context, context.CancelFunc) {
once.Do(func() { initBrowser() })
if browserCtx == nil || browserCtx.Err() != nil {
log.Fatalf("browser is not available: %v", browserCtx.Err())
}
return chromedp.NewContext(browserCtx)
}
// initBrowser starts a browser in which to create new tab for running tasks.
func initBrowser() {
browserCtx, _ = chromedp.NewContext(context.Background())
// to start the browser
if err := chromedp.Run(browserCtx); err != nil {
log.Fatal(err)
}
}
Usage:
go run main.go
curl http://localhost:8080/pdf > sample.pdf
References:
https://github.com/chromedp/chromedp/issues/941
https://github.com/chromedp/chromedp/issues/836

Go HTML template

I have created a simple scraper that takes the top 10 news from a website and returns a JSON with the title and the score. I want to pass the title and the score as HTML template so I can generate a webpage. I'm not familiar with the templating Go language and I don't know how to pass the values for each of the links. Here is the HTML code that I should use and my implementation for now:
<!DOCTYPE html>
<html>
<head><linkrel="stylesheet" href="https://unpkg.com/mvp.css"
/>
</head>
<body>
<h1>{{.PageTitle}}</h1>
<ul>
{{range .Links}}
<li>{{.Title}}: {{.Score}}</li>
{{end}}
</ul>
</body>
</html>
My code:
package main
import (
"encoding/json"
"html/template"
"log"
"net/http"
"strconv"
)
type TopStories struct {
Title string `json:"title"`
Score int `json:"score"`
}
type TopStoriesPayload struct {
TopStories []TopStories
}
type NewsScraper struct {
url string
Data []TopStories
}
type templateData struct {
PageTitle string
Data []TopStories
}
func NewNewsScraper(url string) *NewsScraper {
return &NewsScraper{url: url}
}
func Top10Stories() []string {
req, err := http.NewRequest("GET", "https://hacker-news.firebaseio.com/v0/topstories.json", nil)
if err != nil {
log.Fatal(err)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
var IDs []int
json.NewDecoder(resp.Body).Decode(&IDs)
IDs = IDs[:10]
var IDsString []string
for _, id := range IDs {
IDsString = append(IDsString, strconv.Itoa(id))
}
return IDsString
}
func (n *NewsScraper) GetTopStories() {
req, err := http.NewRequest("GET", n.url, nil)
if err != nil {
log.Fatal(err)
}
for _, id := range Top10Stories() {
req.URL.Path = "/v0/item/" + id + ".json"
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
var topStory TopStories
json.NewDecoder(resp.Body).Decode(&topStory)
n.Data = append(n.Data, topStory)
}
}
//create html template handler for top stories
func HTMLHandler(w http.ResponseWriter, r *http.Request) {
scraper := NewNewsScraper("https://hacker-news.firebaseio.com")
scraper.GetTopStories()
tmpl:= template.Must(template.ParseFiles("template.html"))
data := templateData{
PageTitle: "Top Stories",
Data :[]TopStories{
//what should I put here?
},
}
tmpl.Execute(w, data)
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/top", HTMLHandler)
http.ListenAndServe(":8080", mux)
}
I see three issues with your code:
a) The template.html file should have space between link & rel
<linkrel="stylesheet" href="https://unpkg.com/mvp.css"/>
to
<link rel="stylesheet" href="https://unpkg.com/mvp.css"/>
b) The template.html file should contain .Data instead of .Links.
c) The go code should be replaced from the below
Data :[]TopStories{
//what should I put here?
},
to
Data : scraper.Data,

On message recieve from websocket without causing 'Websocket: Close 1011'

I'm trying to subscribe to an event from a client to a server. I can subscribe to the event using JavaScript but when I use Golang and the Gorilla Websocket library I get a
'websocket: close 1011 (internal server error)'
every time I start a read from the socket.
I tried using the /x/websocket library as well but when I tried to read from the socket I end up hanging and I dont get the '1011' error.
If I use this JavaScript code on my browser it works:
<html>
<head>
</head>
<body>
<script>
var socket = new WebSocket("ws://blehip:blehpport");
function send(data) {
socket.send(JSON.stringify(data));
}
socket.onopen = function() {
send({
action: 'subscribe',
auth_token: 'Bleh',
request_id: 'Bleh',
data: {
account_id: 'bleh',
binding: "bleh"
}
});
}
socket.onmessage = function(raw_message) {
var json_data = JSON.parse(raw_message.data);
console.log(json_data);
};
</script>
</body>
</html>
I get a stream of JSON objects in real time.
This is my small Golang, Gorilla/Websockets script
func main() {
var server = flag.String("server", "blehIP:BlehPort", "server address")
var wg sync.WaitGroup
flag.Parse()
url := url.URL {
Scheme: "ws",
Host: *server,
Path: "",
}
conn, _, err := websocket.DefaultDialer.Dial(url.String(), nil)
if err != nil {
log.Fatal("Dial Error: ", err)
}
defer conn.Close()
payload := []byte(`{"action":"subscribe",`+
`"auth_token":"` + bleh + `",`+
`"request_id":"` + bleh + `",`+
`"data": {`+
`"account_id":"` + bleh + `",`+
`"binding":"` + bleh + `"}`+
`}`)
wg.Add(1)
go func() {
defer conn.Close()
defer wg.Done()
err = conn.WriteMessage(websocket.TextMessage, payload)
if err != nil {
log.Println("Write Error: ", err)
return
}
m := frame{}
err := conn.ReadJSON(&m)
if err != nil {
log.Println("WebScoket closed.", err)
return
}
// msg := string(bytes[:])
fmt.Printf("%v", m)
}()
wg.Wait()
return
}
The error returned is: WebSocket closed. websocket: close 1011
(internal server error)

How to add list of url to gocolly Queue?

I want to scrape a list of url to using gocolly
func main() {
fileName := "output.txt"
var result string
f, err := os.Create(fileName)
if err != nil {
panic(err)
}
defer func() {
if err := f.Close(); err != nil {
panic(err)
}
}()
rows := ReadInput()
q := AddUrl(rows)
// Instantiate default collector
c := colly.NewCollector()
c.OnHTML("body", func(e *colly.HTMLElement) {
result = result + e.Text +"\n"
})
c.OnRequest(func(r *colly.Request) {
fmt.Println("visiting", r.URL)
})
// Set error handler
c.OnError(func(r *colly.Response, err error) {
fmt.Println("Request URL:", r.Request.URL, "failed with response:", r, "\nError:", err)
})
q.Run(c)
f.WriteString(result)
log.Printf("Scraping done, Please check file %q for results\n", fileName)
}
func ReadInput() []string{
// Read from file
b, err := ioutil.ReadFile("input.txt") // just pass the file name
if err != nil {
fmt.Print(err)
}
str := string(b) // convert content to a 'string'
// split each row
rows := strings.Split(str,"\n")
return rows
}
But when I am trying to add url from slice of string(url) to gocolly queue it doesn't add all url, just added the last url.
func AddUrl(rows []string) *queue.Queue {
Q, _ := queue.New(
2, // Number of consumer threads
&queue.InMemoryQueueStorage{MaxSize: 10000},
)
for _,url:=range rows{
Q.AddURL(url)
}
return Q
}
instead of loop if I add url maually then it's work perfectly, but with loop it just add the last element.
func AddUrl(rows []string) *queue.Queue {
Q, _ := queue.New(
2, // Number of consumer threads
&queue.InMemoryQueueStorage{MaxSize: 10000},
)
Q.AddURL("http://bakeshopva.com")
Q.AddURL("http://zekescoffeedc.com")
return Q
}

How to connect container stdin & stdout with websocket?

At present I want to connect container stdin and stdout with websocket. But I can not read stdout if there are no output. e.g. "cd /"
Here is my code:
package main
import (
dcl "github.com/docker/docker/client"
"context"
"html/template"
"github.com/docker/docker/api/types"
"log"
"net/http"
"flag"
"github.com/gorilla/websocket"
"fmt"
"io"
"bufio"
)
var inout chan []byte
var output chan []byte
var addr = flag.String("addr", "localhost:8080", "http service address")
var upgrader = websocket.Upgrader{}
func echo(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print(err)
return
}
defer conn.Close()
cli, err := dcl.NewEnvClient()
if err != nil {
log.Print(err)
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
return
}
ctx := context.Background()
execConfig := types.ExecConfig{
AttachStderr: true,
AttachStdin: true,
AttachStdout: true,
Cmd: []string{"/bin/sh"},
Tty: false,
Detach: false,
}
//set target container
exec, err := cli.ContainerExecCreate(ctx, "ubuntu", execConfig)
if err != nil {
log.Print(err)
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
return
}
execAttachConfig := types.ExecStartCheck{
Detach: false,
Tty: false,
}
containerConn, err := cli.ContainerExecAttach(ctx, exec.ID, execAttachConfig)
if err != nil {
log.Print(err)
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
return
}
defer containerConn.Close()
bufin := bufio.NewReader(containerConn.Reader)
go func(w io.WriteCloser) {
for {
data, ok := <-inout
if !ok {
fmt.Println("!ok")
w.Close()
return
}
fmt.Println(string(data))
w.Write(append(data, '\n'))
}
}(containerConn.Conn)
go func() {
for {
buffer := make([]byte, 4096, 4096)
c, err := bufin.Read(buffer)
if err != nil{
fmt.Println(err)
}
//c, err := containerConn.Reader.Read(buffer)
if c > 0 {
output <- buffer[:c]
}
if c == 0{
output <- []byte{' '}
}
if err != nil {
break
}
}
}()
for {
mt, message, err := conn.ReadMessage()
if err != nil {
log.Println("read:", err)
break
}
log.Printf("recv: %s", message)
inout <- message
data := <-output
err = conn.WriteMessage(mt, data)
if err != nil {
log.Println("write:", err)
break
}
}
}
func home(w http.ResponseWriter, r *http.Request) {
homeTemplate.Execute(w, "ws://"+r.Host+"/echo")
}
func main() {
inout = make(chan []byte)
output = make(chan []byte)
http.HandleFunc("/echo", echo)
http.HandleFunc("/", home)
log.Fatal(http.ListenAndServe(*addr, nil))
}
var homeTemplate = template.Must(template.New("").Parse(`
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
window.addEventListener("load", function(evt) {
var output = document.getElementById("output");
var input = document.getElementById("input");
var ws;
var print = function(message) {
var d = document.createElement("div");
d.innerHTML = message;
output.appendChild(d);
};
document.getElementById("open").onclick = function(evt) {
if (ws) {
return false;
}
ws = new WebSocket("{{.}}");
ws.onopen = function(evt) {
print("OPEN");
}
ws.onclose = function(evt) {
print("CLOSE");
ws = null;
}
ws.onmessage = function(evt) {
print("RESPONSE: " + evt.data);
}
ws.onerror = function(evt) {
print("ERROR: " + evt.data);
}
return false;
};
document.getElementById("send").onclick = function(evt) {
if (!ws) {
return false;
}
print("SEND: " + input.value);
ws.send(input.value);
return false;
};
document.getElementById("close").onclick = function(evt) {
if (!ws) {
return false;
}
ws.close();
return false;
};
});
</script>
</head>
<body>
<table>
<tr><td valign="top" width="50%">
<p>Click "Open" to create a connection to the server,
"Send" to send a message to the server and "Close" to close the connection.
You can change the message and send multiple times.
<p>
<form>
<button id="open">Open</button>
<button id="close">Close</button>
<p><input id="input" type="text" value="ls">
<button id="send">Send</button>
</form>
</td><td valign="top" width="50%">
<div id="output"></div>
</td></tr></table>
</body>
</html>
`))
I try many ways, but no way can work :(
You need to implement the timeout on the call because in this part:
log.Printf("recv: %s", message)
inout <- message
data := <-output
err = conn.WriteMessage(mt, data)
if err != nil {
log.Println("write:", err)
break
}
You are waiting always to get the response for the server.
Here are you code working properly with the timeout implemented and an issue on the socket because needs to send utf8 and needs to be parsed to utf8 before send to the client.
package main
import (
dcl "github.com/docker/docker/client"
"context"
"html/template"
"github.com/docker/docker/api/types"
"log"
"net/http"
"flag"
"github.com/gorilla/websocket"
"fmt"
"io"
"bufio"
"time"
"unicode/utf8"
)
var inout chan []byte
var output chan []byte
var addr = flag.String("addr", "localhost:8080", "http service address")
var upgrader = websocket.Upgrader{}
func echo(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print(err)
return
}
defer conn.Close()
cli, err := dcl.NewEnvClient()
if err != nil {
log.Print(err)
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
return
}
ctx := context.Background()
execConfig := types.ExecConfig{
AttachStderr: true,
AttachStdin: true,
AttachStdout: true,
Cmd: []string{"/bin/sh"},
Tty: false,
Detach: false,
}
//set target container
exec, err := cli.ContainerExecCreate(ctx, "vigorous_mclean", execConfig)
if err != nil {
log.Print(err)
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
return
}
execAttachConfig := types.ExecStartCheck{
Detach: false,
Tty: false,
}
containerConn, err := cli.ContainerExecAttach(ctx, exec.ID, execAttachConfig)
if err != nil {
log.Print(err)
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
return
}
defer containerConn.Close()
bufin := bufio.NewReader(containerConn.Reader)
// Write to docker container
go func(w io.WriteCloser) {
for {
data, ok := <-inout
log.Println("Received to send to docker", data)
if !ok {
fmt.Println("!ok")
w.Close()
return
}
w.Write(append(data, '\n'))
}
}(containerConn.Conn)
// Received of Container Docker
go func() {
for {
buffer := make([]byte, 4096, 4096)
c, err := bufin.Read(buffer)
if err != nil {
fmt.Println(err)
}
//c, err := containerConn.Reader.Read(buffer)
if c > 0 {
output <- buffer[:c]
}
if c == 0 {
output <- []byte{' '}
}
if err != nil {
break
}
}
}()
for {
conn.CloseHandler()
mt, message, err := conn.ReadMessage()
log.Println(mt)
if err != nil {
log.Println("read:", err)
break
} else {
log.Printf("recv: %s\n", message)
inout <- message
select {
case data := <-output:
stringData := string(data[:])
if !utf8.ValidString(stringData) {
v := make([]rune, 0, len(stringData))
for i, r := range stringData {
if r == utf8.RuneError {
_, size := utf8.DecodeRuneInString(stringData[i:])
if size == 1 {
continue
}
}
v = append(v, r)
}
stringData = string(v)
}
err = conn.WriteMessage(mt, []byte(stringData))
if err != nil {
log.Println("write:", err)
}
case <-time.After(time.Second * 1):
log.Println("Timeout")
}
}
}
}
func home(w http.ResponseWriter, r *http.Request) {
homeTemplate.Execute(w, "ws://"+r.Host+"/echo")
}
func main() {
inout = make(chan []byte)
output = make(chan []byte)
http.HandleFunc("/echo", echo)
http.HandleFunc("/", home)
log.Fatal(http.ListenAndServe(*addr, nil))
}
var homeTemplate = template.Must(template.New("").Parse(`
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
window.addEventListener("load", function(evt) {
var output = document.getElementById("output");
var input = document.getElementById("input");
var ws;
var print = function(message) {
var d = document.createElement("div");
d.innerHTML = message;
output.appendChild(d);
};
document.getElementById("open").onclick = function(evt) {
if (ws) {
return false;
}
ws = new WebSocket("{{.}}");
ws.onopen = function(evt) {
print("OPEN");
}
ws.onclose = function(evt) {
print("CLOSE");
ws = null;
}
ws.onmessage = function(evt) {
print("RESPONSE: " + evt.data);
}
ws.onerror = function(evt) {
print("ERROR: " + evt.data);
}
return false;
};
document.getElementById("send").onclick = function(evt) {
if (!ws) {
return false;
}
print("SEND: " + input.value);
ws.send(input.value);
return false;
};
document.getElementById("close").onclick = function(evt) {
if (!ws) {
return false;
}
ws.close();
return false;
};
});
</script>
</head>
<body>
<table>
<tr><td valign="top" width="50%">
<p>Click "Open" to create a connection to the server,
"Send" to send a message to the server and "Close" to close the connection.
You can change the message and send multiple times.
<p>
<form>
<button id="open">Open</button>
<button id="close">Close</button>
<p><input id="input" type="text" value="ls">
<button id="send">Send</button>
</form>
</td><td valign="top" width="50%">
<div id="output"></div>
</td></tr></table>
</body>
</html>
`))
Another method to connect directly the stdout to websocket without any timeout is create a goroutine that when you receive something from the docker send to the client directly and the code can be this
package main
import (
dcl "github.com/docker/docker/client"
"context"
"html/template"
"github.com/docker/docker/api/types"
"log"
"net/http"
"flag"
"github.com/gorilla/websocket"
"fmt"
"io"
"bufio"
"unicode/utf8"
)
var inout chan []byte
var output chan []byte
var addr = flag.String("addr", "localhost:8080", "http service address")
var upgrader = websocket.Upgrader{}
func echo(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print(err)
return
}
defer conn.Close()
cli, err := dcl.NewEnvClient()
if err != nil {
log.Print(err)
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
return
}
ctx := context.Background()
execConfig := types.ExecConfig{
AttachStderr: true,
AttachStdin: true,
AttachStdout: true,
Cmd: []string{"/bin/sh"},
Tty: false,
Detach: false,
}
//set target container
exec, err := cli.ContainerExecCreate(ctx, "sharp_goldwasser", execConfig)
if err != nil {
log.Print(err)
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
return
}
execAttachConfig := types.ExecStartCheck{
Detach: false,
Tty: false,
}
containerConn, err := cli.ContainerExecAttach(ctx, exec.ID, execAttachConfig)
if err != nil {
log.Print(err)
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
return
}
defer containerConn.Close()
bufin := bufio.NewReader(containerConn.Reader)
// Write to docker container
go func(w io.WriteCloser) {
for {
data, ok := <-inout
log.Println("Received to send to docker", data)
if !ok {
fmt.Println("!ok")
w.Close()
return
}
w.Write(append(data, '\n'))
}
}(containerConn.Conn)
// Received of Container Docker
go func() {
for {
buffer := make([]byte, 4096, 4096)
c, err := bufin.Read(buffer)
if err != nil {
fmt.Println(err)
}
//c, err := containerConn.Reader.Read(buffer)
if c > 0 {
output <- buffer[:c]
}
if c == 0 {
output <- []byte{' '}
}
if err != nil {
break
}
}
}()
// Connect the STDOUT to the Socket
go func () {
data := <-output
stringData := string(data[:])
if !utf8.ValidString(stringData) {
v := make([]rune, 0, len(stringData))
for i, r := range stringData {
if r == utf8.RuneError {
_, size := utf8.DecodeRuneInString(stringData[i:])
if size == 1 {
continue
}
}
v = append(v, r)
}
stringData = string(v)
}
err = conn.WriteMessage(1, []byte(stringData))
if err != nil {
log.Println("write:", err)
}
}()
for {
conn.CloseHandler()
_, message, err := conn.ReadMessage()
if err != nil {
log.Println("read:", err)
break
} else {
log.Printf("recv: %s\n", message)
inout <- message
}
}
}
func home(w http.ResponseWriter, r *http.Request) {
homeTemplate.Execute(w, "ws://"+r.Host+"/echo")
}
func main() {
inout = make(chan []byte)
output = make(chan []byte)
http.HandleFunc("/echo", echo)
http.HandleFunc("/", home)
log.Fatal(http.ListenAndServe(*addr, nil))
}
var homeTemplate = template.Must(template.New("").Parse(`
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
window.addEventListener("load", function(evt) {
var output = document.getElementById("output");
var input = document.getElementById("input");
var ws;
var print = function(message) {
var d = document.createElement("div");
d.innerHTML = message;
output.appendChild(d);
};
document.getElementById("open").onclick = function(evt) {
if (ws) {
return false;
}
ws = new WebSocket("{{.}}");
ws.onopen = function(evt) {
print("OPEN");
}
ws.onclose = function(evt) {
print("CLOSE");
ws = null;
}
ws.onmessage = function(evt) {
print("RESPONSE: " + evt.data);
}
ws.onerror = function(evt) {
print("ERROR: " + evt.data);
}
return false;
};
document.getElementById("send").onclick = function(evt) {
if (!ws) {
return false;
}
print("SEND: " + input.value);
ws.send(input.value);
return false;
};
document.getElementById("close").onclick = function(evt) {
if (!ws) {
return false;
}
ws.close();
return false;
};
});
</script>
</head>
<body>
<table>
<tr><td valign="top" width="50%">
<p>Click "Open" to create a connection to the server,
"Send" to send a message to the server and "Close" to close the connection.
You can change the message and send multiple times.
<p>
<form>
<button id="open">Open</button>
<button id="close">Close</button>
<p><input id="input" type="text" value="ls">
<button id="send">Send</button>
</form>
</td><td valign="top" width="50%">
<div id="output"></div>
</td></tr></table>
</body>
</html>
`))

Resources