github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/server/components/notifications/http.go (about) 1 // Copyright 2019 Google LLC. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package notifications defines basic Listener/Notification support for generic 16 // Fleetspeak servers. 17 package notifications 18 19 import ( 20 "context" 21 "fmt" 22 "net" 23 "net/http" 24 "net/url" 25 "path" 26 "sync" 27 28 log "github.com/golang/glog" 29 30 "github.com/google/fleetspeak/fleetspeak/src/common" 31 ) 32 33 // HttpListener is an implementation of the fleetspeak notifications.Listener 34 // interface which listens for notifications over HTTP. 35 // 36 // The port opened by this listener should not be exposed to public. Only other 37 // Fleetspeak servers will need to be able to reach it. 38 type HttpListener struct { 39 // The address that HttpListener should bind to. 40 BindAddress string 41 42 // The address that other servers can find this one at. If unset, a best guess 43 // will be set by Start(). 44 AdvertisedAddress string 45 46 listener net.Listener 47 working sync.WaitGroup 48 out chan<- common.ClientID 49 } 50 51 func (l *HttpListener) Start() (<-chan common.ClientID, error) { 52 li, err := net.Listen("tcp", l.BindAddress) 53 if err != nil { 54 return nil, err 55 } 56 57 l.listener = li 58 if l.AdvertisedAddress == "" { 59 l.AdvertisedAddress = li.Addr().String() 60 } 61 62 c := make(chan common.ClientID) 63 l.out = c 64 65 l.working.Add(1) 66 go l.runServer() 67 return c, nil 68 } 69 70 func (l *HttpListener) Stop() { 71 l.listener.Close() 72 l.working.Wait() 73 } 74 75 func (l *HttpListener) Address() string { 76 return l.AdvertisedAddress 77 } 78 79 func (l *HttpListener) runServer() { 80 defer l.working.Done() 81 82 log.Infof("Starting http notification listener at: %s", l.listener.Addr().String()) 83 err := http.Serve(l.listener, http.HandlerFunc(l.handle)) 84 log.Infof("Http notification listener stopped: %v", err) 85 close(l.out) 86 } 87 88 func (l *HttpListener) handle(w http.ResponseWriter, r *http.Request) { 89 dir, name := path.Split(r.URL.EscapedPath()) 90 if dir != "/client/" { 91 http.Error(w, "not found", http.StatusNotFound) 92 return 93 } 94 id, err := common.StringToClientID(name) 95 if err != nil { 96 http.Error(w, fmt.Sprintf("Failed to parse client id [%s]: %v", name, err), http.StatusBadRequest) 97 return 98 } 99 if id.IsNil() { 100 http.Error(w, fmt.Sprintf("Client id is required."), http.StatusBadRequest) 101 return 102 } 103 if r.Method != "POST" { 104 http.Error(w, fmt.Sprintf("Unexpected method: %s", r.Method), http.StatusBadRequest) 105 return 106 } 107 108 l.out <- id 109 w.WriteHeader(http.StatusOK) 110 } 111 112 // HttpNotifier is an implementation of the fleetspeak notifications.Notifier 113 // interface which is compatible with HttpListener. 114 type HttpNotifier struct { 115 c http.Client 116 } 117 118 func (n *HttpNotifier) NewMessageForClient(ctx context.Context, target string, id common.ClientID) error { 119 req, err := http.NewRequest("POST", (&url.URL{ 120 Scheme: "http", 121 Host: target, 122 Path: fmt.Sprintf("/client/%s", id.String()), 123 }).String(), nil) 124 if err != nil { 125 return err 126 } 127 128 _, err = n.c.Do(req.WithContext(ctx)) 129 return err 130 }