agoo 2.0.5 → 2.1.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: 69a2baa46cde1aaa305d41b6396ce5e78ffc207d0225b910faf280e2beb18749
4
- data.tar.gz: b8ff1ffd30767c254ba0849d3e90593a0ccd687fe15db9b211c21fe4b8d5f98b
3
+ metadata.gz: 729ca7dab10faf0c9196e1297f814dc06471f5de2948a04e4a3d074067f042d6
4
+ data.tar.gz: 65f231b27c3eb5db90db659e920090fc075ffece27f65753d9769c32157f540d
5
5
  SHA512:
6
- metadata.gz: 6d120ee1e1008a9d0fb0382b4f56af522b8eb3e553258632089596d5e506eab7d7d03f221b7275a9f1268e132d4281e0bf0210dd35e69da105d5f12ab1cf7c75
7
- data.tar.gz: 2f90130a772f81b444c27ff5d3d4e148de5d8bf971a01d30b93edb9405ca6dc8a0fd57ef7ad4d4cccd3b7501049985dfd4a9945350556e7be1b66d9eb4592cb5
6
+ metadata.gz: 8135b9c196acc16c80a81aa532d55f8452c1008f4ab22cd285a3f327655cf66f2f4b4f6177c9e7d3781f05964dde9ffb2d238f0808acd5c0150f95bd58eeec24
7
+ data.tar.gz: 7943dc936c2cb113054f04f2780185bbe80d9c62ab94e7a601874219ece58ee2c2ba5f3e8f71e4b23bb8ba24f3a31526bd944ea3351fd62a452c45bf06c0f187
@@ -1,5 +1,13 @@
1
1
  # CHANGELOG
2
2
 
