On Thu, Mar 27, 2014 at 09:20:20PM +0100, Uwe Storbeck wrote:
> Hi Mark,
> 
> sorry for the delay.
> 
> On Mar 08, Mark Hindley wrote:
> > On Fri, Mar 07, 2014 at 05:17:30PM +0100, Uwe Storbeck wrote:
> > > So we have 2 different problems, apt-get/aptitude on stable
> > > and apt-get on testing/unstable throw the HTTP headers into
> > > the changelog output when using apt-cacher and aptitude on
> > > testing/unstable fails to get the changelog because it uses a
> > > different URL than apt-get to fetch it?
> > 
> > Could you test these 2 patches and see if they help?
> 
> I've applied both patches and the patch from your 2nd mail to
> apt-cacher 1.7.6 from stable.
> 
> Both apt-get and aptitude work as expected now, when I switch off
> the use of an external proxy. It looks like the primary issue of
> this bug is fixed by your patches, aptitude changelog does not
> fail anymore.

Good!

> But when I use squid as an external proxy (use_proxy = 1)
> apt-get from stable (version 0.9.7.9+deb7) and testing (version
> 0.9.15.5+b1) and aptitude from stable (version 0.6.8.2-1), but
> not aptitude from testing (version 0.6.10-1), still throw HTTP
> headers into the changelog output when apt-cacher has to
> retrieve the changelog file from the debian server (see attached
> apt-cacher log). The output in apt-get or aptitude looks like
> this:
> 
>   HTTP/1.0 200 Connection established
>   Connection: Keep-Alive
>   Date: Thu, 27 Mar 2014 19:56:32 GMT
>   Via: 1.1 c1:3142 (apt-cacher/1.7.6)
>   Accept-Ranges: bytes
>   Content-Length: 12381
>   Keep-Alive: timeout=15, max=100
>   X-AptCacher-URL: 
> http://packages.debian.org/changelogs/pool/main/e/ed/ed_1.10-2/changelog

OK, could you try this patch please (on top of the others). Although I 
cannot reproduce this behavious (I think I have an upstream transparent 
proxy that is filtering all the redirects), I think I understand what 
was wrong and hope this addresses it. Although the patch looks big, it 
is mostly a refactoring of the logic. I think it should reliably read 
all the headers before going on to the body.

Mark


commit a6136d989260ccae4df148dd08d14af483d7b1e3
Author: Mark Hindley <m...@hindley.org.uk>
Date:   Mon Mar 31 08:16:27 2014 +0100

    Really deal properly with multiple libcurl headers on redirects. Read all 
the
    headers, don't assume that 200 is the final one as we can get this as part 
of a
    HTTPS proxy CONNECT during a redirect sequence.

