Websocket closed very quickly - go

I'm trying this websocket example:
package main
import (
"fmt"
"log"
"net/http"
"golang.org/x/net/websocket"
)
func Echo(ws *websocket.Conn) {
var err error
for {
var reply string
if err = websocket.Message.Receive(ws, &reply); err != nil {
fmt.Println("Can't receive")
break
}
fmt.Println("Received back from client: " + reply)
msg := "Received: " + reply
fmt.Println("Sending to client: " + msg)
if err = websocket.Message.Send(ws, msg); err != nil {
fmt.Println("Can't send")
break
}
}
}
func main() {
http.Handle("/", websocket.Handler(Echo))
if err := http.ListenAndServe(":1234", nil); err != nil {
log.Fatal("ListenAndServe:", err)
}
}
With this client:
<html>
<head></head>
<body>
<script type="text/javascript">
var sock = null;
var wsuri = "ws://localhost:1234";
window.onload = function() {
console.log("onload");
sock = new WebSocket(wsuri);
sock.onopen = function() {
console.log("connected to " + wsuri);
}
sock.onclose = function(e) {
console.log("connection closed (" + e.code + ")");
}
sock.onmessage = function(e) {
console.log("message received: " + e.data);
}
};
function send() {
var msg = document.getElementById('message').value;
sock.send(msg);
};
</script>
<h1>WebSocket Echo Test</h1>
<form>
<p>
Message: <input id="message" type="text" value="Hello, world!">
</p>
</form>
<button onclick="send();">Send Message</button>
</body>
</html>
But getting the below error:

I had trouble with std lib webscokets and (like most Go people) use Gorilla websocket package. Try this code (based on my working code):
package main
import (
"log"
"net/http"
"github.com/gorilla/websocket"
)
func echo(w http.ResponseWriter, r *http.Request) {
conn, err := websocket.Upgrade(w, r, w.Header(), 1024, 1024)
if err != nil {
http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
return
}
for {
messType, message, err := conn.ReadMessage()
if err != nil {
log.Println("read:", err)
break
}
log.Printf("recv: %s", message)
err = conn.WriteMessage(messType, message)
if err != nil {
log.Println("write:", err)
break
}
}
}
func main() {
http.HandleFunc("/", echo)
if err := http.ListenAndServe(":1234", nil); err != nil {
log.Fatal("ListenAndServe:", err)
}
}
I think you'll have a lot more joy this way.

Related

How to use "ListenForWebhook" in go-telegram-bot-api with gin?

if i use the following construction, everything is OK. But this construction uses tgbotapi.Update - it doesn't really work for me. I want to use tgbotapi.UpdatesChannel via function tgbotapi.ListenForWebhook().
import (
"github.com/gin-gonic/gin"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
)
type TelegramBot struct {
API *tgbotapi.BotAPI
Upd tgbotapi.Update
UpdCh tgbotapi.UpdatesChannel
Log *logger.Logger
}
func (tlg *TelegramBot) InitTg(cfg *config.Config) {
bot, err := tgapi.NewBot(cfg.Telegram.Token)
if err != nil {
tlg.Log.Fatal(err)
}
tlg.API = bot
router := gin.Default()
if cfg.TelegramMode != "webhook" {
botUpdate := tgbotapi.NewUpdate(0)
botUpdate.Timeout = 60
tlg.UpdCh = tlg.API.GetUpdatesChan(botUpdate)
go tlg.Start()
} else {
router.POST(cfg.Telegram.Path+"*any", tlg.WebhookHandler)
}
go router.Run(cfg.Listen.BindIP + ":" + cfg.Listen.Port)
//TODO fix me :)
for {
}
}
func (tlg *TelegramBot) WebhookHandler(c *gin.Context) {
defer c.Request.Body.Close()
bytes, err := ioutil.ReadAll(c.Request.Body)
if err != nil {
tlg.Log.Println(err)
return
}
err = json.Unmarshal(bytes, &tlg.Upd)
if err != nil {
log.Println(err)
return
}
go tlg.Start()
c.JSON(http.StatusOK, gin.H{"data": "not you :)"})
}
func (tlg *TelegramBot) Start() {
//TODO: for webhook
if tlg.Upd.Message != nil {
chatID := tlg.Upd.Message.Chat.ID
tlg.analyzeUpdate(chatID)
} else if tlg.Upd.CallbackQuery != nil {
chatID := tlg.Upd.CallbackQuery.Message.Chat.ID
tlg.analyzeUpdate(chatID)
}
//TODO: for polling
for update := range tlg.UpdCh {
if update.Message != nil {
tlg.Upd = update
chatID := tlg.Upd.Message.Chat.ID
tlg.analyzeUpdate(chatID)
} else if update.CallbackQuery != nil {
tlg.Upd = update
chatID := update.CallbackQuery.Message.Chat.ID
tlg.analyzeUpdate(chatID)
}
}
}
How can I do this, if possible (to get the webhook data into the tgbotapi.UpdatesChannel as in polling) :
router.POST(cfg.Telegram.Path+"*any", func(c *gin.Context) {
tlg.UpdCh = bot.ListenForWebhook(cfg.Listen.BindIP + ":" + cfg.Listen.Port + cfg.Telegram.Path + bot.Token)
go tlg.Start()
c.JSON(http.StatusOK, gin.H{"data": "not you :)"})
})

Can't run the go routines while building chat app with golang

I'm trying to build a chat app with golang. However, I've encountered a problem that if I wrap the code of the for loop into a Sender() function and using goroutines, the client will shut down immediately. But if I put it in the main function, it can run correctly.
Here is the client code.
package main
import (
"fmt"
"net"
"os"
"os/signal"
"syscall"
)
const (
SERVER_HOST = "localhost"
SERVER_PORT = "9988"
SERVER_TYPE = "tcp"
)
var connection net.Conn
var err error
func main() {
SetupCloseHandler()
//establish connection
connection, err = net.Dial(SERVER_TYPE, SERVER_HOST+":"+SERVER_PORT)
if err != nil {
panic(err)
}
//receive some data
go Receiver()
// go Sender()
for {
var input string
fmt.Print("Enter text: ")
fmt.Scan(&input)
_, err = connection.Write([]byte(input))
if err != nil {
panic(err)
}
}
}
// func Sender() {
// for {
// var input string
// fmt.Print("Enter text: ")
// fmt.Scan(&input)
// _, err = connection.Write([]byte(input))
// if err != nil {
// panic(err)
// }
// }
// }
func Receiver() {
for {
buffer := make([]byte, 1024)
mLen, err := connection.Read(buffer)
if err != nil {
panic(err)
}
fmt.Println("Received: ", string(buffer[:mLen]))
}
}
func SetupCloseHandler() {
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
fmt.Println("\r- Ctrl+C pressed in Terminal")
os.Exit(0)
}()
}
Here is the server code.
package main
import (
"fmt"
"net"
"os"
"github.com/google/uuid"
)
const (
SERVER_HOST = "localhost"
SERVER_PORT = "9988"
SERVER_TYPE = "tcp"
)
var clientMap = make(map[string]net.Conn)
func main() {
fmt.Println("Server Running...")
server, err := net.Listen(SERVER_TYPE, SERVER_HOST+":"+SERVER_PORT)
if err != nil {
fmt.Println("Error listening:", err.Error())
os.Exit(1)
}
defer server.Close()
fmt.Println("Listening on " + SERVER_HOST + ":" + SERVER_PORT)
fmt.Println("Waiting for client...")
for {
connection, err := server.Accept()
id := uuid.New().String()
clientMap[id] = connection
if err != nil {
fmt.Println("Error accepting: ", err.Error())
os.Exit(1)
}
fmt.Printf("client %s connected\n", id)
go processClient(connection, id)
}
}
func processClient(connection net.Conn, id string) {
defer connection.Close()
for {
buffer := make([]byte, 1024)
mLen, err := connection.Read(buffer)
if err != nil {
fmt.Println("Error reading:", err.Error())
delete(clientMap, id)
connection.Close()
return
}
fmt.Println("Received: ", string(buffer[:mLen]))
for k, c := range clientMap {
if k != id {
c.Write(buffer[:mLen])
}
}
}
}

Is it possible to send a web socket message when a rest api is hit? [duplicate]

This question already has answers here:
global error variable remains nil after initialization
(2 answers)
How to use global var across files in a package?
(3 answers)
Closed 1 year ago.
I need to extend the code of this tutorial to run both a regular rest process and a WebSocket server as part of go routine.
Here is the Go part:
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
var WS = websocket.Conn{}
func reader(conn *websocket.Conn) {
for {
// read in a message
messageType, p, err := conn.ReadMessage()
if err != nil {
log.Println(err)
return
}
// print out that message for clarity
fmt.Println(string(p))
if err := conn.WriteMessage(messageType, p); err != nil {
log.Println(err)
return
}
}
}
func homePage(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Home Page")
}
func restResponsePage(w http.ResponseWriter, r *http.Request) {
text := []byte("rest response")
if err := WS.WriteMessage(websocket.TextMessage, text); err != nil {
log.Println(err)
}
fmt.Fprintf(w, "rest response")
}
func wsEndpoint(w http.ResponseWriter, r *http.Request) {
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
// upgrade this connection to a WebSocket
// connection
WS, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
}
log.Println("Client Connected")
err = WS.WriteMessage(1, []byte("Hi from server!"))
if err != nil {
log.Println(err)
}
reader(WS)
}
func setupRoutes() {
http.HandleFunc("/", homePage)
http.HandleFunc("/restRequest", restResponsePage)
}
func setupWsRoute() {
http.HandleFunc("/ws", wsEndpoint)
}
func main() {
fmt.Println("Hello World")
go func() {
setupWsRoute()
log.Fatal(http.ListenAndServe(":8081", nil))
}()
setupRoutes()
log.Fatal(http.ListenAndServe(":8080", nil))
}
And the client index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Go WebSocket Tutorial</title>
</head>
<body>
<h2>Hello World</h2>
<script>
let socket = new WebSocket("ws://127.0.0.1:8081/ws");
console.log("Attempting Connection...");
socket.onopen = () => {
console.log("Successfully Connected");
socket.send("Hi From the Client!")
};
socket.onmessage = (event) => {
console.log("message from Server: ", event.data);
};
socket.onclose = event => {
console.log("Socket Closed Connection: ", event);
socket.send("Client Closed!")
};
socket.onerror = error => {
console.log("Socket Error: ", error);
};
</script>
</body>
</html>
When I hit the restRequest endpoint, before writing to the restResponsePage response writer w, I want to write a message to the websocket which I declared as a global var WS and initialized inside the reader method when the connection was upgraded.
This is somehow not working, instead, there is an exception and I don't have a clear idea on how to solve it.
Thanks to #ThinkGoodly for his help, changing the way the local WS was being declared and using the assignment operator instead of the short variable declaration did the trick.
Here is the whole code in case someone wants to use it. :)
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
var WS *websocket.Conn
func reader(conn *websocket.Conn) {
for {
// read in a message
messageType, p, err := conn.ReadMessage()
if err != nil {
log.Println(err)
return
}
// print out that message for clarity
fmt.Println(string(p))
if err := conn.WriteMessage(messageType, p); err != nil {
log.Println(err)
return
}
}
}
func homePage(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Home Page")
}
func restResponsePage(w http.ResponseWriter, r *http.Request) {
text := []byte("rest response")
if err := WS.WriteMessage(websocket.TextMessage, text); err != nil {
log.Println(err)
}
fmt.Fprintf(w, "rest response")
}
func wsEndpoint(w http.ResponseWriter, r *http.Request) {
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
// upgrade this connection to a WebSocket
// connection
WS, _ = upgrader.Upgrade(w, r, nil)
// if err != nil {
// log.Println(err)
// }
log.Println("Client Connected")
err1 := WS.WriteMessage(1, []byte("Hi from server!"))
if err1 != nil {
log.Println(err1)
}
reader(WS)
}
func setupRoutes() {
http.HandleFunc("/", homePage)
http.HandleFunc("/restRequest", restResponsePage)
}
func setupWsRoute() {
http.HandleFunc("/ws", wsEndpoint)
}
func main() {
fmt.Println("Hello World")
go func() {
setupWsRoute()
log.Fatal(http.ListenAndServe(":8081", nil))
}()
setupRoutes()
log.Fatal(http.ListenAndServe(":8080", nil))
}

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>
`))

Go: Map related error

I'm rather new to Stackoverflow. I'm stuck at this problem. I'm trying to make a map.
package main
import (
"encoding/json"
"fmt"
"html/template"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
)
func main() {
http.HandleFunc("/", handler)
http.HandleFunc("/showimage", showimage)
fmt.Println("listening...")
err := http.ListenAndServe(GetPort(), nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
func GetPort() string {
var port = os.Getenv("PORT")
if port == "" {
port = "4747"
fmt.Println("INFO: No PORT environment variable detected, defaulting to " + port)
}
return ":" + port
}
func handler (w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, rootForm)
}
const rootForm =
`<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Flickr photos</title>
</head>
<body>
<h1>Flickr photos</h1>
<p>Find photos by tags!</p>
<form action="/showimage" method="post" accept-charset="utf-8">
<input type="text" name="str" value="Type Tags..." id="str">
<input type="submit" value=".. and see the images!">
</form>
</body>
</html>`
var upperTemplate = template.Must(template.New("showimage").Parse(upperTemplateHTML)) //irrelevant to issue here
func showimage(w http.ResponseWriter, r *http.Request) {
tag := r.FormValue("str")
safeTag := url.QueryEscape(tag)
fullUrl := fmt.Sprintf("https://api.instagram.com/v1/users/search?q=%s&access_token=ACCESS-TOKEN&count=1", safeTag)
client := &http.Client{}
req, err := http.NewRequest("GET", fullUrl, nil)
if err != nil {
log.Fatal("NewRequest: ", err)
return
}
resp, requestErr := client.Do(req)
if requestErr != nil {
log.Fatal("Do: ", requestErr)
return
}
defer resp.Body.Close()
body, dataReadErr := ioutil.ReadAll(resp.Body)
if dataReadErr != nil {
log.Fatal("ReadAll: ", dataReadErr)
return
}
res := make(map[string][]map[string]interface{})
However, when I try to put data into the interface
json.Unmarshal(body, &res)
userid, _ := res["data"][0]["username"]
queryUrl := fmt.Sprintf("http://instagram.com/%s", userid)
I get the error
http: panic serving [::1]:63089: runtime error: index out of range
goroutine 28 [running]:
any idea why? This error is resolved if I remove the [] in res:= and userid :=, but I won't be able to access the data I want.
It is wrong with "res"'s using. "res" is a map(key is string, value is a slice),so res["data"] may be a nil in your code, and it will panic when you use res["data"][0] .You should do like this:
json.Unmarshal(body, &res)
s, ok := res["data"]
if ok {
if len(s)>0{
userid , ok := s[0]["username"]
if ok{
queryUrl := fmt.Sprintf("http://instagram.com/%s", userid)
}
}
}

Resources