mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-23 08:38:27 -05:00
229 lines
8.8 KiB
C
229 lines
8.8 KiB
C
|
/*
|
|||
|
* Copyright (c) 2009-2021, Redis Labs Ltd.
|
|||
|
* All rights reserved.
|
|||
|
*
|
|||
|
* Redistribution and use in source and binary forms, with or without
|
|||
|
* modification, are permitted provided that the following conditions are met:
|
|||
|
*
|
|||
|
* * Redistributions of source code must retain the above copyright notice,
|
|||
|
* this list of conditions and the following disclaimer.
|
|||
|
* * Redistributions in binary form must reproduce the above copyright
|
|||
|
* notice, this list of conditions and the following disclaimer in the
|
|||
|
* documentation and/or other materials provided with the distribution.
|
|||
|
* * Neither the name of Redis nor the names of its contributors may be used
|
|||
|
* to endorse or promote products derived from this software without
|
|||
|
* specific prior written permission.
|
|||
|
*
|
|||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|||
|
* POSSIBILITY OF SUCH DAMAGE.
|
|||
|
*/
|
|||
|
|
|||
|
/* ----------------------------------------------------------------------------------------
|
|||
|
* A RESP parser for parsing replies returned by RM_Call or Lua's
|
|||
|
* 'redis.call()'.
|
|||
|
*
|
|||
|
* The parser introduces callbacks that need to be set by the user. Each
|
|||
|
* callback represents a different reply type. Each callback gets a p_ctx that
|
|||
|
* was given to the parseReply function. The callbacks also give the protocol
|
|||
|
* (underlying blob) of the current reply and the size.
|
|||
|
*
|
|||
|
* Some callbacks also get the parser object itself:
|
|||
|
* - array_callback
|
|||
|
* - set_callback
|
|||
|
* - map_callback
|
|||
|
*
|
|||
|
* These callbacks need to continue parsing by calling parseReply a number of
|
|||
|
* times, according to the supplied length. Subsequent parseReply calls may use
|
|||
|
* a different p_ctx, which will be used for nested CallReply objects.
|
|||
|
*
|
|||
|
* These callbacks also do not receive a proto_len, which is not known at the
|
|||
|
* time of parsing. Callers may calculate it themselves after parsing the
|
|||
|
* entire collection.
|
|||
|
*
|
|||
|
* NOTE: This parser is designed to only handle replies generated by Redis
|
|||
|
* itself. It does not perform many required validations and thus NOT SAFE FOR
|
|||
|
* PARSING USER INPUT.
|
|||
|
* ----------------------------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
#include "resp_parser.h"
|
|||
|
#include "server.h"
|
|||
|
|
|||
|
static int parseBulk(ReplyParser *parser, void *p_ctx) {
|
|||
|
const char *proto = parser->curr_location;
|
|||
|
char *p = strchr(proto+1,'\r');
|
|||
|
long long bulklen;
|
|||
|
parser->curr_location = p + 2; /* for \r\n */
|
|||
|
|
|||
|
string2ll(proto+1,p-proto-1,&bulklen);
|
|||
|
if (bulklen == -1) {
|
|||
|
parser->callbacks.null_bulk_string_callback(p_ctx, proto, parser->curr_location - proto);
|
|||
|
} else {
|
|||
|
const char *str = parser->curr_location;
|
|||
|
parser->curr_location += bulklen;
|
|||
|
parser->curr_location += 2; /* for \r\n */
|
|||
|
parser->callbacks.bulk_string_callback(p_ctx, str, bulklen, proto, parser->curr_location - proto);
|
|||
|
}
|
|||
|
|
|||
|
return C_OK;
|
|||
|
}
|
|||
|
|
|||
|
static int parseSimpleString(ReplyParser *parser, void *p_ctx) {
|
|||
|
const char *proto = parser->curr_location;
|
|||
|
char *p = strchr(proto+1,'\r');
|
|||
|
parser->curr_location = p + 2; /* for \r\n */
|
|||
|
parser->callbacks.simple_str_callback(p_ctx, proto+1, p-proto-1, proto, parser->curr_location - proto);
|
|||
|
return C_OK;
|
|||
|
}
|
|||
|
|
|||
|
static int parseError(ReplyParser *parser, void *p_ctx) {
|
|||
|
const char *proto = parser->curr_location;
|
|||
|
char *p = strchr(proto+1,'\r');
|
|||
|
parser->curr_location = p + 2; // for \r\n
|
|||
|
parser->callbacks.error_callback(p_ctx, proto+1, p-proto-1, proto, parser->curr_location - proto);
|
|||
|
return C_OK;
|
|||
|
}
|
|||
|
|
|||
|
static int parseLong(ReplyParser *parser, void *p_ctx) {
|
|||
|
const char *proto = parser->curr_location;
|
|||
|
char *p = strchr(proto+1,'\r');
|
|||
|
parser->curr_location = p + 2; /* for \r\n */
|
|||
|
long long val;
|
|||
|
string2ll(proto+1,p-proto-1,&val);
|
|||
|
parser->callbacks.long_callback(p_ctx, val, proto, parser->curr_location - proto);
|
|||
|
return C_OK;
|
|||
|
}
|
|||
|
|
|||
|
static int parseAttributes(ReplyParser *parser, void *p_ctx) {
|
|||
|
const char *proto = parser->curr_location;
|
|||
|
char *p = strchr(proto+1,'\r');
|
|||
|
long long len;
|
|||
|
string2ll(proto+1,p-proto-1,&len);
|
|||
|
p += 2;
|
|||
|
parser->curr_location = p;
|
|||
|
parser->callbacks.attribute_callback(parser, p_ctx, len, proto);
|
|||
|
return C_OK;
|
|||
|
}
|
|||
|
|
|||
|
static int parseVerbatimString(ReplyParser *parser, void *p_ctx) {
|
|||
|
const char *proto = parser->curr_location;
|
|||
|
char *p = strchr(proto+1,'\r');
|
|||
|
long long bulklen;
|
|||
|
parser->curr_location = p + 2; /* for \r\n */
|
|||
|
string2ll(proto+1,p-proto-1,&bulklen);
|
|||
|
const char *format = parser->curr_location;
|
|||
|
parser->curr_location += bulklen;
|
|||
|
parser->curr_location += 2; /* for \r\n */
|
|||
|
parser->callbacks.verbatim_string_callback(p_ctx, format, format + 4, bulklen - 4, proto, parser->curr_location - proto);
|
|||
|
return C_OK;
|
|||
|
}
|
|||
|
|
|||
|
static int parseBigNumber(ReplyParser *parser, void *p_ctx) {
|
|||
|
const char *proto = parser->curr_location;
|
|||
|
char *p = strchr(proto+1,'\r');
|
|||
|
parser->curr_location = p + 2; /* for \r\n */
|
|||
|
parser->callbacks.big_number_callback(p_ctx, proto+1, p-proto-1, proto, parser->curr_location - proto);
|
|||
|
return C_OK;
|
|||
|
}
|
|||
|
|
|||
|
static int parseNull(ReplyParser *parser, void *p_ctx) {
|
|||
|
const char *proto = parser->curr_location;
|
|||
|
char *p = strchr(proto+1,'\r');
|
|||
|
parser->curr_location = p + 2; /* for \r\n */
|
|||
|
parser->callbacks.null_callback(p_ctx, proto, parser->curr_location - proto);
|
|||
|
return C_OK;
|
|||
|
}
|
|||
|
|
|||
|
static int parseDouble(ReplyParser *parser, void *p_ctx) {
|
|||
|
const char *proto = parser->curr_location;
|
|||
|
char *p = strchr(proto+1,'\r');
|
|||
|
parser->curr_location = p + 2; /* for \r\n */
|
|||
|
char buf[MAX_LONG_DOUBLE_CHARS+1];
|
|||
|
size_t len = p-proto-1;
|
|||
|
double d;
|
|||
|
if (len <= MAX_LONG_DOUBLE_CHARS) {
|
|||
|
memcpy(buf,proto+1,len);
|
|||
|
buf[len] = '\0';
|
|||
|
d = strtod(buf,NULL); /* We expect a valid representation. */
|
|||
|
} else {
|
|||
|
d = 0;
|
|||
|
}
|
|||
|
parser->callbacks.double_callback(p_ctx, d, proto, parser->curr_location - proto);
|
|||
|
return C_OK;
|
|||
|
}
|
|||
|
|
|||
|
static int parseBool(ReplyParser *parser, void *p_ctx) {
|
|||
|
const char *proto = parser->curr_location;
|
|||
|
char *p = strchr(proto+1,'\r');
|
|||
|
parser->curr_location = p + 2; /* for \r\n */
|
|||
|
parser->callbacks.bool_callback(p_ctx, proto[1] == 't', proto, parser->curr_location - proto);
|
|||
|
return C_OK;
|
|||
|
}
|
|||
|
|
|||
|
static int parseArray(ReplyParser *parser, void *p_ctx) {
|
|||
|
const char *proto = parser->curr_location;
|
|||
|
char *p = strchr(proto+1,'\r');
|
|||
|
long long len;
|
|||
|
string2ll(proto+1,p-proto-1,&len);
|
|||
|
p += 2;
|
|||
|
parser->curr_location = p;
|
|||
|
if (len == -1) {
|
|||
|
parser->callbacks.null_array_callback(p_ctx, proto, parser->curr_location - proto);
|
|||
|
} else {
|
|||
|
parser->callbacks.array_callback(parser, p_ctx, len, proto);
|
|||
|
}
|
|||
|
return C_OK;
|
|||
|
}
|
|||
|
|
|||
|
static int parseSet(ReplyParser *parser, void *p_ctx) {
|
|||
|
const char *proto = parser->curr_location;
|
|||
|
char *p = strchr(proto+1,'\r');
|
|||
|
long long len;
|
|||
|
string2ll(proto+1,p-proto-1,&len);
|
|||
|
p += 2;
|
|||
|
parser->curr_location = p;
|
|||
|
parser->callbacks.set_callback(parser, p_ctx, len, proto);
|
|||
|
return C_OK;
|
|||
|
}
|
|||
|
|
|||
|
static int parseMap(ReplyParser *parser, void *p_ctx) {
|
|||
|
const char *proto = parser->curr_location;
|
|||
|
char *p = strchr(proto+1,'\r');
|
|||
|
long long len;
|
|||
|
string2ll(proto+1,p-proto-1,&len);
|
|||
|
p += 2;
|
|||
|
parser->curr_location = p;
|
|||
|
parser->callbacks.map_callback(parser, p_ctx, len, proto);
|
|||
|
return C_OK;
|
|||
|
}
|
|||
|
|
|||
|
/* Parse a reply pointed to by parser->curr_location. */
|
|||
|
int parseReply(ReplyParser *parser, void *p_ctx) {
|
|||
|
switch (parser->curr_location[0]) {
|
|||
|
case '$': return parseBulk(parser, p_ctx);
|
|||
|
case '+': return parseSimpleString(parser, p_ctx);
|
|||
|
case '-': return parseError(parser, p_ctx);
|
|||
|
case ':': return parseLong(parser, p_ctx);
|
|||
|
case '*': return parseArray(parser, p_ctx);
|
|||
|
case '~': return parseSet(parser, p_ctx);
|
|||
|
case '%': return parseMap(parser, p_ctx);
|
|||
|
case '#': return parseBool(parser, p_ctx);
|
|||
|
case ',': return parseDouble(parser, p_ctx);
|
|||
|
case '_': return parseNull(parser, p_ctx);
|
|||
|
case '(': return parseBigNumber(parser, p_ctx);
|
|||
|
case '=': return parseVerbatimString(parser, p_ctx);
|
|||
|
case '|': return parseAttributes(parser, p_ctx);
|
|||
|
default: if (parser->callbacks.error) parser->callbacks.error(p_ctx);
|
|||
|
}
|
|||
|
return C_ERR;
|
|||
|
}
|