diff --git a/apt-cacher b/apt-cacher
index f4b58eb..f306cb3 100755
--- a/apt-cacher
+++ b/apt-cacher
@@ -1623,18 +1623,22 @@ sub libcurl {
                <$libcurl>
            }) {
                # $cfg->{debug} && debug_message('libcurl returned ' . 
length($_) . ' bytes');
+
+               # Handle libcurl status
                if (s/APT-CACHER_LIBCURL_STATUS=(\d+)\n$//) { # Match and 
remove, including newline
                    $curl_status = $1;
                    $curl_errbuf = WWW::Curl::Easy->new->strerror($curl_status);
                    $cfg->{debug} && debug_message("Found EOF marker and status 
$curl_status ($curl_errbuf)");
-                   last if $curl_status; # Bail out if we get a curl error
+                   last if $curl_status; # Bail out if we get a libcurl error
                    # Otherwise go on to parse $_ as it will contain the file 
tail for binary files
                }
+
+               # Handle headers
                if (!$response || m#^HTTP/1\.[01]\s+\d{3}\s+#) {
                    info_message("Got another status line. Redirected?: $_") if 
$response;
                    $response=HTTP::Response->parse($_);
                    if ($response->code) {
-                       $cfg->{debug} && debug_message('libcurl reading of 
headers complete: ' . $response->code);
+                       $cfg->{debug} && debug_message('Successfully parsed 
header: ' . $response->code);
                        chomp_message($response);
                        # Handle chunked
                        if ($response->header('Transfer-Encoding') && lc 
$response->header('Transfer-Encoding') eq 'chunked') {
@@ -1644,105 +1648,109 @@ sub libcurl {
                            $response->remove_header('Transfer-Encoding');
                            $response->remove_header('Content-Length');
                        }
-                                       
-                       if ($curl_request->method eq 'GET' && 
$response->is_success) {
-
-                           # Check space
-                           my $statfs;
-                           if (defined($statfs = df($cfg->{'cache_dir'}, 1)) &&
-                               $response->header('Content-Length') &&
-                               $response->header('Content-Length') >=  
$statfs->{bavail} ||
-                               $cfg->{_disk_usage_limit} &&
-                               $statfs->{used} + 
$response->header('Content-Length') >= $cfg->{_disk_usage_limit}) {
-                               info_message('ALARM: Insuffient space for 
Content-Length: '.
-                                            
$response->header('Content-Length').
-                                            ' in cache_dir with ' . 
$statfs->{bavail} . ' available space');
-                               $response=HTTP::Response->new(503, 'apt-cacher: 
Cache Full');
-                               $response->protocol('HTTP/1.1');
+                       next;
+                   }
+                   else {
+                       info_message("Warning: failed to parse headers: $_");
+                       $response=HTTP::Response->new(502, 'apt-cacher: failed 
to parse headers');
+                       $response->protocol('HTTP/1.1');
+                       last;
+                   }
+               }
+
+               # Fork fetcher for successful GET requests, if not already done
+               if ($curl_request->method eq 'GET' && $response->is_success && 
!defined($fpid)) {
+                   # Check space
+                   my $statfs;
+                   if (defined($statfs = df($cfg->{'cache_dir'}, 1)) &&
+                       $response->header('Content-Length') &&
+                       $response->header('Content-Length') >=  
$statfs->{bavail} ||
+                       $cfg->{_disk_usage_limit} &&
+                       $statfs->{used} + $response->header('Content-Length') 
>= $cfg->{_disk_usage_limit}) {
+                       info_message('ALARM: Insuffient space for 
Content-Length: '.
+                                    $response->header('Content-Length').
+                                    ' in cache_dir with ' . $statfs->{bavail} 
. ' available space');
+                       $response=HTTP::Response->new(503, 'apt-cacher: Cache 
Full');
+                       $response->protocol('HTTP/1.1');
+                   }
+                   else {
+                       # Take a lock on the target file and truncate
+                       _flock($cache->{content}, LOCK_EX) || die 'Unable to 
lock the target file';
+                       $cache->{content}->truncate(0) || die "Truncate failed: 
$!";
+                       seek($cache->{content},0,0) || die "Seek failed: $!";;
+
+                       defined($fpid = fork()) || die "Fork fetcher failed: 
$!";                               
+                       if  ($fpid) {
+                           # Parent
+                           $cfg->{debug} && debug_message("Forked fetcher 
$fpid");
+
+                           # Reopen content filedescriptor
+                           # separately. Relying on on dup(2) at fork
+                           # leaves a shared seek pointer.
+                           if (sysopen my $reopen,  
fd_path($cache->{content}), O_RDWR) {
+                               $cache->{content} = $reopen;
                            }
                            else {
-                               # Take a lock on the target file and truncate
-                               _flock($cache->{content}, LOCK_EX) || die 
'Unable to lock the target file';
-                               $cache->{content}->truncate(0) || die "Truncate 
failed: $!";
-                               seek($cache->{content},0,0) || die "Seek 
failed: $!";;
-
-                               defined($fpid = fork()) || die "Fork fetcher 
failed: $!";                               
-                               if  ($fpid) {
-                                   # Parent
-                                   $cfg->{debug} && debug_message("Forked 
fetcher $fpid");
-
-                                   # Reopen content filedescriptor
-                                   # separately. Relying on on dup(2) at fork
-                                   # leaves a shared seek pointer.
-                                   if (sysopen my $reopen,  
fd_path($cache->{content}), O_RDWR) {
-                                       $cache->{content} = $reopen;
-                                   }
-                                   else {
-                                        die "Failed to reopen content: $!";
-                                   }
-
-                                   if ($curl_request->method eq 'GET') {
-                                       if (!defined $response->content_length) 
{
-                                           # HTTP/1.0 or HTTP/1.1 chunked 
server upstream
-                                           # Wait for fetcher
-                                           $cfg->{debug} && debug_message('No 
Content-Length received for '. $curl_request->uri . '. You may get better 
performance using a different upstream server.');
-                                           _flock($cache->{content}, LOCK_SH); 
# Wait for the fetcher to release lock
-                                           $response->content_length(-s 
$cache->{content});
-                                       }
-
-                                       if($cfg->{checksum}) {
-                                           if (!is_file_type('installer', 
$request->uri->path)
-                                               && 
!is_file_type('skip_checksum', $request->uri->path)) {
-                                               # check for file corruption
-                                               _flock($cache->{content}, 
LOCK_SH); # Wait for the fetcher to release lock
-                                               my $cached_file = 
filename_fh($cache->{content});
-                                               $cfg->{debug} && 
debug_message("Validating checksum for $cached_file");
-                                               my $refreshed_index;
-                                               while 
(!check_sum($cache->{name}, $cache->{content})) {
-                                                   if (is_file_type('index', 
$request->uri->path) && !$refreshed_index) {
-                                                       # If an index file, 
refresh the Release file to update
-                                                       # the checksum database 
and retry
-                                                       info_message("Checksum 
mismatch on fetched $cached_file. Refreshing Release file");
-                                                       
refresh_release($request->uri);
-                                                       $refreshed_index = 1;
-                                                       $cfg->{debug} && 
debug_message("Revalidating checksum for $cached_file");
-                                                       next;
-                                                   }
-                                                   info_message("ALARM! 
checksum mismatch on $cached_file");
-                                                   kill 15, $fpid; # Kill the 
fetcher as it isn't doing anything useful!
-                                                   _flock($cache->{content}, 
LOCK_UN); # Not interested in this any more but leave filesystem unlink to 
fetch_store()
-                                                   $response = 
HTTP::Response->new(502, 'Data corruption');
-                                                   
$response->protocol('HTTP/1.1');
-                                                   last;
-                                               }
-                                           }
+                               die "Failed to reopen content: $!";
+                           }
+                               
+                           if ($curl_request->method eq 'GET') {
+                               if (!defined $response->content_length) {
+                                   # HTTP/1.0 or HTTP/1.1 chunked server 
upstream
+                                   # Wait for fetcher
+                                   $cfg->{debug} && debug_message('No 
Content-Length received for '. $curl_request->uri . '. You may get better 
performance using a different upstream server.');
+                                   _flock($cache->{content}, LOCK_SH); # Wait 
for the fetcher to release lock
+                                   $response->content_length(-s 
$cache->{content});
+                               }
 
-                                           # For internal requests wait for 
the fetcher to finish completely
-                                           # so that import_sums() has run
-                                           if ($cache->{internal}) {
-                                               debug_message("Waiting for 
$fpid");
-                                               waitpid($fpid,0);
+                               if($cfg->{checksum}) {
+                                   if (!is_file_type('installer', 
$request->uri->path)
+                                       && !is_file_type('skip_checksum', 
$request->uri->path)) {
+                                       # check for file corruption
+                                       _flock($cache->{content}, LOCK_SH); # 
Wait for the fetcher to release lock
+                                       my $cached_file = 
filename_fh($cache->{content});
+                                       $cfg->{debug} && 
debug_message("Validating checksum for $cached_file");
+                                       my $refreshed_index;
+                                       while (!check_sum($cache->{name}, 
$cache->{content})) {
+                                           if (is_file_type('index', 
$request->uri->path) && !$refreshed_index) {
+                                               # If an index file, refresh the 
Release file to update
+                                               # the checksum database and 
retry
+                                               info_message("Checksum mismatch 
on fetched $cached_file. Refreshing Release file");
+                                               refresh_release($request->uri);
+                                               $refreshed_index = 1;
+                                               $cfg->{debug} && 
debug_message("Revalidating checksum for $cached_file");
+                                               next;
                                            }
+                                           info_message("ALARM! checksum 
mismatch on $cached_file");
+                                           kill 15, $fpid; # Kill the fetcher 
as it isn't doing anything useful!
+                                           _flock($cache->{content}, LOCK_UN); 
# Not interested in this any more but leave filesystem unlink to fetch_store()
+                                           $response = 
HTTP::Response->new(502, 'Data corruption');
+                                           $response->protocol('HTTP/1.1');
+                                           last;
                                        }
                                    }
-                                   last; # <$libcurl>
-                               }
-                               else {
-                                   # Child continues to fetch
-                                   setpgrp(0,0); # New process group
-                                   $0="$0 [${\$request->uri}]"; # Visible to 
ps and friends, not local
-                                   $cache->{content}->autoflush;
+
+                                   # For internal requests wait for the 
fetcher to finish completely
+                                   # so that import_sums() has run
+                                   if ($cache->{internal}) {
+                                       debug_message("Waiting for $fpid");
+                                       waitpid($fpid,0);
+                                   }
                                }
                            }
+                           last; # <$libcurl>
+                       }
+                       else {
+                           # Child continues to fetch
+                           setpgrp(0,0); # New process group
+                           $0="$0 [${\$request->uri}]"; # Visible to ps and 
friends, not local
+                           $cache->{content}->autoflush;
                        }
-                   }
-                   else {
-                       info_message("Warning: failed to parse headers: $_");
-                       $response=HTTP::Response->new(502, 'apt-cacher: failed 
to parse headers');
-                       $response->protocol('HTTP/1.1');
                    }
                }
-               elsif ($curl_request->method eq 'POST') {
+
+               # Handle body
+               if ($curl_request->method eq 'POST') {
                    $response->add_content($_);
                }
                elsif ($cache->{content}) {
@@ -1753,6 +1761,7 @@ sub libcurl {
                    }
                }
            }
+
            if (!$response) {
                if (!defined $curl_status) {
                    $curl_status=1;


-- 
To UNSUBSCRIBE, email to debian-bugs-dist-requ...@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact listmas...@lists.debian.org

Reply via email to