agoo 2.9.0 → 2.10.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: c40533edbeaf9a5593680f15a5c7ad41ef5e1575b9235c10faf0ecb906bd472c
4
- data.tar.gz: 81c3884669b222a050352386973bc40d79cdce7c748204d85825a684d91ecbe3
3
+ metadata.gz: 0e446f82a7209a72954bdf44c5f345fd9c5e13384dc75d6ce8faa7149d52f023
4
+ data.tar.gz: dea80f2d75d1f4affce64efece36dae4f7753e243d085faf8b7f0b2746aea2db
5
5
  SHA512:
6
- metadata.gz: bc1b0f12bdb19c51a947fa73c981eab8e21b640448363c62d7433138f4320f39a317fc9937f5499398ab7414a15122533af23e2bcc25722bfa26844bdd9c9b7c
7
- data.tar.gz: 939c1a7654f453b5d602097b0faed1010291040edddb4a67f99c0b14c50ac2944a45fc87c7e3eb758760a7ba9cf6c02f449aa54cb0daec5fe1864b331e2c1213
6
+ metadata.gz: 6167c0841ed2f05935998cb861632949b9d437e1c590820477add9d0ef067abd0c1ea1db377279c8f82fd4d58efb6491daf198f979e0fa6dc3f989ba4cd217ef
7
+ data.tar.gz: da0c4b54ee8f4db509e5e657bd2a919d0df4a314b7c6663d0e2afd0b4e89730fbaad7693f3888cefe984f8ca00d9e04237c5c51218e5d96e0278cc91eb483f5d
@@ -4,6 +4,16 @@ All changes to the Agoo gem are documented here. Releases follow semantic versio
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [2.10.0] - 2019-08-29
8
+
9
+ GraphQL subscriptions
10
+
11
+ ### Fixed
12
+ - Rack multiple value header transformation changed to emit multiple entries instead of multiple values.
13
+
14
+ ### Added
15
+ - GraphQL Subscriptions using Websockets and SSE are now supported.
16
+
7
17
  ## [2.9.0] - 2019-07-26
8
18
 
9
19
  Early hints and sub-domains
@@ -12,6 +12,7 @@
12
12
  #include "domain.h"
13
13
  #include "dtime.h"
14
14
  #include "hook.h"
15
+ #include "gqlsub.h"
15
16
  #include "http.h"
16
17
  #include "log.h"
17
18
  #include "page.h"
@@ -67,6 +68,7 @@ agoo_con_create(agooErr err, int sock, uint64_t id, agooBind b) {
67
68
  c->timeout = dtime() + CON_TIMEOUT;
68
69
  c->bind = b;
69
70
  c->loop = NULL;
71
+ pthread_mutex_init(&c->res_lock, 0);
70
72
  }
71
73
  return c;
72
74
  }
@@ -89,18 +91,73 @@ agoo_con_destroy(agooCon c) {
89
91
  }
90
92
  if (NULL != c->up) {
91
93
  agoo_upgraded_release_con(c->up);
92
-
93
94
  c->up = NULL;
94
95
  }
96
+ if (NULL != c->gsub) {
97
+ agoo_server_del_gsub(c->gsub);
98
+ gql_sub_destroy(c->gsub);
99
+ c->gsub = NULL;
100
+ }
95
101
  agoo_log_cat(&agoo_con_cat, "Connection %llu closed.", (unsigned long long)c->id);
96
102
 
97
103
  while (NULL != (res = c->res_head)) {
98
104
  c->res_head = res->next;
99
105
  AGOO_FREE(res);
100
106
  }
107
+ pthread_mutex_destroy(&c->res_lock);
101
108
  AGOO_FREE(c);
102
109
  }
103
110
 
