窥探不同plack中间件之间的http标头

时间:2014-03-28 10:44:22

标签: perl plack

如果我理解正确,PSGI应用程序就像下一个:

  • 从浏览器获取请求
  • 请求是“气泡”通过顺序中的一些中间件,因为它们在builder
  • 中定义
  • 请求来到我的应用
  • 我的应用会产生一些回复
  • 通过一些中间件再次回应泡沫
  • 最后将响应发送到浏览器

当请求登陆$app时,我可以轻松地调试打印所有标题(例如Cookie)。

问题是: 如何通过许多中间件向我的应用程序发出请求时调试打印headers的实际状态,同时通过中间件再次响应。

所以,有一个(简单的)app.psgi,就像下一个:

use strict;
use warnings;
use Plack::Builder;

my $app = sub { ... };

builder {
         # <- debug-print the first request headers
         #                and the last respond headers here
    enable "Debug";
         # <- debug-print the actual state of request/respond headers here
    enable "mid2";
         # <- and here
    enable "mid3";
         # <- and here

    $app; # <- and finally here - this is of course EASY
}

这可能不像是那样简单,

print STDERR Dumper $dont_know_what->request->headers(); #HTTP::Headers ???
print STDERR Dumper $dont_know_what->respond->headers();

所以加一个赏金:);)

2 个答案:

答案 0 :(得分:3)

一种基本方法是创建一个中间件,在执行包装的应用程序之前转储标头,然后立即转储。然后,在您希望查看标头的每个位置启用此中间件,如您在伪代码中指出的那样。

以下代码通过在每次启用时构建内联中间件来实现此目的。

use Plack::Builder;
use Plack::Request;
use Plack::Response;

sub headers_around {
  my $position = shift;

  # build and return the headers_around middleware as a closure
  return sub {
    my $app = shift;
    # gets called each request
    return sub {
      my $env = shift;

      my $req = Plack::Request->new($env);
      # display headers before next middleware
      print STDERR "req headers before $position:\n" . $req->headers->as_string . "\n=====\n";

      # execute the next app on the stack
      my $res = $app->($env);

      my $response = Plack::Response->new(@$res);

      # display headers after previous middleware
      print STDERR "res headers after $position:\n" . $response->headers->as_string . "\n=====\n";
      return $res;
    };
  };
};

builder {

  enable headers_around('Debug');
  enable 'Debug';

  enable headers_around('Lint');
  enable 'Lint';

  enable headers_around('StackTrace');
  enable 'StackTrace', force => 1;

  enable headers_around('App');
  mount '/' => builder { sub {
    return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello World' ] ];
  }}
};

# now build the application enabling regular middleware with our inline middleware
builder {

  enable headers_around('Debug');
  enable 'Debug';

  enable headers_around('Lint');
  enable 'Lint';

  enable headers_around('StackTrace');
  enable 'StackTrace', force => 1;

  enable headers_around('App');
  mount '/' => builder { sub {
        return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello World' ] ];
  }}
};

当我用plackup运行时,我得到以下输出:

$ plackup --app between_middleware.psgi
HTTP::Server::PSGI: Accepting connections at http://0:5000/
req headers before Debug:
Connection: Keep-Alive
Accept: */*
Host: 0:5000
User-Agent: Wget/1.12 (linux-gnu)

=====
req headers before Lint:
Connection: Keep-Alive
Accept: */*
Host: 0:5000
User-Agent: Wget/1.12 (linux-gnu)

=====
req headers before StackTrace:
Connection: Keep-Alive
Accept: */*
Host: 0:5000
User-Agent: Wget/1.12 (linux-gnu)

=====
req headers before App:
Connection: Keep-Alive
Accept: */*
Host: 0:5000
User-Agent: Wget/1.12 (linux-gnu)

=====
res headers after App:
Content-Type: text/plain

=====
res headers after StackTrace:
Content-Type: text/plain

=====
res headers after Lint:
Content-Type: text/plain

=====
res headers after Debug:
Content-Type: text/plain

=====
127.0.0.1 - - [02/Apr/2014:19:37:30 -0700] "GET / HTTP/1.0" 200 11 "-" "Wget/1.12 (linux-gnu)"

显然你可以将它变成像Ashley这样的实际中间件,你可能需要调整它以使用你现有的任何设施发送日志消息。

答案 1 :(得分:2)

中间件

package ShowMeTheHeaders;
use parent "Plack::Middleware";
use Plack::Request;
use Plack::Response
require Text::Wrap;

my $_call_back = sub {
    my $response = Plack::Response->new(@{+shift});
    print "* Response Headers:\n",
        Text::Wrap::wrap("\t", "\t", $response->headers->as_string);
    return; # Explicit return suggested by docs.
};

sub call {
    my $self = shift;
    my $request = Plack::Request->new(shift);
    print "* Request Headers:\n",
        Text::Wrap::wrap("\t", "\t", $request->headers->as_string);
    my $response = $self->app->($request);
    Plack::Util::response_cb($response, $_call_back);
}

1;

您可以在没有客观化的情况下执行此操作(Plack::RequestPlack::Response),但是您必须处理标题字段的原始属性和键,而不是更加愉快的->as_string。另请参阅Plack::Middleware的“响应回调”部分。

demo psgi

use warnings;
use strict;
use Plack::Builder;

my $app = sub {
    [ 200,
      [ "Content-Type" => "text/plain" ],
      [ "O HAI, PLAK!" ]
    ];
};

builder {
    enable "+ShowMeTheHeaders";
    mount "/" => $app;
};