diff --git a/server/server.go b/server/server.go
index 995f2558c7bd118854ba6db28e0f9c5fb63db2f3..59c1e4215a468bea1a3a36813eafc97014bfac99 100644 (file)
--- a/server/server.go
+++ b/server/server.go
// A Config specifies configuration values for a SysDB web server.
type Config struct {
- // Conn specifies a connection to a SysDB server instance.
- Conn *client.Conn
+ // Conns is a slice of connections to a SysDB server instance. The number of
+ // elements specifies the maximum number of parallel queries to the backend.
+ // Note that a client connection is not thread-safe but multiple idle
+ // connections don't impose any load on the server.
+ Conns []*client.Conn
// TemplatePath specifies the relative or absolute location of template files.
TemplatePath string
// A Server implements an http.Handler that serves the SysDB user interface.
type Server struct {
- c *client.Conn
+ conns chan *client.Conn
+
+ // Request multiplexer
+ mux map[string]handler
// Templates:
main *template.Template
results map[string]*template.Template
- // Static content:
- static http.Handler
+ // Base directory of static files.
+ basedir string
}
// New constructs a new SysDB web server using the specified configuration.
func New(cfg Config) (*Server, error) {
- s := &Server{c: cfg.Conn, results: make(map[string]*template.Template)}
+ if len(cfg.Conns) == 0 {
+ return nil, errors.New("need at least one client connection")
+ }
+
+ s := &Server{
+ conns: make(chan *client.Conn, len(cfg.Conns)),
+ results: make(map[string]*template.Template),
+ }
+ for _, c := range cfg.Conns {
+ s.conns <- c
+ }
var err error
s.main, err = cfg.parse("main.tmpl")
}
}
- s.static = http.FileServer(http.Dir(cfg.StaticPath))
+ s.basedir = cfg.StaticPath
+ s.mux = map[string]handler{
+ "images": s.static,
+ "style": s.static,
+ "graph": s.graph,
+ }
return s, nil
}
args []string
}
-var handlers = map[string]func(request, *Server) (template.HTML, error){
+type handler func(http.ResponseWriter, request)
+
+// Content generators for HTML pages.
+var content = map[string]func(request, *Server) (template.HTML, error){
"": index,
// Queries
}
fields := strings.Split(path, "/")
- if fields[0] == "style" || fields[0] == "images" {
- s.static.ServeHTTP(w, r)
- return
- }
-
req := request{
r: r,
cmd: fields[0],
}
}
- f, ok := handlers[req.cmd]
+ if h := s.mux[fields[0]]; h != nil {
+ h(w, req)
+ return
+ }
+
+ f, ok := content[req.cmd]
if !ok {
s.notfound(w, r)
return
io.Copy(w, &buf)
}
+// static serves static content.
+func (s *Server) static(w http.ResponseWriter, req request) {
+ http.ServeFile(w, req.r, filepath.Clean(filepath.Join(s.basedir, req.r.URL.Path)))
+}
+
// Content handlers.
func index(_ request, s *Server) (template.HTML, error) {
}
func (s *Server) query(cmd string) (interface{}, error) {
+ c := <-s.conns
+ defer func() { s.conns <- c }()
+
m := &proto.Message{
Type: proto.ConnectionQuery,
Raw: []byte(cmd),
}
- if err := s.c.Send(m); err != nil {
+ if err := c.Send(m); err != nil {
return nil, fmt.Errorf("Query %q: %v", cmd, err)
}
for {
- m, err := s.c.Receive()
+ m, err := c.Receive()
if err != nil {
return nil, fmt.Errorf("Failed to receive server response: %v", err)
}
var host sysdb.Host
err = proto.Unmarshal(m, &host)
res = host
+ case proto.Timeseries:
+ var ts sysdb.Timeseries
+ err = proto.Unmarshal(m, &ts)
+ res = ts
default:
return nil, fmt.Errorf("Unsupported data type %d", t)
}