Created
June 23, 2015 10:01
-
-
Save vdvm/09b2d80e8b3ece517c0e to your computer and use it in GitHub Desktop.
Distributed PHP sessions using memcached extension and coreos/etcd (PoC)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bufio" | |
"fmt" | |
"net" | |
"regexp" | |
"runtime" | |
"strconv" | |
"strings" | |
"time" | |
"github.com/coreos/go-etcd/etcd" | |
) | |
var ETCD = []string{"http://127.0.0.1:4001"} | |
func main() { | |
runtime.GOMAXPROCS(4) | |
kv := etcd.NewClient(ETCD) | |
ok := kv.SyncCluster() | |
if !ok { | |
panic("Cannot connect to etcd") | |
} | |
kv.Close() | |
ln, err := net.Listen("tcp", "127.0.0.1:11211") | |
for i := 0; err == nil; i++ { | |
var conn net.Conn | |
conn, err = ln.Accept() | |
go handleConnection(conn, i) | |
} | |
} | |
func handleConnection(conn net.Conn, i int) { | |
defer conn.Close() | |
kv := etcd.NewClient(ETCD) | |
duration := 0 | |
kv.SetDialTimeout(time.Duration(duration) * time.Millisecond) | |
defer kv.Close() | |
//fmt.Printf("%d: %v <-> %v\n", i, conn.LocalAddr(), conn.RemoteAddr()) | |
b := bufio.NewReader(conn) | |
for { | |
line, err := b.ReadBytes('\n') | |
if err != nil { | |
break | |
} | |
switch { | |
case strings.HasPrefix(string(line), "get "): | |
handleGetCommand(conn, i, kv, line) | |
case strings.HasPrefix(string(line), "set "): | |
handleSetCommand(conn, i, kv, line, b) | |
case strings.HasPrefix(string(line), "delete "): | |
handleDeleteCommand(conn, i, kv, line) | |
case string(line) == "quit\r\n": | |
conn.Close() | |
case string(line) == "stats\r\n": | |
conn.Write([]byte("END\r\n")) | |
case string(line) == "version\r\n": | |
conn.Write([]byte("VERSION 1.4.22\r\n")) | |
default: | |
fmt.Printf("%d: %v: %v", i, "Unknown command", string(line)) | |
conn.Write([]byte("ERROR\r\n")) | |
} | |
} | |
//fmt.Printf("%d: closed\n", i) | |
} | |
func handleGetCommand(conn net.Conn, i int, kv *etcd.Client, line []byte) bool { | |
re := regexp.MustCompile(`^(get)\s(.*\w+)(?:\r\n)$`) | |
matches := re.FindAllString(string(line), -1) | |
if matches == nil { | |
//fmt.Printf("%d: %v\r\n", i, "CLIENT_ERROR (GET) bad command line format") | |
conn.Write([]byte("CLIENT_ERROR GET bad command line format\r\n")) | |
return false | |
} | |
items := strings.Fields(matches[0]) | |
key := "/sesscache/" + items[1] | |
resp, err := kv.Get(key, false, false) | |
if err != nil { | |
conn.Write([]byte("END\r\n")) | |
return true | |
} | |
reply := fmt.Sprintf("VALUE %v 0 %d\r\n%v\r\nEND\r\n", items[1], len(resp.Node.Value), resp.Node.Value) | |
conn.Write([]byte(reply)) | |
return true | |
} | |
func handleSetCommand(conn net.Conn, i int, kv *etcd.Client, line []byte, b *bufio.Reader) bool { | |
re := regexp.MustCompile(`^(set|add|replace)\s(.*\w+)\s([0-9]{1,})\s([0-9]{1,})\s([0-9]{1,})\s?([a-zA-Z0-9]\w+)?(?:\r\n)$`) | |
matches := re.FindAllString(string(line), -1) | |
if matches == nil { | |
//fmt.Printf("%d: %v\r\n", i, "CLIENT_ERROR (SET1) bad command line format") | |
conn.Write([]byte("CLIENT_ERROR bad command line format\r\n")) | |
return false | |
} | |
items := strings.Fields(matches[0]) | |
key := "/sesscache/" + items[1] | |
ttt, _ := strconv.Atoi(items[3]) | |
ttl := uint64(ttt) | |
bytes, _ := strconv.Atoi(items[4]) | |
//b := bufio.NewReader(conn) | |
line2, err := b.ReadBytes('\n') | |
if err != nil { | |
return false | |
} | |
re2 := regexp.MustCompile(`^(.*)(?:\r\n)$`) | |
matches2 := re2.FindAllString(string(line2), -1) | |
if matches2 == nil { | |
//fmt.Printf("%d: %v\r\n", i, "CLIENT_ERROR (SET2) bad command line format") | |
conn.Write([]byte("CLIENT_ERROR bad command line format\r\n")) | |
return false | |
} | |
items2 := strings.Fields(matches2[0]) | |
value := string(items2[0]) | |
if len(value) != bytes { | |
//fmt.Printf("%d: %v\r\n", i, "CLIENT_ERROR bad data chunk") | |
conn.Write([]byte("CLIENT_ERROR bad bad data chunk\r\n")) | |
return false | |
} | |
if _, err := kv.Set(key, value, ttl); err != nil { | |
//fmt.Printf("%d: %v: %v", i, "SERVER_ERROR", err) | |
conn.Write([]byte("SERVER_ERROR ...\r\n")) | |
return false | |
} | |
conn.Write([]byte("STORED\r\n")) | |
return true | |
} | |
func handleDeleteCommand(conn net.Conn, i int, kv *etcd.Client, line []byte) bool { | |
re := regexp.MustCompile(`^(delete)\s(.*\w+)\s?([a-zA-Z0-9]\w+)?(?:\r\n)$`) | |
matches := re.FindAllString(string(line), -1) | |
if matches == nil { | |
//fmt.Printf("%d: %v", i, "CLIENT_ERROR (DELETE) bad command line format") | |
conn.Write([]byte("CLIENT_ERROR bad command line format\r\n")) | |
return false | |
} | |
items := strings.Fields(matches[0]) | |
key := "/sesscache/" + items[1] | |
_, err := kv.Delete(key, false) | |
if err != nil { | |
conn.Write([]byte("NOT_FOUND\r\n")) | |
return true | |
} | |
conn.Write([]byte("DELETED\r\n")) | |
return true | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment