libcouchbase 0.0.9 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -4
  3. data/README.md +4 -0
  4. data/ext/libcouchbase/CMakeLists.txt +1 -1
  5. data/ext/libcouchbase/RELEASE_NOTES.markdown +42 -0
  6. data/ext/libcouchbase/cmake/Modules/GetVersionInfo.cmake +3 -3
  7. data/ext/libcouchbase/cmake/source_files.cmake +1 -0
  8. data/ext/libcouchbase/include/libcouchbase/cntl.h +27 -1
  9. data/ext/libcouchbase/include/libcouchbase/couchbase.h +0 -10
  10. data/ext/libcouchbase/include/libcouchbase/error.h +8 -1
  11. data/ext/libcouchbase/include/memcached/protocol_binary.h +12 -3
  12. data/ext/libcouchbase/src/auth.cc +0 -4
  13. data/ext/libcouchbase/src/cntl.cc +11 -1
  14. data/ext/libcouchbase/src/connspec.cc +18 -0
  15. data/ext/libcouchbase/src/connspec.h +10 -0
  16. data/ext/libcouchbase/src/dns-srv.cc +13 -14
  17. data/ext/libcouchbase/src/errmap.cc +107 -0
  18. data/ext/libcouchbase/src/errmap.h +113 -0
  19. data/ext/libcouchbase/src/hostlist.cc +0 -35
  20. data/ext/libcouchbase/src/hostlist.h +38 -64
  21. data/ext/libcouchbase/src/http/http.cc +6 -1
  22. data/ext/libcouchbase/src/instance.cc +1 -1
  23. data/ext/libcouchbase/src/internal.h +10 -0
  24. data/ext/libcouchbase/src/mcserver/mcserver.cc +119 -3
  25. data/ext/libcouchbase/src/mcserver/mcserver.h +3 -1
  26. data/ext/libcouchbase/src/mcserver/negotiate.cc +130 -37
  27. data/ext/libcouchbase/src/nodeinfo.cc +1 -1
  28. data/ext/libcouchbase/src/settings.c +3 -0
  29. data/ext/libcouchbase/src/settings.h +5 -0
  30. data/ext/libcouchbase/src/ssl/ssl_common.c +2 -0
  31. data/ext/libcouchbase/tests/basic/t_host.cc +67 -75
  32. data/ext/libcouchbase/tests/iotests/mock-environment.h +2 -1
  33. data/ext/libcouchbase/tests/iotests/t_confmon.cc +3 -4
  34. data/ext/libcouchbase/tests/iotests/t_errmap.cc +97 -0
  35. data/lib/libcouchbase/bucket.rb +27 -12
  36. data/lib/libcouchbase/callbacks.rb +1 -1
  37. data/lib/libcouchbase/connection.rb +18 -5
  38. data/lib/libcouchbase/version.rb +1 -1
  39. data/spec/connection_spec.rb +1 -1
  40. metadata +5 -2
