github.com/machinebox/remoto@v0.1.2-0.20191024144331-eff21a7d321f/go/remotohttp/server.go (about)

     1  package remotohttp
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"net/http"
     7  	"sync"
     8  
     9  	"github.com/machinebox/remoto/go/remotohttp/remototypes"
    10  )
    11  
    12  // Server is an HTTP server for serving Remoto requests.
    13  type Server struct {
    14  	handlers sync.Map
    15  
    16  	// NotFound handles 404 responses.
    17  	NotFound http.Handler
    18  
    19  	// OnErr is called when there has been a system level error,
    20  	// like encoding/decoding.
    21  	OnErr func(w http.ResponseWriter, r *http.Request, err error)
    22  }
    23  
    24  // NewServer makes a new Server.
    25  func NewServer() *Server {
    26  	return &Server{
    27  		NotFound: http.NotFoundHandler(),
    28  		OnErr: func(w http.ResponseWriter, r *http.Request, err error) {
    29  			http.Error(w, err.Error(), http.StatusInternalServerError)
    30  		},
    31  	}
    32  }
    33  
    34  // Register registers the path with the http.Handler.
    35  func (srv *Server) Register(path string, fn http.Handler) {
    36  	srv.handlers.Store(path, fn)
    37  }
    38  
    39  // ServeHTTP calls the registered handler
    40  func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    41  	if r.Method != http.MethodPost {
    42  		if srv.NotFound != nil {
    43  			srv.NotFound.ServeHTTP(w, r)
    44  			return
    45  		}
    46  		http.NotFound(w, r)
    47  		return
    48  	}
    49  	h, ok := srv.handlers.Load(r.URL.Path)
    50  	if !ok {
    51  		if srv.NotFound != nil {
    52  			srv.NotFound.ServeHTTP(w, r)
    53  			return
    54  		}
    55  		http.NotFound(w, r)
    56  		return
    57  	}
    58  	handler, ok := h.(http.Handler)
    59  	if !ok {
    60  		panic("remotohttp: handler is the wrong type")
    61  	}
    62  	opener := func(_ context.Context, file remototypes.File) (io.ReadCloser, error) {
    63  		f, _, err := r.FormFile(file.Fieldname)
    64  		return f, err
    65  	}
    66  	r = r.WithContext(remototypes.WithOpener(r.Context(), opener))
    67  	handler.ServeHTTP(w, r)
    68  }
    69  
    70  // Describe an overview of the endpoints to the specified io.Writer.
    71  func (srv *Server) Describe(w io.Writer) error {
    72  	var err error
    73  	srv.handlers.Range(func(k, v interface{}) bool {
    74  		if _, err = io.WriteString(w, "endpoint: "+k.(string)+"\n"); err != nil {
    75  			return false
    76  		}
    77  		return true
    78  	})
    79  	return err
    80  }
    81  
    82  // Error is an error wrapper for responses.
    83  type Error struct {
    84  	Err error `json:"error"`
    85  }