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  }