| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Hypersonic::UA::Response; |
|
2
|
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
221335
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
30
|
|
|
4
|
1
|
|
|
1
|
|
3
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
38
|
|
|
5
|
1
|
|
|
1
|
|
13
|
use 5.010; |
|
|
1
|
|
|
|
|
5
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = '0.15'; |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
use constant { |
|
10
|
1
|
|
|
|
|
117
|
SLOT_STATUS => 0, |
|
11
|
|
|
|
|
|
|
SLOT_STATUS_TEXT => 1, |
|
12
|
|
|
|
|
|
|
SLOT_HEADERS => 2, |
|
13
|
|
|
|
|
|
|
SLOT_BODY => 3, |
|
14
|
|
|
|
|
|
|
SLOT_CONTENT_TYPE => 4, |
|
15
|
|
|
|
|
|
|
SLOT_CONTENT_LENGTH => 5, |
|
16
|
|
|
|
|
|
|
SLOT_HTTP_VERSION => 6, |
|
17
|
|
|
|
|
|
|
SLOT_REQUEST => 7, |
|
18
|
|
|
|
|
|
|
SLOT_REDIRECTS => 8, |
|
19
|
|
|
|
|
|
|
SLOT_TIMING => 9, |
|
20
|
|
|
|
|
|
|
SLOT_JSON => 10, |
|
21
|
|
|
|
|
|
|
SLOT_RAW_HEADERS => 11, |
|
22
|
|
|
|
|
|
|
SLOT_COUNT => 12, |
|
23
|
1
|
|
|
1
|
|
4
|
}; |
|
|
1
|
|
|
|
|
1
|
|
|
24
|
|
|
|
|
|
|
|
|
25
|
1
|
|
|
1
|
|
4
|
use constant MAX_RESPONSES => 65536; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
1498
|
|
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
sub generate_c_code { |
|
28
|
0
|
|
|
0
|
0
|
0
|
my ($class, $builder, $opts) = @_; |
|
29
|
|
|
|
|
|
|
|
|
30
|
0
|
|
|
|
|
0
|
$class->gen_slot_constants($builder); |
|
31
|
0
|
|
|
|
|
0
|
$class->gen_xs_new($builder); |
|
32
|
0
|
|
|
|
|
0
|
$class->gen_xs_from_raw($builder); |
|
33
|
0
|
|
|
|
|
0
|
$class->gen_xs_status($builder); |
|
34
|
0
|
|
|
|
|
0
|
$class->gen_xs_status_text($builder); |
|
35
|
0
|
|
|
|
|
0
|
$class->gen_xs_body($builder); |
|
36
|
0
|
|
|
|
|
0
|
$class->gen_xs_headers($builder); |
|
37
|
0
|
|
|
|
|
0
|
$class->gen_xs_header($builder); |
|
38
|
0
|
|
|
|
|
0
|
$class->gen_xs_content_type($builder); |
|
39
|
0
|
|
|
|
|
0
|
$class->gen_xs_content_length($builder); |
|
40
|
0
|
|
|
|
|
0
|
$class->gen_xs_raw_headers($builder); |
|
41
|
0
|
|
|
|
|
0
|
$class->gen_xs_is_success($builder); |
|
42
|
0
|
|
|
|
|
0
|
$class->gen_xs_is_redirect($builder); |
|
43
|
0
|
|
|
|
|
0
|
$class->gen_xs_is_error($builder); |
|
44
|
0
|
|
|
|
|
0
|
$class->gen_xs_is_client_error($builder); |
|
45
|
0
|
|
|
|
|
0
|
$class->gen_xs_is_server_error($builder); |
|
46
|
0
|
|
|
|
|
0
|
$class->gen_xs_is_json($builder); |
|
47
|
0
|
|
|
|
|
0
|
$class->gen_xs_json($builder); |
|
48
|
0
|
|
|
|
|
0
|
$class->gen_xs_location($builder); |
|
49
|
|
|
|
|
|
|
} |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
sub get_xs_functions { |
|
52
|
|
|
|
|
|
|
return { |
|
53
|
1
|
|
|
1
|
0
|
1925
|
'Hypersonic::UA::Response::new' => { source => 'xs_response_new', is_xs_native => 1 }, |
|
54
|
|
|
|
|
|
|
'Hypersonic::UA::Response::from_raw' => { source => 'xs_response_from_raw', is_xs_native => 1 }, |
|
55
|
|
|
|
|
|
|
'Hypersonic::UA::Response::status' => { source => 'xs_response_status', is_xs_native => 1 }, |
|
56
|
|
|
|
|
|
|
'Hypersonic::UA::Response::status_text' => { source => 'xs_response_status_text', is_xs_native => 1 }, |
|
57
|
|
|
|
|
|
|
'Hypersonic::UA::Response::body' => { source => 'xs_response_body', is_xs_native => 1 }, |
|
58
|
|
|
|
|
|
|
'Hypersonic::UA::Response::headers' => { source => 'xs_response_headers', is_xs_native => 1 }, |
|
59
|
|
|
|
|
|
|
'Hypersonic::UA::Response::header' => { source => 'xs_response_header', is_xs_native => 1 }, |
|
60
|
|
|
|
|
|
|
'Hypersonic::UA::Response::content_type' => { source => 'xs_response_content_type', is_xs_native => 1 }, |
|
61
|
|
|
|
|
|
|
'Hypersonic::UA::Response::content_length' => { source => 'xs_response_content_length', is_xs_native => 1 }, |
|
62
|
|
|
|
|
|
|
'Hypersonic::UA::Response::raw_headers' => { source => 'xs_response_raw_headers', is_xs_native => 1 }, |
|
63
|
|
|
|
|
|
|
'Hypersonic::UA::Response::is_success' => { source => 'xs_response_is_success', is_xs_native => 1 }, |
|
64
|
|
|
|
|
|
|
'Hypersonic::UA::Response::is_redirect' => { source => 'xs_response_is_redirect', is_xs_native => 1 }, |
|
65
|
|
|
|
|
|
|
'Hypersonic::UA::Response::is_error' => { source => 'xs_response_is_error', is_xs_native => 1 }, |
|
66
|
|
|
|
|
|
|
'Hypersonic::UA::Response::is_client_error' => { source => 'xs_response_is_client_error', is_xs_native => 1 }, |
|
67
|
|
|
|
|
|
|
'Hypersonic::UA::Response::is_server_error' => { source => 'xs_response_is_server_error', is_xs_native => 1 }, |
|
68
|
|
|
|
|
|
|
'Hypersonic::UA::Response::is_json' => { source => 'xs_response_is_json', is_xs_native => 1 }, |
|
69
|
|
|
|
|
|
|
'Hypersonic::UA::Response::json' => { source => 'xs_response_json', is_xs_native => 1 }, |
|
70
|
|
|
|
|
|
|
'Hypersonic::UA::Response::location' => { source => 'xs_response_location', is_xs_native => 1 }, |
|
71
|
|
|
|
|
|
|
}; |
|
72
|
|
|
|
|
|
|
} |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub gen_slot_constants { |
|
75
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
76
|
|
|
|
|
|
|
|
|
77
|
0
|
|
|
|
|
|
$builder->line('#define RESP_SLOT_STATUS ' . SLOT_STATUS) |
|
78
|
|
|
|
|
|
|
->line('#define RESP_SLOT_STATUS_TEXT ' . SLOT_STATUS_TEXT) |
|
79
|
|
|
|
|
|
|
->line('#define RESP_SLOT_HEADERS ' . SLOT_HEADERS) |
|
80
|
|
|
|
|
|
|
->line('#define RESP_SLOT_BODY ' . SLOT_BODY) |
|
81
|
|
|
|
|
|
|
->line('#define RESP_SLOT_CONTENT_TYPE ' . SLOT_CONTENT_TYPE) |
|
82
|
|
|
|
|
|
|
->line('#define RESP_SLOT_CONTENT_LENGTH ' . SLOT_CONTENT_LENGTH) |
|
83
|
|
|
|
|
|
|
->line('#define RESP_SLOT_HTTP_VERSION ' . SLOT_HTTP_VERSION) |
|
84
|
|
|
|
|
|
|
->line('#define RESP_SLOT_REQUEST ' . SLOT_REQUEST) |
|
85
|
|
|
|
|
|
|
->line('#define RESP_SLOT_REDIRECTS ' . SLOT_REDIRECTS) |
|
86
|
|
|
|
|
|
|
->line('#define RESP_SLOT_TIMING ' . SLOT_TIMING) |
|
87
|
|
|
|
|
|
|
->line('#define RESP_SLOT_JSON ' . SLOT_JSON) |
|
88
|
|
|
|
|
|
|
->line('#define RESP_SLOT_RAW_HEADERS ' . SLOT_RAW_HEADERS) |
|
89
|
|
|
|
|
|
|
->line('#define RESP_SLOT_COUNT ' . SLOT_COUNT) |
|
90
|
|
|
|
|
|
|
->blank; |
|
91
|
|
|
|
|
|
|
|
|
92
|
0
|
|
|
|
|
|
$builder->line('static const char* resp_status_text(int status) {') |
|
93
|
|
|
|
|
|
|
->line(' switch (status) {') |
|
94
|
|
|
|
|
|
|
->line(' case 100: return "Continue";') |
|
95
|
|
|
|
|
|
|
->line(' case 101: return "Switching Protocols";') |
|
96
|
|
|
|
|
|
|
->line(' case 200: return "OK";') |
|
97
|
|
|
|
|
|
|
->line(' case 201: return "Created";') |
|
98
|
|
|
|
|
|
|
->line(' case 202: return "Accepted";') |
|
99
|
|
|
|
|
|
|
->line(' case 204: return "No Content";') |
|
100
|
|
|
|
|
|
|
->line(' case 206: return "Partial Content";') |
|
101
|
|
|
|
|
|
|
->line(' case 301: return "Moved Permanently";') |
|
102
|
|
|
|
|
|
|
->line(' case 302: return "Found";') |
|
103
|
|
|
|
|
|
|
->line(' case 303: return "See Other";') |
|
104
|
|
|
|
|
|
|
->line(' case 304: return "Not Modified";') |
|
105
|
|
|
|
|
|
|
->line(' case 307: return "Temporary Redirect";') |
|
106
|
|
|
|
|
|
|
->line(' case 308: return "Permanent Redirect";') |
|
107
|
|
|
|
|
|
|
->line(' case 400: return "Bad Request";') |
|
108
|
|
|
|
|
|
|
->line(' case 401: return "Unauthorized";') |
|
109
|
|
|
|
|
|
|
->line(' case 403: return "Forbidden";') |
|
110
|
|
|
|
|
|
|
->line(' case 404: return "Not Found";') |
|
111
|
|
|
|
|
|
|
->line(' case 405: return "Method Not Allowed";') |
|
112
|
|
|
|
|
|
|
->line(' case 408: return "Request Timeout";') |
|
113
|
|
|
|
|
|
|
->line(' case 409: return "Conflict";') |
|
114
|
|
|
|
|
|
|
->line(' case 410: return "Gone";') |
|
115
|
|
|
|
|
|
|
->line(' case 413: return "Payload Too Large";') |
|
116
|
|
|
|
|
|
|
->line(' case 415: return "Unsupported Media Type";') |
|
117
|
|
|
|
|
|
|
->line(' case 422: return "Unprocessable Entity";') |
|
118
|
|
|
|
|
|
|
->line(' case 429: return "Too Many Requests";') |
|
119
|
|
|
|
|
|
|
->line(' case 500: return "Internal Server Error";') |
|
120
|
|
|
|
|
|
|
->line(' case 501: return "Not Implemented";') |
|
121
|
|
|
|
|
|
|
->line(' case 502: return "Bad Gateway";') |
|
122
|
|
|
|
|
|
|
->line(' case 503: return "Service Unavailable";') |
|
123
|
|
|
|
|
|
|
->line(' case 504: return "Gateway Timeout";') |
|
124
|
|
|
|
|
|
|
->line(' default: return "Unknown";') |
|
125
|
|
|
|
|
|
|
->line(' }') |
|
126
|
|
|
|
|
|
|
->line('}') |
|
127
|
|
|
|
|
|
|
->blank; |
|
128
|
|
|
|
|
|
|
} |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
sub gen_xs_new { |
|
131
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
132
|
|
|
|
|
|
|
|
|
133
|
0
|
|
|
|
|
|
$builder->comment('Create new response from parsed data') |
|
134
|
|
|
|
|
|
|
->xs_function('xs_response_new') |
|
135
|
|
|
|
|
|
|
->xs_preamble |
|
136
|
|
|
|
|
|
|
->line('int status;') |
|
137
|
|
|
|
|
|
|
->line('HV* opts;') |
|
138
|
|
|
|
|
|
|
->line('AV* self;') |
|
139
|
|
|
|
|
|
|
->line('SV** val;') |
|
140
|
|
|
|
|
|
|
->line('SV* obj;') |
|
141
|
|
|
|
|
|
|
->blank |
|
142
|
|
|
|
|
|
|
->line('if (items < 2) croak("Usage: new(class, status, ...)");') |
|
143
|
|
|
|
|
|
|
->blank |
|
144
|
|
|
|
|
|
|
->line('status = (int)SvIV(ST(1));') |
|
145
|
|
|
|
|
|
|
->line('opts = (items > 2 && SvROK(ST(2))) ? (HV*)SvRV(ST(2)) : NULL;') |
|
146
|
|
|
|
|
|
|
->blank |
|
147
|
|
|
|
|
|
|
->line('self = newAV();') |
|
148
|
|
|
|
|
|
|
->line('av_extend(self, RESP_SLOT_COUNT - 1);') |
|
149
|
|
|
|
|
|
|
->blank |
|
150
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_STATUS, newSViv(status));') |
|
151
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_STATUS_TEXT, newSVpv(resp_status_text(status), 0));') |
|
152
|
|
|
|
|
|
|
->blank |
|
153
|
|
|
|
|
|
|
->if('opts') |
|
154
|
|
|
|
|
|
|
->line('val = hv_fetchs(opts, "headers", 0);') |
|
155
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_HEADERS, val && *val ? newSVsv(*val) : newRV_noinc((SV*)newHV()));') |
|
156
|
|
|
|
|
|
|
->line('val = hv_fetchs(opts, "body", 0);') |
|
157
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_BODY, val && *val ? newSVsv(*val) : newSVpvn("", 0));') |
|
158
|
|
|
|
|
|
|
->line('val = hv_fetchs(opts, "content_type", 0);') |
|
159
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_CONTENT_TYPE, val && *val ? newSVsv(*val) : newSVpvn("", 0));') |
|
160
|
|
|
|
|
|
|
->line('val = hv_fetchs(opts, "content_length", 0);') |
|
161
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_CONTENT_LENGTH, val && *val ? newSVsv(*val) : newSViv(-1));') |
|
162
|
|
|
|
|
|
|
->line('val = hv_fetchs(opts, "raw_headers", 0);') |
|
163
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_RAW_HEADERS, val && *val ? newSVsv(*val) : newSVpvn("", 0));') |
|
164
|
|
|
|
|
|
|
->else |
|
165
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_HEADERS, newRV_noinc((SV*)newHV()));') |
|
166
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_BODY, newSVpvn("", 0));') |
|
167
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_CONTENT_TYPE, newSVpvn("", 0));') |
|
168
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_CONTENT_LENGTH, newSViv(-1));') |
|
169
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_RAW_HEADERS, newSVpvn("", 0));') |
|
170
|
|
|
|
|
|
|
->endif |
|
171
|
|
|
|
|
|
|
->blank |
|
172
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_HTTP_VERSION, newSVpvn("1.1", 3));') |
|
173
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_REQUEST, &PL_sv_undef);') |
|
174
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_REDIRECTS, newRV_noinc((SV*)newAV()));') |
|
175
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_TIMING, newRV_noinc((SV*)newHV()));') |
|
176
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_JSON, &PL_sv_undef);') |
|
177
|
|
|
|
|
|
|
->blank |
|
178
|
|
|
|
|
|
|
->line('obj = sv_bless(newRV_noinc((SV*)self), gv_stashpv("Hypersonic::UA::Response", GV_ADD));') |
|
179
|
|
|
|
|
|
|
->line('ST(0) = sv_2mortal(obj);') |
|
180
|
|
|
|
|
|
|
->xs_return('1') |
|
181
|
|
|
|
|
|
|
->xs_end |
|
182
|
|
|
|
|
|
|
->blank; |
|
183
|
|
|
|
|
|
|
} |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
sub gen_xs_from_raw { |
|
186
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
187
|
|
|
|
|
|
|
|
|
188
|
0
|
|
|
|
|
|
$builder->comment('Create response from raw HTTP response') |
|
189
|
|
|
|
|
|
|
->xs_function('xs_response_from_raw') |
|
190
|
|
|
|
|
|
|
->xs_preamble |
|
191
|
|
|
|
|
|
|
->line('STRLEN raw_len;') |
|
192
|
|
|
|
|
|
|
->line('const char* raw;') |
|
193
|
|
|
|
|
|
|
->line('int http_minor;') |
|
194
|
|
|
|
|
|
|
->line('int status;') |
|
195
|
|
|
|
|
|
|
->line('const char* body_start;') |
|
196
|
|
|
|
|
|
|
->line('size_t headers_len;') |
|
197
|
|
|
|
|
|
|
->line('size_t body_len;') |
|
198
|
|
|
|
|
|
|
->line('HV* headers;') |
|
199
|
|
|
|
|
|
|
->line('AV* self;') |
|
200
|
|
|
|
|
|
|
->line('SV** ct;') |
|
201
|
|
|
|
|
|
|
->line('SV** cl;') |
|
202
|
|
|
|
|
|
|
->line('SV* obj;') |
|
203
|
|
|
|
|
|
|
->comment('Parse using HTTP1 helpers (must be linked)') |
|
204
|
|
|
|
|
|
|
->line('extern int http1_parse_status_line(const char*, size_t, int*);') |
|
205
|
|
|
|
|
|
|
->line('extern const char* http1_find_body_start(const char*, size_t);') |
|
206
|
|
|
|
|
|
|
->line('extern HV* http1_parse_headers_into_hv(const char*, size_t);') |
|
207
|
|
|
|
|
|
|
->blank |
|
208
|
|
|
|
|
|
|
->line('if (items != 2) croak("Usage: from_raw(class, raw)");') |
|
209
|
|
|
|
|
|
|
->blank |
|
210
|
|
|
|
|
|
|
->line('raw = SvPV(ST(1), raw_len);') |
|
211
|
|
|
|
|
|
|
->blank |
|
212
|
|
|
|
|
|
|
->line('status = http1_parse_status_line(raw, raw_len, &http_minor);') |
|
213
|
|
|
|
|
|
|
->if('status < 0') |
|
214
|
|
|
|
|
|
|
->line('ST(0) = &PL_sv_undef;') |
|
215
|
|
|
|
|
|
|
->line('XSRETURN(1);') |
|
216
|
|
|
|
|
|
|
->endif |
|
217
|
|
|
|
|
|
|
->blank |
|
218
|
|
|
|
|
|
|
->line('body_start = http1_find_body_start(raw, raw_len);') |
|
219
|
|
|
|
|
|
|
->if('!body_start') |
|
220
|
|
|
|
|
|
|
->line('ST(0) = &PL_sv_undef;') |
|
221
|
|
|
|
|
|
|
->line('XSRETURN(1);') |
|
222
|
|
|
|
|
|
|
->endif |
|
223
|
|
|
|
|
|
|
->blank |
|
224
|
|
|
|
|
|
|
->line('headers_len = body_start - raw;') |
|
225
|
|
|
|
|
|
|
->line('body_len = raw_len - headers_len;') |
|
226
|
|
|
|
|
|
|
->blank |
|
227
|
|
|
|
|
|
|
->line('headers = http1_parse_headers_into_hv(raw, headers_len);') |
|
228
|
|
|
|
|
|
|
->blank |
|
229
|
|
|
|
|
|
|
->line('self = newAV();') |
|
230
|
|
|
|
|
|
|
->line('av_extend(self, RESP_SLOT_COUNT - 1);') |
|
231
|
|
|
|
|
|
|
->blank |
|
232
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_STATUS, newSViv(status));') |
|
233
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_STATUS_TEXT, newSVpv(resp_status_text(status), 0));') |
|
234
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_HEADERS, newRV_noinc((SV*)headers));') |
|
235
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_BODY, newSVpvn(body_start, body_len));') |
|
236
|
|
|
|
|
|
|
->blank |
|
237
|
|
|
|
|
|
|
->line('ct = hv_fetchs(headers, "content_type", 0);') |
|
238
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_CONTENT_TYPE, ct && *ct ? newSVsv(*ct) : newSVpvn("", 0));') |
|
239
|
|
|
|
|
|
|
->blank |
|
240
|
|
|
|
|
|
|
->line('cl = hv_fetchs(headers, "content_length", 0);') |
|
241
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_CONTENT_LENGTH, cl && *cl ? newSViv(atoi(SvPV_nolen(*cl))) : newSViv(-1));') |
|
242
|
|
|
|
|
|
|
->blank |
|
243
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_HTTP_VERSION, newSVpvn("1.1", 3));') |
|
244
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_REQUEST, &PL_sv_undef);') |
|
245
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_REDIRECTS, newRV_noinc((SV*)newAV()));') |
|
246
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_TIMING, newRV_noinc((SV*)newHV()));') |
|
247
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_JSON, &PL_sv_undef);') |
|
248
|
|
|
|
|
|
|
->line('av_store(self, RESP_SLOT_RAW_HEADERS, newSVpvn(raw, headers_len));') |
|
249
|
|
|
|
|
|
|
->blank |
|
250
|
|
|
|
|
|
|
->line('obj = sv_bless(newRV_noinc((SV*)self), gv_stashpv("Hypersonic::UA::Response", GV_ADD));') |
|
251
|
|
|
|
|
|
|
->line('ST(0) = sv_2mortal(obj);') |
|
252
|
|
|
|
|
|
|
->xs_return('1') |
|
253
|
|
|
|
|
|
|
->xs_end |
|
254
|
|
|
|
|
|
|
->blank; |
|
255
|
|
|
|
|
|
|
} |
|
256
|
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
sub gen_xs_status { |
|
258
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
259
|
0
|
|
|
|
|
|
$class->_gen_slot_accessor($builder, 'status', 'RESP_SLOT_STATUS'); |
|
260
|
|
|
|
|
|
|
} |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
sub gen_xs_status_text { |
|
263
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
264
|
0
|
|
|
|
|
|
$class->_gen_slot_accessor($builder, 'status_text', 'RESP_SLOT_STATUS_TEXT'); |
|
265
|
|
|
|
|
|
|
} |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub gen_xs_body { |
|
268
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
269
|
0
|
|
|
|
|
|
$class->_gen_slot_accessor($builder, 'body', 'RESP_SLOT_BODY'); |
|
270
|
|
|
|
|
|
|
} |
|
271
|
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
sub gen_xs_content_type { |
|
273
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
274
|
0
|
|
|
|
|
|
$class->_gen_slot_accessor($builder, 'content_type', 'RESP_SLOT_CONTENT_TYPE'); |
|
275
|
|
|
|
|
|
|
} |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
sub gen_xs_content_length { |
|
278
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
279
|
0
|
|
|
|
|
|
$class->_gen_slot_accessor($builder, 'content_length', 'RESP_SLOT_CONTENT_LENGTH'); |
|
280
|
|
|
|
|
|
|
} |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
sub gen_xs_raw_headers { |
|
283
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
284
|
0
|
|
|
|
|
|
$class->_gen_slot_accessor($builder, 'raw_headers', 'RESP_SLOT_RAW_HEADERS'); |
|
285
|
|
|
|
|
|
|
} |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
sub _gen_slot_accessor { |
|
288
|
0
|
|
|
0
|
|
|
my ($class, $builder, $name, $slot) = @_; |
|
289
|
|
|
|
|
|
|
|
|
290
|
0
|
|
|
|
|
|
$builder->comment("Get $name") |
|
291
|
|
|
|
|
|
|
->xs_function("xs_response_$name") |
|
292
|
|
|
|
|
|
|
->xs_preamble |
|
293
|
|
|
|
|
|
|
->line('if (items != 1) croak("Usage: ' . $name . '(self)");') |
|
294
|
|
|
|
|
|
|
->line('SV** ary = AvARRAY((AV*)SvRV(ST(0)));') |
|
295
|
|
|
|
|
|
|
->line("ST(0) = ary[$slot];") |
|
296
|
|
|
|
|
|
|
->xs_return('1') |
|
297
|
|
|
|
|
|
|
->xs_end |
|
298
|
|
|
|
|
|
|
->blank; |
|
299
|
|
|
|
|
|
|
} |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
sub gen_xs_headers { |
|
302
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
303
|
|
|
|
|
|
|
|
|
304
|
0
|
|
|
|
|
|
$builder->comment('Get headers hashref') |
|
305
|
|
|
|
|
|
|
->xs_function('xs_response_headers') |
|
306
|
|
|
|
|
|
|
->xs_preamble |
|
307
|
|
|
|
|
|
|
->line('if (items != 1) croak("Usage: headers(self)");') |
|
308
|
|
|
|
|
|
|
->line('SV** ary = AvARRAY((AV*)SvRV(ST(0)));') |
|
309
|
|
|
|
|
|
|
->line('ST(0) = ary[RESP_SLOT_HEADERS];') |
|
310
|
|
|
|
|
|
|
->xs_return('1') |
|
311
|
|
|
|
|
|
|
->xs_end |
|
312
|
|
|
|
|
|
|
->blank; |
|
313
|
|
|
|
|
|
|
} |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
sub gen_xs_header { |
|
316
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
317
|
|
|
|
|
|
|
|
|
318
|
0
|
|
|
|
|
|
$builder->comment('Get single header (normalized)') |
|
319
|
|
|
|
|
|
|
->xs_function('xs_response_header') |
|
320
|
|
|
|
|
|
|
->xs_preamble |
|
321
|
|
|
|
|
|
|
->line('if (items != 2) croak("Usage: header(self, name)");') |
|
322
|
|
|
|
|
|
|
->line('SV** ary = AvARRAY((AV*)SvRV(ST(0)));') |
|
323
|
|
|
|
|
|
|
->line('HV* headers = (HV*)SvRV(ary[RESP_SLOT_HEADERS]);') |
|
324
|
|
|
|
|
|
|
->blank |
|
325
|
|
|
|
|
|
|
->line('STRLEN klen;') |
|
326
|
|
|
|
|
|
|
->line('const char* key = SvPV(ST(1), klen);') |
|
327
|
|
|
|
|
|
|
->blank |
|
328
|
|
|
|
|
|
|
->line('char norm_key[256];') |
|
329
|
|
|
|
|
|
|
->line('size_t i;') |
|
330
|
|
|
|
|
|
|
->line('if (klen > 255) klen = 255;') |
|
331
|
|
|
|
|
|
|
->line('for (i = 0; i < klen; i++) {') |
|
332
|
|
|
|
|
|
|
->line(' char c = key[i];') |
|
333
|
|
|
|
|
|
|
->line(' if (c >= \'A\' && c <= \'Z\') c += 32;') |
|
334
|
|
|
|
|
|
|
->line(' else if (c == \'-\') c = \'_\';') |
|
335
|
|
|
|
|
|
|
->line(' norm_key[i] = c;') |
|
336
|
|
|
|
|
|
|
->line('}') |
|
337
|
|
|
|
|
|
|
->blank |
|
338
|
|
|
|
|
|
|
->line('SV** val = hv_fetch(headers, norm_key, klen, 0);') |
|
339
|
|
|
|
|
|
|
->line('ST(0) = (val && *val) ? *val : &PL_sv_undef;') |
|
340
|
|
|
|
|
|
|
->xs_return('1') |
|
341
|
|
|
|
|
|
|
->xs_end |
|
342
|
|
|
|
|
|
|
->blank; |
|
343
|
|
|
|
|
|
|
} |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
sub gen_xs_is_success { |
|
346
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
347
|
0
|
|
|
|
|
|
$class->_gen_status_check($builder, 'is_success', 'status >= 200 && status < 300'); |
|
348
|
|
|
|
|
|
|
} |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
sub gen_xs_is_redirect { |
|
351
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
352
|
0
|
|
|
|
|
|
$class->_gen_status_check($builder, 'is_redirect', 'status >= 300 && status < 400'); |
|
353
|
|
|
|
|
|
|
} |
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
sub gen_xs_is_error { |
|
356
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
357
|
0
|
|
|
|
|
|
$class->_gen_status_check($builder, 'is_error', 'status >= 400'); |
|
358
|
|
|
|
|
|
|
} |
|
359
|
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
sub gen_xs_is_client_error { |
|
361
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
362
|
0
|
|
|
|
|
|
$class->_gen_status_check($builder, 'is_client_error', 'status >= 400 && status < 500'); |
|
363
|
|
|
|
|
|
|
} |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
sub gen_xs_is_server_error { |
|
366
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
367
|
0
|
|
|
|
|
|
$class->_gen_status_check($builder, 'is_server_error', 'status >= 500'); |
|
368
|
|
|
|
|
|
|
} |
|
369
|
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
sub _gen_status_check { |
|
371
|
0
|
|
|
0
|
|
|
my ($class, $builder, $name, $condition) = @_; |
|
372
|
|
|
|
|
|
|
|
|
373
|
0
|
|
|
|
|
|
$builder->comment("Check $name") |
|
374
|
|
|
|
|
|
|
->xs_function("xs_response_$name") |
|
375
|
|
|
|
|
|
|
->xs_preamble |
|
376
|
|
|
|
|
|
|
->line('SV** ary = AvARRAY((AV*)SvRV(ST(0)));') |
|
377
|
|
|
|
|
|
|
->line('IV status = SvIV(ary[RESP_SLOT_STATUS]);') |
|
378
|
|
|
|
|
|
|
->line("ST(0) = ($condition) ? &PL_sv_yes : &PL_sv_no;") |
|
379
|
|
|
|
|
|
|
->xs_return('1') |
|
380
|
|
|
|
|
|
|
->xs_end |
|
381
|
|
|
|
|
|
|
->blank; |
|
382
|
|
|
|
|
|
|
} |
|
383
|
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
sub gen_xs_is_json { |
|
385
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
386
|
|
|
|
|
|
|
|
|
387
|
0
|
|
|
|
|
|
$builder->comment('Check if content type is JSON') |
|
388
|
|
|
|
|
|
|
->xs_function('xs_response_is_json') |
|
389
|
|
|
|
|
|
|
->xs_preamble |
|
390
|
|
|
|
|
|
|
->line('SV** ary = AvARRAY((AV*)SvRV(ST(0)));') |
|
391
|
|
|
|
|
|
|
->line('SV* ct = ary[RESP_SLOT_CONTENT_TYPE];') |
|
392
|
|
|
|
|
|
|
->if('!SvOK(ct)') |
|
393
|
|
|
|
|
|
|
->line('ST(0) = &PL_sv_no;') |
|
394
|
|
|
|
|
|
|
->line('XSRETURN(1);') |
|
395
|
|
|
|
|
|
|
->endif |
|
396
|
|
|
|
|
|
|
->line('STRLEN ct_len;') |
|
397
|
|
|
|
|
|
|
->line('const char* ct_str = SvPV(ct, ct_len);') |
|
398
|
|
|
|
|
|
|
->line('ST(0) = (strstr(ct_str, "application/json") || strstr(ct_str, "+json")) ? &PL_sv_yes : &PL_sv_no;') |
|
399
|
|
|
|
|
|
|
->xs_return('1') |
|
400
|
|
|
|
|
|
|
->xs_end |
|
401
|
|
|
|
|
|
|
->blank; |
|
402
|
|
|
|
|
|
|
} |
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
sub gen_xs_json { |
|
405
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
406
|
|
|
|
|
|
|
|
|
407
|
0
|
|
|
|
|
|
$builder->comment('Get parsed JSON (lazy, cached)') |
|
408
|
|
|
|
|
|
|
->xs_function('xs_response_json') |
|
409
|
|
|
|
|
|
|
->xs_preamble |
|
410
|
|
|
|
|
|
|
->line('SV** ary = AvARRAY((AV*)SvRV(ST(0)));') |
|
411
|
|
|
|
|
|
|
->blank |
|
412
|
|
|
|
|
|
|
->comment('Return cached if available') |
|
413
|
|
|
|
|
|
|
->line('SV* cached = ary[RESP_SLOT_JSON];') |
|
414
|
|
|
|
|
|
|
->if('SvOK(cached)') |
|
415
|
|
|
|
|
|
|
->line('ST(0) = cached;') |
|
416
|
|
|
|
|
|
|
->line('XSRETURN(1);') |
|
417
|
|
|
|
|
|
|
->endif |
|
418
|
|
|
|
|
|
|
->blank |
|
419
|
|
|
|
|
|
|
->comment('Parse JSON using Cpanel::JSON::XS') |
|
420
|
|
|
|
|
|
|
->line('dSP;') |
|
421
|
|
|
|
|
|
|
->line('PUSHMARK(SP);') |
|
422
|
|
|
|
|
|
|
->line('XPUSHs(ary[RESP_SLOT_BODY]);') |
|
423
|
|
|
|
|
|
|
->line('PUTBACK;') |
|
424
|
|
|
|
|
|
|
->line('int count = call_pv("Cpanel::JSON::XS::decode_json", G_SCALAR | G_EVAL);') |
|
425
|
|
|
|
|
|
|
->line('SPAGAIN;') |
|
426
|
|
|
|
|
|
|
->blank |
|
427
|
|
|
|
|
|
|
->if('SvTRUE(ERRSV)') |
|
428
|
|
|
|
|
|
|
->line('POPs;') |
|
429
|
|
|
|
|
|
|
->line('ST(0) = &PL_sv_undef;') |
|
430
|
|
|
|
|
|
|
->line('XSRETURN(1);') |
|
431
|
|
|
|
|
|
|
->endif |
|
432
|
|
|
|
|
|
|
->blank |
|
433
|
|
|
|
|
|
|
->line('SV* result = POPs;') |
|
434
|
|
|
|
|
|
|
->line('SvREFCNT_inc(result);') |
|
435
|
|
|
|
|
|
|
->line('av_store((AV*)SvRV(ST(0)), RESP_SLOT_JSON, result);') |
|
436
|
|
|
|
|
|
|
->line('ST(0) = result;') |
|
437
|
|
|
|
|
|
|
->xs_return('1') |
|
438
|
|
|
|
|
|
|
->xs_end |
|
439
|
|
|
|
|
|
|
->blank; |
|
440
|
|
|
|
|
|
|
} |
|
441
|
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
sub gen_xs_location { |
|
443
|
0
|
|
|
0
|
0
|
|
my ($class, $builder) = @_; |
|
444
|
|
|
|
|
|
|
|
|
445
|
0
|
|
|
|
|
|
$builder->comment('Get Location header') |
|
446
|
|
|
|
|
|
|
->xs_function('xs_response_location') |
|
447
|
|
|
|
|
|
|
->xs_preamble |
|
448
|
|
|
|
|
|
|
->line('SV** ary = AvARRAY((AV*)SvRV(ST(0)));') |
|
449
|
|
|
|
|
|
|
->line('HV* headers = (HV*)SvRV(ary[RESP_SLOT_HEADERS]);') |
|
450
|
|
|
|
|
|
|
->line('SV** val = hv_fetchs(headers, "location", 0);') |
|
451
|
|
|
|
|
|
|
->line('ST(0) = (val && *val) ? *val : &PL_sv_undef;') |
|
452
|
|
|
|
|
|
|
->xs_return('1') |
|
453
|
|
|
|
|
|
|
->xs_end |
|
454
|
|
|
|
|
|
|
->blank; |
|
455
|
|
|
|
|
|
|
} |
|
456
|
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
1; |
|
458
|
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
__END__ |