|
62 | 62 |
|
63 | 63 | // ErrNoServers is returned when no servers are configured or available. |
64 | 64 | ErrNoServers = errors.New("memcache: no servers configured or available") |
| 65 | + |
| 66 | + // ErrNotAuthenticated is returned when Client Authentication failed due to any reason. |
| 67 | + ErrNotAuthenticated = errors.New("memcache: Client Authentication Failed") |
65 | 68 | ) |
66 | 69 |
|
67 | 70 | const ( |
@@ -114,6 +117,13 @@ var ( |
114 | 117 |
|
115 | 118 | resultClientErrorPrefix = []byte("CLIENT_ERROR ") |
116 | 119 | versionPrefix = []byte("VERSION") |
| 120 | + |
| 121 | + // Authentication Related Error |
| 122 | + resultUnauthenticatedError = []byte("CLIENT_ERROR unauthenticated\r\n") |
| 123 | + resultAuthenticationFailure = []byte("CLIENT_ERROR authentication failure\r\n") |
| 124 | + resultBadCommandFormat = []byte("CLIENT_ERROR bad command line format\r\n") |
| 125 | + resultBadCommandFormatTermination = []byte("CLIENT_ERROR bad command line format termination\r\n") |
| 126 | + resultBadAuthenticationTokenFormat = []byte("CLIENT_ERROR bad authentication token format\r\n") |
117 | 127 | ) |
118 | 128 |
|
119 | 129 | // New returns a memcache client using the provided server(s) |
@@ -181,6 +191,12 @@ type Item struct { |
181 | 191 | // It's populated by get requests and then the same value is |
182 | 192 | // required for a CompareAndSwap request to succeed. |
183 | 193 | CasID uint64 |
| 194 | + |
| 195 | + // Username for Authentication |
| 196 | + User string |
| 197 | + |
| 198 | + // Password for Authentication |
| 199 | + Pass string |
184 | 200 | } |
185 | 201 |
|
186 | 202 | // conn is a connection to a server. |
@@ -320,7 +336,7 @@ func (c *Client) onItem(item *Item, fn func(*Client, *bufio.ReadWriter, *Item) e |
320 | 336 | return err |
321 | 337 | } |
322 | 338 | defer cn.condRelease(&err) |
323 | | - if err = fn(c, cn.rw, item); err != nil { |
| 339 | + if err := fn(c, cn.rw, item); err != nil { |
324 | 340 | return err |
325 | 341 | } |
326 | 342 | return nil |
@@ -847,3 +863,48 @@ func (c *Client) Close() error { |
847 | 863 | c.freeconn = nil |
848 | 864 | return ret |
849 | 865 | } |
| 866 | + |
| 867 | +// Memcached Authentication Support |
| 868 | + |
| 869 | +func (c *Client) SetAuth(item *Item) error { |
| 870 | + return c.onItem(item, (*Client).setAuth) |
| 871 | +} |
| 872 | + |
| 873 | +func (c *Client) setAuth(rw *bufio.ReadWriter, item *Item) error { |
| 874 | + return c.authFunc(rw, "set", item) |
| 875 | +} |
| 876 | + |
| 877 | +func (c *Client) authFunc(rw *bufio.ReadWriter, verb string, item *Item) error { |
| 878 | + if !legalKey(item.Key) { |
| 879 | + return ErrMalformedKey |
| 880 | + } |
| 881 | + var err error |
| 882 | + _, err = fmt.Fprintf(rw, "%s %s %d %d %d\r\n%s %s\r\n", |
| 883 | + verb, item.Key, item.Flags, item.Expiration, len(item.User)+len(item.Pass)+1, item.User, item.Pass) |
| 884 | + if err != nil { |
| 885 | + return err |
| 886 | + } |
| 887 | + if err := rw.Flush(); err != nil { |
| 888 | + return err |
| 889 | + } |
| 890 | + line, err := rw.ReadSlice('\n') |
| 891 | + |
| 892 | + if err != nil { |
| 893 | + return err |
| 894 | + } |
| 895 | + switch { |
| 896 | + case bytes.Equal(line, resultStored): |
| 897 | + return nil |
| 898 | + case bytes.Equal(line, resultUnauthenticatedError): |
| 899 | + return ErrNotAuthenticated |
| 900 | + case bytes.Equal(line, resultAuthenticationFailure): |
| 901 | + return ErrNotAuthenticated |
| 902 | + case bytes.Equal(line, resultBadCommandFormat): |
| 903 | + return ErrNotAuthenticated |
| 904 | + case bytes.Equal(line, resultBadCommandFormatTermination): |
| 905 | + return ErrNotAuthenticated |
| 906 | + case bytes.Equal(line, resultBadAuthenticationTokenFormat): |
| 907 | + return ErrNotAuthenticated |
| 908 | + } |
| 909 | + return fmt.Errorf("memcache: unexpected response line from %q: %q", verb, string(line)) |
| 910 | +} |
0 commit comments