Skip to content

Instantly share code, notes, and snippets.

@ii64
Last active June 15, 2019 13:01
Show Gist options
  • Save ii64/adbee49ef4ab118bdff9c5c5e7d3a49a to your computer and use it in GitHub Desktop.
Save ii64/adbee49ef4ab118bdff9c5c5e7d3a49a to your computer and use it in GitHub Desktop.
redis server implemented with golang
package main
/*
https://github.com/anysz
*/
import (
"net"
"fmt"
"strconv"
)
var (
Port string = ":3232"
ReadMem int = 512*1
)
var (
TList byte = 42
TSimpleString byte = 43
TString byte = 36
TInt byte = 58
TErr byte = 45
CR byte = 13
CN byte = 10
)
var (
RES_OK = []byte("+OK\r\n")
RES_PONG = []byte("+PONG\r\n")
ERR_INV_ARG_LEN = []byte("-ERR command not resolved, needed first argument as key to resolve shard node\r\n")
ERR_IMPL = []byte("-ERR 500 Not Implemented")
EMPTY_BYTE_STACK = []byte{}
)
func main() {
svr, err := net.Listen("tcp", Port)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("Server on", Port)
// Accepting incomming tcp connect
for {
conn, err := svr.Accept()
if err != nil {
continue
}
go hConn(conn)
}
}
func hConn(c net.Conn) {
tmp_byte := []byte{}
for {
//fmt.Println("\r ============ RECEIVED =============")
data := make([]byte, ReadMem)
n, err := c.Read(data)
if err != nil {
//fmt.Println(err)
c.Close()
return
}
if n == ReadMem {
//incomplete_read := hProcessor(c, data)
tmp_byte = append(tmp_byte, data...)
}else{
tmp_byte = append(tmp_byte, data[:n]...)
go hProcessor(c, tmp_byte)
tmp_byte = EMPTY_BYTE_STACK
}
//fmt.Println("n value:", n)
//fmt.Printf("data: %#+v\r\n", string(data))
//fmt.Printf("data cut: %#+v\r\n", string(data[:n]))
}
}
type rsWriter struct {
writer net.Conn
buf []byte
}
func NewResWriter(c net.Conn) rsWriter {
return rsWriter{c, []byte{}}
}
func (s *rsWriter) Flush() {
s.writer.Write(s.buf)
s.buf = []byte{}
}
func (s *rsWriter) Write(b []byte) {
//s.buf = append(s.buf, b...)
s.writer.Write(b)
}
func (s *rsWriter) WriteNil() {
s.buf = append(s.buf, TString, 45, 49, CR, CN)
}
func (s *rsWriter) WriteString(d string) {
p := []byte(d)
s.buf = append(s.buf, TString)
s.buf = append(s.buf, []byte(strconv.Itoa(len(p)))...)
s.buf = append(s.buf, CR, CN)
s.buf = append(s.buf, p...)
s.buf = append(s.buf, CR, CN)
}
func (s *rsWriter) WriteInt(d int) {
s.buf = append(s.buf, TInt)
s.buf = append(s.buf, []byte(strconv.Itoa(d))...)
s.buf = append(s.buf, CR, CN)
}
func (s *rsWriter) WriteRes(d interface{}) {
switch d.(type) {
case nil:
s.WriteNil()
case string:
s.WriteString(d.(string))
case int:
s.WriteInt(d.(int))
case []string:
p := d.([]string)
s.buf = append(s.buf, TList)
s.buf = append(s.buf, []byte(strconv.Itoa(len(p)))...)
s.buf = append(s.buf, CR, CN)
for _, item := range p {
s.WriteRes(item)
}
case []int:
p := d.([]int)
s.buf = append(s.buf, TList)
s.buf = append(s.buf, []byte(strconv.Itoa(len(p)))...)
s.buf = append(s.buf, CR, CN)
for _, item := range p {
s.WriteRes(item)
}
case []interface{}:
p := d.([]interface{})
s.buf = append(s.buf, TList)
s.buf = append(s.buf, []byte(strconv.Itoa(len(p)))...)
s.buf = append(s.buf, CR, CN)
for _, item := range p {
s.WriteRes(item)
}
}
return
}
type rqReader struct {
buf []byte
}
func NewReqReader(buf []byte) rqReader { return rqReader{buf} }
func (s *rqReader) ReadVarint() (r int, e error) {
if len(s.buf) < 1 {}
cnv := []byte{}
last := 0
for i, b := range s.buf {
// byte of numbers
if b == 48 || b == 49 || b == 50 || b == 51 || b == 52 || b == 53 || b == 54 || b == 55 || b == 56 || b == 57 {
cnv = append(cnv, b)
} else if b == CN {
last = i + 1
break
}
}
s.buf = s.buf[last:]
r, e = strconv.Atoi(string(cnv))
return
}
func (s *rqReader) ReadData() (r []byte, e error) {
// Stricted due to redis-cli send all args as string
size, e := s.ReadVarint()
r = s.buf[0:size]
s.buf = s.buf[size+2:]
return
}
func (s *rqReader) ReadSkip(skip int) {
// Skip based on first varint of Args TList
if skip < 1 { return }
skip = skip *2
foundCN := 0
for i, b := range s.buf {
if b == CN {
foundCN += 1
if foundCN == skip {
s.buf = s.buf[i+1:]
break
}
}
}
}
func (s *rqReader) IsPING() bool {
if len(s.buf) > 4 {
if ping := s.buf[:4]; (ping[0] == 112 || ping[0] == 80 /*pP*/) && (ping[1] == 105 || ping[1] == 73) && (ping[2] == 110 || ping[2] == 78) && (ping[3] == 103 || ping[3] == 71) {
// mark as readed
s.buf = s.buf[6:]
return true
}
}
return false
}
func hProcessor(c net.Conn, data []byte) (incomplete []byte) {
rd := NewReqReader(data)
w := NewResWriter(c)
//fmt.Printf("hProc %#+v\r\n", string(data))
for {
// Check ping request
if len(rd.buf) == 0 { break }
if rd.IsPING() {
//fmt.Println("Ping received!")
go func() {
w.Write(RES_PONG)
}()
}else{
argslen, _ := rd.ReadVarint()
cmd, _ := rd.ReadData()
_ = cmd
rd.ReadSkip(argslen-1) // skip entire args
//fmt.Println("Recv cmd", string(cmd))
w.Write(RES_OK)
}
}
w.Flush()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment