server.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  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. "bytes"
  7. "net/http"
  8. "path"
  9. "strings"
  10. "time"
  11. )
  12. type captchaHandler struct {
  13. imgWidth int
  14. imgHeight int
  15. }
  16. // Server returns a handler that serves HTTP requests with image or
  17. // audio representations of captchas. Image dimensions are accepted as
  18. // arguments. The server decides which captcha to serve based on the last URL
  19. // path component: file name part must contain a captcha id, file extension —
  20. // its format (PNG or WAV).
  21. //
  22. // For example, for file name "LBm5vMjHDtdUfaWYXiQX.png" it serves an image captcha
  23. // with id "LBm5vMjHDtdUfaWYXiQX", and for "LBm5vMjHDtdUfaWYXiQX.wav" it serves the
  24. // same captcha in audio format.
  25. //
  26. // To serve a captcha as a downloadable file, the URL must be constructed in
  27. // such a way as if the file to serve is in the "download" subdirectory:
  28. // "/download/LBm5vMjHDtdUfaWYXiQX.wav".
  29. //
  30. // To reload captcha (get a different solution for the same captcha id), append
  31. // "?reload=x" to URL, where x may be anything (for example, current time or a
  32. // random number to make browsers refetch an image instead of loading it from
  33. // cache).
  34. //
  35. // By default, the Server serves audio in English language. To serve audio
  36. // captcha in one of the other supported languages, append "lang" value, for
  37. // example, "?lang=ru".
  38. func Server(imgWidth, imgHeight int) http.Handler {
  39. return &captchaHandler{imgWidth, imgHeight}
  40. }
  41. func (h *captchaHandler) serve(w http.ResponseWriter, r *http.Request, id, ext, lang string, download bool) error {
  42. w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
  43. w.Header().Set("Pragma", "no-cache")
  44. w.Header().Set("Expires", "0")
  45. var content bytes.Buffer
  46. switch ext {
  47. case ".png":
  48. w.Header().Set("Content-Type", "image/png")
  49. WriteImage(&content, id, h.imgWidth, h.imgHeight)
  50. case ".wav":
  51. w.Header().Set("Content-Type", "audio/x-wav")
  52. WriteAudio(&content, id, lang)
  53. default:
  54. return ErrNotFound
  55. }
  56. if download {
  57. w.Header().Set("Content-Type", "application/octet-stream")
  58. }
  59. http.ServeContent(w, r, id+ext, time.Time{}, bytes.NewReader(content.Bytes()))
  60. return nil
  61. }
  62. func (h *captchaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  63. dir, file := path.Split(r.URL.Path)
  64. ext := path.Ext(file)
  65. id := file[:len(file)-len(ext)]
  66. if ext == "" || id == "" {
  67. http.NotFound(w, r)
  68. return
  69. }
  70. if r.FormValue("reload") != "" {
  71. Reload(id)
  72. }
  73. lang := strings.ToLower(r.FormValue("lang"))
  74. download := path.Base(dir) == "download"
  75. if h.serve(w, r, id, ext, lang, download) == ErrNotFound {
  76. http.NotFound(w, r)
  77. }
  78. // Ignore other errors.
  79. }