@@ -0,0 +1,113 @@
1
+ #ifndef LCB_ERRMAP_H
2
+ #define LCB_ERRMAP_H
3
+
4
+ #ifdef __cplusplus
5
+
6
+ #include <map>
7
+ #include <set>
8
+ #include <string>
9
+
10
+ namespace lcb {
11
+ namespace errmap {
12
+
13
+ enum ErrorAttribute {
14
+ #define LCB_XERRMAP_ATTRIBUTES(X) \
15
+ X(TEMPORARY, "temp") \
16
+ X(SUBDOC, "subdoc") \
17
+ X(RETRY_NOW, "retry-now") \
18
+ X(RETRY_LATER, "retry-later") \
19
+ X(INVALID_INPUT, "invalid-input") \
20
+ X(NOT_ENABLED, "support") \
21
+ X(AUTH, "auth") \
22
+ X(CONN_STATE_INVALIDATED, "conn-state-invalidated") \
23
+ X(CONSTRAINT_FAILURE, "item-only") \
24
+ X(RETRY_EXP_BACKOFF, "retry-exp-backoff") \
25
+ X(RETRY_LINEAR_BACKOFF, "retry-linear-backoff") \
26
+ X(INTERNAL, "internal") \
27
+ X(DCP, "dcp") \
28
+ X(FETCH_CONFIG, "fetch-config") \
29
+ X(SPECIAL_HANDLING, "special-handling")
30
+
31
+ #define X(c, s) c,
32
+ LCB_XERRMAP_ATTRIBUTES(X)
33
+ #undef X
34
+
35
+ INVALID_ATTRIBUTE
36
+ };
37
+
38
+ struct Error {
39
+ uint16_t code;
40
+ std::string shortname;
41
+ std::string description;
42
+ std::set<ErrorAttribute> attributes;
43
+
44
+ Error() : code(-1) {
45
+ }
46
+
47
+ bool isValid() const {
48
+ return code != uint16_t(-1);
49
+ }
50
+
51
+ bool hasAttribute(ErrorAttribute attr) const {
52
+ return attributes.find(attr) != attributes.end();
53
+ }
54
+ };
55
+
56
+ class ErrorMap {
57
+ public:
58
+ enum ParseStatus {
59
+ /** Couldn't parse JSON!*/
60
+ PARSE_ERROR,
61
+
62
+ /** Version is too high */
63
+ UNKNOWN_VERSION,
64
+
65
+ /** Current version/revision is higher or equal */
66
+ NOT_UPDATED,
67
+
68
+ /** Updated */
69
+ UPDATED
70
+ };
71
+
72
+ ErrorMap();
73
+ ParseStatus parse(const char *s, size_t n, std::string& errmsg);
74
+ ParseStatus parse(const char *s, size_t n) {
75
+ std::string tmp;
76
+ return parse(s, n, tmp);
77
+ }
78
+ size_t getVersion() const { return version; }
79
+ size_t getRevision() const { return revision; };
80
+ const Error& getError(uint16_t code) const;
81
+ bool isLoaded() const {
82
+ return !errors.empty();
83
+ }
84
+
85
+ private:
86
+ static const uint32_t MAX_VERSION;
87
+ ErrorMap(const ErrorMap&);
88
+ typedef std::map<uint16_t, Error> MapType;
89
+ MapType errors;
90
+ uint32_t revision;
91
+ uint32_t version;
92
+ };
93
+
94
+ } // namespace
95
+ } // namespace
96
+
97
+ typedef lcb::errmap::ErrorMap* lcb_pERRMAP;
98
+ #else
99
+ typedef struct lcb_ERRMAP* lcb_pERRMAP;
100
+ #endif /* __cplusplus */
101
+
102
+ #ifdef __cplusplus
103
+ extern "C" {
104
+ #endif
105
+
106
+ lcb_pERRMAP lcb_errmap_new(void);
107
+ void lcb_errmap_free(lcb_pERRMAP);
108
+
109
+ #ifdef __cplusplus
110
+ }
111
+ #endif
112
+
113
+ #endif /* LCB_ERRMAP_H */
@@ -263,38 +263,3 @@ Hostlist::assign(const Hostlist& src)
263
263
  }
264
264
  return *this;
265
265
  }
