3 * Copyright 2015 gRPC authors.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
19 //go:generate protoc -I ../routeguide --go_out=plugins=grpc:../routeguide ../routeguide/route_guide.proto
21 // Package main implements a simple gRPC server that demonstrates how to use gRPC-Go libraries
22 // to perform unary, client streaming, server streaming and full duplex RPCs.
24 // It implements the route guide service whose definition can be found in routeguide/route_guide.proto.
38 "golang.org/x/net/context"
39 "google.golang.org/grpc"
41 "google.golang.org/grpc/credentials"
42 "google.golang.org/grpc/testdata"
44 "github.com/golang/protobuf/proto"
46 pb "google.golang.org/grpc/examples/route_guide/routeguide"
50 tls = flag.Bool("tls", false, "Connection uses TLS if true, else plain TCP")
51 certFile = flag.String("cert_file", "", "The TLS cert file")
52 keyFile = flag.String("key_file", "", "The TLS key file")
53 jsonDBFile = flag.String("json_db_file", "testdata/route_guide_db.json", "A json file containing a list of features")
54 port = flag.Int("port", 10000, "The server port")
57 type routeGuideServer struct {
58 savedFeatures []*pb.Feature
59 routeNotes map[string][]*pb.RouteNote
62 // GetFeature returns the feature at the given point.
63 func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) {
64 for _, feature := range s.savedFeatures {
65 if proto.Equal(feature.Location, point) {
69 // No feature was found, return an unnamed feature
70 return &pb.Feature{Location: point}, nil
73 // ListFeatures lists all features contained within the given bounding Rectangle.
74 func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error {
75 for _, feature := range s.savedFeatures {
76 if inRange(feature.Location, rect) {
77 if err := stream.Send(feature); err != nil {
85 // RecordRoute records a route composited of a sequence of points.
87 // It gets a stream of points, and responds with statistics about the "trip":
88 // number of points, number of known features visited, total distance traveled, and
90 func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) error {
91 var pointCount, featureCount, distance int32
92 var lastPoint *pb.Point
93 startTime := time.Now()
95 point, err := stream.Recv()
98 return stream.SendAndClose(&pb.RouteSummary{
99 PointCount: pointCount,
100 FeatureCount: featureCount,
102 ElapsedTime: int32(endTime.Sub(startTime).Seconds()),
109 for _, feature := range s.savedFeatures {
110 if proto.Equal(feature.Location, point) {
114 if lastPoint != nil {
115 distance += calcDistance(lastPoint, point)
121 // RouteChat receives a stream of message/location pairs, and responds with a stream of all
122 // previous messages at each of those locations.
123 func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error {
125 in, err := stream.Recv()
132 key := serialize(in.Location)
133 if _, present := s.routeNotes[key]; !present {
134 s.routeNotes[key] = []*pb.RouteNote{in}
136 s.routeNotes[key] = append(s.routeNotes[key], in)
138 for _, note := range s.routeNotes[key] {
139 if err := stream.Send(note); err != nil {
146 // loadFeatures loads features from a JSON file.
147 func (s *routeGuideServer) loadFeatures(filePath string) {
148 file, err := ioutil.ReadFile(filePath)
150 log.Fatalf("Failed to load default features: %v", err)
152 if err := json.Unmarshal(file, &s.savedFeatures); err != nil {
153 log.Fatalf("Failed to load default features: %v", err)
157 func toRadians(num float64) float64 {
158 return num * math.Pi / float64(180)
161 // calcDistance calculates the distance between two points using the "haversine" formula.
162 // This code was taken from http://www.movable-type.co.uk/scripts/latlong.html.
163 func calcDistance(p1 *pb.Point, p2 *pb.Point) int32 {
164 const CordFactor float64 = 1e7
165 const R float64 = float64(6371000) // metres
166 lat1 := float64(p1.Latitude) / CordFactor
167 lat2 := float64(p2.Latitude) / CordFactor
168 lng1 := float64(p1.Longitude) / CordFactor
169 lng2 := float64(p2.Longitude) / CordFactor
170 φ1 := toRadians(lat1)
171 φ2 := toRadians(lat2)
172 Δφ := toRadians(lat2 - lat1)
173 Δλ := toRadians(lng2 - lng1)
175 a := math.Sin(Δφ/2)*math.Sin(Δφ/2) +
176 math.Cos(φ1)*math.Cos(φ2)*
177 math.Sin(Δλ/2)*math.Sin(Δλ/2)
178 c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
181 return int32(distance)
184 func inRange(point *pb.Point, rect *pb.Rectangle) bool {
185 left := math.Min(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude))
186 right := math.Max(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude))
187 top := math.Max(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude))
188 bottom := math.Min(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude))
190 if float64(point.Longitude) >= left &&
191 float64(point.Longitude) <= right &&
192 float64(point.Latitude) >= bottom &&
193 float64(point.Latitude) <= top {
199 func serialize(point *pb.Point) string {
200 return fmt.Sprintf("%d %d", point.Latitude, point.Longitude)
203 func newServer() *routeGuideServer {
204 s := new(routeGuideServer)
205 s.loadFeatures(*jsonDBFile)
206 s.routeNotes = make(map[string][]*pb.RouteNote)
212 lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port))
214 log.Fatalf("failed to listen: %v", err)
216 var opts []grpc.ServerOption
219 *certFile = testdata.Path("server1.pem")
222 *keyFile = testdata.Path("server1.key")
224 creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)
226 log.Fatalf("Failed to generate credentials %v", err)
228 opts = []grpc.ServerOption{grpc.Creds(creds)}
230 grpcServer := grpc.NewServer(opts...)
231 pb.RegisterRouteGuideServer(grpcServer, newServer())
232 grpcServer.Serve(lis)