agoo 2.3.0 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of agoo might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 68f159d050def385814b8fecb4b3d28cd638bedaa658543881a74f2587957260
4
- data.tar.gz: 72a2429c4eb44db0ddf7a033d7fde7a7fcdc112e032b9102340d7fb74a92f68b
3
+ metadata.gz: 4b76a004b8311f4a6966175ff830f0f38babc63efddde69c39a7b4ccc3d90b4a
4
+ data.tar.gz: 3356bf438fb1ebdcf6aa9c2d22c3536e5f5fe7c23cd909962effc965d269fc7d
5
5
  SHA512:
6
- metadata.gz: bc963d7748c0406fa4e82133af064ec1c97607e3cc6c1695939fc5869b35d83f134b7ae440386f06531e9b65d27fcdb9bbc7db9122bdc5fdb1987f5f049703b3
7
- data.tar.gz: fe94670810539c14abba9cdaba57843e1cbd0c8425b72d18d9ac036ee83fc95d513263cac5b2e0156dd94a8a908d70ea26e1fadd50334b7faa7bf5b94c88e38d
6
+ metadata.gz: 200da7c5f41b3822cdcb68a0ddd79e7c66a976e33da33245eafa9a66907166be3144874c889c98ac6891a0f2c46746019fcdf100e18df8f6d4a3884355dc7986
7
+ data.tar.gz: 1753cd691ec0bd0443a5a2d6e7af894ca56d705a9e6c3ebe645f4be7ae2156784bc94b4d3dcf6ecb17a5a4003831991120ebb19cade82435fb0599cf71d605e0
@@ -1,5 +1,11 @@
1
1
  # CHANGELOG
2
2
 
3
+ ### 2.4.0 - 2018-07-04
4
+
5
+ - Rack hijack now supported.
6
+
7
+ - Upgraded handler `on_error` now supported and called on Websocket or SSE errors.
8
+
3
9
  ### 2.3.0 - 2018-06-29
4
10
 
5
11
  - Added an `env` method to the upgrade (Websocket and SSE) client.
@@ -152,6 +152,25 @@ page_response(Con c, Page p, char *hend) {
152
152
  return false;
153
153
  }
154
154
 
155
+ static void
156
+ push_error(Upgraded up, const char *msg, int mlen) {
157
+ if (NULL != up && Qnil != up->handler && up->on_error) {
158
+ Req req = request_create(mlen);
159
+
160
+ if (NULL == req) {
161
+ return;
162
+ }
163
+ memcpy(req->msg, msg, mlen);
164
+ req->msg[mlen] = '\0';
165
+ req->up = up;
166
+ req->method = ON_ERROR;
167
+ req->handler_type = PUSH_HOOK;
168
+ req->handler = up->handler;
169
+ upgraded_ref(up);
170
+ queue_push(&the_server.eval_queue, (void*)req);
171
+ }
172
+ }
173
+
155
174
  // Returns:
156
175
  // 0 - when header has not been read
157
176
  // message length - when length can be determined
@@ -487,6 +506,9 @@ con_ws_read(Con c) {
487
506
  if (0 == cnt) {
488
507
  log_cat(&warn_cat, "Nothing to read. Client closed socket on connection %llu.", (unsigned long long)c->id);
489
508
  } else {
509
+ char msg[1024];
510
+ int len = snprintf(msg, sizeof(msg) - 1, "Failed to read WebSocket message. %s.", strerror(errno));
511
+ push_error(c->up, msg, len);
490
512
  log_cat(&warn_cat, "Failed to read WebSocket message. %s.", strerror(errno));
491
513
  }
492
514
  }
@@ -525,10 +547,16 @@ con_ws_read(Con c) {
525
547
  }
526
548
  break;
527
549
  case WS_OP_CONT:
528
- default:
550
+ default: {
551
+ char msg[1024];
552
+ int len = snprintf(msg, sizeof(msg) - 1, "WebSocket op 0x%02x not supported on %llu.",
553
+ op, (unsigned long long)c->id);
554
+
555
+ push_error(c->up, msg, len);
529
556
  log_cat(&error_cat, "WebSocket op 0x%02x not supported on %llu.", op, (unsigned long long)c->id);
530
557
  return true;
531
558
  }
559
+ }
532
560
  }
