Apache-Test
view release on metacpan or search on metacpan
lib/Apache/TestRequest.pm view on Meta::CPAN
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package Apache::TestRequest;
use strict;
use warnings FATAL => 'all';
BEGIN {
$ENV{PERL_LWP_USE_HTTP_10} = 1; # default to http/1.0
$ENV{APACHE_TEST_HTTP_09_OK} ||= 0; # 0.9 responses are ok
}
use Apache::Test ();
use Apache::TestConfig ();
use Carp;
use constant TRY_TIMES => 200;
use constant INTERP_KEY => 'X-PerlInterpreter';
use constant UA_TIMEOUT => 60 * 10; #longer timeout for debugging
my $have_lwp = 0;
# APACHE_TEST_PRETEND_NO_LWP=1 pretends that LWP is not available so
# one can test whether the test suite survives if the user doesn't
# have lwp installed
unless ($ENV{APACHE_TEST_PRETEND_NO_LWP}) {
$have_lwp = eval {
require LWP::UserAgent;
require HTTP::Request::Common;
unless (defined &HTTP::Request::Common::OPTIONS) {
package HTTP::Request::Common;
no strict 'vars';
*OPTIONS = sub { _simple_req(OPTIONS => @_) };
push @EXPORT, 'OPTIONS';
}
1;
};
}
unless ($have_lwp) {
require Apache::TestClient;
}
sub has_lwp { $have_lwp }
unless ($have_lwp) {
#need to define the shortcuts even though the wont be used
#so Perl can parse test scripts
@HTTP::Request::Common::EXPORT = qw(GET HEAD POST PUT OPTIONS);
}
sub install_http11 {
eval {
die "no LWP" unless $have_lwp;
LWP->VERSION(5.60); #minimal version
require LWP::Protocol::http;
#LWP::Protocol::http10 is used by default
LWP::Protocol::implementor('http', 'LWP::Protocol::http');
};
}
use vars qw(@EXPORT @ISA $RedirectOK $DebugLWP);
require Exporter;
*import = \&Exporter::import;
@EXPORT = @HTTP::Request::Common::EXPORT;
@ISA = qw(LWP::UserAgent);
my $UA;
my $REDIR = $have_lwp ? undef : 1;
my $conn_opts = {};
sub module {
my $module = shift;
$Apache::TestRequest::Module = $module if $module;
$Apache::TestRequest::Module;
}
sub scheme {
my $scheme = shift;
$Apache::TestRequest::Scheme = $scheme if $scheme;
$Apache::TestRequest::Scheme;
}
sub module2path {
my $package = shift;
# httpd (1.3 && 2) / winFU have problems when the first path's
# segment includes ':' (security precaution which breaks the rfc)
# so we can't use /TestFoo::bar as path_info
(my $path = $package) =~ s/::/__/g;
return $path;
}
sub module2url {
my $module = shift;
my $opt = shift || {};
my $scheme = $opt->{scheme} || 'http';
my $path = exists $opt->{path} ? $opt->{path} : module2path($module);
module($module);
my $config = Apache::Test::config();
my $hostport = hostport($config);
$path =~ s|^/||;
return "$scheme://$hostport/$path";
}
sub user_agent {
my $args = {@_};
if (delete $args->{reset}) {
$UA = undef;
}
if (exists $args->{requests_redirectable}) {
my $redir = $args->{requests_redirectable};
if (ref $redir and (@$redir > 1 or $redir->[0] ne 'POST')) {
# Set our internal flag if there's no LWP.
$REDIR = $have_lwp ? undef : 1;
} elsif ($redir) {
if ($have_lwp) {
$args->{requests_redirectable} = [ qw/GET HEAD POST/ ];
$REDIR = undef;
} else {
# Set our internal flag.
$REDIR = 1;
}
} else {
# Make sure our internal flag is false if there's no LWP.
$REDIR = $have_lwp ? undef : 0;
}
}
$args->{keep_alive} ||= $ENV{APACHE_TEST_HTTP11};
if ($args->{keep_alive}) {
install_http11();
eval {
require LWP::Protocol::https; #https10 is the default
LWP::Protocol::implementor('https', 'LWP::Protocol::https');
};
}
# in LWP 6, verify_hostname defaults to on, so SSL_ca_file
# needs to be set accordingly
if ($have_lwp and $LWP::VERSION >= 6.0 and not exists $args->{ssl_opts}->{SSL_ca_file}) {
my $vars = Apache::Test::vars();
my $cafile = "$vars->{sslca}/$vars->{sslcaorg}/certs/ca.crt";
$args->{ssl_opts}->{SSL_ca_file} = $cafile;
# IO::Socket:SSL raw socket compatibility
$conn_opts->{SSL_ca_file} = $cafile;
}
eval { $UA ||= __PACKAGE__->new(%$args); };
}
sub user_agent_request_num {
my $res = shift;
$res->header('Client-Request-Num') || #lwp 5.60
$res->header('Client-Response-Num'); #lwp 5.62+
}
sub user_agent_keepalive {
$ENV{APACHE_TEST_HTTP11} = shift;
}
sub do_request {
my($ua, $method, $url, $callback) = @_;
my $r = HTTP::Request->new($method, resolve_url($url));
my $response = $ua->request($r, $callback);
lwp_trace($response);
}
sub hostport {
my $config = shift || Apache::Test::config();
my $vars = $config->{vars};
local $vars->{scheme} =
$Apache::TestRequest::Scheme || $vars->{scheme};
my $hostport = $config->hostport;
my $default_hostport = join ':', $vars->{servername}, $vars->{port};
if (my $module = $Apache::TestRequest::Module) {
$hostport = $module eq 'default'
? $default_hostport
: $config->{vhosts}->{$module}->{hostport};
}
$hostport || $default_hostport;
}
sub resolve_url {
my $url = shift;
Carp::croak("no url passed") unless defined $url;
return $url if $url =~ m,^(\w+):/,;
$url = "/$url" unless $url =~ m,^/,;
my $vars = Apache::Test::vars();
local $vars->{scheme} =
$Apache::TestRequest::Scheme || $vars->{scheme} || 'http';
scheme_fixup($vars->{scheme});
my $hostport = hostport();
return "$vars->{scheme}://$hostport$url";
}
my %wanted_args = map {$_, 1} qw(username password realm content filename
redirect_ok cert);
sub wanted_args {
\%wanted_args;
}
sub redirect_ok {
my $self = shift;
if ($have_lwp) {
# Return user setting or let LWP handle it.
return $RedirectOK if defined $RedirectOK;
return $self->SUPER::redirect_ok(@_);
}
# No LWP. We don't support redirect on POST.
return 0 if $self->method eq 'POST';
# Return user setting or our internal calculation.
return $RedirectOK if defined $RedirectOK;
return $REDIR;
lib/Apache/TestRequest.pm view on Meta::CPAN
if ($have_lwp) {
user_agent();
$url = resolve_url($url);
}
else {
lwp_debug() if $ENV{APACHE_TEST_DEBUG_LWP};
}
my($pass, $keep) = Apache::TestConfig::filter_args(\@_, \%wanted_args);
%credentials = ();
if (defined $keep->{username}) {
$credentials{$keep->{realm} || '__ALL__'} =
[$keep->{username}, $keep->{password}];
}
if (defined(my $content = $keep->{content})) {
if ($content eq '-') {
$content = join '', <STDIN>;
}
elsif ($content =~ /^x(\d+)$/) {
$content = 'a' x $1;
}
push @$pass, content => $content;
}
if (exists $keep->{cert}) {
set_client_cert($keep->{cert});
}
return ($url, $pass, $keep);
}
sub UPLOAD {
my($url, $pass, $keep) = prepare(@_);
local $RedirectOK = exists $keep->{redirect_ok}
? $keep->{redirect_ok}
: $RedirectOK;
if ($keep->{filename}) {
return upload_file($url, $keep->{filename}, $pass);
}
else {
return upload_string($url, $keep->{content});
}
}
sub UPLOAD_BODY {
UPLOAD(@_)->content;
}
sub UPLOAD_BODY_ASSERT {
content_assert(UPLOAD(@_));
}
#lwp only supports files
sub upload_string {
my($url, $data) = @_;
my $CRLF = "\015\012";
my $bound = 742617000027;
my $req = HTTP::Request->new(POST => $url);
my $content = join $CRLF,
"--$bound",
"Content-Disposition: form-data; name=\"HTTPUPLOAD\"; filename=\"b\"",
"Content-Type: text/plain", "",
$data, "--$bound--", "";
$req->header("Content-Length", length($content));
$req->content_type("multipart/form-data; boundary=$bound");
$req->content($content);
$UA->request($req);
}
sub upload_file {
my($url, $file, $args) = @_;
my $content = [@$args, filename => [$file]];
$UA->request(HTTP::Request::Common::POST($url,
Content_Type => 'form-data',
Content => $content,
));
}
#useful for POST_HEAD and $DebugLWP (see below)
sub lwp_as_string {
my($r, $want_body) = @_;
my $content = $r->content;
unless ($r->isa('HTTP::Request') or
$r->header('Content-Length') or
$r->header('Transfer-Encoding'))
{
$r->header('Content-Length' => length $content);
$r->header('X-Content-length-note' => 'added by Apache::TestRequest');
}
$r->content('') unless $want_body;
(my $string = $r->as_string) =~ s/^/\#/mg;
$r->content($content); #reset
$string;
}
$DebugLWP = 0; #1 == print METHOD URL and header response for all requests
#2 == #1 + response body
#other == passed to LWP::Debug->import
sub lwp_debug {
package main; #wtf: else package in perldb changes
my $val = $_[0] || $ENV{APACHE_TEST_DEBUG_LWP};
return unless $val;
if ($val =~ /^\d+$/) {
$Apache::TestRequest::DebugLWP = $val;
return "\$Apache::TestRequest::DebugLWP = $val\n";
}
else {
my(@args) = @_ ? @_ : split /\s+/, $val;
require LWP::Debug;
LWP::Debug->import(@args);
return "LWP::Debug->import(@args)\n";
}
}
sub lwp_trace {
my $r = shift;
unless ($r->request->protocol) {
#lwp always sends a request, but never sets
#$r->request->protocol, happens deeper in the
#LWP::Protocol::http* modules
my $proto = user_agent_request_num($r) ? "1.1" : "1.0";
$r->request->protocol("HTTP/$proto");
}
my $want_body = $DebugLWP > 1;
print "#lwp request:\n",
lwp_as_string($r->request, $want_body);
print "#server response:\n",
lwp_as_string($r, $want_body);
}
sub lwp_call {
my($name, $shortcut) = (shift, shift);
my $r = (\&{$name})->(@_);
Carp::croak("$name(@_) didn't return a response object") unless $r;
my $error = "";
unless ($shortcut) {
#GET, HEAD, POST
if ($r->method eq "POST" && !defined($r->header("Content-Length"))) {
$r->header('Content-Length' => length($r->content));
}
$r = $UA ? $UA->request($r) : $r;
my $proto = $r->protocol;
if (defined($proto)) {
if ($proto !~ /^HTTP\/(\d\.\d)$/) {
$error = "response had no protocol (is LWP broken or something?)";
}
if ($1 ne "1.0" && $1 ne "1.1") {
$error = "response had protocol HTTP/$1 (headers not sent?)"
unless ($1 eq "0.9" && $ENV{APACHE_TEST_HTTP_09_OK});
}
}
}
if ($DebugLWP and not $shortcut) {
lwp_trace($r);
}
Carp::croak($error) if $error;
return $shortcut ? $r->$shortcut() : $r;
}
my %shortcuts = (RC => sub { shift->code },
OK => sub { shift->is_success },
STR => sub { shift->as_string },
HEAD => sub { lwp_as_string(shift, 0) },
BODY => sub { shift->content },
BODY_ASSERT => sub { content_assert(shift) },
);
for my $name (@EXPORT) {
my $package = $have_lwp ?
'HTTP::Request::Common': 'Apache::TestClient';
my $method = join '::', $package, $name;
no strict 'refs';
next unless defined &$method;
*$name = sub {
my($url, $pass, $keep) = prepare(@_);
local $RedirectOK = exists $keep->{redirect_ok}
? $keep->{redirect_ok}
: $RedirectOK;
return lwp_call($method, undef, $url, @$pass);
};
while (my($shortcut, $cv) = each %shortcuts) {
my $alias = join '_', $name, $shortcut;
*$alias = sub { lwp_call($name, $cv, @_) };
}
}
my @export_std = @EXPORT;
for my $method (@export_std) {
push @EXPORT, map { join '_', $method, $_ } keys %shortcuts;
}
push @EXPORT, qw(UPLOAD UPLOAD_BODY UPLOAD_BODY_ASSERT);
sub to_string {
my $obj = shift;
ref($obj) ? $obj->as_string : $obj;
}
# request an interpreter instance and use this interpreter id to
# select the same interpreter in requests below
sub same_interp_tie {
my($url) = @_;
my $res = GET($url, INTERP_KEY, 'tie');
unless ($res->code == 200) {
die sprintf "failed to init the same_handler data (url=%s). " .
"Failed with code=%s, response:\n%s",
$url, $res->code, $res->content;
}
my $same_interp = $res->header(INTERP_KEY);
return $same_interp;
}
# run the request though the selected perl interpreter, by polling
# until we found it
# currently supports only GET, HEAD, PUT, POST subs
sub same_interp_do {
my($same_interp, $sub, $url, @args) = @_;
die "must pass an interpreter id, obtained via same_interp_tie()"
unless defined $same_interp and $same_interp;
push @args, (INTERP_KEY, $same_interp);
my $res = '';
( run in 1.457 second using v1.01-cache-2.11-cpan-39bf76dae61 )