1 // Copyright (C) 2016 Sebastian 'tokkee' Harl <sh@tokkee.org>
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions
6 // are met:
7 // 1. Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 // 2. Redistributions in binary form must reproduce the above copyright
10 // notice, this list of conditions and the following disclaimer in the
11 // documentation and/or other materials provided with the distribution.
12 //
13 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14 // ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
15 // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
17 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 // server is the backend server implementation.
26 package main
28 import (
29 "flag"
30 "log"
31 "net"
32 "strconv"
33 "strings"
35 "golang.org/x/net/context"
37 "google.golang.org/grpc"
38 "google.golang.org/grpc/codes"
39 "google.golang.org/grpc/peer"
41 pb "tokkee.org/go-talk/grpc/proto/backend"
42 )
44 var (
45 listen = flag.String("listen", ":50051", "listening address")
46 )
48 // A server implements the Backend service.
49 type server struct{}
51 // Query runs the user-provided query and returns the result.
52 func (*server) Query(ctx context.Context, in *pb.QueryRequest) (*pb.QueryReply, error) {
53 if peer, ok := peer.FromContext(ctx); ok {
54 log.Printf("Query() called from %v", peer)
55 } else {
56 log.Print("No peer information available")
57 }
59 t, n, err := runQuery(in.Query)
60 if err != nil {
61 return nil, err
62 }
64 return &pb.QueryReply{
65 Type: t,
66 N: n,
67 }, nil
68 }
70 // runQuery executes a query. It implements a highly sophisticated query
71 // engine.
72 func runQuery(q string) (string, int64, error) {
73 fields := strings.SplitN(q, " ", 2)
74 if len(fields) != 2 {
75 return "", 0, grpc.Errorf(codes.InvalidArgument, "invalid query %q: want <cmd> <arg>", q)
76 }
78 var n int64
79 cmd, arg := strings.ToUpper(fields[0]), fields[1]
80 switch cmd {
81 case "COUNT":
82 n = int64(len(arg))
83 case "RANDOM":
84 i, err := strconv.Atoi(arg)
85 if err != nil {
86 return "", 0, grpc.Errorf(codes.InvalidArgument, "RANDOM: %v", err)
87 }
89 // Chosen by fair dice roll.
90 n = 4
91 if i <= 4 {
92 n = 0
93 }
94 default:
95 return "", 0, grpc.Errorf(codes.InvalidArgument, "unknown query command %q", cmd)
96 }
98 return cmd, n, nil
99 }
101 func main() {
102 flag.Parse()
104 l, err := net.Listen("tcp", *listen)
105 if err != nil {
106 log.Fatalf("Failed to listen on %s: %v", *listen, err)
107 }
109 log.Printf("Listening on %s ...", *listen)
110 s := grpc.NewServer()
111 pb.RegisterBackendServer(s, &server{})
112 s.Serve(l)
113 }