forked from DivinumOfficium/divinum-officium
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.psgi
More file actions
160 lines (133 loc) · 4.84 KB
/
Copy pathapp.psgi
File metadata and controls
160 lines (133 loc) · 4.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use strict;
use warnings;
use Encode qw(encode_utf8);
use File::Basename;
use File::Spec;
use Plack::Builder;
use Plack::App::CGIBin;
use Plack::App::File;
my $app_root = "/var/www";
my $cache_dir = "$app_root/web/ordo-cache";
# Set library paths once at startup, not per-request
$ENV{PERL5LIB} = join(':',
"$app_root/web/cgi-bin",
"$app_root/web/DivinumOfficium"
);
unshift @INC, "$app_root/web/cgi-bin";
require DivinumOfficium::Lexicon;
DivinumOfficium::Lexicon::preload();
# PRE-LOAD: These stay in memory (Persistent)
my $cgi_app = Plack::App::CGIBin->new(
root => "$app_root/web/cgi-bin",
exec_cb => sub { 1 }
)->to_app;
my $static_app = Plack::App::File->new(root => "$app_root/web")->to_app;
# Helper: URL-decode a string
sub url_decode {
my $v = shift;
$v =~ s/\+/ /g;
$v =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/ge;
return $v;
}
# Helper: parse a query string or POST body into a hash
sub parse_params {
my $str = shift || '';
my %p;
for my $pair (split /&/, $str) {
my ($k, $v) = split /=/, $pair, 2;
next unless defined $k && defined $v;
$p{url_decode($k)} = url_decode($v);
}
return %p;
}
# Helper: build a filesystem-safe cache key from a version string.
# Must match the logic in warm-ordo-cache.sh
sub version_to_cache_key {
my $v = lc(shift);
$v =~ s/[^a-z0-9]/-/g;
$v =~ s/-+/-/g;
$v =~ s/^-//; $v =~ s/-$//;
return $v;
}
# Helper: serve a cached ordo HTML file with correct encoding
sub serve_cache_file {
my ($file) = @_;
open my $fh, '<:encoding(UTF-8)', $file or return undef;
local $/;
my $content = <$fh>;
close $fh;
return undef unless $content;
# Encode to raw UTF-8 bytes — Starman requires bytes, not Perl wide chars
my $bytes = encode_utf8($content);
my $size = length($bytes);
return [
200,
[
'Content-Type' => 'text/html; charset=utf-8',
'Content-Length' => $size,
'X-Ordo-Cache' => 'HIT',
],
[ $bytes ],
];
}
builder {
# 1. Setup Environment once per request
enable sub {
my $app = shift;
sub {
my $env = shift;
# Handle Root Redirect
if ($env->{PATH_INFO} eq '/' || $env->{PATH_INFO} eq '') {
$env->{PATH_INFO} = '/index.html';
}
return $app->($env);
};
};
# 2. THE DISPATCHER: Routes CGI requests vs static files
sub {
my $env = shift;
if ($env->{PATH_INFO} =~ m|^/cgi-bin/|) {
# --- ORDO CACHE: intercept full-year (Totus) kalendar requests ---
if ($env->{PATH_INFO} =~ m|kalendar\.pl|) {
# Read params from both GET query string and POST body
my %params = parse_params($env->{QUERY_STRING});
if ($env->{REQUEST_METHOD} eq 'POST') {
# Read the POST body without consuming it for the CGI handler
my $body = '';
if ($env->{'psgi.input'}) {
$env->{'psgi.input'}->read($body, $env->{CONTENT_LENGTH} || 0);
# Restore the input stream so the CGI handler can read it too
open my $fh, '<', \$body;
$env->{'psgi.input'} = $fh;
}
my %post_params = parse_params($body);
%params = (%params, %post_params);
}
if (($params{kmonth} || '') eq '14') {
my $year = $params{kyear} || (localtime)[5] + 1900;
my $version = $params{version} || 'Rubrics 1960 - 1960';
my $key = version_to_cache_key($version);
my $cache_file = "$cache_dir/${year}-${key}.html";
if (-f $cache_file && -s $cache_file) {
my $response = serve_cache_file($cache_file);
return $response if $response;
# Fall through to live CGI if file read fails
}
# Cache miss — fall through to live CGI
}
}
# --- END ORDO CACHE ---
# Fix CWD for the script so relative file paths in CGI scripts work.
# Note: chdir() is global per-worker — acceptable under low concurrency
# but may cause intermittent path issues under heavy parallel load.
my $script_path = File::Spec->catfile("$app_root/web", $env->{PATH_INFO});
my $script_dir = dirname($script_path);
chdir($script_dir) if -d $script_dir;
# Strip '/cgi-bin' so CGIBin finds the file relative to its root
$env->{PATH_INFO} =~ s|^/cgi-bin||;
return $cgi_app->($env);
}
# Otherwise, serve as a static file
return $static_app->($env);
};
};