1 #include "mctest.h"
2 #include "mc/mcreq-flush-inl.h"
3 #include "mc/forward.h"
4 #include "pktmaker.h"
5 
6 using namespace PacketMaker;
7 using std::vector;
8 using std::string;
9 
10 struct Vars {
11     mc_PACKET *pkt;
12     mc_PIPELINE *pl;
13     nb_IOV iovs[10];
14     mc_IOVINFO ioi;
15     vector<char> reqbuf;
16 
17     Vars() {
18         pkt = NULL;
19         pl = NULL;
20         memset(&ioi, 0, sizeof(ioi));
21         memset(&iovs, 0, sizeof(iovs));
22     }
23 
24     lcb_error_t requestPacket(mc_CMDQUEUE *cq) {
25         return mc_forward_packet(cq, &ioi, &pkt, &pl, 0);
26     }
27 
28     void initInfo() {
29         mc_iovinfo_init(&ioi, iovs, 10);
30     }
31 };
32 
33 class McFwd : public ::testing::Test {};
34 
35 static void
36 setupRequestBuf(vector<char>& out, size_t nkey, size_t nval)
37 {
38     string k(nkey, 'K');
39     string v(nval, 'V');
40     StorageRequest sr(k, v);
41     out.clear();
42     sr.serialize(out);
43     EXPECT_EQ(nkey + nval + 24, out.size());
44 }
45 
46 TEST_F(McFwd, testForwardSingle)
47 {
48     CQWrap cq;
49     StorageRequest sr(string("fookey"), string("foovalue"));
50 
51     mc_IOVINFO iovinfo;
52 
53     nb_IOV iovs[10];
54     // Enqueue first packet inside entire body.
55     vector<char> reqbody;
56     sr.serialize(reqbody);
57 
58     memset(iovs, 0, sizeof(iovs));
59     memset(&iovinfo, 0, sizeof(iovinfo));
60     mc_iovinfo_init(&iovinfo, iovs, 10);
61     ASSERT_EQ(10, iovinfo.c.niov);
62     ASSERT_EQ(&iovs[0], iovinfo.c.iov);
63     ASSERT_NE(0, reqbody.size());
64 
65     iovs->iov_base = &reqbody[0];
66     iovs->iov_len = reqbody.size();
67     iovinfo.total = reqbody.size();
68 
69     mc_PACKET *pkt = NULL;
70     mc_PIPELINE *pl = NULL;
71     lcb_error_t rc = mc_forward_packet(&cq, &iovinfo, &pkt, &pl, 0);
72     ASSERT_EQ(LCB_SUCCESS, rc);
73     ASSERT_EQ(0, iovinfo.wanted);
74     ASSERT_EQ(reqbody.size(), iovinfo.consumed);
75     ASSERT_EQ(9, iovinfo.c.niov);
76     ASSERT_EQ(0, iovinfo.c.offset);
77     mcreq_sched_fail(&cq);
78 }
79 
80 TEST_F(McFwd, testFragmentedBasic)
81 {
82     CQWrap cq;
83     nb_IOV iovs[10];
84     vector<char> reqbuf;
85 
86     memset(iovs, 0, sizeof(iovs));
87     setupRequestBuf(reqbuf, 10, 10);
88 
89     iovs[0].iov_base = &reqbuf[0];
90     iovs[0].iov_len = 34;
91 
92     iovs[1].iov_base = &reqbuf[34];
93     iovs[1].iov_len = 10;
94 
95     mc_IOVINFO ioi;
96     memset(&ioi, 0, sizeof(ioi));
97     mc_iovinfo_init(&ioi, iovs, 10);
98     lcb_error_t rc;
99     mc_PACKET *pkt;
100     mc_PIPELINE *pl;
101 
102     rc = mc_forward_packet(&cq, &ioi, &pkt, &pl, 0);
103     ASSERT_EQ(LCB_SUCCESS, rc);
104     ASSERT_EQ(0, ioi.wanted);
105     ASSERT_EQ(44, ioi.consumed);
106     ASSERT_EQ(0, ioi.c.offset);
107     ASSERT_EQ(8, ioi.c.niov);
108     ASSERT_EQ(0, ioi.c.iov[0].iov_len);
109     mcreq_sched_fail(&cq);
110 }
111 
112 TEST_F(McFwd, testFragmentedHeader) {
113     CQWrap cq;
114     Vars vars;
115 
116     setupRequestBuf(vars.reqbuf, 100, 100);
117     vars.iovs[0].iov_base = &vars.reqbuf[0];
118     vars.iovs[0].iov_len = 10;
119 
120     vars.iovs[1].iov_base = &vars.reqbuf[10];
121     vars.iovs[1].iov_len = 10;
122 
123     vars.iovs[2].iov_base = &vars.reqbuf[20];
124     vars.iovs[2].iov_len = vars.reqbuf.size() - 20;
125     vars.initInfo();
126     ASSERT_EQ(vars.reqbuf.size(), vars.ioi.total);
127 
128     lcb_error_t rc = vars.requestPacket(&cq);
129     ASSERT_EQ(LCB_SUCCESS, rc);
130     ASSERT_EQ(0, vars.pkt->flags & MCREQ_F_KEY_NOCOPY);
131     ASSERT_EQ(0, vars.ioi.total);
132     ASSERT_EQ(0, vars.ioi.c.offset);
133     ASSERT_EQ(vars.reqbuf.size(), vars.ioi.consumed);
134     ASSERT_EQ(0, vars.ioi.c.iov[0].iov_len);
135     ASSERT_EQ(7, vars.ioi.c.niov);
136 
137     mcreq_sched_fail(&cq);
138 }
139 
140 TEST_F(McFwd, testInsufficientHeader)
141 {
142     CQWrap cq;
143     Vars vars;
144     lcb_error_t rc;
145 
146     setupRequestBuf(vars.reqbuf, 100, 100);
147 
148     // Test with no data
149     vars.iovs[0].iov_base = NULL;
150     vars.iovs[0].iov_len = 0;
151     vars.initInfo();
152     rc = vars.requestPacket(&cq);
153     ASSERT_EQ(LCB_INCOMPLETE_PACKET, rc);
154     ASSERT_EQ(24, vars.ioi.wanted);
155 
156     // Test with partial (but incomplete header)
157     vars.iovs[0].iov_base = &vars.reqbuf[0];
158     vars.iovs[0].iov_len = 20;
159     vars.initInfo();
160     rc = vars.requestPacket(&cq);
161     ASSERT_EQ(LCB_INCOMPLETE_PACKET, rc);
162     ASSERT_EQ(24, vars.ioi.wanted);
163 
164     // Test with full header but partial key
165     vars.iovs[0].iov_base = &vars.reqbuf[0];
166     vars.iovs[0].iov_len = 30;
167     vars.initInfo();
168     rc = vars.requestPacket(&cq);
169     ASSERT_EQ(rc, LCB_INCOMPLETE_PACKET);
170     ASSERT_EQ(vars.reqbuf.size(), vars.ioi.wanted);
171 }
172 
173 TEST_F(McFwd, testMultiValue)
174 {
175     CQWrap cq;
176     Vars vars;
177     lcb_error_t rc;
178     setupRequestBuf(vars.reqbuf, 1, 810);
179 
180     vars.iovs[0].iov_base = &vars.reqbuf[0];
181     vars.iovs[0].iov_len = 25;
182 
183     for (int ii = 1; ii < 10; ii++) {
184         vars.iovs[ii].iov_base = &vars.reqbuf[25 + (ii-1) * 90];
185         vars.iovs[ii].iov_len = 90;
186     }
187 
188     vars.initInfo();
189     ASSERT_EQ(835, vars.reqbuf.size());
190     ASSERT_EQ(835, vars.ioi.total);
191 
192     rc = vars.requestPacket(&cq);
193     ASSERT_EQ(LCB_SUCCESS, rc);
194     ASSERT_NE(0, vars.pkt->flags & MCREQ_F_VALUE_IOV);
195     mcreq_sched_fail(&cq);
196 
197     // Eh, let's check these other nifty things. Why not?
198     ASSERT_EQ(0, vars.ioi.wanted);
199     ASSERT_EQ(0, vars.ioi.c.niov);
200 }
201 
202 TEST_F(McFwd, testNoMap)
203 {
204     CQWrap cq;
205     lcb_error_t err;
206     protocol_binary_request_header hdr;
207     memset(&hdr, 0, sizeof hdr);
208     hdr.request.magic = PROTOCOL_BINARY_REQ;
209     hdr.request.opcode = 0x50;
210     hdr.request.extlen = 8;
211     hdr.request.bodylen = htonl(8);
212     hdr.request.vbucket = 0;
213     char reqbuf[32] = { 0 };
214     memcpy(reqbuf, hdr.bytes, sizeof hdr.bytes);
215     mc_IOVINFO ioi;
216     nb_IOV iov;
217     iov.iov_base = reqbuf;
218     iov.iov_len = sizeof reqbuf;
219     mc_iovinfo_init(&ioi, &iov, 1);
220 
221     mc_PACKET *pkt_tmp;
222     mc_PIPELINE *pl_tmp = cq.pipelines[0];
223     err = mc_forward_packet(&cq, &ioi, &pkt_tmp, &pl_tmp, MC_FWD_OPT_NOMAP);
224 
225     ASSERT_EQ(LCB_SUCCESS, err);
226     ASSERT_NE(0, pkt_tmp->flags & MCREQ_F_UFWD);
227 
228     // Get the key
229     const void *key;
230     lcb_SIZE nkey;
231     mcreq_get_key(pkt_tmp, &key, &nkey);
232     ASSERT_EQ(0, nkey);
233 
234     // Ensure we have no vBucket stamping
235     protocol_binary_request_header hdr2;
236     mcreq_read_hdr(pkt_tmp, &hdr2);
237     ASSERT_EQ(0, hdr2.request.vbucket);
238     mcreq_sched_fail(&cq);
239 }
240