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 +4 -4
- data/CHANGELOG.md +10 -0
- data/ext/agoo/con.c +104 -78
- data/ext/agoo/con.h +9 -4
- data/ext/agoo/debug.c +9 -9
- data/ext/agoo/debug.h +1 -0
- data/ext/agoo/error_stream.c +14 -6
- data/ext/agoo/gqleval.c +93 -14
- data/ext/agoo/gqlsub.c +37 -0
- data/ext/agoo/gqlsub.h +24 -0
- data/ext/agoo/gqlvalue.c +14 -0
- data/ext/agoo/gqlvalue.h +1 -0
- data/ext/agoo/graphql.c +13 -2
- data/ext/agoo/pub.c +0 -3
- data/ext/agoo/res.c +19 -5
- data/ext/agoo/res.h +1 -1
- data/ext/agoo/rgraphql.c +37 -0
- data/ext/agoo/rserver.c +26 -11
- data/ext/agoo/sdl.c +13 -13
- data/ext/agoo/sdl.h +2 -1
- data/ext/agoo/server.c +115 -0
- data/ext/agoo/server.h +8 -0
- data/ext/agoo/text.c +6 -4
- data/ext/agoo/websocket.c +9 -19
- data/lib/agoo/version.rb +1 -1
- data/test/rack_handler_test.rb +6 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e446f82a7209a72954bdf44c5f345fd9c5e13384dc75d6ce8faa7149d52f023
|
4
|
+
data.tar.gz: dea80f2d75d1f4affce64efece36dae4f7753e243d085faf8b7f0b2746aea2db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6167c0841ed2f05935998cb861632949b9d437e1c590820477add9d0ef067abd0c1ea1db377279c8f82fd4d58efb6491daf198f979e0fa6dc3f989ba4cd217ef
|
7
|
+
data.tar.gz: da0c4b54ee8f4db509e5e657bd2a919d0df4a314b7c6663d0e2afd0b4e89730fbaad7693f3888cefe984f8ca00d9e04237c5c51218e5d96e0278cc91eb483f5d
|
data/CHANGELOG.md
CHANGED
@@ -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
|
data/ext/agoo/con.c
CHANGED
@@ -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
|
-
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
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
|
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(
|
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
|
-
|
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)
|
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
|
-
|
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
|
-
|
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
|
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 !=
|
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 !=
|
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 !=
|
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
|
-
|
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
|
-
|
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 !=
|
1105
|
-
agooConKind 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)) {
|
data/ext/agoo/con.h
CHANGED
@@ -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
|
-
|
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);
|