github.com/hdt3213/godis@v1.2.9/redis/parser/parser.go (about)

     1  package parser
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"errors"
     7  	"github.com/hdt3213/godis/interface/redis"
     8  	"github.com/hdt3213/godis/lib/logger"
     9  	"github.com/hdt3213/godis/redis/protocol"
    10  	"io"
    11  	"runtime/debug"
    12  	"strconv"
    13  	"strings"
    14  )
    15  
    16  // Payload stores redis.Reply or error
    17  type Payload struct {
    18  	Data redis.Reply
    19  	Err  error
    20  }
    21  
    22  // ParseStream reads data from io.Reader and send payloads through channel
    23  func ParseStream(reader io.Reader) <-chan *Payload {
    24  	ch := make(chan *Payload)
    25  	go parse0(reader, ch)
    26  	return ch
    27  }
    28  
    29  // ParseBytes reads data from []byte and return all replies
    30  func ParseBytes(data []byte) ([]redis.Reply, error) {
    31  	ch := make(chan *Payload)
    32  	reader := bytes.NewReader(data)
    33  	go parse0(reader, ch)
    34  	var results []redis.Reply
    35  	for payload := range ch {
    36  		if payload == nil {
    37  			return nil, errors.New("no protocol")
    38  		}
    39  		if payload.Err != nil {
    40  			if payload.Err == io.EOF {
    41  				break
    42  			}
    43  			return nil, payload.Err
    44  		}
    45  		results = append(results, payload.Data)
    46  	}
    47  	return results, nil
    48  }
    49  
    50  // ParseOne reads data from []byte and return the first payload
    51  func ParseOne(data []byte) (redis.Reply, error) {
    52  	ch := make(chan *Payload)
    53  	reader := bytes.NewReader(data)
    54  	go parse0(reader, ch)
    55  	payload := <-ch // parse0 will close the channel
    56  	if payload == nil {
    57  		return nil, errors.New("no protocol")
    58  	}
    59  	return payload.Data, payload.Err
    60  }
    61  
    62  func parse0(rawReader io.Reader, ch chan<- *Payload) {
    63  	defer func() {
    64  		if err := recover(); err != nil {
    65  			logger.Error(err, string(debug.Stack()))
    66  		}
    67  	}()
    68  	reader := bufio.NewReader(rawReader)
    69  	for {
    70  		line, err := reader.ReadBytes('\n')
    71  		if err != nil {
    72  			ch <- &Payload{Err: err}
    73  			close(ch)
    74  			return
    75  		}
    76  		length := len(line)
    77  		if length <= 2 || line[length-2] != '\r' {
    78  			// there are some empty lines within replication traffic, ignore this error
    79  			//protocolError(ch, "empty line")
    80  			continue
    81  		}
    82  		line = bytes.TrimSuffix(line, []byte{'\r', '\n'})
    83  		switch line[0] {
    84  		case '+':
    85  			content := string(line[1:])
    86  			ch <- &Payload{
    87  				Data: protocol.MakeStatusReply(content),
    88  			}
    89  			if strings.HasPrefix(content, "FULLRESYNC") {
    90  				err = parseRDBBulkString(reader, ch)
    91  				if err != nil {
    92  					ch <- &Payload{Err: err}
    93  					close(ch)
    94  					return
    95  				}
    96  			}
    97  		case '-':
    98  			ch <- &Payload{
    99  				Data: protocol.MakeErrReply(string(line[1:])),
   100  			}
   101  		case ':':
   102  			value, err := strconv.ParseInt(string(line[1:]), 10, 64)
   103  			if err != nil {
   104  				protocolError(ch, "illegal number "+string(line[1:]))
   105  				continue
   106  			}
   107  			ch <- &Payload{
   108  				Data: protocol.MakeIntReply(value),
   109  			}
   110  		case '$':
   111  			err = parseBulkString(line, reader, ch)
   112  			if err != nil {
   113  				ch <- &Payload{Err: err}
   114  				close(ch)
   115  				return
   116  			}
   117  		case '*':
   118  			err = parseArray(line, reader, ch)
   119  			if err != nil {
   120  				ch <- &Payload{Err: err}
   121  				close(ch)
   122  				return
   123  			}
   124  		default:
   125  			args := bytes.Split(line, []byte{' '})
   126  			ch <- &Payload{
   127  				Data: protocol.MakeMultiBulkReply(args),
   128  			}
   129  		}
   130  	}
   131  }
   132  
   133  func parseBulkString(header []byte, reader *bufio.Reader, ch chan<- *Payload) error {
   134  	strLen, err := strconv.ParseInt(string(header[1:]), 10, 64)
   135  	if err != nil || strLen < -1 {
   136  		protocolError(ch, "illegal bulk string header: "+string(header))
   137  		return nil
   138  	} else if strLen == -1 {
   139  		ch <- &Payload{
   140  			Data: protocol.MakeNullBulkReply(),
   141  		}
   142  		return nil
   143  	}
   144  	body := make([]byte, strLen+2)
   145  	_, err = io.ReadFull(reader, body)
   146  	if err != nil {
   147  		return err
   148  	}
   149  	ch <- &Payload{
   150  		Data: protocol.MakeBulkReply(body[:len(body)-2]),
   151  	}
   152  	return nil
   153  }
   154  
   155  // there is no CRLF between RDB and following AOF, therefore it needs to be treated differently
   156  func parseRDBBulkString(reader *bufio.Reader, ch chan<- *Payload) error {
   157  	header, err := reader.ReadBytes('\n')
   158  	header = bytes.TrimSuffix(header, []byte{'\r', '\n'})
   159  	if len(header) == 0 {
   160  		return errors.New("empty header")
   161  	}
   162  	strLen, err := strconv.ParseInt(string(header[1:]), 10, 64)
   163  	if err != nil || strLen <= 0 {
   164  		return errors.New("illegal bulk header: " + string(header))
   165  	}
   166  	body := make([]byte, strLen)
   167  	_, err = io.ReadFull(reader, body)
   168  	if err != nil {
   169  		return err
   170  	}
   171  	ch <- &Payload{
   172  		Data: protocol.MakeBulkReply(body[:len(body)]),
   173  	}
   174  	return nil
   175  }
   176  
   177  func parseArray(header []byte, reader *bufio.Reader, ch chan<- *Payload) error {
   178  	nStrs, err := strconv.ParseInt(string(header[1:]), 10, 64)
   179  	if err != nil || nStrs < 0 {
   180  		protocolError(ch, "illegal array header "+string(header[1:]))
   181  		return nil
   182  	} else if nStrs == 0 {
   183  		ch <- &Payload{
   184  			Data: protocol.MakeEmptyMultiBulkReply(),
   185  		}
   186  		return nil
   187  	}
   188  	lines := make([][]byte, 0, nStrs)
   189  	for i := int64(0); i < nStrs; i++ {
   190  		var line []byte
   191  		line, err = reader.ReadBytes('\n')
   192  		if err != nil {
   193  			return err
   194  		}
   195  		length := len(line)
   196  		if length < 4 || line[length-2] != '\r' || line[0] != '$' {
   197  			protocolError(ch, "illegal bulk string header "+string(line))
   198  			break
   199  		}
   200  		strLen, err := strconv.ParseInt(string(line[1:length-2]), 10, 64)
   201  		if err != nil || strLen < -1 {
   202  			protocolError(ch, "illegal bulk string length "+string(line))
   203  			break
   204  		} else if strLen == -1 {
   205  			lines = append(lines, []byte{})
   206  		} else {
   207  			body := make([]byte, strLen+2)
   208  			_, err := io.ReadFull(reader, body)
   209  			if err != nil {
   210  				return err
   211  			}
   212  			lines = append(lines, body[:len(body)-2])
   213  		}
   214  	}
   215  	ch <- &Payload{
   216  		Data: protocol.MakeMultiBulkReply(lines),
   217  	}
   218  	return nil
   219  }
   220  
   221  func protocolError(ch chan<- *Payload, msg string) {
   222  	err := errors.New("protocol error: " + msg)
   223  	ch <- &Payload{Err: err}
   224  }