-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.go
150 lines (120 loc) · 3.48 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package go_remote
import (
"context"
"encoding/json"
"errors"
"net/http"
"reflect"
)
// Guard is a guard function that allows or denies code execution based on the context
type Guard = func(r context.Context) bool
// Connect extends of blocks request based on context value
type Connect = func(r *http.Request) (context.Context, error)
// Server structure stores all methods, events and data of API
type Server struct {
services map[string]*service
data map[string]dataRecord
config *ServerConfig
Connect Connect
Events *Hub
Dependencies *dependencyStore
}
type ServerConfig struct {
WebSocket bool
WithoutKey bool
}
// Response handles results of remote calls
type Response struct {
ID string `json:"id"`
Data interface{} `json:"data"`
Error string `json:"error"`
}
// NewServer creates a new Server instance
func NewServer(config *ServerConfig) *Server {
s := Server{}
s.services = make(map[string]*service)
s.data = make(map[string]dataRecord)
s.config = config
if s.config == nil {
s.config = &ServerConfig{}
}
s.Events = newHub()
if s.config.WebSocket {
go s.Events.Run()
}
s.Dependencies = newDependencyStore()
s.Connect = func(r *http.Request) (context.Context, error) { return r.Context(), nil }
return &s
}
// AddService exposes all public methods of the provided object
func (s *Server) AddService(name string, rcvr interface{}) error {
return s.register(name, rcvr, nil)
}
// AddServiceWithGuard exposes all public methods of the provided object with a guard
func (s *Server) AddServiceWithGuard(name string, rcvr interface{}, guard Guard) error {
return s.register(name, rcvr, guard)
}
// AddVariable adds a variable data to the API
func (s *Server) AddVariable(name string, rcvr interface{}) error {
return s.registerData(name, rcvr, false)
}
// AddConstant adds a constant data to the API
func (s *Server) AddConstant(name string, rcvr interface{}) error {
return s.registerData(name, rcvr, true)
}
func (s *Server) registerData(name string, rcvr interface{}, isConstant bool) error {
if _, ok := s.data[name]; ok {
return errors.New("service name already used")
}
if isConstant {
if reflect.TypeOf(rcvr).Kind() == reflect.Ptr {
rcvr = reflect.ValueOf(rcvr).Elem().Interface()
}
s.data[name] = dataRecord{isConstant: true, value: rcvr}
} else {
s.data[name] = dataRecord{isConstant: false, rtype: reflect.TypeOf(rcvr)}
}
return nil
}
func (s *Server) register(name string, rcvr interface{}, guard Guard) error {
service := newService(rcvr, guard)
if name == "" {
name = service.name
}
// store the service
s.services[name] = service
return nil
}
// Process starts the package processing, executing all requested methods
func (s *Server) Process(input []byte, c context.Context) []Response {
data := callData{}
err := json.Unmarshal(input, &data)
response := make([]Response, len(data))
if err != nil {
log.Errorf(err.Error())
return response
}
res := make(chan *Response)
for i := range data {
data[i].parse()
data[i].dependencies = s.Dependencies
data[i].ctx = c
go s.Call(data[i], res)
}
for i := range data {
response[i] = *(<-res)
}
return response
}
// Call allows to execute some Servers's method
func (s *Server) Call(call *callInfo, res chan *Response) {
response := Response{ID: call.ID}
log.Debugf("Call %s.%s", call.service, call.method)
service, ok := s.services[call.service]
if !ok {
response.Error = "Unknown service"
} else {
service.Call(call, &response)
}
res <- &response
}