Module: Nyara::Ext

Defined in:
ext/nyara.c

Class Method Summary collapse

Class Method Details

.decode_uri_kvObject

test only


192
193
194
195
196
197
# File 'ext/url_encoded.c', line 192

static VALUE ext_decode_uri_kv(VALUE _, VALUE str) {
  volatile VALUE k = _new_blank_str();
  volatile VALUE v = _new_blank_str();
  nyara_decode_uri_kv(k, v, RSTRING_PTR(str), RSTRING_LEN(str));
  return rb_ary_new3(2, k, v);
}

.escapeObject

or component ('/', '+' are changed)


239
240
241
242
243
244
245
246
247
248
249
250
# File 'ext/url_encoded.c', line 239

static VALUE ext_escape(VALUE _, VALUE s, VALUE v_ispath) {
  Check_Type(s, T_STRING);
  long len = RSTRING_LEN(s);
  const char* ptr = RSTRING_PTR(s);
  volatile VALUE res = rb_str_buf_new(len);
  bool ispath = RTEST(v_ispath);
  for (long i = 0; i < len; i++) {
    _concat_char(res, ptr[i], ispath);
  }
  rb_enc_associate(res, u8_encoding);
  return res;
}

.fd_recvObject

simulate blocking recv len or eof


409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
# File 'ext/event.c', line 409

static VALUE ext_fd_recv(VALUE _, VALUE v_fd, VALUE v_len, VALUE v_flags) {
  int flags = NUM2INT(v_flags);
  int fd = NUM2INT(v_fd);
  long buf_len = NUM2INT(v_len); // int shall be large enough...

  volatile VALUE str = rb_tainted_str_new(0, buf_len);
  volatile VALUE klass = RBASIC(str)->klass;
  rb_obj_hide(str);

  char* s = RSTRING_PTR(str);
  while(buf_len) {
    long recved = recv(fd, s, buf_len, flags);
    if (recved < 0) {
      if (errno == EAGAIN || errno == EWOULDBLOCK) {
        rb_fiber_yield(1, &sym_reading);
        continue;
      } else {
        rb_sys_fail("recv(2)");
        break;
      }
    } else if (recved == 0) { // reached EOF
      break;
    }
    s += recved;
    buf_len -= recved;
  }

  if (RBASIC(str)->klass || RSTRING_LEN(str) != NUM2INT(v_len)) {
    rb_raise(rb_eRuntimeError, "buffer string modified");
  }
  rb_obj_reveal(str, klass);
  if (buf_len) {
    rb_str_set_len(str, RSTRING_LEN(str) - buf_len);
  }
  rb_obj_taint(str);

  return str;
}

.fd_sendObject

returns sent length


378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
# File 'ext/event.c', line 378

static VALUE ext_fd_send(VALUE _, VALUE v_fd, VALUE v_data, VALUE v_flags) {
  int flags = NUM2INT(v_flags);
  int fd = NUM2INT(v_fd);
  char* buf = RSTRING_PTR(v_data);
  long len = RSTRING_LEN(v_data);

  // similar to _send_data in request.c
  while(len) {
    long written = send(fd, buf, len, flags);
    if (written <= 0) {
      if (errno == EAGAIN || errno == EWOULDBLOCK) {
        rb_fiber_yield(1, &sym_writing);
        continue;
      } else {
        rb_sys_fail("send(2)");
        break;
      }
    } else {
      buf += written;
      len -= written;
      if (len) {
        rb_fiber_yield(1, &sym_writing);
      }
    }
  }

  return LONG2NUM(RSTRING_LEN(v_data) - len);
}

.fd_unwatchObject


369
370
371
372
373
374
# File 'ext/event.c', line 369

static VALUE ext_fd_unwatch(VALUE _, VALUE v_fd) {
  int fd = NUM2INT(v_fd);
  rb_ary_delete(q.curr_request->watched_fds, v_fd);
  DEL_E(fd);
  return Qnil;
}

.fd_watchObject


362
363
364
365
366
367
# File 'ext/event.c', line 362

static VALUE ext_fd_watch(VALUE _, VALUE v_fd) {
  int fd = NUM2INT(v_fd);
  rb_ary_push(q.curr_request->watched_fds, v_fd);
  ADD_E(fd, q.curr_request->rid);
  return Qnil;
}

.graceful_quitObject

set graceful quit flag and do not accept server_fd anymore


316
317
318
319
320
321
# File 'ext/event.c', line 316

static VALUE ext_graceful_quit(VALUE _, VALUE v_server_fd) {
  q.graceful_quit = true;
  int fd = FIX2INT(v_server_fd);
  DEL_E(fd);
  return Qnil;
}

.handle_requestObject

(for test) run request handler, read from associated fd and write to it


449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
# File 'ext/event.c', line 449

