server.go (view raw)
1package captcha
2
3import (
4 "http"
5 "os"
6 "path"
7 "strconv"
8)
9
10type captchaHandler struct {
11 imgWidth int
12 imgHeight int
13}
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 "B9QTvDV1RXbVJ3Ac.png" it serves an image captcha
22// with id "B9QTvDV1RXbVJ3Ac", and for "B9QTvDV1RXbVJ3Ac.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 "download" subdirectory:
27// "/download/B9QTvDV1RXbVJ3Ac.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).
33func Server(w, h int) http.Handler { return &captchaHandler{w, h} }
34
35func (h *captchaHandler) serve(w http.ResponseWriter, id, ext string, download bool) os.Error {
36 if download {
37 w.Header().Set("Content-Type", "application/octet-stream")
38 }
39 switch ext {
40 case ".png":
41 if !download {
42 w.Header().Set("Content-Type", "image/png")
43 }
44 return WriteImage(w, id, h.imgWidth, h.imgHeight)
45 case ".wav":
46 if !download {
47 w.Header().Set("Content-Type", "audio/x-wav")
48 }
49 //return WriteAudio(w, id)
50 //XXX(dchest) Workaround for Chrome: it wants content-length,
51 //or else will start playing NOT from the beginning.
52 //Filed issue: http://code.google.com/p/chromium/issues/detail?id=80565
53 d := globalStore.Get(id, false)
54 if d == nil {
55 return ErrNotFound
56 }
57 a := NewAudio(d)
58 w.Header().Set("Content-Length", strconv.Itoa(a.EncodedLen()))
59 _, err := a.WriteTo(w)
60 return err
61 }
62 return ErrNotFound
63}
64
65func (h *captchaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
66 dir, file := path.Split(r.URL.Path)
67 ext := path.Ext(file)
68 id := file[:len(file)-len(ext)]
69 if ext == "" || id == "" {
70 http.NotFound(w, r)
71 return
72 }
73 if r.FormValue("reload") != "" {
74 Reload(id)
75 }
76 download := path.Base(dir) == "download"
77 if err := h.serve(w, id, ext, download); err != nil {
78 if err == ErrNotFound {
79 http.NotFound(w, r)
80 return
81 }
82 http.Error(w, "error serving captcha", http.StatusInternalServerError)
83 }
84}