File Coverage

blib/lib/PAGI/Middleware/Session/State/Header.pm
Criterion Covered Total %
statement 28 28 100.0
branch 10 10 100.0
condition 1 2 50.0
subroutine 7 7 100.0
pod 3 3 100.0
total 49 50 98.0


line stmt bran cond sub pod time code
1             package PAGI::Middleware::Session::State::Header;
2              
3 2     2   168553 use strict;
  2         3  
  2         58  
4 2     2   6 use warnings;
  2         3  
  2         97  
5 2     2   351 use parent 'PAGI::Middleware::Session::State';
  2         241  
  2         11  
6              
7             =head1 NAME
8              
9             PAGI::Middleware::Session::State::Header - Header-based session ID transport
10              
11             =head1 SYNOPSIS
12              
13             use PAGI::Middleware::Session::State::Header;
14              
15             my $state = PAGI::Middleware::Session::State::Header->new(
16             header_name => 'X-Session-ID',
17             );
18              
19             # With extraction pattern
20             my $state = PAGI::Middleware::Session::State::Header->new(
21             header_name => 'X-Auth-Token',
22             pattern => qr/^Token\s+(.+)$/i,
23             );
24              
25             # Extract session ID from request
26             my $id = $state->extract($scope);
27              
28             =head1 DESCRIPTION
29              
30             Implements the L interface using a custom
31             HTTP header for session ID transport. The session ID is read from the
32             specified request header. Injection is a no-op because the client is
33             responsible for managing header-based transport.
34              
35             =head1 CONFIGURATION
36              
37             =over 4
38              
39             =item * header_name (required)
40              
41             Name of the HTTP header containing the session ID.
42              
43             =item * pattern (optional)
44              
45             A regex with a capture group to extract the session ID from the header
46             value. If not provided, the full header value is used as the session ID.
47              
48             =back
49              
50             =cut
51              
52             sub new {
53 14     14 1 172123 my ($class, %options) = @_;
54              
55 14 100       55 die "header_name is required for $class" unless defined $options{header_name};
56              
57 13         47 return $class->SUPER::new(%options);
58             }
59              
60             =head2 extract
61              
62             my $session_id = $state->extract($scope);
63              
64             Find the configured header in C<$scope-E{headers}> (case-insensitive)
65             and return its value as the session ID. If a C is configured,
66             apply it and return the first capture group. Returns undef if the header
67             is not found or the pattern does not match.
68              
69             =cut
70              
71             sub extract {
72 12     12 1 66 my ($self, $scope) = @_;
73              
74 12         50 my $value = $self->_get_header($scope, $self->{header_name});
75 12 100       34 return unless defined $value;
76              
77 7 100       15 if (my $pattern = $self->{pattern}) {
78 4 100       30 if ($value =~ $pattern) {
79 2         13 return $1;
80             }
81 2         10 return;
82             }
83              
84 3         21 return $value;
85             }
86              
87             =head2 inject
88              
89             $state->inject(\@headers, $id, \%options);
90              
91             No-op. Header-based session transport is managed by the client, so the
92             server does not inject any response headers.
93              
94             =cut
95              
96             sub inject {
97 4     4 1 18 my ($self, $headers, $id, $options) = @_;
98              
99             # No-op: client manages header-based transport
100 4         26 return;
101             }
102              
103             sub _get_header {
104 12     12   19 my ($self, $scope, $name) = @_;
105              
106 12         23 $name = lc($name);
107 12   50     17 for my $h (@{$scope->{headers} // []}) {
  12         29  
108 9 100       29 return $h->[1] if lc($h->[0]) eq $name;
109             }
110 5         17 return;
111             }
112              
113             1;
114              
115             __END__