AnyEvent-HTTPD-SendMultiHeaderPatch

 view release on metacpan or  search on metacpan

lib/AnyEvent/HTTPD/SendMultiHeaderPatch.pm  view on Meta::CPAN

package AnyEvent::HTTPD::SendMultiHeaderPatch;

use 5.006;
use strict;
use warnings FATAL => 'all';
no warnings 'redefine';

use AnyEvent::HTTPD::HTTPConnection;

=head1 NAME

AnyEvent::HTTPD::SendMultiHeaderPatch -
    Patch (hack) of AnyEvent::HTTPD for sending multiple headers with the same field name.

=head1 VERSION

Version 0.1.3

=cut

our $VERSION = '0.001003';

use AnyEvent::HTTPD;
use AnyEvent::HTTPD::Util;
use AnyEvent::HTTPD::HTTPConnection;

use Scalar::Util qw(weaken);

push @AnyEvent::HTTPD::Util::EXPORT, qw(header_add header_gets);

*AnyEvent::HTTPD::Util::header_add = sub {
    my ($hdrs, $name, $value) = @_;
    $name = AnyEvent::HTTPD::Util::_header_transform_case_insens ($hdrs, $name);
    if( exists $hdrs->{$name} ) {
        $hdrs->{$name} .= "\0".$value;
    }
    else {
       $hdrs->{$name} = $value;
    }
};

*AnyEvent::HTTPD::Util::header_gets = sub {
    my ($hdrs, $name) = @_;
    $name = AnyEvent::HTTPD::Util::_header_transform_case_insens ($hdrs, $name);
    exists $hdrs->{$name} ? [split /\0/, $hdrs->{$name}] : []
};

*AnyEvent::HTTPD::HTTPConnection::response = sub {
   my ($self, $code, $msg, $hdr, $content, $no_body) = @_;
   return if $self->{disconnected};
   return unless $self->{hdl};

   my $res = "HTTP/1.0 $code $msg\015\012";
   header_set ($hdr, 'Date' => AnyEvent::HTTPD::HTTPConnection::_time_to_http_date time)
      unless header_exists ($hdr, 'Date');
   header_set ($hdr, 'Expires' => header_get ($hdr, 'Date'))
      unless header_exists ($hdr, 'Expires');
   header_set ($hdr, 'Cache-Control' => "max-age=0")
      unless header_exists ($hdr, 'Cache-Control');
   header_set ($hdr, 'Connection' =>
                    ($self->{keep_alive} ? 'Keep-Alive' : 'close'));

   header_set ($hdr, 'Content-Length' => length "$content")
      unless header_exists ($hdr, 'Content-Length')
             || ref $content;

   unless (defined header_get ($hdr, 'Content-Length')) {
      # keep alive with no content length will NOT work.
      delete $self->{keep_alive};
      header_set ($hdr, 'Connection' => 'close');
   }

   while (my ($h, $v) = each %$hdr) {
      next unless defined $v;
      for my $vv ( split /\0/, $v ) {
          $res .= "$h: $vv\015\012";
      }
   }

   $res .= "\015\012";

   if ($no_body) { # for HEAD requests!
      $self->{hdl}->push_write ($res);
      $self->response_done;
      return;
   }

   if (ref ($content) eq 'CODE') {
      weaken $self;

      my $chunk_cb = sub {
         my ($chunk) = @_;

         return 0 unless defined ($self) && defined ($self->{hdl}) && !$self->{disconnected};

         delete $self->{transport_polled};

         if (defined ($chunk) && length ($chunk) > 0) {
            $self->{hdl}->push_write ($chunk);

         } else {
            $self->response_done;
         }

         return 1;
      };

      $self->{transfer_cb} = $content;

      $self->{hdl}->on_drain (sub {
         return unless $self;

         if (length $res) {
            my $r = $res;
            undef $res;
            $chunk_cb->($r);

         } elsif (not $self->{transport_polled}) {
            $self->{transport_polled} = 1;
            $self->{transfer_cb}->($chunk_cb) if $self;
         }
      });

   } else {
      $res .= $content;
      $self->{hdl}->push_write ($res);
      $self->response_done;
   }
};

=head1 SYNOPSIS

    use AnyEvent::HTTPD; # Optional,
                         # because the patch module will use it first.
    use AnyEvent::HTTPD::SendMultiHeaderPatch;

    # In the http request handler,
    # separate the multiple values of the same field with \0 character.
    sub {
        my($httpd, $req) = @_;
        # ...
        $req->respond(
            200, 'OK', {
                'Set-Cookie' => "a=123; path=/; domain=.example.com\0b=456; path=/; domain=.example.com"
            }, "Set the cookies"
        );
    }

    # Or use the added util function header_add in AnyEvent::HTTPD::Util.



( run in 2.391 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )