Based on https://github.com/nnevatie/capnwebcpp
1package main
2
3import (
4 "encoding/json"
5 "fmt"
6 "log"
7 "os"
8
9 "github.com/gocapnweb"
10)
11
12// User represents a user object.
13type User struct {
14 ID string `json:"id"`
15 Name string `json:"name"`
16}
17
18// Profile represents a user profile.
19type Profile struct {
20 ID string `json:"id"`
21 Bio string `json:"bio"`
22}
23
24// UserServer implements RPC methods for user operations with pipelining support.
25type UserServer struct {
26 *gocapnweb.BaseRpcTarget
27 users map[string]User // session token -> user
28 profiles map[string]Profile // user ID -> profile
29 notifications map[string][]string // user ID -> notifications
30}
31
32// NewUserServer creates a new UserServer instance with sample data.
33func NewUserServer() *UserServer {
34 server := &UserServer{
35 BaseRpcTarget: gocapnweb.NewBaseRpcTarget(),
36 users: make(map[string]User),
37 profiles: make(map[string]Profile),
38 notifications: make(map[string][]string),
39 }
40
41 // Initialize sample data
42 server.initializeData()
43
44 // Register RPC methods
45 server.Method("authenticate", server.authenticate)
46 server.Method("getUserProfile", server.getUserProfile)
47 server.Method("getNotifications", server.getNotifications)
48
49 return server
50}
51
52func (s *UserServer) initializeData() {
53 // Initialize users (session token -> user object)
54 s.users["cookie-123"] = User{
55 ID: "u_1",
56 Name: "Ada Lovelace",
57 }
58 s.users["cookie-456"] = User{
59 ID: "u_2",
60 Name: "Alan Turing",
61 }
62
63 // Initialize profiles (user ID -> profile object)
64 s.profiles["u_1"] = Profile{
65 ID: "u_1",
66 Bio: "Mathematician & first programmer",
67 }
68 s.profiles["u_2"] = Profile{
69 ID: "u_2",
70 Bio: "Mathematician & computer science pioneer",
71 }
72
73 // Initialize notifications (user ID -> array of notifications)
74 s.notifications["u_1"] = []string{
75 "Welcome to jsrpc!",
76 "You have 2 new followers",
77 }
78 s.notifications["u_2"] = []string{
79 "New feature: pipelining!",
80 "Security tips for your account",
81 }
82}
83
84func (s *UserServer) authenticate(args json.RawMessage) (interface{}, error) {
85 // Extract session token from arguments
86 var sessionToken string
87
88 // Try to parse as array first
89 var argArray []string
90 if err := json.Unmarshal(args, &argArray); err == nil && len(argArray) > 0 {
91 sessionToken = argArray[0]
92 } else {
93 // Try to parse as string
94 if err := json.Unmarshal(args, &sessionToken); err != nil {
95 return nil, err
96 }
97 }
98
99 // Look up user by session token
100 user, exists := s.users[sessionToken]
101 if !exists {
102 return nil, fmt.Errorf("invalid session")
103 }
104
105 return user, nil
106}
107
108func (s *UserServer) getUserProfile(args json.RawMessage) (interface{}, error) {
109 // Extract user ID from arguments
110 var userID string
111
112 // Try to parse as array first
113 var argArray []string
114 if err := json.Unmarshal(args, &argArray); err == nil && len(argArray) > 0 {
115 userID = argArray[0]
116 } else {
117 // Try to parse as string
118 if err := json.Unmarshal(args, &userID); err != nil {
119 return nil, err
120 }
121 }
122
123 // Look up profile by user ID
124 profile, exists := s.profiles[userID]
125 if !exists {
126 return nil, fmt.Errorf("no such user")
127 }
128
129 return profile, nil
130}
131
132func (s *UserServer) getNotifications(args json.RawMessage) (interface{}, error) {
133 // Extract user ID from arguments
134 var userID string
135
136 // Try to parse as array first
137 var argArray []string
138 if err := json.Unmarshal(args, &argArray); err == nil && len(argArray) > 0 {
139 userID = argArray[0]
140 } else {
141 // Try to parse as string
142 if err := json.Unmarshal(args, &userID); err != nil {
143 return nil, err
144 }
145 }
146
147 // Look up notifications by user ID
148 notifications, exists := s.notifications[userID]
149 if !exists {
150 return []string{}, nil // Return empty array if no notifications
151 }
152
153 return notifications, nil
154}
155
156func main() {
157 // Default to serving static files from the examples/static directory
158 staticPath := "/static"
159 if len(os.Args) >= 2 {
160 staticPath = os.Args[1]
161 }
162
163 port := ":8000"
164
165 // Create Echo server with middleware
166 e := gocapnweb.SetupEchoServer()
167
168 // Setup RPC endpoint
169 server := NewUserServer()
170 gocapnweb.SetupRpcEndpoint(e, "/rpc", server)
171
172 // Setup static file endpoint
173 gocapnweb.SetupFileEndpoint(e, "/static", staticPath)
174
175 log.Printf("🚀 Batch Pipelining Go Server (Echo) starting on port %s", port)
176 log.Printf("🔌 HTTP Batch RPC endpoint: http://localhost%s/rpc", port)
177 log.Printf("🌐 Demo URL: http://localhost:3000 (available once you start the Svelte development server)")
178 log.Println()
179 log.Println("Sample data:")
180 log.Println(" Session tokens: cookie-123, cookie-456")
181 log.Println(" Users: u_1 (Ada Lovelace), u_2 (Alan Turing)")
182
183 if err := e.Start(port); err != nil {
184 log.Fatal("Failed to start server:", err)
185 }
186}