Compare commits
5 Commits
93e1344846
...
29e4666bd9
Author | SHA1 | Date |
---|---|---|
Alexander Andreev | 29e4666bd9 | |
Alexander Andreev | 6e4242e6cd | |
Alexander Andreev | c0d776fa40 | |
Alexander Andreev | cebf4b572f | |
Alexander Andreev | be2f34f629 |
|
@ -53,7 +53,6 @@ Responds with an empty body and one of the HTTP codes.
|
|||
#### HTTP codes
|
||||
- `202` if solved
|
||||
- `403` if not solved
|
||||
- `404` if doesn't exist
|
||||
|
||||
### Check if captcha is solved
|
||||
|
||||
|
@ -63,8 +62,7 @@ Responds with an empty body and one of the HTTP codes.
|
|||
|
||||
If an optional `remove` parameter without a value supplied CAPTCHA will be
|
||||
removed without checking and a HTTP code `204` will be sent. Otherwise, a `403`
|
||||
HTTP code will be sent if it is not solved. If a CAPTCHA doesn't exist a `404`
|
||||
HTTP code still will be returned.
|
||||
HTTP code will be sent if it is not solved.
|
||||
|
||||
A `remove` parameter was added because browsers will print an error in a console
|
||||
if HTTP code is not within `2xx`, so to keep a console clean you can provide
|
||||
|
@ -76,7 +74,6 @@ to be expired. E.g. when a visitor requests for a new CAPTCHA or leaving a page.
|
|||
#### HTTP codes
|
||||
- `204` if solved
|
||||
- `403` if not solved
|
||||
- `404` if doesn't exist
|
||||
|
||||
### Example of interaction
|
||||
|
||||
|
|
|
@ -11,9 +11,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
const errMsgNotFound = "CAPTCHA with such ID doesn't exist"
|
||||
const errMsgWrongAnswer = "An answer provided was wrong"
|
||||
const errMsgFailedToGenImage = "failed to generate an image for a CAPTCHA"
|
||||
const errMsgImageNotFound = "cannot get an image for a non-existing CAPTCHA"
|
||||
|
||||
type CaptchaHandlers struct {
|
||||
|
@ -36,16 +34,9 @@ func (h *CaptchaHandlers) Image(w http.ResponseWriter, r *http.Request) {
|
|||
captchaID := captcha.ID(server.GetURLParam(r, "captcha"))
|
||||
captchaStyle := r.URL.Query().Get("style")
|
||||
|
||||
captchaImage, err := inmemdb.Image(captchaID, captchaStyle)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, errMsgImageNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
captchaImage := inmemdb.Image(captchaID, captchaStyle)
|
||||
if captchaImage == nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprint(w, errMsgFailedToGenImage)
|
||||
http.Error(w, errMsgImageNotFound, http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -60,16 +51,8 @@ func (h *CaptchaHandlers) Solve(w http.ResponseWriter, r *http.Request) {
|
|||
r.ParseForm()
|
||||
answer := captcha.Answer(r.FormValue("answer"))
|
||||
|
||||
ok, err := inmemdb.Solve(captchaID, answer)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, errMsgNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if !ok {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
fmt.Fprint(w, errMsgWrongAnswer)
|
||||
if ok := inmemdb.Solve(captchaID, answer); !ok {
|
||||
http.Error(w, errMsgWrongAnswer, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -80,22 +63,16 @@ func (h *CaptchaHandlers) IsSolved(w http.ResponseWriter, r *http.Request) {
|
|||
captchaID := captcha.ID(server.GetURLParam(r, "captcha"))
|
||||
isJustRemove := r.URL.Query().Has("remove")
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
if isJustRemove {
|
||||
inmemdb.Remove(captchaID)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
solved, err := inmemdb.IsSolved(captchaID)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, errMsgNotFound)
|
||||
if solved := inmemdb.IsSolved(captchaID); !solved {
|
||||
http.Error(w, errMsgWrongAnswer, http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
if !solved {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
fmt.Fprint(w, errMsgWrongAnswer)
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
|
|
@ -4,14 +4,11 @@ import (
|
|||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"image"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ErrorNotFound = errors.New("captcha not found")
|
||||
|
||||
const DefaultExpiredScanInterval = 60 * time.Second
|
||||
|
||||
// ID is a CAPTCHA identifier.
|
||||
|
@ -38,8 +35,8 @@ type CaptchaDB interface {
|
|||
New(data string, captcha Captcha) (Captcha, ID)
|
||||
GetExpiry() time.Duration
|
||||
SetExpiry(expiry time.Duration)
|
||||
Image(id ID, style string) (*image.Image, error)
|
||||
Solve(id ID, answer Answer) (bool, error)
|
||||
IsSolved(id ID) (bool, error)
|
||||
Remove(id ID) error
|
||||
Image(id ID, style string) *image.Image
|
||||
Solve(id ID, answer Answer) bool
|
||||
IsSolved(id ID) bool
|
||||
Remove(id ID)
|
||||
}
|
||||
|
|
|
@ -61,18 +61,18 @@ func (imcdb *InMemoryCaptchaDB) SetExpiry(expiry time.Duration) {
|
|||
}
|
||||
|
||||
// Image returns a freshly generated image for a CAPTCHA.
|
||||
func (imcdb *InMemoryCaptchaDB) Image(id captcha.ID, style string) (*image.Image, error) {
|
||||
func (imcdb *InMemoryCaptchaDB) Image(id captcha.ID, style string) *image.Image {
|
||||
imcdb.Lock()
|
||||
defer imcdb.Unlock()
|
||||
if c, ok := imcdb.db[id]; ok {
|
||||
return c.Image(style), nil
|
||||
return c.Image(style)
|
||||
}
|
||||
return nil, captcha.ErrorNotFound
|
||||
return nil
|
||||
}
|
||||
|
||||
// Solve compares given answer with a stored one and if failed
|
||||
// deletes a CAPTCHA from database.
|
||||
func (imcdb *InMemoryCaptchaDB) Solve(id captcha.ID, answer captcha.Answer) (bool, error) {
|
||||
func (imcdb *InMemoryCaptchaDB) Solve(id captcha.ID, answer captcha.Answer) bool {
|
||||
imcdb.Lock()
|
||||
defer imcdb.Unlock()
|
||||
if c, ok := imcdb.db[id]; ok {
|
||||
|
@ -80,32 +80,30 @@ func (imcdb *InMemoryCaptchaDB) Solve(id captcha.ID, answer captcha.Answer) (boo
|
|||
if !ok {
|
||||
delete(imcdb.db, id)
|
||||
}
|
||||
return ok, nil
|
||||
return ok
|
||||
}
|
||||
return false, captcha.ErrorNotFound
|
||||
return false
|
||||
}
|
||||
|
||||
// IsSolved checks if CAPTCHA was solved and removes it
|
||||
// from a database.
|
||||
func (imcdb *InMemoryCaptchaDB) IsSolved(id captcha.ID) (bool, error) {
|
||||
func (imcdb *InMemoryCaptchaDB) IsSolved(id captcha.ID) bool {
|
||||
imcdb.Lock()
|
||||
defer imcdb.Unlock()
|
||||
if c, ok := imcdb.db[id]; ok {
|
||||
delete(imcdb.db, id)
|
||||
return c.IsSolved(), nil
|
||||
return c.IsSolved()
|
||||
}
|
||||
return false, captcha.ErrorNotFound
|
||||
return false
|
||||
}
|
||||
|
||||
// Remove a CAPTCHA from a database.
|
||||
func (imcdb *InMemoryCaptchaDB) Remove(id captcha.ID) error {
|
||||
func (imcdb *InMemoryCaptchaDB) Remove(id captcha.ID) {
|
||||
imcdb.Lock()
|
||||
defer imcdb.Unlock()
|
||||
if _, ok := imcdb.db[id]; ok {
|
||||
delete(imcdb.db, id)
|
||||
return nil
|
||||
}
|
||||
return captcha.ErrorNotFound
|
||||
}
|
||||
|
||||
// cleanExpired removes expired CAPTCHAs in a loop.
|
||||
|
@ -140,18 +138,18 @@ func New(data string, captcha captcha.Captcha) (captcha.Captcha, captcha.ID) {
|
|||
return imcdb.New(data, captcha)
|
||||
}
|
||||
|
||||
func Image(id captcha.ID, style string) (*image.Image, error) {
|
||||
func Image(id captcha.ID, style string) *image.Image {
|
||||
return imcdb.Image(id, style)
|
||||
}
|
||||
|
||||
func Solve(id captcha.ID, answer captcha.Answer) (bool, error) {
|
||||
func Solve(id captcha.ID, answer captcha.Answer) bool {
|
||||
return imcdb.Solve(id, answer)
|
||||
}
|
||||
|
||||
func IsSolved(id captcha.ID) (bool, error) {
|
||||
func IsSolved(id captcha.ID) bool {
|
||||
return imcdb.IsSolved(id)
|
||||
}
|
||||
|
||||
func Remove(id captcha.ID) error {
|
||||
return imcdb.Remove(id)
|
||||
func Remove(id captcha.ID) {
|
||||
imcdb.Remove(id)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue