github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/api/middleware.go (about)

     1  package api
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"time"
     7  
     8  	"github.com/gorilla/mux"
     9  	"github.com/qri-io/qri/api/util"
    10  	"github.com/qri-io/qri/dsref"
    11  )
    12  
    13  // Middleware handles request logging
    14  func (s Server) Middleware(handler http.HandlerFunc) http.HandlerFunc {
    15  	return s.mwFunc(handler, true)
    16  }
    17  
    18  // NoLogMiddleware runs middleware without logging the request
    19  func (s Server) NoLogMiddleware(handler http.HandlerFunc) http.HandlerFunc {
    20  	return s.mwFunc(handler, false)
    21  }
    22  
    23  func (s Server) mwFunc(handler http.HandlerFunc, shouldLog bool) http.HandlerFunc {
    24  	return func(w http.ResponseWriter, r *http.Request) {
    25  		if shouldLog {
    26  			log.Infof("%s %s %s", r.Method, r.URL.Path, time.Now())
    27  		}
    28  
    29  		handler.ServeHTTP(w, r)
    30  	}
    31  }
    32  
    33  // corsMiddleware adds Cross-Origin Resource Sharing headers for any request
    34  // who's origin matches one of allowedOrigins
    35  func corsMiddleware(allowedOrigins []string) mux.MiddlewareFunc {
    36  	return func(next http.Handler) http.Handler {
    37  		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    38  			origin := r.Header.Get("Origin")
    39  			for _, o := range allowedOrigins {
    40  				if origin == o {
    41  					w.Header().Set("Access-Control-Allow-Origin", origin)
    42  					w.Header().Set("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS")
    43  					w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Authorization")
    44  					w.Header().Set("Access-Control-Allow-Credentials", "true")
    45  				}
    46  			}
    47  
    48  			// intercept OPTIONS requests with an early return
    49  			if r.Method == http.MethodOptions {
    50  				util.EmptyOkHandler(w, r)
    51  				return
    52  			}
    53  
    54  			next.ServeHTTP(w, r)
    55  		})
    56  	}
    57  }
    58  
    59  // muxVarsToQueryParamMiddleware moves all mux variables to query parameter
    60  // values, failing with an error if a name collision with user-provided query
    61  // params occurs
    62  func muxVarsToQueryParamMiddleware(next http.Handler) http.Handler {
    63  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    64  		if err := setMuxVarsToQueryParams(r); err != nil {
    65  			util.WriteErrResponse(w, http.StatusBadRequest, err)
    66  			return
    67  		}
    68  		next.ServeHTTP(w, r)
    69  	})
    70  }
    71  
    72  func setMuxVarsToQueryParams(r *http.Request) error {
    73  	q := r.URL.Query()
    74  	for varName, val := range mux.Vars(r) {
    75  		if q.Get(varName) != "" {
    76  			return fmt.Errorf("conflict in query param: %s = %s", varName, val)
    77  		}
    78  		q.Add(varName, val)
    79  	}
    80  	r.URL.RawQuery = q.Encode()
    81  	return nil
    82  }
    83  
    84  // refStringMiddleware converts gorilla mux params to a "refstr" query parmeter
    85  // and adds it to an http request
    86  func refStringMiddleware(next http.Handler) http.Handler {
    87  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    88  		setRefStringFromMuxVars(r)
    89  		next.ServeHTTP(w, r)
    90  	})
    91  }
    92  
    93  func setRefStringFromMuxVars(r *http.Request) {
    94  	mvars := mux.Vars(r)
    95  	ref := dsref.Ref{
    96  		Username: mvars["username"],
    97  		Name:     mvars["name"],
    98  		Path:     muxVarsPath(mvars),
    99  	}
   100  
   101  	if refstr := ref.String(); refstr != "" {
   102  		q := r.URL.Query()
   103  		q.Add("ref", refstr)
   104  		r.URL.RawQuery = q.Encode()
   105  	}
   106  }
   107  
   108  func muxVarsPath(mvars map[string]string) string {
   109  	fs := mvars["fs"]
   110  	hash := mvars["hash"]
   111  	if fs != "" && hash != "" {
   112  		return fmt.Sprintf("/%s/%s", fs, hash)
   113  	}
   114  	return ""
   115  }