Based on https://github.com/nnevatie/capnwebcpp
at main 3.0 kB view raw
1package gocapnweb 2 3import ( 4 "bufio" 5 "log" 6 "net/http" 7 "strings" 8 9 "github.com/gorilla/websocket" 10 "github.com/labstack/echo/v4" 11 "github.com/labstack/echo/v4/middleware" 12) 13 14var upgrader = websocket.Upgrader{ 15 CheckOrigin: func(r *http.Request) bool { 16 return true // Allow all origins for simplicity 17 }, 18} 19 20// SetupRpcEndpoint sets up both WebSocket and HTTP POST endpoints for RPC using Echo. 21func SetupRpcEndpoint(e *echo.Echo, path string, target RpcTarget) { 22 session := NewRpcSession(target) 23 24 // Setup WebSocket endpoint 25 e.GET(path, func(c echo.Context) error { 26 conn, err := upgrader.Upgrade(c.Response(), c.Request(), nil) 27 if err != nil { 28 log.Printf("WebSocket upgrade error: %v", err) 29 return err 30 } 31 defer conn.Close() 32 33 sessionData := NewSessionData(target) 34 session.OnOpen(sessionData) 35 defer session.OnClose(sessionData) 36 37 for { 38 _, message, err := conn.ReadMessage() 39 if err != nil { 40 if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { 41 log.Printf("WebSocket error: %v", err) 42 } 43 break 44 } 45 46 response, err := session.HandleMessage(sessionData, string(message)) 47 if err != nil { 48 log.Printf("Error processing WebSocket message: %v", err) 49 continue 50 } 51 52 if response != "" { 53 if err := conn.WriteMessage(websocket.TextMessage, []byte(response)); err != nil { 54 log.Printf("Error writing WebSocket response: %v", err) 55 break 56 } 57 } 58 } 59 return nil 60 }) 61 62 // Setup HTTP POST endpoint for batch RPC 63 e.POST(path, func(c echo.Context) error { 64 // CORS headers are handled by Echo middleware 65 c.Response().Header().Set("Content-Type", "text/plain") 66 67 defer c.Request().Body.Close() 68 scanner := bufio.NewScanner(c.Request().Body) 69 70 // Create a session data for this HTTP batch request 71 sessionData := NewSessionData(target) 72 var responses []string 73 74 // Process each line as a separate RPC message 75 for scanner.Scan() { 76 line := strings.TrimSpace(scanner.Text()) 77 if line == "" { 78 continue 79 } 80 81 response, err := session.HandleMessage(sessionData, line) 82 if err != nil { 83 log.Printf("Error processing HTTP message: %v", err) 84 continue 85 } 86 87 if response != "" { 88 responses = append(responses, response) 89 } 90 } 91 92 if err := scanner.Err(); err != nil { 93 log.Printf("Error reading HTTP body: %v", err) 94 return echo.NewHTTPError(http.StatusInternalServerError, "Error reading request body") 95 } 96 97 // Join responses with newlines 98 responseBody := strings.Join(responses, "\n") 99 return c.String(http.StatusOK, responseBody) 100 }) 101 102 // OPTIONS endpoint is handled automatically by Echo CORS middleware 103} 104 105// SetupEchoServer creates and configures an Echo server with common middleware. 106func SetupEchoServer() *echo.Echo { 107 e := echo.New() 108 109 // Add middleware 110 e.Use(middleware.Logger()) 111 e.Use(middleware.Recover()) 112 e.Use(middleware.CORS()) 113 114 // Hide Echo banner for cleaner output 115 e.HideBanner = true 116 117 return e 118}