static VALUE ext_handle_request(VALUE _, VALUE request) {
  Request* p;
  Data_Get_Struct(request, Request, p);

  while (p->fiber == Qnil || rb_fiber_alive_p(p->fiber)) {
    _handle_request(request);
    // stop if no more to read
    // NOTE this condition is sufficient to terminate handle, because
    // - there's no connect yield during test
    // - there's no view pause yield up to _handle_request
    if (!p->sleeping) {
      char buf[1];
      if (recv(p->fd, buf, 1, MSG_PEEK) <= 0) {
        break;
      }
    }
  }
  return p->instance;
}

.init_queueObject


284
285
286
287
# File 'ext/event.c', line 284

static VALUE ext_init_queue(VALUE _) {
  INIT_E();
  return Qnil;
}

.parse_accept_valueObject


105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'ext/accept.c', line 105

VALUE ext_parse_accept_value(VALUE _, volatile VALUE str) {
  if (str == Qnil) {
    return rb_ary_new();
  }

  str = trim_space_and_truncate(str);
  const char* s = RSTRING_PTR(str);
  long len = RSTRING_LEN(str);
  volatile VALUE out = rb_ary_new();
  qarray_len = 0;
  while (len > 0) {
    long seg_len = find_seg(s, len);
    if (seg_len == 0) {
      break;
    }
    parse_seg(s, seg_len, out);
    s += seg_len + 1;
    len -= seg_len + 1;
  }
  return out;
}

.parse_multipart_boundaryObject

for test


246
247
248
249
250
251
252
253
254
255
# File 'ext/request_parse.c', line 246

static VALUE ext_parse_multipart_boundary(VALUE _, VALUE header) {
  char* s = _parse_multipart_boundary(header);
  if (s) {
    volatile VALUE res = rb_str_new2(s);
    xfree(s);
    return res;
  } else {
    return Qnil;
  }
}

.parse_pathObject

returns parsed length


271
272
273
274
# File 'ext/url_encoded.c', line 271

static VALUE ext_parse_path(VALUE self, VALUE output, VALUE input) {
  long parsed = nyara_parse_path(output, RSTRING_PTR(input), RSTRING_LEN(input));
  return ULONG2NUM(parsed);
}

.rdtscObject


42
43
44
45
# File 'ext/nyara.c', line 42

static VALUE ext_rdtsc(VALUE _) {
  unsigned long long diff = rdtsc() - last_rdtsc;
  return ULL2NUM(diff);
}

.rdtsc_startObject


37
38
39
40
# File 'ext/nyara.c', line 37

static VALUE ext_rdtsc_start(VALUE _) {
  last_rdtsc = rdtsc();
  return Qnil;
}

.request_newObject

for test: find or create a request with a fd


309
310
311
# File 'ext/request.c', line 309

static VALUE ext_request_new(VALUE _) {
  return _request_alloc()->self;
}

.request_send_chunkObject


284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'ext/request.c', line 284

static VALUE ext_request_send_chunk(VALUE _, VALUE self, VALUE str) {
  long len = RSTRING_LEN(str);
  if (!len) {
    return Qnil;
  }
  P;

  char pre_buf[20]; // enough space to hold a long + 2 chars
  long pre_len = sprintf(pre_buf, "%lx\r\n", len);
  if (pre_len <= 0) {
    rb_raise(rb_eRuntimeError, "fail to format chunk length for len: %ld", len);
  }
  bool success = \
    nyara_send_data(p->fd, pre_buf, pre_len) &&
    nyara_send_data(p->fd, RSTRING_PTR(str), len) &&
    nyara_send_data(p->fd, "\r\n", 2);

  if (!success) {
    rb_sys_fail("write(2)");
  }

  return Qnil;
}

.request_send_dataObject


276
277
278
279
280
281
282
# File 'ext/request.c', line 276

static VALUE ext_request_send_data(VALUE _, VALUE self, VALUE data) {
  P;
  char* buf = RSTRING_PTR(data);
  long len = RSTRING_LEN(data);
  nyara_send_data(p->fd, buf, len);
  return Qnil;
}

.request_set_attrsObject

method_num is required, others are optional


333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'ext/request.c', line 333

static VALUE ext_request_set_attrs(VALUE _, VALUE self, VALUE attrs) {
# define ATTR(key) rb_hash_delete(attrs, ID2SYM(rb_intern(key)))
# define HEADER_HASH_NEW rb_class_new_instance(0, NULL, nyara_header_hash_class)
  P;

  VALUE method_num = ATTR("method_num");
  if (method_num == Qnil) {
    rb_raise(rb_eArgError, "bad method_num");
  }
  p->method                      = NUM2INT(method_num);
  p->path                        = ATTR("path");
  p->query                       = ATTR("query");
  p->fiber                       = ATTR("fiber");
  p->scope                       = ATTR("scope");
  p->header                      = ATTR("header");
  p->format                      = ATTR("format");
  p->body                        = ATTR("body");
  p->cookie                      = ATTR("cookie");
  p->session                     = ATTR("session");
  p->flash                       = ATTR("flash");
  p->response_header             = ATTR("response_header");
  p->response_header_extra_lines = ATTR("response_header_extra_lines");
  p->parse_state                 = PS_MESSAGE_COMPLETE;

  if (!RTEST(p->header)) p->header = HEADER_HASH_NEW;
  if (!RTEST(p->response_header)) p->response_header = HEADER_HASH_NEW;
  if (!RTEST(p->response_header_extra_lines)) p->response_header_extra_lines = rb_ary_new();

  if (!RTEST(rb_funcall(attrs, rb_intern("empty?"), 0))) {
    VALUE attrs_inspect = rb_funcall(attrs, rb_intern("inspect"), 0);
    rb_raise(rb_eArgError, "unkown attrs: %.*s", (int)RSTRING_LEN(attrs_inspect), RSTRING_PTR(attrs_inspect));
  }
  return self;
# undef HEADER_HASH_NEW
# undef ATTR
}

