File Coverage

blib/lib/Hypersonic/Request.pm
Criterion Covered Total %
statement 79 81 97.5
branch 9 12 75.0
condition 1 2 50.0
subroutine 12 13 92.3
pod 4 6 66.6
total 105 114 92.1


line stmt bran cond sub pod time code
1             package Hypersonic::Request;
2              
3 16     16   80909 use strict;
  16         31  
  16         708  
4 16     16   94 use warnings;
  16         41  
  16         2087  
5 16     16   403 use 5.010;
  16         96  
6              
7             our $VERSION = '0.15';
8              
9             # JIT-compiled Request object using array-based slots for maximum speed
10             # Generates XS accessors at compile time via XS::JIT::Builder
11              
12 16     16   542 use XS::JIT;
  16         884  
  16         804  
13 16     16   595 use XS::JIT::Builder;
  16         1231  
  16         2681  
14              
15             # Request slots (array indices) - these map to the array-based request object
16             # The order MUST match what C code generates in call_dynamic_handler
17             use constant {
18 16         4994 SLOT_METHOD => 0,
19             SLOT_PATH => 1,
20             SLOT_BODY => 2,
21             SLOT_PARAMS => 3, # HV* of named path params
22             SLOT_QUERY => 4, # HV* of query string params
23             SLOT_QUERY_STRING => 5, # Raw query string
24             SLOT_HEADERS => 6, # HV* of headers
25             SLOT_COOKIES => 7, # HV* of cookies
26             SLOT_JSON => 8, # Parsed JSON body
27             SLOT_FORM => 9, # HV* of form data
28             SLOT_SEGMENTS => 10, # AV* of path segments
29             SLOT_ID => 11, # Last path segment (legacy)
30             SLOT_SESSION => 12, # Session data hashref
31             SLOT_SESSION_ID => 13, # Session ID string
32             SLOT_SESSION_MODIFIED => 14, # Session modified flag
33             SLOT_COUNT => 15, # Total number of slots
34 16     16   112 };
  16         25  
35              
36             # Export slot constants for direct access
37 16     16   142 use Exporter 'import';
  16         23  
  16         29515  
