| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG |
|
3
|
|
|
|
|
|
|
static SV* |
|
4
|
170
|
|
|
|
|
|
fetch_av_normal (pTHX_ AV *av, I32 i) |
|
5
|
|
|
|
|
|
|
{ |
|
6
|
170
|
|
|
|
|
|
SV **elt = av_fetch(av, i, 0); |
|
7
|
170
|
50
|
|
|
|
|
if (elt == NULL) return NULL; |
|
8
|
170
|
|
|
|
|
|
SV *sv = *elt; |
|
9
|
170
|
100
|
|
|
|
|
if (unlikely(SvMAGICAL(sv))) sv = sv_2mortal(newSVsv(sv)); |
|
10
|
170
|
100
|
|
|
|
|
if (unlikely(!SvOK(sv))) return NULL; |
|
11
|
|
|
|
|
|
|
// usually array ref elems aren't RVs (for PSGI anyway) |
|
12
|
167
|
100
|
|
|
|
|
if (unlikely(SvROK(sv))) sv = SvRV(sv); |
|
13
|
167
|
|
|
|
|
|
return sv; |
|
14
|
|
|
|
|
|
|
} |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG |
|
17
|
|
|
|
|
|
|
static struct iomatrix * |
|
18
|
5660
|
|
|
|
|
|
next_iomatrix (struct feer_conn *c) |
|
19
|
|
|
|
|
|
|
{ |
|
20
|
5660
|
|
|
|
|
|
bool add_iomatrix = 0; |
|
21
|
|
|
|
|
|
|
struct iomatrix *m; |
|
22
|
|
|
|
|
|
|
|
|
23
|
5660
|
100
|
|
|
|
|
if (!c->wbuf_rinq) { |
|
24
|
|
|
|
|
|
|
trace3("next_iomatrix(%d): head\n", c->fd); |
|
25
|
714
|
|
|
|
|
|
add_iomatrix = 1; |
|
26
|
|
|
|
|
|
|
} |
|
27
|
|
|
|
|
|
|
else { |
|
28
|
|
|
|
|
|
|
// get the tail-end struct |
|
29
|
4946
|
|
|
|
|
|
m = (struct iomatrix *)c->wbuf_rinq->prev->ref; |
|
30
|
|
|
|
|
|
|
trace3("next_iomatrix(%d): tail, count=%d, offset=%d\n", |
|
31
|
|
|
|
|
|
|
c->fd, m->count, m->offset); |
|
32
|
4946
|
50
|
|
|
|
|
if (m->count >= FEERSUM_IOMATRIX_SIZE) { |
|
33
|
0
|
|
|
|
|
|
add_iomatrix = 1; |
|
34
|
|
|
|
|
|
|
} |
|
35
|
|
|
|
|
|
|
} |
|
36
|
|
|
|
|
|
|
|
|
37
|
5660
|
100
|
|
|
|
|
if (add_iomatrix) { |
|
38
|
|
|
|
|
|
|
trace3("next_iomatrix(%d): alloc\n", c->fd); |
|
39
|
714
|
100
|
|
|
|
|
IOMATRIX_ALLOC(m); |
|
40
|
714
|
|
|
|
|
|
m->offset = m->count = 0; |
|
41
|
714
|
|
|
|
|
|
rinq_push(&c->wbuf_rinq, m); |
|
42
|
|
|
|
|
|
|
} |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
trace3("next_iomatrix(%d): end, count=%d, offset=%d\n", |
|
45
|
|
|
|
|
|
|
c->fd, m->count, m->offset); |
|
46
|
5660
|
|
|
|
|
|
return m; |
|
47
|
|
|
|
|
|
|
} |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG |
|
50
|
|
|
|
|
|
|
static STRLEN |
|
51
|
2249
|
|
|
|
|
|
add_sv_to_wbuf(struct feer_conn *c, SV *sv) |
|
52
|
|
|
|
|
|
|
{ |
|
53
|
2249
|
|
|
|
|
|
struct iomatrix *m = next_iomatrix(c); |
|
54
|
2249
|
|
|
|
|
|
unsigned idx = m->count++; |
|
55
|
|
|
|
|
|
|
STRLEN cur; |
|
56
|
2249
|
100
|
|
|
|
|
if (unlikely(SvMAGICAL(sv))) { |
|
57
|
2
|
|
|
|
|
|
sv = newSVsv(sv); // copy to force it to be normal. |
|
58
|
|
|
|
|
|
|
} |
|
59
|
2247
|
50
|
|
|
|
|
else if (unlikely(SvPADTMP(sv))) { |
|
60
|
|
|
|
|
|
|
// PADTMPs have their PVs re-used, so we can't simply keep a |
|
61
|
|
|
|
|
|
|
// reference. TEMPs maybe behave in a similar way and are potentially |
|
62
|
|
|
|
|
|
|
// stealable. If not stealing, we must make a copy. |
|
63
|
|
|
|
|
|
|
#ifdef FEERSUM_STEAL |
|
64
|
0
|
0
|
|
|
|
|
if (SvFLAGS(sv) == (SVs_PADTMP|SVf_POK|SVp_POK)) { |
|
65
|
|
|
|
|
|
|
trace3("STEALING\n"); |
|
66
|
0
|
|
|
|
|
|
SV *thief = newSV(0); |
|
67
|
0
|
|
|
|
|
|
sv_upgrade(thief, SVt_PV); |
|
68
|
|
|
|
|
|
|
|
|
69
|
0
|
|
|
|
|
|
SvPV_set(thief, SvPVX(sv)); |
|
70
|
0
|
|
|
|
|
|
SvLEN_set(thief, SvLEN(sv)); |
|
71
|
0
|
|
|
|
|
|
SvCUR_set(thief, SvCUR(sv)); |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
// make the temp null |
|
74
|
0
|
0
|
|
|
|
|
(void)SvOK_off(sv); |
|
75
|
0
|
|
|
|
|
|
SvPV_set(sv, NULL); |
|
76
|
0
|
|
|
|
|
|
SvLEN_set(sv, 0); |
|
77
|
0
|
|
|
|
|
|
SvCUR_set(sv, 0); |
|
78
|
|
|
|
|
|
|
|
|
79
|
0
|
|
|
|
|
|
SvFLAGS(thief) |= SVf_READONLY|SVf_POK|SVp_POK; |
|
80
|
|
|
|
|
|
|
|
|
81
|
0
|
|
|
|
|
|
sv = thief; |
|
82
|
|
|
|
|
|
|
} |
|
83
|
|
|
|
|
|
|
else { |
|
84
|
0
|
|
|
|
|
|
sv = newSVsv(sv); |
|
85
|
|
|
|
|
|
|
} |
|
86
|
|
|
|
|
|
|
#else |
|
87
|
|
|
|
|
|
|
sv = newSVsv(sv); |
|
88
|
|
|
|
|
|
|
#endif |
|
89
|
|
|
|
|
|
|
} |
|
90
|
|
|
|
|
|
|
else { |
|
91
|
2247
|
|
|
|
|
|
sv = SvREFCNT_inc(sv); |
|
92
|
|
|
|
|
|
|
} |
|
93
|
|
|
|
|
|
|
|
|
94
|
2249
|
|
|
|
|
|
m->iov[idx].iov_base = SvPV(sv, cur); |
|
95
|
2249
|
|
|
|
|
|
m->iov[idx].iov_len = cur; |
|
96
|
2249
|
|
|
|
|
|
m->sv[idx] = sv; |
|
97
|
|
|
|
|
|
|
|
|
98
|
2249
|
|
|
|
|
|
c->wbuf_len += cur; |
|
99
|
2249
|
|
|
|
|
|
return cur; |
|
100
|
|
|
|
|
|
|
} |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG |
|
103
|
|
|
|
|
|
|
static STRLEN |
|
104
|
2947
|
|
|
|
|
|
add_const_to_wbuf(struct feer_conn *c, const char *str, size_t str_len) |
|
105
|
|
|
|
|
|
|
{ |
|
106
|
2947
|
|
|
|
|
|
struct iomatrix *m = next_iomatrix(c); |
|
107
|
2947
|
|
|
|
|
|
unsigned idx = m->count++; |
|
108
|
2947
|
|
|
|
|
|
m->iov[idx].iov_base = (void*)str; |
|
109
|
2947
|
|
|
|
|
|
m->iov[idx].iov_len = str_len; |
|
110
|
2947
|
|
|
|
|
|
m->sv[idx] = NULL; |
|
111
|
2947
|
|
|
|
|
|
c->wbuf_len += str_len; |
|
112
|
2947
|
|
|
|
|
|
return str_len; |
|
113
|
|
|
|
|
|
|
} |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG |
|
116
|
|
|
|
|
|
|
static void |
|
117
|
464
|
|
|
|
|
|
add_placeholder_to_wbuf(struct feer_conn *c, SV **sv, struct iovec **iov_ref) |
|
118
|
|
|
|
|
|
|
{ |
|
119
|
464
|
|
|
|
|
|
struct iomatrix *m = next_iomatrix(c); |
|
120
|
464
|
|
|
|
|
|
unsigned idx = m->count++; |
|
121
|
464
|
|
|
|
|
|
*sv = newSV(31); |
|
122
|
464
|
|
|
|
|
|
SvPOK_on(*sv); |
|
123
|
464
|
|
|
|
|
|
m->sv[idx] = *sv; |
|
124
|
464
|
|
|
|
|
|
*iov_ref = &m->iov[idx]; |
|
125
|
464
|
|
|
|
|
|
} |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG |
|
128
|
|
|
|
|
|
|
static void |
|
129
|
57
|
|
|
|
|
|
finish_wbuf(struct feer_conn *c) |
|
130
|
|
|
|
|
|
|
{ |
|
131
|
57
|
100
|
|
|
|
|
if (!c->use_chunked) return; // nothing required unless chunked encoding |
|
132
|
40
|
|
|
|
|
|
add_const_to_wbuf(c, "0\r\n\r\n", 5); // terminating chunk |
|
133
|
|
|
|
|
|
|
} |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG |
|
136
|
|
|
|
|
|
|
static void |
|
137
|
464
|
|
|
|
|
|
update_wbuf_placeholder(struct feer_conn *c, SV *sv, struct iovec *iov) |
|
138
|
|
|
|
|
|
|
{ |
|
139
|
|
|
|
|
|
|
STRLEN cur; |
|
140
|
|
|
|
|
|
|
// can't pass iov_len for cur; incompatible pointer type on some systems: |
|
141
|
464
|
|
|
|
|
|
iov->iov_base = SvPV(sv,cur); |
|
142
|
464
|
|
|
|
|
|
iov->iov_len = cur; |
|
143
|
464
|
|
|
|
|
|
c->wbuf_len += cur; |
|
144
|
464
|
|
|
|
|
|
} |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
static void |
|
147
|
59
|
|
|
|
|
|
add_chunk_sv_to_wbuf(struct feer_conn *c, SV *sv) |
|
148
|
|
|
|
|
|
|
{ |
|
149
|
|
|
|
|
|
|
STRLEN len; |
|
150
|
59
|
|
|
|
|
|
(void)SvPV(sv, len); |
|
151
|
59
|
50
|
|
|
|
|
if (unlikely(len == 0)) return; /* skip: "0\r\n\r\n" is the terminal chunk */ |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
SV *chunk; |
|
154
|
|
|
|
|
|
|
struct iovec *chunk_iov; |
|
155
|
59
|
|
|
|
|
|
add_placeholder_to_wbuf(c, &chunk, &chunk_iov); |
|
156
|
59
|
|
|
|
|
|
STRLEN cur = add_sv_to_wbuf(c, sv); |
|
157
|
59
|
|
|
|
|
|
add_crlf_to_wbuf(c); |
|
158
|
59
|
|
|
|
|
|
sv_setpvf(chunk, "%"Sz_xf CRLF, (Sz)cur); |
|
159
|
59
|
|
|
|
|
|
update_wbuf_placeholder(c, chunk, chunk_iov); |
|
160
|
|
|
|
|
|
|
} |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
static const char * |
|
163
|
189
|
|
|
|
|
|
http_code_to_msg (int code) { |
|
164
|
189
|
|
|
|
|
|
switch (code) { |
|
165
|
0
|
|
|
|
|
|
case 100: return "Continue"; |
|
166
|
0
|
|
|
|
|
|
case 101: return "Switching Protocols"; |
|
167
|
0
|
|
|
|
|
|
case 102: return "Processing"; // RFC 2518 |
|
168
|
0
|
|
|
|
|
|
case 200: return "OK"; |
|
169
|
0
|
|
|
|
|
|
case 201: return "Created"; |
|
170
|
0
|
|
|
|
|
|
case 202: return "Accepted"; |
|
171
|
0
|
|
|
|
|
|
case 203: return "Non Authoritative Information"; |
|
172
|
0
|
|
|
|
|
|
case 204: return "No Content"; |
|
173
|
0
|
|
|
|
|
|
case 205: return "Reset Content"; |
|
174
|
0
|
|
|
|
|
|
case 206: return "Partial Content"; |
|
175
|
0
|
|
|
|
|
|
case 207: return "Multi-Status"; // RFC 4918 (WebDav) |
|
176
|
0
|
|
|
|
|
|
case 300: return "Multiple Choices"; |
|
177
|
0
|
|
|
|
|
|
case 301: return "Moved Permanently"; |
|
178
|
0
|
|
|
|
|
|
case 302: return "Found"; |
|
179
|
0
|
|
|
|
|
|
case 303: return "See Other"; |
|
180
|
0
|
|
|
|
|
|
case 304: return "Not Modified"; |
|
181
|
0
|
|
|
|
|
|
case 305: return "Use Proxy"; |
|
182
|
0
|
|
|
|
|
|
case 307: return "Temporary Redirect"; |
|
183
|
146
|
|
|
|
|
|
case 400: return "Bad Request"; |
|
184
|
0
|
|
|
|
|
|
case 401: return "Unauthorized"; |
|
185
|
0
|
|
|
|
|
|
case 402: return "Payment Required"; |
|
186
|
0
|
|
|
|
|
|
case 403: return "Forbidden"; |
|
187
|
0
|
|
|
|
|
|
case 404: return "Not Found"; |
|
188
|
7
|
|
|
|
|
|
case 405: return "Method Not Allowed"; |
|
189
|
0
|
|
|
|
|
|
case 406: return "Not Acceptable"; |
|
190
|
0
|
|
|
|
|
|
case 407: return "Proxy Authentication Required"; |
|
191
|
7
|
|
|
|
|
|
case 408: return "Request Timeout"; |
|
192
|
0
|
|
|
|
|
|
case 409: return "Conflict"; |
|
193
|
0
|
|
|
|
|
|
case 410: return "Gone"; |
|
194
|
4
|
|
|
|
|
|
case 411: return "Length Required"; |
|
195
|
0
|
|
|
|
|
|
case 412: return "Precondition Failed"; |
|
196
|
3
|
|
|
|
|
|
case 413: return "Request Entity Too Large"; |
|
197
|
3
|
|
|
|
|
|
case 414: return "Request URI Too Long"; |
|
198
|
0
|
|
|
|
|
|
case 415: return "Unsupported Media Type"; |
|
199
|
0
|
|
|
|
|
|
case 416: return "Requested Range Not Satisfiable"; |
|
200
|
9
|
|
|
|
|
|
case 417: return "Expectation Failed"; |
|
201
|
0
|
|
|
|
|
|
case 418: return "I'm a teapot"; |
|
202
|
0
|
|
|
|
|
|
case 421: return "Misdirected Request"; // RFC 9110 |
|
203
|
0
|
|
|
|
|
|
case 422: return "Unprocessable Entity"; // RFC 4918 |
|
204
|
0
|
|
|
|
|
|
case 423: return "Locked"; // RFC 4918 |
|
205
|
0
|
|
|
|
|
|
case 424: return "Failed Dependency"; // RFC 4918 |
|
206
|
0
|
|
|
|
|
|
case 425: return "Unordered Collection"; // RFC 3648 |
|
207
|
0
|
|
|
|
|
|
case 426: return "Upgrade Required"; // RFC 2817 |
|
208
|
0
|
|
|
|
|
|
case 429: return "Too Many Requests"; // RFC 6585 |
|
209
|
2
|
|
|
|
|
|
case 431: return "Request Header Fields Too Large"; // RFC 6585 |
|
210
|
0
|
|
|
|
|
|
case 449: return "Retry With"; // Microsoft |
|
211
|
0
|
|
|
|
|
|
case 450: return "Blocked by Parental Controls"; // Microsoft |
|
212
|
2
|
|
|
|
|
|
case 500: return "Internal Server Error"; |
|
213
|
6
|
|
|
|
|
|
case 501: return "Not Implemented"; |
|
214
|
0
|
|
|
|
|
|
case 502: return "Bad Gateway"; |
|
215
|
0
|
|
|
|
|
|
case 503: return "Service Unavailable"; |
|
216
|
0
|
|
|
|
|
|
case 504: return "Gateway Timeout"; |
|
217
|
0
|
|
|
|
|
|
case 505: return "HTTP Version Not Supported"; |
|
218
|
0
|
|
|
|
|
|
case 506: return "Variant Also Negotiates"; // RFC 2295 |
|
219
|
0
|
|
|
|
|
|
case 507: return "Insufficient Storage"; // RFC 4918 |
|
220
|
0
|
|
|
|
|
|
case 509: return "Bandwidth Limit Exceeded"; // Apache mod |
|
221
|
0
|
|
|
|
|
|
case 510: return "Not Extended"; // RFC 2774 |
|
222
|
0
|
|
|
|
|
|
case 530: return "User access denied"; // ?? |
|
223
|
0
|
|
|
|
|
|
default: break; |
|
224
|
|
|
|
|
|
|
} |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
// default to the Nxx group names in RFC 2616 |
|
227
|
0
|
0
|
|
|
|
|
if (100 <= code && code <= 199) { |
|
|
|
0
|
|
|
|
|
|
|
228
|
0
|
|
|
|
|
|
return "Informational"; |
|
229
|
|
|
|
|
|
|
} |
|
230
|
0
|
0
|
|
|
|
|
else if (200 <= code && code <= 299) { |
|
|
|
0
|
|
|
|
|
|
|
231
|
0
|
|
|
|
|
|
return "Success"; |
|
232
|
|
|
|
|
|
|
} |
|
233
|
0
|
0
|
|
|
|
|
else if (300 <= code && code <= 399) { |
|
|
|
0
|
|
|
|
|
|
|
234
|
0
|
|
|
|
|
|
return "Redirection"; |
|
235
|
|
|
|
|
|
|
} |
|
236
|
0
|
0
|
|
|
|
|
else if (400 <= code && code <= 499) { |
|
|
|
0
|
|
|
|
|
|
|
237
|
0
|
|
|
|
|
|
return "Client Error"; |
|
238
|
|
|
|
|
|
|
} |
|
239
|
|
|
|
|
|
|
else { |
|
240
|
0
|
|
|
|
|
|
return "Error"; |
|
241
|
|
|
|
|
|
|
} |
|
242
|
|
|
|
|
|
|
} |
|
243
|
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
static int |
|
245
|
589
|
|
|
|
|
|
prep_socket(int fd, int is_tcp) |
|
246
|
|
|
|
|
|
|
{ |
|
247
|
|
|
|
|
|
|
#ifdef HAS_ACCEPT4 |
|
248
|
589
|
|
|
|
|
|
int flags = 1; |
|
249
|
|
|
|
|
|
|
#else |
|
250
|
|
|
|
|
|
|
int flags; |
|
251
|
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
// make it non-blocking (preserve existing flags) |
|
253
|
|
|
|
|
|
|
flags = fcntl(fd, F_GETFL); |
|
254
|
|
|
|
|
|
|
if (unlikely(flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)) |
|
255
|
|
|
|
|
|
|
return -1; |
|
256
|
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
flags = 1; |
|
258
|
|
|
|
|
|
|
#endif |
|
259
|
589
|
50
|
|
|
|
|
if (likely(is_tcp)) { |
|
260
|
589
|
50
|
|
|
|
|
if (unlikely(setsockopt(fd, SOL_TCP, TCP_NODELAY, &flags, sizeof(int)))) |
|
261
|
0
|
|
|
|
|
|
return -1; |
|
262
|
|
|
|
|
|
|
} |
|
263
|
|
|
|
|
|
|
|
|
264
|
589
|
|
|
|
|
|
return 0; |
|
265
|
|
|
|
|
|
|
} |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
// TCP cork/uncork for batching writes (Linux: TCP_CORK, BSD: TCP_NOPUSH) |
|
268
|
|
|
|
|
|
|
#if defined(TCP_CORK) |
|
269
|
|
|
|
|
|
|
# define FEERSUM_TCP_CORK TCP_CORK |
|
270
|
|
|
|
|
|
|
#elif defined(TCP_NOPUSH) |
|
271
|
|
|
|
|
|
|
# define FEERSUM_TCP_CORK TCP_NOPUSH |
|
272
|
|
|
|
|
|
|
#endif |
|
273
|
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
#ifdef FEERSUM_TCP_CORK |
|
275
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG static void |
|
276
|
6
|
|
|
|
|
|
set_cork(struct feer_conn *c, int cork) |
|
277
|
|
|
|
|
|
|
{ |
|
278
|
6
|
50
|
|
|
|
|
if (likely(c->cached_is_tcp)) { |
|
279
|
6
|
|
|
|
|
|
setsockopt(c->fd, SOL_TCP, FEERSUM_TCP_CORK, &cork, sizeof(cork)); |
|
280
|
|
|
|
|
|
|
} |
|
281
|
6
|
|
|
|
|
|
} |
|
282
|
|
|
|
|
|
|
#else |
|
283
|
|
|
|
|
|
|
# define set_cork(c, cork) ((void)0) |
|
284
|
|
|
|
|
|
|
#endif |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
static void |
|
287
|
6
|
|
|
|
|
|
invoke_shutdown_cb(pTHX_ struct feer_server *server) |
|
288
|
|
|
|
|
|
|
{ |
|
289
|
6
|
|
|
|
|
|
dSP; |
|
290
|
6
|
|
|
|
|
|
SV *cb = server->shutdown_cb_cv; |
|
291
|
6
|
|
|
|
|
|
server->shutdown_cb_cv = NULL; |
|
292
|
6
|
|
|
|
|
|
ENTER; |
|
293
|
6
|
|
|
|
|
|
SAVETMPS; |
|
294
|
6
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
295
|
6
|
|
|
|
|
|
PUTBACK; |
|
296
|
6
|
|
|
|
|
|
call_sv(cb, G_EVAL|G_VOID|G_DISCARD|G_NOARGS); |
|
297
|
6
|
|
|
|
|
|
SPAGAIN; |
|
298
|
|
|
|
|
|
|
trace3("called shutdown handler\n"); |
|
299
|
6
|
50
|
|
|
|
|
if (SvTRUE(ERRSV)) |
|
|
|
100
|
|
|
|
|
|
|
300
|
1
|
50
|
|
|
|
|
sv_setsv(ERRSV, &PL_sv_undef); |
|
301
|
6
|
|
|
|
|
|
SvREFCNT_dec(cb); |
|
302
|
6
|
50
|
|
|
|
|
FREETMPS; |
|
303
|
6
|
|
|
|
|
|
LEAVE; |
|
304
|
6
|
|
|
|
|
|
} |
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
static void |
|
307
|
1134
|
|
|
|
|
|
safe_close_conn(struct feer_conn *c, const char *where) |
|
308
|
|
|
|
|
|
|
{ |
|
309
|
1134
|
100
|
|
|
|
|
if (unlikely(c->fd < 0)) |
|
310
|
545
|
|
|
|
|
|
return; |
|
311
|
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
#ifdef FEERSUM_HAS_H2 |
|
313
|
|
|
|
|
|
|
if (c->is_h2_stream) { |
|
314
|
|
|
|
|
|
|
/* Pseudo-conns share parent's fd — do NOT close or shutdown it. |
|
315
|
|
|
|
|
|
|
* The parent connection owns the fd and will close it. */ |
|
316
|
|
|
|
|
|
|
c->fd = -1; |
|
317
|
|
|
|
|
|
|
return; |
|
318
|
|
|
|
|
|
|
} |
|
319
|
|
|
|
|
|
|
#endif |
|
320
|
|
|
|
|
|
|
|
|
321
|
589
|
50
|
|
|
|
|
CLOSE_SENDFILE_FD(c); |
|
|
|
0
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
#ifdef FEERSUM_HAS_TLS |
|
324
|
|
|
|
|
|
|
// Best-effort TLS close_notify before TCP shutdown |
|
325
|
589
|
100
|
|
|
|
|
if (c->tls && c->tls_handshake_done) { |
|
|
|
100
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
ptls_buffer_t closebuf; |
|
327
|
42
|
|
|
|
|
|
ptls_buffer_init(&closebuf, "", 0); |
|
328
|
42
|
|
|
|
|
|
ptls_send_alert(c->tls, &closebuf, PTLS_ALERT_LEVEL_WARNING, PTLS_ALERT_CLOSE_NOTIFY); |
|
329
|
42
|
50
|
|
|
|
|
if (closebuf.off > 0) { |
|
330
|
|
|
|
|
|
|
ssize_t wr PERL_UNUSED_DECL; |
|
331
|
42
|
|
|
|
|
|
wr = write(c->fd, closebuf.base, closebuf.off); |
|
332
|
|
|
|
|
|
|
} |
|
333
|
42
|
|
|
|
|
|
ptls_buffer_dispose(&closebuf); |
|
334
|
|
|
|
|
|
|
} |
|
335
|
|
|
|
|
|
|
#endif |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
// Graceful TCP shutdown: send FIN to peer before close |
|
338
|
|
|
|
|
|
|
// This ensures client sees clean EOF instead of RST |
|
339
|
589
|
|
|
|
|
|
shutdown(c->fd, SHUT_WR); |
|
340
|
|
|
|
|
|
|
|
|
341
|
589
|
50
|
|
|
|
|
if (unlikely(close(c->fd) < 0)) |
|
342
|
0
|
|
|
|
|
|
trouble("close(%s) fd=%d: %s\n", where, c->fd, strerror(errno)); |
|
343
|
|
|
|
|
|
|
|
|
344
|
589
|
|
|
|
|
|
c->fd = -1; |
|
345
|
|
|
|
|
|
|
} |
|
346
|
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
static struct feer_conn * |
|
348
|
589
|
|
|
|
|
|
new_feer_conn (EV_P_ int conn_fd, struct sockaddr *sa, socklen_t sa_len, |
|
349
|
|
|
|
|
|
|
struct feer_server *srvr, struct feer_listen *lsnr) |
|
350
|
|
|
|
|
|
|
{ |
|
351
|
589
|
|
|
|
|
|
SV *self = newSV(0); |
|
352
|
589
|
50
|
|
|
|
|
SvUPGRADE(self, SVt_PVMG); // ensures sv_bless doesn't reallocate |
|
353
|
589
|
50
|
|
|
|
|
SvGROW(self, sizeof(struct feer_conn)); |
|
|
|
50
|
|
|
|
|
|
|
354
|
589
|
|
|
|
|
|
SvPOK_only(self); |
|
355
|
589
|
|
|
|
|
|
SvIOK_on(self); |
|
356
|
589
|
|
|
|
|
|
SvIV_set(self,conn_fd); |
|
357
|
|
|
|
|
|
|
|
|
358
|
589
|
|
|
|
|
|
struct feer_conn *c = (struct feer_conn *)SvPVX(self); |
|
359
|
589
|
|
|
|
|
|
Zero(c, 1, struct feer_conn); |
|
360
|
|
|
|
|
|
|
|
|
361
|
589
|
|
|
|
|
|
c->self = self; |
|
362
|
589
|
|
|
|
|
|
c->server = srvr; |
|
363
|
589
|
|
|
|
|
|
c->listener = lsnr; |
|
364
|
589
|
|
|
|
|
|
SvREFCNT_inc_void_NN(srvr->self); // prevent server GC while conn alive |
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
// Cache hot config fields to avoid c->server->/c->listener-> indirection |
|
367
|
589
|
|
|
|
|
|
c->cached_read_timeout = srvr->read_timeout; |
|
368
|
589
|
|
|
|
|
|
c->cached_write_timeout = srvr->write_timeout; |
|
369
|
589
|
|
|
|
|
|
c->cached_max_conn_reqs = srvr->max_connection_reqs; |
|
370
|
589
|
|
|
|
|
|
c->cached_is_tcp = lsnr->is_tcp; |
|
371
|
589
|
|
|
|
|
|
c->cached_keepalive_default = srvr->is_keepalive; |
|
372
|
589
|
|
|
|
|
|
c->cached_use_reverse_proxy = srvr->use_reverse_proxy; |
|
373
|
589
|
|
|
|
|
|
c->cached_request_cb_is_psgi = srvr->request_cb_is_psgi; |
|
374
|
589
|
|
|
|
|
|
c->cached_max_read_buf = srvr->max_read_buf; |
|
375
|
589
|
|
|
|
|
|
c->cached_max_body_len = srvr->max_body_len; |
|
376
|
589
|
|
|
|
|
|
c->cached_max_uri_len = srvr->max_uri_len; |
|
377
|
589
|
|
|
|
|
|
c->cached_wbuf_low_water = srvr->wbuf_low_water; |
|
378
|
589
|
|
|
|
|
|
c->fd = conn_fd; |
|
379
|
589
|
|
|
|
|
|
memcpy(&c->sa, sa, sa_len); // copy into embedded storage |
|
380
|
589
|
100
|
|
|
|
|
c->receiving = srvr->use_proxy_protocol ? RECEIVE_PROXY_HEADER : RECEIVE_HEADERS; |
|
381
|
589
|
|
|
|
|
|
c->sendfile_fd = -1; // no sendfile pending |
|
382
|
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
#ifdef FEERSUM_HAS_TLS |
|
384
|
589
|
100
|
|
|
|
|
if (lsnr->tls_ctx_ref) { |
|
385
|
59
|
|
|
|
|
|
ev_io_init(&c->read_ev_io, try_tls_conn_read, conn_fd, EV_READ); |
|
386
|
59
|
|
|
|
|
|
ev_io_init(&c->write_ev_io, try_tls_conn_write, conn_fd, EV_WRITE); |
|
387
|
59
|
|
|
|
|
|
feer_tls_init_conn(c, lsnr->tls_ctx_ref); |
|
388
|
|
|
|
|
|
|
} else { |
|
389
|
|
|
|
|
|
|
#endif |
|
390
|
530
|
|
|
|
|
|
ev_io_init(&c->read_ev_io, try_conn_read, conn_fd, EV_READ); |
|
391
|
530
|
|
|
|
|
|
ev_io_init(&c->write_ev_io, try_conn_write, conn_fd, EV_WRITE); |
|
392
|
|
|
|
|
|
|
#ifdef FEERSUM_HAS_TLS |
|
393
|
|
|
|
|
|
|
} |
|
394
|
|
|
|
|
|
|
#endif |
|
395
|
589
|
|
|
|
|
|
ev_set_priority(&c->read_ev_io, srvr->read_priority); |
|
396
|
589
|
|
|
|
|
|
c->read_ev_io.data = (void *)c; |
|
397
|
|
|
|
|
|
|
|
|
398
|
589
|
|
|
|
|
|
ev_set_priority(&c->write_ev_io, srvr->write_priority); |
|
399
|
589
|
|
|
|
|
|
c->write_ev_io.data = (void *)c; |
|
400
|
|
|
|
|
|
|
|
|
401
|
589
|
|
|
|
|
|
ev_init(&c->read_ev_timer, conn_read_timeout); |
|
402
|
589
|
|
|
|
|
|
ev_set_priority(&c->read_ev_timer, srvr->read_priority); |
|
403
|
589
|
|
|
|
|
|
c->read_ev_timer.data = (void *)c; |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
// Slowloris protection: header deadline timer (non-resetting) |
|
406
|
589
|
|
|
|
|
|
ev_init(&c->header_ev_timer, conn_header_timeout); |
|
407
|
589
|
|
|
|
|
|
ev_set_priority(&c->header_ev_timer, srvr->read_priority); |
|
408
|
589
|
|
|
|
|
|
c->header_ev_timer.data = (void *)c; |
|
409
|
|
|
|
|
|
|
|
|
410
|
589
|
|
|
|
|
|
ev_init(&c->write_ev_timer, conn_write_timeout); |
|
411
|
589
|
|
|
|
|
|
ev_set_priority(&c->write_ev_timer, srvr->write_priority); |
|
412
|
589
|
|
|
|
|
|
c->write_ev_timer.data = (void *)c; |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
trace3("made conn fd=%d self=%p, c=%p, cur=%"Sz_uf", len=%"Sz_uf"\n", |
|
415
|
|
|
|
|
|
|
c->fd, self, c, (Sz)SvCUR(self), (Sz)SvLEN(self)); |
|
416
|
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
if (FEERSUM_CONN_NEW_ENABLED()) { |
|
418
|
|
|
|
|
|
|
feersum_set_conn_remote_info(aTHX_ c); |
|
419
|
|
|
|
|
|
|
FEERSUM_CONN_NEW(c->fd, SvPV_nolen(c->remote_addr), (int)SvIV(c->remote_port)); |
|
420
|
|
|
|
|
|
|
} |
|
421
|
|
|
|
|
|
|
|
|
422
|
589
|
|
|
|
|
|
SV *rv = newRV_inc(c->self); |
|
423
|
589
|
|
|
|
|
|
sv_bless(rv, feer_conn_stash); // so DESTROY can get called on read errors |
|
424
|
589
|
|
|
|
|
|
SvREFCNT_dec(rv); |
|
425
|
|
|
|
|
|
|
|
|
426
|
589
|
|
|
|
|
|
SvREADONLY_on(self); |
|
427
|
589
|
|
|
|
|
|
srvr->active_conns++; |
|
428
|
589
|
|
|
|
|
|
return c; |
|
429
|
|
|
|
|
|
|
} |
|
430
|
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG |
|
432
|
|
|
|
|
|
|
static struct feer_conn * |
|
433
|
1382
|
|
|
|
|
|
sv_2feer_conn (SV *rv) |
|
434
|
|
|
|
|
|
|
{ |
|
435
|
1382
|
50
|
|
|
|
|
if (unlikely(!sv_isa(rv,"Feersum::Connection"))) |
|
436
|
0
|
|
|
|
|
|
croak("object is not of type Feersum::Connection"); |
|
437
|
1382
|
|
|
|
|
|
return (struct feer_conn *)SvPVX(SvRV(rv)); |
|
438
|
|
|
|
|
|
|
} |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG |
|
441
|
|
|
|
|
|
|
static SV* |
|
442
|
498
|
|
|
|
|
|
feer_conn_2sv (struct feer_conn *c) |
|
443
|
|
|
|
|
|
|
{ |
|
444
|
498
|
|
|
|
|
|
return newRV_inc(c->self); |
|
445
|
|
|
|
|
|
|
} |
|
446
|
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
static feer_conn_handle * |
|
448
|
621
|
|
|
|
|
|
sv_2feer_conn_handle (SV *rv, bool can_croak) |
|
449
|
|
|
|
|
|
|
{ |
|
450
|
|
|
|
|
|
|
trace3("sv 2 conn_handle\n"); |
|
451
|
621
|
50
|
|
|
|
|
if (unlikely(!SvROK(rv))) croak("Expected a reference"); |
|
452
|
|
|
|
|
|
|
// do not allow subclassing |
|
453
|
621
|
|
|
|
|
|
SV *sv = SvRV(rv); |
|
454
|
621
|
50
|
|
|
|
|
if (likely( |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
sv_isobject(rv) && |
|
456
|
|
|
|
|
|
|
(SvSTASH(sv) == feer_conn_writer_stash || |
|
457
|
|
|
|
|
|
|
SvSTASH(sv) == feer_conn_reader_stash) |
|
458
|
|
|
|
|
|
|
)) { |
|
459
|
621
|
|
|
|
|
|
UV uv = SvUV(sv); |
|
460
|
621
|
100
|
|
|
|
|
if (uv == 0) { |
|
461
|
85
|
100
|
|
|
|
|
if (can_croak) croak("Operation not allowed: Handle is closed."); |
|
462
|
70
|
|
|
|
|
|
return NULL; |
|
463
|
|
|
|
|
|
|
} |
|
464
|
536
|
|
|
|
|
|
return INT2PTR(feer_conn_handle*,uv); |
|
465
|
|
|
|
|
|
|
} |
|
466
|
|
|
|
|
|
|
|
|
467
|
0
|
0
|
|
|
|
|
if (can_croak) |
|
468
|
0
|
|
|
|
|
|
croak("Expected a Feersum::Connection::Writer or ::Reader object"); |
|
469
|
0
|
|
|
|
|
|
return NULL; |
|
470
|
|
|
|
|
|
|
} |
|
471
|
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
static SV * |
|
473
|
349
|
|
|
|
|
|
new_feer_conn_handle (pTHX_ struct feer_conn *c, bool is_writer) |
|
474
|
|
|
|
|
|
|
{ |
|
475
|
|
|
|
|
|
|
SV *sv; |
|
476
|
349
|
|
|
|
|
|
SvREFCNT_inc_void_NN(c->self); |
|
477
|
349
|
|
|
|
|
|
sv = newRV_noinc(newSVuv(PTR2UV(c))); |
|
478
|
349
|
100
|
|
|
|
|
sv_bless(sv, is_writer ? feer_conn_writer_stash : feer_conn_reader_stash); |
|
479
|
349
|
|
|
|
|
|
return sv; |
|
480
|
|
|
|
|
|
|
} |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
static void |
|
483
|
184
|
|
|
|
|
|
init_feer_server (struct feer_server *s) |
|
484
|
|
|
|
|
|
|
{ |
|
485
|
|
|
|
|
|
|
int i; |
|
486
|
184
|
|
|
|
|
|
Zero(s, 1, struct feer_server); |
|
487
|
184
|
|
|
|
|
|
s->read_timeout = READ_TIMEOUT; |
|
488
|
184
|
|
|
|
|
|
s->header_timeout = HEADER_TIMEOUT; |
|
489
|
184
|
|
|
|
|
|
s->write_timeout = WRITE_TIMEOUT; |
|
490
|
184
|
|
|
|
|
|
s->max_accept_per_loop = DEFAULT_MAX_ACCEPT_PER_LOOP; |
|
491
|
184
|
|
|
|
|
|
s->max_connections = 10000; |
|
492
|
184
|
|
|
|
|
|
s->max_read_buf = MAX_READ_BUF; |
|
493
|
184
|
|
|
|
|
|
s->max_body_len = MAX_BODY_LEN; |
|
494
|
184
|
|
|
|
|
|
s->max_uri_len = MAX_URI_LEN; |
|
495
|
184
|
|
|
|
|
|
s->psgix_io = true; |
|
496
|
3128
|
100
|
|
|
|
|
for (i = 0; i < FEER_MAX_LISTENERS; i++) { |
|
497
|
2944
|
|
|
|
|
|
s->listeners[i].fd = -1; |
|
498
|
2944
|
|
|
|
|
|
s->listeners[i].server = s; |
|
499
|
|
|
|
|
|
|
#ifdef __linux__ |
|
500
|
2944
|
|
|
|
|
|
s->listeners[i].epoll_fd = -1; |
|
501
|
|
|
|
|
|
|
#endif |
|
502
|
|
|
|
|
|
|
} |
|
503
|
184
|
|
|
|
|
|
} |
|
504
|
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
static struct feer_server * |
|
506
|
184
|
|
|
|
|
|
new_feer_server (pTHX) |
|
507
|
|
|
|
|
|
|
{ |
|
508
|
184
|
|
|
|
|
|
SV *self = newSV(0); |
|
509
|
184
|
50
|
|
|
|
|
SvUPGRADE(self, SVt_PVMG); |
|
510
|
184
|
50
|
|
|
|
|
SvGROW(self, sizeof(struct feer_server)); |
|
|
|
50
|
|
|
|
|
|
|
511
|
184
|
|
|
|
|
|
SvPOK_only(self); |
|
512
|
184
|
|
|
|
|
|
SvIOK_on(self); |
|
513
|
|
|
|
|
|
|
|
|
514
|
184
|
|
|
|
|
|
struct feer_server *s = (struct feer_server *)SvPVX(self); |
|
515
|
184
|
|
|
|
|
|
init_feer_server(s); |
|
516
|
184
|
|
|
|
|
|
s->self = self; |
|
517
|
|
|
|
|
|
|
|
|
518
|
184
|
|
|
|
|
|
SV *rv = newRV_inc(self); |
|
519
|
184
|
|
|
|
|
|
sv_bless(rv, feer_stash); |
|
520
|
184
|
|
|
|
|
|
SvREFCNT_dec(rv); |
|
521
|
|
|
|
|
|
|
|
|
522
|
184
|
|
|
|
|
|
SvREADONLY_on(self); |
|
523
|
184
|
|
|
|
|
|
return s; |
|
524
|
|
|
|
|
|
|
} |
|
525
|
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG |
|
527
|
|
|
|
|
|
|
static struct feer_server * |
|
528
|
27253
|
|
|
|
|
|
sv_2feer_server (SV *rv) |
|
529
|
|
|
|
|
|
|
{ |
|
530
|
|
|
|
|
|
|
// Accept both instance ($obj->method) and class (Feersum->method) calls |
|
531
|
27253
|
100
|
|
|
|
|
if (sv_isa(rv, "Feersum")) { |
|
532
|
27220
|
|
|
|
|
|
return (struct feer_server *)SvPVX(SvRV(rv)); |
|
533
|
|
|
|
|
|
|
} |
|
534
|
|
|
|
|
|
|
// Class method call: "Feersum"->method() - return default server |
|
535
|
33
|
50
|
|
|
|
|
if (SvPOK(rv) && strEQ(SvPV_nolen(rv), "Feersum")) { |
|
|
|
50
|
|
|
|
|
|
|
536
|
33
|
|
|
|
|
|
return default_server; |
|
537
|
|
|
|
|
|
|
} |
|
538
|
0
|
|
|
|
|
|
croak("object is not of type Feersum"); |
|
539
|
|
|
|
|
|
|
return NULL; // unreachable |
|
540
|
|
|
|
|
|
|
} |
|
541
|
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG |
|
543
|
|
|
|
|
|
|
static SV* |
|
544
|
172
|
|
|
|
|
|
feer_server_2sv (struct feer_server *s) |
|
545
|
|
|
|
|
|
|
{ |
|
546
|
172
|
|
|
|
|
|
return newRV_inc(s->self); |
|
547
|
|
|
|
|
|
|
} |
|
548
|
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG static void |
|
550
|
128
|
|
|
|
|
|
start_read_watcher(struct feer_conn *c) { |
|
551
|
|
|
|
|
|
|
ASSERT_EV_LOOP_INITIALIZED(); |
|
552
|
128
|
100
|
|
|
|
|
if (unlikely(ev_is_active(&c->read_ev_io))) |
|
553
|
12
|
|
|
|
|
|
return; |
|
554
|
|
|
|
|
|
|
trace("start read watcher %d\n",c->fd); |
|
555
|
116
|
|
|
|
|
|
ev_io_start(feersum_ev_loop, &c->read_ev_io); |
|
556
|
116
|
|
|
|
|
|
SvREFCNT_inc_void_NN(c->self); |
|
557
|
|
|
|
|
|
|
} |
|
558
|
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG static void |
|
560
|
908
|
|
|
|
|
|
stop_read_watcher(struct feer_conn *c) { |
|
561
|
908
|
100
|
|
|
|
|
if (unlikely(!ev_is_active(&c->read_ev_io))) |
|
562
|
799
|
|
|
|
|
|
return; |
|
563
|
|
|
|
|
|
|
trace("stop read watcher %d\n",c->fd); |
|
564
|
109
|
|
|
|
|
|
ev_io_stop(feersum_ev_loop, &c->read_ev_io); |
|
565
|
109
|
|
|
|
|
|
SvREFCNT_dec(c->self); |
|
566
|
|
|
|
|
|
|
} |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG static void |
|
569
|
117
|
|
|
|
|
|
restart_read_timer(struct feer_conn *c) { |
|
570
|
117
|
100
|
|
|
|
|
if (likely(!ev_is_active(&c->read_ev_timer))) { |
|
571
|
|
|
|
|
|
|
trace("restart read timer %d\n",c->fd); |
|
572
|
97
|
|
|
|
|
|
c->read_ev_timer.repeat = c->cached_read_timeout; |
|
573
|
97
|
|
|
|
|
|
SvREFCNT_inc_void_NN(c->self); |
|
574
|
|
|
|
|
|
|
} |
|
575
|
117
|
|
|
|
|
|
ev_timer_again(feersum_ev_loop, &c->read_ev_timer); |
|
576
|
117
|
|
|
|
|
|
} |
|
577
|
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG static void |
|
579
|
911
|
|
|
|
|
|
stop_read_timer(struct feer_conn *c) { |
|
580
|
911
|
100
|
|
|
|
|
if (unlikely(!ev_is_active(&c->read_ev_timer))) |
|
581
|
816
|
|
|
|
|
|
return; |
|
582
|
|
|
|
|
|
|
trace("stop read timer %d\n",c->fd); |
|
583
|
95
|
|
|
|
|
|
ev_timer_stop(feersum_ev_loop, &c->read_ev_timer); |
|
584
|
95
|
|
|
|
|
|
SvREFCNT_dec(c->self); |
|
585
|
|
|
|
|
|
|
} |
|
586
|
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG static void |
|
588
|
584
|
|
|
|
|
|
start_write_watcher(struct feer_conn *c) { |
|
589
|
|
|
|
|
|
|
ASSERT_EV_LOOP_INITIALIZED(); |
|
590
|
584
|
100
|
|
|
|
|
if (unlikely(ev_is_active(&c->write_ev_io))) |
|
591
|
118
|
|
|
|
|
|
return; |
|
592
|
|
|
|
|
|
|
trace("start write watcher %d\n",c->fd); |
|
593
|
466
|
|
|
|
|
|
ev_io_start(feersum_ev_loop, &c->write_ev_io); |
|
594
|
466
|
|
|
|
|
|
SvREFCNT_inc_void_NN(c->self); |
|
595
|
|
|
|
|
|
|
} |
|
596
|
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG static void |
|
598
|
735
|
|
|
|
|
|
stop_write_watcher(struct feer_conn *c) { |
|
599
|
735
|
100
|
|
|
|
|
if (unlikely(!ev_is_active(&c->write_ev_io))) |
|
600
|
269
|
|
|
|
|
|
return; |
|
601
|
|
|
|
|
|
|
trace("stop write watcher %d\n",c->fd); |
|
602
|
466
|
|
|
|
|
|
ev_io_stop(feersum_ev_loop, &c->write_ev_io); |
|
603
|
466
|
|
|
|
|
|
SvREFCNT_dec(c->self); |
|
604
|
|
|
|
|
|
|
} |
|
605
|
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG static void |
|
607
|
711
|
|
|
|
|
|
restart_write_timer(struct feer_conn *c) { |
|
608
|
711
|
100
|
|
|
|
|
if (c->cached_write_timeout <= 0.0) return; |
|
609
|
1
|
50
|
|
|
|
|
if (likely(!ev_is_active(&c->write_ev_timer))) { |
|
610
|
|
|
|
|
|
|
trace("restart write timer %d\n",c->fd); |
|
611
|
1
|
|
|
|
|
|
c->write_ev_timer.repeat = c->cached_write_timeout; |
|
612
|
1
|
|
|
|
|
|
SvREFCNT_inc_void_NN(c->self); |
|
613
|
|
|
|
|
|
|
} |
|
614
|
1
|
|
|
|
|
|
ev_timer_again(feersum_ev_loop, &c->write_ev_timer); |
|
615
|
|
|
|
|
|
|
} |
|
616
|
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG static void |
|
618
|
750
|
|
|
|
|
|
stop_write_timer(struct feer_conn *c) { |
|
619
|
750
|
100
|
|
|
|
|
if (unlikely(!ev_is_active(&c->write_ev_timer))) |
|
620
|
749
|
|
|
|
|
|
return; |
|
621
|
|
|
|
|
|
|
trace("stop write timer %d\n",c->fd); |
|
622
|
1
|
|
|
|
|
|
ev_timer_stop(feersum_ev_loop, &c->write_ev_timer); |
|
623
|
1
|
|
|
|
|
|
SvREFCNT_dec(c->self); |
|
624
|
|
|
|
|
|
|
} |
|
625
|
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG static void |
|
627
|
1581
|
|
|
|
|
|
stop_header_timer(struct feer_conn *c) { |
|
628
|
1581
|
100
|
|
|
|
|
if (unlikely(!ev_is_active(&c->header_ev_timer))) |
|
629
|
879
|
|
|
|
|
|
return; |
|
630
|
|
|
|
|
|
|
trace("stop header timer %d\n", c->fd); |
|
631
|
702
|
|
|
|
|
|
ev_timer_stop(feersum_ev_loop, &c->header_ev_timer); |
|
632
|
702
|
|
|
|
|
|
SvREFCNT_dec(c->self); |
|
633
|
|
|
|
|
|
|
} |
|
634
|
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG static void |
|
636
|
129
|
|
|
|
|
|
restart_header_timer(struct feer_conn *c) { |
|
637
|
129
|
|
|
|
|
|
double timeout = c->server->header_timeout; |
|
638
|
129
|
50
|
|
|
|
|
if (timeout <= 0.0) return; |
|
639
|
129
|
|
|
|
|
|
stop_header_timer(c); |
|
640
|
129
|
|
|
|
|
|
ev_timer_set(&c->header_ev_timer, timeout, 0.0); |
|
641
|
129
|
|
|
|
|
|
ev_timer_start(feersum_ev_loop, &c->header_ev_timer); |
|
642
|
129
|
|
|
|
|
|
SvREFCNT_inc_void_NN(c->self); |
|
643
|
|
|
|
|
|
|
} |
|
644
|
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
INLINE_UNLESS_DEBUG static void |
|
646
|
45
|
|
|
|
|
|
stop_all_watchers(struct feer_conn *c) { |
|
647
|
45
|
|
|
|
|
|
stop_read_watcher(c); |
|
648
|
45
|
|
|
|
|
|
stop_read_timer(c); |
|
649
|
45
|
|
|
|
|
|
stop_header_timer(c); |
|
650
|
45
|
|
|
|
|
|
stop_write_watcher(c); |
|
651
|
45
|
|
|
|
|
|
stop_write_timer(c); |
|
652
|
45
|
|
|
|
|
|
} |
|
653
|
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
static void |
|
655
|
1577
|
|
|
|
|
|
feer_conn_set_busy(struct feer_conn *c) |
|
656
|
|
|
|
|
|
|
{ |
|
657
|
1577
|
100
|
|
|
|
|
if (c->idle_rinq_node) { |
|
658
|
24
|
|
|
|
|
|
rinq_remove(&c->server->idle_keepalive_rinq, c->idle_rinq_node); |
|
659
|
24
|
|
|
|
|
|
c->idle_rinq_node = NULL; |
|
660
|
|
|
|
|
|
|
} |
|
661
|
1577
|
|
|
|
|
|
} |
|
662
|
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
static void |
|
664
|
25
|
|
|
|
|
|
feer_conn_set_idle(struct feer_conn *c) |
|
665
|
|
|
|
|
|
|
{ |
|
666
|
25
|
50
|
|
|
|
|
if (c->idle_rinq_node) return; // already idle |
|
667
|
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
struct rinq *node; |
|
669
|
25
|
50
|
|
|
|
|
RINQ_NEW(node, c); |
|
|
|
0
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
|
|
671
|
25
|
|
|
|
|
|
struct rinq **head = &c->server->idle_keepalive_rinq; |
|
672
|
25
|
100
|
|
|
|
|
if (*head == NULL) { |
|
673
|
22
|
|
|
|
|
|
*head = node; |
|
674
|
|
|
|
|
|
|
} else { |
|
675
|
3
|
|
|
|
|
|
node->next = *head; |
|
676
|
3
|
|
|
|
|
|
node->prev = (*head)->prev; |
|
677
|
3
|
|
|
|
|
|
node->next->prev = node->prev->next = node; |
|
678
|
|
|
|
|
|
|
} |
|
679
|
25
|
|
|
|
|
|
c->idle_rinq_node = node; |
|
680
|
|
|
|
|
|
|
trace("conn fd=%d is now idle (added to MRU)\n", c->fd); |
|
681
|
|
|
|
|
|
|
} |
|
682
|
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
static int |
|
684
|
8
|
|
|
|
|
|
feer_server_recycle_idle_conn(struct feer_server *srvr) |
|
685
|
|
|
|
|
|
|
{ |
|
686
|
8
|
100
|
|
|
|
|
if (!srvr->idle_keepalive_rinq) return 0; |
|
687
|
|
|
|
|
|
|
|
|
688
|
1
|
|
|
|
|
|
struct feer_conn *c = (struct feer_conn *)rinq_shift(&srvr->idle_keepalive_rinq); |
|
689
|
1
|
50
|
|
|
|
|
if (unlikely(!c)) return 0; |
|
690
|
|
|
|
|
|
|
|
|
691
|
1
|
|
|
|
|
|
c->idle_rinq_node = NULL; // node was shifted |
|
692
|
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
trace("recycling idle keepalive conn fd=%d to make room for new accept\n", c->fd); |
|
694
|
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
// Gracefully shut down the idle connection. |
|
696
|
|
|
|
|
|
|
// Guard: after setup_accepted_conn drops the base refcount, connections |
|
697
|
|
|
|
|
|
|
// are alive only via watcher refcounts. stop_all_watchers can drop |
|
698
|
|
|
|
|
|
|
// refcount to 0 → DESTROY fires before safe_close_conn. |
|
699
|
1
|
|
|
|
|
|
SvREFCNT_inc_void_NN(c->self); |
|
700
|
1
|
|
|
|
|
|
stop_all_watchers(c); |
|
701
|
1
|
|
|
|
|
|
safe_close_conn(c, "recycled for new connection"); |
|
702
|
1
|
|
|
|
|
|
change_responding_state(c, RESPOND_SHUTDOWN); |
|
703
|
1
|
|
|
|
|
|
SvREFCNT_dec(c->self); |
|
704
|
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
// active_conns will be decremented in DESTROY when refcount drops. |
|
706
|
1
|
|
|
|
|
|
return 1; |
|
707
|
|
|
|
|
|
|
} |
|
708
|
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
static void |
|
711
|
329
|
|
|
|
|
|
process_request_ready_rinq (struct feer_server *server) |
|
712
|
|
|
|
|
|
|
{ |
|
713
|
811
|
100
|
|
|
|
|
while (server->request_ready_rinq) { |
|
714
|
|
|
|
|
|
|
struct feer_conn *c = |
|
715
|
482
|
|
|
|
|
|
(struct feer_conn *)rinq_shift(&server->request_ready_rinq); |
|
716
|
482
|
50
|
|
|
|
|
if (unlikely(!c)) break; |
|
717
|
|
|
|
|
|
|
|
|
718
|
482
|
|
|
|
|
|
call_request_callback(c); |
|
719
|
|
|
|
|
|
|
|
|
720
|
482
|
100
|
|
|
|
|
if (likely(c->wbuf_rinq)) { |
|
721
|
|
|
|
|
|
|
// this was deferred until after the perl callback |
|
722
|
460
|
|
|
|
|
|
conn_write_ready(c); |
|
723
|
|
|
|
|
|
|
} |
|
724
|
|
|
|
|
|
|
#ifdef FEERSUM_HAS_H2 |
|
725
|
|
|
|
|
|
|
else if (c->is_h2_stream) { |
|
726
|
|
|
|
|
|
|
// H2 pseudo-conns don't use wbuf_rinq; flush deferred session_send |
|
727
|
|
|
|
|
|
|
struct feer_h2_stream *stream = |
|
728
|
|
|
|
|
|
|
(struct feer_h2_stream *)c->read_ev_timer.data; |
|
729
|
|
|
|
|
|
|
if (stream && stream->parent) { |
|
730
|
|
|
|
|
|
|
struct feer_conn *parent = stream->parent; |
|
731
|
|
|
|
|
|
|
SvREFCNT_inc_void_NN(parent->self); |
|
732
|
|
|
|
|
|
|
feer_h2_session_send(parent); |
|
733
|
|
|
|
|
|
|
h2_check_stream_poll_cbs(aTHX_ parent); |
|
734
|
|
|
|
|
|
|
SvREFCNT_dec(parent->self); |
|
735
|
|
|
|
|
|
|
} |
|
736
|
|
|
|
|
|
|
} |
|
737
|
|
|
|
|
|
|
#endif |
|
738
|
482
|
|
|
|
|
|
SvREFCNT_dec(c->self); // for the rinq |
|
739
|
|
|
|
|
|
|
} |
|
740
|
329
|
|
|
|
|
|
} |
|
741
|
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
static void |
|
743
|
138
|
|
|
|
|
|
prepare_cb (EV_P_ ev_prepare *w, int revents) |
|
744
|
|
|
|
|
|
|
{ |
|
745
|
138
|
|
|
|
|
|
struct feer_server *srvr = (struct feer_server *)w->data; |
|
746
|
|
|
|
|
|
|
int i; |
|
747
|
|
|
|
|
|
|
|
|
748
|
138
|
50
|
|
|
|
|
if (unlikely(revents & EV_ERROR)) { |
|
749
|
0
|
|
|
|
|
|
trouble("EV error in prepare, revents=0x%08x\n", revents); |
|
750
|
0
|
|
|
|
|
|
ev_break(EV_A, EVBREAK_ALL); |
|
751
|
0
|
|
|
|
|
|
return; |
|
752
|
|
|
|
|
|
|
} |
|
753
|
|
|
|
|
|
|
|
|
754
|
138
|
50
|
|
|
|
|
if (!srvr->shutting_down) { |
|
755
|
357
|
100
|
|
|
|
|
for (i = 0; i < srvr->n_listeners; i++) { |
|
756
|
219
|
|
|
|
|
|
struct feer_listen *lsnr = &srvr->listeners[i]; |
|
757
|
219
|
100
|
|
|
|
|
if (!ev_is_active(&lsnr->accept_w) && !lsnr->paused) { |
|
|
|
50
|
|
|
|
|
|
|
758
|
146
|
|
|
|
|
|
ev_io_start(EV_A, &lsnr->accept_w); |
|
759
|
|
|
|
|
|
|
} |
|
760
|
|
|
|
|
|
|
} |
|
761
|
|
|
|
|
|
|
} |
|
762
|
138
|
|
|
|
|
|
ev_prepare_stop(EV_A, w); |
|
763
|
|
|
|
|
|
|
} |
|
764
|
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
static void |
|
766
|
4534
|
|
|
|
|
|
check_cb (EV_P_ ev_check *w, int revents) |
|
767
|
|
|
|
|
|
|
{ |
|
768
|
4534
|
|
|
|
|
|
struct feer_server *server = (struct feer_server *)w->data; |
|
769
|
|
|
|
|
|
|
|
|
770
|
4534
|
50
|
|
|
|
|
if (unlikely(revents & EV_ERROR)) { |
|
771
|
0
|
|
|
|
|
|
trouble("EV error in check, revents=0x%08x\n", revents); |
|
772
|
0
|
|
|
|
|
|
ev_break(EV_A, EVBREAK_ALL); |
|
773
|
0
|
|
|
|
|
|
return; |
|
774
|
|
|
|
|
|
|
} |
|
775
|
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
trace3("check! head=%p\n", server->request_ready_rinq); |
|
777
|
4534
|
100
|
|
|
|
|
if (server->request_ready_rinq) |
|
778
|
329
|
|
|
|
|
|
process_request_ready_rinq(server); |
|
779
|
|
|
|
|
|
|
} |
|
780
|
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
static void |
|
782
|
326
|
|
|
|
|
|
idle_cb (EV_P_ ev_idle *w, int revents) |
|
783
|
|
|
|
|
|
|
{ |
|
784
|
326
|
|
|
|
|
|
struct feer_server *server = (struct feer_server *)w->data; |
|
785
|
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
trace("idle_cb called, revents=0x%08x\n", revents); |
|
787
|
326
|
50
|
|
|
|
|
if (unlikely(revents & EV_ERROR)) { |
|
788
|
0
|
|
|
|
|
|
trouble("EV error in idle, revents=0x%08x\n", revents); |
|
789
|
0
|
|
|
|
|
|
ev_break(EV_A, EVBREAK_ALL); |
|
790
|
0
|
|
|
|
|
|
return; |
|
791
|
|
|
|
|
|
|
} |
|
792
|
|
|
|
|
|
|
trace3("idle! head=%p\n", server->request_ready_rinq); |
|
793
|
326
|
50
|
|
|
|
|
if (server->request_ready_rinq) |
|
794
|
0
|
|
|
|
|
|
process_request_ready_rinq(server); |
|
795
|
326
|
|
|
|
|
|
ev_idle_stop(EV_A, w); |
|
796
|
|
|
|
|
|
|
} |
|
797
|
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
/* |
|
799
|
|
|
|
|
|
|
* Shared keepalive-or-close logic for both plain and TLS write paths. |
|
800
|
|
|
|
|
|
|
* read_cb is try_conn_read (plain) or try_tls_conn_read (TLS). |
|
801
|
|
|
|
|
|
|
*/ |
|
802
|
|
|
|
|
|
|
static void |
|
803
|
654
|
|
|
|
|
|
handle_keepalive_or_close(struct feer_conn *c, conn_read_cb_t read_cb) |
|
804
|
|
|
|
|
|
|
{ |
|
805
|
654
|
|
|
|
|
|
stop_write_watcher(c); |
|
806
|
654
|
|
|
|
|
|
stop_write_timer(c); |
|
807
|
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
/* If request had a Content-Length body and the app didn't consume it all, |
|
809
|
|
|
|
|
|
|
* rbuf contains unread body bytes mixed with any pipelined data. |
|
810
|
|
|
|
|
|
|
* Force-close to prevent pipeline desync (body bytes parsed as HTTP). */ |
|
811
|
654
|
100
|
|
|
|
|
if (c->is_keepalive && c->expected_cl > 0 && c->rbuf) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
812
|
9
|
|
|
|
|
|
ssize_t consumed = c->received_cl - (ssize_t)SvCUR(c->rbuf); |
|
813
|
9
|
100
|
|
|
|
|
if (consumed < c->expected_cl) { |
|
814
|
|
|
|
|
|
|
trace("body not consumed fd=%d consumed=%"Ssz_df" expected=%"Ssz_df"\n", |
|
815
|
|
|
|
|
|
|
c->fd, (Ssz)consumed, (Ssz)c->expected_cl); |
|
816
|
2
|
|
|
|
|
|
c->is_keepalive = 0; |
|
817
|
|
|
|
|
|
|
} |
|
818
|
|
|
|
|
|
|
} |
|
819
|
|
|
|
|
|
|
|
|
820
|
654
|
100
|
|
|
|
|
if (c->is_keepalive) { |
|
821
|
126
|
|
|
|
|
|
change_responding_state(c, RESPOND_NOT_STARTED); |
|
822
|
126
|
|
|
|
|
|
change_receiving_state(c, RECEIVE_WAIT); |
|
823
|
126
|
|
|
|
|
|
STRLEN pipelined = 0; |
|
824
|
126
|
100
|
|
|
|
|
if (c->rbuf) { pipelined = SvCUR(c->rbuf); } |
|
825
|
126
|
50
|
|
|
|
|
if (likely(c->req)) { |
|
826
|
148
|
100
|
|
|
|
|
if (likely(pipelined == 0) && c->req->buf && c->rbuf) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
827
|
22
|
|
|
|
|
|
SV *tmp = c->rbuf; |
|
828
|
22
|
|
|
|
|
|
c->rbuf = c->req->buf; |
|
829
|
22
|
|
|
|
|
|
c->req->buf = NULL; |
|
830
|
22
|
|
|
|
|
|
SvCUR_set(c->rbuf, 0); |
|
831
|
22
|
|
|
|
|
|
SvREFCNT_dec(tmp); |
|
832
|
104
|
50
|
|
|
|
|
} else if (c->req->buf) { |
|
833
|
104
|
|
|
|
|
|
SvREFCNT_dec(c->req->buf); |
|
834
|
104
|
|
|
|
|
|
c->req->buf = NULL; |
|
835
|
|
|
|
|
|
|
} |
|
836
|
126
|
|
|
|
|
|
free_request(c); |
|
837
|
|
|
|
|
|
|
} |
|
838
|
126
|
100
|
|
|
|
|
if (unlikely(pipelined > 0 && c->is_http11)) { |
|
|
|
50
|
|
|
|
|
|
|
839
|
101
|
|
|
|
|
|
c->pipelined = pipelined; |
|
840
|
101
|
50
|
|
|
|
|
if (c->pipeline_depth <= MAX_PIPELINE_DEPTH) { |
|
841
|
101
|
|
|
|
|
|
c->pipeline_depth++; |
|
842
|
101
|
|
|
|
|
|
restart_header_timer(c); |
|
843
|
101
|
|
|
|
|
|
read_cb(feersum_ev_loop, &c->read_ev_io, 0); |
|
844
|
101
|
|
|
|
|
|
c->pipeline_depth--; |
|
845
|
|
|
|
|
|
|
} else { |
|
846
|
|
|
|
|
|
|
trace("pipeline depth limit reached on %d\n", c->fd); |
|
847
|
0
|
|
|
|
|
|
start_read_watcher(c); |
|
848
|
0
|
|
|
|
|
|
restart_read_timer(c); |
|
849
|
0
|
|
|
|
|
|
restart_header_timer(c); |
|
850
|
0
|
|
|
|
|
|
feer_conn_set_idle(c); |
|
851
|
|
|
|
|
|
|
} |
|
852
|
|
|
|
|
|
|
} else { |
|
853
|
25
|
|
|
|
|
|
c->pipelined = 0; |
|
854
|
25
|
|
|
|
|
|
start_read_watcher(c); |
|
855
|
25
|
|
|
|
|
|
restart_read_timer(c); |
|
856
|
25
|
|
|
|
|
|
restart_header_timer(c); |
|
857
|
25
|
|
|
|
|
|
feer_conn_set_idle(c); |
|
858
|
|
|
|
|
|
|
} |
|
859
|
|
|
|
|
|
|
} else { |
|
860
|
528
|
50
|
|
|
|
|
if (c->responding != RESPOND_SHUTDOWN) |
|
861
|
0
|
|
|
|
|
|
change_responding_state(c, RESPOND_SHUTDOWN); |
|
862
|
528
|
|
|
|
|
|
safe_close_conn(c, "close at write shutdown"); |
|
863
|
|
|
|
|
|
|
} |
|
864
|
654
|
|
|
|
|
|
} |
|
865
|
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
static void |
|
867
|
658
|
|
|
|
|
|
try_conn_write(EV_P_ struct ev_io *w, int revents) |
|
868
|
|
|
|
|
|
|
{ |
|
869
|
658
|
|
|
|
|
|
dCONN; |
|
870
|
|
|
|
|
|
|
unsigned i; |
|
871
|
|
|
|
|
|
|
struct iomatrix *m; |
|
872
|
|
|
|
|
|
|
|
|
873
|
658
|
|
|
|
|
|
SvREFCNT_inc_void_NN(c->self); |
|
874
|
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
// if it's marked writeable EV suggests we simply try write to it. |
|
876
|
|
|
|
|
|
|
// Otherwise it is stopped and we should ditch this connection. |
|
877
|
658
|
50
|
|
|
|
|
if (unlikely(revents & EV_ERROR && !(revents & EV_WRITE))) { |
|
|
|
0
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
trace("EV error on write, fd=%d revents=0x%08x\n", w->fd, revents); |
|
879
|
0
|
|
|
|
|
|
change_responding_state(c, RESPOND_SHUTDOWN); |
|
880
|
0
|
|
|
|
|
|
goto try_write_finished; |
|
881
|
|
|
|
|
|
|
} |
|
882
|
|
|
|
|
|
|
|
|
883
|
658
|
100
|
|
|
|
|
if (unlikely(!c->wbuf_rinq)) { |
|
884
|
23
|
100
|
|
|
|
|
if (unlikely(c->responding >= RESPOND_SHUTDOWN)) |
|
885
|
3
|
|
|
|
|
|
goto try_write_finished; |
|
886
|
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
#ifdef __linux__ |
|
888
|
|
|
|
|
|
|
// Check for sendfile pending (headers already sent) |
|
889
|
20
|
50
|
|
|
|
|
if (c->sendfile_fd >= 0) |
|
890
|
0
|
|
|
|
|
|
goto try_sendfile; |
|
891
|
|
|
|
|
|
|
#endif |
|
892
|
|
|
|
|
|
|
|
|
893
|
20
|
50
|
|
|
|
|
if (!c->poll_write_cb) { |
|
894
|
|
|
|
|
|
|
// no callback and no data: wait for app to push to us. |
|
895
|
0
|
0
|
|
|
|
|
if (c->responding == RESPOND_STREAMING) |
|
896
|
0
|
|
|
|
|
|
goto try_write_paused; |
|
897
|
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
trace("tried to write with an empty buffer %d resp=%d\n",w->fd,c->responding); |
|
899
|
0
|
|
|
|
|
|
change_responding_state(c, RESPOND_SHUTDOWN); |
|
900
|
0
|
|
|
|
|
|
goto try_write_finished; |
|
901
|
|
|
|
|
|
|
} |
|
902
|
|
|
|
|
|
|
|
|
903
|
20
|
100
|
|
|
|
|
if (c->poll_write_cb_is_io_handle) |
|
904
|
11
|
|
|
|
|
|
pump_io_handle(c); |
|
905
|
|
|
|
|
|
|
else |
|
906
|
9
|
|
|
|
|
|
call_poll_callback(c, 1); |
|
907
|
|
|
|
|
|
|
|
|
908
|
|
|
|
|
|
|
// callback didn't write anything: |
|
909
|
20
|
50
|
|
|
|
|
if (unlikely(!c->wbuf_rinq)) goto try_write_again; |
|
910
|
|
|
|
|
|
|
} |
|
911
|
|
|
|
|
|
|
// Low-water-mark: buffer not empty but below threshold — refill before writing |
|
912
|
635
|
100
|
|
|
|
|
else if (c->cached_wbuf_low_water > 0 |
|
913
|
1
|
50
|
|
|
|
|
&& c->wbuf_len <= c->cached_wbuf_low_water |
|
914
|
1
|
50
|
|
|
|
|
&& c->responding == RESPOND_STREAMING && c->poll_write_cb) { |
|
|
|
50
|
|
|
|
|
|
|
915
|
1
|
50
|
|
|
|
|
if (c->poll_write_cb_is_io_handle) |
|
916
|
0
|
|
|
|
|
|
pump_io_handle(c); |
|
917
|
|
|
|
|
|
|
else |
|
918
|
1
|
|
|
|
|
|
call_poll_callback(c, 1); |
|
919
|
|
|
|
|
|
|
} |
|
920
|
|
|
|
|
|
|
|
|
921
|
655
|
|
|
|
|
|
try_write_again_immediately: |
|
922
|
|
|
|
|
|
|
#if defined(__linux__) && defined(FEERSUM_TCP_CORK) |
|
923
|
|
|
|
|
|
|
// Cork socket when writing headers before sendfile for optimal packet framing |
|
924
|
655
|
100
|
|
|
|
|
if (c->sendfile_fd >= 0) |
|
925
|
3
|
|
|
|
|
|
set_cork(c, 1); |
|
926
|
|
|
|
|
|
|
#endif |
|
927
|
655
|
|
|
|
|
|
m = (struct iomatrix *)c->wbuf_rinq->ref; |
|
928
|
|
|
|
|
|
|
#if DEBUG >= 2 |
|
929
|
|
|
|
|
|
|
warn("going to write to %d:\n",c->fd); |
|
930
|
|
|
|
|
|
|
for (i=0; i < m->count; i++) { |
|
931
|
|
|
|
|
|
|
fprintf(stderr,"%.*s", |
|
932
|
|
|
|
|
|
|
(int)m->iov[i].iov_len, (char*)m->iov[i].iov_base); |
|
933
|
|
|
|
|
|
|
} |
|
934
|
|
|
|
|
|
|
#endif |
|
935
|
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
trace("going to write %d off=%d count=%d\n", w->fd, m->offset, m->count); |
|
937
|
655
|
|
|
|
|
|
errno = 0; |
|
938
|
655
|
|
|
|
|
|
int iov_count = m->count - m->offset; |
|
939
|
|
|
|
|
|
|
ssize_t wrote; |
|
940
|
655
|
100
|
|
|
|
|
if (iov_count == 1) { |
|
941
|
|
|
|
|
|
|
// Single element: write() is slightly faster than writev() |
|
942
|
209
|
|
|
|
|
|
wrote = write(w->fd, m->iov[m->offset].iov_base, m->iov[m->offset].iov_len); |
|
943
|
|
|
|
|
|
|
} else { |
|
944
|
446
|
|
|
|
|
|
wrote = writev(w->fd, &m->iov[m->offset], iov_count); |
|
945
|
|
|
|
|
|
|
} |
|
946
|
|
|
|
|
|
|
trace("wrote %"Ssz_df" bytes to %d, errno=%d\n", (Ssz)wrote, w->fd, errno); |
|
947
|
|
|
|
|
|
|
|
|
948
|
655
|
50
|
|
|
|
|
if (unlikely(wrote <= 0)) { |
|
949
|
0
|
0
|
|
|
|
|
if (likely(errno == EAGAIN || errno == EINTR)) |
|
|
|
0
|
|
|
|
|
|
|
950
|
0
|
|
|
|
|
|
goto try_write_again; |
|
951
|
0
|
|
|
|
|
|
trouble("try_conn_write fd=%d: %s\n", w->fd, strerror(errno)); |
|
952
|
0
|
0
|
|
|
|
|
CLOSE_SENDFILE_FD(c); |
|
|
|
0
|
|
|
|
|
|
|
953
|
0
|
|
|
|
|
|
set_cork(c, 0); |
|
954
|
0
|
|
|
|
|
|
stop_write_timer(c); |
|
955
|
0
|
|
|
|
|
|
change_responding_state(c, RESPOND_SHUTDOWN); |
|
956
|
0
|
|
|
|
|
|
goto try_write_finished; |
|
957
|
|
|
|
|
|
|
} |
|
958
|
|
|
|
|
|
|
|
|
959
|
655
|
|
|
|
|
|
c->wbuf_len -= wrote; |
|
960
|
655
|
|
|
|
|
|
restart_write_timer(c); |
|
961
|
|
|
|
|
|
|
|
|
962
|
655
|
|
|
|
|
|
bool consume = 1; |
|
963
|
5710
|
100
|
|
|
|
|
for (i = m->offset; i < m->count && consume; i++) { |
|
|
|
50
|
|
|
|
|
|
|
964
|
5055
|
|
|
|
|
|
struct iovec *v = &m->iov[i]; |
|
965
|
5055
|
50
|
|
|
|
|
if (unlikely(v->iov_len > wrote)) { |
|
966
|
|
|
|
|
|
|
trace3("offset vector %d base=%p len=%"Sz_uf"\n", |
|
967
|
|
|
|
|
|
|
w->fd, v->iov_base, (Sz)v->iov_len); |
|
968
|
0
|
|
|
|
|
|
v->iov_base = (char*)v->iov_base + wrote; |
|
969
|
0
|
|
|
|
|
|
v->iov_len -= wrote; |
|
970
|
|
|
|
|
|
|
// don't consume any more: |
|
971
|
0
|
|
|
|
|
|
consume = 0; |
|
972
|
|
|
|
|
|
|
} |
|
973
|
|
|
|
|
|
|
else { |
|
974
|
|
|
|
|
|
|
trace3("consume vector %d base=%p len=%"Sz_uf" sv=%p\n", |
|
975
|
|
|
|
|
|
|
w->fd, v->iov_base, (Sz)v->iov_len, m->sv[i]); |
|
976
|
5055
|
|
|
|
|
|
wrote -= v->iov_len; |
|
977
|
5055
|
|
|
|
|
|
m->offset++; |
|
978
|
5055
|
100
|
|
|
|
|
if (m->sv[i]) { |
|
979
|
2424
|
|
|
|
|
|
SvREFCNT_dec(m->sv[i]); |
|
980
|
2424
|
|
|
|
|
|
m->sv[i] = NULL; |
|
981
|
|
|
|
|
|
|
} |
|
982
|
|
|
|
|
|
|
} |
|
983
|
|
|
|
|
|
|
} |
|
984
|
|
|
|
|
|
|
|
|
985
|
655
|
50
|
|
|
|
|
if (likely(m->offset >= m->count)) { |
|
986
|
|
|
|
|
|
|
trace2("all done with iomatrix %d state=%d\n",w->fd,c->responding); |
|
987
|
655
|
|
|
|
|
|
rinq_shift(&c->wbuf_rinq); |
|
988
|
655
|
50
|
|
|
|
|
IOMATRIX_FREE(m); |
|
989
|
655
|
50
|
|
|
|
|
if (!c->wbuf_rinq) { |
|
990
|
|
|
|
|
|
|
#ifdef __linux__ |
|
991
|
|
|
|
|
|
|
// sendfile pending? do zero-copy file transfer |
|
992
|
655
|
100
|
|
|
|
|
if (c->sendfile_fd >= 0) |
|
993
|
3
|
|
|
|
|
|
goto try_sendfile; |
|
994
|
|
|
|
|
|
|
#endif |
|
995
|
652
|
|
|
|
|
|
goto try_write_finished; |
|
996
|
|
|
|
|
|
|
} |
|
997
|
|
|
|
|
|
|
// Low-water-mark: yield to event loop so poll_cb can fire |
|
998
|
0
|
0
|
|
|
|
|
if (c->cached_wbuf_low_water > 0 |
|
999
|
0
|
0
|
|
|
|
|
&& c->wbuf_len <= c->cached_wbuf_low_water |
|
1000
|
0
|
0
|
|
|
|
|
&& c->responding == RESPOND_STREAMING && c->poll_write_cb) { |
|
|
|
0
|
|
|
|
|
|
|
1001
|
0
|
|
|
|
|
|
goto try_write_again; |
|
1002
|
|
|
|
|
|
|
} |
|
1003
|
|
|
|
|
|
|
trace2("write again immediately %d state=%d\n",w->fd,c->responding); |
|
1004
|
0
|
|
|
|
|
|
goto try_write_again_immediately; |
|
1005
|
|
|
|
|
|
|
} |
|
1006
|
|
|
|
|
|
|
// else, fallthrough: |
|
1007
|
|
|
|
|
|
|
trace2("write fallthrough %d state=%d\n",w->fd,c->responding); |
|
1008
|
0
|
|
|
|
|
|
goto try_write_again; |
|
1009
|
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
#ifdef __linux__ |
|
1011
|
3
|
|
|
|
|
|
try_sendfile: |
|
1012
|
|
|
|
|
|
|
{ |
|
1013
|
|
|
|
|
|
|
trace("sendfile %d: fd=%d off=%ld remain=%zu\n", |
|
1014
|
|
|
|
|
|
|
w->fd, c->sendfile_fd, (long)c->sendfile_off, c->sendfile_remain); |
|
1015
|
3
|
|
|
|
|
|
ssize_t sent = sendfile(w->fd, c->sendfile_fd, |
|
1016
|
3
|
|
|
|
|
|
&c->sendfile_off, c->sendfile_remain); |
|
1017
|
3
|
50
|
|
|
|
|
if (sent > 0) { |
|
1018
|
3
|
|
|
|
|
|
c->sendfile_remain -= sent; |
|
1019
|
|
|
|
|
|
|
trace("sendfile sent %zd, remain=%zu\n", sent, c->sendfile_remain); |
|
1020
|
3
|
50
|
|
|
|
|
if (c->sendfile_remain == 0) { |
|
1021
|
3
|
50
|
|
|
|
|
CLOSE_SENDFILE_FD(c); |
|
|
|
50
|
|
|
|
|
|
|
1022
|
3
|
|
|
|
|
|
set_cork(c, 0); |
|
1023
|
3
|
|
|
|
|
|
change_responding_state(c, RESPOND_SHUTDOWN); |
|
1024
|
3
|
|
|
|
|
|
goto try_write_finished; |
|
1025
|
|
|
|
|
|
|
} |
|
1026
|
|
|
|
|
|
|
// More to send, wait for socket to be writable again |
|
1027
|
0
|
|
|
|
|
|
goto try_write_again; |
|
1028
|
|
|
|
|
|
|
} |
|
1029
|
0
|
0
|
|
|
|
|
else if (sent == 0) { |
|
1030
|
|
|
|
|
|
|
// EOF on file (shouldn't happen if sendfile_remain was correct) |
|
1031
|
0
|
0
|
|
|
|
|
CLOSE_SENDFILE_FD(c); |
|
|
|
0
|
|
|
|
|
|
|
1032
|
0
|
|
|
|
|
|
set_cork(c, 0); |
|
1033
|
0
|
0
|
|
|
|
|
if (c->responding == RESPOND_STREAMING) { |
|
1034
|
0
|
|
|
|
|
|
change_responding_state(c, RESPOND_SHUTDOWN); |
|
1035
|
|
|
|
|
|
|
} |
|
1036
|
0
|
|
|
|
|
|
goto try_write_finished; |
|
1037
|
|
|
|
|
|
|
} |
|
1038
|
|
|
|
|
|
|
else { |
|
1039
|
|
|
|
|
|
|
// sent < 0, error |
|
1040
|
0
|
0
|
|
|
|
|
if (errno == EAGAIN || errno == EINTR) { |
|
|
|
0
|
|
|
|
|
|
|
1041
|
|
|
|
|
|
|
// Socket not ready, wait |
|
1042
|
0
|
|
|
|
|
|
goto try_write_again; |
|
1043
|
|
|
|
|
|
|
} |
|
1044
|
|
|
|
|
|
|
// Real error |
|
1045
|
0
|
|
|
|
|
|
trouble("sendfile fd=%d: %s\n", c->fd, strerror(errno)); |
|
1046
|
0
|
0
|
|
|
|
|
CLOSE_SENDFILE_FD(c); |
|
|
|
0
|
|
|
|
|
|
|
1047
|
0
|
|
|
|
|
|
set_cork(c, 0); |
|
1048
|
0
|
|
|
|
|
|
change_responding_state(c, RESPOND_SHUTDOWN); |
|
1049
|
0
|
|
|
|
|
|
goto try_write_finished; |
|
1050
|
|
|
|
|
|
|
} |
|
1051
|
|
|
|
|
|
|
} |
|
1052
|
|
|
|
|
|
|
#endif |
|
1053
|
|
|
|
|
|
|
|
|
1054
|
14
|
|
|
|
|
|
try_write_again: |
|
1055
|
|
|
|
|
|
|
trace("write again %d state=%d\n",w->fd,c->responding); |
|
1056
|
14
|
|
|
|
|
|
start_write_watcher(c); |
|
1057
|
14
|
|
|
|
|
|
goto try_write_cleanup; |
|
1058
|
|
|
|
|
|
|
|
|
1059
|
658
|
|
|
|
|
|
try_write_finished: |
|
1060
|
|
|
|
|
|
|
// should always be responding, but just in case |
|
1061
|
658
|
|
|
|
|
|
switch(c->responding) { |
|
1062
|
0
|
|
|
|
|
|
case RESPOND_NOT_STARTED: |
|
1063
|
|
|
|
|
|
|
// the write watcher shouldn't ever get called before starting to |
|
1064
|
|
|
|
|
|
|
// respond. Shut it down if it does. |
|
1065
|
|
|
|
|
|
|
trace("unexpected try_write when response not started %d\n",c->fd); |
|
1066
|
0
|
|
|
|
|
|
goto try_write_shutdown; |
|
1067
|
0
|
|
|
|
|
|
case RESPOND_NORMAL: |
|
1068
|
0
|
|
|
|
|
|
goto try_write_shutdown; |
|
1069
|
50
|
|
|
|
|
|
case RESPOND_STREAMING: |
|
1070
|
50
|
100
|
|
|
|
|
if (c->poll_write_cb) goto try_write_again; |
|
1071
|
36
|
|
|
|
|
|
else goto try_write_paused; |
|
1072
|
608
|
|
|
|
|
|
case RESPOND_SHUTDOWN: |
|
1073
|
608
|
|
|
|
|
|
goto try_write_shutdown; |
|
1074
|
0
|
|
|
|
|
|
default: |
|
1075
|
0
|
|
|
|
|
|
goto try_write_cleanup; |
|
1076
|
|
|
|
|
|
|
} |
|
1077
|
|
|
|
|
|
|
|
|
1078
|
36
|
|
|
|
|
|
try_write_paused: |
|
1079
|
|
|
|
|
|
|
trace3("write PAUSED %d, refcnt=%d, state=%d\n", c->fd, SvREFCNT(c->self), c->responding); |
|
1080
|
36
|
|
|
|
|
|
stop_write_watcher(c); |
|
1081
|
36
|
|
|
|
|
|
stop_write_timer(c); |
|
1082
|
36
|
|
|
|
|
|
goto try_write_cleanup; |
|
1083
|
|
|
|
|
|
|
|
|
1084
|
608
|
|
|
|
|
|
try_write_shutdown: |
|
1085
|
608
|
|
|
|
|
|
handle_keepalive_or_close(c, try_conn_read); |
|
1086
|
|
|
|
|
|
|
|
|
1087
|
658
|
|
|
|
|
|
try_write_cleanup: |
|
1088
|
658
|
|
|
|
|
|
SvREFCNT_dec(c->self); |
|
1089
|
658
|
|
|
|
|
|
return; |
|
1090
|
|
|
|
|
|
|
} |
|
1091
|
|
|
|
|
|
|
|
|
1092
|
|
|
|
|
|
|
// Parse PROXY protocol v1 text header |
|
1093
|
|
|
|
|
|
|
// Format: "PROXY TCP4|TCP6|UNKNOWN src_addr dst_addr src_port dst_port\r\n" |
|
1094
|
|
|
|
|
|
|
// Returns: bytes consumed on success, -1 on error, -2 if need more data |
|
1095
|
|
|
|
|
|
|
static int |
|
1096
|
151
|
|
|
|
|
|
parse_proxy_v1(struct feer_conn *c) |
|
1097
|
|
|
|
|
|
|
{ |
|
1098
|
151
|
|
|
|
|
|
char *buf = SvPVX(c->rbuf); |
|
1099
|
151
|
|
|
|
|
|
STRLEN len = SvCUR(c->rbuf); |
|
1100
|
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
// Need at least "PROXY \r\n" (minimum valid line) |
|
1102
|
151
|
100
|
|
|
|
|
if (len < 8) |
|
1103
|
21
|
|
|
|
|
|
return -2; // need more data |
|
1104
|
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
// Verify prefix |
|
1106
|
130
|
50
|
|
|
|
|
if (memcmp(buf, PROXY_V1_PREFIX, PROXY_V1_PREFIX_LEN) != 0) |
|
1107
|
0
|
|
|
|
|
|
return -1; // invalid |
|
1108
|
|
|
|
|
|
|
|
|
1109
|
|
|
|
|
|
|
// Find CRLF (max 108 bytes total) |
|
1110
|
130
|
|
|
|
|
|
char *crlf = NULL; |
|
1111
|
130
|
|
|
|
|
|
STRLEN search_len = len > PROXY_V1_MAX_LINE ? PROXY_V1_MAX_LINE : len; |
|
1112
|
|
|
|
|
|
|
STRLEN i; |
|
1113
|
2949
|
100
|
|
|
|
|
for (i = PROXY_V1_PREFIX_LEN; i < search_len - 1; i++) { |
|
1114
|
2846
|
100
|
|
|
|
|
if (buf[i] == '\r' && buf[i+1] == '\n') { |
|
|
|
50
|
|
|
|
|
|
|
1115
|
27
|
|
|
|
|
|
crlf = buf + i; |
|
1116
|
27
|
|
|
|
|
|
break; |
|
1117
|
|
|
|
|
|
|
} |
|
1118
|
|
|
|
|
|
|
} |
|
1119
|
|
|
|
|
|
|
|
|
1120
|
130
|
100
|
|
|
|
|
if (!crlf) { |
|
1121
|
103
|
100
|
|
|
|
|
if (len >= PROXY_V1_MAX_LINE) |
|
1122
|
1
|
|
|
|
|
|
return -1; // line too long, invalid |
|
1123
|
102
|
|
|
|
|
|
return -2; // need more data |
|
1124
|
|
|
|
|
|
|
} |
|
1125
|
|
|
|
|
|
|
|
|
1126
|
27
|
|
|
|
|
|
size_t header_len = (crlf - buf) + 2; // include CRLF |
|
1127
|
|
|
|
|
|
|
|
|
1128
|
|
|
|
|
|
|
// Null-terminate the line for parsing (temporarily) |
|
1129
|
27
|
|
|
|
|
|
char saved = *crlf; |
|
1130
|
27
|
|
|
|
|
|
*crlf = '\0'; |
|
1131
|
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
// Parse protocol family: TCP4, TCP6, or UNKNOWN |
|
1133
|
27
|
|
|
|
|
|
char *p = buf + PROXY_V1_PREFIX_LEN; |
|
1134
|
|
|
|
|
|
|
|
|
1135
|
27
|
100
|
|
|
|
|
if (strncmp(p, "UNKNOWN", 7) == 0 && (p[7] == '\0' || p[7] == ' ')) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1136
|
|
|
|
|
|
|
// UNKNOWN - keep original address (used for health checks) |
|
1137
|
1
|
|
|
|
|
|
*crlf = saved; |
|
1138
|
1
|
|
|
|
|
|
c->proxy_proto_version = 1; |
|
1139
|
|
|
|
|
|
|
trace("PROXY v1 UNKNOWN, keeping original address\n"); |
|
1140
|
1
|
|
|
|
|
|
return (int)header_len; |
|
1141
|
|
|
|
|
|
|
} |
|
1142
|
|
|
|
|
|
|
|
|
1143
|
26
|
|
|
|
|
|
int is_ipv6 = 0; |
|
1144
|
26
|
100
|
|
|
|
|
if (strncmp(p, "TCP4 ", 5) == 0) { |
|
1145
|
23
|
|
|
|
|
|
p += 5; |
|
1146
|
3
|
100
|
|
|
|
|
} else if (strncmp(p, "TCP6 ", 5) == 0) { |
|
1147
|
2
|
|
|
|
|
|
p += 5; |
|
1148
|
2
|
|
|
|
|
|
is_ipv6 = 1; |
|
1149
|
|
|
|
|
|
|
} else { |
|
1150
|
1
|
|
|
|
|
|
*crlf = saved; |
|
1151
|
1
|
|
|
|
|
|
return -1; // unknown protocol |
|
1152
|
|
|
|
|
|
|
} |
|
1153
|
|
|
|
|
|
|
|
|
1154
|
|
|
|
|
|
|
// Parse: src_addr dst_addr src_port dst_port |
|
1155
|
|
|
|
|
|
|
char src_addr[46], dst_addr[46]; // max IPv6 length |
|
1156
|
|
|
|
|
|
|
int src_port, dst_port; |
|
1157
|
|
|
|
|
|
|
|
|
1158
|
|
|
|
|
|
|
// Use sscanf to parse the addresses and ports |
|
1159
|
25
|
50
|
|
|
|
|
if (sscanf(p, "%45s %45s %d %d", src_addr, dst_addr, &src_port, &dst_port) != 4) { |
|
1160
|
0
|
|
|
|
|
|
*crlf = saved; |
|
1161
|
0
|
|
|
|
|
|
return -1; // parse error |
|
1162
|
|
|
|
|
|
|
} |
|
1163
|
|
|
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
// Validate port ranges |
|
1165
|
25
|
50
|
|
|
|
|
if (src_port < 0 || src_port > 65535 || dst_port < 0 || dst_port > 65535) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
1166
|
1
|
|
|
|
|
|
*crlf = saved; |
|
1167
|
1
|
|
|
|
|
|
return -1; |
|
1168
|
|
|
|
|
|
|
} |
|
1169
|
|
|
|
|
|
|
|
|
1170
|
24
|
|
|
|
|
|
*crlf = saved; // restore |
|
1171
|
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
// Update connection's source address |
|
1173
|
24
|
100
|
|
|
|
|
if (is_ipv6) { |
|
1174
|
2
|
|
|
|
|
|
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&c->sa; |
|
1175
|
2
|
|
|
|
|
|
sa6->sin6_family = AF_INET6; |
|
1176
|
2
|
50
|
|
|
|
|
if (inet_pton(AF_INET6, src_addr, &sa6->sin6_addr) != 1) { |
|
1177
|
0
|
|
|
|
|
|
return -1; // invalid address |
|
1178
|
|
|
|
|
|
|
} |
|
1179
|
2
|
|
|
|
|
|
sa6->sin6_port = htons((uint16_t)src_port); |
|
1180
|
|
|
|
|
|
|
} else { |
|
1181
|
22
|
|
|
|
|
|
struct sockaddr_in *sa4 = (struct sockaddr_in *)&c->sa; |
|
1182
|
22
|
|
|
|
|
|
sa4->sin_family = AF_INET; |
|
1183
|
22
|
100
|
|
|
|
|
if (inet_pton(AF_INET, src_addr, &sa4->sin_addr) != 1) { |
|
1184
|
1
|
|
|
|
|
|
return -1; // invalid address |
|
1185
|
|
|
|
|
|
|
} |
|
1186
|
21
|
|
|
|
|
|
sa4->sin_port = htons((uint16_t)src_port); |
|
1187
|
|
|
|
|
|
|
} |
|
1188
|
|
|
|
|
|
|
|
|
1189
|
23
|
|
|
|
|
|
c->proxy_proto_version = 1; |
|
1190
|
23
|
|
|
|
|
|
c->proxy_dst_port = (uint16_t)dst_port; |
|
1191
|
|
|
|
|
|
|
trace("PROXY v1 %s src=%s:%d dst_port=%d\n", is_ipv6 ? "TCP6" : "TCP4", src_addr, src_port, dst_port); |
|
1192
|
23
|
|
|
|
|
|
return (int)header_len; |
|
1193
|
|
|
|
|
|
|
} |
|
1194
|
|
|
|
|
|
|
|
|
1195
|
|
|
|
|
|
|
// Parse PROXY protocol v2 binary header |
|
1196
|
|
|
|
|
|
|
// Returns: bytes consumed on success, -1 on error, -2 if need more data |
|
1197
|
|
|
|
|
|
|
static int |
|
1198
|
117
|
|
|
|
|
|
parse_proxy_v2(struct feer_conn *c) |
|
1199
|
|
|
|
|
|
|
{ |
|
1200
|
117
|
|
|
|
|
|
unsigned char *buf = (unsigned char *)SvPVX(c->rbuf); |
|
1201
|
117
|
|
|
|
|
|
STRLEN len = SvCUR(c->rbuf); |
|
1202
|
|
|
|
|
|
|
|
|
1203
|
|
|
|
|
|
|
// Need at least minimum header (16 bytes) |
|
1204
|
117
|
100
|
|
|
|
|
if (len < PROXY_V2_HDR_MIN) |
|
1205
|
3
|
|
|
|
|
|
return -2; |
|
1206
|
|
|
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
// Verify signature |
|
1208
|
114
|
100
|
|
|
|
|
if (memcmp(buf, PROXY_V2_SIG, PROXY_V2_SIG_LEN) != 0) |
|
1209
|
27
|
|
|
|
|
|
return -1; |
|
1210
|
|
|
|
|
|
|
|
|
1211
|
|
|
|
|
|
|
// Parse version and command (byte 12) |
|
1212
|
87
|
|
|
|
|
|
unsigned char ver_cmd = buf[12]; |
|
1213
|
87
|
|
|
|
|
|
unsigned char version = ver_cmd & 0xF0; |
|
1214
|
87
|
|
|
|
|
|
unsigned char command = ver_cmd & 0x0F; |
|
1215
|
|
|
|
|
|
|
|
|
1216
|
87
|
100
|
|
|
|
|
if (version != PROXY_V2_VERSION) |
|
1217
|
29
|
|
|
|
|
|
return -1; // unsupported version |
|
1218
|
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
// Parse family and protocol (byte 13) |
|
1220
|
58
|
|
|
|
|
|
unsigned char fam_proto = buf[13]; |
|
1221
|
58
|
|
|
|
|
|
unsigned char family = fam_proto & 0xF0; |
|
1222
|
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
// Parse address length (bytes 14-15, big-endian) |
|
1224
|
58
|
|
|
|
|
|
uint16_t addr_len = (buf[14] << 8) | buf[15]; |
|
1225
|
58
|
100
|
|
|
|
|
if (unlikely(addr_len > 4096)) { /* spec allows 65535, cap for sanity */ |
|
1226
|
3
|
|
|
|
|
|
trouble("PROXY v2 addr_len too large: %u\n", addr_len); |
|
1227
|
3
|
|
|
|
|
|
return -1; |
|
1228
|
|
|
|
|
|
|
} |
|
1229
|
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
// Total header length |
|
1231
|
55
|
|
|
|
|
|
size_t total_len = PROXY_V2_HDR_MIN + addr_len; |
|
1232
|
55
|
100
|
|
|
|
|
if (len < total_len) |
|
1233
|
3
|
|
|
|
|
|
return -2; // need more data |
|
1234
|
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
// Handle command |
|
1236
|
52
|
100
|
|
|
|
|
if (command == PROXY_V2_CMD_LOCAL) { |
|
1237
|
|
|
|
|
|
|
// LOCAL command - keep original address (health checks, etc.) |
|
1238
|
1
|
|
|
|
|
|
c->proxy_proto_version = 2; |
|
1239
|
|
|
|
|
|
|
trace("PROXY v2 LOCAL, keeping original address\n"); |
|
1240
|
1
|
|
|
|
|
|
return (int)total_len; |
|
1241
|
|
|
|
|
|
|
} |
|
1242
|
|
|
|
|
|
|
|
|
1243
|
51
|
50
|
|
|
|
|
if (command != PROXY_V2_CMD_PROXY) { |
|
1244
|
0
|
|
|
|
|
|
return -1; // unknown command |
|
1245
|
|
|
|
|
|
|
} |
|
1246
|
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
// PROXY command - update source address |
|
1248
|
51
|
|
|
|
|
|
unsigned char *addr_data = buf + PROXY_V2_HDR_MIN; |
|
1249
|
|
|
|
|
|
|
|
|
1250
|
51
|
100
|
|
|
|
|
if (family == PROXY_V2_FAM_INET) { |
|
1251
|
|
|
|
|
|
|
// IPv4 - need 12 bytes: src_addr(4) + dst_addr(4) + src_port(2) + dst_port(2) |
|
1252
|
49
|
50
|
|
|
|
|
if (addr_len < PROXY_V2_ADDR_V4_LEN) |
|
1253
|
0
|
|
|
|
|
|
return -1; |
|
1254
|
|
|
|
|
|
|
|
|
1255
|
49
|
|
|
|
|
|
struct sockaddr_in *sa4 = (struct sockaddr_in *)&c->sa; |
|
1256
|
49
|
|
|
|
|
|
sa4->sin_family = AF_INET; |
|
1257
|
49
|
|
|
|
|
|
memcpy(&sa4->sin_addr, addr_data, 4); // src addr |
|
1258
|
49
|
|
|
|
|
|
memcpy(&sa4->sin_port, addr_data + 8, 2); // src port (already network order) |
|
1259
|
|
|
|
|
|
|
|
|
1260
|
|
|
|
|
|
|
// Extract dst_port for scheme inference (offset 10, network byte order) |
|
1261
|
|
|
|
|
|
|
uint16_t dst_port_n; |
|
1262
|
49
|
|
|
|
|
|
memcpy(&dst_port_n, addr_data + 10, 2); |
|
1263
|
49
|
|
|
|
|
|
c->proxy_dst_port = ntohs(dst_port_n); |
|
1264
|
|
|
|
|
|
|
|
|
1265
|
|
|
|
|
|
|
trace("PROXY v2 TCP4 src=%d.%d.%d.%d:%d dst_port=%d\n", |
|
1266
|
|
|
|
|
|
|
addr_data[0], addr_data[1], addr_data[2], addr_data[3], |
|
1267
|
|
|
|
|
|
|
ntohs(sa4->sin_port), c->proxy_dst_port); |
|
1268
|
2
|
100
|
|
|
|
|
} else if (family == PROXY_V2_FAM_INET6) { |
|
1269
|
|
|
|
|
|
|
// IPv6 - need 36 bytes: src_addr(16) + dst_addr(16) + src_port(2) + dst_port(2) |
|
1270
|
1
|
50
|
|
|
|
|
if (addr_len < PROXY_V2_ADDR_V6_LEN) |
|
1271
|
0
|
|
|
|
|
|
return -1; |
|
1272
|
|
|
|
|
|
|
|
|
1273
|
1
|
|
|
|
|
|
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&c->sa; |
|
1274
|
1
|
|
|
|
|
|
sa6->sin6_family = AF_INET6; |
|
1275
|
1
|
|
|
|
|
|
memcpy(&sa6->sin6_addr, addr_data, 16); // src addr |
|
1276
|
1
|
|
|
|
|
|
memcpy(&sa6->sin6_port, addr_data + 32, 2); // src port (already network order) |
|
1277
|
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
// Extract dst_port for scheme inference (offset 34, network byte order) |
|
1279
|
|
|
|
|
|
|
uint16_t dst_port_n; |
|
1280
|
1
|
|
|
|
|
|
memcpy(&dst_port_n, addr_data + 34, 2); |
|
1281
|
1
|
|
|
|
|
|
c->proxy_dst_port = ntohs(dst_port_n); |
|
1282
|
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
trace("PROXY v2 TCP6 port=%d dst_port=%d\n", ntohs(sa6->sin6_port), c->proxy_dst_port); |
|
1284
|
1
|
50
|
|
|
|
|
} else if (family == PROXY_V2_FAM_UNSPEC) { |
|
1285
|
|
|
|
|
|
|
// Unspecified - keep original address |
|
1286
|
|
|
|
|
|
|
trace("PROXY v2 UNSPEC, keeping original address\n"); |
|
1287
|
|
|
|
|
|
|
} else { |
|
1288
|
0
|
|
|
|
|
|
return -1; // unsupported family |
|
1289
|
|
|
|
|
|
|
} |
|
1290
|
|
|
|
|
|
|
|
|
1291
|
|
|
|
|
|
|
// Parse TLVs if present |
|
1292
|
51
|
|
|
|
|
|
size_t addr_size = 0; |
|
1293
|
51
|
100
|
|
|
|
|
if (family == PROXY_V2_FAM_INET) addr_size = PROXY_V2_ADDR_V4_LEN; |
|
1294
|
2
|
100
|
|
|
|
|
else if (family == PROXY_V2_FAM_INET6) addr_size = PROXY_V2_ADDR_V6_LEN; |
|
1295
|
|
|
|
|
|
|
|
|
1296
|
51
|
100
|
|
|
|
|
if (addr_len > addr_size) { |
|
1297
|
|
|
|
|
|
|
dTHX; /* Perl API calls below (newHV, newSVpvn, hv_store, etc.) */ |
|
1298
|
|
|
|
|
|
|
// TLVs are present |
|
1299
|
22
|
|
|
|
|
|
unsigned char *tlv_start = addr_data + addr_size; |
|
1300
|
22
|
|
|
|
|
|
size_t tlv_remaining = addr_len - addr_size; |
|
1301
|
|
|
|
|
|
|
|
|
1302
|
|
|
|
|
|
|
// Create hash for TLVs |
|
1303
|
22
|
|
|
|
|
|
HV *tlv_hv = newHV(); |
|
1304
|
|
|
|
|
|
|
|
|
1305
|
26
|
100
|
|
|
|
|
while (tlv_remaining >= 3) { // minimum TLV: 1 type + 2 length |
|
1306
|
22
|
|
|
|
|
|
unsigned char tlv_type = tlv_start[0]; |
|
1307
|
22
|
|
|
|
|
|
uint16_t tlv_len = (tlv_start[1] << 8) | tlv_start[2]; |
|
1308
|
|
|
|
|
|
|
|
|
1309
|
22
|
100
|
|
|
|
|
if (tlv_remaining < 3 + (size_t)tlv_len) { |
|
1310
|
|
|
|
|
|
|
// Malformed TLV - reject the whole PROXY header per spec |
|
1311
|
|
|
|
|
|
|
trace("PROXY v2 malformed TLV: need %u bytes, have %zu\n", |
|
1312
|
|
|
|
|
|
|
tlv_len, tlv_remaining - 3); |
|
1313
|
18
|
|
|
|
|
|
SvREFCNT_dec((SV *)tlv_hv); |
|
1314
|
18
|
|
|
|
|
|
return -1; |
|
1315
|
|
|
|
|
|
|
} |
|
1316
|
|
|
|
|
|
|
|
|
1317
|
|
|
|
|
|
|
// Check for SSL TLV (indicates connection was over SSL/TLS) |
|
1318
|
|
|
|
|
|
|
// PP2_TYPE_SSL requires minimum 5 bytes (client flags + verify) |
|
1319
|
4
|
100
|
|
|
|
|
if (tlv_type == PP2_TYPE_SSL && tlv_len >= 5) { |
|
|
|
50
|
|
|
|
|
|
|
1320
|
2
|
|
|
|
|
|
c->proxy_ssl = 1; |
|
1321
|
|
|
|
|
|
|
trace("PROXY v2 TLV PP2_TYPE_SSL detected\n"); |
|
1322
|
|
|
|
|
|
|
} |
|
1323
|
|
|
|
|
|
|
|
|
1324
|
|
|
|
|
|
|
// Store TLV value (skip NOOP type) |
|
1325
|
4
|
50
|
|
|
|
|
if (tlv_type != PP2_TYPE_NOOP) { |
|
1326
|
4
|
|
|
|
|
|
SV *val = newSVpvn((char *)(tlv_start + 3), tlv_len); |
|
1327
|
|
|
|
|
|
|
char key[8]; |
|
1328
|
4
|
|
|
|
|
|
int key_len = snprintf(key, sizeof(key), "%u", tlv_type); |
|
1329
|
4
|
|
|
|
|
|
hv_store(tlv_hv, key, key_len, val, 0); |
|
1330
|
|
|
|
|
|
|
trace("PROXY v2 TLV type=%u len=%u\n", tlv_type, tlv_len); |
|
1331
|
|
|
|
|
|
|
} |
|
1332
|
|
|
|
|
|
|
|
|
1333
|
4
|
|
|
|
|
|
tlv_start += 3 + (size_t)tlv_len; |
|
1334
|
4
|
|
|
|
|
|
tlv_remaining -= 3 + (size_t)tlv_len; |
|
1335
|
|
|
|
|
|
|
} |
|
1336
|
|
|
|
|
|
|
|
|
1337
|
|
|
|
|
|
|
// Store hash in connection if non-empty |
|
1338
|
4
|
50
|
|
|
|
|
if (HvKEYS(tlv_hv) > 0) { |
|
|
|
100
|
|
|
|
|
|
|
1339
|
3
|
|
|
|
|
|
c->proxy_tlvs = newRV_noinc((SV *)tlv_hv); |
|
1340
|
|
|
|
|
|
|
} else { |
|
1341
|
1
|
|
|
|
|
|
SvREFCNT_dec((SV *)tlv_hv); |
|
1342
|
|
|
|
|
|
|
} |
|
1343
|
|
|
|
|
|
|
} |
|
1344
|
|
|
|
|
|
|
|
|
1345
|
33
|
|
|
|
|
|
c->proxy_proto_version = 2; |
|
1346
|
33
|
|
|
|
|
|
return (int)total_len; |
|
1347
|
|
|
|
|
|
|
} |
|
1348
|
|
|
|
|
|
|
|
|
1349
|
|
|
|
|
|
|
// Try to parse PROXY protocol header (auto-detect v1 or v2) |
|
1350
|
|
|
|
|
|
|
// Returns: bytes consumed on success, -1 on error, -2 if need more data |
|
1351
|
|
|
|
|
|
|
static int |
|
1352
|
283
|
|
|
|
|
|
try_parse_proxy_header(struct feer_conn *c) |
|
1353
|
|
|
|
|
|
|
{ |
|
1354
|
283
|
50
|
|
|
|
|
if (SvCUR(c->rbuf) == 0) |
|
1355
|
0
|
|
|
|
|
|
return -2; // need data |
|
1356
|
|
|
|
|
|
|
|
|
1357
|
283
|
|
|
|
|
|
unsigned char first = ((unsigned char *)SvPVX(c->rbuf))[0]; |
|
1358
|
|
|
|
|
|
|
|
|
1359
|
283
|
100
|
|
|
|
|
if (first == 'P') { |
|
1360
|
151
|
|
|
|
|
|
return parse_proxy_v1(c); |
|
1361
|
132
|
100
|
|
|
|
|
} else if (first == 0x0D) { |
|
1362
|
117
|
|
|
|
|
|
return parse_proxy_v2(c); |
|
1363
|
|
|
|
|
|
|
} else { |
|
1364
|
15
|
|
|
|
|
|
return -1; // neither v1 nor v2 |
|
1365
|
|
|
|
|
|
|
} |
|
1366
|
|
|
|
|
|
|
} |
|
1367
|
|
|
|
|
|
|
|