Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package perl-Furl for openSUSE:Factory 
checked in at 2026-11-10 16:30:04
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/perl-Furl (Old)
 and      /work/SRC/openSUSE:Factory/.perl-Furl.new.3759290 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "perl-Furl"

Tue Nov 10 16:30:04 2026 rev:5 rq:1396532 version:3.160.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/perl-Furl/perl-Furl.changes      2025-10-22 
12:18:33.421214844 +0200
+++ /work/SRC/openSUSE:Factory/.perl-Furl.new.3759290/perl-Furl.changes 
2026-11-10 16:30:22.247668414 +0100
@@ -1,0 +2,16 @@
+Tue Nov 10 13:31:22 UTC 2026 - Tina Müller <[email protected]>
+
+- updated to 3.160.0 (3.16)
+   see /usr/share/doc/packages/perl-Furl/Changes
+
+  3.16 2026-11-06T10:22:58Z
+      - [SECURITY] Fix memory exhaustion via unbounded decompression of 
gzip/deflate
+        responses, a.k.a. "zip bomb" (CVE-2026-65059) (#139, robrwo++)
+        bsc#1289200
+      - Add max_decompressed_size option to limit the size of a decoded 
response body.
+        By default the limit is 100 MiB for responses held in memory, and 
there is no
+        limit for responses streamed with write_file/write_code. Going over the
+        limit makes the request return an internal 500 response (with
+        X-Internal-Response) (#140)
+
+-------------------------------------------------------------------

Old:
----
  Furl-3.15.tar.gz

New:
----
  Furl-3.16.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ perl-Furl.spec ++++++
--- /var/tmp/diff_new_pack.zDGvg5/_old  2026-11-10 16:30:23.816734045 +0100
+++ /var/tmp/diff_new_pack.zDGvg5/_new  2026-11-10 16:30:23.818734128 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package perl-Furl
 #
-# Copyright (c) 2025 SUSE LLC and contributors
+# Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,14 +18,14 @@
 
 %define cpan_name Furl
 Name:           perl-Furl
-Version:        3.150.0
+Version:        3.160.0
 Release:        0
-# 3.15 -> normalize -> 3.150.0
-%define cpan_version 3.15
+# 3.16 -> normalize -> 3.160.0
+%define cpan_version 3.16
 License:        Artistic-1.0 OR GPL-1.0-or-later
 Summary:        Lightning-fast URL fetcher
 URL:            https://metacpan.org/release/%{cpan_name}
-Source0:        
https://cpan.metacpan.org/authors/id/S/SY/SYOHEX/%{cpan_name}-%{cpan_version}.tar.gz
+Source0:        
https://cpan.metacpan.org/authors/id/T/TO/TOKUHIROM/%{cpan_name}-%{cpan_version}.tar.gz
 Source1:        cpanspec.yml
 Source100:      README.md
 Patch0:         0001-Remove-use-of-Mozilla-CA.patch

++++++ Furl-3.15.tar.gz -> Furl-3.16.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Furl-3.15/Changes new/Furl-3.16/Changes
--- old/Furl-3.15/Changes       2025-09-25 02:25:58.000000000 +0200
+++ new/Furl-3.16/Changes       2026-11-06 11:22:58.000000000 +0100
@@ -1,5 +1,14 @@
 Revision history for Perl module Furl
 
+3.16 2026-11-06T10:22:58Z
+    - [SECURITY] Fix memory exhaustion via unbounded decompression of 
gzip/deflate
+      responses, a.k.a. "zip bomb" (CVE-2026-65059) (#139, robrwo++)
+    - Add max_decompressed_size option to limit the size of a decoded response 
body.
+      By default the limit is 100 MiB for responses held in memory, and there 
is no
+      limit for responses streamed with write_file/write_code. Going over the
+      limit makes the request return an internal 500 response (with
+      X-Internal-Response) (#140)
+
 3.15 2025-09-25T00:24:03Z
     - Fix the issue that when timeout happens, then request method returns 
success when
       there is no Content-Length headers(#137)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Furl-3.15/MANIFEST new/Furl-3.16/MANIFEST
--- old/Furl-3.15/MANIFEST      2025-09-25 02:25:58.000000000 +0200
+++ new/Furl-3.16/MANIFEST      2026-11-06 11:22:58.000000000 +0100
@@ -64,6 +64,7 @@
 t/300_high/05_suppress_dup_host_header.t
 t/300_high/06_keep_request.t
 t/300_high/07_cookie.t
+t/300_high/41_max_decompressed_size.t
 t/300_high/99_error.t
 t/400_components/001_response-coding/01-file.t
 t/400_components/001_response-coding/t-euc-jp.html
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Furl-3.15/META.json new/Furl-3.16/META.json
--- old/Furl-3.15/META.json     2025-09-25 02:25:58.000000000 +0200
+++ new/Furl-3.16/META.json     2026-11-06 11:22:58.000000000 +0100
@@ -4,7 +4,7 @@
       "Tokuhiro Matsuno <[email protected]>"
    ],
    "dynamic_config" : 0,
-   "generated_by" : "Minilla/v3.1.28",
+   "generated_by" : "Minilla/v3.3.2",
    "license" : [
       "perl_5"
    ],
@@ -110,14 +110,14 @@
    "provides" : {
       "Furl" : {
          "file" : "lib/Furl.pm",
-         "version" : "3.15"
+         "version" : "3.16"
       },
       "Furl::ConnectionCache" : {
          "file" : "lib/Furl/ConnectionCache.pm"
       },
       "Furl::HTTP" : {
          "file" : "lib/Furl/HTTP.pm",
-         "version" : "3.15"
+         "version" : "3.16"
       },
       "Furl::Headers" : {
          "file" : "lib/Furl/Headers.pm"
@@ -144,11 +144,13 @@
          "web" : "https://github.com/tokuhirom/Furl";
       }
    },
-   "version" : "3.15",
+   "version" : "3.16",
    "x_contributors" : [
       "Audrey Tang <[email protected]>",
       "Breno G. de Oliveira <[email protected]>",
+      "Claude <[email protected]>",
       "Debabrata Deka <[email protected]>",
+      "Ed Sabol <[email protected]>",
       "Fuji Goro <[email protected]>",
       "Fuji, Goro <[email protected]>",
       "Fuji, Goro <[email protected]>",
@@ -160,6 +162,7 @@
       "Keiji, Yoshimi <[email protected]>",
       "Masahiro Nagano <[email protected]>",
       "Neil Bowers <[email protected]>",
+      "Robert Rothenberg <[email protected]>",
       "Shohei YOSHIDA <[email protected]>",
       "Shoichi Kaji <[email protected]>",
       "Slaven Rezic <[email protected]>",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Furl-3.15/META.yml new/Furl-3.16/META.yml
--- old/Furl-3.15/META.yml      2025-09-25 02:25:58.000000000 +0200
+++ new/Furl-3.16/META.yml      2026-11-06 11:22:58.000000000 +0100
@@ -10,7 +10,7 @@
 configure_requires:
   Module::Build::Tiny: '0.035'
 dynamic_config: 0
-generated_by: 'Minilla/v3.1.28, CPAN::Meta::Converter version 2.150010'
+generated_by: 'Minilla/v3.3.2, CPAN::Meta::Converter version 2.150010'
 license: perl
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -29,12 +29,12 @@
 provides:
   Furl:
     file: lib/Furl.pm
-    version: '3.15'
+    version: '3.16'
   Furl::ConnectionCache:
     file: lib/Furl/ConnectionCache.pm
   Furl::HTTP:
     file: lib/Furl/HTTP.pm
-    version: '3.15'
+    version: '3.16'
   Furl::Headers:
     file: lib/Furl/Headers.pm
   Furl::Request:
@@ -62,11 +62,13 @@
   bugtracker: https://github.com/tokuhirom/Furl/issues
   homepage: https://github.com/tokuhirom/Furl
   repository: https://github.com/tokuhirom/Furl.git
-version: '3.15'
+version: '3.16'
 x_contributors:
   - 'Audrey Tang <[email protected]>'
   - 'Breno G. de Oliveira <[email protected]>'
+  - 'Claude <[email protected]>'
   - 'Debabrata Deka <[email protected]>'
+  - 'Ed Sabol <[email protected]>'
   - 'Fuji Goro <[email protected]>'
   - 'Fuji, Goro <[email protected]>'
   - 'Fuji, Goro <[email protected]>'
@@ -78,6 +80,7 @@
   - 'Keiji, Yoshimi <[email protected]>'
   - 'Masahiro Nagano <[email protected]>'
   - 'Neil Bowers <[email protected]>'
+  - 'Robert Rothenberg <[email protected]>'
   - 'Shohei YOSHIDA <[email protected]>'
   - 'Shoichi Kaji <[email protected]>'
   - 'Slaven Rezic <[email protected]>'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Furl-3.15/README.md new/Furl-3.16/README.md
--- old/Furl-3.15/README.md     2025-09-25 02:25:58.000000000 +0200
+++ new/Furl-3.16/README.md     2026-11-06 11:22:58.000000000 +0100
@@ -46,6 +46,10 @@
 - agent :Str = "Furl/$VERSION"
 - timeout :Int = 10
 - max\_redirects :Int = 7
+- max\_decompressed\_size :Int
+
+    Maximum size of a gzip/deflate-decoded response body; `0` disables the 
limit. Defaults to 100 MiB for responses held in memory, and to no limit for 
responses streamed with `write_file` or `write_code`. See ["new" in 
Furl::HTTP](https://metacpan.org/pod/Furl%3A%3AHTTP#new).
+
 - capture\_request :Bool = false
 
     If this parameter is true, 
[Furl::HTTP](https://metacpan.org/pod/Furl%3A%3AHTTP) captures raw request 
string.
@@ -159,7 +163,7 @@
 
 # FAQ
 
-- Does Furl depends on XS modules?
+- Does Furl depend on XS modules?
 
     No. Although some optional features require XS modules, basic features are
     available without XS modules.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Furl-3.15/lib/Furl/HTTP.pm 
new/Furl-3.16/lib/Furl/HTTP.pm
--- old/Furl-3.15/lib/Furl/HTTP.pm      2025-09-25 02:25:58.000000000 +0200
+++ new/Furl-3.16/lib/Furl/HTTP.pm      2026-11-06 11:22:58.000000000 +0100
@@ -4,7 +4,7 @@
 use base qw/Exporter/;
 use 5.008001;
 
-our $VERSION = '3.15';
+our $VERSION = '3.16';
 
 use Carp ();
 use Furl::ConnectionCache;
@@ -36,6 +36,9 @@
 my $HTTP_TOKEN         = '[^\x00-\x31\x7F]+';
 my $HTTP_QUOTED_STRING = q{"([^"]+|\\.)*"};
 
+# Default for max_decompressed_size when the body is held in memory.
+our $DEFAULT_MAX_DECOMPRESSED_SIZE = 100 * 1024 * 1024;
+
 sub new {
     my $class = shift;
     my %args = @_ == 1 ? %{$_[0]} : @_;
@@ -534,7 +537,16 @@
     if (exists $COMPRESSED{ $special_headers->{'content-encoding'} }) {
         _requires('Furl/ZlibStream.pm', 'Content-Encoding', 
'Compress::Raw::Zlib');
 
-        $res_content = Furl::ZlibStream->new($res_content);
+        # An explicit limit (per request, or given to new) always applies.
+        # Otherwise the default limit applies only when the body is held in
+        # memory; a body streamed with write_file/write_code does not grow
+        # in memory, so it is left unlimited as before.
+        my $max_decompressed_size
+            = defined($args{max_decompressed_size})  ? 
$args{max_decompressed_size}
+            : defined($self->{max_decompressed_size}) ? 
$self->{max_decompressed_size}
+            : ref($res_content)                       ? 0
+            :                                           
$DEFAULT_MAX_DECOMPRESSED_SIZE;
+        $res_content = Furl::ZlibStream->new($res_content, 
$max_decompressed_size);
     }
 
     my $chunked        = ($special_headers->{'transfer-encoding'} eq 
'chunked');
@@ -548,20 +560,31 @@
             || $res_status == 204
             || $res_status == 304) {
         my @err;
-        if ( $chunked ) {
-            @err = $self->_read_body_chunked($sock,
-                \$res_content, $rest_header, $timeout_at);
-        } else {
-            $res_content .= $rest_header;
-            if (ref $res_content || !defined($content_length)) {
-                @err = $self->_read_body_normal($sock,
-                    \$res_content, length($rest_header),
-                    $content_length, $timeout_at);
+        my $ok = eval {
+            if ( $chunked ) {
+                @err = $self->_read_body_chunked($sock,
+                    \$res_content, $rest_header, $timeout_at);
             } else {
-                @err = $self->_read_body_normal_to_string_buffer($sock,
-                    \$res_content, length($rest_header),
-                    $content_length, $timeout_at);
+                $res_content .= $rest_header;
+                if (ref $res_content || !defined($content_length)) {
+                    @err = $self->_read_body_normal($sock,
+                        \$res_content, length($rest_header),
+                        $content_length, $timeout_at);
+                } else {
+                    @err = $self->_read_body_normal_to_string_buffer($sock,
+                        \$res_content, length($rest_header),
+                        $content_length, $timeout_at);
+                }
             }
+            1;
+        };
+        unless ($ok) {
+            my $e = $@;
+            # The rest of the body is left unread, so the connection is
+            # dropped rather than returned to the pool.
+            return $self->_r500("$e")
+                if ref($e) && ref($e) eq 'Furl::ZlibStream::LimitExceeded';
+            die $e;
         }
         if(@err) {
             return @err;
@@ -1060,7 +1083,7 @@
 1;
 __END__
 
-=for stopwords sockaddr
+=for stopwords sockaddr MiB
 
 =encoding utf8
 
@@ -1121,6 +1144,12 @@
 
 =item max_redirects :Int = 7
 
+=item max_decompressed_size :Int
+
+Maximum size, in bytes, of a response body after B<Content-Encoding> (gzip, 
deflate) decoding, so that a small response from a malicious or compromised 
server cannot expand without bound in memory (a "decompression bomb"). When a 
body inflates past this limit, reading stops, the connection is closed and C<< 
$furl->request >> returns an internal C<500> response (with the 
C<X-Internal-Response> header) whose message mentions C<max_decompressed_size>. 
Set it to C<0> to disable the limit.
+
+If you do not set it, a limit of 104857600 bytes (100 MiB) applies to 
responses held in memory, and no limit applies to responses streamed with 
C<write_file> or C<write_code>, as their bodies do not grow in memory. If you 
set it, the limit applies to both.
+
 =item proxy :Str
 
 =item no_proxy :Str
@@ -1233,6 +1262,10 @@
 
 It's like a C<:content_cb> in L<LWP::UserAgent>.
 
+=item max_decompressed_size :Int
+
+Overrides the instance-wide C<max_decompressed_size> given to C<new> for this 
request.
+
 =back
 
 The C<request()> method assumes the first argument to be an instance
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Furl-3.15/lib/Furl/ZlibStream.pm 
new/Furl-3.16/lib/Furl/ZlibStream.pm
--- old/Furl-3.15/lib/Furl/ZlibStream.pm        2025-09-25 02:25:58.000000000 
+0200
+++ new/Furl-3.16/lib/Furl/ZlibStream.pm        2026-11-06 11:22:58.000000000 
+0100
@@ -6,28 +6,68 @@
 use Carp ();
 use Compress::Raw::Zlib qw(Z_OK Z_STREAM_END);
 
+# Compressed input is inflated in slices of this size. DEFLATE expands at
+# most ~1032:1, so one inflate() call yields at most a few MB, and the
+# size limit is checked before a single call can produce an unbounded
+# amount of output (a chunked response can hand us one huge chunk).
+my $SLICE = 4096;
+
 sub new {
-    my ( $class, $buffer ) = @_;
+    my ( $class, $buffer, $max_size ) = @_;
 
     my ( $zlib, $status ) = Compress::Raw::Zlib::Inflate->new(
         -WindowBits => Compress::Raw::Zlib::WANT_GZIP_OR_ZLIB(), );
     $status == Z_OK
         or Carp::croak("Cannot initialize zlib: $status");
 
-    bless { buffer => $buffer, zlib => $zlib }, $class;
+    bless {
+        buffer   => $buffer,
+        zlib     => $zlib,
+        max_size => $max_size,
+        size     => 0,
+    }, $class;
 }
 
 sub append {
     my ( $self, $partial ) = @_;
 
-    my $status = $self->{zlib}->inflate( $partial, \my $deflated );
-    ($status == Z_OK or $status == Z_STREAM_END)
-        or Carp::croak("Uncompress error: $status");
-    $self->{buffer} .= $deflated;
+    for ( my $offset = 0; $offset < length $partial; $offset += $SLICE ) {
+        # inflate() consumes its input, so hand it a copy of the slice.
+        my $input = substr( $partial, $offset, $SLICE );
+        my $status = $self->{zlib}->inflate( $input, \my $deflated );
+        ($status == Z_OK or $status == Z_STREAM_END)
+            or Carp::croak("Uncompress error: $status");
+
+        $self->{size} += length $deflated;
+        if ( $self->{max_size} && $self->{size} > $self->{max_size} ) {
+            # Thrown as an object so that Furl::HTTP can tell it apart from
+            # other exceptions (e.g. one thrown by a write_code callback)
+            # and turn it into an internal 500 response.
+            die Furl::ZlibStream::LimitExceeded->new( $self->{max_size} );
+        }
+        $self->{buffer} .= $deflated;
+
+        last if $status == Z_STREAM_END;
+    }
 
     return $self;
 }
 
 sub get_response_string { ref $_[0]->{buffer} ? undef : $_[0]->{buffer} }
 
+package # hide from PAUSE
+    Furl::ZlibStream::LimitExceeded;
+use overload '""' => 'message', fallback => 1;
+
+sub new {
+    my ( $class, $max_size ) = @_;
+    bless { max_size => $max_size }, $class;
+}
+
+sub message {
+    my $self = shift;
+    "Decompressed response body exceeds max_decompressed_size"
+        . " ($self->{max_size} bytes)";
+}
+
 1;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Furl-3.15/lib/Furl.pm new/Furl-3.16/lib/Furl.pm
--- old/Furl-3.15/lib/Furl.pm   2025-09-25 02:25:58.000000000 +0200
+++ new/Furl-3.16/lib/Furl.pm   2026-11-06 11:22:58.000000000 +0100
@@ -6,7 +6,7 @@
 use Furl::Request;
 use Furl::Response;
 use Carp ();
-our $VERSION = '3.15';
+our $VERSION = '3.16';
 
 use 5.008001;
 
@@ -125,6 +125,8 @@
 
 =encoding utf8
 
+=for stopwords MiB
+
 =head1 NAME
 
 Furl - Lightning-fast URL fetcher
@@ -178,6 +180,10 @@
 
 =item max_redirects :Int = 7
 
+=item max_decompressed_size :Int
+
+Maximum size of a gzip/deflate-decoded response body; C<0> disables the limit. 
Defaults to 100 MiB for responses held in memory, and to no limit for responses 
streamed with C<write_file> or C<write_code>. See L<Furl::HTTP/new>.
+
 =item capture_request :Bool = false
 
 If this parameter is true, L<Furl::HTTP> captures raw request string.
@@ -313,7 +319,7 @@
 
 =over 4
 
-=item Does Furl depends on XS modules?
+=item Does Furl depend on XS modules?
 
 No. Although some optional features require XS modules, basic features are
 available without XS modules.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Furl-3.15/t/300_high/41_max_decompressed_size.t 
new/Furl-3.16/t/300_high/41_max_decompressed_size.t
--- old/Furl-3.15/t/300_high/41_max_decompressed_size.t 1970-01-01 
01:00:00.000000000 +0100
+++ new/Furl-3.16/t/300_high/41_max_decompressed_size.t 2026-11-06 
11:22:58.000000000 +0100
@@ -0,0 +1,169 @@
+use strict;
+use warnings;
+use Test::Requires 'Compress::Raw::Zlib';
+use Furl;
+use Furl::HTTP;
+use IO::Socket::INET;
+use Test::TCP;
+use Test::More;
+
+# A small gzip/deflate response must not inflate without bound in memory.
+# Once the decoded body passes max_decompressed_size, Furl stops reading and
+# returns an internal 500 response.
+
+use Compress::Raw::Zlib qw(WANT_GZIP MAX_WBITS Z_OK Z_FINISH);
+
+my $MiB = 1024 * 1024;
+
+sub compress_zeros {
+    my ($bytes, $window_bits) = @_;
+    my ($d, $status) = Compress::Raw::Zlib::Deflate->new(
+        -WindowBits => $window_bits, -Level => 9);
+    $status == Z_OK or die "deflate init: $status";
+    my $block = "\0" x $MiB;
+    my $out = '';
+    while ($bytes > 0) {
+        my $in = $bytes >= $MiB ? $block : "\0" x $bytes;
+        $bytes -= length $in;
+        $d->deflate($in, my $o);
+        $out .= $o;
+    }
+    $d->flush(my $o, Z_FINISH);
+    return $out . $o;
+}
+
+my %body = (
+    '/small' => do {
+        my ($d) = Compress::Raw::Zlib::Deflate->new(-WindowBits => WANT_GZIP);
+        $d->deflate('hello', my $o);
+        $d->flush(my $t, Z_FINISH);
+        $o . $t;
+    },
+    '/big'         => compress_zeros(2 * $MiB, WANT_GZIP),
+    '/big-deflate' => compress_zeros(2 * $MiB, MAX_WBITS),
+    '/big-stream'  => compress_zeros(101 * $MiB, WANT_GZIP),
+);
+# Inflating 100 MiB in memory is too heavy for every smoker.
+$body{'/over-default'} = $body{'/big-stream'} if $ENV{AUTHOR_TESTING};
+# Text that compresses to many KB, so it is inflated in many pieces.
+my $TEXT = join '', map { "line $_ of the response body\n" } 1 .. 30000;
+$body{'/text'} = do {
+    my ($d) = Compress::Raw::Zlib::Deflate->new(-WindowBits => WANT_GZIP);
+    $d->deflate($TEXT, my $o);
+    $d->flush(my $t, Z_FINISH);
+    $o . $t;
+};
+$body{'/text-chunked'} = $body{'/text'};
+$body{'/big-chunked'} = $body{'/big'};
+
+test_tcp(
+    client => sub {
+        my $port = shift;
+        my $base = "http://127.0.0.1:$port";;
+
+        my $furl = Furl::HTTP->new(max_decompressed_size => $MiB);
+
+        my (undef, $code, undef, undef, $content)
+            = $furl->request(url => "$base/small");
+        is $code, 200, 'small gzip response under the limit';
+        is $content, 'hello', '... decoded';
+
+        for my $path (qw(/text /text-chunked)) {
+            (undef, $code, undef, undef, $content)
+                = $furl->request(url => "$base$path");
+            is $code, 200, "$path: larger body under the limit";
+            ok $content eq $TEXT, '... decoded intact';
+        }
+
+        for my $path (qw(/big /big-deflate /big-chunked)) {
+            my (undef, $code, $msg, $headers, $content)
+                = $furl->request(url => "$base$path");
+            is $code, 500, "$path: 2 MiB decoded body over a 1 MiB limit is a 
500";
+            like $msg, qr/max_decompressed_size/, '... with the limit error';
+            is {@$headers}->{'X-Internal-Response'}, 1, '... as an internal 
response';
+        }
+
+        my $streamed = 0;
+        (undef, $code, my $msg) = $furl->request(
+            url        => "$base/big",
+            write_code => sub { $streamed += length $_[3] },
+        );
+        is $code, 500, 'an explicit limit applies with write_code';
+        like $msg, qr/max_decompressed_size/, '... with the limit error';
+        cmp_ok $streamed, '<=', $MiB, '... and stops before the limit';
+
+        # Other exceptions still propagate.
+        my $ok = eval {
+            $furl->request(
+                url        => "$base/small",
+                write_code => sub { die "callback died\n" },
+            );
+            1;
+        };
+        ok !$ok, 'an exception from write_code still propagates';
+        is $@, "callback died\n", '... unchanged';
+
+        (undef, $code, undef, undef, $content) = $furl->request(
+            url                   => "$base/big",
+            max_decompressed_size => 0,
+        );
+        is $code, 200, 'per-request max_decompressed_size => 0 disables the 
limit';
+        is length($content), 2 * $MiB, '... full body decoded';
+
+        my $res = Furl->new->get("$base/big");
+        is $res->code, 200, 'default limit allows an ordinary 2 MiB body';
+        is length($res->content), 2 * $MiB, '... full body decoded';
+
+        # No default limit when streaming: the body does not grow in memory.
+        $streamed = 0;
+        $res = Furl->new->request(
+            url        => "$base/big-stream",
+            write_code => sub { $streamed += length $_[3] },
+        );
+        is $res->code, 200, 'default limit does not apply with write_code';
+        is $streamed, 101 * $MiB, '... full body streamed';
+
+        SKIP: {
+            skip 'set AUTHOR_TESTING to inflate 100 MiB in memory', 2
+                unless $ENV{AUTHOR_TESTING};
+            $res = Furl->new->get("$base/over-default");
+            is $res->code, 500, 'default limit (100 MiB) stops a 101 MiB 
decoded body';
+            like $res->message, qr/max_decompressed_size/, '... with the limit 
error';
+        }
+
+        done_testing;
+    },
+    server => sub {
+        my $port = shift;
+        local $SIG{PIPE} = 'IGNORE';
+        my $sock = IO::Socket::INET->new(
+            LocalAddr => '127.0.0.1',
+            LocalPort => $port,
+            Listen    => 5,
+            ReuseAddr => 1,
+        ) or die "listen: $!";
+        while (my $c = $sock->accept) {
+            my $req = <$c>;
+            next unless defined $req; # Test::TCP probing the port
+            while (defined(my $line = <$c>)) { last if $line eq "\015\012" }
+            my ($path) = $req =~ m{^\S+ (\S+)};
+            my $payload = $body{$path};
+            my $encoding = $path eq '/big-deflate' ? 'deflate' : 'gzip';
+            if ($path =~ /-chunked\z/) {
+                print {$c} "HTTP/1.1 200 OK\015\012",
+                    "Content-Encoding: $encoding\015\012",
+                    "Transfer-Encoding: chunked\015\012",
+                    "Connection: close\015\012\015\012",
+                    sprintf("%x\015\012", length $payload), $payload,
+                    "\015\012", "0\015\012\015\012";
+            }
+            else {
+                print {$c} "HTTP/1.1 200 OK\015\012",
+                    "Content-Encoding: $encoding\015\012",
+                    "Content-Length: ", length($payload), "\015\012",
+                    "Connection: close\015\012\015\012", $payload;
+            }
+            close $c;
+        }
+    },
+);

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.zDGvg5/_old  2026-11-10 16:30:24.158748350 +0100
+++ /var/tmp/diff_new_pack.zDGvg5/_new  2026-11-10 16:30:24.172748936 +0100
@@ -1,6 +1,6 @@
-mtime: 1759771260
-commit: 8f10239077d874d86e0a8e48a3c29b09c23f70c3d957ee2a4eb01c3dde3541f7
-url: https://src.opensuse.org/perl/perl-Furl.git
-revision: 8f10239077d874d86e0a8e48a3c29b09c23f70c3d957ee2a4eb01c3dde3541f7
+mtime: 1794319226
+commit: e3457169ae6f9f88e54c6988d6f7645fb6035d199ce007427ea3fcc1306c30b6
+url: https://src.opensuse.org/perl/perl-Furl
+revision: e3457169ae6f9f88e54c6988d6f7645fb6035d199ce007427ea3fcc1306c30b6
 projectscmsync: https://src.opensuse.org/perl/_ObsPrj
 

++++++ build.specials.obscpio ++++++

++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore      1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore      2026-11-10 15:00:26.000000000 +0100
@@ -0,0 +1 @@
+.osc

Reply via email to