266
-
267
- extern "C" {
268
- Hostlist* hostlist_create(void) { return new Hostlist(); }
269
- void hostlist_destroy(hostlist_t l) { delete l; }
270
- void hostlist_clear(hostlist_t l) { l->clear(); }
271
- void hostlist_reset_strlist(hostlist_t l) { l->reset_strlist(); }
272
- lcb_error_t hostlist_add_host(hostlist_t l, const lcb_host_t *h) { l->add(*h); return LCB_SUCCESS; }
273
- lcb_host_t *hostlist_shift_next(hostlist_t hl, int wrap) { return hl->next(wrap); }
274
- int hostlist_finished(hostlist_t l) { return l->ix == l->hosts.size(); }
275
- size_t hostlist_size(hostlist_t l) { return l->size(); }
276
- void hostlist_randomize(hostlist_t l) { l->randomize(); }
277
-
278
- lcb_error_t
279
- hostlist_add_string(hostlist_t hl, const char *spec, int len, int deflport) {
280
- return hl->add(spec, len, deflport);
281
- }
282
-
283
- void
284
- hostlist_assign(hostlist_t dst, const hostlist_t src) {
285
- dst->assign(*src);
286
- }
287
-
288
- const lcb_host_t*
289
- hostlist_get(const hostlist_t h, size_t ix) { return &h->hosts[ix]; }
290
-
291
- const char * const *
292
- hostlist_strents(const hostlist_t h) {
293
- h->ensure_strlist();
294
- if (h->hoststrs.size()) {
295
- return &h->hoststrs[0];
296
- } else {
297
- return NULL;
298
- }
299
- }
300
- }
@@ -45,56 +45,74 @@ struct Hostlist {
45
45
  Hostlist() : ix(0) {}
46
46
  ~Hostlist();
47
47
 
48
- void add(const lcb_host_t&);
48
+ /**
49
+ * Adds a string to the hostlist. See lcb_host_parse for details.
50
+ * Note that if the host already exists (see 'lcb_host_equals') it will
51
+ * not be added
52
+ * @param s the string to parse
53
+ * @param len the length of the string
54
+ * @param deflport If `s` does not contain an explicit port, use this
55
+ * port instead.
56
+ * @return LCB_EINVAL if the host string is not valid
57
+ */
49
58
  lcb_error_t add(const char *s, long len, int deflport);
50
59
  lcb_error_t add(const char *s, int deflport) { return add(s, -1, deflport); }
60
+ void add(const lcb_host_t&);
61
+
51
62
  bool exists(const lcb_host_t&) const;
52
63
  bool exists(const char *hostport) const;
64
+
65
+ /**
66
+ * Return the next host in the list.
67
+ * @param wrap If the internal iterator has reached its limit, this
68
+ * indicates whether it should be reset, or if it should return NULL
69
+ * @return a new host if available, or NULL if the list is empty or the
70
+ * iterator is finished.
71
+ */
53
72
  lcb_host_t *next(bool wrap);
54
73
  bool finished() const;
55
74
 
56
75
  size_t size() const { return hosts.size(); }
57
76
  bool empty() const { return hosts.empty(); }
58
77
  Hostlist& assign(const Hostlist& other);
78
+
79
+ /** Clears the hostlist */
59
80
  void clear() { hosts.clear(); reset_strlist(); ix = 0; }
81
+
82
+ /** Randomize the hostlist by shuffling the order. */
60
83
  void randomize();
84
+
85
+ /**
86
+ * String list handling functions. These are used to return the hostlist via
87
+ * the API where we return a char*[] terminated by a NULL pointer.
88
+ */
89
+
90
+ /** Ensure that the string list contains at least one entry */
61
91
  void ensure_strlist();
92
+
93
+ /** Frees the current list of strings */
62
94
  void reset_strlist();
95
+
96
+ const char * const *get_strlist() const { return &hoststrs[0]; }
97
+
63
98
  unsigned int ix;
64
99
  const lcb_host_t& operator[](size_t ix_) const { return hosts[ix_]; }
65
100
 
66
101
  std::vector<lcb_host_t> hosts;
67
102
  std::vector<const char *> hoststrs;
68
- static Hostlist* from_c(hostlist_st* src) {
69
- return reinterpret_cast<Hostlist*>(src);
70
- }
71
103
  };
72
104
  }
73
105
  typedef lcb::Hostlist* hostlist_t;
74
106
 
75
107
  struct hostlist_st : lcb::Hostlist {
108
+ hostlist_st() : Hostlist() {
109
+ }
76
110
  };
77
111
  #endif
78
112
 
79
113
  #ifdef __cplusplus
