5
* Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
6
* See COPYRIGHT.txt for details.
11
#include "hstcpcli.hpp"
12
#include "auto_file.hpp"
13
#include "string_util.hpp"
14
#include "auto_addrinfo.hpp"
19
#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(MSG_NOSIGNAL)
20
#define MSG_NOSIGNAL 0
27
struct hstcpcli : public hstcpcli_i, private noncopyable {
28
hstcpcli(const socket_args& args);
30
virtual int reconnect();
31
virtual bool stable_point();
32
virtual void request_buf_open_index(size_t pst_id, const char *dbn,
33
const char *tbl, const char *idx, const char *retflds, const char *filflds);
35
virtual void request_buf_find(size_t pst_id, const string_ref& op,
36
const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip);
37
virtual void request_buf_insert(size_t pst_id, const string_ref *fvs,
39
virtual void request_buf_update(size_t pst_id, const string_ref& op,
40
const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip,
41
const string_ref *mvs, size_t mvslen);
42
virtual void request_buf_delete(size_t pst_id, const string_ref& op,
43
const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip);
45
virtual void request_buf_exec_generic(size_t pst_id, const string_ref& op,
46
const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip,
47
const string_ref& mod_op, const string_ref *mvs, size_t mvslen,
48
const hstcpcli_filter *fils, size_t filslen);
49
virtual int request_send();
50
virtual int response_recv(size_t& num_flds_r);
51
virtual const string_ref *get_next_row();
52
virtual void response_buf_remove();
53
virtual int get_error_code();
54
virtual std::string get_error();
58
int set_error(int code, const std::string& str);
62
string_buffer readbuf;
63
string_buffer writebuf;
64
size_t response_end_offset; /* incl newline */
65
size_t cur_row_offset;
67
size_t num_req_bufd; /* buffered but not yet sent */
68
size_t num_req_sent; /* sent but not yet received */
69
size_t num_req_rcvd; /* received but not yet removed */
71
std::string error_str;
72
std::vector<string_ref> flds;
75
hstcpcli::hstcpcli(const socket_args& args)
76
: sargs(args), response_end_offset(0), cur_row_offset(0), num_flds(0),
77
num_req_bufd(0), num_req_sent(0), num_req_rcvd(0), error_code(0)
80
if (socket_connect(fd, sargs, err) != 0) {
92
response_end_offset = 0;
101
hstcpcli::reconnect()
106
if (socket_connect(fd, sargs, err) != 0) {
113
hstcpcli::stable_point()
115
/* returns true if cli can send a new request */
116
return fd.get() >= 0 && num_req_bufd == 0 && num_req_sent == 0 &&
117
num_req_rcvd == 0 && response_end_offset == 0;
121
hstcpcli::get_error_code()
127
hstcpcli::get_error()
133
hstcpcli::read_more()
135
const size_t block_size = 4096; // FIXME
136
char *const wp = readbuf.make_space(block_size);
137
const ssize_t rlen = read(fd.get(), wp, block_size);
140
error_str = "read: failed";
142
error_str = "read: eof";
146
readbuf.space_wrote(rlen);
151
hstcpcli::clear_error()
153
DBG(fprintf(stderr, "CLEAR_ERROR: %d\n", error_code));
159
hstcpcli::set_error(int code, const std::string& str)
161
DBG(fprintf(stderr, "SET_ERROR: %d\n", code));
168
hstcpcli::request_buf_open_index(size_t pst_id, const char *dbn,
169
const char *tbl, const char *idx, const char *retflds, const char *filflds)
171
if (num_req_sent > 0 || num_req_rcvd > 0) {
173
set_error(-1, "request_buf_open_index: protocol out of sync");
176
const string_ref dbn_ref(dbn, strlen(dbn));
177
const string_ref tbl_ref(tbl, strlen(tbl));
178
const string_ref idx_ref(idx, strlen(idx));
179
const string_ref rfs_ref(retflds, strlen(retflds));
180
writebuf.append_literal("P\t");
181
append_uint32(writebuf, pst_id); // FIXME size_t ?
182
writebuf.append_literal("\t");
183
writebuf.append(dbn_ref.begin(), dbn_ref.end());
184
writebuf.append_literal("\t");
185
writebuf.append(tbl_ref.begin(), tbl_ref.end());
186
writebuf.append_literal("\t");
187
writebuf.append(idx_ref.begin(), idx_ref.end());
188
writebuf.append_literal("\t");
189
writebuf.append(rfs_ref.begin(), rfs_ref.end());
191
const string_ref fls_ref(filflds, strlen(filflds));
192
writebuf.append_literal("\t");
193
writebuf.append(fls_ref.begin(), fls_ref.end());
195
writebuf.append_literal("\n");
202
append_delim_value(string_buffer& buf, const char *start, const char *finish)
206
const char t[] = "\t\0";
207
buf.append(t, t + 2);
210
buf.append_literal("\t");
211
escape_string(buf, start, finish);
218
hstcpcli::request_buf_exec_generic(size_t pst_id, const string_ref& op,
219
const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip,
220
const string_ref& mod_op, const string_ref *mvs, size_t mvslen,
221
const hstcpcli_filter *fils, size_t filslen)
223
if (num_req_sent > 0 || num_req_rcvd > 0) {
225
set_error(-1, "request_buf_exec_generic: protocol out of sync");
228
append_uint32(writebuf, pst_id); // FIXME size_t ?
229
writebuf.append_literal("\t");
230
writebuf.append(op.begin(), op.end());
231
writebuf.append_literal("\t");
232
append_uint32(writebuf, kvslen); // FIXME size_t ?
233
for (size_t i = 0; i < kvslen; ++i) {
234
const string_ref& kv = kvs[i];
235
append_delim_value(writebuf, kv.begin(), kv.end());
237
if (limit != 0 || skip != 0 || mod_op.size() != 0 || filslen != 0) {
238
writebuf.append_literal("\t");
239
append_uint32(writebuf, limit); // FIXME size_t ?
240
if (skip != 0 || mod_op.size() != 0 || filslen != 0) {
241
writebuf.append_literal("\t");
242
append_uint32(writebuf, skip); // FIXME size_t ?
244
for (size_t i = 0; i < filslen; ++i) {
245
const hstcpcli_filter& f = fils[i];
246
writebuf.append_literal("\t");
247
writebuf.append(f.filter_type.begin(), f.filter_type.end());
248
writebuf.append_literal("\t");
249
writebuf.append(f.op.begin(), f.op.end());
250
writebuf.append_literal("\t");
251
append_uint32(writebuf, f.ff_offset);
252
append_delim_value(writebuf, f.val.begin(), f.val.end());
254
if (mod_op.size() != 0) {
255
writebuf.append_literal("\t");
256
writebuf.append(mod_op.begin(), mod_op.end());
257
for (size_t i = 0; i < mvslen; ++i) {
258
const string_ref& mv = mvs[i];
259
append_delim_value(writebuf, mv.begin(), mv.end());
263
writebuf.append_literal("\n");
269
hstcpcli::request_buf_find(size_t pst_id, const string_ref& op,
270
const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip)
272
return request_buf_exec_generic(pst_id, op, kvs, kvslen, limit, skip,
277
hstcpcli::request_buf_insert(size_t pst_id, const string_ref *fvs,
280
const string_ref insert_op("+", 1);
281
return request_buf_exec_generic(pst_id, insert_op, fvs, fvslen,
282
0, 0, string_ref(), 0, 0);
286
hstcpcli::request_buf_update(size_t pst_id, const string_ref& op,
287
const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip,
288
const string_ref *mvs, size_t mvslen)
290
const string_ref modop_update("U", 1);
291
return request_buf_exec_generic(pst_id, op, kvs, kvslen, limit, skip,
292
modop_update, mvs, mvslen);
296
hstcpcli::request_buf_delete(size_t pst_id, const string_ref& op,
297
const string_ref *kvs, size_t kvslen, uint32_t limit, uint32_t skip)
299
const string_ref modop_delete("D", 1);
300
return request_buf_exec_generic(pst_id, op, kvs, kvslen, limit, skip,
306
hstcpcli::request_send()
308
if (error_code < 0) {
314
return set_error(-1, "write: closed");
316
if (num_req_bufd == 0 || num_req_sent > 0 || num_req_rcvd > 0) {
318
return set_error(-1, "request_send: protocol out of sync");
320
const size_t wrlen = writebuf.size();
321
const ssize_t r = send(fd.get(), writebuf.begin(), wrlen, MSG_NOSIGNAL);
324
return set_error(-1, r < 0 ? "write: failed" : "write: eof");
326
writebuf.erase_front(r);
327
if (static_cast<size_t>(r) != wrlen) {
329
return set_error(-1, "write: incomplete");
331
num_req_sent = num_req_bufd;
333
DBG(fprintf(stderr, "REQSEND 0\n"));
338
hstcpcli::response_recv(size_t& num_flds_r)
340
if (error_code < 0) {
344
if (num_req_bufd > 0 || num_req_sent == 0 || num_req_rcvd > 0 ||
345
response_end_offset != 0) {
347
return set_error(-1, "response_recv: protocol out of sync");
350
num_flds_r = num_flds = 0;
352
return set_error(-1, "read: closed");
356
const char *const lbegin = readbuf.begin() + offset;
357
const char *const lend = readbuf.end();
358
const char *const nl = memchr_char(lbegin, '\n', lend - lbegin);
360
offset = (nl + 1) - readbuf.begin();
363
if (read_more() <= 0) {
365
return set_error(-1, "read: eof");
368
response_end_offset = offset;
371
char *start = readbuf.begin();
372
char *const finish = start + response_end_offset - 1;
373
const size_t resp_code = read_ui32(start, finish);
374
skip_one(start, finish);
375
num_flds_r = num_flds = read_ui32(start, finish);
376
if (resp_code != 0) {
377
skip_one(start, finish);
378
char *const err_begin = start;
379
read_token(start, finish);
380
char *const err_end = start;
381
std::string e = std::string(err_begin, err_end - err_begin);
385
return set_error(resp_code, e);
387
cur_row_offset = start - readbuf.begin();
388
DBG(fprintf(stderr, "[%s] ro=%zu eol=%zu\n",
389
std::string(readbuf.begin(), readbuf.begin() + response_end_offset)
391
cur_row_offset, response_end_offset));
392
DBG(fprintf(stderr, "RES 0\n"));
397
hstcpcli::get_next_row()
400
DBG(fprintf(stderr, "GNR NF 0\n"));
403
if (flds.size() < num_flds) {
404
flds.resize(num_flds);
406
char *start = readbuf.begin() + cur_row_offset;
407
char *const finish = readbuf.begin() + response_end_offset - 1;
408
if (start >= finish) { /* start[0] == nl */
409
DBG(fprintf(stderr, "GNR FIN 0 %p %p\n", start, finish));
412
for (size_t i = 0; i < num_flds; ++i) {
413
skip_one(start, finish);
414
char *const fld_begin = start;
415
read_token(start, finish);
416
char *const fld_end = start;
417
char *wp = fld_begin;
418
if (is_null_expression(fld_begin, fld_end)) {
420
flds[i] = string_ref();
422
unescape_string(wp, fld_begin, fld_end); /* in-place */
423
flds[i] = string_ref(fld_begin, wp);
426
cur_row_offset = start - readbuf.begin();
431
hstcpcli::response_buf_remove()
433
if (response_end_offset == 0) {
435
set_error(-1, "response_buf_remove: protocol out of sync");
438
readbuf.erase_front(response_end_offset);
439
response_end_offset = 0;
447
hstcpcli_i::create(const socket_args& args)
449
return hstcpcli_ptr(new hstcpcli(args));