Skip to content

Instantly share code, notes, and snippets.

@mackee
Created December 18, 2018 01:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mackee/caafebc13193638b1e11b36ff0bf479d to your computer and use it in GitHub Desktop.
Save mackee/caafebc13193638b1e11b36ff0bf479d to your computer and use it in GitHub Desktop.
Poor Socket.IO Proxy for debug
package main
import (
"encoding/json"
"fmt"
"log"
)
type Parser interface {
Parse(string) error
}
//go:generate stringer -type=EngineIOPacketType
type EngineIOPacketType rune
const (
EngineIOPacketOpen EngineIOPacketType = '0' + iota
EngineIOPacketClose
EngineIOPacketPing
EngineIOPacketPong
EngineIOPacketMessage
EngineIOPacketUpgrade
EngineIOPacketNoop
)
type EngineIO struct {
InnerParser Parser
}
type EngineIOOpenMessage struct {
PingInterval uint64 `json:"pingInterval"`
PingTimeout uint64 `json:"pingTimeout"`
Upgrades []string `json:"upgrades"`
SessionID string `json:"sid"`
}
func (eio *EngineIO) Parse(message string) error {
t := EngineIOPacketType(message[0])
log.Printf("[INFO] Engine.IO type is %s", t)
if len(message) == 1 {
return nil
}
if t == EngineIOPacketOpen {
var v EngineIOOpenMessage
err := json.Unmarshal([]byte(message[1:]), &v)
if err != nil {
return fmt.Errorf("open message error: %s", err)
}
log.Printf("[INFO] Engine.IO open message is %+v", v)
return nil
}
if eio.InnerParser != nil {
err := eio.InnerParser.Parse(message[1:])
if err != nil {
return fmt.Errorf("inner parser error: %s", err)
}
}
return nil
}
package main
import (
"io"
"log"
"net/http"
"strings"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool { return true },
}
var parser = &EngineIO{&SocketIO{}}
func main() {
http.HandleFunc("/socket.io/", func(w http.ResponseWriter, r *http.Request) {
log.Println("[INFO] ===================================================")
for key, value := range r.Header {
values := strings.Join(value, ",")
log.Printf("[INFO] header=%s, value=%s", key, values)
}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Printf("[ERROR] websocket upgrade error: %s", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
ctx := r.Context()
url := r.URL
url.Host = "localhost:3000"
url.Scheme = "ws"
h := http.Header{}
for _, header := range []string{"Cookie", "Origin"} {
h.Add(header, r.Header.Get(header))
}
client, _, err := websocket.DefaultDialer.DialContext(ctx, url.String(), h)
if err != nil {
log.Printf("[ERROR] dial origin error: %s", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
go func() {
for {
t, bs, err := client.ReadMessage()
if err == io.EOF {
break
}
parser.Parse(string(bs))
switch t {
case websocket.TextMessage:
log.Printf("send message: %s", string(bs))
case websocket.BinaryMessage:
log.Printf("send binary message")
case websocket.PingMessage:
log.Printf("send ping: %s", string(bs))
case websocket.PongMessage:
log.Printf("send pong: %s", string(bs))
}
if err := conn.WriteMessage(t, bs); err != nil {
log.Printf("[ERROR] fail send message: %s", err)
}
}
}()
for {
t, bs, err := conn.ReadMessage()
if err == io.EOF {
break
}
parser.Parse(string(bs))
switch t {
case websocket.TextMessage:
log.Printf("recv message: %s", string(bs))
case websocket.BinaryMessage:
log.Printf("recv binary message")
case websocket.PingMessage:
log.Printf("recv ping: %s", string(bs))
case websocket.PongMessage:
log.Printf("recv pong: %s", string(bs))
}
if err := client.WriteMessage(t, bs); err != nil {
log.Printf("[ERROR] fail recv message: %s", err)
}
}
})
log.Println("[INFO] start server")
err := http.ListenAndServe(":8888", nil)
if err != nil {
log.Printf("[ERROR] fail launch server: %s", err)
}
}
package main
import (
"encoding/json"
"fmt"
"log"
)
//go:generate stringer -type=SocketIOPacketType
type SocketIOPacketType rune
const (
SocketIOPacketConnect SocketIOPacketType = '0' + iota
SocketIOPacketDisconnect
SocketIOPacketEvent
SocketIOPacketAck
SocketIOPacketError
SocketIOPacketBinaryEvent
SocketIOPacketBinaryAck
)
type SocketIO struct {
}
type SocketIOEvent struct {
Name string
Message json.RawMessage
}
func (e *SocketIOEvent) UnmarshalJSON(b []byte) error {
var rawEvent []json.RawMessage
err := json.Unmarshal(b, &rawEvent)
if err != nil {
return err
}
if len(rawEvent) != 2 {
return fmt.Errorf("message is few length or too length")
}
e.Name = string(rawEvent[0])
e.Message = rawEvent[1]
return nil
}
func (eio *SocketIO) Parse(message string) error {
t := SocketIOPacketType(message[0])
log.Printf("[INFO] Socket.IO type is %s", t)
if len(message) == 1 {
return nil
}
if t == SocketIOPacketEvent {
var event SocketIOEvent
err := json.Unmarshal([]byte(message[1:]), &event)
if err != nil {
return fmt.Errorf("Socket.IO fail parse error: %s", err)
}
log.Printf("[INFO] Socket.IO event is name=%s, message=%s", event.Name, string(event.Message))
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment