agoo 2.8.4 → 2.9.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.

@@ -34,13 +34,13 @@ extern int agoo_pages_init(agooErr err);
34
34
  extern int agoo_pages_set_root(agooErr err, const char *root);
35
35
  extern void agoo_pages_cleanup();
36
36
 
37
- extern agooGroup group_create(const char *path);
38
- extern agooDir group_add(agooErr err, agooGroup g, const char *dir);
39
- extern agooPage group_get(agooErr err, const char *path, int plen);
37
+ extern agooGroup agoo_group_create(const char *path);
38
+ extern agooDir agoo_group_add(agooErr err, agooGroup g, const char *dir);
39
+ extern agooPage agoo_group_get(agooErr err, const char *path, int plen);
40
40
 
41
41
  extern agooPage agoo_page_create(const char *path);
42
42
  extern agooPage agoo_page_immutable(agooErr err, const char *path, const char *content, int clen);
43
- extern agooPage agoo_page_get(agooErr err, const char *path, int plen);
43
+ extern agooPage agoo_page_get(agooErr err, const char *path, int plen, const char *root);
44
44
  extern int mime_set(agooErr err, const char *key, const char *value);
45
45
  extern int agoo_header_rule(agooErr err, const char *path, const char *mime, const char *key, const char *value);
46
46
 
@@ -6,6 +6,7 @@
6
6
 
7
7
  #include "debug.h"
8
8
  #include "con.h"
9
+ #include "early_hints.h"
9
10
  #include "error_stream.h"
10
11
  #include "rack_logger.h"
11
12
  #include "request.h"
@@ -17,6 +18,7 @@ static VALUE connect_val = Qundef;
17
18
  static VALUE content_length_val = Qundef;
18
19
  static VALUE content_type_val = Qundef;
19
20
  static VALUE delete_val = Qundef;
21
+ static VALUE early_hints_val = Qundef;
20
22
  static VALUE empty_val = Qundef;
21
23
  static VALUE get_val = Qundef;
22
24
  static VALUE head_val = Qundef;
@@ -564,6 +566,11 @@ request_env(agooReq req, VALUE self) {
564
566
  rb_hash_aset(env, rack_hijack_val, self);
565
567
  rb_hash_aset(env, rack_hijack_io_val, Qnil);
566
568
 
569
+ if (agoo_server.rack_early_hints) {
570
+ volatile VALUE eh = agoo_early_hints_new(req);
571
+
572
+ rb_hash_aset(env, early_hints_val, eh);
573
+ }
567
574
  req->env = (void*)env;
568
575
  }
569
576
  return (VALUE)req->env;
@@ -603,7 +610,7 @@ to_s(VALUE self) {
603
610
  *
604
611
  * call-seq: call()
605
612
  *
606
- * Returns an IO like object and hijacks the connection.
613
+ * Returns an IO like object and hijack the connection.
607
614
  */
608
615
  static VALUE
609
616
  call(VALUE self) {
@@ -679,6 +686,7 @@ request_init(VALUE mod) {
679
686
  content_length_val = rb_str_new_cstr("CONTENT_LENGTH"); rb_gc_register_address(&content_length_val);
680
687
  content_type_val = rb_str_new_cstr("CONTENT_TYPE"); rb_gc_register_address(&content_type_val);
681
688
  delete_val = rb_str_new_cstr("DELETE"); rb_gc_register_address(&delete_val);
689
+ early_hints_val = rb_str_new_cstr("early_hints"); rb_gc_register_address(&early_hints_val);
682
690
  empty_val = rb_str_new_cstr(""); rb_gc_register_address(&empty_val);
683
691
  get_val = rb_str_new_cstr("GET"); rb_gc_register_address(&get_val);
684
692
  head_val = rb_str_new_cstr("HEAD"); rb_gc_register_address(&head_val);
@@ -1,5 +1,6 @@
1
1
  // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
2
 
3
+ #include <stdarg.h>
3
4
  #include <stdio.h>
4
5
  #include <stdlib.h>
5
6
 
@@ -26,9 +27,11 @@ agoo_res_create(agooCon con) {
26
27
  }
27
28
  }
28
29
  res->next = NULL;
29
- atomic_init(&res->message, NULL);
30
+ res->message = NULL;
31
+ pthread_mutex_init(&res->lock, NULL);
30
32
  res->con = con;
31
33
  res->con_kind = AGOO_CON_HTTP;
34
+ res->final = false;
32
35
  res->close = false;
33
36
  res->ping = false;
34
37
  res->pong = false;
@@ -39,10 +42,8 @@ agoo_res_create(agooCon con) {
39
42
  void
40
43
  agoo_res_destroy(agooRes res) {
41
44
  if (NULL != res) {
42
- agooText message = agoo_res_message(res);
43
-
44
- if (NULL != message) {
45
- agoo_text_release(message);
45
+ if (NULL != res->message) {
46
+ agoo_text_release(res->message);
46
47
  }
47
48
  res->next = NULL;
48
49
  pthread_mutex_lock(&res->con->loop->lock);
@@ -57,10 +58,67 @@ agoo_res_destroy(agooRes res) {
57
58
  }
58
59
 
59
60
  void
60
- agoo_res_set_message(agooRes res, agooText t) {
61
+ agoo_res_message_push(agooRes res, agooText t, bool final) {
61
62
  if (NULL != t) {
62
63
  agoo_text_ref(t);
63
64
  }
64
- atomic_store(&res->message, t);
65
+ pthread_mutex_lock(&res->lock);
66
+ if (!res->final) {
67
+ if (NULL == res->message) {
68
+ res->message = t;
69
+ } else {
70
+ agooText end = res->message;
71
+
72
+ for (; NULL != end->next; end = end->next) {
73
+ }
74
+ end->next = t;
75
+ }
76
+ res->final = final;
77
+ }
78
+ pthread_mutex_unlock(&res->lock);
79
+ }
80
+
81
+ static const char early_103[] = "HTTP/1.1 103 Early Hints\r\n";
82
+
83
+ void
84
+ agoo_res_add_early(agooRes res, agooEarly early) {
85
+ agooText t = agoo_text_allocate(1024);
86
+
87
+ t = agoo_text_append(t, early_103, sizeof(early_103) - 1);
88
+ for (; NULL != early; early = early->next) {
89
+ t = agoo_text_append(t, "Link: ", 6);
90
+ t = agoo_text_append(t, early->link, -1);
91
+ t = agoo_text_append(t, "\r\n", 2);
92
+ }
93
+ t = agoo_text_append(t, "\r\n", 2);
94
+ agoo_res_message_push(res, t, false);
95
+ }
96
+
97
+ agooText
98
+ agoo_res_message_peek(agooRes res) {
99
+ agooText t;
100
+
101
+ pthread_mutex_lock(&res->lock);
102
+ t = res->message;
103
+ pthread_mutex_unlock(&res->lock);
104
+
105
+ return t;
65
106
  }
66
107
 
108
+ agooText
109
+ agoo_res_message_next(agooRes res) {
110
+ agooText t;
111
+
112
+ pthread_mutex_lock(&res->lock);
113
+ if (NULL != res->message) {
114
+ agooText t2 = res->message;
115
+
116
+ res->message = res->message->next;
117
+ // TBD make sure it is not release by the called, change code if it is
118
+ agoo_text_release(t2);
119
+ }
120
+ t = res->message;
121
+ pthread_mutex_unlock(&res->lock);
122
+
123
+ return t;
124
+ }
@@ -3,10 +3,12 @@
3
3
  #ifndef AGOO_RES_H
4
4
  #define AGOO_RES_H
5
5
 
6
+ #include <pthread.h>
6
7
  #include <stdbool.h>
7
8
 
8
9
  #include "atomic.h"
9
10
  #include "con.h"
11
+ #include "early.h"
10
12
  #include "text.h"
11
13
 
12
14
  struct _agooCon;
@@ -14,20 +16,21 @@ struct _agooCon;
14
16
  typedef struct _agooRes {
15
17
  struct _agooRes *next;
16
18
  struct _agooCon *con;
17
- _Atomic(agooText) message;
19
+ volatile agooText message;
20
+ pthread_mutex_t lock; // a lock around message changes
21
+ volatile bool final;
18
22
  agooConKind con_kind;
19
23
  bool close;
20
24
  bool ping;
21
25
  bool pong;
22
26
  } *agooRes;
23
27
 
24
- extern agooRes agoo_res_create(struct _agooCon *con);
25
- extern void agoo_res_destroy(agooRes res);
26
- extern void agoo_res_set_message(agooRes res, agooText t);
28
+ extern agooRes agoo_res_create(struct _agooCon *con);
29
+ extern void agoo_res_destroy(agooRes res);
27
30
 
28
- static inline agooText
29
- agoo_res_message(agooRes res) {
30
- return atomic_load(&res->message);
31
- }
31
+ extern void agoo_res_message_push(agooRes res, agooText t, bool final);
32
+ extern void agoo_res_add_early(agooRes res, agooEarly early);
33
+ extern agooText agoo_res_message_peek(agooRes res);
34
+ extern agooText agoo_res_message_next(agooRes res);
32
35
 
33
36
  #endif // AGOO_RES_H
@@ -15,6 +15,7 @@
15
15
  #include "bind.h"
16
16
  #include "con.h"
17
17
  #include "debug.h"
18
+ #include "domain.h"
18
19
  #include "dtime.h"
19
20
  #include "err.h"
20
21
  #include "graphql.h"
@@ -311,7 +312,7 @@ rescue_error(VALUE x) {
311
312
  message = agoo_text_create(buf, cnt);
312
313
 
313
314
  req->res->close = true;
314
- agoo_res_set_message(req->res, message);
315
+ agoo_res_message_push(req->res, message, true);
315
316
  agoo_queue_wakeup(&agoo_server.con_queue);
316
317
  } else {
317
318
  /*
@@ -339,7 +340,7 @@ handle_base_inner(void *x) {
339
340
  rb_funcall((VALUE)req->hook->handler, on_request_id, 2, rr, rres);
340
341
  }
341
342
  DATA_PTR(rr) = NULL;
342
- agoo_res_set_message(req->res, response_text(rres));
343
+ agoo_res_message_push(req->res, response_text(rres), true);
343
344
  agoo_queue_wakeup(&agoo_server.con_queue);
344
345
 
345
346
  return Qfalse;
@@ -514,7 +515,7 @@ handle_rack_inner(void *x) {
514
515
  req->hook = agoo_hook_create(AGOO_NONE, NULL, (void*)handler, PUSH_HOOK, &agoo_server.eval_queue);
515
516
  rupgraded_create(req->res->con, handler, request_env(req, Qnil));
516
517
  t = agoo_sse_upgrade(req, t);
517
- agoo_res_set_message(req->res, t);
518
+ agoo_res_message_push(req->res, t, true);
518
519
  agoo_queue_wakeup(&agoo_server.con_queue);
519
520
  return Qfalse;
520
521
  default:
@@ -545,7 +546,7 @@ handle_rack_inner(void *x) {
545
546
  rb_iterate(rb_each, bv, body_append_cb, (VALUE)&t);
546
547
  }
547
548
  }
548
- agoo_res_set_message(req->res, t);
549
+ agoo_res_message_push(req->res, t, true);
549
550
  agoo_queue_wakeup(&agoo_server.con_queue);
550
551
 
551
552
  return Qfalse;
@@ -571,7 +572,7 @@ handle_wab_inner(void *x) {
571
572
  rb_funcall((VALUE)req->hook->handler, on_request_id, 2, rr, rres);
572
573
  }
573
574
  DATA_PTR(rr) = NULL;
574
- agoo_res_set_message(req->res, response_text(rres));
575
+ agoo_res_message_push(req->res, response_text(rres), true);
575
576
  agoo_queue_wakeup(&agoo_server.con_queue);
576
577
 
577
578
  return Qfalse;
@@ -685,7 +686,7 @@ handle_protected(agooReq req, bool gvi) {
685
686
  agooText message = agoo_text_create(buf, cnt);
686
687
 
687
688
  req->res->close = true;
688
- agoo_res_set_message(req->res, message);
689
+ agoo_res_message_push(req->res, message, true);
689
690
  agoo_queue_wakeup(&agoo_server.con_queue);
690
691
  break;
691
692
  }
@@ -1023,7 +1024,7 @@ path_group(VALUE self, VALUE path, VALUE dirs) {
1023
1024
  rb_check_type(path, T_STRING);
1024
1025
  rb_check_type(dirs, T_ARRAY);
1025
1026
 
1026
- if (NULL != (g = group_create(StringValuePtr(path)))) {
1027
+ if (NULL != (g = agoo_group_create(StringValuePtr(path)))) {
1027
1028
  int i;
1028
1029
  int dcnt = (int)RARRAY_LEN(dirs);
1029
1030
  VALUE entry;
@@ -1033,7 +1034,7 @@ path_group(VALUE self, VALUE path, VALUE dirs) {
1033
1034
  if (T_STRING != rb_type(entry)) {
1034
1035
  entry = rb_funcall(entry, rb_intern("to_s"), 0);
1035
1036
  }
1036
- if (NULL == group_add(&err, g, StringValuePtr(entry))) {
1037
+ if (NULL == agoo_group_add(&err, g, StringValuePtr(entry))) {
1037
1038
  rb_raise(rb_eStandardError, "%s", err.msg);
1038
1039
  }
1039
1040
  }
@@ -1072,6 +1073,69 @@ header_rule(VALUE self, VALUE path, VALUE mime, VALUE key, VALUE value) {
1072
1073
  return Qnil;
1073
1074
  }
1074
1075
 
1076
+ /* Document-method: domain
1077
+ *
1078
+ * call-seq: domain(host, path)
1079
+ *
1080
+ * Sets up a sub-domain. The first argument, _host_ should be either a String
1081
+ * or a Regexp that includes variable replacement elements. The _path_
1082
+ * argument should also be a string. If the _host_ argument is a Regex then
1083
+ * the $(n) sequence will be replaced by the matching variable in the Regex
1084
+ * result. The _path_ is the root of the sub-domain.
1085
+ */
1086
+ static VALUE
1087
+ domain(VALUE self, VALUE host, VALUE path) {
1088
+ struct _agooErr err = AGOO_ERR_INIT;
1089
+
1090
+ switch(rb_type(host)) {
1091
+ case RUBY_T_STRING:
1092
+ rb_check_type(path, T_STRING);
1093
+ if (AGOO_ERR_OK != agoo_domain_add(&err, rb_string_value_ptr((VALUE*)&host), rb_string_value_ptr((VALUE*)&path))) {
1094
+ rb_raise(rb_eArgError, "%s", err.msg);
1095
+ }
1096
+ break;
1097
+ case RUBY_T_REGEXP: {
1098
+ volatile VALUE v = rb_funcall(host, rb_intern("inspect"), 0);
1099
+ char rx[1024];
1100
+
1101
+ if (sizeof(rx) <= RSTRING_LEN(v)) {
1102
+ rb_raise(rb_eArgError, "host Regex limited to %ld characters", sizeof(rx));
1103
+ }
1104
+ strcpy(rx, rb_string_value_ptr((VALUE*)&v) + 1);
1105
+ rx[RSTRING_LEN(v) - 2] = '\0';
1106
+ if (AGOO_ERR_OK != agoo_domain_add_regex(&err, rx, rb_string_value_ptr((VALUE*)&path))) {
1107
+ rb_raise(rb_eArgError, "%s", err.msg);
1108
+ }
1109
+ break;
1110
+ }
1111
+ default:
1112
+ rb_raise(rb_eArgError, "host must be a String or Regex");
1113
+ break;
1114
+ }
1115
+ return Qnil;
1116
+ }
1117
+
1118
+ /* Document-method: rack_early_hints
1119
+ *
1120
+ * call-seq: rack_early_hints(on)
1121
+ *
1122
+ * Turns on or off the inclusion of a early_hints object in the rack call env
1123
+ * Hash. If the argument is nil then the current value is returned.
1124
+ */
1125
+ static VALUE
1126
+ rack_early_hints(VALUE self, VALUE on) {
1127
+ if (Qtrue == on) {
1128
+ agoo_server.rack_early_hints = true;
1129
+ } else if (Qfalse == on) {
1130
+ agoo_server.rack_early_hints = false;
1131
+ } else if (Qnil == on) {
1132
+ on = agoo_server.rack_early_hints ? Qtrue : Qfalse;
1133
+ } else {
1134
+ rb_raise(rb_eArgError, "rack_early_hints can only be set to true or false");
1135
+ }
1136
+ return on;
1137
+ }
1138
+
1075
1139
  /* Document-class: Agoo::Server
1076
1140
  *
1077
1141
  * An HTTP server that support the rack API as well as some other optimized
@@ -1090,6 +1154,9 @@ server_init(VALUE mod) {
1090
1154
  rb_define_module_function(server_mod, "add_mime", add_mime, 2);
1091
1155
  rb_define_module_function(server_mod, "path_group", path_group, 2);
1092
1156
  rb_define_module_function(server_mod, "header_rule", header_rule, 4);
1157
+ rb_define_module_function(server_mod, "domain", domain, 2);
1158
+
1159
+ rb_define_module_function(server_mod, "rack_early_hints", rack_early_hints, 1);
1093
1160
 
1094
1161
  call_id = rb_intern("call");
1095
1162
  each_id = rb_intern("each");
@@ -10,6 +10,7 @@
10
10
  #include <unistd.h>
11
11
 
12
12
  #include "con.h"
13
+ #include "domain.h"
13
14
  #include "dtime.h"
14
15
  #include "http.h"
15
16
  #include "hook.h"
@@ -247,6 +248,7 @@ agoo_server_shutdown(const char *app_name, void (*stop)()) {
247
248
 
248
249
  agoo_pages_cleanup();
249
250
  agoo_http_cleanup();
251
+ agoo_domain_cleanup();
250
252
  }
251
253
  }
252
254
 
@@ -24,6 +24,7 @@ typedef struct _agooServer {
24
24
  int thread_cnt;
25
25
  bool pedantic;
26
26
  bool root_first;
27
+ bool rack_early_hints;
27
28
  pthread_t listen_thread;
28
29
  struct _agooQueue con_queue;
29
30
  agooHook hooks;
@@ -47,6 +47,7 @@ agoo_text_create(const char *str, int len) {
47
47
  agooText t = (agooText)AGOO_MALLOC(sizeof(struct _agooText) - AGOO_TEXT_MIN_SIZE + len + 1);
48
48
 
49
49
  if (NULL != t) {
50
+ t->next = NULL;
50
51
  t->len = len;
51
52
  t->alen = len;
52
53
  t->bin = false;
@@ -63,6 +64,7 @@ agoo_text_dup(agooText t0) {
63
64
 
64
65
  if (NULL != t0) {
65
66
  if (NULL != (t = (agooText)AGOO_MALLOC(sizeof(struct _agooText) - AGOO_TEXT_MIN_SIZE + t0->alen + 1))) {
67
+ t->next = NULL;
66
68
  t->len = t0->len;
67
69
  t->alen = t0->alen;
68
70
  t->bin = false;
@@ -78,6 +80,7 @@ agoo_text_allocate(int len) {
78
80
  agooText t = (agooText)AGOO_MALLOC(sizeof(struct _agooText) - AGOO_TEXT_MIN_SIZE + len + 1);
79
81
 
80
82
  if (NULL != t) {
83
+ t->next = NULL;
81
84
  t->len = 0;
82
85
  t->alen = len;
83
86
  t->bin = false;
@@ -10,11 +10,12 @@
10
10
  #define AGOO_TEXT_MIN_SIZE 8
11
11
 
12
12
  typedef struct _agooText {
13
- long len; // length of valid text
14
- long alen; // size of allocated text
15
- atomic_int ref_cnt;
16
- bool bin;
17
- char text[AGOO_TEXT_MIN_SIZE];
13
+ struct _agooText *next;
14
+ long len; // length of valid text
15
+ long alen; // size of allocated text
16
+ atomic_int ref_cnt;
17
+ bool bin;
18
+ char text[AGOO_TEXT_MIN_SIZE];
18
19
  } *agooText;
19
20
 
20
21
  extern agooText agoo_text_create(const char *str, int len);
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Agoo
3
3
  # Agoo version.
4
- VERSION = '2.8.4'
4
+ VERSION = '2.9.0'
5
5
  end
@@ -0,0 +1,84 @@
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 'agoo'
14
+
15
+ class StaticTest < Minitest::Test
16
+ @@server_started = false
17
+
18
+ def start_server
19
+ Agoo::Log.configure(dir: '',
20
+ console: true,
21
+ classic: true,
22
+ colorize: true,
23
+ states: {
24
+ INFO: false,
25
+ DEBUG: false,
26
+ connect: false,
27
+ request: false,
28
+ response: false,
29
+ eval: true,
30
+ })
31
+
32
+ Agoo::Server.init(6474, 'root', thread_count: 1)
33
+ Agoo::Server.domain('domain1.ohler.com', './domain1')
34
+ Agoo::Server.domain(/^domain([0-9]+).ohler.com$/, './domain$(1)')
35
+ Agoo::Server.start()
36
+
37
+ @@server_started = true
38
+ end
39
+
40
+ def setup
41
+ unless @@server_started
42
+ start_server
43
+ end
44
+ end
45
+
46
+ Minitest.after_run {
47
+ GC.start
48
+ Agoo::shutdown
49
+ }
50
+
51
+ def test_one
52
+ uri = URI('http://localhost:6474/name.txt')
53
+ req = Net::HTTP::Get.new(uri)
54
+ req['Host'] = 'domain1.ohler.com'
55
+ res = Net::HTTP.start(uri.hostname, uri.port) { |h|
56
+ h.request(req)
57
+ }
58
+ content = res.body
59
+ assert_equal('domain one
60
+ ', content)
61
+ end
62
+
63
+ def test_two
64
+ uri = URI('http://localhost:6474/name.txt')
65
+ req = Net::HTTP::Get.new(uri)
66
+ req['Host'] = 'domain2.ohler.com'
67
+ res = Net::HTTP.start(uri.hostname, uri.port) { |h|
68
+ h.request(req)
69
+ }
70
+ content = res.body
71
+ assert_equal('domain two
72
+ ', content)
73
+ end
74
+
75
+ def test_nothing
76
+ uri = URI('http://localhost:6474/name.txt')
77
+ req = Net::HTTP::Get.new(uri)
78
+ res = Net::HTTP.start(uri.hostname, uri.port) { |h|
79
+ h.request(req)
80
+ }
81
+ assert_equal("404", res.code)
82
+ end
83
+
84
+ end