80
114
  extern "C" {
81
115
  #endif
82
-
83
- /**
84
- * Creates a new hostlist. Returns NULL on allocation failure
85
- */
86
- hostlist_t hostlist_create(void);
87
-
88
- /**
89
- * Frees resources associated with the hostlist
90
- */
91
- void hostlist_destroy(hostlist_t hostlist);
92
-
93
- /**
94
- * Clears the hostlist
95
- */
96
- void hostlist_clear(hostlist_t hostlist);
97
-
98
116
  /**
99
117
  * Parses a string into a hostlist
100
118
  * @param host the target host to populate
@@ -126,50 +144,6 @@ lcb_host_parse(lcb_host_t *host, const char *spec, int speclen, int deflport);
126
144
  */
127
145
  int lcb_host_equals(const lcb_host_t *a, const lcb_host_t *b);
128
146
 
129
- /**
130
- * Adds a string to the hostlist. See lcb_host_parse for details.
131
- * Note that if the host already exists (see 'lcb_host_equals') it will
132
- * not be added
133
- * @return LCB_EINVAL if the host string is not value, LCB_CLIENT_ENOMEM on
134
- * allocation failure.
135
- */
136
- lcb_error_t hostlist_add_string(hostlist_t hostlist,
137
- const char *spec,
138
- int speclen,
139
- int deflport);
140
-
141
- #define hostlist_add_stringz(hostlist, spec, deflport) \
142
- hostlist_add_string(hostlist, spec, -1, deflport)
143
-
144
- lcb_error_t hostlist_add_host(hostlist_t hostlist, const lcb_host_t *host);
145
-
146
- /**
147
- * Return the next host in the list.
148
- * @param hostlist the hostlist to use
149
- * @param rollover If the internal iterator has reached its limit, this
150
- * indicates whether it should be reset, or if it should return NULL
151
- * @return a new host if available, or NULL if the list is empty or the
152
- * iterator is finished.
153
- */
154
- lcb_host_t *hostlist_shift_next(hostlist_t hostlist, int rollover);
155
-
156
- /**
157
- * Randomize the hostlist
158
- */
159
- void hostlist_randomize(hostlist_t hostlist);
160
-
161
- /**
162
- * String list handling functions. These are used to return the hostlist via
163
- * the API where we return a char*[] terminated by a NULL pointer.
164
- */
165
- void hostlist_reset_strlist(hostlist_t hostlist);
166
-
167
- /** Whether the internal iterator has finished. */
168
- int hostlist_finished(hostlist_t);
169
- size_t hostlist_size(const hostlist_t hl);
170
- void hostlist_assign(hostlist_t dst, const hostlist_t src);
171
- const lcb_host_t *hostlist_get(const hostlist_t, size_t);
172
- const char * const *hostlist_strents(const hostlist_t hl);
173
147
  #ifdef __cplusplus
174
148
  }
175
149
  #endif
@@ -459,7 +459,12 @@ Request::setup_inputs(const lcb_CMDHTTP *cmd)
459
459
  return rc;
460
460
  }
461
461
 
462
- add_header("User-Agent", "libcouchbase/" LCB_VERSION_STRING);
462
+ std::string ua("libcouchbase/" LCB_VERSION_STRING);
463
+ if (instance->settings->client_string) {
464
+ ua.append(" ").append(instance->settings->client_string);
465
+ }
466
+ add_header("User-Agent", ua);
467
+
463
468
  if (instance->http_sockpool->maxidle == 0 || !is_data_request()) {
464
469
  add_header("Connection", "close");
465
470
  }
@@ -129,7 +129,7 @@ lcb_st::process_dns_srv(Connspec& spec)
129
129
 
130
130
  const Spechost& host = spec.hosts().front();
131
131
  lcb_error_t rc = LCB_ERROR;
132
- Hostlist* hl = lcb_dnssrv_getbslist(host.hostname.c_str(), host.isSSL(), &rc);
132
+ Hostlist* hl = dnssrv_getbslist(host.hostname.c_str(), host.isSSL(), rc);
133
133
 
134
134
  if (hl == NULL) {
135
135
  lcb_log(LOGARGS(this, INFO), "DNS SRV lookup failed: %s. Ignore this if not relying on DNS SRV records", lcb_strerror(this, rc));
@@ -165,6 +165,16 @@ struct lcb_st {
165
165
  }
166
166
  return bs_state->bootstrap(options);
167
167
  }
168
+
169
+ lcbvb_CONFIG *getConfig() const {
170
+ return cur_configinfo->vbc;
171
+ }
172
+
173
+ int map_key(const std::string& key) {
174
+ int srvix, tmpvb;
175
+ lcbvb_map_key(getConfig(), key.c_str(), key.size(), &tmpvb, &srvix);
176
+ return srvix;
177
+ }
168
178
  #endif