111
+ void
112
+ agoo_con_res_append(agooCon c, agooRes res) {
113
+ pthread_mutex_lock(&c->res_lock);
114
+ if (NULL == c->res_tail) {
115
+ c->res_head = res;
116
+ } else {
117
+ c->res_tail->next = res;
118
+ }
119
+ c->res_tail = res;
120
+ pthread_mutex_unlock(&c->res_lock);
121
+ }
122
+
123
+ static void
124
+ agoo_con_res_prepend(agooCon c, agooRes res) {
125
+ pthread_mutex_lock(&c->res_lock);
126
+ res->next = c->res_head;
127
+ c->res_head = res;
128
+ if (NULL == c->res_tail) {
129
+ c->res_tail = res;
130
+ }
131
+ pthread_mutex_unlock(&c->res_lock);
132
+ }
133
+
134
+ static agooRes
135
+ agoo_con_res_pop(agooCon c) {
136
+ agooRes res;
137
+
138
+ pthread_mutex_lock(&c->res_lock);
139
+ if (NULL != (res = c->res_head)) {
140
+ c->res_head = res->next;
141
+ if (res == c->res_tail) {
142
+ c->res_tail = NULL;
143
+ }
144
+ }
145
+ pthread_mutex_unlock(&c->res_lock);
146
+
147
+ return res;
148
+ }
149
+
150
+ static agooRes
151
+ agoo_con_res_peek(agooCon c) {
152
+ agooRes res;
153
+
154
+ pthread_mutex_lock(&c->res_lock);
155
+ res = c->res_head;
156
+ pthread_mutex_unlock(&c->res_lock);
157
+
158
+ return res;
159
+ }
160
+
104
161
  const char*
105
162
  agoo_con_header_value(const char *header, int hlen, const char *key, int *vlen) {
106
163
  // Search for \r then check for \n and then the key followed by a :. Keep
@@ -146,14 +203,9 @@ bad_request(agooCon c, int status, int line) {
146
203
  "HTTP/1.1 %d %s\r\nConnection: Close\r\nContent-Length: 0\r\n\r\n", status, msg);
147
204
  agooText message = agoo_text_create(buf, cnt);
148
205
 
149
- if (NULL == c->res_tail) {
150
- c->res_head = res;
151
- } else {
152
- c->res_tail->next = res;
153
- }
154
- c->res_tail = res;
206
+ agoo_con_res_append(c, res);
155
207
  res->close = true;
156
- agoo_res_message_push(res, message, true);
208
+ agoo_res_message_push(res, message);
157
209
  }
158
210
  return HEAD_ERR;
159
211
  }
@@ -177,19 +229,14 @@ page_response(agooCon c, agooPage p, char *hend) {
177
229
  if (NULL == (res = agoo_res_create(c))) {
178
230
  return true;
179
231
  }
180
- if (NULL == c->res_tail) {
181
- c->res_head = res;
182
- } else {
183
- c->res_tail->next = res;
184
- }
185
- c->res_tail = res;
232
+ agoo_con_res_append(c, res);
186
233
 
187
234
  b = strstr(c->buf, "\r\n");
188
235
  res->close = should_close(b, (int)(hend - b));
189
236
  if (res->close) {
190
237
  c->closing = true;
191
238
  }
192
- agoo_res_message_push(res, p->resp, true);
239
+ agoo_res_message_push(res, p->resp);
193
240
 
194
241
  return false;
195
242
  }
@@ -504,12 +551,7 @@ agoo_con_http_read(agooCon c) {
504
551
  agoo_log_cat(&agoo_error_cat, "memory allocation of response failed on connection %llu.", (unsigned long long)c->id);
505
552
  return bad_request(c, 500, __LINE__);
506
553
  } else {
507
- if (NULL == c->res_tail) {
508
- c->res_head = res;
509
- } else {
510
- c->res_tail->next = res;
511
- }
512
- c->res_tail = res;
554
+ agoo_con_res_append(c, res);
513
555
  res->close = should_close(c->req->header.start, c->req->header.len);
514
556
  if (res->close) {
515
557
  c->closing = true;
@@ -584,6 +626,11 @@ con_ws_read(agooCon c) {
584
626
  switch (op) {
585
627
  case AGOO_WS_OP_TEXT:
586
628
  case AGOO_WS_OP_BIN:
629
+ if (NULL != c->gsub) {
630
+ // GraphQL subscriptions do not accept input on the
631
+ // connection.
632
+ break;
633
+ }
587
634
  if (agoo_ws_create_req(c, mlen)) {
588
635
  return true;
589
636
  }
@@ -648,7 +695,8 @@ con_ws_read(agooCon c) {
648
695
  // return false to remove/close connection
649
696
  bool
650
697
  agoo_con_http_write(agooCon c) {
651
- agooText message = agoo_res_message_peek(c->res_head);
698
+ agooRes res = agoo_con_res_pop(c);
699
+ agooText message = agoo_res_message_peek(res);
652
700
  ssize_t cnt;
653
701
 
654
702
  if (NULL == message) {
@@ -684,22 +732,19 @@ agoo_con_http_write(agooCon c) {
684
732
  }
685
733
  c->wcnt += cnt;
686
734
  if (c->wcnt == message->len) { // finished
687
- agooRes res = c->res_head;
688
735
  agooText next = agoo_res_message_next(res);
689
736
 
690
737
  c->wcnt = 0;
691
738
  if (NULL == next && res->final) {
692
739
  bool done = res->close;
693
740
 
694
- c->res_head = res->next;
695
- if (res == c->res_tail) {
696
- c->res_tail = NULL;
697
- }
698
741
  agoo_res_destroy(res);
699
742
 
700
743
  return !done;
701
744
  }
702
745
  }
746
+ agoo_con_res_prepend(c, res);
747
+
703
748
  return true;
704
749
  }
705
750
 
@@ -708,7 +753,7 @@ static const char pong_msg[] = "\x8a\x00";
708
753
 
709
754
  static bool
710
755
  con_ws_write(agooCon c) {
711
- agooRes res = c->res_head;
756
+ agooRes res = agoo_con_res_pop(c);
712
757
  agooText message = agoo_res_message_peek(res);
713
758
  ssize_t cnt;
714
759
 
@@ -748,18 +793,10 @@ con_ws_write(agooCon c) {
748
793
  }
749
794
  } else {
750
795
  agoo_ws_req_close(c);
751
- c->res_head = res->next;
752
- if (res == c->res_tail) {
753
- c->res_tail = NULL;
754
- }
755
796
  agoo_res_destroy(res);
756
797
 
757
798
  return false;
758
799
  }
759
- c->res_head = res->next;
760
- if (res == c->res_tail) {
761
- c->res_tail = NULL;
762
- }
763
800
  return true;
764
801
  }
765
802
  c->timeout = dtime() + CON_TIMEOUT;
@@ -775,7 +812,7 @@ con_ws_write(agooCon c) {
775
812
  }
776
813
  t = agoo_ws_expand(message);
777
814
  if (t != message) {
778
- agoo_res_message_push(res, t, true);
815
+ agoo_res_message_push(res, t);
779
816
  message = t;
780
817
  }
781
818
  }
@@ -795,28 +832,25 @@ con_ws_write(agooCon c) {
795
832
  }
796
833
  c->wcnt += cnt;
797
834
  if (c->wcnt == message->len) { // finished
798
- agooRes res = c->res_head;
799
835
  agooText next = agoo_res_message_next(res);
800
836
 
801
837
  c->wcnt = 0;
802
838
  if (NULL == next && res->final) {
803
839
  bool done = res->close;
804
840
 
805
- c->res_head = res->next;
806
- if (res == c->res_tail) {
807
- c->res_tail = NULL;
808
- }
809
841
  agoo_res_destroy(res);
810
842
 
811
843
  return !done;
812
844
  }
813
845
  }
846
+ agoo_con_res_prepend(c, res);
847
+
814
848
  return true;
815
849
  }
816
850
 
817
851
  static bool
818
852
  con_sse_write(agooCon c) {
819
- agooRes res = c->res_head;
853
+ agooRes res = agoo_con_res_pop(c);
820
854
  agooText message = agoo_res_message_peek(res);
821
855
  ssize_t cnt;
822
856
 
@@ -834,7 +868,7 @@ con_sse_write(agooCon c) {
834
868
  }
835
869
  t = agoo_sse_expand(message);
836
870
  if (t != message) {
837
- agoo_res_message_push(res, t, true);
871
+ agoo_res_message_push(res, t);
838
872
  message = t;
839
873
  }
840
874
  }
@@ -853,22 +887,19 @@ con_sse_write(agooCon c) {
853
887
  }
854
888
  c->wcnt += cnt;
855
889
  if (c->wcnt == message->len) { // finished
856
- agooText next = agoo_res_message_next(c->res_head);
890
+ agooText next = agoo_res_message_next(res);
857
891
 
858
892
  c->wcnt = 0;
859
893
  if (NULL == next) {
860
- agooRes res = c->res_head;
861
894
  bool done = res->close;
862
895
 
863
- c->res_head = res->next;
864
- if (res == c->res_tail) {
865
- c->res_tail = NULL;
866
- }
867
896
  agoo_res_destroy(res);
868
897
 
869
898
  return !done;
870
899
  }
871
900
  }
901
+ agoo_con_res_prepend(c, res);
902
+
872
903
  return true;
873
904
  }
874
905
 
@@ -883,14 +914,9 @@ publish_pub(agooPub pub, agooConLoop loop) {
883
914
  agooRes res = agoo_res_create(up->con);
884
915
 
885
916
  if (NULL != res) {
886
- if (NULL == up->con->res_tail) {
887
- up->con->res_head = res;
888
- } else {
889
- up->con->res_tail->next = res;
890
- }
891
- up->con->res_tail = res;
917
+ agoo_con_res_append(up->con, res);
892
918
  res->con_kind = AGOO_CON_ANY;
893
- agoo_res_message_push(res, agoo_text_dup(pub->msg), true);
919
+ agoo_res_message_push(res, agoo_text_dup(pub->msg));
894
920
  cnt++;
895
921
  }
896
922
  }
@@ -939,12 +965,7 @@ process_pub_con(agooPub pub, agooConLoop loop) {
939
965
  agooRes res = agoo_res_create(up->con);
940
966
 
941
967
  if (NULL != res) {
942
- if (NULL == up->con->res_tail) {
943
- up->con->res_head = res;
944
- } else {
945
- up->con->res_tail->next = res;
946
- }
947
- up->con->res_tail = res;
968
+ agoo_con_res_append(up->con, res);
948
969
  res->con_kind = up->con->bind->kind;
949
970
  res->close = true;
950
971
  }
@@ -957,14 +978,9 @@ process_pub_con(agooPub pub, agooConLoop loop) {
957
978
  agooRes res = agoo_res_create(up->con);
958
979
 
959
980
  if (NULL != res) {
960
- if (NULL == up->con->res_tail) {
961
- up->con->res_head = res;
962
- } else {
963
- up->con->res_tail->next = res;
964
- }
965
- up->con->res_tail = res;
981
+ agoo_con_res_append(up->con, res);
966
982
  res->con_kind = AGOO_CON_ANY;
967
- agoo_res_message_push(res, pub->msg, true);
983
+ agoo_res_message_push(res, pub->msg);
968
984
  }
969
985
  }
970
986
  break;
@@ -992,8 +1008,9 @@ process_pub_con(agooPub pub, agooConLoop loop) {
992
1008
  short
993
1009
  agoo_con_http_events(agooCon c) {
994
1010
  short events = 0;
1011
+ agooRes res = agoo_con_res_peek(c);
995
1012
 
996
- if (NULL != c->res_head && NULL != c->res_head->message) {
1013
+ if (NULL != res && NULL != res->message) {
997
1014
  events = POLLIN | POLLOUT;
998
1015
  } else if (!c->closing) {
999
1016
  events = POLLIN;
@@ -1004,8 +1021,9 @@ agoo_con_http_events(agooCon c) {
1004
1021
  static short
1005
1022
  con_ws_events(agooCon c) {
1006
1023
  short events = 0;
1024
+ agooRes res = agoo_con_res_peek(c);
1007
1025
 
1008
- if (NULL != c->res_head && (c->res_head->close || c->res_head->ping || NULL != c->res_head->message)) {
1026
+ if (NULL != res && (res->close || res->ping || NULL != res->message)) {
1009
1027
  events = POLLIN | POLLOUT;
1010
1028
  } else if (!c->closing) {
1011
1029
  events = POLLIN;
@@ -1016,8 +1034,9 @@ con_ws_events(agooCon c) {
1016
1034
  static short
1017
1035
  con_sse_events(agooCon c) {
1018
1036
  short events = 0;
1037
+ agooRes res = agoo_con_res_peek(c);
1019
1038
 
1020
- if (NULL != c->res_head && NULL != c->res_head->message) {
1039
+ if (NULL != res && NULL != res->message) {
1021
1040
  events = POLLOUT;
1022
1041
  }
1023
1042
  return events;
@@ -1026,7 +1045,9 @@ con_sse_events(agooCon c) {
1026
1045
  static bool
1027
1046
  remove_dead_res(agooCon c) {
1028
1047
  agooRes res;
1048
+ bool empty;
1029
1049
 
1050
+ pthread_mutex_lock(&c->res_lock);
1030
1051
  while (NULL != (res = c->res_head)) {
1031
1052
  if (NULL == agoo_res_message_peek(c->res_head) && !c->res_head->close && !c->res_head->ping) {
1032
1053
  break;
@@ -1037,7 +1058,10 @@ remove_dead_res(agooCon c) {
1037
1058
  }
1038
1059
  agoo_res_destroy(res);
1039
1060
  }
1040
- return NULL == c->res_head;
1061
+ empty = NULL == c->res_head;
1062
+ pthread_mutex_unlock(&c->res_lock);
1063
+
1064
+ return empty;
1041
1065
  }
1042
1066
 
1043
1067
  static agooReadyIO
@@ -1071,8 +1095,9 @@ con_ready_check(void *ctx, double now) {
1071
1095
  }
1072
1096
  } else if (AGOO_CON_WS == c->bind->kind || AGOO_CON_SSE == c->bind->kind) {
1073
1097
  c->timeout = dtime() + CON_TIMEOUT;
1074
- agoo_ws_ping(c);
1075
-
1098
+ if (AGOO_CON_WS == c->bind->kind) {
1099
+ agoo_ws_ping(c);
1100
+ }
1076
1101
  return true;
1077
1102
  } else {
1078
1103
  c->closing = true;
@@ -1100,9 +1125,10 @@ con_ready_read(agooReady ready, void *ctx) {
1100
1125
  static bool
1101
1126
  con_ready_write(void *ctx) {
1102
1127
  agooCon c = (agooCon)ctx;
1128
+ agooRes res = agoo_con_res_peek(c);
1103
1129
 
1104
- if (NULL != c->res_head) {
1105
- agooConKind kind = c->res_head->con_kind;
1130
+ if (NULL != res) {
1131
+ agooConKind kind = res->con_kind;
1106
1132
 
1107
1133
  if (NULL != c->bind->write) {
1108
1134
  if (c->bind->write(c)) {
@@ -21,20 +21,21 @@ struct _agooReq;
21
21
  struct _agooRes;
22
22
  struct _agooBind;
23
23
  struct _agooQueue;
24
+ struct _gqlSub;
24
25
 
25
26
  typedef struct _agooConLoop {
26
27
  struct _agooConLoop *next;
27
28
  struct _agooQueue pub_queue;
28
29
  pthread_t thread;
29
30
  int id;
30
- // TBD use mutex for head and tail, volatile also
31
+
32
+ // Cache of responses to be reused.
31
33
  struct _agooRes *res_head;
32
34
  struct _agooRes *res_tail;
33
-
34
35
  pthread_mutex_t lock;
35
-
36
+
36
37
  } *agooConLoop;
37
-
38
+
38
39
  typedef struct _agooCon {
39
40
  struct _agooCon *next;
40
41
  int sock;
@@ -54,8 +55,10 @@ typedef struct _agooCon {
54
55
  struct _agooReq *req;
55
56
  struct _agooRes *res_head;
56
57
  struct _agooRes *res_tail;
58
+ pthread_mutex_t res_lock;
57
59
 
58
60
  struct _agooUpgraded *up; // only set for push connections
61
+ struct _gqlSub *gsub; // for graphql subscription
59
62
  agooConLoop loop;
60
63
  } *agooCon;
61
64
 
@@ -66,6 +69,8 @@ extern const char* agoo_con_header_value(const char *header, int hlen, const cha
66
69
  extern agooConLoop agoo_conloop_create(agooErr err, int id);
67
70
  extern void agoo_conloop_destroy(agooConLoop loop);
68
71
 
72
+ extern void agoo_con_res_append(agooCon c, struct _agooRes *res);
73
+
69
74
  extern bool agoo_con_http_read(agooCon c);
70
75
  extern bool agoo_con_http_write(agooCon c);
71
76
  extern short agoo_con_http_events(agooCon c);