3
+ ### 2.1.0 - 2018-05-10
4
+
5
+ - This is a minor release even though the API has changed. The changed API is the one for Rack based WebSocket and SSE connection upgrades. The PR for the spec addition is currently stalled but some suggestions for a stateless API are implemented in this release. The proposed Rack SPEC is [here](misc/SPEC). The PR is [here](https://github.com/rack/rack/pull/1272)
6
+
7
+ - Publish and subscribe to WebSocket and SSE connections now available. An example in the push subdirectory demonstrates how it works.
8
+
9
+ - Slight performance boost.
10
+
3
11
  ### 2.0.5 - 2018-05-06
4
12
 
5
13
  - Changed to putting all the path on the `REQUEST_PATH` variable instead of the `SCRIPT_NAME` to accomodate Rails which only uses the `REQUEST_PATH`.
data/README.md CHANGED
@@ -22,7 +22,10 @@ end
22
22
  handler = MyHandler.new
23
23
  Agoo::Server.handle(:GET, "/hello", handler)
24
24
  Agoo::Server.start()
25
- # To run this example type the following then go to a browser and enter a URL of localhost:6464/hello.
25
+
26
+ # To run this example type the following then go to a browser and enter a URL
27
+ # of localhost:6464/hello.
28
+ #
26
29
  # ruby hello.rb
27
30
  ```
28
31
 
@@ -45,7 +48,7 @@ href="https://github.com/ohler55/perfer">Perfer</a> to hit the Agoo
45
48
  limits. Ruby benchmarks driver could not push Agoo hard enough.
46
49
 
47
50
  Agoo supports the [Ruby rack API](https://rack.github.io) which allows for the
48
- use of rack compatible gems. Agoo also supports WebSockets and SSE.
51
+ use of rack compatible gems such as Hanami and Rails. Agoo also supports WebSockets and SSE.
49
52
 
50
53
  ## Releases
51
54
 
@@ -8,11 +8,11 @@
8
8
  #include "debug.h"
9
9
  #include "error_stream.h"
10
10
  #include "log.h"
11
+ #include "pub.h"
11
12
  #include "rack_logger.h"
12
13
  #include "request.h"
13
14
  #include "response.h"
14
15
  #include "server.h"
15
- #include "subscription.h"
16
16
  #include "upgraded.h"
17
17
 
18
18
  void
@@ -34,6 +34,38 @@ ragoo_shutdown(VALUE self) {
34
34
  return Qnil;
35
35
  }
36
36
 
37
+ /* Document-method: publish
38
+ *
39
+ * call-seq: publish(subject, message)
40
+ *
41
+ * Publish a message on the given subject.
42
+ */
43
+ VALUE
44
+ ragoo_publish(VALUE self, VALUE subject, VALUE message) {
45
+ rb_check_type(subject, T_STRING);
46
+ rb_check_type(message, T_STRING);
47
+
48
+ queue_push(&the_server.pub_queue, pub_publish(StringValuePtr(subject), (int)RSTRING_LEN(subject),
49
+ StringValuePtr(message), (int)RSTRING_LEN(message)));
50
+
51
+ return Qnil;
52
+ }
53
+
54
+ /* Document-method: unsubscribe
55
+ *
56
+ * call-seq: unsubscribe(subject)
57
+ *
58
+ * Unsubscribes on client listeners on the specified subject.
59
+ */
60
+ static VALUE
61
+ ragoo_unsubscribe(VALUE self, VALUE subject) {
62
+ rb_check_type(subject, T_STRING);
63
+
64
+ queue_push(&the_server.pub_queue, pub_unsubscribe(NULL, StringValuePtr(subject), (int)RSTRING_LEN(subject)));
65
+
66
+ return Qnil;
67
+ }
68
+
37
69
  static void
38
70
  sig_handler(int sig) {
39
71
  agoo_shutdown();
@@ -60,10 +92,11 @@ Init_agoo() {
60
92
  request_init(mod);
61
93
  response_init(mod);
62
94
  server_init(mod);
63
- subscription_init(mod);
64
95
  upgraded_init(mod);
65
96
 
66
97
  rb_define_module_function(mod, "shutdown", ragoo_shutdown, 0);
98
+ rb_define_module_function(mod, "publish", ragoo_publish, 2);
99
+ rb_define_module_function(mod, "unsubscribe", ragoo_unsubscribe, 1);
67
100
 
68
101
  signal(SIGINT, sig_handler);
69
102
  signal(SIGTERM, sig_handler);
@@ -12,19 +12,19 @@
12
12
  #include "res.h"
13
13
  #include "server.h"
14
14
  #include "sse.h"
15
+ #include "subject.h"
16
+ #include "upgraded.h"
15
17
  #include "websocket.h"
16
18
 
17
- #define MAX_SOCK 4096
18
- #define CON_TIMEOUT 5.0
19
+ #define CON_TIMEOUT 5.0
20
+ #define INITIAL_POLL_SIZE 1024
21
+
22
+ extern void agoo_shutdown();
19
23
 
20
24
  Con
21
25
  con_create(Err err, int sock, uint64_t id) {
22
26
  Con c;
23
27
 
24
- if (MAX_SOCK <= sock) {
25
- err_set(err, ERR_TOO_MANY, "Too many connections.");
26
- return NULL;
27
- }
28
28
  if (NULL == (c = (Con)malloc(sizeof(struct _Con)))) {
29
29
  err_set(err, ERR_MEMORY, "Failed to allocate memory for a connection.");
30
30
  } else {
@@ -50,9 +50,9 @@ con_destroy(Con c) {
50
50
  if (NULL != c->req) {
51
51
  request_destroy(c->req);
52
52
  }
53
- if (NULL != c->slot) {
54
- cc_remove_con(&the_server.con_cache, c->id);
55
- c->slot = NULL;
53
+ if (NULL != c->up) {
54
+ upgraded_release_con(c->up);
55
+ c->up = NULL;
56
56
  }
57
57
  DEBUG_FREE(mem_con, c)
58
58
  free(c);
@@ -94,7 +94,7 @@ bad_request(Con c, int status, int line) {
94
94
  Res res;
95
95
  const char *msg = http_code_message(status);
96
96
 
97
- if (NULL == (res = res_create())) {
97
+ if (NULL == (res = res_create(c))) {
98
98
  log_cat(&error_cat, "memory allocation of response failed on connection %llu @ %d.", c->id, line);
99
99
  } else {
100
100
  char buf[256];
@@ -254,7 +254,7 @@ con_header_read(Con c) {
254
254
  }
255
255
  return bad_request(c, 404, __LINE__);
256
256
  }
257
- if (NULL == (res = res_create())) {
257
+ if (NULL == (res = res_create(c))) {
258
258
  return bad_request(c, 500, __LINE__);
259
259
  }
260
260
  if (NULL == c->res_tail) {
@@ -266,6 +266,9 @@ con_header_read(Con c) {
266
266
 
267
267
  b = strstr(c->buf, "\r\n");
268
268
  res->close = should_close(b, (int)(hend - b));
269
+ if (res->close) {
270
+ c->closing = true;
271
+ }
269
272
  res_set_message(res, p->resp);
270
273
 
271
274
  return -mlen;
@@ -287,7 +290,7 @@ HOOKED:
287
290
  c->req->msg[mlen] = '\0';
288
291
  c->req->method = method;
289
292
  c->req->upgrade = UP_NONE;
290
- c->req->cid = c->id;
293
+ c->req->up = NULL;
291
294
  c->req->path.start = c->req->msg + (path - c->buf);
292
295
  c->req->path.len = (int)(pend - path);
293
296
  c->req->query.start = c->req->msg + (query - c->buf);
@@ -339,7 +342,10 @@ check_upgrade(Con c) {
339
342
  static bool
340
343
  con_http_read(Con c) {
341
344
  ssize_t cnt;
342
-
345
+
346
+ if (c->dead || 0 == c->sock || c->closing) {
347
+ return true;
348
+ }
343
349
  if (NULL != c->req) {
344
350
  cnt = recv(c->sock, c->req->msg + c->bcnt, c->req->mlen - c->bcnt, 0);
345
351
  } else {
@@ -390,7 +396,7 @@ con_http_read(Con c) {
390
396
  if (debug_cat.on && NULL != c->req && NULL != c->req->body.start) {
391
397
  log_cat(&debug_cat, "request on %llu: %s", c->id, c->req->body.start);
392
398
  }
393
- if (NULL == (res = res_create())) {
399
+ if (NULL == (res = res_create(c))) {
394
400
  c->req = NULL;
395
401
  log_cat(&error_cat, "memory allocation of response failed on connection %llu.", c->id);
396
402
  return bad_request(c, 500, __LINE__);
@@ -402,6 +408,9 @@ con_http_read(Con c) {
402
408
  }
403
409
  c->res_tail = res;
404
410
  res->close = should_close(c->req->header.start, c->req->header.len);
411
+ if (res->close) {
412
+ c->closing = true;
413
+ }
405
414
  }
406
415
  c->req->res = res;
407
416
  mlen = c->req->mlen;
@@ -493,6 +502,7 @@ con_ws_read(Con c) {
493
502
  }
494
503
  }
495
504
  }
505
+ upgraded_ref(c->up);
496
506
  queue_push(&the_server.eval_queue, (void*)c->req);
497
507
  if (mlen < (long)c->bcnt) {
498
508
  memmove(c->buf, c->buf + mlen, c->bcnt - mlen);
@@ -657,14 +667,15 @@ con_ws_write(Con c) {
657
667
  }
658
668
  c->wcnt = 0;
659
669
  res_destroy(res);
660
- if (1 == (pending = atomic_fetch_sub(&c->slot->pending, 1))) {
661
- if (NULL != c->slot && Qnil != c->slot->handler && c->slot->on_empty) {
670
+ if (1 == (pending = atomic_fetch_sub(&c->up->pending, 1))) {
671
+ if (NULL != c->up && Qnil != c->up->handler && c->up->on_empty) {
662
672
  Req req = request_create(0);
663
673
 
664
- req->cid = c->id;
674
+ req->up = c->up;
665
675
  req->method = ON_EMPTY;
666
676
  req->handler_type = PUSH_HOOK;
667
- req->handler = c->slot->handler;
677
+ req->handler = c->up->handler;
678
+ upgraded_ref(c->up);
668
679
  queue_push(&the_server.eval_queue, (void*)req);
669
680
  }
670
681
  }
@@ -718,14 +729,15 @@ con_sse_write(Con c) {
718
729
  }
719
730
  c->wcnt = 0;
720
731
  res_destroy(res);
721
- if (1 == (pending = atomic_fetch_sub(&c->slot->pending, 1))) {
722
- if (NULL != c->slot && Qnil != c->slot->handler && c->slot->on_empty) {
732
+ if (1 == (pending = atomic_fetch_sub(&c->up->pending, 1))) {
733
+ if (NULL != c->up && Qnil != c->up->handler && c->up->on_empty) {
723
734
  Req req = request_create(0);
724
735
 
725
- req->cid = c->id;
736
+ req->up = c->up;
726
737
  req->method = ON_EMPTY;
727
738
  req->handler_type = PUSH_HOOK;
728
- req->handler = c->slot->handler;
739
+ req->handler = c->up->handler;
740
+ upgraded_ref(c->up);
729
741
  queue_push(&the_server.eval_queue, (void*)req);
730
742
  }
731
743
  }
@@ -754,66 +766,103 @@ con_write(Con c) {
754
766
  }
755
767
  if (kind != c->kind) {
756
768
  c->kind = kind;
769
+ /*
757
770
  if (CON_HTTP != kind && !remove) {
758
- c->slot = cc_set_con(&the_server.con_cache, c);
771
+ // TBD add to up_list now or later?
772
+ //c->slot = cc_set_con(&the_server.con_cache, c);
759
773
  }
774
+ */
760
775
  }
761
776
  return remove;
762
777
  }
763
778
 
779
+ static void
780
+ publish_pub(Pub pub) {
781
+ Upgraded up;
782
+ const char *sub = pub->subject->pattern;
783
+
784
+ for (up = the_server.up_list; NULL != up; up = up->next) {
785
+ if (NULL != up->con && upgraded_match(up, sub)) {
786
+ Res res = res_create(up->con);
787
+
788
+ if (NULL != res) {
789
+ if (NULL == up->con->res_tail) {
790
+ up->con->res_head = res;
791
+ } else {
792
+ up->con->res_tail->next = res;
793
+ }
794
+ up->con->res_tail = res;
795
+ res->con_kind = up->con->kind;
796
+ res_set_message(res, text_dup(pub->msg));
797
+ }
798
+ }
799
+ }
800
+ }
801
+
802
+ static void
803
+ unsubscribe_pub(Pub pub) {
804
+ if (NULL == pub->up) {
805
+ Upgraded up;
806
+
807
+ for (up = the_server.up_list; NULL != up; up = up->next) {
808
+ upgraded_del_subject(up, pub->subject);
809
+ }
810
+ } else {
811
+ upgraded_del_subject(pub->up, pub->subject);
812
+ }
813
+ }
814
+
764
815
  static void
765
816
  process_pub_con(Pub pub) {
766
- CSlot slot;
817
+ Upgraded up = pub->up;
767
818
 
768
819
  switch (pub->kind) {
769
820
  case PUB_CLOSE:
770
- if (NULL == (slot = cc_get_slot(&the_server.con_cache, pub->cid)) || NULL == slot->con) {
771
- log_cat(&warn_cat, "Socket %llu already closed.", pub->cid);
772
- pub_destroy(pub);
773
- return;
774
- } else {
775
- Res res = res_create();;
821
+ // An close after already closed is used to decrement the reference
822
+ // count on the upgraded so it can be destroyed in the con loop
823
+ // threads.
824
+ if (NULL != up->con) {
825
+ Res res = res_create(up->con);
776
826
 
777
827
  if (NULL != res) {
778
- if (NULL == slot->con->res_tail) {
779
- slot->con->res_head = res;
828
+ if (NULL == up->con->res_tail) {
829
+ up->con->res_head = res;
780
830
  } else {
781
- slot->con->res_tail->next = res;
831
+ up->con->res_tail->next = res;
782
832
  }
783
- slot->con->res_tail = res;
784
- res->con_kind = slot->con->kind;
833
+ up->con->res_tail = res;
834
+ res->con_kind = up->con->kind;
785
835
  res->close = true;
786
836
  }
787
837
  }
788
838
  break;
789
839
  case PUB_WRITE: {
790
- if (NULL == (slot = cc_get_slot(&the_server.con_cache, pub->cid)) || NULL == slot->con) {
791
- log_cat(&warn_cat, "Socket %llu already closed. WebSocket write failed.", pub->cid);
792
- pub_destroy(pub);
793
- return;
840
+ if (NULL == up->con) {
841
+ log_cat(&warn_cat, "Connection already closed. WebSocket write failed.");
794
842
  } else {
795
- Res res = res_create();
843
+ Res res = res_create(up->con);
796
844
 
797
845
  if (NULL != res) {
798
- if (NULL == slot->con->res_tail) {
799
- slot->con->res_head = res;
846
+ if (NULL == up->con->res_tail) {
847
+ up->con->res_head = res;
800
848
  } else {
801
- slot->con->res_tail->next = res;
849
+ up->con->res_tail->next = res;
802
850
  }
803
- slot->con->res_tail = res;
804
- res->con_kind = slot->con->kind;
851
+ up->con->res_tail = res;
852
+ res->con_kind = up->con->kind;
805
853
  res_set_message(res, pub->msg);
806
854
  }
807
855
  }
808
856
  break;
809
857
  case PUB_SUB:
810
- // TBD handle a subscribe when implementing pub/sub
858
+ upgraded_add_subject(pub->up, pub->subject);
859
+ pub->subject = NULL;
811
860
  break;
812
861
  case PUB_UN:
813
- // TBD handle a subscribe when implementing pub/sub
862
+ unsubscribe_pub(pub);
814
863
  break;
815
864
  case PUB_MSG:
816
- // TBD handle a subscribe when implementing pub/sub
865
+ publish_pub(pub);
817
866
  break;
818
867
  }
819
868
  default:
@@ -823,12 +872,7 @@ process_pub_con(Pub pub) {
823
872
  }
824
873
 
825
874
  static struct pollfd*
826
- poll_setup(Con *ca, int ccnt, struct pollfd *pp) {
827
- Con *cp;
828
- Con *end = ca + MAX_SOCK;
829
- Con c;
830
- int i;
831
-
875
+ poll_setup(Con c, struct pollfd *pp) {
832
876
  // The first two pollfd are for the con_queue and the pub_queue in that
833
877
  // order.
834
878
  pp->fd = queue_listen(&the_server.con_queue);
@@ -839,11 +883,10 @@ poll_setup(Con *ca, int ccnt, struct pollfd *pp) {
839
883
  pp->events = POLLIN;
840
884
  pp->revents = 0;
841
885
  pp++;
842
- for (i = ccnt, cp = ca; 0 < i && cp < end; cp++) {
843
- if (NULL == *cp) {
886
+ for (; NULL != c; c = c->next) {
887
+ if (c->dead) {
844
888
  continue;
845
889
  }
846
- c = *cp;
847
890
  c->pp = pp;
848
891
  pp->fd = c->sock;
849
892
  pp->events = 0;
@@ -851,14 +894,14 @@ poll_setup(Con *ca, int ccnt, struct pollfd *pp) {
851
894
  case CON_HTTP:
852
895
  if (NULL != c->res_head && NULL != res_message(c->res_head)) {
853
896
  pp->events = POLLIN | POLLOUT;
854
- } else {
897
+ } else if (!c->closing) {
855
898
  pp->events = POLLIN;
856
899
  }
857
900
  break;
858
901
  case CON_WS:
859
902
  if (NULL != c->res_head && (c->res_head->close || c->res_head->ping || NULL != res_message(c->res_head))) {
860
903
  pp->events = POLLIN | POLLOUT;
861
- } else {
904
+ } else if (!c->closing) {
862
905
  pp->events = POLLIN;
863
906
  }
864
907
  break;
@@ -872,39 +915,67 @@ poll_setup(Con *ca, int ccnt, struct pollfd *pp) {
872
915
  break;
873
916
  }
874
917
  pp->revents = 0;
875
- i--;
876
918
  pp++;
877
919
  }
878
920
  return pp;
879
921
  }
880
922
 
923
+ static bool
924
+ remove_dead_res(Con c) {
925
+ Res res;
926
+
927
+ while (NULL != (res = c->res_head)) {
928
+ if (NULL == res_message(c->res_head) && !c->res_head->close && !c->res_head->ping) {
929
+ break;
930
+ }
931
+ c->res_head = res->next;
932
+ if (res == c->res_tail) {
933
+ c->res_tail = NULL;
934
+ }
935
+ res_destroy(res);
936
+ }
937
+ return NULL == c->res_head;
938
+ }
939
+
881
940
  void*
882
941
  con_loop(void *x) {
883
942
  Con c;
884
- Con ca[MAX_SOCK + 2];
885
- struct pollfd pa[MAX_SOCK + 2];
943
+ Con prev;
944
+ Con next;
945
+ Con cons = NULL;
946
+ size_t size = sizeof(struct pollfd) * INITIAL_POLL_SIZE;
947
+ struct pollfd *pa = (struct pollfd*)malloc(size);
948
+ struct pollfd *pend = pa + INITIAL_POLL_SIZE;
886
949
  struct pollfd *pp;
887
- Con *end = ca + MAX_SOCK;
888
- Con *cp;
889
950
  int ccnt = 0;
890
951
  int i;
891
- long mcnt = 0;
892
952
  double now;
893
953
  Pub pub;
894
954
 
895
955
  atomic_fetch_add(&the_server.running, 1);
896
- memset(ca, 0, sizeof(ca));
897
- memset(pa, 0, sizeof(pa));
956
+ memset(pa, 0, size);
898
957
  while (the_server.active) {
899
958
  while (NULL != (c = (Con)queue_pop(&the_server.con_queue, 0.0))) {
900
- mcnt++;
901
- ca[c->sock] = c;
959
+ c->next = cons;
960
+ cons = c;
902
961
  ccnt++;
962
+ if (pend - pa < ccnt + 2) {
963
+ size_t cnt = (pend - pa) * 2;
964
+
965
+ size = sizeof(struct pollfd) * cnt;
966
+ if (NULL == (pa = (struct pollfd*)malloc(size))) {
967
+ log_cat(&error_cat, "Out of memory.");
968
+ agoo_shutdown();
969
+ return NULL;
970
+ }
971
+ pend = pa + cnt;
972
+ }
903
973
  }
904
974
  while (NULL != (pub = (Pub)queue_pop(&the_server.pub_queue, 0.0))) {
905
975
  process_pub_con(pub);
906
976
  }
907
- pp = poll_setup(ca, ccnt, pa);
977
+
978
+ pp = poll_setup(cons, pa);
908
979
  if (0 > (i = poll(pa, (nfds_t)(pp - pa), 100))) {
909
980
  if (EAGAIN == errno) {
910
981
  continue;
@@ -920,9 +991,20 @@ con_loop(void *x) {
920
991
  if (0 != (pa->revents & POLLIN)) {
921
992
  queue_release(&the_server.con_queue);
922
993
  while (NULL != (c = (Con)queue_pop(&the_server.con_queue, 0.0))) {
923
- mcnt++;
924
- ca[c->sock] = c;
994
+ c->next = cons;
995
+ cons = c;
925
996
  ccnt++;
997
+ if (pend - pa < ccnt + 2) {
998
+ size_t cnt = (pend - pa) * 2;
999
+
1000
+ size = sizeof(struct pollfd) * cnt;
1001
+ if (NULL == (pa = (struct pollfd*)malloc(size))) {
1002
+ log_cat(&error_cat, "Out of memory.");
1003
+ agoo_shutdown();
1004
+ return NULL;
1005
+ }
1006
+ pend = pa + cnt;
1007
+ }
926
1008
  }
927
1009
  }
928
1010
  // Check pub_queue if an event is waiting.
@@ -933,21 +1015,23 @@ con_loop(void *x) {
933
1015
  }
934
1016
  }
935
1017
  }
936
- for (i = ccnt, cp = ca; 0 < i && cp < end; cp++) {
937
- if (NULL == *cp || 0 == (*cp)->sock || NULL == (*cp)->pp) {
1018
+ prev = NULL;
1019
+ for (c = cons; NULL != c; c = next) {
1020
+ next = c->next;
1021
+ if (0 == c->sock || NULL == c->pp) {
938
1022
  continue;
939
1023
  }
940
- c = *cp;
941
- i--;
942
1024
  pp = c->pp;
943
1025
  if (0 != (pp->revents & POLLIN)) {
944
1026
  if (con_read(c)) {
945
- goto CON_RM;
1027
+ c->dead = true;
1028
+ goto CON_CHECK;
946
1029
  }
947
1030
  }
948
1031
  if (0 != (pp->revents & POLLOUT)) {
949
1032
  if (con_write(c)) {
950
- goto CON_RM;
1033
+ c->dead = true;
1034
+ goto CON_CHECK;
951
1035
  }
952
1036
  }
953
1037
  if (0 != (pp->revents & (POLLERR | POLLHUP | POLLNVAL))) {
@@ -958,12 +1042,21 @@ con_loop(void *x) {
958
1042
  log_cat(&error_cat, "Socket %llu error. %s", c->id, strerror(errno));
959
1043
  }
960
1044
  }
961
- goto CON_RM;
1045
+ c->dead = true;
1046
+ goto CON_CHECK;
962
1047
  }
963
- if (0.0 == c->timeout || now < c->timeout) {
1048
+ CON_CHECK:
1049
+ if (c->dead) {
1050
+ if (remove_dead_res(c)) {
1051
+ goto CON_RM;
1052
+ }
1053
+ } else if (0.0 == c->timeout || now < c->timeout) {
1054
+ prev = c;
964
1055
  continue;
965
1056
  } else if (c->closing) {
966
- goto CON_RM;
1057
+ if (remove_dead_res(c)) {
1058
+ goto CON_RM;
1059
+ }
967
1060
  } else if (CON_WS == c->kind || CON_SSE == c->kind) {
968
1061
  c->timeout = dtime() + CON_TIMEOUT;
969
1062
  ws_ping(c);
@@ -972,20 +1065,25 @@ con_loop(void *x) {
972
1065
  c->closing = true;
973
1066
  c->timeout = now + 0.5;
974
1067
  //wush_text_set(&c->resp, (char*)close_resp, sizeof(close_resp) - 1, false);
1068
+ prev = c;
975
1069
  continue;
976
1070
  }
1071
+ prev = c;
977
1072
  continue;
978
1073
  CON_RM:
979
- ca[c->sock] = NULL;
1074
+ if (NULL == prev) {
1075
+ cons = next;
1076
+ } else {
1077
+ prev->next = next;
1078
+ }
980
1079
  ccnt--;
981
1080
  log_cat(&con_cat, "Connection %llu closed.", c->id);
982
1081
  con_destroy(c);
983
1082
  }
984
1083
  }
985
- for (i = ccnt, cp = ca; 0 < i && cp < end; cp++) {
986
- if (NULL != *cp) {
987
- con_destroy(*cp);
988
- }
1084
+ while (NULL != (c = cons)) {
1085
+ cons = c->next;
1086
+ con_destroy(c);
989
1087
  }
990
1088
  atomic_fetch_sub(&the_server.running, 1);
991
1089