169
179
  };
170
180
 
@@ -29,6 +29,8 @@
29
29
  #define LOGARGS_T(lvl) LOGARGS(this, lvl)
30
30
 
31
31
  #define LOGFMT "<%s:%s> (SRV=%p,IX=%d) "
32
+ #define PKTFMT "OP=0x%x, RC=0x%x, SEQ=%u"
33
+ #define PKTARGS(pkt) (pkt).opcode(), (pkt).status(), (pkt).opaque()
32
34
 
33
35
  #define LOGID(server) get_ctx_host(server->connctx), get_ctx_port(server->connctx), (void*)server, server->index
34
36
  #define LOGID_T() LOGID(this)
@@ -164,6 +166,107 @@ Server::handle_nmv(MemcachedResponse& resinfo, mc_PACKET *oldpkt)
164
166
  return true;
165
167
  }
166
168
 
169
+ /**
170
+ * Determine if this is an error code that we can pass to the user, or can
171
+ * otherwise handle "innately"
172
+ */
173
+ static bool is_fastpath_error(uint16_t rc) {
174
+ switch (rc) {
175
+ case PROTOCOL_BINARY_RESPONSE_SUCCESS:
176
+ case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
177
+ case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
178
+ case PROTOCOL_BINARY_RESPONSE_E2BIG:
179
+ case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
180
+ case PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL:
181
+ case PROTOCOL_BINARY_RESPONSE_ERANGE:
182
+ case PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED:
183
+ case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
184
+ case PROTOCOL_BINARY_RESPONSE_ETMPFAIL:
185
+ case PROTOCOL_BINARY_RESPONSE_ENOMEM:
186
+ case PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_ENOENT:
187
+ case PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_EEXISTS:
188
+ case PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_MISMATCH:
189
+ case PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_EINVAL:
190
+ case PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_E2BIG:
191
+ case PROTOCOL_BINARY_RESPONSE_SUBDOC_VALUE_CANTINSERT:
192
+ case PROTOCOL_BINARY_RESPONSE_SUBDOC_VALUE_ETOODEEP:
193
+ case PROTOCOL_BINARY_RESPONSE_SUBDOC_DOC_NOTJSON:
194
+ case PROTOCOL_BINARY_RESPONSE_SUBDOC_NUM_ERANGE:
195
+ case PROTOCOL_BINARY_RESPONSE_SUBDOC_DELTA_ERANGE:
196
+ case PROTOCOL_BINARY_RESPONSE_SUBDOC_INVALID_COMBO:
197
+ case PROTOCOL_BINARY_RESPONSE_SUBDOC_MULTI_PATH_FAILURE:
198
+ return true;
199
+ default:
200
+ if (rc >= 0xc0 && rc <= 0xcc) {
201
+ // other subdoc?
202
+ return true;
203
+ } else {
204
+ return false;
205
+ }
206
+ break;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Handle an unknown memcached error
212
+ *
213
+ * @param mcresp Response which contains the unknown error
214
+ * @param[out] newerr more user-friendly based on error map attributes
215
+ *
216
+ * @return true if this function handled the error specially (by disconnecting)
217
+ * or false if normal handling should continue.
218
+ */
219
+ bool Server::handle_unknown_error(const MemcachedResponse& mcresp,
220
+ lcb_error_t& newerr) {
221
+
222
+ if (!settings->errmap->isLoaded() || !settings->use_errmap) {
223
+ // If there's no error map, just return false
224
+ return false;
225
+ }
226
+
227
+ // Look up the error map definition for this error
228
+ const errmap::Error& err = settings->errmap->getError(mcresp.status());
229
+
230
+ if (!err.isValid() || err.hasAttribute(errmap::SPECIAL_HANDLING)) {
231
+ lcb_log(LOGARGS_T(ERR), LOGFMT "Received error not in error map or requires special handling! " PKTFMT, LOGID_T(), PKTARGS(mcresp));
232
+ lcbio_ctx_senderr(connctx, LCB_PROTOCOL_ERROR);
233
+ return true;
234
+ } else {
235
+ lcb_log(LOGARGS_T(WARN), LOGFMT "Received server error %s (0x%x) on packet: " PKTFMT, LOGID_T(), err.shortname.c_str(), err.code, PKTARGS(mcresp));
236
+ }
237
+
238
+ if (err.hasAttribute(errmap::FETCH_CONFIG)) {
239
+ instance->bootstrap(BS_REFRESH_THROTTLE);
240
+ }
241
+
242
+ if (err.hasAttribute(errmap::TEMPORARY)) {
243
+ newerr = LCB_GENERIC_TMPERR;
244
+ }
245
+
246
+ if (err.hasAttribute(errmap::CONSTRAINT_FAILURE)) {
247
+ newerr = LCB_GENERIC_CONSTRAINT_ERR;
248
+ }
249
+
250
+ if (err.hasAttribute(errmap::AUTH)) {
251
+ newerr = LCB_AUTH_ERROR;
252
+ }
253
+
254
+ if (err.hasAttribute(errmap::SUBDOC) && newerr == LCB_SUCCESS) {
255
+ newerr = LCB_GENERIC_SUBDOCERR;
256
+ }
257
+
258
+ if (err.hasAttribute(errmap::CONN_STATE_INVALIDATED)) {
259
+ if (newerr != LCB_SUCCESS) {
260
+ newerr = LCB_ERROR;
261
+ }
262
+ lcbio_ctx_senderr(connctx, newerr);
263
+ return true;
264
+ }
265
+
266
+ return false;
267
+
268
+ }
269
+
167
270
  /* This function is called within a loop to process a single packet.
168
271
  *
169
272
  * If a full packet is available, it will process the packet and return
@@ -224,7 +327,14 @@ Server::try_read(lcbio_CTX *ctx, rdb_IOROPE *ior)
224
327
  return PKT_READ_COMPLETE;
225
328
  }
226
329
 
227
- if (mcresp.status() == PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET) {
330
+ lcb_error_t err_override = LCB_SUCCESS;
331
+ ReadState rdstate = PKT_READ_COMPLETE;
332
+
333
+ /* Check if the status code is one which must be handled carefully by the
334
+ * client */
335
+ if (is_fastpath_error(mcresp.status())) {
336
+ // Nothing here!
337
+ } else if (mcresp.status() == PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET) {
228
338
  /* consume the header */
229
339
  DO_ASSIGN_PAYLOAD()
230
340
  if (!handle_nmv(mcresp, request)) {
@@ -232,13 +342,19 @@ Server::try_read(lcbio_CTX *ctx, rdb_IOROPE *ior)
232
342
  }
233
343
  DO_SWALLOW_PAYLOAD()
234
344
  goto GT_DONE;
345
+ } else if (handle_unknown_error(mcresp, err_override)) {
346
+ DO_ASSIGN_PAYLOAD()
347
+ mcreq_dispatch_response(this, request, &mcresp, err_override);
348
+ DO_SWALLOW_PAYLOAD()
349
+ rdstate = PKT_READ_ABORT;
350
+ goto GT_DONE;
235
351
  }
236
352
 
237
353
  /* Figure out if the request is 'ufwd' or not */
238
354
  if (!(request->flags & MCREQ_F_UFWD)) {
239
355
  DO_ASSIGN_PAYLOAD();
240
356
  mcresp.bufh = rdb_get_first_segment(ior);
241
- mcreq_dispatch_response(this, request, &mcresp, LCB_SUCCESS);
357
+ mcreq_dispatch_response(this, request, &mcresp, err_override);
242
358
  DO_SWALLOW_PAYLOAD()
243
359
 
244
360
  } else {
@@ -265,7 +381,7 @@ Server::try_read(lcbio_CTX *ctx, rdb_IOROPE *ior)
265
381
  if (is_last) {
266
382
  mcreq_packet_handled(this, request);
267
383
  }
268
- return PKT_READ_COMPLETE;
384
+ return rdstate;
269
385
  }
270
386
 
271
387
  static void