Based on https://github.com/nnevatie/capnwebcpp

Batch pipelining working

Changed files
+88 -22
examples
batch-pipelining
+11 -10
examples/batch-pipelining/go.mod
··· 1 1 module github.com/gocapnweb/examples/batch-pipelining 2 2 3 - go 1.21 3 + go 1.23.0 4 + 5 + toolchain go1.24.2 4 6 5 7 replace github.com/gocapnweb => ../.. 6 8 7 - require ( 8 - github.com/gocapnweb v0.0.0-00010101000000-000000000000 9 - github.com/labstack/echo/v4 v4.11.4 10 - ) 9 + require github.com/gocapnweb v0.0.0-00010101000000-000000000000 11 10 12 11 require ( 13 12 github.com/gorilla/websocket v1.5.0 // indirect 13 + github.com/labstack/echo/v4 v4.13.4 // indirect 14 14 github.com/labstack/gommon v0.4.2 // indirect 15 - github.com/mattn/go-colorable v0.1.13 // indirect 15 + github.com/mattn/go-colorable v0.1.14 // indirect 16 16 github.com/mattn/go-isatty v0.0.20 // indirect 17 17 github.com/valyala/bytebufferpool v1.0.0 // indirect 18 18 github.com/valyala/fasttemplate v1.2.2 // indirect 19 - golang.org/x/crypto v0.17.0 // indirect 20 - golang.org/x/net v0.19.0 // indirect 21 - golang.org/x/sys v0.15.0 // indirect 22 - golang.org/x/text v0.14.0 // indirect 19 + golang.org/x/crypto v0.38.0 // indirect 20 + golang.org/x/net v0.40.0 // indirect 21 + golang.org/x/sys v0.33.0 // indirect 22 + golang.org/x/text v0.25.0 // indirect 23 + golang.org/x/time v0.11.0 // indirect 23 24 )
+31 -2
examples/batch-pipelining/go.sum
··· 1 - github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= 2 - github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 1 + github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 + github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 3 github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= 4 4 github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 5 + github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA= 6 + github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ= 7 + github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= 8 + github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= 9 + github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= 10 + github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= 11 + github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 12 + github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 13 + github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 14 + github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 15 + github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 16 + github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 17 + github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 18 + github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 19 + github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= 20 + github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 21 + golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= 22 + golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= 23 + golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= 24 + golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= 25 + golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 26 + golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= 27 + golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 28 + golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= 29 + golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= 30 + golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= 31 + golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= 32 + gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 33 + gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-1
examples/batch-pipelining/main.go
··· 7 7 "os" 8 8 9 9 "github.com/gocapnweb" 10 - "github.com/labstack/echo/v4" 11 10 ) 12 11 13 12 // User represents a user object.
+46 -9
rpc.go
··· 248 248 return nil, err 249 249 } 250 250 251 - // Cache the result 251 + // Normalize the result for pipeline traversal 252 + normalizedResult, err := s.normalizeResult(result) 253 + if err != nil { 254 + return nil, err 255 + } 256 + 257 + // Cache the normalized result 252 258 sessionData.mu.Lock() 253 - sessionData.PendingResults[refExportID] = result 259 + sessionData.PendingResults[refExportID] = normalizedResult 254 260 delete(sessionData.PendingOperations, refExportID) 255 261 sessionData.mu.Unlock() 256 262 257 263 // If there's a path, traverse it 258 264 if len(v) >= 3 { 259 265 if pathArray, ok := v[2].([]interface{}); ok { 260 - return s.traversePath(result, pathArray) 266 + return s.traversePath(normalizedResult, pathArray) 261 267 } 262 268 } 263 - return result, nil 269 + return normalizedResult, nil 264 270 } 265 271 sessionData.mu.RUnlock() 266 272 ··· 384 390 return s.createErrorResponse(exportID, "MethodError", err.Error()), nil 385 391 } 386 392 387 - // Store the result for future reference 393 + // Normalize the result to ensure it's JSON-compatible for pipeline traversal 394 + normalizedResult, err := s.normalizeResult(result) 395 + if err != nil { 396 + return s.createErrorResponse(exportID, "SerializationError", err.Error()), nil 397 + } 398 + 399 + // Store the normalized result for future reference 388 400 sessionData.mu.Lock() 389 - sessionData.PendingResults[exportID] = result 401 + sessionData.PendingResults[exportID] = normalizedResult 390 402 sessionData.mu.Unlock() 391 403 392 404 // Send as resolve 393 - if _, ok := result.([]interface{}); ok { 394 - return []interface{}{"resolve", exportID, []interface{}{result}}, nil 405 + if _, ok := normalizedResult.([]interface{}); ok { 406 + return []interface{}{"resolve", exportID, []interface{}{normalizedResult}}, nil 395 407 } 396 - return []interface{}{"resolve", exportID, result}, nil 408 + return []interface{}{"resolve", exportID, normalizedResult}, nil 397 409 } 398 410 sessionData.mu.RUnlock() 399 411 ··· 417 429 errorBytes, _ := json.Marshal(errorData) 418 430 log.Printf("Abort received: %s", string(errorBytes)) 419 431 } 432 + 433 + // normalizeResult ensures that Go structs are converted to map[string]interface{} 434 + // for proper pipeline traversal. This is necessary because Go structs need to be 435 + // JSON-marshaled and then unmarshaled to become navigable objects. 436 + func (s *RpcSession) normalizeResult(result interface{}) (interface{}, error) { 437 + // If it's already a map[string]interface{} or basic type, return as-is 438 + switch result.(type) { 439 + case map[string]interface{}, []interface{}, string, float64, bool, nil: 440 + return result, nil 441 + } 442 + 443 + // For other types (like structs), marshal to JSON and unmarshal to interface{} 444 + // This converts structs to map[string]interface{} which can be traversed 445 + jsonBytes, err := json.Marshal(result) 446 + if err != nil { 447 + return nil, fmt.Errorf("failed to marshal result: %w", err) 448 + } 449 + 450 + var normalized interface{} 451 + if err := json.Unmarshal(jsonBytes, &normalized); err != nil { 452 + return nil, fmt.Errorf("failed to unmarshal result: %w", err) 453 + } 454 + 455 + return normalized, nil 456 + }