533
561
  if (NULL != c->req) {
534
562
  mlen = c->req->mlen;
@@ -638,9 +666,15 @@ con_ws_write(Con c) {
638
666
  if (NULL == message) {
639
667
  if (res->ping) {
640
668
  if (0 > (cnt = send(c->sock, ping_msg, sizeof(ping_msg) - 1, 0))) {
669
+ char msg[1024];
670
+ int len;
671
+
641
672
  if (EAGAIN == errno) {
642
673
  return false;
643
674
  }
675
+ len = snprintf(msg, sizeof(msg) - 1, "Socket error @ %llu.", (unsigned long long)c->id);
676
+ push_error(c->up, msg, len);
677
+
644
678
  log_cat(&error_cat, "Socket error @ %llu.", (unsigned long long)c->id);
645
679
  ws_req_close(c);
646
680
  res_destroy(res);
@@ -649,9 +683,14 @@ con_ws_write(Con c) {
649
683
  }
650
684
  } else if (res->pong) {
651
685
  if (0 > (cnt = send(c->sock, pong_msg, sizeof(pong_msg) - 1, 0))) {
686
+ char msg[1024];
687
+ int len;
688
+
652
689
  if (EAGAIN == errno) {
653
690
  return false;
654
691
  }
692
+ len = snprintf(msg, sizeof(msg) - 1, "Socket error @ %llu.", (unsigned long long)c->id);
693
+ push_error(c->up, msg, len);
655
694
  log_cat(&error_cat, "Socket error @ %llu.", (unsigned long long)c->id);
656
695
  ws_req_close(c);
657
696
  res_destroy(res);
@@ -691,9 +730,14 @@ con_ws_write(Con c) {
691
730
  }
692
731
  }
693
732
  if (0 > (cnt = send(c->sock, message->text + c->wcnt, message->len - c->wcnt, 0))) {
733
+ char msg[1024];
734
+ int len;
735
+
694
736
  if (EAGAIN == errno) {
695
737
  return false;
696
738
  }
739
+ len = snprintf(msg, sizeof(msg) - 1, "Socket error @ %llu.", (unsigned long long)c->id);
740
+ push_error(c->up, msg, len);
697
741
  log_cat(&error_cat, "Socket error @ %llu.", (unsigned long long)c->id);
698
742
  ws_req_close(c);
699
743
 
@@ -741,9 +785,14 @@ con_sse_write(Con c) {
741
785
  }
742
786
  }
743
787
  if (0 > (cnt = send(c->sock, message->text + c->wcnt, message->len - c->wcnt, 0))) {
788
+ char msg[1024];
789
+ int len;
790
+
744
791
  if (EAGAIN == errno) {
745
792
  return false;
746
793
  }
794
+ len = snprintf(msg, sizeof(msg) - 1, "Socket error @ %llu.", (unsigned long long)c->id);
795
+ push_error(c->up, msg, len);
747
796
  log_cat(&error_cat, "Socket error @ %llu.", (unsigned long long)c->id);
748
797
  ws_req_close(c);
749
798
 
@@ -923,7 +972,11 @@ poll_setup(Con c, struct pollfd *pp) {
923
972
  pp->revents = 0;
924
973
  pp++;
925
974
  for (; NULL != c; c = c->next) {
926
- if (c->dead) {
975
+ if (c->dead || 0 == c->sock) {
976
+ continue;
977
+ }
978
+ if (c->hijacked) {
979
+ c->sock = 0;
927
980
  continue;
928
981
  }
929
982
  c->pp = pp;
@@ -1085,7 +1138,7 @@ con_loop(void *x) {
1085
1138
  goto CON_CHECK;
1086
1139
  }
1087
1140
  CON_CHECK:
1088
- if (c->dead) {
1141
+ if (c->dead || 0 == c->sock) {
1089
1142
  if (remove_dead_res(c)) {
1090
1143
  goto CON_RM;
1091
1144
  }
@@ -32,6 +32,7 @@ typedef struct _Con {
32
32
  double timeout;
33
33
  bool closing;
34
34
  bool dead;
35
+ volatile bool hijacked;
35
36
  Req req;
36
37
  Res res_head;
37
38
  Res res_tail;
@@ -20,6 +20,7 @@ typedef enum {
20
20
  ON_CLOSE = 'X', // use for on_close callback
21
21
  ON_SHUTDOWN = 'S', // use for on_shotdown callback
22
22
  ON_EMPTY = 'E', // use for on_drained callback
23
+ ON_ERROR = 'F', // use for on_error callback
23
24
  } Method;
24
25
 
25
26
  #endif // __AGOO_METHOD_H__
@@ -26,6 +26,9 @@ static VALUE post_val = Qundef;
26
26
  static VALUE put_val = Qundef;
27
27
  static VALUE query_string_val = Qundef;
28
28
  static VALUE rack_errors_val = Qundef;
29
+ static VALUE rack_hijack_io_val = Qundef;
30
+ static VALUE rack_hijack_val = Qundef;
31
+ static VALUE rack_hijackq_val = Qundef;
29
32
  static VALUE rack_input_val = Qundef;
30
33
  static VALUE rack_logger_val = Qundef;
31
34
  static VALUE rack_multiprocess_val = Qundef;
@@ -546,7 +549,7 @@ rack_logger(VALUE self) {
546
549
  * request is a more efficient encapsulation of the rack environment.
547
550
  */
548
551
  VALUE
549
- request_env(Req req) {
552
+ request_env(Req req, VALUE self) {
550
553
  if (Qnil == req->env) {
551
554
  volatile VALUE env = rb_hash_new();
552
555
 
@@ -570,6 +573,17 @@ request_env(Req req) {
570
573
  rb_hash_aset(env, rack_run_once_val, Qfalse);
571
574
  rb_hash_aset(env, rack_logger_val, req_rack_logger(req));
572
575
  rb_hash_aset(env, rack_upgrade_val, req_rack_upgrade(req));
576
+ rb_hash_aset(env, rack_hijackq_val, Qtrue);
577
+
578
+ // TBD should return IO on #call and set hijack_io on env object that
579
+ // has a call method that wraps the req->res->con->sock then set the
580
+ // sock to 0 or maybe con. mutex? env[rack.hijack_io] = IO.new(sock,
581
+ // "rw") - maybe it works.
582
+ //
583
+ // set a flag on con to indicate it has been hijacked
584
+ // then set sock to 0 in con loop and destroy con
585
+ rb_hash_aset(env, rack_hijack_val, self);
586
+ rb_hash_aset(env, rack_hijack_io_val, Qnil);
573
587
 
574
588
  req->env = env;
575
589
  }
@@ -590,7 +604,7 @@ to_h(VALUE self) {
590
604
  if (NULL == r) {
591
605
  rb_raise(rb_eArgError, "Request is no longer valid.");
592
606
  }
593
- return request_env(r);
607
+ return request_env(r, self);
594
608
  }
595
609
 
596
610
  /* Document-method: to_s
@@ -606,6 +620,33 @@ to_s(VALUE self) {
606
620
  return rb_funcall(h, rb_intern("to_s"), 0);
607
621
  }
608
622
 
623
+ /* Document-method: call
624
+ *
625
+ * call-seq: call()
626
+ *
627
+ * Returns an IO like object and hijacks the connection.
628
+ */
629
+ static VALUE
630
+ call(VALUE self) {
631
+ Req r = DATA_PTR(self);
632
+ VALUE args[1];
633
+ volatile VALUE io;
634
+
635
+ if (NULL == r) {
636
+ rb_raise(rb_eArgError, "Request is no longer valid.");
637
+ }
638
+ r->res->con->hijacked = true;
639
+
640
+ // TBD try basic IO first. If that fails define a socket class
641
+ // is a mode needed?
642
+
643
+ args[0] = INT2NUM(r->res->con->sock);
644
+
645
+ io = rb_class_new_instance(1, args, rb_cIO);
646
+ rb_hash_aset(r->env, rack_hijack_io_val, io);
647
+
648
+ return io;
649
+ }
609
650
  void
610
651
  request_destroy(Req req) {
611
652
  DEBUG_FREE(mem_req, req)
@@ -649,6 +690,7 @@ request_init(VALUE mod) {
649
690
  rb_define_method(req_class, "headers", headers, 0);
650
691
  rb_define_method(req_class, "body", body, 0);
651
692
  rb_define_method(req_class, "rack_logger", rack_logger, 0);
693
+ rb_define_method(req_class, "call", call, 0);
652
694
 
653
695
  new_id = rb_intern("new");
654
696
 
@@ -674,6 +716,9 @@ request_init(VALUE mod) {
674
716
  put_val = rb_str_new_cstr("PUT"); rb_gc_register_address(&put_val);
675
717
  query_string_val = rb_str_new_cstr("QUERY_STRING"); rb_gc_register_address(&query_string_val);
676
718
  rack_errors_val = rb_str_new_cstr("rack.errors"); rb_gc_register_address(&rack_errors_val);
719
+ rack_hijack_io_val = rb_str_new_cstr("rack.hijack_io"); rb_gc_register_address(&rack_hijack_io_val);
720
+ rack_hijack_val = rb_str_new_cstr("rack.hijack"); rb_gc_register_address(&rack_hijack_val);
721
+ rack_hijackq_val = rb_str_new_cstr("rack.hijack?"); rb_gc_register_address(&rack_hijackq_val);
677
722
  rack_input_val = rb_str_new_cstr("rack.input"); rb_gc_register_address(&rack_input_val);
678
723
  rack_logger_val = rb_str_new_cstr("rack.logger"); rb_gc_register_address(&rack_logger_val);
679
724
  rack_multiprocess_val = rb_str_new_cstr("rack.multiprocess");rb_gc_register_address(&rack_multiprocess_val);
@@ -44,7 +44,7 @@ typedef struct _Req {
44
44
  extern Req request_create(size_t mlen);
45
45
  extern void request_init(VALUE mod);
46
46
  extern VALUE request_wrap(Req req);
47
- extern VALUE request_env(Req req);
47
+ extern VALUE request_env(Req req, VALUE self);
48
48
  extern void request_destroy(Req req);
49
49
 
50
50
  #endif // __AGOO_REQUEST_H__
@@ -56,6 +56,7 @@ static ID call_id;
56
56
  static ID each_id;
57
57
  static ID on_close_id;
58
58
  static ID on_drained_id;
59
+ static ID on_error_id;
59
60
  static ID on_message_id;
60
61
  static ID on_request_id;
61
62
  static ID to_i_id;
@@ -508,7 +509,7 @@ static VALUE
508
509
  handle_rack_inner(void *x) {
509
510
  Req req = (Req)x;
510
511
  Text t;
511
- volatile VALUE env = request_env(req);
512
+ volatile VALUE env = request_env(req, request_wrap(req));
512
513
  volatile VALUE res = rb_funcall(req->handler, call_id, 1, env);
513
514
  volatile VALUE hv;
514
515
  volatile VALUE bv;
@@ -516,6 +517,10 @@ handle_rack_inner(void *x) {
516
517
  const char *status_msg;
517
518
  int bsize = 0;
518
519
 
520
+ if (req->res->con->hijacked) {
521
+ queue_wakeup(&the_server.con_queue);
522
+ return false;
523
+ }
519
524
  rb_check_type(res, T_ARRAY);
520
525
  if (3 != RARRAY_LEN(res)) {
521
526
  rb_raise(rb_eArgError, "a rack call() response must be an array of 3 members.");
@@ -599,7 +604,7 @@ handle_rack_inner(void *x) {
599
604
  break;
600
605
  }
601
606
  req->handler_type = PUSH_HOOK;
602
- upgraded_create(req->res->con, req->handler, request_env(req));
607
+ upgraded_create(req->res->con, req->handler, request_env(req, Qnil));
603
608
  t->len = snprintf(t->text, 1024, "HTTP/1.1 101 %s\r\n", status_msg);
604
609
  t = ws_add_headers(req, t);
605
610
  break;
@@ -611,7 +616,7 @@ handle_rack_inner(void *x) {
611
616
  break;
612
617
  }
613
618
  req->handler_type = PUSH_HOOK;
614
- upgraded_create(req->res->con, req->handler, request_env(req));
619
+ upgraded_create(req->res->con, req->handler, request_env(req, Qnil));
615
620
  t = sse_upgrade(req, t);
616
621
  res_set_message(req->res, t);
617
622
  queue_wakeup(&the_server.con_queue);
@@ -708,6 +713,14 @@ handle_push_inner(void *x) {
708
713
  case ON_SHUTDOWN:
709
714
  rb_funcall(req->handler, rb_intern("on_shutdown"), 1, req->up->wrap);
710
715
  break;
716
+ case ON_ERROR:
717
+ if (req->up->on_msg) {
718
+ volatile VALUE rstr = rb_str_new(req->msg, req->mlen);
719
+
720
+ rb_enc_associate(rstr, rb_ascii8bit_encoding());
721
+ rb_funcall(req->handler, on_error_id, 2, req->up->wrap, rstr);
722
+ }
723
+ break;
711
724
  case ON_EMPTY:
712
725
  rb_funcall(req->handler, on_drained_id, 1, req->up->wrap);
713
726
  break;
@@ -1034,6 +1047,7 @@ server_init(VALUE mod) {
1034
1047
  each_id = rb_intern("each");
1035
1048
  on_close_id = rb_intern("on_close");
1036
1049
  on_drained_id = rb_intern("on_drained");
1050
+ on_error_id = rb_intern("on_error");
1037
1051
  on_message_id = rb_intern("on_message");
1038
1052
  on_request_id = rb_intern("on_request");
1039
1053
  to_i_id = rb_intern("to_i");
@@ -351,6 +351,7 @@ upgraded_create(Con c, VALUE obj, VALUE env) {
351
351
  up->on_close = rb_respond_to(obj, rb_intern("on_close"));
352
352
  up->on_shut = rb_respond_to(obj, rb_intern("on_shutdown"));
353
353
  up->on_msg = rb_respond_to(obj, rb_intern("on_message"));
354
+ up->on_error = rb_respond_to(obj, rb_intern("on_error"));
354
355
  up->wrap = Data_Wrap_Struct(upgraded_class, NULL, NULL, up);
355
356
  up->subjects = NULL;
356
357
  up->prev = NULL;
@@ -24,6 +24,7 @@ typedef struct _Upgraded {
24
24
  bool on_close;
25
25
  bool on_shut;
26
26
  bool on_msg;
27
+ bool on_error;
27
28
  } *Upgraded;
28
29
 
29
30
  extern void upgraded_init(VALUE mod);
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Agoo
3
3
  # Agoo version.
4
- VERSION = '2.3.0'
4
+ VERSION = '2.4.0'
5
5
  end
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.dirname(__FILE__)
4
+ $root_dir = File.dirname(File.expand_path(File.dirname(__FILE__)))
5
+ %w(lib ext).each do |dir|
6
+ $: << File.join($root_dir, dir)
7
+ end
8
+
9
+ require 'minitest'
10
+ require 'minitest/autorun'
11
+ require 'net/http'
12
+
13
+ require 'oj'
14
+
15
+ require 'agoo'
16
+
17
+ class RackHandlerTest < Minitest::Test
18
+
19
+ class HijackHandler
20
+ def call(env)
21
+ io = env['rack.hijack'].call
22
+ io.write(%|HTTP/1.1 200 OK
23
+ Content-Length: 11
24
+
25
+ hello world|)
26
+ io.flush
27
+ [ -1, {}, []]
28
+ end
29
+ end
30
+
31
+ def test_hijack
32
+ begin
33
+ Agoo::Log.configure(dir: '',
34
+ console: true,
35
+ classic: true,
36
+ colorize: true,
37
+ states: {
38
+ INFO: false,
39
+ DEBUG: false,
40
+ connect: false,
41
+ request: false,
42
+ response: false,
43
+ eval: true,
44
+ })
45
+
46
+ Agoo::Server.init(6471, 'root', thread_count: 1)
47
+
48
+ handler = HijackHandler.new
49
+ Agoo::Server.handle(:GET, "/hijack", handler)
50
+
51
+ Agoo::Server.start()
52
+
53
+ jack
54
+
55
+ ensure
56
+ Agoo.shutdown
57
+ end
58
+ end
59
+
60
+ def jack
61
+ uri = URI('http://localhost:6471/hijack')
62
+ req = Net::HTTP::Get.new(uri)
63
+ # Set the headers the way we want them.
64
+ req['Accept-Encoding'] = '*'
65
+ req['User-Agent'] = 'Ruby'
66
+ req['Host'] = 'localhost:6471'
67
+
68
+ res = Net::HTTP.start(uri.hostname, uri.port) { |h|
69
+ h.request(req)
70
+ }
71
+ content = res.body
72
+
73
+ assert_equal('hello world', content, 'content mismatch')
74
+ end
75
+
76
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: agoo
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-06-29 00:00:00.000000000 Z
11
+ date: 2018-07-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oj
@@ -107,6 +107,7 @@ files:
107
107
  - lib/agoo/version.rb
108
108
  - lib/rack/handler/agoo.rb
109
109
  - test/base_handler_test.rb
110
+ - test/hijack_test.rb
110
111
  - test/log_test.rb
111
112
  - test/rack_handler_test.rb
112
113
  - test/static_test.rb
@@ -148,5 +149,6 @@ summary: An HTTP server
148
149
  test_files:
149
150
  - test/log_test.rb
150
151
  - test/rack_handler_test.rb
152
+ - test/hijack_test.rb
151
153
  - test/base_handler_test.rb
152
154
  - test/static_test.rb