| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Hypersonic::Middleware::RequestId; |
|
2
|
1
|
|
|
1
|
|
2010
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
54
|
|
|
3
|
1
|
|
|
1
|
|
7
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
664
|
|
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
our $VERSION = '0.15'; |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
# Middleware builder pattern - generates inline C at compile time |
|
8
|
|
|
|
|
|
|
# Zero Perl in the hot path - ID generation is pure C |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
# Constructor - creates a builder instance |
|
11
|
|
|
|
|
|
|
sub new { |
|
12
|
3
|
|
|
3
|
0
|
9
|
my ($class, %opts) = @_; |
|
13
|
|
|
|
|
|
|
bless { |
|
14
|
|
|
|
|
|
|
header => $opts{header} // 'X-Request-ID', |
|
15
|
3
|
|
50
|
|
|
19
|
type => $opts{type} // 'both', # 'before', 'after', or 'both' |
|
|
|
|
50
|
|
|
|
|
|
16
|
|
|
|
|
|
|
}, $class; |
|
17
|
|
|
|
|
|
|
} |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
# Declare slot requirements - compiler allocates and passes back in context |
|
20
|
|
|
|
|
|
|
sub slot_requirements { |
|
21
|
0
|
|
|
0
|
0
|
0
|
return { request_id => 1 }; # Need 1 slot for request_id |
|
22
|
|
|
|
|
|
|
} |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# Factory methods that return builder instances (not coderefs) |
|
25
|
|
|
|
|
|
|
sub middleware { |
|
26
|
2
|
|
|
2
|
0
|
296
|
my (%opts) = @_; |
|
27
|
2
|
|
|
|
|
7
|
return __PACKAGE__->new(%opts, type => 'before'); |
|
28
|
|
|
|
|
|
|
} |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
sub after_middleware { |
|
31
|
1
|
|
|
1
|
0
|
2
|
my (%opts) = @_; |
|
32
|
1
|
|
|
|
|
10
|
return __PACKAGE__->new(%opts, type => 'after'); |
|
33
|
|
|
|
|
|
|
} |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# Builder interface: generate C helper functions at file scope |
|
36
|
|
|
|
|
|
|
sub build_helpers { |
|
37
|
0
|
|
|
0
|
0
|
|
my ($self, $builder) = @_; |
|
38
|
|
|
|
|
|
|
|
|
39
|
0
|
|
|
|
|
|
$builder->comment('RequestId: JIT-compiled request ID generation (zero Perl overhead)') |
|
40
|
|
|
|
|
|
|
->line('static __thread unsigned long g_reqid_counter = 0;') |
|
41
|
|
|
|
|
|
|
->line('static pid_t g_reqid_pid = 0;') |
|
42
|
|
|
|
|
|
|
->blank |
|
43
|
|
|
|
|
|
|
->line('static void hypersonic_generate_request_id(char* buf, size_t buflen) {') |
|
44
|
|
|
|
|
|
|
->line(' if (g_reqid_pid == 0) g_reqid_pid = getpid();') |
|
45
|
|
|
|
|
|
|
->line(' unsigned long ts = (unsigned long)time(NULL);') |
|
46
|
|
|
|
|
|
|
->line(' unsigned long cnt = g_reqid_counter++;') |
|
47
|
|
|
|
|
|
|
->line(' snprintf(buf, buflen, "%lx-%lx-%x", ts, cnt, (unsigned int)g_reqid_pid);') |
|
48
|
|
|
|
|
|
|
->line('}'); |
|
49
|
|
|
|
|
|
|
} |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# Builder interface: generate inline C for before middleware |
|
52
|
|
|
|
|
|
|
sub build_before { |
|
53
|
0
|
|
|
0
|
0
|
|
my ($self, $builder, $ctx) = @_; |
|
54
|
0
|
|
0
|
|
|
|
my $req_var = $ctx->{req_var} // 'req'; |
|
55
|
|
|
|
|
|
|
my $slot = $ctx->{slots}{request_id} |
|
56
|
0
|
|
0
|
|
|
|
// die "RequestId: compiler must provide slots->{request_id}"; |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# Store slot for build_after to use |
|
59
|
0
|
|
|
|
|
|
$self->{_slot} = $slot; |
|
60
|
|
|
|
|
|
|
|
|
61
|
0
|
|
|
|
|
|
$builder->comment('RequestId before: generate and store request ID') |
|
62
|
|
|
|
|
|
|
->line(' {') |
|
63
|
|
|
|
|
|
|
->line(' char _reqid_buf[64];') |
|
64
|
|
|
|
|
|
|
->line(' hypersonic_generate_request_id(_reqid_buf, sizeof(_reqid_buf));') |
|
65
|
|
|
|
|
|
|
->line(" av_store($req_var, $slot, newSVpv(_reqid_buf, 0));") |
|
66
|
|
|
|
|
|
|
->line(' }'); |
|
67
|
|
|
|
|
|
|
} |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# Builder interface: generate inline C for after middleware |
|
70
|
|
|
|
|
|
|
sub build_after { |
|
71
|
0
|
|
|
0
|
0
|
|
my ($self, $builder, $ctx) = @_; |
|
72
|
0
|
|
0
|
|
|
|
my $req_var = $ctx->{req_var} // 'req'; |
|
73
|
0
|
|
0
|
|
|
|
my $res_var = $ctx->{res_var} // 'result'; |
|
74
|
|
|
|
|
|
|
my $slot = $ctx->{slots}{request_id} // $self->{_slot} |
|
75
|
0
|
|
0
|
|
|
|
// die "RequestId: compiler must provide slots->{request_id}"; |
|
|
|
|
0
|
|
|
|
|
|
76
|
0
|
|
|
|
|
|
my $header = $self->{header}; |
|
77
|
0
|
|
|
|
|
|
my $header_len = length($header); |
|
78
|
|
|
|
|
|
|
|
|
79
|
0
|
|
|
|
|
|
$builder->comment("RequestId after: add $header to response") |
|
80
|
|
|
|
|
|
|
->line(' {') |
|
81
|
|
|
|
|
|
|
->line(" SV** _reqid_ref = av_fetch($req_var, $slot, 0);") |
|
82
|
|
|
|
|
|
|
->line(' if (_reqid_ref && SvOK(*_reqid_ref)) {') |
|
83
|
|
|
|
|
|
|
->line(' /* Request ID available - will be added to response headers */') |
|
84
|
|
|
|
|
|
|
->line(' /* Note: actual header injection handled by response builder */') |
|
85
|
|
|
|
|
|
|
->line(' }') |
|
86
|
|
|
|
|
|
|
->line(' }'); |
|
87
|
|
|
|
|
|
|
} |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
1; |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
__END__ |