)
my $config = $heap->{'self'}->{'config'};
my %data;
grep {
defined $_ &&
($call eq 'homepage' || $call eq $_->dsn || $call eq $_->source_uri || $call eq $_->version_uri) &&
($data{$_->source_uri}{$_->version_uri} = $_);
} $config->adaptors();
my $resp = $WRAPPERS->{'sources'}->{'open'};
while (my ($s_uri, $s_data) = each %data) {
my @versions = keys %{$s_data};
if(!scalar @versions) {
next;
}
for my $i (0..(scalar @versions -1)) {
eval {
$resp .= $s_data->{$versions[$i]}->das_sourcedata({
'skip_open' => $i > 0,
'skip_close' => $i+1 < scalar @versions,
});
} or do {
carp "Error generating source data for '$versions[$i]':\n$EVAL_ERROR\n";
};
}
}
$resp .= $WRAPPERS->{'sources'}->{'close'};
my $response = HTTP::Response->new(200);
$response->content_type('text/xml');
$response->content($resp);
return $response;
}
sub response_homepage {
my ($heap, $request) = @_;
my $config = $heap->{'self'}->{'config'};
my $response = HTTP::Response->new(200);
$response->content_type('text/html');
my $content = qq(
Welcome to ProServer v$VERSION
Welcome to ProServer v$VERSION
Core by Roger Pettett © Genome Research Ltd.
ProServer: A simple, extensible Perl DAS server.
- Finn RD,
- Stalker JW,
- Jackson DK,
- Kulesha E,
- Clements J,
- Pettett R.
Bioinformatics 2007;
doi: 10.1093/bioinformatics/btl650; PMID: 17237073
);
my $maintainer = $config->{'maintainer'};
if ($maintainer) {
$content .= qq(This server is maintained by $maintainer.
\n);
} else {
$content .= qq(This server has no configured maintainer.
\n);
}
$content .= sprintf q(Perform a DSN or SOURCES request.
)."\n", ## no critic
$config->response_protocol(),
$config->response_hostname(),
$config->response_port(),
$config->response_baseuri();
if(scalar $config->adaptors()) {
$content .= qq(| Source | Mapmaster | Description | Capabilities |
@{[map {
my $mm = $_->mapmaster();
$mm = $mm?qq($mm):'-';
sprintf q(| %5$s | %s | %s | %s |
)."\n",
$config->response_protocol(),
$config->response_hostname(),
$config->response_port(),
$config->response_baseuri(),
$_->dsn(),
$mm,
$_->description(),
$_->das_capabilities() || '-';
} sort { lc $a->dsn cmp lc $b->dsn } grep { defined $_ } $config->adaptors()]}
\n);
} else {
$content .= qq(
No adaptors configured.\n);
}
$content .= '';
for my $module ((map { 'Bio::Das::'.$_ } sort keys %Bio::Das::),
(map { 'Bio::Das::ProServer::'.$_ } sort keys %Bio::Das::ProServer::),
(map { 'Bio::Das::ProServer::Authenticator::'.$_ } sort keys %Bio::Das::ProServer::Authenticator::),
(map { 'Bio::Das::ProServer::SourceAdaptor::'.$_ } sort keys %Bio::Das::ProServer::SourceAdaptor::),
(map { 'Bio::Das::ProServer::SourceAdaptor::Transport::'.$_ } sort keys %Bio::Das::ProServer::SourceAdaptor::Transport::),
(map { 'Bio::Das::ProServer::SourceHydra::'.$_ } sort keys %Bio::Das::ProServer::SourceHydra::),
) {
if($module !~ /::$/mx) {
next;
}
my $cpkg = substr $module, 0, -2;
my $str = $cpkg->VERSION;
$str || next;
$content .= qq(- $cpkg v$str
\n);
}
$content .= qq(
ProServer homepage | DAS registry | BioDAS.org
\n);
$response->content($content);
return $response;
}
sub build_das_response {
my ($heap, $request) = @_;
my $config = $heap->{'self'}->{'config'};
#########
# Handle DAS responses here
#
my $response;
my $uri = $request->uri();
my ($dsn, $call) = $uri =~ m{/das1?(?:/([^/\?\#]+))(?:/([^/\?\#]+))?}mx;
$dsn ||= q();
if($dsn && !$call) {
$call = 'homepage';
}
if($dsn eq 'dsn.xsl') { ## no critic
$response = response_xsl($heap, $request, 'dsn.xsl');
} elsif($dsn eq 'sources.xsl' || $call eq 'sources.xsl') {
$response = response_xsl($heap, $request, 'sources.xsl');
} elsif($dsn && $config->knows($dsn)) {
$response = response_general($heap, $request, $dsn, $call);
} elsif($dsn eq 'sources') {
$response = response_sources($heap, $request, $call);
} elsif($dsn eq 'dsn') {
$response = response_dsn($heap, $request);
} elsif(!$dsn) {
$response = response_homepage($heap, $request);
} else {
$response = HTTP::Response->new(200);
$response->content_type('text/plain');
$response->header('X-DAS-Status' => 401);
$response->content("Bad data source (data source unknown: $dsn)\nuri=@{[$uri||q()]}, dsn=@{[$dsn||q()]}, call=@{[$call||q()]}");
}
$response->content_length(length $response->content);
#########
# Add custom X-DAS headers
#
$response->header('X-DAS-Version' => $config->das_version);
$response->header('X-DAS-Server' => $config->server_version);
if (!$response->header('X-DAS-Status')) {
$response->header('X-DAS-Status' => $response->code());
}
if($dsn && $config->knows($dsn) && (my $adaptor = $config->adaptor($dsn))) {
eval {
$response->header('X-DAS-Capabilities' => $adaptor->das_capabilities()||q());
$adaptor->cleanup();
1;
} or do {
carp $EVAL_ERROR;
};
} else {
$response->header('X-DAS-Capabilities' => q(dsn/1.0; sources/1.0));
}
#
# Finished handling das responses
#########
#########
# Generate access log
#
my $logline = $heap->{'self'}->{'logformat'};
$logline =~ s/%i/inet_ntoa($heap->{peer_addr})/emx; # remote ip
$logline =~ s/%h/gethostbyaddr($heap->{peer_addr}, AF_INET);/emx; # remote hostname
$logline =~ s/%t/strftime '%Y-%m-%dT%H:%M:%S', localtime/emx; # datetime yyyy-mm-ddThh:mm:ss
$logline =~ s/%r/$uri/mx; # request uri
$logline =~ s/%>?s/@{[$response->code(), $response->header('X-DAS-Status')]}/mx; # status
if($heap->{'method'} &&
$heap->{'method'} eq 'cgi') {
__PACKAGE__->log($logline);
} else {
print $logline, "\n" or croak $OS_ERROR;
}
return $response;
}
# Does keyword substitution for response URLs
sub _substitute {
my ($heap, $text, $dsn) = @_;
my $config = $heap->{'self'}->{'config'};
my $subst = {
'host' => $config->response_hostname(),
'port' => $config->response_port() || q(),
'protocol' => $config->response_protocol() || 'http',
'baseuri' => $config->response_baseuri() || q(),
'dsn' => $dsn || q(),
};
$text =~ s/\%([a-z]+)/$subst->{$1}/smgxi;
return $text;
}
### The client handler received an error. Stop the ReadWrite wheel,
### which also closes the socket.
sub client_got_error {
my @args = @_;
my ( $heap, $operation, $errnum, $errstr ) = @args[ HEAP, ARG0, ARG1, ARG2 ];
DEBUG and
carp( "Client handler $PID/", $args[SESSION]->ID,
" got $operation error $errnum: $errstr\n",
"Client handler $PID/", $args[SESSION]->ID, " is shutting down.\n"
);
return delete $heap->{client};
}
### The client handler has flushed its response to the socket. We're
### done with the client connection, so stop the ReadWrite wheel.
sub client_flushed_request {
my @args = @_;
my $heap = $args[HEAP];
DEBUG and
carp( "Client handler $PID/", $args[SESSION]->ID,
" flushed its response.\n",
"Client handler $PID/", $args[SESSION]->ID, " is shutting down.\n"
);
return delete $heap->{client};
}
### We're done.
sub make_pidfile {
my ($self, $pidfile) = @_;
my ($spidfile) = $pidfile =~ /([a-zA-Z0-9\.\/_\-]+)/mx;
__PACKAGE__->log(qq(Writing pidfile $spidfile));
$self->{'pidfile'} = $pidfile;
open my $fh, '>', $spidfile or croak "Cannot create pid file: $ERRNO\n";
print {$fh} "$PID\n" or croak $OS_ERROR;
close $fh or carp "Error closing pid file: $ERRNO";
return $PID;
}
sub remove_pidfile {
my ($self) = @_;
my $spidfile = $self->{'pidfile'};
if($spidfile && -f $spidfile) {
unlink $spidfile;
DEBUG and carp 'Removed pidfile';
}
return;
}
sub log { ## no critic
my ($self, @args) = @_;
print {*STDERR} (strftime '[%Y-%m-%d %H:%M:%S] ', localtime), @args, "\n" or croak $OS_ERROR;
return;
}
__END__
=head1 NAME
Bio::Das::ProServer
=head1 VERSION
$LastChangedRevision: 553 $
=head1 SYNOPSIS
eg/proserver -help
=head1 DESCRIPTION
ProServer is a server implementation of the DAS protocol.
http://biodas.org/
ProServer is based on example preforking POEserver at
http://poe.perl.org/?POE_Cookbook/Web_Server_With_Forking
=head1 DIAGNOSTICS
To run in non-pre-forking, debug mode:
eg/proserver -debug -x
Otherwise stdout logs to proserver-hostname.log and stderr to proserver-hostname.err
=head1 CONFIGURATION AND ENVIRONMENT
See eg/proserver.ini
=head1 SUBROUTINES/METHODS
=head2 run
=head2 DEBUG
=head2 server_spawn
=head2 server_start
=head2 server_stop
=head2 server_got_error
=head2 server_do_fork
=head2 server_got_sig_hup
=head2 server_got_sig_int
=head2 server_got_sig_chld
=head2 server_got_connection
=head2 client_start
=head2 client_stop
=head2 client_got_request
=head2 response_xsl
=head2 response_general
=head2 response_dsn
=head2 response_sources
=head2 response_homepage
=head2 build_das_response
=head2 client_got_error
=head2 client_flushed_request
=head2 make_pidfile
=head2 remove_pidfile
=head2 log
=head1 DEPENDENCIES
Bio::Das::ProServer::Config
CGI :cgi
HTTP::Request
HTTP::Response
Compress::Zlib
Getopt::Long
POE
POE::Filter::HTTPD
POE::Wheel::ReadWrite
POE::Wheel::SocketFactory
POSIX setsid strftime
File::Spec
Sys::Hostname
Bio::Das::ProServer::SourceAdaptor
Bio::Das::ProServer::SourceHydra
Socket
English
Carp
=head1 INCOMPATIBILITIES
=head1 BUGS AND LIMITATIONS
=head1 AUTHOR
$Author: Roger Pettett$
=head1 LICENSE AND COPYRIGHT
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
=cut