| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Hypersonic::UA::Protocol::HTTP1; |
|
2
|
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
212613
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
29
|
|
|
4
|
1
|
|
|
1
|
|
3
|
use warnings; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
45
|
|
|
5
|
1
|
|
|
1
|
|
13
|
use 5.010; |
|
|
1
|
|
|
|
|
3
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = '0.15'; |
|
8
|
|
|
|
|
|
|
|
|
9
|
1
|
|
|
1
|
|
6
|
use constant MAX_HEADER_SIZE => 16384; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
48
|
|
|
10
|
1
|
|
|
1
|
|
3
|
use constant DECODE_BUF_SIZE => 1048576; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
1248
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
sub generate_c_code { |
|
13
|
0
|
|
|
0
|
0
|
0
|
my ($class, $builder, $opts) = @_; |
|
14
|
|
|
|
|
|
|
|
|
15
|
0
|
|
|
|
|
0
|
$class->gen_helpers($builder); |
|
16
|
0
|
|
|
|
|
0
|
$class->gen_xs_build_request($builder); |
|
17
|
0
|
|
|
|
|
0
|
$class->gen_xs_parse_status_line($builder); |
|
18
|
0
|
|
|
|
|
0
|
$class->gen_xs_parse_headers($builder); |
|
19
|
0
|
|
|
|
|
0
|
$class->gen_xs_find_body_start($builder); |
|
20
|
0
|
|
|
|
|
0
|
$class->gen_xs_get_content_length($builder); |
|
21
|
0
|
|
|
|
|
0
|
$class->gen_xs_decode_chunked($builder); |
|
22
|
0
|
|
|
|
|
0
|
$class->gen_xs_parse_response($builder); |
|
23
|
|
|
|
|
|
|
} |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
sub get_xs_functions { |
|
26
|
|
|
|
|
|
|
return { |
|
27
|
1
|
|
|
1
|
0
|
1388
|
'Hypersonic::UA::Protocol::HTTP1::build_request' => { source => 'xs_http1_build_request', is_xs_native => 1 }, |
|
28
|
|
|
|
|
|
|
'Hypersonic::UA::Protocol::HTTP1::parse_status_line' => { source => 'xs_http1_parse_status_line', is_xs_native => 1 }, |
|
29
|
|
|
|
|
|
|
'Hypersonic::UA::Protocol::HTTP1::parse_headers' => { source => 'xs_http1_parse_headers', is_xs_native => 1 }, |
|
30
|
|
|
|
|
|
|
'Hypersonic::UA::Protocol::HTTP1::find_body_start' => { source => 'xs_http1_find_body_start', is_xs_native => 1 }, |
|
31
|
|
|
|
|
|
|
'Hypersonic::UA::Protocol::HTTP1::get_content_length' => { source => 'xs_http1_get_content_length', is_xs_native => 1 }, |
|
32
|
|
|
|
|
|
|
'Hypersonic::UA::Protocol::HTTP1::decode_chunked' => { source => 'xs_http1_decode_chunked', is_xs_native => 1 }, |
|
33
|
|
|
|
|
|
|
'Hypersonic::UA::Protocol::HTTP1::parse_response' => { source => 'xs_http1_parse_response', is_xs_native => 1 }, |
|
34
|
|
|
|
|
|
|
}; |
|
35
|
|
|
|
|
|
|
} |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub gen_helpers { |
|
38
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
39
|
|
|
|
|
|
|
|
|
40
|
0
|
|
|
|
|
|
$builder->line("#define HTTP1_MAX_HEADER_SIZE " . MAX_HEADER_SIZE) |
|
41
|
|
|
|
|
|
|
->line("#define HTTP1_DECODE_BUF_SIZE " . DECODE_BUF_SIZE) |
|
42
|
|
|
|
|
|
|
->blank |
|
43
|
|
|
|
|
|
|
->line("static char g_http1_decode_buf[HTTP1_DECODE_BUF_SIZE];") |
|
44
|
|
|
|
|
|
|
->blank; |
|
45
|
|
|
|
|
|
|
|
|
46
|
0
|
|
|
|
|
|
$builder->comment('Parse HTTP/1.x response status line') |
|
47
|
|
|
|
|
|
|
->comment('Returns: status code, or -1 on error') |
|
48
|
|
|
|
|
|
|
->line('static int http1_parse_status_line(const char* buf, size_t len, int* http_minor) {') |
|
49
|
|
|
|
|
|
|
->line(' int i;') |
|
50
|
|
|
|
|
|
|
->line(' if (len < 12) return -1;') |
|
51
|
|
|
|
|
|
|
->line(' if (memcmp(buf, "HTTP/1.", 7) != 0) return -1;') |
|
52
|
|
|
|
|
|
|
->blank |
|
53
|
|
|
|
|
|
|
->line(' *http_minor = buf[7] - \'0\';') |
|
54
|
|
|
|
|
|
|
->line(' if (buf[8] != \' \') return -1;') |
|
55
|
|
|
|
|
|
|
->blank |
|
56
|
|
|
|
|
|
|
->line(' int status = 0;') |
|
57
|
|
|
|
|
|
|
->line(' for (i = 9; i < 12; i++) {') |
|
58
|
|
|
|
|
|
|
->line(' if (buf[i] < \'0\' || buf[i] > \'9\') return -1;') |
|
59
|
|
|
|
|
|
|
->line(' status = status * 10 + (buf[i] - \'0\');') |
|
60
|
|
|
|
|
|
|
->line(' }') |
|
61
|
|
|
|
|
|
|
->line(' return status;') |
|
62
|
|
|
|
|
|
|
->line('}') |
|
63
|
|
|
|
|
|
|
->blank; |
|
64
|
|
|
|
|
|
|
|
|
65
|
0
|
|
|
|
|
|
$builder->comment('Find body start (after \\r\\n\\r\\n)') |
|
66
|
|
|
|
|
|
|
->line('static const char* http1_find_body_start(const char* buf, size_t len) {') |
|
67
|
|
|
|
|
|
|
->line(' if (len < 4) return NULL;') |
|
68
|
|
|
|
|
|
|
->line(' const char* end = buf + len - 3;') |
|
69
|
|
|
|
|
|
|
->line(' for (const char* p = buf; p < end; p++) {') |
|
70
|
|
|
|
|
|
|
->line(' if (p[0] == \'\\r\' && p[1] == \'\\n\' && p[2] == \'\\r\' && p[3] == \'\\n\') {') |
|
71
|
|
|
|
|
|
|
->line(' return p + 4;') |
|
72
|
|
|
|
|
|
|
->line(' }') |
|
73
|
|
|
|
|
|
|
->line(' }') |
|
74
|
|
|
|
|
|
|
->line(' return NULL;') |
|
75
|
|
|
|
|
|
|
->line('}') |
|
76
|
|
|
|
|
|
|
->blank; |
|
77
|
|
|
|
|
|
|
|
|
78
|
0
|
|
|
|
|
|
$builder->comment('Parse response headers into Perl HV') |
|
79
|
|
|
|
|
|
|
->line('static HV* http1_parse_headers_into_hv(const char* buf, size_t len) {') |
|
80
|
|
|
|
|
|
|
->line(' HV* headers = newHV();') |
|
81
|
|
|
|
|
|
|
->line(' const char* p = buf;') |
|
82
|
|
|
|
|
|
|
->line(' const char* end = buf + len;') |
|
83
|
|
|
|
|
|
|
->blank |
|
84
|
|
|
|
|
|
|
->line(' while (p < end && *p != \'\\n\') p++;') |
|
85
|
|
|
|
|
|
|
->line(' if (p < end) p++;') |
|
86
|
|
|
|
|
|
|
->blank |
|
87
|
|
|
|
|
|
|
->line(' while (p < end - 1) {') |
|
88
|
|
|
|
|
|
|
->line(' if (p[0] == \'\\r\' && p[1] == \'\\n\') break;') |
|
89
|
|
|
|
|
|
|
->line(' if (p[0] == \'\\n\') break;') |
|
90
|
|
|
|
|
|
|
->blank |
|
91
|
|
|
|
|
|
|
->line(' const char* colon = p;') |
|
92
|
|
|
|
|
|
|
->line(' while (colon < end && *colon != \':\' && *colon != \'\\r\') colon++;') |
|
93
|
|
|
|
|
|
|
->line(' if (colon >= end || *colon != \':\') break;') |
|
94
|
|
|
|
|
|
|
->blank |
|
95
|
|
|
|
|
|
|
->line(' int name_len = colon - p;') |
|
96
|
|
|
|
|
|
|
->blank |
|
97
|
|
|
|
|
|
|
->line(' const char* value = colon + 1;') |
|
98
|
|
|
|
|
|
|
->line(' while (value < end && *value == \' \') value++;') |
|
99
|
|
|
|
|
|
|
->blank |
|
100
|
|
|
|
|
|
|
->line(' const char* vend = value;') |
|
101
|
|
|
|
|
|
|
->line(' while (vend < end && *vend != \'\\r\' && *vend != \'\\n\') vend++;') |
|
102
|
|
|
|
|
|
|
->blank |
|
103
|
|
|
|
|
|
|
->line(' int i;') |
|
104
|
|
|
|
|
|
|
->line(' char norm_name[256];') |
|
105
|
|
|
|
|
|
|
->line(' if (name_len > 255) name_len = 255;') |
|
106
|
|
|
|
|
|
|
->line(' for (i = 0; i < name_len; i++) {') |
|
107
|
|
|
|
|
|
|
->line(' char c = p[i];') |
|
108
|
|
|
|
|
|
|
->line(' if (c >= \'A\' && c <= \'Z\') c += 32;') |
|
109
|
|
|
|
|
|
|
->line(' else if (c == \'-\') c = \'_\';') |
|
110
|
|
|
|
|
|
|
->line(' norm_name[i] = c;') |
|
111
|
|
|
|
|
|
|
->line(' }') |
|
112
|
|
|
|
|
|
|
->blank |
|
113
|
|
|
|
|
|
|
->line(' hv_store(headers, norm_name, name_len, newSVpvn(value, vend - value), 0);') |
|
114
|
|
|
|
|
|
|
->blank |
|
115
|
|
|
|
|
|
|
->line(' p = vend;') |
|
116
|
|
|
|
|
|
|
->line(' if (p < end && *p == \'\\r\') p++;') |
|
117
|
|
|
|
|
|
|
->line(' if (p < end && *p == \'\\n\') p++;') |
|
118
|
|
|
|
|
|
|
->line(' }') |
|
119
|
|
|
|
|
|
|
->blank |
|
120
|
|
|
|
|
|
|
->line(' return headers;') |
|
121
|
|
|
|
|
|
|
->line('}') |
|
122
|
|
|
|
|
|
|
->blank; |
|
123
|
|
|
|
|
|
|
|
|
124
|
0
|
|
|
|
|
|
$builder->comment('Decode chunked transfer encoding') |
|
125
|
|
|
|
|
|
|
->line('static ssize_t http1_decode_chunked_data(const char* input, size_t input_len, char* output, size_t output_size) {') |
|
126
|
|
|
|
|
|
|
->line(' const char* p = input;') |
|
127
|
|
|
|
|
|
|
->line(' const char* end = input + input_len;') |
|
128
|
|
|
|
|
|
|
->line(' size_t out_pos = 0;') |
|
129
|
|
|
|
|
|
|
->blank |
|
130
|
|
|
|
|
|
|
->line(' while (p < end) {') |
|
131
|
|
|
|
|
|
|
->line(' size_t chunk_size = 0;') |
|
132
|
|
|
|
|
|
|
->line(' while (p < end && *p != \'\\r\' && *p != \'\\n\') {') |
|
133
|
|
|
|
|
|
|
->line(' char c = *p;') |
|
134
|
|
|
|
|
|
|
->line(' if (c >= \'0\' && c <= \'9\') chunk_size = chunk_size * 16 + (c - \'0\');') |
|
135
|
|
|
|
|
|
|
->line(' else if (c >= \'a\' && c <= \'f\') chunk_size = chunk_size * 16 + (c - \'a\' + 10);') |
|
136
|
|
|
|
|
|
|
->line(' else if (c >= \'A\' && c <= \'F\') chunk_size = chunk_size * 16 + (c - \'A\' + 10);') |
|
137
|
|
|
|
|
|
|
->line(' else if (c == \';\') break;') |
|
138
|
|
|
|
|
|
|
->line(' else break;') |
|
139
|
|
|
|
|
|
|
->line(' p++;') |
|
140
|
|
|
|
|
|
|
->line(' }') |
|
141
|
|
|
|
|
|
|
->blank |
|
142
|
|
|
|
|
|
|
->line(' while (p < end && *p != \'\\r\' && *p != \'\\n\') p++;') |
|
143
|
|
|
|
|
|
|
->line(' if (p < end && *p == \'\\r\') p++;') |
|
144
|
|
|
|
|
|
|
->line(' if (p < end && *p == \'\\n\') p++;') |
|
145
|
|
|
|
|
|
|
->blank |
|
146
|
|
|
|
|
|
|
->line(' if (chunk_size == 0) {') |
|
147
|
|
|
|
|
|
|
->line(' while (p < end - 1 && !(p[0] == \'\\r\' && p[1] == \'\\n\')) {') |
|
148
|
|
|
|
|
|
|
->line(' while (p < end && *p != \'\\n\') p++;') |
|
149
|
|
|
|
|
|
|
->line(' if (p < end) p++;') |
|
150
|
|
|
|
|
|
|
->line(' }') |
|
151
|
|
|
|
|
|
|
->line(' return (ssize_t)out_pos;') |
|
152
|
|
|
|
|
|
|
->line(' }') |
|
153
|
|
|
|
|
|
|
->blank |
|
154
|
|
|
|
|
|
|
->line(' if (p + chunk_size + 2 > end) return -1;') |
|
155
|
|
|
|
|
|
|
->line(' if (out_pos + chunk_size > output_size) return -2;') |
|
156
|
|
|
|
|
|
|
->blank |
|
157
|
|
|
|
|
|
|
->line(' memcpy(output + out_pos, p, chunk_size);') |
|
158
|
|
|
|
|
|
|
->line(' out_pos += chunk_size;') |
|
159
|
|
|
|
|
|
|
->line(' p += chunk_size;') |
|
160
|
|
|
|
|
|
|
->blank |
|
161
|
|
|
|
|
|
|
->line(' if (p < end && *p == \'\\r\') p++;') |
|
162
|
|
|
|
|
|
|
->line(' if (p < end && *p == \'\\n\') p++;') |
|
163
|
|
|
|
|
|
|
->line(' }') |
|
164
|
|
|
|
|
|
|
->blank |
|
165
|
|
|
|
|
|
|
->line(' return -1;') |
|
166
|
|
|
|
|
|
|
->line('}') |
|
167
|
|
|
|
|
|
|
->blank; |
|
168
|
|
|
|
|
|
|
} |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub gen_xs_build_request { |
|
171
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
172
|
|
|
|
|
|
|
|
|
173
|
0
|
|
|
|
|
|
$builder->comment('Build HTTP/1.1 request string') |
|
174
|
|
|
|
|
|
|
->xs_function('xs_http1_build_request') |
|
175
|
|
|
|
|
|
|
->xs_preamble |
|
176
|
|
|
|
|
|
|
->line('if (items < 4) croak("Usage: build_request(method, path, host, headers_hv, [body])");') |
|
177
|
|
|
|
|
|
|
->blank |
|
178
|
|
|
|
|
|
|
->line('STRLEN method_len, path_len, host_len;') |
|
179
|
|
|
|
|
|
|
->line('const char* method = SvPV(ST(0), method_len);') |
|
180
|
|
|
|
|
|
|
->line('const char* path = SvPV(ST(1), path_len);') |
|
181
|
|
|
|
|
|
|
->line('const char* host = SvPV(ST(2), host_len);') |
|
182
|
|
|
|
|
|
|
->line('HV* headers = (HV*)SvRV(ST(3));') |
|
183
|
|
|
|
|
|
|
->line('SV* body_sv = (items > 4) ? ST(4) : NULL;') |
|
184
|
|
|
|
|
|
|
->blank |
|
185
|
|
|
|
|
|
|
->line('size_t request_size = method_len + 1 + path_len + 12 + 6 + host_len + 2;') |
|
186
|
|
|
|
|
|
|
->blank |
|
187
|
|
|
|
|
|
|
->line('hv_iterinit(headers);') |
|
188
|
|
|
|
|
|
|
->line('HE* entry;') |
|
189
|
|
|
|
|
|
|
->line('while ((entry = hv_iternext(headers)) != NULL) {') |
|
190
|
|
|
|
|
|
|
->line(' SV* key_sv = hv_iterkeysv(entry);') |
|
191
|
|
|
|
|
|
|
->line(' SV* val_sv = hv_iterval(headers, entry);') |
|
192
|
|
|
|
|
|
|
->line(' STRLEN key_len, val_len;') |
|
193
|
|
|
|
|
|
|
->line(' SvPV(key_sv, key_len);') |
|
194
|
|
|
|
|
|
|
->line(' SvPV(val_sv, val_len);') |
|
195
|
|
|
|
|
|
|
->line(' request_size += key_len + 2 + val_len + 2;') |
|
196
|
|
|
|
|
|
|
->line('}') |
|
197
|
|
|
|
|
|
|
->blank |
|
198
|
|
|
|
|
|
|
->line('STRLEN body_len = 0;') |
|
199
|
|
|
|
|
|
|
->if('body_sv && SvOK(body_sv)') |
|
200
|
|
|
|
|
|
|
->line('SvPV(body_sv, body_len);') |
|
201
|
|
|
|
|
|
|
->line('request_size += 20 + body_len;') |
|
202
|
|
|
|
|
|
|
->endif |
|
203
|
|
|
|
|
|
|
->line('request_size += 2;') |
|
204
|
|
|
|
|
|
|
->blank |
|
205
|
|
|
|
|
|
|
->line('SV* request = newSV(request_size);') |
|
206
|
|
|
|
|
|
|
->line('SvPOK_on(request);') |
|
207
|
|
|
|
|
|
|
->line('char* rp = SvPVX(request);') |
|
208
|
|
|
|
|
|
|
->blank |
|
209
|
|
|
|
|
|
|
->line('memcpy(rp, method, method_len); rp += method_len;') |
|
210
|
|
|
|
|
|
|
->line('*rp++ = \' \';') |
|
211
|
|
|
|
|
|
|
->line('memcpy(rp, path, path_len); rp += path_len;') |
|
212
|
|
|
|
|
|
|
->line('memcpy(rp, " HTTP/1.1\\r\\n", 11); rp += 11;') |
|
213
|
|
|
|
|
|
|
->blank |
|
214
|
|
|
|
|
|
|
->line('memcpy(rp, "Host: ", 6); rp += 6;') |
|
215
|
|
|
|
|
|
|
->line('memcpy(rp, host, host_len); rp += host_len;') |
|
216
|
|
|
|
|
|
|
->line('*rp++ = \'\\r\'; *rp++ = \'\\n\';') |
|
217
|
|
|
|
|
|
|
->blank |
|
218
|
|
|
|
|
|
|
->line('hv_iterinit(headers);') |
|
219
|
|
|
|
|
|
|
->line('while ((entry = hv_iternext(headers)) != NULL) {') |
|
220
|
|
|
|
|
|
|
->line(' SV* key_sv = hv_iterkeysv(entry);') |
|
221
|
|
|
|
|
|
|
->line(' SV* val_sv = hv_iterval(headers, entry);') |
|
222
|
|
|
|
|
|
|
->line(' STRLEN key_len, val_len;') |
|
223
|
|
|
|
|
|
|
->line(' const char* key = SvPV(key_sv, key_len);') |
|
224
|
|
|
|
|
|
|
->line(' const char* val = SvPV(val_sv, val_len);') |
|
225
|
|
|
|
|
|
|
->line(' memcpy(rp, key, key_len); rp += key_len;') |
|
226
|
|
|
|
|
|
|
->line(' *rp++ = \':\'; *rp++ = \' \';') |
|
227
|
|
|
|
|
|
|
->line(' memcpy(rp, val, val_len); rp += val_len;') |
|
228
|
|
|
|
|
|
|
->line(' *rp++ = \'\\r\'; *rp++ = \'\\n\';') |
|
229
|
|
|
|
|
|
|
->line('}') |
|
230
|
|
|
|
|
|
|
->blank |
|
231
|
|
|
|
|
|
|
->if('body_len > 0') |
|
232
|
|
|
|
|
|
|
->line('rp += sprintf(rp, "Content-Length: %zu\\r\\n", body_len);') |
|
233
|
|
|
|
|
|
|
->endif |
|
234
|
|
|
|
|
|
|
->blank |
|
235
|
|
|
|
|
|
|
->line('*rp++ = \'\\r\'; *rp++ = \'\\n\';') |
|
236
|
|
|
|
|
|
|
->if('body_len > 0') |
|
237
|
|
|
|
|
|
|
->line('const char* body = SvPV_nolen(body_sv);') |
|
238
|
|
|
|
|
|
|
->line('memcpy(rp, body, body_len); rp += body_len;') |
|
239
|
|
|
|
|
|
|
->endif |
|
240
|
|
|
|
|
|
|
->blank |
|
241
|
|
|
|
|
|
|
->line('SvCUR_set(request, rp - SvPVX(request));') |
|
242
|
|
|
|
|
|
|
->line('ST(0) = sv_2mortal(request);') |
|
243
|
|
|
|
|
|
|
->xs_return('1') |
|
244
|
|
|
|
|
|
|
->xs_end |
|
245
|
|
|
|
|
|
|
->blank; |
|
246
|
|
|
|
|
|
|
} |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
sub gen_xs_parse_status_line { |
|
249
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
250
|
|
|
|
|
|
|
|
|
251
|
0
|
|
|
|
|
|
$builder->comment('Parse status line, return status code') |
|
252
|
|
|
|
|
|
|
->xs_function('xs_http1_parse_status_line') |
|
253
|
|
|
|
|
|
|
->xs_preamble |
|
254
|
|
|
|
|
|
|
->line('if (items != 1) croak("Usage: parse_status_line(raw)");') |
|
255
|
|
|
|
|
|
|
->blank |
|
256
|
|
|
|
|
|
|
->line('STRLEN raw_len;') |
|
257
|
|
|
|
|
|
|
->line('const char* raw = SvPV(ST(0), raw_len);') |
|
258
|
|
|
|
|
|
|
->line('int http_minor;') |
|
259
|
|
|
|
|
|
|
->line('int status = http1_parse_status_line(raw, raw_len, &http_minor);') |
|
260
|
|
|
|
|
|
|
->blank |
|
261
|
|
|
|
|
|
|
->line('ST(0) = sv_2mortal(newSViv(status));') |
|
262
|
|
|
|
|
|
|
->xs_return('1') |
|
263
|
|
|
|
|
|
|
->xs_end |
|
264
|
|
|
|
|
|
|
->blank; |
|
265
|
|
|
|
|
|
|
} |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub gen_xs_parse_headers { |
|
268
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
269
|
|
|
|
|
|
|
|
|
270
|
0
|
|
|
|
|
|
$builder->comment('Parse headers into hashref') |
|
271
|
|
|
|
|
|
|
->xs_function('xs_http1_parse_headers') |
|
272
|
|
|
|
|
|
|
->xs_preamble |
|
273
|
|
|
|
|
|
|
->line('if (items != 1) croak("Usage: parse_headers(raw)");') |
|
274
|
|
|
|
|
|
|
->blank |
|
275
|
|
|
|
|
|
|
->line('STRLEN raw_len;') |
|
276
|
|
|
|
|
|
|
->line('const char* raw = SvPV(ST(0), raw_len);') |
|
277
|
|
|
|
|
|
|
->blank |
|
278
|
|
|
|
|
|
|
->line('const char* body_start = http1_find_body_start(raw, raw_len);') |
|
279
|
|
|
|
|
|
|
->line('size_t headers_len = body_start ? (body_start - raw) : raw_len;') |
|
280
|
|
|
|
|
|
|
->blank |
|
281
|
|
|
|
|
|
|
->line('HV* headers = http1_parse_headers_into_hv(raw, headers_len);') |
|
282
|
|
|
|
|
|
|
->line('ST(0) = sv_2mortal(newRV_noinc((SV*)headers));') |
|
283
|
|
|
|
|
|
|
->xs_return('1') |
|
284
|
|
|
|
|
|
|
->xs_end |
|
285
|
|
|
|
|
|
|
->blank; |
|
286
|
|
|
|
|
|
|
} |
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
sub gen_xs_find_body_start { |
|
289
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
290
|
|
|
|
|
|
|
|
|
291
|
0
|
|
|
|
|
|
$builder->comment('Find offset of body start') |
|
292
|
|
|
|
|
|
|
->xs_function('xs_http1_find_body_start') |
|
293
|
|
|
|
|
|
|
->xs_preamble |
|
294
|
|
|
|
|
|
|
->line('if (items != 1) croak("Usage: find_body_start(raw)");') |
|
295
|
|
|
|
|
|
|
->blank |
|
296
|
|
|
|
|
|
|
->line('STRLEN raw_len;') |
|
297
|
|
|
|
|
|
|
->line('const char* raw = SvPV(ST(0), raw_len);') |
|
298
|
|
|
|
|
|
|
->blank |
|
299
|
|
|
|
|
|
|
->line('const char* body = http1_find_body_start(raw, raw_len);') |
|
300
|
|
|
|
|
|
|
->if('body') |
|
301
|
|
|
|
|
|
|
->line('ST(0) = sv_2mortal(newSViv(body - raw));') |
|
302
|
|
|
|
|
|
|
->else |
|
303
|
|
|
|
|
|
|
->line('ST(0) = sv_2mortal(newSViv(-1));') |
|
304
|
|
|
|
|
|
|
->endif |
|
305
|
|
|
|
|
|
|
->xs_return('1') |
|
306
|
|
|
|
|
|
|
->xs_end |
|
307
|
|
|
|
|
|
|
->blank; |
|
308
|
|
|
|
|
|
|
} |
|
309
|
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
sub gen_xs_get_content_length { |
|
311
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
312
|
|
|
|
|
|
|
|
|
313
|
0
|
|
|
|
|
|
$builder->comment('Extract Content-Length from headers') |
|
314
|
|
|
|
|
|
|
->xs_function('xs_http1_get_content_length') |
|
315
|
|
|
|
|
|
|
->xs_preamble |
|
316
|
|
|
|
|
|
|
->line('if (items != 1) croak("Usage: get_content_length(headers_str)");') |
|
317
|
|
|
|
|
|
|
->blank |
|
318
|
|
|
|
|
|
|
->line('STRLEN len;') |
|
319
|
|
|
|
|
|
|
->line('const char* headers = SvPV(ST(0), len);') |
|
320
|
|
|
|
|
|
|
->blank |
|
321
|
|
|
|
|
|
|
->line('const char* cl = strcasestr(headers, "\\r\\nContent-Length:");') |
|
322
|
|
|
|
|
|
|
->line('if (!cl) cl = strcasestr(headers, "\\nContent-Length:");') |
|
323
|
|
|
|
|
|
|
->if('!cl') |
|
324
|
|
|
|
|
|
|
->line('ST(0) = sv_2mortal(newSViv(-1));') |
|
325
|
|
|
|
|
|
|
->line('XSRETURN(1);') |
|
326
|
|
|
|
|
|
|
->endif |
|
327
|
|
|
|
|
|
|
->blank |
|
328
|
|
|
|
|
|
|
->line('cl += (cl[0] == \'\\r\') ? 17 : 16;') |
|
329
|
|
|
|
|
|
|
->line('while (*cl == \' \') cl++;') |
|
330
|
|
|
|
|
|
|
->blank |
|
331
|
|
|
|
|
|
|
->line('ssize_t length = 0;') |
|
332
|
|
|
|
|
|
|
->line('while (*cl >= \'0\' && *cl <= \'9\') {') |
|
333
|
|
|
|
|
|
|
->line(' length = length * 10 + (*cl - \'0\');') |
|
334
|
|
|
|
|
|
|
->line(' cl++;') |
|
335
|
|
|
|
|
|
|
->line('}') |
|
336
|
|
|
|
|
|
|
->line('ST(0) = sv_2mortal(newSViv(length));') |
|
337
|
|
|
|
|
|
|
->xs_return('1') |
|
338
|
|
|
|
|
|
|
->xs_end |
|
339
|
|
|
|
|
|
|
->blank; |
|
340
|
|
|
|
|
|
|
} |
|
341
|
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
sub gen_xs_decode_chunked { |
|
343
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
344
|
|
|
|
|
|
|
|
|
345
|
0
|
|
|
|
|
|
$builder->comment('Decode chunked transfer encoding') |
|
346
|
|
|
|
|
|
|
->xs_function('xs_http1_decode_chunked') |
|
347
|
|
|
|
|
|
|
->xs_preamble |
|
348
|
|
|
|
|
|
|
->line('if (items != 1) croak("Usage: decode_chunked(body)");') |
|
349
|
|
|
|
|
|
|
->blank |
|
350
|
|
|
|
|
|
|
->line('STRLEN input_len;') |
|
351
|
|
|
|
|
|
|
->line('const char* input = SvPV(ST(0), input_len);') |
|
352
|
|
|
|
|
|
|
->blank |
|
353
|
|
|
|
|
|
|
->line('ssize_t decoded_len = http1_decode_chunked_data(input, input_len, g_http1_decode_buf, HTTP1_DECODE_BUF_SIZE);') |
|
354
|
|
|
|
|
|
|
->if('decoded_len >= 0') |
|
355
|
|
|
|
|
|
|
->line('ST(0) = sv_2mortal(newSVpvn(g_http1_decode_buf, decoded_len));') |
|
356
|
|
|
|
|
|
|
->else |
|
357
|
|
|
|
|
|
|
->line('ST(0) = &PL_sv_undef;') |
|
358
|
|
|
|
|
|
|
->endif |
|
359
|
|
|
|
|
|
|
->xs_return('1') |
|
360
|
|
|
|
|
|
|
->xs_end |
|
361
|
|
|
|
|
|
|
->blank; |
|
362
|
|
|
|
|
|
|
} |
|
363
|
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
sub gen_xs_parse_response { |
|
365
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
366
|
|
|
|
|
|
|
|
|
367
|
0
|
|
|
|
|
|
$builder->comment('Parse raw HTTP response into [status, headers_hv, body, raw_headers]') |
|
368
|
|
|
|
|
|
|
->xs_function('xs_http1_parse_response') |
|
369
|
|
|
|
|
|
|
->xs_preamble |
|
370
|
|
|
|
|
|
|
->line('if (items != 1) croak("Usage: parse_response(raw_response)");') |
|
371
|
|
|
|
|
|
|
->blank |
|
372
|
|
|
|
|
|
|
->line('STRLEN raw_len;') |
|
373
|
|
|
|
|
|
|
->line('const char* raw = SvPV(ST(0), raw_len);') |
|
374
|
|
|
|
|
|
|
->blank |
|
375
|
|
|
|
|
|
|
->line('int http_minor;') |
|
376
|
|
|
|
|
|
|
->line('int status = http1_parse_status_line(raw, raw_len, &http_minor);') |
|
377
|
|
|
|
|
|
|
->if('status < 0') |
|
378
|
|
|
|
|
|
|
->line('ST(0) = &PL_sv_undef;') |
|
379
|
|
|
|
|
|
|
->line('XSRETURN(1);') |
|
380
|
|
|
|
|
|
|
->endif |
|
381
|
|
|
|
|
|
|
->blank |
|
382
|
|
|
|
|
|
|
->line('const char* body_start = http1_find_body_start(raw, raw_len);') |
|
383
|
|
|
|
|
|
|
->if('!body_start') |
|
384
|
|
|
|
|
|
|
->line('ST(0) = &PL_sv_undef;') |
|
385
|
|
|
|
|
|
|
->line('XSRETURN(1);') |
|
386
|
|
|
|
|
|
|
->endif |
|
387
|
|
|
|
|
|
|
->blank |
|
388
|
|
|
|
|
|
|
->line('size_t headers_len = body_start - raw;') |
|
389
|
|
|
|
|
|
|
->line('size_t body_len = raw_len - headers_len;') |
|
390
|
|
|
|
|
|
|
->blank |
|
391
|
|
|
|
|
|
|
->line('HV* headers = http1_parse_headers_into_hv(raw, headers_len);') |
|
392
|
|
|
|
|
|
|
->blank |
|
393
|
|
|
|
|
|
|
->line('SV** te = hv_fetch(headers, "transfer_encoding", 17, 0);') |
|
394
|
|
|
|
|
|
|
->line('int chunked = (te && *te && strstr(SvPV_nolen(*te), "chunked"));') |
|
395
|
|
|
|
|
|
|
->blank |
|
396
|
|
|
|
|
|
|
->line('SV* body_sv;') |
|
397
|
|
|
|
|
|
|
->if('chunked') |
|
398
|
|
|
|
|
|
|
->line('ssize_t decoded_len = http1_decode_chunked_data(body_start, body_len, g_http1_decode_buf, HTTP1_DECODE_BUF_SIZE);') |
|
399
|
|
|
|
|
|
|
->if('decoded_len >= 0') |
|
400
|
|
|
|
|
|
|
->line('body_sv = newSVpvn(g_http1_decode_buf, decoded_len);') |
|
401
|
|
|
|
|
|
|
->else |
|
402
|
|
|
|
|
|
|
->line('body_sv = newSVpvn(body_start, body_len);') |
|
403
|
|
|
|
|
|
|
->endif |
|
404
|
|
|
|
|
|
|
->else |
|
405
|
|
|
|
|
|
|
->line('body_sv = newSVpvn(body_start, body_len);') |
|
406
|
|
|
|
|
|
|
->endif |
|
407
|
|
|
|
|
|
|
->blank |
|
408
|
|
|
|
|
|
|
->line('AV* result = newAV();') |
|
409
|
|
|
|
|
|
|
->line('av_push(result, newSViv(status));') |
|
410
|
|
|
|
|
|
|
->line('av_push(result, newRV_noinc((SV*)headers));') |
|
411
|
|
|
|
|
|
|
->line('av_push(result, body_sv);') |
|
412
|
|
|
|
|
|
|
->line('av_push(result, newSVpvn(raw, headers_len));') |
|
413
|
|
|
|
|
|
|
->blank |
|
414
|
|
|
|
|
|
|
->line('ST(0) = sv_2mortal(newRV_noinc((SV*)result));') |
|
415
|
|
|
|
|
|
|
->xs_return('1') |
|
416
|
|
|
|
|
|
|
->xs_end |
|
417
|
|
|
|
|
|
|
->blank; |
|
418
|
|
|
|
|
|
|
} |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
1; |