ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-HTTP/HTTP.pm
(Generate patch)

Comparing AnyEvent-HTTP/HTTP.pm (file contents):
Revision 1.81 by root, Sun Jan 2 01:20:17 2011 UTC vs.
Revision 1.82 by root, Sun Jan 2 04:50:40 2011 UTC

475 while ( 475 while (
476 m{ 476 m{
477 \G\s* 477 \G\s*
478 (?: 478 (?:
479 expires \s*=\s* ([A-Z][a-z][a-z]+,\ [^,;]+) 479 expires \s*=\s* ([A-Z][a-z][a-z]+,\ [^,;]+)
480 | ([^=;,[:space:]]+) \s*=\s* (?: "((?:[^\\"]+|\\.)*)" | ([^=;,[:space:]]*) ) 480 | ([^=;,[:space:]]+) (?: \s*=\s* (?: "((?:[^\\"]+|\\.)*)" | ([^=;,[:space:]]*) ) )?
481 ) 481 )
482 }gcxsi 482 }gcxsi
483 ) { 483 ) {
484 my $name = $2; 484 my $name = $2;
485 my $value = $4; 485 my $value = $4;
486 486
487 unless (defined $name) { 487 if (defined $1) {
488 # expires 488 # expires
489 $name = "expires"; 489 $name = "expires";
490 $value = $1; 490 $value = $1;
491 } elsif (!defined $value) { 491 } elsif (defined $3) {
492 # quoted 492 # quoted
493 $value = $3; 493 $value = $3;
494 $value =~ s/\\(.)/$1/gs; 494 $value =~ s/\\(.)/$1/gs;
495 } 495 }
496 496
658 $state{slot_guard} = shift; 658 $state{slot_guard} = shift;
659 659
660 return unless $state{connect_guard}; 660 return unless $state{connect_guard};
661 661
662 my $ae_error = 595; # connecting 662 my $ae_error = 595; # connecting
663
664 # handle actual, non-tunneled, request
665 my $handle_actual_request = sub {
666 $ae_error = 596; # request phase
667
668 $state{handle}->starttls ("connect") if $uscheme eq "https" && !exists $state{handle}{tls};
669
670 # send request
671 $state{handle}->push_write (
672 "$method $rpath HTTP/1.1\015\012"
673 . (join "", map "\u$_: $hdr{$_}\015\012", grep defined $hdr{$_}, keys %hdr)
674 . "\015\012"
675 . (delete $arg{body})
676 );
677
678 # return if error occured during push_write()
679 return unless %state;
680
681 %hdr = (); # reduce memory usage, save a kitten, also make it possible to re-use
682
683 # status line and headers
684 $state{read_response} = sub {
685 for ("$_[1]") {
686 y/\015//d; # weed out any \015, as they show up in the weirdest of places.
687
688 /^HTTP\/0*([0-9\.]+) \s+ ([0-9]{3}) (?: \s+ ([^\012]*) )? \012/gxci
689 or return (%state = (), $cb->(undef, { @pseudo, Status => 599, Reason => "Invalid server response" }));
690
691 # 100 Continue handling
692 # should not happen as we don't send expect: 100-continue,
693 # but we handle it just in case.
694 # since we send the request body regardless, if we get an error
695 # we are out of-sync, which we currently do NOT handle correctly.
696 return $state{handle}->push_read (line => $qr_nlnl, $state{read_response})
697 if $2 eq 100;
698
699 push @pseudo,
700 HTTPVersion => $1,
701 Status => $2,
702 Reason => $3,
703 ;
704
705 my $hdr = parse_hdr
706 or return (%state = (), $cb->(undef, { @pseudo, Status => 599, Reason => "Garbled response headers" }));
707
708 %hdr = (%$hdr, @pseudo);
709 }
710
711 # redirect handling
712 # microsoft and other shitheads don't give a shit for following standards,
713 # try to support some common forms of broken Location headers.
714 if ($hdr{location} !~ /^(?: $ | [^:\/?\#]+ : )/x) {
715 $hdr{location} =~ s/^\.\/+//;
716
717 my $url = "$rscheme://$uhost:$uport";
718
719 unless ($hdr{location} =~ s/^\///) {
720 $url .= $upath;
721 $url =~ s/\/[^\/]*$//;
722 }
723
724 $hdr{location} = "$url/$hdr{location}";
725 }
726
727 my $redirect;
728
729 if ($recurse) {
730 my $status = $hdr{Status};
731
732 # industry standard is to redirect POST as GET for
733 # 301, 302 and 303, in contrast to HTTP/1.0 and 1.1.
734 # also, the UA should ask the user for 301 and 307 and POST,
735 # industry standard seems to be to simply follow.
736 # we go with the industry standard.
737 if ($status == 301 or $status == 302 or $status == 303) {
738 # HTTP/1.1 is unclear on how to mutate the method
739 $method = "GET" unless $method eq "HEAD";
740 $redirect = 1;
741 } elsif ($status == 307) {
742 $redirect = 1;
743 }
744 }
745
746 my $finish = sub { # ($data, $err_status, $err_reason[, $keepalive])
747 my $may_keep_alive = $_[3];
748
749 $state{handle}->destroy if $state{handle};
750 %state = ();
751
752 if (defined $_[1]) {
753 $hdr{OrigStatus} = $hdr{Status}; $hdr{Status} = $_[1];
754 $hdr{OrigReason} = $hdr{Reason}; $hdr{Reason} = $_[2];
755 }
756
757 # set-cookie processing
758 if ($arg{cookie_jar}) {
759 cookie_jar_set_cookie $arg{cookie_jar}, $hdr{"set-cookie"}, $uhost, $hdr{date};
760 }
761
762 if ($redirect && exists $hdr{location}) {
763 # we ignore any errors, as it is very common to receive
764 # Content-Length != 0 but no actual body
765 # we also access %hdr, as $_[1] might be an erro
766 http_request (
767 $method => $hdr{location},
768 %arg,
769 recurse => $recurse - 1,
770 Redirect => [$_[0], \%hdr],
771 $cb);
772 } else {
773 $cb->($_[0], \%hdr);
774 }
775 };
776
777 $ae_error = 597; # body phase
778
779 my $len = $hdr{"content-length"};
780
781 if (!$redirect && $arg{on_header} && !$arg{on_header}(\%hdr)) {
782 $finish->(undef, 598 => "Request cancelled by on_header");
783 } elsif (
784 $hdr{Status} =~ /^(?:1..|204|205|304)$/
785 or $method eq "HEAD"
786 or (defined $len && !$len)
787 ) {
788 # no body
789 $finish->("", undef, undef, 1);
790 } else {
791 # body handling, many different code paths
792 # - no body expected
793 # - want_body_handle
794 # - te chunked
795 # - 2x length known (with or without on_body)
796 # - 2x length not known (with or without on_body)
797 if (!$redirect && $arg{want_body_handle}) {
798 $_[0]->on_eof (undef);
799 $_[0]->on_error (undef);
800 $_[0]->on_read (undef);
801
802 $finish->(delete $state{handle});
803
804 } elsif ($hdr{"transfer-encoding"} =~ /\bchunked\b/i) {
805 my $cl = 0;
806 my $body = undef;
807 my $on_body = $arg{on_body} || sub { $body .= shift; 1 };
808
809 $state{read_chunk} = sub {
810 $_[1] =~ /^([0-9a-fA-F]+)/
811 or $finish->(undef, $ae_error => "Garbled chunked transfer encoding");
812
813 my $len = hex $1;
814
815 if ($len) {
816 $cl += $len;
817
818 $_[0]->push_read (chunk => $len, sub {
819 $on_body->($_[1], \%hdr)
820 or return $finish->(undef, 598 => "Request cancelled by on_body");
821
822 $_[0]->push_read (line => sub {
823 length $_[1]
824 and return $finish->(undef, $ae_error => "Garbled chunked transfer encoding");
825 $_[0]->push_read (line => $state{read_chunk});
826 });
827 });
828 } else {
829 $hdr{"content-length"} ||= $cl;
830
831 $_[0]->push_read (line => $qr_nlnl, sub {
832 if (length $_[1]) {
833 for ("$_[1]") {
834 y/\015//d; # weed out any \015, as they show up in the weirdest of places.
835
836 my $hdr = parse_hdr
837 or return $finish->(undef, $ae_error => "Garbled response trailers");
838
839 %hdr = (%hdr, %$hdr);
840 }
841 }
842
843 $finish->($body, undef, undef, 1);
844 });
845 }
846 };
847
848 $_[0]->push_read (line => $state{read_chunk});
849
850 } elsif ($arg{on_body}) {
851 if ($len) {
852 $_[0]->on_read (sub {
853 $len -= length $_[0]{rbuf};
854
855 $arg{on_body}(delete $_[0]{rbuf}, \%hdr)
856 or return $finish->(undef, 598 => "Request cancelled by on_body");
857
858 $len > 0
859 or $finish->("", undef, undef, 1);
860 });
861 } else {
862 $_[0]->on_eof (sub {
863 $finish->("");
864 });
865 $_[0]->on_read (sub {
866 $arg{on_body}(delete $_[0]{rbuf}, \%hdr)
867 or $finish->(undef, 598 => "Request cancelled by on_body");
868 });
869 }
870 } else {
871 $_[0]->on_eof (undef);
872
873 if ($len) {
874 $_[0]->on_read (sub {
875 $finish->((substr delete $_[0]{rbuf}, 0, $len, ""), undef, undef, 1)
876 if $len <= length $_[0]{rbuf};
877 });
878 } else {
879 $_[0]->on_error (sub {
880 ($! == Errno::EPIPE || !$!)
881 ? $finish->(delete $_[0]{rbuf})
882 : $finish->(undef, $ae_error => $_[2]);
883 });
884 $_[0]->on_read (sub { });
885 }
886 }
887 }
888 };
889
890 $state{handle}->push_read (line => $qr_nlnl, $state{read_response});
891 };
663 892
664 my $connect_cb = sub { 893 my $connect_cb = sub {
665 $state{fh} = shift 894 $state{fh} = shift
666 or do { 895 or do {
667 my $err = "$!"; 896 my $err = "$!";
698# $hdr{connection} = "keep-alive"; 927# $hdr{connection} = "keep-alive";
699# } 928# }
700 929
701 $state{handle}->starttls ("connect") if $rscheme eq "https"; 930 $state{handle}->starttls ("connect") if $rscheme eq "https";
702 931
703 # handle actual, non-tunneled, request
704 my $handle_actual_request = sub {
705 $ae_error = 596; # request phase
706
707 $state{handle}->starttls ("connect") if $uscheme eq "https" && !exists $state{handle}{tls};
708
709 # send request
710 $state{handle}->push_write (
711 "$method $rpath HTTP/1.1\015\012"
712 . (join "", map "\u$_: $hdr{$_}\015\012", grep defined $hdr{$_}, keys %hdr)
713 . "\015\012"
714 . (delete $arg{body})
715 );
716
717 # return if error occured during push_write()
718 return unless %state;
719
720 %hdr = (); # reduce memory usage, save a kitten, also make it possible to re-use
721
722 # status line and headers
723 $state{read_response} = sub {
724 for ("$_[1]") {
725 y/\015//d; # weed out any \015, as they show up in the weirdest of places.
726
727 /^HTTP\/0*([0-9\.]+) \s+ ([0-9]{3}) (?: \s+ ([^\012]*) )? \012/gxci
728 or return (%state = (), $cb->(undef, { @pseudo, Status => 599, Reason => "Invalid server response" }));
729
730 # 100 Continue handling
731 # should not happen as we don't send expect: 100-continue,
732 # but we handle it just in case.
733 # since we send the request body regardless, if we get an error
734 # we are out of-sync, which we currently do NOT handle correctly.
735 return $state{handle}->push_read (line => $qr_nlnl, $state{read_response})
736 if $2 eq 100;
737
738 push @pseudo,
739 HTTPVersion => $1,
740 Status => $2,
741 Reason => $3,
742 ;
743
744 my $hdr = parse_hdr
745 or return (%state = (), $cb->(undef, { @pseudo, Status => 599, Reason => "Garbled response headers" }));
746
747 %hdr = (%$hdr, @pseudo);
748 }
749
750 # redirect handling
751 # microsoft and other shitheads don't give a shit for following standards,
752 # try to support some common forms of broken Location headers.
753 if ($hdr{location} !~ /^(?: $ | [^:\/?\#]+ : )/x) {
754 $hdr{location} =~ s/^\.\/+//;
755
756 my $url = "$rscheme://$uhost:$uport";
757
758 unless ($hdr{location} =~ s/^\///) {
759 $url .= $upath;
760 $url =~ s/\/[^\/]*$//;
761 }
762
763 $hdr{location} = "$url/$hdr{location}";
764 }
765
766 my $redirect;
767
768 if ($recurse) {
769 my $status = $hdr{Status};
770
771 # industry standard is to redirect POST as GET for
772 # 301, 302 and 303, in contrast to HTTP/1.0 and 1.1.
773 # also, the UA should ask the user for 301 and 307 and POST,
774 # industry standard seems to be to simply follow.
775 # we go with the industry standard.
776 if ($status == 301 or $status == 302 or $status == 303) {
777 # HTTP/1.1 is unclear on how to mutate the method
778 $method = "GET" unless $method eq "HEAD";
779 $redirect = 1;
780 } elsif ($status == 307) {
781 $redirect = 1;
782 }
783 }
784
785 my $finish = sub { # ($data, $err_status, $err_reason[, $keepalive])
786 my $may_keep_alive = $_[3];
787
788 $state{handle}->destroy if $state{handle};
789 %state = ();
790
791 if (defined $_[1]) {
792 $hdr{OrigStatus} = $hdr{Status}; $hdr{Status} = $_[1];
793 $hdr{OrigReason} = $hdr{Reason}; $hdr{Reason} = $_[2];
794 }
795
796 # set-cookie processing
797 if ($arg{cookie_jar}) {
798 cookie_jar_set_cookie $arg{cookie_jar}, $hdr{"set-cookie"}, $uhost, $hdr{date};
799 }
800
801 if ($redirect && exists $hdr{location}) {
802 # we ignore any errors, as it is very common to receive
803 # Content-Length != 0 but no actual body
804 # we also access %hdr, as $_[1] might be an erro
805 http_request (
806 $method => $hdr{location},
807 %arg,
808 recurse => $recurse - 1,
809 Redirect => [$_[0], \%hdr],
810 $cb);
811 } else {
812 $cb->($_[0], \%hdr);
813 }
814 };
815
816 $ae_error = 597; # body phase
817
818 my $len = $hdr{"content-length"};
819
820 if (!$redirect && $arg{on_header} && !$arg{on_header}(\%hdr)) {
821 $finish->(undef, 598 => "Request cancelled by on_header");
822 } elsif (
823 $hdr{Status} =~ /^(?:1..|204|205|304)$/
824 or $method eq "HEAD"
825 or (defined $len && !$len)
826 ) {
827 # no body
828 $finish->("", undef, undef, 1);
829 } else {
830 # body handling, many different code paths
831 # - no body expected
832 # - want_body_handle
833 # - te chunked
834 # - 2x length known (with or without on_body)
835 # - 2x length not known (with or without on_body)
836 if (!$redirect && $arg{want_body_handle}) {
837 $_[0]->on_eof (undef);
838 $_[0]->on_error (undef);
839 $_[0]->on_read (undef);
840
841 $finish->(delete $state{handle});
842
843 } elsif ($hdr{"transfer-encoding"} =~ /\bchunked\b/i) {
844 my $cl = 0;
845 my $body = undef;
846 my $on_body = $arg{on_body} || sub { $body .= shift; 1 };
847
848 $state{read_chunk} = sub {
849 $_[1] =~ /^([0-9a-fA-F]+)/
850 or $finish->(undef, $ae_error => "Garbled chunked transfer encoding");
851
852 my $len = hex $1;
853
854 if ($len) {
855 $cl += $len;
856
857 $_[0]->push_read (chunk => $len, sub {
858 $on_body->($_[1], \%hdr)
859 or return $finish->(undef, 598 => "Request cancelled by on_body");
860
861 $_[0]->push_read (line => sub {
862 length $_[1]
863 and return $finish->(undef, $ae_error => "Garbled chunked transfer encoding");
864 $_[0]->push_read (line => $state{read_chunk});
865 });
866 });
867 } else {
868 $hdr{"content-length"} ||= $cl;
869
870 $_[0]->push_read (line => $qr_nlnl, sub {
871 if (length $_[1]) {
872 for ("$_[1]") {
873 y/\015//d; # weed out any \015, as they show up in the weirdest of places.
874
875 my $hdr = parse_hdr
876 or return $finish->(undef, $ae_error => "Garbled response trailers");
877
878 %hdr = (%hdr, %$hdr);
879 }
880 }
881
882 $finish->($body, undef, undef, 1);
883 });
884 }
885 };
886
887 $_[0]->push_read (line => $state{read_chunk});
888
889 } elsif ($arg{on_body}) {
890 if ($len) {
891 $_[0]->on_read (sub {
892 $len -= length $_[0]{rbuf};
893
894 $arg{on_body}(delete $_[0]{rbuf}, \%hdr)
895 or return $finish->(undef, 598 => "Request cancelled by on_body");
896
897 $len > 0
898 or $finish->("", undef, undef, 1);
899 });
900 } else {
901 $_[0]->on_eof (sub {
902 $finish->("");
903 });
904 $_[0]->on_read (sub {
905 $arg{on_body}(delete $_[0]{rbuf}, \%hdr)
906 or $finish->(undef, 598 => "Request cancelled by on_body");
907 });
908 }
909 } else {
910 $_[0]->on_eof (undef);
911
912 if ($len) {
913 $_[0]->on_read (sub {
914 $finish->((substr delete $_[0]{rbuf}, 0, $len, ""), undef, undef, 1)
915 if $len <= length $_[0]{rbuf};
916 });
917 } else {
918 $_[0]->on_error (sub {
919 ($! == Errno::EPIPE || !$!)
920 ? $finish->(delete $_[0]{rbuf})
921 : $finish->(undef, $ae_error => $_[2]);
922 });
923 $_[0]->on_read (sub { });
924 }
925 }
926 }
927 };
928
929 $state{handle}->push_read (line => $qr_nlnl, $state{read_response});
930 };
931
932 # now handle proxy-CONNECT method 932 # now handle proxy-CONNECT method
933 if ($proxy && $uscheme eq "https") { 933 if ($proxy && $uscheme eq "https") {
934 # oh dear, we have to wrap it into a connect request 934 # oh dear, we have to wrap it into a connect request
935 935
936 # maybe re-use $uauthority with patched port? 936 # maybe re-use $uauthority with patched port?
954 954
955 my $tcp_connect = $arg{tcp_connect} 955 my $tcp_connect = $arg{tcp_connect}
956 || do { require AnyEvent::Socket; \&AnyEvent::Socket::tcp_connect }; 956 || do { require AnyEvent::Socket; \&AnyEvent::Socket::tcp_connect };
957 957
958 $state{connect_guard} = $tcp_connect->($rhost, $rport, $connect_cb, $arg{on_prepare} || sub { $timeout }); 958 $state{connect_guard} = $tcp_connect->($rhost, $rport, $connect_cb, $arg{on_prepare} || sub { $timeout });
959
960 }; 959 };
961 960
962 defined wantarray && AnyEvent::Util::guard { %state = () } 961 defined wantarray && AnyEvent::Util::guard { %state = () }
963} 962}
964 963

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines