Coverage Report

Created: 2025-06-22 06:19

/src/h2o/deps/libyrmcds/send_text.c
Line
Count
Source (jump to first uncovered line)
1
// (C) 2016 Cybozu
2
3
#include "yrmcds_text.h"
4
#include "yrmcds_portability.h"
5
6
#ifdef LIBYRMCDS_USE_LZ4
7
#  include "lz4/lib/lz4.h"
8
#endif
9
10
#include <errno.h>
11
#include <stdlib.h>
12
#include <string.h>
13
#include <sys/socket.h>
14
#include <sys/types.h>
15
#include <sys/uio.h>
16
#include <unistd.h>
17
18
0
#define MAX_KEY_LENGTH 250  // from memcached spec.
19
#define TEXTBUF_SIZE  1000  // enough for any command & parameters.
20
0
#define EXPAND_STR(s) (s), (sizeof(s) - 1)
21
static const char CRLF[2] = {'\r', '\n'};
22
23
#ifdef LIBYRMCDS_USE_LZ4
24
static inline void
25
hton32(uint32_t i, char* p) {
26
    uint32_t n = htobe32(i);
27
    memcpy(p, &n, sizeof(n));
28
}
29
#endif
30
31
static inline yrmcds_error
32
0
check_key(const char* key, size_t key_len) {
33
0
    if( key_len > MAX_KEY_LENGTH )
34
0
        return YRMCDS_BAD_KEY;
35
36
0
    size_t i;
37
0
    for( i = 0; i < key_len; i++ ) {
38
0
        char c = key[i];
39
0
        if( c <= ' ' ) return YRMCDS_BAD_KEY;  // SPC and control chars
40
0
        if( c == 127 ) return YRMCDS_BAD_KEY;  // DEL
41
0
    }
42
43
0
    return YRMCDS_OK;
44
0
}
45
46
typedef struct {
47
    char* pos;
48
    char buffer[TEXTBUF_SIZE];
49
} textbuf_t;
50
51
static inline size_t
52
0
textbuf_length(const textbuf_t* buf) {
53
0
    return (size_t)(buf->pos - buf->buffer);
54
0
}
55
56
static inline void
57
0
textbuf_init(textbuf_t* buf) {
58
0
    buf->pos = buf->buffer;
59
0
}
60
61
static inline void
62
0
textbuf_append_char(textbuf_t* buf, char c) {
63
0
    *buf->pos = c;
64
0
    ++buf->pos;
65
0
}
66
67
static inline void
68
0
textbuf_append_string(textbuf_t* buf, const char* s, size_t len) {
69
0
    memcpy(buf->pos, s, len);
70
0
    buf->pos += len;
71
0
}
72
73
#define textbuf_append_const_string(b, s)       \
74
0
    textbuf_append_string(b, s, sizeof(s) - 1)
