Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 58 additions & 81 deletions libmemcached/response.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,94 +54,79 @@
#include <libmemcached/common.h>
#include <libmemcached/string.hpp>

static size_t tokenize(char *buffer, size_t length, char **tokens, size_t max_tokens)
{
if (buffer[length - 2] != '\r' || buffer[length - 1] != '\n')
return 0;
memset(buffer + length - 2, 0, 2);

char *saveptr;
char *token= strtok_r(buffer, " ", &saveptr);
size_t ntoken= 0;
while (token != NULL)
{
if (ntoken >= max_tokens) return max_tokens + 1;
tokens[ntoken++]= token;
token= strtok_r(NULL, " ", &saveptr);
}
return ntoken;
}

static bool safe_strtoui(char *str, void *out, uintmax_t max_num)
{
/* string to unsinged integer */
errno= 0;
char *endptr;
*(uintmax_t *)out= 0;
uintmax_t num= strtoull(str, &endptr, 10);
if (errno == ERANGE || *endptr != '\0' || num > max_num)
return false;
*(uintmax_t *)out= num;
return true;
}

static memcached_return_t textual_value_fetch(memcached_server_write_instance_st ptr,
char *buffer, size_t buffer_length,
memcached_result_st *result)
{
char *string_ptr;
char *end_ptr;
char *next_ptr;
size_t value_length;
size_t to_read;
ssize_t read_length= 0;
ssize_t value_length;
ssize_t read_length;

if (ptr->root->flags.use_udp)
return memcached_set_error(*ptr, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);

WATCHPOINT_ASSERT(ptr->root);
end_ptr= buffer + buffer_length;

memcached_result_reset(result);

string_ptr= buffer;
string_ptr+= 6; /* "VALUE " */
string_ptr= buffer + 6; /* "VALUE " */

char *tokens[4]= {NULL, };
size_t ntoken= tokenize(string_ptr, buffer_length - 6, tokens, 4);
if (ntoken < 3 || ntoken > 4)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tokens λ°°μ—΄μ˜ 크기λ₯Ό 4둜 μ•Œλ €μ€˜μ„œ tokenize ν•œ κ²ƒμ΄λ―€λ‘œ, λ¦¬ν„΄λ˜λŠ” ntokensλŠ” 4보닀 클 μˆ˜κ°€ 없을 것 κ°™μŠ΅λ‹ˆλ‹€.
if (ntoken < 3) 쑰건만 μ‚¬μš©ν•΄λ„ λ˜μ§€λ§Œ, μ΄λŒ€λ‘œ λ‘λŠ” κ²ƒμœΌλ‘œ ν•˜μ£ .

Copy link
Collaborator Author

@ing-eoking ing-eoking Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

λ§Œμ•½ partial read둜 μΈν•œ 경우 max_tokdn + 1을 λ°˜ν™˜ν•˜λ„λ‘ ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. 였히렀 ntoken > 4 κ°€ 더 μ•Œλ§žμ€ 쑰건이라고 λ³΄μ‹œλ©΄ λ©λ‹ˆλ‹€.

return MEMCACHED_PARTIAL_READ;

/* We load the key */
{
char *key= result->item_key;
size_t key_length= 0;

string_ptr += memcached_array_size(ptr->root->_namespace); /* prefix length */

while (!iscntrl(*string_ptr) && !isspace(*string_ptr)) {
if (key_length < MEMCACHED_MAX_KEY) {
key[key_length]= *string_ptr;
}
key_length++;
string_ptr++;
}

if (key_length < MEMCACHED_MAX_KEY) {
key[key_length]= 0;
result->key_length= key_length;
} else {
snprintf(key + MEMCACHED_MAX_KEY - 4, 4, "...");
result->key_length= MEMCACHED_MAX_KEY - 1;
Copy link
Contributor

@jhpark816 jhpark816 Oct 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ν‚€ λ¬Έμžμ—΄μ„ μ½μ–΄λ‚΄λŠ” 뢀뢄은 κΈ°μ‘΄ μ½”λ“œλ₯Ό κ·ΈλŒ€λ‘œ μœ μ§€ν•©μ‹œλ‹€.
ν‚€ λ¬Έμžμ—΄μ˜ tokenize와 μˆ«μžν˜• λ¬Έμžμ—΄μ˜ tokenize 방식이 λ‹€λ₯΄κ³ ,
ν‚€ λ¬Έμžμ—΄μ˜ tokenizeν•˜λŠ” κΈ°μ‘΄ μ½”λ“œκ°€ μΆ©λΆ„νžˆ μ΄ν•΄ν•˜κΈ° 쉽기 λ•Œλ¬Έμž…λ‹ˆλ‹€.

char *key= tokens[0] + memcached_array_size(ptr->root->_namespace);
int key_length= snprintf(result->item_key, MEMCACHED_MAX_KEY, "%s", key);
if (key_length >= MEMCACHED_MAX_KEY)
{
snprintf(result->item_key + MEMCACHED_MAX_KEY - 4, 4, "...");
key_length= MEMCACHED_MAX_KEY - 1;
memcached_set_error(*ptr, MEMCACHED_KEY_TOO_BIG, MEMCACHED_AT);
}
result->key_length= (size_t)key_length;
}

if (end_ptr == string_ptr)
goto read_error;

/* Flags fetch move past space */
string_ptr++;
if (end_ptr == string_ptr)
goto read_error;

for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++) {};
result->item_flags= (uint32_t) strtoul(next_ptr, &string_ptr, 10);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

μˆ«μžν˜•μ˜ λ¬Έμžμ—΄λ§Œμ„ ν† ν°ν™”ν•˜λ„λ‘ ν•©μ‹œλ‹€.
기쑴처럼 isdigit() ν•¨μˆ˜λ₯Ό μ΄μš©ν•˜μ—¬ tokenize ν•œλ‹€λ©΄, strtoul() 후에 validation checkλŠ” 없어도 λ©λ‹ˆλ‹€.

int tokenize_numbers(char *buffer, size_t length, char **tokens, size_t max_tokens)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

토큰화 방식을 ν™œμš©ν•΄ λ¦¬νŒ©ν† λ§μ„ μ§„ν–‰ν•˜λŠ” 경우, κ²°κ³Όμ—μ„œ key λΆ€λΆ„λ§Œ λ”°λ‘œ νŒŒμ‹±ν•˜κ³  λ‚˜λ¨Έμ§€ 뢀뢄은 ν† ν°ν™”ν•˜μ—¬ μ²˜λ¦¬ν•˜λŠ” 방식은 κ°œμΈμ μœΌλ‘œλŠ” λ‹€μ†Œ λΆ€μžμ—°μŠ€λŸ½κ²Œ λŠκ»΄μ§‘λ‹ˆλ‹€.

java-client의 κ΅¬ν˜„ 방식을 μ‚΄νŽ΄λ³Έ κ²°κ³Ό, 전체 κ²°κ³Όλ₯Ό λͺ¨λ‘ ν† ν°ν™”ν•œ λ’€ νŒŒμ‹±ν•˜λŠ” 방식을 μ‚¬μš©ν•˜κ³  μžˆμ—ˆμŠ΅λ‹ˆλ‹€.


if (end_ptr == string_ptr)
goto read_error;

/* Length fetch move past space*/
string_ptr++;
if (end_ptr == string_ptr)
goto read_error;

for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++) {};
value_length= (size_t)strtoull(next_ptr, &string_ptr, 10);