38             our @EXPORT_OK = qw(
39             SLOT_METHOD SLOT_PATH SLOT_BODY SLOT_PARAMS SLOT_QUERY
40             SLOT_QUERY_STRING SLOT_HEADERS SLOT_COOKIES SLOT_JSON
41             SLOT_FORM SLOT_SEGMENTS SLOT_ID
42             SLOT_SESSION SLOT_SESSION_ID SLOT_SESSION_MODIFIED
43             SLOT_COUNT
44             );
45              
46             my $COMPILED = 0;
47             my $MODULE_ID = 0;
48              
49             # Unified compile interface
50             sub compile {
51 0     0 0 0 my ($class, %opts) = @_;
52 0         0 return $class->compile_accessors(%opts);
53             }
54              
55             # Generate and compile XS accessors
56             sub compile_accessors {
57 22     22 0 135350 my ($class, %opts) = @_;
58              
59 22 100       96 return 1 if $COMPILED;
60              
61 16   50     105 my $cache_dir = $opts{cache_dir} // '_hypersonic_cache/request';
62 16         54 my $module_name = 'Hypersonic::Request::Accessors_' . $MODULE_ID++;
63              
64 16         425 my $builder = XS::JIT::Builder->new;
65              
66             # Generate read-only accessors for simple scalar slots
67 16         350 $builder->op_ro_accessor('jit_method', SLOT_METHOD);
68 16         126 $builder->op_ro_accessor('jit_path', SLOT_PATH);
69 16         86 $builder->op_ro_accessor('jit_body', SLOT_BODY);
70 16         81 $builder->op_ro_accessor('jit_query_string', SLOT_QUERY_STRING);
71 16         111 $builder->op_ro_accessor('jit_id', SLOT_ID);
72              
73             # Generate accessors for hashref slots (params, query, headers, cookies, form)
74             # These return the hashref directly for fast access
75 16         84 $builder->op_ro_accessor('jit_params', SLOT_PARAMS);
76 16         91 $builder->op_ro_accessor('jit_query', SLOT_QUERY);
77 16         79 $builder->op_ro_accessor('jit_headers', SLOT_HEADERS);
78 16         76 $builder->op_ro_accessor('jit_cookies', SLOT_COOKIES);
79 16         85 $builder->op_ro_accessor('jit_form', SLOT_FORM);
80 16         81 $builder->op_ro_accessor('jit_json', SLOT_JSON);
81 16         128 $builder->op_ro_accessor('jit_segments', SLOT_SEGMENTS);
82              
83             # Generate param($name) accessor - fetches from params hashref
84 16         420 $builder->xs_function('jit_param')
85             ->xs_preamble
86             ->line('SV** ary = AvARRAY((AV*)SvRV(ST(0)));')
87             ->line('HV* params = (HV*)SvRV(ary[' . SLOT_PARAMS . ']);')
88             ->if('items > 1')
89             ->line('STRLEN klen;')
90             ->line('const char* key = SvPV(ST(1), klen);')
91             ->line('SV** val = hv_fetch(params, key, klen, 0);')
92             ->line('ST(0) = val && *val ? *val : &PL_sv_undef;')
93             ->else
94             ->line('ST(0) = newRV_inc((SV*)params);')
95             ->endif
96             ->xs_return('1')
97             ->xs_end;
98              
99             # Generate query_param($name) accessor
100 16         437 $builder->xs_function('jit_query_param')
101             ->xs_preamble
102             ->line('SV** ary = AvARRAY((AV*)SvRV(ST(0)));')
103             ->line('HV* query = (HV*)SvRV(ary[' . SLOT_QUERY . ']);')
104             ->if('items > 1')
105             ->line('STRLEN klen;')
106             ->line('const char* key = SvPV(ST(1), klen);')
107             ->line('SV** val = hv_fetch(query, key, klen, 0);')
108             ->line('ST(0) = val && *val ? *val : &PL_sv_undef;')
109             ->else
110             ->line('ST(0) = newRV_inc((SV*)query);')
111             ->endif
112             ->xs_return('1')
113             ->xs_end;
114              
115             # Generate header($name) accessor - normalizes header name
116 16         661 $builder->xs_function('jit_header')
117             ->xs_preamble
118             ->line('SV** ary = AvARRAY((AV*)SvRV(ST(0)));')
119             ->line('HV* headers = (HV*)SvRV(ary[' . SLOT_HEADERS . ']);')
120             ->if('items > 1')
121             ->line('STRLEN klen;')
122             ->line('const char* key = SvPV(ST(1), klen);')
123             ->comment('Normalize: lowercase, hyphens to underscores')
124             ->line('char norm_key[256];')
125             ->line('int i;')
126             ->line('for (i = 0; i < klen && i < 255; i++) {')
127             ->line(' char c = key[i];')
128             ->line(' norm_key[i] = (c >= \'A\' && c <= \'Z\') ? c + 32 : (c == \'-\') ? \'_\' : c;')
129             ->line('}')
130             ->line('norm_key[i] = 0;')
131             ->line('SV** val = hv_fetch(headers, norm_key, i, 0);')
132             ->line('ST(0) = val && *val ? *val : &PL_sv_undef;')
133             ->else
134             ->line('ST(0) = newRV_inc((SV*)headers);')
135             ->endif
136             ->xs_return('1')
137             ->xs_end;
138              
139             # Generate cookie($name) accessor
140 16         579 $builder->xs_function('jit_cookie')
141             ->xs_preamble
142             ->line('SV** ary = AvARRAY((AV*)SvRV(ST(0)));')
143             ->line('HV* cookies = (HV*)SvRV(ary[' . SLOT_COOKIES . ']);')
144             ->if('items > 1')
145             ->line('STRLEN klen;')
146             ->line('const char* key = SvPV(ST(1), klen);')
147             ->line('SV** val = hv_fetch(cookies, key, klen, 0);')
148             ->line('ST(0) = val && *val ? *val : &PL_sv_undef;')
149             ->else
150             ->line('ST(0) = newRV_inc((SV*)cookies);')
151             ->endif
152             ->xs_return('1')
153             ->xs_end;
154              
155             # Generate form($name) accessor
156 16         409 $builder->xs_function('jit_form_field')
157             ->xs_preamble
158             ->line('SV** ary = AvARRAY((AV*)SvRV(ST(0)));')
159             ->line('HV* form = (HV*)SvRV(ary[' . SLOT_FORM . ']);')
160             ->if('items > 1')
161             ->line('STRLEN klen;')
162             ->line('const char* key = SvPV(ST(1), klen);')
163             ->line('SV** val = hv_fetch(form, key, klen, 0);')
164             ->line('ST(0) = val && *val ? *val : &PL_sv_undef;')
165             ->else
166             ->line('ST(0) = newRV_inc((SV*)form);')
167             ->endif
168             ->xs_return('1')
169             ->xs_end;
170              
171             # Generate is_json() - check Content-Type header
172 16         527 $builder->xs_function('jit_is_json')
173             ->xs_preamble
174             ->line('SV** ary = AvARRAY((AV*)SvRV(ST(0)));')
175             ->line('HV* headers = (HV*)SvRV(ary[' . SLOT_HEADERS . ']);')
176             ->line('SV** ct = hv_fetch(headers, "content_type", 12, 0);')
177             ->if('ct && *ct && SvOK(*ct)')
178             ->line('STRLEN len;')
179             ->line('const char* str = SvPV(*ct, len);')
180             ->if('len >= 16 && memcmp(str, "application/json", 16) == 0')
181             ->line('ST(0) = &PL_sv_yes;')
182             ->else
183             ->line('ST(0) = &PL_sv_no;')
184             ->endif
185             ->else
186             ->line('ST(0) = &PL_sv_no;')
187             ->endif
188             ->xs_return('1')
189             ->xs_end;
190              
191             # Generate is_form() - check Content-Type header
192 16         356 $builder->xs_function('jit_is_form')
193             ->xs_preamble
194             ->line('SV** ary = AvARRAY((AV*)SvRV(ST(0)));')
195             ->line('HV* headers = (HV*)SvRV(ary[' . SLOT_HEADERS . ']);')
196             ->line('SV** ct = hv_fetch(headers, "content_type", 12, 0);')
197             ->if('ct && *ct && SvOK(*ct)')
198             ->line('STRLEN len;')
199             ->line('const char* str = SvPV(*ct, len);')
200             ->if('len >= 33 && memcmp(str, "application/x-www-form-urlencoded", 33) == 0')
201             ->line('ST(0) = &PL_sv_yes;')
202             ->else
203             ->line('ST(0) = &PL_sv_no;')
204             ->endif
205             ->else
206             ->line('ST(0) = &PL_sv_no;')
207             ->endif
208             ->xs_return('1')
209             ->xs_end;
210              
211             # Build functions hash
212 16         430 my %functions = (
213             # Simple slot accessors
214             'Hypersonic::Request::method' => { source => 'jit_method', is_xs_native => 1 },
215             'Hypersonic::Request::path' => { source => 'jit_path', is_xs_native => 1 },
216             'Hypersonic::Request::body' => { source => 'jit_body', is_xs_native => 1 },
217             'Hypersonic::Request::query_string' => { source => 'jit_query_string', is_xs_native => 1 },
218             'Hypersonic::Request::id' => { source => 'jit_id', is_xs_native => 1 },
219              
220             # Hashref slot accessors
221             'Hypersonic::Request::params' => { source => 'jit_params', is_xs_native => 1 },
222             'Hypersonic::Request::query' => { source => 'jit_query', is_xs_native => 1 },
223             'Hypersonic::Request::headers' => { source => 'jit_headers', is_xs_native => 1 },
224             'Hypersonic::Request::cookies' => { source => 'jit_cookies', is_xs_native => 1 },
225             'Hypersonic::Request::form' => { source => 'jit_form_field', is_xs_native => 1 },
226             'Hypersonic::Request::json' => { source => 'jit_json', is_xs_native => 1 },
227             'Hypersonic::Request::segments' => { source => 'jit_segments', is_xs_native => 1 },
228              
229             # Keyed accessors
230             'Hypersonic::Request::param' => { source => 'jit_param', is_xs_native => 1 },
231             'Hypersonic::Request::query_param' => { source => 'jit_query_param', is_xs_native => 1 },
232             'Hypersonic::Request::header' => { source => 'jit_header', is_xs_native => 1 },
233             'Hypersonic::Request::cookie' => { source => 'jit_cookie', is_xs_native => 1 },
234              
235             # Type checks
236             'Hypersonic::Request::is_json' => { source => 'jit_is_json', is_xs_native => 1 },
237             'Hypersonic::Request::is_form' => { source => 'jit_is_form', is_xs_native => 1 },
238             );
239              
240             # JIT: Only generate response helpers if requested
241 16 100       75 if ($opts{response_helpers}) {
242             # text_response($text, $status) - returns {status, headers, body}
243 1         25 $builder->xs_function('jit_text_response')
244             ->xs_preamble
245             ->line('SV* text = items > 1 ? ST(1) : newSVpvs("");')
246             ->line('IV status = items > 2 && SvOK(ST(2)) ? SvIV(ST(2)) : 200;')
247             ->line('HV* resp = newHV();')
248             ->line('HV* hdrs = newHV();')
249             ->line('hv_store(hdrs, "Content-Type", 12, newSVpvs("text/plain"), 0);')
250             ->line('hv_store(resp, "status", 6, newSViv(status), 0);')
251             ->line('hv_store(resp, "headers", 7, newRV_noinc((SV*)hdrs), 0);')
252             ->line('hv_store(resp, "body", 4, newSVsv(text), 0);')
253             ->line('ST(0) = sv_2mortal(newRV_noinc((SV*)resp));')
254             ->xs_return('1')
255             ->xs_end;
256              
257             # html_response($html, $status) - returns {status, headers, body}
258 1         44 $builder->xs_function('jit_html_response')
259             ->xs_preamble
260             ->line('SV* html = items > 1 ? ST(1) : newSVpvs("");')
261             ->line('IV status = items > 2 && SvOK(ST(2)) ? SvIV(ST(2)) : 200;')
262             ->line('HV* resp = newHV();')
263             ->line('HV* hdrs = newHV();')
264             ->line('hv_store(hdrs, "Content-Type", 12, newSVpvs("text/html"), 0);')
265             ->line('hv_store(resp, "status", 6, newSViv(status), 0);')
266             ->line('hv_store(resp, "headers", 7, newRV_noinc((SV*)hdrs), 0);')
267             ->line('hv_store(resp, "body", 4, newSVsv(html), 0);')
268             ->line('ST(0) = sv_2mortal(newRV_noinc((SV*)resp));')
269             ->xs_return('1')
270             ->xs_end;
271              
272             # redirect($url, $status) - returns {status, headers, body}
273 1         19 $builder->xs_function('jit_redirect')
274             ->xs_preamble
275             ->line('SV* url = items > 1 ? ST(1) : newSVpvs("/");')
276             ->line('IV status = items > 2 && SvOK(ST(2)) ? SvIV(ST(2)) : 302;')
277             ->line('HV* resp = newHV();')
278             ->line('HV* hdrs = newHV();')
279             ->line('hv_store(hdrs, "Location", 8, newSVsv(url), 0);')
280             ->line('hv_store(resp, "status", 6, newSViv(status), 0);')
281             ->line('hv_store(resp, "headers", 7, newRV_noinc((SV*)hdrs), 0);')
282             ->line('hv_store(resp, "body", 4, newSVpvs(""), 0);')
283             ->line('ST(0) = sv_2mortal(newRV_noinc((SV*)resp));')
284             ->xs_return('1')
285             ->xs_end;
286              
287             # error($message, $status) - returns {status, headers, body} as JSON
288 1         41 $builder->xs_function('jit_error')
289             ->xs_preamble
290             ->line('const char* msg = "Internal Server Error";')
291             ->line('STRLEN msg_len = 21;')
292             ->line('if (items > 1 && SvOK(ST(1))) { msg = SvPV(ST(1), msg_len); }')
293             ->line('IV status = items > 2 && SvOK(ST(2)) ? SvIV(ST(2)) : 500;')
294             ->line('HV* resp = newHV();')
295             ->line('HV* hdrs = newHV();')
296             ->line('hv_store(hdrs, "Content-Type", 12, newSVpvs("application/json"), 0);')
297             ->line('hv_store(resp, "status", 6, newSViv(status), 0);')
298             ->line('hv_store(resp, "headers", 7, newRV_noinc((SV*)hdrs), 0);')
299             ->comment('Build JSON: {"error":"message"}')
300             ->line('SV* body = newSV(msg_len + 12);')
301             ->line('sv_setpvs(body, "{\"error\":\"");')
302             ->line('sv_catpvn(body, msg, msg_len);')
303             ->line('sv_catpvs(body, "\"}");')
304             ->line('hv_store(resp, "body", 4, body, 0);')
305             ->line('ST(0) = sv_2mortal(newRV_noinc((SV*)resp));')
306             ->xs_return('1')
307             ->xs_end;
308              
309             # not_found($message) - calls error with 404
310 1         53 $builder->xs_function('jit_not_found')
311             ->xs_preamble
312             ->line('const char* msg = "Not Found";')
313             ->line('STRLEN msg_len = 9;')
314             ->line('if (items > 1 && SvOK(ST(1))) { msg = SvPV(ST(1), msg_len); }')
315             ->line('HV* resp = newHV();')
316             ->line('HV* hdrs = newHV();')
317             ->line('hv_store(hdrs, "Content-Type", 12, newSVpvs("application/json"), 0);')
318             ->line('hv_store(resp, "status", 6, newSViv(404), 0);')
319             ->line('hv_store(resp, "headers", 7, newRV_noinc((SV*)hdrs), 0);')
320             ->line('SV* body = newSV(msg_len + 12);')
321             ->line('sv_setpvs(body, "{\"error\":\"");')
322             ->line('sv_catpvn(body, msg, msg_len);')
323             ->line('sv_catpvs(body, "\"}");')
324             ->line('hv_store(resp, "body", 4, body, 0);')
325             ->line('ST(0) = sv_2mortal(newRV_noinc((SV*)resp));')
326             ->xs_return('1')
327             ->xs_end;
328              
329             # bad_request($message) - calls error with 400
330 1         39 $builder->xs_function('jit_bad_request')
331             ->xs_preamble
332             ->line('const char* msg = "Bad Request";')
333             ->line('STRLEN msg_len = 11;')
334             ->line('if (items > 1 && SvOK(ST(1))) { msg = SvPV(ST(1), msg_len); }')
335             ->line('HV* resp = newHV();')
336             ->line('HV* hdrs = newHV();')
337             ->line('hv_store(hdrs, "Content-Type", 12, newSVpvs("application/json"), 0);')
338             ->line('hv_store(resp, "status", 6, newSViv(400), 0);')
339             ->line('hv_store(resp, "headers", 7, newRV_noinc((SV*)hdrs), 0);')
340             ->line('SV* body = newSV(msg_len + 12);')
341             ->line('sv_setpvs(body, "{\"error\":\"");')
342             ->line('sv_catpvn(body, msg, msg_len);')
343             ->line('sv_catpvs(body, "\"}");')
344             ->line('hv_store(resp, "body", 4, body, 0);')
345             ->line('ST(0) = sv_2mortal(newRV_noinc((SV*)resp));')
346             ->xs_return('1')
347             ->xs_end;
348              
349             # unauthorized($message) - calls error with 401
350 1         27 $builder->xs_function('jit_unauthorized')
351             ->xs_preamble
352             ->line('const char* msg = "Unauthorized";')
353             ->line('STRLEN msg_len = 12;')
354             ->line('if (items > 1 && SvOK(ST(1))) { msg = SvPV(ST(1), msg_len); }')
355             ->line('HV* resp = newHV();')
356             ->line('HV* hdrs = newHV();')
357             ->line('hv_store(hdrs, "Content-Type", 12, newSVpvs("application/json"), 0);')
358             ->line('hv_store(resp, "status", 6, newSViv(401), 0);')
359             ->line('hv_store(resp, "headers", 7, newRV_noinc((SV*)hdrs), 0);')
360             ->line('SV* body = newSV(msg_len + 12);')
361             ->line('sv_setpvs(body, "{\"error\":\"");')
362             ->line('sv_catpvn(body, msg, msg_len);')
363             ->line('sv_catpvs(body, "\"}");')
364             ->line('hv_store(resp, "body", 4, body, 0);')
365             ->line('ST(0) = sv_2mortal(newRV_noinc((SV*)resp));')
366             ->xs_return('1')
367             ->xs_end;
368              
369             # forbidden($message) - calls error with 403
370 1         53 $builder->xs_function('jit_forbidden')
371             ->xs_preamble
372             ->line('const char* msg = "Forbidden";')
373             ->line('STRLEN msg_len = 9;')
374             ->line('if (items > 1 && SvOK(ST(1))) { msg = SvPV(ST(1), msg_len); }')
375             ->line('HV* resp = newHV();')
376             ->line('HV* hdrs = newHV();')
377             ->line('hv_store(hdrs, "Content-Type", 12, newSVpvs("application/json"), 0);')
378             ->line('hv_store(resp, "status", 6, newSViv(403), 0);')
379             ->line('hv_store(resp, "headers", 7, newRV_noinc((SV*)hdrs), 0);')
380             ->line('SV* body = newSV(msg_len + 12);')
381             ->line('sv_setpvs(body, "{\"error\":\"");')
382             ->line('sv_catpvn(body, msg, msg_len);')
383             ->line('sv_catpvs(body, "\"}");')
384             ->line('hv_store(resp, "body", 4, body, 0);')
385             ->line('ST(0) = sv_2mortal(newRV_noinc((SV*)resp));')
386             ->xs_return('1')
387             ->xs_end;
388              
389             # json_response($data, $status) - needs to call Perl JSON encoder
390             # This one calls back to Perl for Cpanel::JSON::XS::encode_json
391 1         33 $builder->xs_function('jit_json_response')
392             ->xs_preamble
393             ->line('SV* data = items > 1 ? ST(1) : newRV_noinc((SV*)newHV());')
394             ->line('IV status = items > 2 && SvOK(ST(2)) ? SvIV(ST(2)) : 200;')
395             ->line('ENTER; SAVETMPS;')
396             ->line('PUSHMARK(SP);')
397             ->line('XPUSHs(data);')
398             ->line('PUTBACK;')
399             ->line('int count = call_pv("Cpanel::JSON::XS::encode_json", G_SCALAR);')
400             ->line('SPAGAIN;')
401             ->line('SV* json_body = count > 0 ? POPs : newSVpvs("{}");')
402             ->line('SvREFCNT_inc(json_body);')
403             ->line('PUTBACK; FREETMPS; LEAVE;')
404             ->line('HV* resp = newHV();')
405             ->line('HV* hdrs = newHV();')
406             ->line('hv_store(hdrs, "Content-Type", 12, newSVpvs("application/json"), 0);')
407             ->line('hv_store(resp, "status", 6, newSViv(status), 0);')
408             ->line('hv_store(resp, "headers", 7, newRV_noinc((SV*)hdrs), 0);')
409             ->line('hv_store(resp, "body", 4, json_body, 0);')
410             ->line('ST(0) = sv_2mortal(newRV_noinc((SV*)resp));')
411             ->xs_return('1')
412             ->xs_end;
413              
414             # Add response helpers to functions hash
415 1         5 $functions{'Hypersonic::Request::text_response'} = { source => 'jit_text_response', is_xs_native => 1 };
416 1         5 $functions{'Hypersonic::Request::html_response'} = { source => 'jit_html_response', is_xs_native => 1 };
417 1         3 $functions{'Hypersonic::Request::redirect'} = { source => 'jit_redirect', is_xs_native => 1 };
418 1         4 $functions{'Hypersonic::Request::error'} = { source => 'jit_error', is_xs_native => 1 };
419 1         5 $functions{'Hypersonic::Request::not_found'} = { source => 'jit_not_found', is_xs_native => 1 };
420 1         4 $functions{'Hypersonic::Request::bad_request'} = { source => 'jit_bad_request', is_xs_native => 1 };
421 1         4 $functions{'Hypersonic::Request::unauthorized'} = { source => 'jit_unauthorized', is_xs_native => 1 };
422 1         4 $functions{'Hypersonic::Request::forbidden'} = { source => 'jit_forbidden', is_xs_native => 1 };
423 1         3 $functions{'Hypersonic::Request::json_response'} = { source => 'jit_json_response', is_xs_native => 1 };
424             }
425              
426             # Compile via XS::JIT
427             XS::JIT->compile(
428 16         9655197 code => $builder->code,
429             name => $module_name,
430             cache_dir => $cache_dir,
431             functions => \%functions,
432             );
433              
434 16         199 $COMPILED = 1;
435 16         687 return 1;
436             }
437              
438             # Session methods - implemented in Perl because they need to interact
439             # with the session store. JIT: require cached after first load.
440              
441             my $SESSION_LOADED;
442              
443             sub session {
444 5     5 1 5920 my $self = shift;
445 5 100       22 require Hypersonic::Session unless $SESSION_LOADED++;
446 5         39 return Hypersonic::Session::get_set($self, @_);
447             }
448              
449             sub session_data {
450 1     1 1 2 my $self = shift;
451 1 50       4 require Hypersonic::Session unless $SESSION_LOADED++;
452 1         4 return Hypersonic::Session::get_all($self);
453             }
454              
455             sub session_clear {
456 1     1 1 739 my $self = shift;
457 1 50       6 require Hypersonic::Session unless $SESSION_LOADED++;
458 1         5 return Hypersonic::Session::clear($self);
459             }
460              
461             sub session_regenerate {
462 1     1 1 25 my $self = shift;
463 1 50       9 require Hypersonic::Session unless $SESSION_LOADED++;
464 1         6 return Hypersonic::Session::regenerate($self);
465             }
466              
467             1;
468              
469             __END__