.request_set_fdObject

NOTE: must pair with request_unset_fd


314
315
316
317
318
319
320
321
322
# File 'ext/request.c', line 314

static VALUE ext_request_set_fd(VALUE _, VALUE self, VALUE vfd) {
  P;
  int fd = NUM2INT(vfd);
  if (fd) {
    nyara_set_nonblock(fd);
    p->fd = fd;
  }
  return Qnil;
}

.request_set_statusObject

hide internal methods in ext


249
250
251
252
253
# File 'ext/request.c', line 249

static VALUE ext_request_set_status(VALUE _, VALUE self, VALUE n) {
  P;
  p->status = NUM2INT(n);
  return n;
}

.request_sleepObject

put request into sleep


330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
# File 'ext/event.c', line 330

static VALUE ext_request_sleep(VALUE _, VALUE request) {
  Request* p;
  Data_Get_Struct(request, Request, p);

  p->sleeping = true;
  if (!q.fd) {
    // we are in a test
    return Qnil;
  }

  VALUE* v_fds = RARRAY_PTR(p->watched_fds);
  long v_fds_len = RARRAY_LEN(p->watched_fds);
  for (long i = 0; i < v_fds_len; i++) {
    DEL_E(FIX2INT(v_fds[i]));
  }
  DEL_E(p->fd);
  return Qnil;
}

.request_unset_fdObject

unset fd so the free won't cause segfault


325
326
327
328
329
# File 'ext/request.c', line 325

static VALUE ext_request_unset_fd(VALUE _, VALUE self) {
  P;
  p->fd = 0;
  return Qnil;
}

.request_wakeupObject

NOTE this will be executed in another thread, resuming fiber in a non-main thread will stuck


350
351
352
353
354
# File 'ext/event.c', line 350

static VALUE ext_request_wakeup(VALUE _, VALUE request) {
  // NOTE should not use curr_request
  rb_ary_push(q.to_resume_requests, request);
  return Qnil;
}

.run_queueObject

run queue loop with server_fd


290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'ext/event.c', line 290

static VALUE ext_run_queue(VALUE _, VALUE v_server_fd) {
  q.tcp_server_fd = FIX2INT(v_server_fd);
  nyara_set_nonblock(q.tcp_server_fd);
  ADD_E(q.tcp_server_fd, sym_accept);

  st_table* rids = st_init_numtable(); // to uniq rids for every round
  int round_counter = 0;

  while (true) {
    // in an edge-trigger system, there can be
    // invoke full-loop body every 10 rounds
    round_counter++;
    if (round_counter % 10 == 0) {
      round_counter = 0;
      _loop_body_full();
    } else {
      int accept_sz = SELECT_E(rids);
      _loop_body(rids, accept_sz);
      st_clear(rids);
    }
  }

  return Qnil;
}

.set_inactive_timeoutObject

if request is inactive after [timeout] seconds, kill it


324
325
326
327
# File 'ext/event.c', line 324

static VALUE ext_set_inactive_timeout(VALUE _, VALUE v_timeout) {
  q.inactive_timeout = NUM2INT(v_timeout);
  return Qnil;
}

.set_nonblockObject

fd operations


356
357
358
359
360
# File 'ext/event.c', line 356

static VALUE ext_set_nonblock(VALUE _, VALUE v_fd) {
  int fd = FIX2INT(v_fd);
  nyara_set_nonblock(fd);
  return Qnil;
}

.unescapeObject

  • matrix uri params and query are ignored

255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'ext/url_encoded.c', line 255

static VALUE ext_unescape(VALUE _, volatile VALUE s, VALUE v_is_path) {
  Check_Type(s, T_STRING);
  if (RTEST(v_is_path)) {
    volatile VALUE output = _new_blank_str();
    if (nyara_parse_path(output, RSTRING_PTR(s), RSTRING_LEN(s))) {
    }
    return output;
  } else {
    volatile VALUE output = _new_blank_str();
    _decode_url_seg(output, RSTRING_PTR(s), RSTRING_LEN(s), '=');
    return output;
  }
}