if (end_ptr == string_ptr)
goto read_error;

/* Skip spaces */
if (*string_ptr == '\r')
if (!safe_strtoui(tokens[1], (void *)&result->item_flags, UINT32_MAX) ||
!safe_strtoui(tokens[2], (void *)&value_length, SSIZE_MAX) ||
(ntoken == 4 && !safe_strtoui(tokens[3], (void *)&result->item_cas, ULLONG_MAX)))
{
/* Skip past the \r\n */
string_ptr+= 2;
}
else
{
string_ptr++;
for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++) {};
result->item_cas= strtoull(next_ptr, &string_ptr, 10);
return MEMCACHED_PROTOCOL_ERROR;
}

if (end_ptr < string_ptr)
goto read_error;

/* We add two bytes so that we can walk the \r\n */
if (memcached_failed(memcached_string_check(&result->value, value_length +2)))
{
Expand All @@ -157,7 +142,7 @@ static memcached_return_t textual_value_fetch(memcached_server_write_instance_st
We are null terminating through, which will most likely make
some people lazy about using the return length.
*/
to_read= (value_length) + 2;
size_t to_read= (value_length) + 2;
memcached_return_t rrc= memcached_io_read(ptr, value_ptr, to_read, &read_length);
if (memcached_failed(rrc))
{
Expand All @@ -167,26 +152,18 @@ static memcached_return_t textual_value_fetch(memcached_server_write_instance_st
}
return rrc;
}
}

if (read_length != (ssize_t)(value_length + 2))
{
goto read_error;
}
if (read_length != value_length + 2)
{
return MEMCACHED_PARTIAL_READ;
}

/* This next bit blows the API, but this is internal....*/
{
char *char_ptr;
char_ptr= memcached_string_value_mutable(&result->value);;
char_ptr[value_length]= 0;
char_ptr[value_length +1]= 0;
/* Replace the last "\r\n" characters with '\0' */
memset(value_ptr + value_length, 0, 2);
memcached_string_set_length(&result->value, value_length);
}

return MEMCACHED_SUCCESS;

read_error:
return MEMCACHED_PARTIAL_READ;
}

static memcached_return_t textual_version_fetch(memcached_server_write_instance_st instance, char *buffer)
Expand Down Expand Up @@ -359,7 +336,7 @@ static memcached_return_t textual_read_one_response(memcached_server_write_insta
{
/* We add back in one because we will need to search for END */
memcached_server_response_increment(ptr);
return textual_value_fetch(ptr, buffer, buffer_length, result);
return textual_value_fetch(ptr, buffer, total_read, result);
}
else if (memcmp(buffer, "VERSION", 7) == 0)
{
Expand Down