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.

@@ -0,0 +1,16 @@
1
+ // Copyright 2019 by Peter Ohler, All Rights Reserved
2
+
3
+ #ifndef AGOO_DOMAIN_H
4
+ #define AGOO_DOMAIN_H
5
+
6
+ #include <stdbool.h>
7
+
8
+ #include "err.h"
9
+
10
+ extern bool agoo_domain_use();
11
+ extern int agoo_domain_add(agooErr err, const char *host, const char *path);
12
+ extern int agoo_domain_add_regex(agooErr err, const char *host, const char *path);
13
+ extern const char* agoo_domain_resolve(const char *host, char *buf, size_t blen);
14
+ extern void agoo_domain_cleanup();
15
+
16
+ #endif // AGOO_DOMAIN_H
@@ -0,0 +1,28 @@
1
+ // Copyright (c) 2019, Peter Ohler, All rights reserved.
2
+
3
+ #include "debug.h"
4
+ #include "early.h"
5
+
6
+ agooEarly
7
+ agoo_early_create(const char *link) {
8
+ agooEarly early = (agooEarly)AGOO_CALLOC(1, sizeof(struct _agooEarly));
9
+
10
+ if (NULL != early) {
11
+ if (NULL == (early->link = AGOO_STRDUP(link))) {
12
+ AGOO_FREE(early);
13
+ return NULL;
14
+ }
15
+ }
16
+ return early;
17
+ }
18
+
19
+ void
20
+ agoo_early_destroy(agooEarly early) {
21
+ if (NULL != early) {
22
+ if (NULL != early->next) {
23
+ agoo_early_destroy(early->next);
24
+ }
25
+ AGOO_FREE(early->link);
26
+ AGOO_FREE(early);
27
+ }
28
+ }
@@ -0,0 +1,23 @@
1
+ // Copyright (c) 2019, Peter Ohler, All rights reserved.
2
+
3
+ #ifndef AGOO_EARLY_H
4
+ #define AGOO_EARLY_H
5
+
6
+ #include <stdbool.h>
7
+
8
+ #include "atomic.h"
9
+ #include "con.h"
10
+ #include "text.h"
11
+
12
+ struct _agooCon;
13
+
14
+ typedef struct _agooEarly {
15
+ struct _agooEarly *next;
16
+ bool sent;
17
+ char *link;
18
+ } *agooEarly;
19
+
20
+ extern agooEarly agoo_early_create(const char *link);
21
+ extern void agoo_early_destroy(agooEarly early);
22
+
23
+ #endif // AGOO_EARLY_H
@@ -0,0 +1,116 @@
1
+ // Copyright (c) 2019, Peter Ohler, All rights reserved.
2
+
3
+ #include <stdio.h>
4
+ #include <stdlib.h>
5
+ #include <ctype.h>
6
+
7
+ #include "debug.h"
8
+ #include "early.h"
9
+ #include "early_hints.h"
10
+ #include "req.h"
11
+ #include "res.h"
12
+
13
+ static VALUE eh_class = Qundef;
14
+
15
+ typedef struct _earlyHints {
16
+ agooReq req;
17
+ agooEarly links;
18
+ } *EarlyHints;
19
+
20
+ static void
21
+ eh_free(void *ptr) {
22
+ if (NULL != ptr) {
23
+ AGOO_FREE(ptr);
24
+ }
25
+ }
26
+
27
+ VALUE
28
+ agoo_early_hints_new(agooReq req) {
29
+ EarlyHints eh = (EarlyHints)AGOO_CALLOC(1, sizeof(struct _earlyHints));
30
+
31
+ if (NULL == eh) {
32
+ rb_raise(rb_eNoMemError, "Failed to allocate memory for an early hints object.");
33
+ }
34
+ eh->req = req;
35
+
36
+ return Data_Wrap_Struct(eh_class, NULL, eh_free, eh);
37
+ }
38
+
39
+ /* Document-method: call
40
+ *
41
+ * call-seq: call(links)
42
+ *
43
+ * Write early hints, response with status 103 using the provided headers in
44
+ * an Array of Array.
45
+ *
46
+ * Example:
47
+ *
48
+ * early_hints.call([
49
+ * ["link", "</style.css>; rel=preload; as=style"],
50
+ * ["link", "</script.js>; rel=preload; as=script"],
51
+ * ])
52
+ */
53
+ static VALUE
54
+ eh_call(VALUE self, VALUE links) {
55
+ EarlyHints eh = (EarlyHints)DATA_PTR(self);
56
+ agooEarly link;
57
+ agooEarly ll = NULL;
58
+ int i, cnt;
59
+ VALUE lv;
60
+ VALUE v;
61
+ const char *key;
62
+ const char *content;
63
+
64
+ if (NULL == eh) {
65
+ rb_raise(rb_eIOError, "early hints has been closed.");
66
+ }
67
+ rb_check_type(links, RUBY_T_ARRAY);
68
+
69
+ // Link: </style.css>; rel=preload; as=style
70
+ cnt = (int)RARRAY_LEN(links);
71
+ for (i = cnt - 1; 0 <= i; i--) {
72
+ lv = rb_ary_entry(links, i);
73
+ switch (rb_type(lv)) {
74
+ case RUBY_T_STRING:
75
+ content = rb_string_value_ptr((VALUE*)&v);
76
+ if (NULL == (link = agoo_early_create(content))) {
77
+ rb_raise(rb_eNoMemError, "out of memory");
78
+ }
79
+ break;
80
+ case RUBY_T_ARRAY:
81
+ if (2 != RARRAY_LEN(lv)) {
82
+ rb_raise(rb_eArgError, "early hints call argument must be an array of arrays that have 2 string members.");
83
+ }
84
+ v = rb_ary_entry(lv, 0);
85
+ key = rb_string_value_ptr((VALUE*)&v);
86
+ if (0 != strcasecmp("link", key)) {
87
+ rb_raise(rb_eArgError, "Only a 'Link' header is allowed in early hints. '%s' is not allowed", key);
88
+ }
89
+ v = rb_ary_entry(lv, 1);
90
+ content = rb_string_value_ptr((VALUE*)&v);
91
+ if (NULL == (link = agoo_early_create(content))) {
92
+ rb_raise(rb_eNoMemError, "out of memory");
93
+ }
94
+ break;
95
+ default:
96
+ rb_raise(rb_eArgError, "early hints call argument must be an array of arrays or an array of strings.");
97
+ }
98
+ link->next = ll;
99
+ ll = link;
100
+ }
101
+ agoo_res_add_early(eh->req->res, ll);
102
+ agoo_early_destroy(ll);
103
+
104
+ return Qnil;
105
+ }
106
+
107
+ /* Document-class: Agoo::EarlyHints
108
+ *
109
+ * Used to provide early hints (HTTP 103) responses to requests.
110
+ */
111
+ void
112
+ early_hints_init(VALUE mod) {
113
+ eh_class = rb_define_class_under(mod, "EarlyHints", rb_cObject);
114
+
115
+ rb_define_method(eh_class, "call", eh_call, 1);
116
+ }
@@ -0,0 +1,13 @@
1
+ // Copyright (c) 2019, Peter Ohler, All rights reserved.
2
+
3
+ #ifndef AGOO_EARLY_HINTS_H
4
+ #define AGOO_EARLY_HINTS_H
5
+
6
+ #include <ruby.h>
7
+
8
+ struct _agooReq;
9
+
10
+ extern void early_hints_init(VALUE mod);
11
+ extern VALUE agoo_early_hints_new(struct _agooReq *req);
12
+
13
+ #endif // AGOO_EARLY_HINTS_H
@@ -54,7 +54,7 @@ err_resp(agooRes res, agooErr err, int status) {
54
54
  code,
55
55
  at.year, at.mon, at.day, at.hour, at.min, at.sec, frac);
56
56
 
57
- agoo_res_set_message(res, agoo_text_create(buf, cnt));
57
+ agoo_res_message_push(res, agoo_text_create(buf, cnt), true);
58
58
  }
59
59
 
60
60
  static void
@@ -84,7 +84,7 @@ value_resp(agooRes res, gqlValue result, int status, int indent) {
84
84
  if (NULL == (text = agoo_text_prepend(text, buf, cnt))) {
85
85
  agoo_log_cat(&agoo_error_cat, "Failed to allocate memory for a response.");
86
86
  }
87
- agoo_res_set_message(res, text);
87
+ agoo_res_message_push(res, text, true);
88
88
  }
89
89
 
90
90
  gqlValue
@@ -1647,7 +1647,7 @@ gql_dump_hook(agooReq req) {
1647
1647
  if (NULL == (text = agoo_text_prepend(text, buf, cnt))) {
1648
1648
  agoo_log_cat(&agoo_error_cat, "Failed to allocate memory for a GraphQL dump.");
1649
1649
  }
1650
- agoo_res_set_message(req->res, text);
1650
+ agoo_res_message_push(req->res, text, true);
1651
1651
  }
1652
1652
 
1653
1653
  gqlField
@@ -48,6 +48,7 @@ typedef struct _mimeSlot {
48
48
 
49
49
  typedef struct _cache {
50
50
  Slot buckets[PAGE_BUCKET_SIZE];
51
+ Slot ruckets[PAGE_BUCKET_SIZE];
51
52
  MimeSlot muckets[MIME_BUCKET_SIZE];
52
53
  char *root;
53
54
  agooGroup groups;
@@ -112,6 +113,7 @@ static const char page_min_fmt[] = "HTTP/1.1 200 OK\r\nContent-Length: %ld\r\n";
112
113
 
113
114
  static struct _cache cache = {
114
115
  .buckets = {0},
116
+ .ruckets = {0},
115
117
  .muckets = {0},
116
118
  .root = NULL,
117
119
  .groups = NULL,
@@ -150,6 +152,11 @@ get_bucketp(uint64_t h) {
150
152
  return cache.buckets + (PAGE_BUCKET_MASK & (h ^ (h << 5) ^ (h >> 7)));
151
153
  }
152
154
 
155
+ static Slot*
156
+ get_rucketp(uint64_t h) {
157
+ return cache.ruckets + (PAGE_BUCKET_MASK & (h ^ (h << 5) ^ (h >> 7)));
158
+ }
159
+
153
160
  static MimeSlot*
154
161
  get_mime_bucketp(uint64_t h) {
155
162
  return cache.muckets + (MIME_BUCKET_MASK & (h ^ (h << 5) ^ (h >> 7)));
@@ -192,6 +199,24 @@ cache_get(const char *key, int klen) {
192
199
  return v;
193
200
  }
194
201
 
202
+ agooPage
203
+ cache_root_get(const char *key, int klen) {
204
+ int len = klen;
205
+ int64_t h = calc_hash(key, &len);
206
+ Slot *bucket = get_rucketp(h);
207
+ Slot s;
208
+ agooPage v = NULL;
209
+
210
+ for (s = *bucket; NULL != s; s = s->next) {
211
+ if (h == (int64_t)s->hash && len == (int)s->klen &&
212
+ ((0 <= len && len <= MAX_KEY_UNIQ) || 0 == strncmp(s->key, key, klen))) {
213
+ v = s->value;
214
+ break;
215
+ }
216
+ }
217
+ return v;
218
+ }
219
+
195
220
  int
196
221
  mime_set(agooErr err, const char *key, const char *value) {
197
222
  int klen = (int)strlen(key);
@@ -199,7 +224,7 @@ mime_set(agooErr err, const char *key, const char *value) {
199
224
  int64_t h = calc_hash(key, &len);
200
225
  MimeSlot *bucket = get_mime_bucketp(h);
201
226
  MimeSlot s;
202
-
227
+
203
228
  if (MAX_MIME_KEY_LEN < len) {
204
229
  return agoo_err_set(err, AGOO_ERR_ARG, "%s is too long for a file extension. Maximum is %d", key, MAX_MIME_KEY_LEN);
205
230
  }
@@ -235,19 +260,16 @@ mime_set(agooErr err, const char *key, const char *value) {
235
260
  }
236
261
 
237
262
  static agooPage
238
- cache_set(const char *key, int klen, agooPage value) {
239
- int len = klen;
240
- int64_t h = calc_hash(key, &len);
241
- Slot *bucket = get_bucketp(h);
263
+ bucket_set(Slot *bucket, int64_t h, const char *key, int klen, agooPage value) {
242
264
  Slot s;
243
265
  agooPage old = NULL;
244
-
245
- if (MAX_KEY_LEN < len) {
266
+
267
+ if (MAX_KEY_LEN < klen) {
246
268
  return value;
247
269
  }
248
270
  for (s = *bucket; NULL != s; s = s->next) {
249
- if (h == (int64_t)s->hash && len == s->klen &&
250
- ((0 <= len && len <= MAX_KEY_UNIQ) || 0 == strncmp(s->key, key, len))) {
271
+ if (h == (int64_t)s->hash && klen == s->klen &&
272
+ ((0 <= klen && klen <= MAX_KEY_UNIQ) || 0 == strncmp(s->key, key, klen))) {
251
273
 
252
274
  old = s->value;
253
275
  // replace
@@ -260,12 +282,12 @@ cache_set(const char *key, int klen, agooPage value) {
260
282
  return value;
261
283
  }
262
284
  s->hash = h;
263
- s->klen = len;
285
+ s->klen = klen;
264
286
  if (NULL == key) {
265
287
  *s->key = '\0';
266
288
  } else {
267
- strncpy(s->key, key, len);
268
- s->key[len] = '\0';
289
+ strncpy(s->key, key, klen);
290
+ s->key[klen] = '\0';
269
291
  }
270
292
  s->value = value;
271
293
  s->next = *bucket;
@@ -274,10 +296,28 @@ cache_set(const char *key, int klen, agooPage value) {
274
296
  return old;
275
297
  }
276
298
 
299
+ static agooPage
300
+ cache_set(const char *key, int klen, agooPage value) {
301
+ int len = klen;
302
+ int64_t h = calc_hash(key, &len);
303
+ Slot *bucket = get_bucketp(h);
304
+
305
+ return bucket_set(bucket, h, key, len, value);
306
+ }
307
+
308
+ static agooPage
309
+ cache_root_set(const char *key, int klen, agooPage value) {
310
+ int len = klen;
311
+ int64_t h = calc_hash(key, &len);
312
+ Slot *bucket = get_rucketp(h);
313
+
314
+ return bucket_set(bucket, h, key, len, value);
315
+ }
316
+
277
317
  int
278
318
  agoo_pages_init(agooErr err) {
279
319
  Mime m;
280
-
320
+
281
321
  memset(&cache, 0, sizeof(struct _cache));
282
322
  if (NULL == (cache.root = AGOO_STRDUP("."))) {
283
323
  return agoo_err_set(err, AGOO_ERR_ARG, "out of memory allocating root path");
@@ -330,6 +370,14 @@ agoo_pages_cleanup() {
330
370
  }
331
371
  *sp = NULL;
332
372
  }
373
+ for (sp = cache.ruckets, i = PAGE_BUCKET_SIZE; 0 < i; i--, sp++) {
374
+ for (s = *sp; NULL != s; s = n) {
375
+ n = s->next;
376
+ agoo_page_destroy(s->value);
377
+ AGOO_FREE(s);
378
+ }
379
+ *sp = NULL;
380
+ }
333
381
  for (i = MIME_BUCKET_SIZE; 0 < i; i--, mp++) {
334
382
  for (sm = *mp; NULL != sm; sm = m) {
335
383
  m = sm->next;
@@ -461,10 +509,10 @@ agoo_page_immutable(agooErr err, const char *path, const char *content, int clen
461
509
  int plen = 0;
462
510
  long hlen = 0;
463
511
  HeadRule hr;
464
-
512
+
465
513
  if (NULL == p) {
466
514
  AGOO_ERR_MEM(err, "Page");
467
- return NULL;
515
+ return NULL;
468
516
  }
469
517
  if (NULL == path) {
470
518
  p->path = NULL;
@@ -476,7 +524,7 @@ agoo_page_immutable(agooErr err, const char *path, const char *content, int clen
476
524
  plen = (int)strlen(path);
477
525
  if (NULL != cache.root) {
478
526
  int rlen = strlen(cache.root);
479
-
527
+
480
528
  if (0 == strncmp(cache.root, p->path, rlen) && '/' == p->path[rlen]) {
481
529
  rel_path = p->path + rlen + 1;
482
530
  } else {
@@ -509,7 +557,7 @@ agoo_page_immutable(agooErr err, const char *path, const char *content, int clen
509
557
  }
510
558
  if (0 < hlen) {
511
559
  bool has_ct;
512
-
560
+
513
561
  cnt = sprintf(p->resp->text, page_min_fmt, (long)clen); // HTTP/1.1 200 OK\r\nContent-Length: %ld\r\n
514
562
  for (hr = cache.head_rules; NULL != hr; hr = hr->next) {
515
563
  if (head_rule_match(hr, rel_path, mime)) {
@@ -610,7 +658,7 @@ update_contents(agooPage p) {
610
658
 
611
659
  if (NULL != cache.root) {
612
660
  int rlen = strlen(cache.root);
613
-
661
+
614
662
  if (0 == strncmp(cache.root, path, rlen) && '/' == path[rlen]) {
615
663
  rel_path = path + rlen + 1;
616
664
  }
@@ -628,7 +676,7 @@ update_contents(agooPage p) {
628
676
  }
629
677
  if (0 < hlen) {
630
678
  bool has_ct = false;
631
-
679
+
632
680
  cnt = sprintf(t->text, page_min_fmt, size); // HTTP/1.1 200 OK\r\nContent-Length: %ld\r\n
633
681
  for (hr = cache.head_rules; NULL != hr; hr = hr->next) {
634
682
  if (head_rule_match(hr, rel_path, mime)) {
@@ -722,48 +770,87 @@ page_check(agooErr err, agooPage page) {
722
770
  }
723
771
 
724
772
  agooPage
725
- agoo_page_get(agooErr err, const char *path, int plen) {
726
- agooPage page;
773
+ agoo_page_get(agooErr err, const char *path, int plen, const char *root) {
774
+ agooPage page = NULL;
727
775
 
728
776
  if (NULL != strstr(path, "../")) {
729
777
  return NULL;
730
778
  }
731
- if (NULL == (page = cache_get(path, plen))) {
732
- if (NULL != cache.root) {
733
- agooPage old;
734
- char full_path[2048];
735
- char *s = stpcpy(full_path, cache.root);
779
+ if (NULL != root) {
780
+ char full_path[2048];
781
+ char *s = stpcpy(full_path, root);
736
782
 
737
- if ('/' != *cache.root && '/' != *path) {
738
- *s++ = '/';
739
- }
740
- if ((int)sizeof(full_path) <= plen + (s - full_path)) {
741
- AGOO_ERR_MEM(err, "Page path");
742
- return NULL;
743
- }
744
- strncpy(s, path, plen);
745
- s[plen] = '\0';
746
- if (NULL == (page = agoo_page_create(full_path))) {
747
- AGOO_ERR_MEM(err, "Page");
748
- return NULL;
749
- }
750
- if (!update_contents(page) || NULL == page->resp) {
751
- agoo_page_destroy(page);
752
- agoo_err_set(err, AGOO_ERR_NOT_FOUND, "not found.");
753
- return NULL;
754
- }
755
- if (NULL != (old = cache_set(path, plen, page))) {
756
- agoo_page_destroy(old);
783
+ if (NULL != strstr(path, "../")) {
784
+ return NULL;
785
+ }
786
+ if ((int)sizeof(full_path) <= plen + (s - full_path)) {
787
+ AGOO_ERR_MEM(err, "Page path");
788
+ return NULL;
789
+ }
790
+ if ('/' != *root && '/' != *(s - 1) && '/' != *path) {
791
+ *s++ = '/';
792
+ }
793
+ strncpy(s, path, plen);
794
+ s[plen] = '\0';
795
+
796
+ if (NULL == (page = cache_root_get(full_path, plen))) {
797
+ if (NULL != cache.root) {
798
+ agooPage old;
799
+
800
+ if (NULL == (page = agoo_page_create(full_path))) {
801
+ AGOO_ERR_MEM(err, "Page");
802
+ return NULL;
803
+ }
804
+ if (!update_contents(page) || NULL == page->resp) {
805
+ agoo_page_destroy(page);
806
+ agoo_err_set(err, AGOO_ERR_NOT_FOUND, "not found.");
807
+ return NULL;
808
+ }
809
+ if (NULL != (old = cache_root_set(full_path, plen, page))) {
810
+ agoo_page_destroy(old);
811
+ }
757
812
  }
813
+ } else {
814
+ page = page_check(err, page);
758
815
  }
759
816
  } else {
760
- page = page_check(err, page);
817
+ if (NULL == (page = cache_get(path, plen))) {
818
+ if (NULL != cache.root) {
819
+ agooPage old;
820
+ char full_path[2048];
821
+ char *s = stpcpy(full_path, cache.root);
822
+
823
+ if ('/' != *cache.root && '/' != *path) {
824
+ *s++ = '/';
825
+ }
826
+ if ((int)sizeof(full_path) <= plen + (s - full_path)) {
827
+ AGOO_ERR_MEM(err, "Page path");
828
+ return NULL;
829
+ }
830
+ strncpy(s, path, plen);
831
+ s[plen] = '\0';
832
+ if (NULL == (page = agoo_page_create(full_path))) {
833
+ AGOO_ERR_MEM(err, "Page");
834
+ return NULL;
835
+ }
836
+ if (!update_contents(page) || NULL == page->resp) {
837
+ agoo_page_destroy(page);
838
+ agoo_err_set(err, AGOO_ERR_NOT_FOUND, "not found.");
839
+ return NULL;
840
+ }
841
+ if (NULL != (old = cache_set(path, plen, page))) {
842
+ agoo_page_destroy(old);
843
+ }
844
+ }
845
+ } else {
846
+ page = page_check(err, page);
847
+ }
761
848
  }
762
849
  return page;
763
850
  }
764
851
 
765
852
  agooPage
766
- group_get(agooErr err, const char *path, int plen) {
853
+ agoo_group_get(agooErr err, const char *path, int plen) {
767
854
  agooPage page = NULL;
768
855
  agooGroup g = NULL;
769
856
  char full_path[2048];
@@ -833,7 +920,7 @@ group_get(agooErr err, const char *path, int plen) {
833
920
  }
834
921
 
835
922
  agooGroup
836
- group_create(const char *path) {
923
+ agoo_group_create(const char *path) {
837
924
  agooGroup g = (agooGroup)AGOO_MALLOC(sizeof(struct _agooGroup));
838
925
 
839
926
  if (NULL != g) {
@@ -850,7 +937,7 @@ group_create(const char *path) {
850
937
  }
851
938
 
852
939
  agooDir
853
- group_add(agooErr err, agooGroup g, const char *dir) {
940
+ agoo_group_add(agooErr err, agooGroup g, const char *dir) {
854
941
  agooDir d = (agooDir)AGOO_MALLOC(sizeof(struct _agooDir));
855
942
 
856
943
  if (NULL != d) {
@@ -877,7 +964,7 @@ agoo_header_rule(agooErr err, const char *path, const char *mime, const char *ke
877
964
  }
878
965
  if (NULL == (hr->path = AGOO_STRDUP(path)) ||
879
966
  NULL == (hr->key = AGOO_STRDUP(key)) ||
880
- NULL == (hr->value = AGOO_STRDUP(value))) {
967
+ NULL == (hr->value = AGOO_STRDUP(value))) {
881
968
  goto ERROR;
882
969
  }
883
970
  if ('*' == *mime && '\0' == mime[1]) {
@@ -888,7 +975,7 @@ agoo_header_rule(agooErr err, const char *path, const char *mime, const char *ke
888
975
  hr->len = strlen(hr->key) + strlen(hr->value) + 4;
889
976
  hr->next = cache.head_rules;
890
977
  cache.head_rules = hr;
891
-
978
+
892
979
  return AGOO_ERR_OK;
893
980
 
894
981
  ERROR: