restendpointhelper.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. package utils
  2. import (
  3. "errors"
  4. "fmt"
  5. "net/http"
  6. "strings"
  7. )
  8. const restURLPatternHeaderKey = "Owncast-Resturl-Pattern"
  9. // takes the segment pattern of an Url string and returns the segment before the first dynamic REST parameter.
  10. func getPatternForRestEndpoint(pattern string) string {
  11. firstIndex := strings.Index(pattern, "/{")
  12. if firstIndex == -1 {
  13. return pattern
  14. }
  15. return strings.TrimRight(pattern[:firstIndex], "/") + "/"
  16. }
  17. func zip2D(iterable1 *[]string, iterable2 *[]string) map[string]string {
  18. dict := make(map[string]string)
  19. for index, key := range *iterable1 {
  20. dict[key] = (*iterable2)[index]
  21. }
  22. return dict
  23. }
  24. func mapPatternWithRequestURL(pattern string, requestURL string) (map[string]string, error) {
  25. patternSplit := strings.Split(pattern, "/")
  26. requestURLSplit := strings.Split(requestURL, "/")
  27. if len(patternSplit) == len(requestURLSplit) {
  28. return zip2D(&patternSplit, &requestURLSplit), nil
  29. }
  30. return nil, errors.New("the length of pattern and request Url does not match")
  31. }
  32. func readParameter(pattern string, requestURL string, paramName string) (string, error) {
  33. all, err := mapPatternWithRequestURL(pattern, requestURL)
  34. if err != nil {
  35. return "", err
  36. }
  37. if value, exists := all[fmt.Sprintf("{%s}", paramName)]; exists {
  38. return value, nil
  39. }
  40. return "", fmt.Errorf("parameter with name %s not found", paramName)
  41. }
  42. // ReadRestURLParameter will return the parameter from the request of the requested name.
  43. func ReadRestURLParameter(r *http.Request, parameterName string) (string, error) {
  44. pattern, found := r.Header[restURLPatternHeaderKey]
  45. if !found {
  46. return "", fmt.Errorf("this HandlerFunc is not marked as REST-Endpoint. Cannot read Parameter '%s' from Request", parameterName)
  47. }
  48. return readParameter(pattern[0], r.URL.Path, parameterName)
  49. }
  50. // RestEndpoint wraps a handler to use the rest endpoint helper.
  51. func RestEndpoint(pattern string, handler http.HandlerFunc) (string, http.HandlerFunc) {
  52. baseURL := getPatternForRestEndpoint(pattern)
  53. return baseURL, func(w http.ResponseWriter, r *http.Request) {
  54. r.Header[restURLPatternHeaderKey] = []string{pattern}
  55. handler(w, r)
  56. }
  57. }