server.go 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. // Copyright 2011 Dmitry Chestnykh. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package captcha
  5. import (
  6. "net/http"
  7. "path"
  8. "strconv"
  9. "strings"
  10. )
  11. type captchaHandler struct {
  12. imgWidth int
  13. imgHeight int
  14. }
  15. // Server returns a handler that serves HTTP requests with image or
  16. // audio representations of captchas. Image dimensions are accepted as
  17. // arguments. The server decides which captcha to serve based on the last URL
  18. // path component: file name part must contain a captcha id, file extension —
  19. // its format (PNG or WAV).
  20. //
  21. // For example, for file name "LBm5vMjHDtdUfaWYXiQX.png" it serves an image captcha
  22. // with id "LBm5vMjHDtdUfaWYXiQX", and for "LBm5vMjHDtdUfaWYXiQX.wav" it serves the
  23. // same captcha in audio format.
  24. //
  25. // To serve a captcha as a downloadable file, the URL must be constructed in
  26. // such a way as if the file to serve is in the "download" subdirectory:
  27. // "/download/LBm5vMjHDtdUfaWYXiQX.wav".
  28. //
  29. // To reload captcha (get a different solution for the same captcha id), append
  30. // "?reload=x" to URL, where x may be anything (for example, current time or a
  31. // random number to make browsers refetch an image instead of loading it from
  32. // cache).
  33. //
  34. // By default, the Server serves audio in English language. To serve audio
  35. // captcha in one of the other supported languages, append "lang" value, for
  36. // example, "?lang=ru".
  37. func Server(imgWidth, imgHeight int) http.Handler {
  38. return &captchaHandler{imgWidth, imgHeight}
  39. }
  40. func (h *captchaHandler) serve(w http.ResponseWriter, id, ext string, lang string, download bool) error {
  41. w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
  42. w.Header().Set("Pragma", "no-cache")
  43. w.Header().Set("Expires", "0")
  44. if download {
  45. w.Header().Set("Content-Type", "application/octet-stream")
  46. }
  47. switch ext {
  48. case ".png":
  49. if !download {
  50. w.Header().Set("Content-Type", "image/png")
  51. }
  52. return WriteImage(w, id, h.imgWidth, h.imgHeight)
  53. case ".wav":
  54. //XXX(dchest) Workaround for Chrome: it wants content-length,
  55. //or else will start playing NOT from the beginning.
  56. //Filed issue: http://code.google.com/p/chromium/issues/detail?id=80565
  57. d := globalStore.Get(id, false)
  58. if d == nil {
  59. return ErrNotFound
  60. }
  61. a := NewAudio(id, d, lang)
  62. if !download {
  63. w.Header().Set("Content-Type", "audio/x-wav")
  64. }
  65. w.Header().Set("Content-Length", strconv.Itoa(a.EncodedLen()))
  66. _, err := a.WriteTo(w)
  67. return err
  68. }
  69. return ErrNotFound
  70. }
  71. func (h *captchaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  72. dir, file := path.Split(r.URL.Path)
  73. ext := path.Ext(file)
  74. id := file[:len(file)-len(ext)]
  75. if ext == "" || id == "" {
  76. http.NotFound(w, r)
  77. return
  78. }
  79. if r.FormValue("reload") != "" {
  80. Reload(id)
  81. }
  82. lang := strings.ToLower(r.FormValue("lang"))
  83. download := path.Base(dir) == "download"
  84. if h.serve(w, id, ext, lang, download) == ErrNotFound {
  85. http.NotFound(w, r)
  86. }
  87. // Ignore other errors.
  88. }