get_short.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. package web
  2. import (
  3. "fmt"
  4. "net/http"
  5. "time"
  6. "code.osinet.fr/fgm/kurz/domain"
  7. "github.com/gorilla/mux"
  8. )
  9. /*
  10. Template variables:
  11. - FullyQualifiedAssetsBaseURL http://plusvite-cdn.osinet.eu
  12. - FullyQualifiedShortURL (short string)
  13. - FullyQualifiedSiteURL http://plusvite.net/
  14. - FullyQualifiedTargetURL (target string)
  15. - RefreshDelay (seconds int)
  16. - SiteName PlusVite
  17. */
  18. type bytes = []byte
  19. func build403(w http.ResponseWriter, short string) (bytes, error) {
  20. type Short403Data struct {
  21. FullyQualifiedAssetsBaseURL string
  22. FullyQualifiedShortURL string
  23. FullyQualifiedSiteURL string
  24. RefreshDelay time.Duration
  25. SiteName string
  26. }
  27. data := Short403Data{}
  28. tmpl.Execute(w, data)
  29. return bytes(""), nil
  30. }
  31. func build404(w http.ResponseWriter, short string) (bytes, error) {
  32. type Short404Data struct {
  33. AssetsVersion int
  34. FullyQualifiedAssetsBaseURL string
  35. FullyQualifiedShortURL string
  36. FullyQualifiedSiteURL string
  37. FullyQualifiedTargetURL string
  38. RefreshDelay time.Duration
  39. SiteName string
  40. }
  41. data := Short404Data{}
  42. err := tmpl.Execute(w, data)
  43. if err != nil {
  44. fmt.Println(err)
  45. }
  46. return bytes(""), nil
  47. }
  48. func build451(w http.ResponseWriter, short string) (bytes, error) {
  49. return nil, nil
  50. }
  51. // handleGetShort handles path /<short>
  52. func handleGetShort(w http.ResponseWriter, r *http.Request) {
  53. short, ok := mux.Vars(r)["short"]
  54. if !ok {
  55. w.WriteHeader(http.StatusBadRequest)
  56. return
  57. }
  58. target, err := domain.GetTargetURL(short)
  59. // Happy path.
  60. if err == nil {
  61. w.Header().Set("Location", target)
  62. w.WriteHeader(http.StatusTemporaryRedirect)
  63. return
  64. }
  65. // Very sad path.
  66. domainErr, ok := err.(domain.Error)
  67. if !ok {
  68. // All errors return by the API should be domain-specific errors.
  69. w.WriteHeader(http.StatusInternalServerError)
  70. return
  71. }
  72. // Normal sad paths.
  73. // TODO improve UX: build pages instead of just HTTP 4xx.
  74. var status int
  75. switch domainErr.Kind {
  76. case domain.ShortNotFound:
  77. status = http.StatusNotFound
  78. if page, err := build404(w, short); err != nil {
  79. status = http.StatusInternalServerError
  80. w.WriteHeader(status)
  81. } else {
  82. w.WriteHeader(status)
  83. w.Write(page)
  84. }
  85. case domain.TargetBlockedError:
  86. status = http.StatusForbidden
  87. if page, err := build403(w, short); err != nil {
  88. status = http.StatusInternalServerError
  89. w.WriteHeader(status)
  90. } else {
  91. w.WriteHeader(status)
  92. w.Write(page)
  93. }
  94. case domain.TargetCensoredError:
  95. status = http.StatusUnavailableForLegalReasons
  96. if page, err := build451(w, short); err != nil {
  97. status = http.StatusInternalServerError
  98. w.WriteHeader(status)
  99. } else {
  100. w.WriteHeader(status)
  101. w.Write(page)
  102. }
  103. default:
  104. // TargetInvalid is not supposed to happen in this case, so it is an internal error too.
  105. status = http.StatusInternalServerError
  106. w.WriteHeader(status)
  107. }
  108. }