server.go 2.4 KB

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