75
76
static void
77
0
textbuf_append_uint64(textbuf_t* buf, uint64_t n) {
78
    // UINT64_MAX = 18446744073709551615 -> char[20]
79
0
    char nbuf[20];
80
0
    char* pos = (nbuf) + 20;
81
82
0
    do {
83
0
        pos--;
84
0
        uint64_t m = n % 10;
85
0
        n /= 10;
86
0
        *pos = (char)('0' + m);
87
0
    } while( n != 0 );
88
89
0
    textbuf_append_string(buf, pos, (size_t)(nbuf - pos + 20));
90
0
}
91
92
93
static yrmcds_error
94
0
send_command(yrmcds* c, textbuf_t* buf, uint32_t* serial) {
95
0
    memcpy(buf->pos, CRLF, sizeof(CRLF));
96
0
    buf->pos += sizeof(CRLF);
97
0
    const char* p = buf->buffer;
98
0
    size_t len = textbuf_length(buf);
99
100
0
#ifndef LIBYRMCDS_NO_INTERNAL_LOCK
101
0
    int e = pthread_mutex_lock(&c->lock);
102
0
    if( e != 0 ) {
103
0
        errno = e;
104
0
        return YRMCDS_SYSTEM_ERROR;
105
0
    }
106
0
#endif // ! LIBYRMCDS_NO_INTERNAL_LOCK
107
108
0
    c->serial = c->serial + 1;
109
0
    if( serial != NULL )
110
0
        *serial = c->serial;
111
112
0
    yrmcds_error ret = YRMCDS_OK;
113
0
    while( len > 0 ) {
114
0
        ssize_t n = send(c->sock, p, len, 0);
115
0
        if( n == -1 ) {
116
0
            if( errno == EINTR ) continue;
117
0
            ret = YRMCDS_SYSTEM_ERROR;
118
0
            goto OUT;
119
0
        }
120
0
        size_t n2 = (size_t)n;
121
0
        p += n2;
122
0
        len -= n2;
123
0
    }
124
125
0
  OUT:
126
0
#ifndef LIBYRMCDS_NO_INTERNAL_LOCK
127
0
    pthread_mutex_unlock(&c->lock);
128
0
#endif
129
0
    return ret;
130
0
}
131
132
static yrmcds_error
133
send_data(yrmcds* c, const char* cmd, size_t cmd_len,
134
          const char* key, size_t key_len,
135
          const char* data, size_t data_len,
136
          uint32_t flags, uint32_t expire, uint64_t cas,
137
0
          int quiet, uint32_t* serial) {
138
0
    if( key == NULL || key_len == 0 || data == NULL || data_len == 0 || quiet )
139
0
        return YRMCDS_BAD_ARGUMENT;
140
141
0
    yrmcds_error ret;
142
0
    ret = check_key(key, key_len);
143
0
    if( ret != YRMCDS_OK ) return ret;
144
145
0
    if( cas != 0 ) {
146
0
        cmd = "cas";
147
0
        cmd_len = 3;
148
0
    }
149
150
0
    int compressed = 0;
151
#ifdef LIBYRMCDS_USE_LZ4
152
    if( (c->compress_size > 0) && (data_len > c->compress_size) ) {
153
        if( flags & YRMCDS_FLAG_COMPRESS )
154
            return YRMCDS_BAD_ARGUMENT;
155
156
        size_t bound = (size_t)LZ4_compressBound((int)data_len);
157
        char* new_data = (char*)malloc(bound + sizeof(uint32_t));
158
        if( new_data == NULL )
159
            return YRMCDS_OUT_OF_MEMORY;
160
        uint32_t new_size =
161
            (uint32_t)LZ4_compress(data, new_data + sizeof(uint32_t),
162
                                   (int)data_len);
163
        if( new_size == 0 ) {
164
            free(new_data);
165
            return YRMCDS_COMPRESS_FAILED;
166
        }
167
        hton32((uint32_t)data_len, new_data);
168
        flags |= YRMCDS_FLAG_COMPRESS;
169
        data_len = sizeof(uint32_t) + new_size;
170
        data = new_data;
171
        compressed = 1;
172
    }
173
#endif // LIBYRMCDS_USE_LZ4
174
175
0
    textbuf_t buf[1];
176
0
    textbuf_init(buf);
177
178
    // "cmd key flags expire bytes (cas)"
179
0
    textbuf_append_string(buf, cmd, cmd_len);
180
0
    textbuf_append_char(buf, ' ');
181
0
    textbuf_append_string(buf, key, key_len);
182
0
    textbuf_append_char(buf, ' ');
183
0
    textbuf_append_uint64(buf, flags);
184
0
    textbuf_append_char(buf, ' ');
185
0
    textbuf_append_uint64(buf, expire);
186
0
    textbuf_append_char(buf, ' ');
187
0
    textbuf_append_uint64(buf, (uint64_t)data_len);
188
0
    if( cas != 0 ) {
189
0
        textbuf_append_char(buf, ' ');
190
0
        textbuf_append_uint64(buf, cas);
191
0
    }
192
0
    textbuf_append_string(buf, CRLF, sizeof(CRLF));
193
194
0
    struct iovec iov[3];
195
0
    int iovcnt = 3;
196
0
    iov[0].iov_base = buf[0].buffer;
197
0
    iov[0].iov_len = textbuf_length(buf);
198
0
    iov[1].iov_base = (void*)data;
199
0
    iov[1].iov_len = data_len;
200
0
    iov[2].iov_base = (void*)CRLF;
201
0
    iov[2].iov_len = sizeof(CRLF);
202
203
0
#ifndef LIBYRMCDS_NO_INTERNAL_LOCK
204
0
    int e = pthread_mutex_lock(&c->lock);
205
0
    if( e != 0 ) {
206
0
        errno = e;
207
0
        return YRMCDS_SYSTEM_ERROR;
208
0
    }
209
0
#endif // ! LIBYRMCDS_NO_INTERNAL_LOCK
210
211
0
    c->serial = c->serial + 1;
212
0
    if( serial != NULL )
213
0
        *serial = c->serial;
214
215
0
    while( iovcnt > 0 ) {
216
0
        ssize_t n = writev(c->sock, iov, iovcnt);
217
0
        if( n == -1 ) {
218
0
            if( errno == EINTR ) continue;
219
0
            ret = YRMCDS_SYSTEM_ERROR;
220
0
            goto OUT;
221
0
        }
222
0
        size_t n2 = (size_t)n;
223
0
        while( n2 > 0 ) {
224
0
            if( n2 < iov[0].iov_len ) {
225
0
                iov[0].iov_base = (char*)iov[0].iov_base + n2;
226
0
                iov[0].iov_len -= n2;
227
0
                break;
228
0
            }
229
0
            n2 -= iov[0].iov_len;
230
0
            iovcnt --;
231
0
            if( iovcnt == 0 )
232
0
                break;
233
234
0
            int i;
235
0
            for( i = 0; i < iovcnt; ++i )
236
0
                iov[i] = iov[i+1];
237
0
        }
238
0
    }
239
240
0
  OUT:
241
0
#ifndef LIBYRMCDS_NO_INTERNAL_LOCK
242
0
    pthread_mutex_unlock(&c->lock);
243
0
#endif
244
0
    if( compressed )
245
0
        free((void*)data);
246
0
    return ret;
247
0
}
248
249
250
// public functions.
251
yrmcds_error yrmcds_text_get(yrmcds* c, const char* key, size_t key_len,
252
0
                             int quiet, uint32_t* serial) {
253
0
    if( key == NULL || key_len == 0 || quiet )
254
0
        return YRMCDS_BAD_ARGUMENT;
255
256
0
    yrmcds_error ret;
257
0
    ret = check_key(key, key_len);
258
0
    if( ret != YRMCDS_OK ) return ret;
259
260
0
    textbuf_t buf[1];
261
0
    textbuf_init(buf);
262
263
0
    textbuf_append_const_string(buf, "gets ");
264
0
    textbuf_append_string(buf, key, key_len);
265
266
0
    return send_command(c, buf, serial);
267
0
}
268
269
yrmcds_error yrmcds_text_touch(yrmcds* c, const char* key, size_t key_len,
270
0
                               uint32_t expire, int quiet, uint32_t* serial) {
271
0
    if( key == NULL || key_len == 0 || quiet )
272
0
        return YRMCDS_BAD_ARGUMENT;
273
274
0
    yrmcds_error ret;
275
0
    ret = check_key(key, key_len);
276
0
    if( ret != YRMCDS_OK ) return ret;
277
278
0
    textbuf_t buf[1];
279
0
    textbuf_init(buf);
280
281
0
    textbuf_append_const_string(buf, "touch ");
282
0
    textbuf_append_string(buf, key, key_len);
283
0
    textbuf_append_char(buf, ' ');
284
0
    textbuf_append_uint64(buf, expire);
285
286
0
    return send_command(c, buf, serial);
287
0
}
288
289
yrmcds_error yrmcds_text_set(yrmcds* c, const char* key, size_t key_len,
290
                             const char* data, size_t data_len,
291
                             uint32_t flags, uint32_t expire, uint64_t cas,
292
0
                             int quiet, uint32_t* serial) {
293
0
    return send_data(c, EXPAND_STR("set"), key, key_len, data, data_len,
294
0
                     flags, expire, cas, quiet, serial);
295
0
}
296
297
yrmcds_error yrmcds_text_replace(yrmcds* c, const char* key, size_t key_len,
298
                                 const char* data, size_t data_len,
299
                                 uint32_t flags, uint32_t expire, uint64_t cas,
300
0
                                 int quiet, uint32_t* serial) {
301
0
    return send_data(c, EXPAND_STR("replace"), key, key_len, data, data_len,
302
0
                     flags, expire, cas, quiet, serial);
303
0
}
304
305
yrmcds_error yrmcds_text_add(yrmcds* c, const char* key, size_t key_len,
306
                             const char* data, size_t data_len,
307
                             uint32_t flags, uint32_t expire, uint64_t cas,
308
0
                             int quiet, uint32_t* serial) {
309
0
    return send_data(c, EXPAND_STR("add"), key, key_len, data, data_len,
310
0
                     flags, expire, cas, quiet, serial);
311
0
}
312
313
yrmcds_error yrmcds_text_append(yrmcds* c, const char* key, size_t key_len,
314
                                const char* data, size_t data_len,
315
0
                                int quiet, uint32_t* serial) {
316
0
    return send_data(c, EXPAND_STR("append"), key, key_len, data, data_len,
317
0
                     0, 0, 0, quiet, serial);
318
0
}
319
320
yrmcds_error yrmcds_text_prepend(yrmcds* c, const char* key, size_t key_len,
321
                                 const char* data, size_t data_len,
322
0
                                 int quiet, uint32_t* serial) {
323
0
    return send_data(c, EXPAND_STR("prepend"), key, key_len, data, data_len,
324
0
                     0, 0, 0, quiet, serial);
325
0
}
326
327
yrmcds_error yrmcds_text_incr(yrmcds* c, const char* key, size_t key_len,
328
0
                              uint64_t value, int quiet, uint32_t* serial) {
329
0
    if( key == NULL || key_len == 0 || quiet )
330
0
        return YRMCDS_BAD_ARGUMENT;
331
332
0
    yrmcds_error ret;
333
0
    ret = check_key(key, key_len);
334
0
    if( ret != YRMCDS_OK ) return ret;
335
336
0
    textbuf_t buf[1];
337
0
    textbuf_init(buf);
338
339
0
    textbuf_append_const_string(buf, "incr ");
340
0
    textbuf_append_string(buf, key, key_len);
341
0
    textbuf_append_char(buf, ' ');
342
0
    textbuf_append_uint64(buf, value);
343
344
0
    return send_command(c, buf, serial);
345
0
}
346
347
yrmcds_error yrmcds_text_decr(yrmcds* c, const char* key, size_t key_len,
348
0
                              uint64_t value, int quiet, uint32_t* serial) {
349
0
    if( key == NULL || key_len == 0 || quiet )
350
0
        return YRMCDS_BAD_ARGUMENT;
351
352
0
    yrmcds_error ret;
353
0
    ret = check_key(key, key_len);
354
0
    if( ret != YRMCDS_OK ) return ret;
355
356
0
    textbuf_t buf[1];
357
0
    textbuf_init(buf);
358
359
0
    textbuf_append_const_string(buf, "decr ");
360
0
    textbuf_append_string(buf, key, key_len);
361
0
    textbuf_append_char(buf, ' ');
362
0
    textbuf_append_uint64(buf, value);
363
364
0
    return send_command(c, buf, serial);
365
0
}
366
367
yrmcds_error yrmcds_text_remove(yrmcds* c, const char* key, size_t key_len,
368
0
                                int quiet, uint32_t* serial) {
369
0
    if( key == NULL || key_len == 0 || quiet )
370
0
        return YRMCDS_BAD_ARGUMENT;
371
372
0
    yrmcds_error ret;
373
0
    ret = check_key(key, key_len);
374
0
    if( ret != YRMCDS_OK ) return ret;
375
376
0
    textbuf_t buf[1];
377
0
    textbuf_init(buf);
378
379
0
    textbuf_append_const_string(buf, "delete ");
380
0
    textbuf_append_string(buf, key, key_len);
381
382
0
    return send_command(c, buf, serial);
383
0
}
384
385
yrmcds_error yrmcds_text_flush(yrmcds* c, uint32_t delay,
386
0
                               int quiet, uint32_t* serial) {
387
0
    if( quiet )
388
0
        return YRMCDS_BAD_ARGUMENT;
389
390
0
    textbuf_t buf[1];
391
0
    textbuf_init(buf);
392
393
0
    textbuf_append_const_string(buf, "flush_all");
394
0
    if( delay != 0 ) {
395
0
        textbuf_append_char(buf, ' ');
396
0
        textbuf_append_uint64(buf, delay);
397
0
    }
398
399
0
    return send_command(c, buf, serial);
400
0
}
401
402
0
yrmcds_error yrmcds_text_version(yrmcds* c, uint32_t* serial) {
403
0
    textbuf_t buf[1];
404
0
    textbuf_init(buf);
405
0
    textbuf_append_const_string(buf, "version");
406
0
    return send_command(c, buf, serial);
407
0
}
408
409
0
yrmcds_error yrmcds_text_quit(yrmcds* c, uint32_t* serial) {
410
0
    textbuf_t buf[1];
411
0
    textbuf_init(buf);
412
0
    textbuf_append_const_string(buf, "quit");
413
0
    return send_command(c, buf, serial);
414
0
}