Initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
vendor_perl/**
|
||||||
103
.perlcriticrc
Normal file
103
.perlcriticrc
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
#!/usr/bin/env perlcritic
|
||||||
|
severity = 5
|
||||||
|
verbose = 8
|
||||||
|
|
||||||
|
# minorify some of warnings that are not really critical
|
||||||
|
[-InputOutput::RequireCheckedClose]
|
||||||
|
# What can we handle in case of error?
|
||||||
|
|
||||||
|
[-Modules::ProhibitExcessMainComplexity]
|
||||||
|
# Noone cares
|
||||||
|
|
||||||
|
[-Subroutines::ProhibitExcessComplexity]
|
||||||
|
# Noone cares
|
||||||
|
|
||||||
|
[-Subroutines::ProhibitExplicitReturnUndef]
|
||||||
|
# But it's very handy sometimes!
|
||||||
|
|
||||||
|
[-ValuesAndExpressions::ProhibitMagicNumbers]
|
||||||
|
# this policy makes too much false positives
|
||||||
|
|
||||||
|
[-RegularExpressions::RequireLineBoundaryMatching]
|
||||||
|
# We use /m as needed
|
||||||
|
|
||||||
|
[-RegularExpressions::RequireExtendedFormatting]
|
||||||
|
# We do not use extended formatting of regex, they still not THAT complex
|
||||||
|
|
||||||
|
[-RegularExpressions::RequireDotMatchAnything]
|
||||||
|
# huh?
|
||||||
|
|
||||||
|
[-RegularExpressions::ProhibitEscapedMetacharacters]
|
||||||
|
# Looks like it pointless
|
||||||
|
|
||||||
|
[-ControlStructures::ProhibitCascadingIfElse]
|
||||||
|
# I like long if..else chains
|
||||||
|
|
||||||
|
[-ControlStructures::ProhibitPostfixControls]
|
||||||
|
# In short lines we can afford ourselves to use postfix controls
|
||||||
|
# but of course we should avoid such constructions in long lines.
|
||||||
|
|
||||||
|
[-Subroutines::ProhibitSubroutinePrototypes]
|
||||||
|
# Contrary to Perl::Critic we consider prototypes as useful, even if
|
||||||
|
# the compile-time checks of Perl are not perfect, Perl may give some
|
||||||
|
# hints.
|
||||||
|
|
||||||
|
[-CodeLayout::RequireTidyCode]
|
||||||
|
# Impossible to complete, because of on higher linting levels "## no critic..."
|
||||||
|
# require be per-case on same line where critique is, but tidy align them
|
||||||
|
# one line down.
|
||||||
|
|
||||||
|
[-CodeLayout::ProhibitParensWithBuiltins]
|
||||||
|
# duh...
|
||||||
|
|
||||||
|
[-ValuesAndExpressions::ProhibitEmptyQuotes]
|
||||||
|
# cause we consider following correct and useful: my $var = '';
|
||||||
|
# the recommended use of q{} looks too complex: my $var = q{};
|
||||||
|
|
||||||
|
[-ValuesAndExpressions::ProhibitNoisyQuotes]
|
||||||
|
# Other people, other opinions: "," is better readable than qw(,)
|
||||||
|
|
||||||
|
[-BuiltinFunctions::ProhibitSleepViaSelect]
|
||||||
|
# We do not wish to use too much external modules.
|
||||||
|
# select undef, undef, undef, 0.25; # actually is okay if you add comment that
|
||||||
|
# it is sleep for 0.25 seconds
|
||||||
|
|
||||||
|
[-NamingConventions::Capitalization]
|
||||||
|
# duh...
|
||||||
|
# Noone cares, really
|
||||||
|
|
||||||
|
[-ControlStructures::ProhibitUnlessBlocks]
|
||||||
|
# They should not be too complex - only one condition should be checked,
|
||||||
|
# that's all
|
||||||
|
|
||||||
|
[-Miscellanea::ProhibitTies]
|
||||||
|
# Just allow it. It's very handy stuff and extremely simple and safe enough to use.
|
||||||
|
|
||||||
|
[-ValuesAndExpressions::RequireNumberSeparators]
|
||||||
|
# Not really
|
||||||
|
|
||||||
|
[-InputOutput::RequireBracedFileHandleWithPrint]
|
||||||
|
# We prefer not to use braced file handles
|
||||||
|
|
||||||
|
[-Documentation::PodSpelling]
|
||||||
|
# It doesn't help, but emits annoying errors
|
||||||
|
|
||||||
|
[Documentation::RequirePodSections]
|
||||||
|
# Considering this as "soft" requirement. For modules that is (or will be)
|
||||||
|
# stand-alone libs well formed documentation is highly desired.
|
||||||
|
lib_sections = NAME | VERSION | DESCRIPTION | AUTHOR | LICENSE AND COPYRIGHT
|
||||||
|
|
||||||
|
[-ErrorHandling::RequireCheckingReturnValueOfEval]
|
||||||
|
# In this app eval is not critical to error, so there is no need to check for it
|
||||||
|
|
||||||
|
[-InputOutput::RequireBriefOpen]
|
||||||
|
# Yes, i do!
|
||||||
|
|
||||||
|
[-InputOutput::RequireCheckedSyscalls]
|
||||||
|
# What can go wrong in "say"? what and how should i handle in this case?
|
||||||
|
|
||||||
|
[-ControlStructures::ProhibitDeepNests]
|
||||||
|
# Oh, really? Maybe you know better solution?
|
||||||
|
|
||||||
|
[-Variables::ProhibitPunctuationVars]
|
||||||
|
# I don't want use English here
|
||||||
21
LICENSE.txt
Normal file
21
LICENSE.txt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
Copyright (c) 2022 Sergei Fedosoff <eleksir@gmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
50
README.md
Normal file
50
README.md
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# Joyproxy
|
||||||
|
|
||||||
|
Эта версия приложения устаревшая, она не поддерживается и размещена
|
||||||
|
исключительно в ознакомительных целях.
|
||||||
|
|
||||||
|
## Что это?
|
||||||
|
|
||||||
|
Своего рода API, делающий запросы в img#.reactor.cc
|
||||||
|
|
||||||
|
## Зачем это нужно?
|
||||||
|
|
||||||
|
Сайт reactor.cc не даёт возможности постить на внешние ресурсы ссылки без
|
||||||
|
указания правильного referer-а.
|
||||||
|
|
||||||
|
Эта прокся призвана частично решить эту проблему через создание линков вида
|
||||||
|
`https://сайт/joyproxy/img1.reactor.cc/pics/post/mp4/видосик.mp4`
|
||||||
|
|
||||||
|
Сгенерировать ссылку можно запостив урл с видосиком в форму на страничке
|
||||||
|
`https://сайт/joyurl`
|
||||||
|
|
||||||
|
## Как это работает?
|
||||||
|
|
||||||
|
При обращении к сгенерённому урлу, апи вначале идёт на сайт, выкачивает
|
||||||
|
видосик во временный каталог, отдаёт видосик клиенту. Есть встроенное
|
||||||
|
ограничение на размер файла в 10Мб.
|
||||||
|
|
||||||
|
N.B. Файлы из временного каталога автоматически не вычищаются. Получается
|
||||||
|
своего рода "перманентный" кэш.
|
||||||
|
|
||||||
|
## Как это запустить?
|
||||||
|
|
||||||
|
Я это запускаю как [PSGI][1]-приложение, через [UWSGI][2].
|
||||||
|
|
||||||
|
Для запуска понадобятся:
|
||||||
|
|
||||||
|
* uwsgi версии 2.0.15 или новее
|
||||||
|
* perl-5.18 или новее (вплоть до 5.32.x)
|
||||||
|
* модули perl-HTTP-Tiny, perl-URI-URL, perl-URL-Escape, perl-local-lib.
|
||||||
|
|
||||||
|
Можно использовать модули из системы, а можно подтянуть их из CPAN-а с помощью
|
||||||
|
[cpanm][3] (но, правда, perl-local-lib всё же придётся поставить, так как без
|
||||||
|
него ничего не заработает).
|
||||||
|
|
||||||
|
## Кому сказать спасибо?
|
||||||
|
|
||||||
|
Авторам, редакторам и тем, кто поддерживает `reactor.cc`
|
||||||
|
|
||||||
|
[1]: https://uwsgi-docs.readthedocs.io/en/latest/Perl.html
|
||||||
|
[2]: https://github.com/unbit/uwsgi
|
||||||
|
[3]: https://github.com/miyagawa/cpanminus
|
||||||
5
bootstrap.sh
Normal file
5
bootstrap.sh
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cpanm -l vendor_perl --self-contained --force --update --installdeps .
|
||||||
|
|
||||||
|
# vim: set ft=perl noet ai ts=4 sw=4 sts=4:
|
||||||
4
cpanfile
Normal file
4
cpanfile
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
requires 'HTTP::Tiny';
|
||||||
|
requires 'URI::URL';
|
||||||
|
requires 'URI::Escape';
|
||||||
|
requires 'Plack::Util';
|
||||||
12
joyproxy.ini
Normal file
12
joyproxy.ini
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[settings]
|
||||||
|
; This server can be behind reverse-proxy, set here http or https proto, correct for incomming connecions
|
||||||
|
proto = http
|
||||||
|
|
||||||
|
; we listen on *:this_port
|
||||||
|
port = 9980
|
||||||
|
|
||||||
|
; timeout for upstream connection
|
||||||
|
timeout = 30
|
||||||
|
|
||||||
|
; loglevel - info or debug, info is what you want
|
||||||
|
loglevel = debug
|
||||||
193
joyproxy.pm
Normal file
193
joyproxy.pm
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
package joyproxy;
|
||||||
|
|
||||||
|
use 5.018;
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use utf8;
|
||||||
|
use open qw (:std :utf8);
|
||||||
|
|
||||||
|
use Carp qw (carp);
|
||||||
|
use Fcntl;
|
||||||
|
use HTTP::Tiny;
|
||||||
|
use URI::URL;
|
||||||
|
use URI::Escape qw (uri_unescape);
|
||||||
|
|
||||||
|
use Exporter qw (import);
|
||||||
|
use version; our $VERSION = qw (1.1.1);
|
||||||
|
our @EXPORT_OK = qw (joyproxy joyurl);
|
||||||
|
|
||||||
|
sub joyproxy ($) {
|
||||||
|
my $str = shift;
|
||||||
|
chomp $str;
|
||||||
|
|
||||||
|
# formal url check
|
||||||
|
if ($str !~ /^img\d+\.reactor\.cc/xmsg) {
|
||||||
|
return ('500', 'text/plain', 'This is not reactor video');
|
||||||
|
}
|
||||||
|
|
||||||
|
my ($file, $filesize) = __dlfunc ("http://$str");
|
||||||
|
|
||||||
|
if (defined $file) {
|
||||||
|
my $buf = '';
|
||||||
|
|
||||||
|
if (open my $VID, '<', $file) {
|
||||||
|
binmode $VID;
|
||||||
|
my $readlen = read ($VID, $buf, $filesize);
|
||||||
|
close $VID;
|
||||||
|
|
||||||
|
if (defined $readlen) {
|
||||||
|
if ($readlen != $filesize) {
|
||||||
|
carp "Unable to read $file readlen does not match filesize: $readlen vs $filesize";
|
||||||
|
return ('500', 'text/plain', "Unable to read file in temporary location\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
carp "Unable to read $file: $!";
|
||||||
|
return ('500', 'text/plain', "Unable to read file in temporary location: $!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($file =~ /\.mp4$/xmsgi) {
|
||||||
|
return ('200', 'video/mp4', $buf);
|
||||||
|
} elsif ($file =~ /\.webm$/xmsgi) {
|
||||||
|
return ('200', 'video/webm', $buf);
|
||||||
|
} else {
|
||||||
|
return ('200', 'video/mpeg', $buf);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (-f $file) {
|
||||||
|
unlink $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ('500', 'text/plain', "Unable to open file in temporary location: $!\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return ('500', 'text/plain', "Unable to get file from remote source!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub joyurl (@) {
|
||||||
|
my $str = shift;
|
||||||
|
my $host = shift;
|
||||||
|
|
||||||
|
# TODO: handle error here
|
||||||
|
$str = uri_unescape $str;
|
||||||
|
|
||||||
|
if (length ($str) < 60) {
|
||||||
|
$str = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($str =~ /^joyurl=https?\:\/\/(img\d+\.reactor\.cc.+)/xmsg) {
|
||||||
|
my $string = $1;
|
||||||
|
# img1.reactor.cc/pics/post/webm/видосик.webm
|
||||||
|
my @url = split /\//msx, $string;
|
||||||
|
|
||||||
|
if (($url[3] eq 'webm' || $url[3] eq 'mp4') &&
|
||||||
|
($url[4] =~ /\.webm$/xmsg || $url[4] =~ /\.mp4$/xmsg)) {
|
||||||
|
# we prefer mp4, right?, so
|
||||||
|
my $fname;
|
||||||
|
$fname = substr ($url[4], 0, -5) if (substr ($url[4], -5, 6) eq '.webm');
|
||||||
|
$fname = substr ($url[4], 0, -4) if (substr ($url[4], -4, 4) eq '.mp4');
|
||||||
|
|
||||||
|
$string = sprintf (
|
||||||
|
'https://%s/joyproxy/%s/%s/%s/mp4/%s.mp4',
|
||||||
|
$host,
|
||||||
|
$url[0],
|
||||||
|
$url[1],
|
||||||
|
$url[2],
|
||||||
|
$fname
|
||||||
|
);
|
||||||
|
|
||||||
|
$str = __urlencode ($string);
|
||||||
|
} else {
|
||||||
|
$str = '';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$str = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
my $msg = << "EOL";
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<form method='get' action='joyurl'>
|
||||||
|
<input type='text' name='joyurl' size=100 autofocus><br />
|
||||||
|
<input type='submit' value='Post it!' style='font-size:115%;' />
|
||||||
|
<br>$str
|
||||||
|
</body>
|
||||||
|
</html>\n
|
||||||
|
EOL
|
||||||
|
|
||||||
|
return ('200', 'text/html', $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub __urlencode($) {
|
||||||
|
my $str = shift;
|
||||||
|
my $urlobj = url $str;
|
||||||
|
$str = $urlobj->as_string;
|
||||||
|
$urlobj = '';
|
||||||
|
return $str;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub __dlfunc($) {
|
||||||
|
my $url = shift;
|
||||||
|
|
||||||
|
unless (-d '/tmp/joyproxy') {
|
||||||
|
carp 'no /tmp/joyproxy';
|
||||||
|
|
||||||
|
unless (mkdir '/tmp/joyproxy') {
|
||||||
|
return (undef, undef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my @explodedurl = split /\//msx, $url;
|
||||||
|
my $file = $explodedurl[$#explodedurl]; ## no critic (Variables::RequireNegativeIndices)
|
||||||
|
$#explodedurl = -1; undef @explodedurl;
|
||||||
|
$file = '/tmp/joyproxy/' . $file;
|
||||||
|
$url = __urlencode ($url);
|
||||||
|
|
||||||
|
my $http = HTTP::Tiny->new (max_size => 10485760);
|
||||||
|
|
||||||
|
# use ->get because of ->mirror does not respect max_size
|
||||||
|
my $response = $http->get (
|
||||||
|
$url,
|
||||||
|
{
|
||||||
|
headers => {
|
||||||
|
'Accept' => '*/*',
|
||||||
|
'Accept-Encoding' => 'identity;q=1, *;q=0',
|
||||||
|
'Accept-Language' => 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7',
|
||||||
|
'Range' => 'bytes=0-',
|
||||||
|
'Referer' => 'http://old.reactor.cc/all',
|
||||||
|
'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$http = '';
|
||||||
|
|
||||||
|
if ($response->{success}) {
|
||||||
|
my $filesize = 0;
|
||||||
|
|
||||||
|
sysopen (my $FILE, $file, O_CREAT|O_TRUNC|O_WRONLY) or do {
|
||||||
|
$response = '';
|
||||||
|
return (undef, undef);
|
||||||
|
};
|
||||||
|
|
||||||
|
binmode $FILE;
|
||||||
|
|
||||||
|
syswrite ($FILE, $response->{content}) or do {
|
||||||
|
close $FILE;
|
||||||
|
$response = '';
|
||||||
|
return (undef, undef);
|
||||||
|
};
|
||||||
|
|
||||||
|
close $FILE;
|
||||||
|
$response = '';
|
||||||
|
$filesize = (stat $file )[7];
|
||||||
|
return ($file, $filesize);
|
||||||
|
} else {
|
||||||
|
$response = '';
|
||||||
|
return (undef, undef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
# vim: set ft=perl noet ai ts=4 sw=4 sts=4:
|
||||||
48
joyproxy.psgi
Normal file
48
joyproxy.psgi
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
|
||||||
|
use 5.018;
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use utf8;
|
||||||
|
use open qw (:std :utf8);
|
||||||
|
use version; our $VERSION = qw (1.1.0);
|
||||||
|
|
||||||
|
use lib qw (. ./vendor_perl/lib/perl5);
|
||||||
|
use joyproxy qw (joyurl joyproxy);
|
||||||
|
|
||||||
|
my $app = sub {
|
||||||
|
my $env = shift;
|
||||||
|
|
||||||
|
my $msg = "Your Opinion is very important for us, please stand by.\n";
|
||||||
|
my $status = '404';
|
||||||
|
my $content = 'text/plain';
|
||||||
|
|
||||||
|
if ($env->{PATH_INFO} =~ /^\/joyproxy\/(.+)/xmsg) {
|
||||||
|
my $joyproxyurl = $1;
|
||||||
|
($status, $msg) = ('400', "Bad Request?\n");
|
||||||
|
|
||||||
|
if (($joyproxyurl =~ /\.mp4$/xmsgi) or ($joyproxyurl =~ /\.webm$/xmsgi)) {
|
||||||
|
($status, $content, $msg) = joyproxy $joyproxyurl ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($env->{PATH_INFO} =~ /^\/joyurl/xmsg) {
|
||||||
|
my $querystring = $env->{QUERY_STRING} // '';
|
||||||
|
|
||||||
|
($status, $content, $msg) = joyurl ($querystring, $env->{HTTP_HOST});
|
||||||
|
}
|
||||||
|
|
||||||
|
use bytes;
|
||||||
|
my $length = length $msg;
|
||||||
|
no bytes;
|
||||||
|
|
||||||
|
return [
|
||||||
|
$status,
|
||||||
|
[ 'Content-Type' => $content, 'Content-Length' => $length ],
|
||||||
|
[ $msg ],
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
__END__
|
||||||
|
# vim: set ft=perl noet ai ts=4 sw=4 sts=4:
|
||||||
17
joyproxy.yaml
Normal file
17
joyproxy.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
uwsgi:
|
||||||
|
http-socket: 127.0.0.1:3081
|
||||||
|
socket-protocol: http
|
||||||
|
so-keepalive: 0
|
||||||
|
logformat: %(var.REMOTE_ADDR) %(var.REQUEST_METHOD) %(var.REQUEST_URI) %(status)
|
||||||
|
processes: 1
|
||||||
|
need-app: true
|
||||||
|
log-reopen: yes
|
||||||
|
reload-mercy: 5
|
||||||
|
buffer-size: 32768
|
||||||
|
die-on-term: true
|
||||||
|
pidfile: /run/joyproxy/joyproxy.pid
|
||||||
|
logto: /var/log/joyproxy/joyproxy.log
|
||||||
|
plugins: psgi
|
||||||
|
chdir: /var/lib/joyproxy
|
||||||
|
psgi: /var/lib/joyproxy/joyproxy.psgi
|
||||||
|
perl-local-lib: /var/lib/joyproxy/vendor_perl
|
||||||
Reference in New Issue
Block a user