# Bugs encountered were reported to bug-Danga-Socket@rt.cpan.org,
# fixed in Danga::Socket 1.62 and visible at:
# https://rt.cpan.org/Public/Dist/Display.html?Name=Danga-Socket
+#
+# fields:
+# sock: underlying socket
+# rbuf: scalarref, usually undef
+# wbuf: arrayref of coderefs or tmpio (autovivified))
+# (tmpio = [ GLOB, offset, [ length ] ])
package PublicInbox::DS;
use strict;
use bytes;
use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC);
use parent qw(Exporter);
our @EXPORT_OK = qw(now msg_more);
-use warnings;
use 5.010_001;
use Scalar::Util qw(blessed);
-
use PublicInbox::Syscall qw(:epoll);
use PublicInbox::Tmpfile;
-
-use fields ('sock', # underlying socket
- 'rbuf', # scalarref, usually undef
- 'wbuf', # arrayref of coderefs or tmpio (autovivified))
- # (tmpio = [ GLOB, offset, [ length ] ])
- );
-
use Errno qw(EAGAIN EINVAL);
use Carp qw(confess carp);
=cut
sub new {
my ($self, $sock, $ev) = @_;
- $self = fields::new($self) unless ref $self;
-
$self->{sock} = $sock;
my $fd = fileno($sock);
# Used by public-inbox-watch for Maildir (and possibly MH in the future)
package PublicInbox::DirIdle;
use strict;
-use base 'PublicInbox::DS';
-use fields qw(inot);
+use parent 'PublicInbox::DS';
use PublicInbox::Syscall qw(EPOLLIN EPOLLET);
use PublicInbox::In2Tie;
sub new {
my ($class, $dirs, $cb) = @_;
- my $self = fields::new($class);
+ my $self = bless {}, $class;
my $inot;
if ($ino_cls) {
$inot = $ino_cls->new or die "E: $ino_cls->new: $!";
package PublicInbox::GitAsyncCat;
use strict;
use parent qw(PublicInbox::DS Exporter);
-use fields qw(git);
use PublicInbox::Syscall qw(EPOLLIN EPOLLET);
our @EXPORT = qw(git_async_cat);
sub _add {
my ($class, $git) = @_;
- my $self = fields::new($class);
$git->batch_prepare;
+ my $self = bless { git => $git }, $class;
$self->SUPER::new($git->{in}, EPOLLIN|EPOLLET);
- $self->{git} = $git;
\undef; # this is a true ref()
}
# to learn different ways to admin both NNTP and HTTP components.
# There's nothing which depends on public-inbox, here.
# Each instance of this class represents a HTTP client socket
-
+#
+# fields:
+# httpd: PublicInbox::HTTPD ref
+# env: PSGI env hashref
+# input_left: bytes left to read in request body (e.g. POST/PUT)
+# remote_addr: remote IP address as a string (e.g. "127.0.0.1")
+# remote_port: peer port
+# forward: response body object, response to ->getline + ->close
+# alive: HTTP keepalive state:
+# 0: drop connection when done
+# 1: keep connection when done
+# 2: keep connection, chunk responses
package PublicInbox::HTTP;
use strict;
-use warnings;
-use base qw(PublicInbox::DS);
-use fields qw(httpd env input_left remote_addr remote_port forward alive);
+use parent qw(PublicInbox::DS);
use bytes (); # only for bytes::length
use Fcntl qw(:seek);
use Plack::HTTPParser qw(parse_http_request); # XS or pure Perl
sub new ($$$) {
my ($class, $sock, $addr, $httpd) = @_;
- my $self = fields::new($class);
+ my $self = bless { httpd => $httpd }, $class;
my $ev = EPOLLIN;
my $wbuf;
if ($sock->can('accept_SSL') && !$sock->accept_SSL) {
$ev = PublicInbox::TLS::epollbit();
$wbuf = [ \&PublicInbox::DS::accept_tls_step ];
}
- $self->SUPER::new($sock, $ev | EPOLLONESHOT);
- $self->{httpd} = $httpd;
$self->{wbuf} = $wbuf if $wbuf;
($self->{remote_addr}, $self->{remote_port}) =
PublicInbox::Daemon::host_with_port($addr);
- $self;
+ $self->SUPER::new($sock, $ev | EPOLLONESHOT);
}
sub event_step { # called by PublicInbox::DS
# The name of this key is not even stable!
# Currently intended for use with read-only pipes with expensive
# processes such as git-http-backend(1), cgit(1)
+#
+# fields:
+# http: PublicInbox::HTTP ref
+# fh: PublicInbox::HTTP::{Identity,Chunked} ref (can ->write + ->close)
+# cb: initial read callback
+# arg: arg for {cb}
+# end_obj: CODE or object which responds to ->event_step when ->close is called
package PublicInbox::HTTPD::Async;
use strict;
-use warnings;
-use base qw(PublicInbox::DS);
-use fields qw(http fh cb arg end_obj);
+use parent qw(PublicInbox::DS);
use Errno qw(EAGAIN);
use PublicInbox::Syscall qw(EPOLLIN EPOLLET);
die '$end_obj unsupported w/o $io' if $end_obj;
return;
}
-
- my $self = fields::new($class);
+ my $self = bless {
+ cb => $cb, # initial read callback
+ arg => $arg, # arg for $cb
+ end_obj => $end_obj, # like END{}, can ->event_step
+ }, $class;
IO::Handle::blocking($io, 0);
$self->SUPER::new($io, EPOLLIN | EPOLLET);
- $self->{cb} = $cb; # initial read callback
- $self->{arg} = $arg; # arg for $cb
- $self->{end_obj} = $end_obj; # like END{}, can ->event_step
- $self;
}
sub event_step {
# as a 50K uint16_t array (via pack("S*", ...)). "UID offset"
# is the offset from {uid_base} which determines the start of
# the mailbox slice.
-
+#
+# fields:
+# imapd: PublicInbox::IMAPD ref
+# ibx: PublicInbox::Inbox ref
+# long_cb: long_response private data
+# uid_base: base UID for mailbox slice (0-based)
+# -login_tag: IMAP TAG for LOGIN
+# -idle_tag: IMAP response tag for IDLE
+# uo2m: UID-to-MSN mapping
package PublicInbox::IMAP;
use strict;
-use base qw(PublicInbox::DS);
-use fields qw(imapd ibx long_cb -login_tag
- uid_base -idle_tag uo2m);
+use parent qw(PublicInbox::DS);
use PublicInbox::Eml;
use PublicInbox::EmlContentFoo qw(parse_content_disposition);
use PublicInbox::DS qw(now);
use PublicInbox::GitAsyncCat;
use Text::ParseWords qw(parse_line);
use Errno qw(EAGAIN);
-use Hash::Util qw(unlock_hash); # dependency of fields for perl 5.10+, anyways
use PublicInbox::Search;
use PublicInbox::IMAPsearchqp;
*mdocid = \&PublicInbox::Search::mdocid;
sub new ($$$) {
my ($class, $sock, $imapd) = @_;
- my $self = fields::new('PublicInbox::IMAP_preauth');
- unlock_hash(%$self);
+ my $self = bless { imapd => $imapd }, 'PublicInbox::IMAP_preauth';
my $ev = EPOLLIN;
my $wbuf;
if ($sock->can('accept_SSL') && !$sock->accept_SSL) {
$wbuf = [ \&PublicInbox::DS::accept_tls_step, \&greet ];
}
$self->SUPER::new($sock, $ev | EPOLLONESHOT);
- $self->{imapd} = $imapd;
if ($wbuf) {
$self->{wbuf} = $wbuf;
} else {
# Copyright (C) 2020 all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+# fields:
+# pi_config: PublicInbox::Config ref
+# inot: Linux::Inotify2-like object
+# pathmap => { inboxdir => [ ibx, watch1, watch2, watch3... ] } mapping
package PublicInbox::InboxIdle;
use strict;
-use base qw(PublicInbox::DS);
-use fields qw(pi_config inot pathmap);
+use parent qw(PublicInbox::DS);
use Cwd qw(abs_path);
use PublicInbox::Syscall qw(EPOLLIN EPOLLET);
my $IN_MODIFY = 0x02; # match Linux inotify
sub new {
my ($class, $pi_config) = @_;
- my $self = fields::new($class);
+ my $self = bless {}, $class;
my $inot;
if ($ino_cls) {
$inot = $ino_cls->new or die "E: $ino_cls->new: $!";
# Used by -nntpd for listen sockets
package PublicInbox::Listener;
use strict;
-use warnings;
-use base 'PublicInbox::DS';
+use parent 'PublicInbox::DS';
use Socket qw(SOL_SOCKET SO_KEEPALIVE IPPROTO_TCP TCP_NODELAY);
-use fields qw(post_accept);
use IO::Handle;
use PublicInbox::Syscall qw(EPOLLIN EPOLLEXCLUSIVE EPOLLET);
use Errno qw(EAGAIN ECONNABORTED EPERM);
setsockopt($s, SOL_SOCKET, SO_KEEPALIVE, 1);
setsockopt($s, IPPROTO_TCP, TCP_NODELAY, 1); # ignore errors on non-TCP
listen($s, 1024);
- my $self = fields::new($class);
+ my $self = bless { post_accept => $cb }, $class;
$self->SUPER::new($s, EPOLLIN|EPOLLET|EPOLLEXCLUSIVE);
- $self->{post_accept} = $cb;
- $self
}
sub event_step {
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
#
# Each instance of this represents a NNTP client socket
+# fields:
+# nntpd: PublicInbox::NNTPD ref
+# article: per-session current article number
+# ng: PublicInbox::Inbox ref
+# long_cb: long_response private data
package PublicInbox::NNTP;
use strict;
-use warnings;
-use base qw(PublicInbox::DS);
-use fields qw(nntpd article ng long_cb);
+use parent qw(PublicInbox::DS);
use PublicInbox::MID qw(mid_escape $MID_EXTRACT);
use PublicInbox::Eml;
use POSIX qw(strftime);
sub new ($$$) {
my ($class, $sock, $nntpd) = @_;
- my $self = fields::new($class);
+ my $self = bless { nntpd => $nntpd }, $class;
my $ev = EPOLLIN;
my $wbuf;
if ($sock->can('accept_SSL') && !$sock->accept_SSL) {
$wbuf = [ \&PublicInbox::DS::accept_tls_step, \&greet ];
}
$self->SUPER::new($sock, $ev | EPOLLONESHOT);
- $self->{nntpd} = $nntpd;
if ($wbuf) {
$self->{wbuf} = $wbuf;
} else {
# efficient in terms of server memory usage.
package PublicInbox::NNTPdeflate;
use strict;
-use warnings;
use 5.010_001;
-use base qw(PublicInbox::NNTP);
+use parent qw(PublicInbox::NNTP);
use Compress::Raw::Zlib;
-use Hash::Util qw(unlock_hash); # dependency of fields for perl 5.10+, anyways
my %IN_OPT = (
-Bufsize => PublicInbox::NNTP::LINE_MAX,
$self->res('403 Unable to activate compression');
return;
}
- unlock_hash(%$self);
$self->res('206 Compression active');
bless $self, $class;
$self->{zin} = $in;
# notified if the master process dies.
package PublicInbox::ParentPipe;
use strict;
-use warnings;
-use base qw(PublicInbox::DS);
-use fields qw(cb);
+use parent qw(PublicInbox::DS);
use PublicInbox::Syscall qw(EPOLLIN EPOLLONESHOT);
sub new ($$$) {
my ($class, $pipe, $worker_quit) = @_;
- my $self = fields::new($class);
+ my $self = bless { cb => $worker_quit }, $class;
$self->SUPER::new($pipe, EPOLLIN|EPOLLONESHOT);
- $self->{cb} = $worker_quit;
- $self;
}
# master process died, time to call worker_quit ourselves
# Copyright (C) 2019-2020 all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+
+# Wraps a signalfd (or similar) for PublicInbox::DS
+# fields: (sig: hashref similar to %SIG, but signal numbers as keys)
package PublicInbox::Sigfd;
use strict;
use parent qw(PublicInbox::DS);
-use fields qw(sig); # hashref similar to %SIG, but signal numbers as keys
use PublicInbox::Syscall qw(signalfd EPOLLIN EPOLLET SFD_NONBLOCK);
use POSIX qw(:signal_h);
use IO::Handle ();
# are available.
sub new {
my ($class, $sig, $flags) = @_;
- my $self = fields::new($class);
my %signo = map {;
my $cb = $sig->{$_};
# SIGWINCH is 28 on FreeBSD, NetBSD, OpenBSD
};
$num => $cb;
} keys %$sig;
+ my $self = bless { sig => \%signo }, $class;
my $io;
my $fd = signalfd(-1, [keys %signo], $flags);
if (defined $fd && $fd >= 0) {
$self->SUPER::new($io, EPOLLIN | EPOLLET);
} else { # master main loop
$self->{sock} = $io;
+ $self;
}
- $self->{sig} = \%signo;
- $self;
}
# PublicInbox::Daemon in master main loop (blocking)
package IMAPC;
use strict;
-use base qw(PublicInbox::DS);
-use fields qw(step zin);
+use parent qw(PublicInbox::DS);
+# fields: step: state machine, zin: Zlib inflate context
use PublicInbox::Syscall qw(EPOLLIN EPOLLOUT EPOLLONESHOT);
use Errno qw(EAGAIN);
# determines where we start event_step
sub new {
my ($class, $io) = @_;
- my $self = fields::new($class);
-
- # wait for connect(), and maybe SSL_connect()
- $self->SUPER::new($io, EPOLLOUT|EPOLLONESHOT);
+ my $self = bless { step => FIRST_STEP }, $class;
if ($io->can('connect_SSL')) {
$self->{wbuf} = [ \&connect_tls_step ];
}
- $self->{step} = FIRST_STEP;
- $self;
+ # wait for connect(), and maybe SSL_connect()
+ $self->SUPER::new($io, EPOLLOUT|EPOLLONESHOT);
}
1;
package IMAPCdeflate;
use strict;
-use base qw(IMAPC); # parent doesn't work for fields
-use Hash::Util qw(unlock_hash); # dependency of fields for perl 5.10+, anyways
+our @ISA;
use Compress::Raw::Zlib;
use PublicInbox::IMAPdeflate;
my %ZIN_OPT;
BEGIN {
+ @ISA = qw(IMAPC);
%ZIN_OPT = ( -WindowBits => -15, -AppendOutput => 1 );
*write = \&PublicInbox::IMAPdeflate::write;
*do_read = \&PublicInbox::IMAPdeflate::do_read;
my ($class, $self) = @_;
my ($in, $err) = Compress::Raw::Zlib::Inflate->new(%ZIN_OPT);
die "Inflate->new failed: $err" if $err != Z_OK;
- unlock_hash(%$self);
bless $self, $class;
$self->{zin} = $in;
}