Mime-Type configuration for Debian official Mirrors - best practices?

Javier Fernandez-Sanguino <[email protected]>
Newsgroups gmane.linux.debian.user.mirrors
Message-ID <CAB9B7Us9B_Lof=c_Hmp0DZ_CVSotxObG3_iU1w=LTBP_VNOKrg@mail.gmail.com>
Dear mirrors maintainers and colleagues,

Are there any best practices for defining the mime-type of Debian mirrors
when serving files over HTTP? I have been unable to find any guidance on
how to best configure this.

This week a user reached out to me, as maintainer of ftp.es.debian.org,
asking me why they had to download some files to read them, and could not
see in the web browser. This happened for some text files that lack an
extension such as the README or the ChangeLog. For these files the web
server listed the MIME-type as "application/octet-stream"

After reviewing the behaviour of other official mirrors and after fiddling
with our setup with lighttpd I ended up adding the attached file to
/usr/local/bin/ and changing in lighttpd's conf file, this:

include_shell "/usr/share/lighttpd/create-mime.assign.pl"

to

include_shell "/usr/local/bin/lighttpd-create-mimetypes.conf.pl"


The attached Perl script, derived from a script for lighttpd 2, sets the
mime types for some of the text filenames that are found in the archive.

I opted to do this instead of setting as the default mime-type to
"text/plain" to prevent binary files from being sent as text files.

If other mirror admins can suggest better or alternative approaches I would
be happy to hear about them.

Best regards

Javier
lighttpd-create-mimetypes.conf.pl (application/octet-stream, 5.6 KB)
#!/usr/bin/perl -w


# Based on create-mime.assign.pl in debian lighttpd (1.4.x) package
# Creates an example mimetypes.conf from /etc/mime.types
#
# URL: https://github.com/lighttpd/lighttpd2/blob/master/contrib/create-mimetypes.conf.pl

# Modified for lighttpd 1 by Javier Fernandez-Sanguino <[email protected]>
# Also added mime-types for Debian mirrors

use strict;

my $verbose = 0; # Set to 1 to use verbose mode

# text/* subtypes to serve as "text/...; charset=utf-8"
# text/html IS NOT INCLUDED: html has its own method for defining charset
#   (<meta>), but the standards specify that content-type in HTTP wins over
#   the setting in the html document.
my %text_utf8 = map { $_ => 1 } qw(
	css
	csv
	plain
	x-bibtex
	x-boo
	x-c++hdr
	x-c++src
	x-chdr
	x-csh
	x-csrc
	x-dsrc
	x-diff
	x-haskell
	x-java
	x-lilypond
	x-literate-haskell
	x-makefile
	x-moc
	x-pascal
	x-perl
	x-python
	x-scala
	x-sh
	x-tcl
	x-tex
);

# map extension to hash which maps types to the type they should be replaced with
my %manual_conflicts_resolve = (
	'.ra' => {
		'audio/x-pn-realaudio' => 'audio/x-realaudio',
	},
);

open MIMETYPES, "/etc/mime.types" or die "Can't open mime.types: $!";

my %extensions;
sub set {
	my ($extension, $mimetype) = @_;
	$extensions{$extension} = $mimetype;
}
sub add {
	my ($extension, $mimetype) = @_;
	my $have = $extensions{$extension};

	my $r = $manual_conflicts_resolve{$extension};
	# update @_ too for calls to set
	$_[1] = $mimetype = $r->{$mimetype} if $r && $r->{$mimetype};

	# mime.types can have same extension for different mime types
	if ($have) {
		# application/octet-stream means we couldn't resolve another conflict
		return if $have eq $mimetype || $have eq 'application/octet-stream';

		my ($have_type, $have_subtype) = split /\//, $have, 2;
		my ($type, $subtype) = split /\//, $mimetype, 2;

		my $have_x = ($have_type =~ /^x-/ || $have_subtype =~ /^x-/);
		my $x = ($type =~ /^x-/ || $subtype =~ /^x-/);

		# entries without x- prefix in type/subtype win:
		if ($have_x && !$x) {
			return set @_; # overwrite
		} elsif ($x && !$have_x) {
			return; # ignore
		}

		# text/ wins over application/ for same subtype
		if ($subtype eq $have_subtype) {
			if ($type eq "text" && $have_type eq "application") {
				return set @_; # overwrite
			} elsif ($have_type eq "text" && $type eq "application") {
				return; # ignore
			}
		}

		print STDERR "Duplicate mimetype: '${extension}' => '${mimetype}' (already have '${have}'), merging to 'application/octet-stream'\n" if $verbose;
		set ($extension, 'application/octet-stream');
	} else {
		set @_;
	}
}

sub print_type {
	my ($extension, $mimetype) = @_;
	if ($mimetype =~ /^text\/(.*)$/) {
		$mimetype .= "; charset=utf-8" if $text_utf8{$1};
	}

	print "\t\t\"${extension}\" => \"${mimetype}\",\n";
}

while (<MIMETYPES>) {
	chomp;
	s/\#.*//;
	next if /^\w*$/;
	if (/^([a-z0-9\/+-.]+)\s+((?:[a-z0-9.+-]+[ ]?)+)$/) {
		my $mimetype = $1;
		my @extensions = split / /, $2;

		foreach my $ext (@extensions) {
			add(".${ext}", $mimetype);
		}
	}
}

# missing in /etc/mime.types;
# from http://www.iana.org/assignments/media-types/media-types.xhtml
add(".dtd", "application/xml-dtd");


print <<EOF;
# created by /usr/local/bin/lighttpd2-create-mimetypes.conf.pl

mimetype.assign = (
# /etc/mime.types
#and from http://www.iana.org/assignments/media-types/media-types.xhtml

EOF

# sort "x-" and "vnd." prefixed names after everything else
sub mimecmpvalue {
	my ($mimetype) = @_;
	$mimetype =~ s/(^|\/)(x-|vnd\.)/~$1$2/g;
	return $mimetype;
}
for my $ext (sort { mimecmpvalue($extensions{$a}) cmp mimecmpvalue($extensions{$b}) || $a cmp $b } keys(%extensions)) {
	print_type($ext, $extensions{$ext});
}

# array instead of hash to keep order as given here
my @useful = (
	".tar.gz"  , "application/x-gtar-compressed",
	".gz"      , "application/x-gzip",
	".tbz"     , "application/x-gtar-compressed",
	".tar.bz2" , "application/x-gtar-compressed",
	".bz2"     , "application/x-bzip",
	".log"     , "text/plain",
	".conf"    , "text/plain",
	".spec"    , "text/plain",
	"README"   , "text/plain",
	"Makefile" , "text/x-makefile",
# Files found in Debian mirrors
# Text files
	"Release"  , "text/plain",
	"InRelease"  , "text/plain",
	"MANIFEST" , "text/plain",
	"MANIFEST.udebs" , "text/plain",
	"ChangeLog",	"text/plain",
	"README.CD-manufacture", "text/plain",
	"udeb.list" , "text/plain",
	"MD5SUMS"  , "text/plain",
	"SHA256SUMS", "text/plain",
	"Index"    , "text/plain",
	"config-debian"    , "text/plain",
	".list"    , "text/plain",
	".cfg"     , "text/plain",
	".lst"     , "text/plain",
	".mod"     , "text/plain",
# Signed files
	".gpg"     , "application/pgp-encrypted",
	".asc"     , "application/pgp-signature",
	".pgp"     , "application/pgp-encrypted",
	".key"     , "application/pgp-keys",
# Binary files, can be listed easily by running
#
# find . -type f \! -name "*.*" | xargs file |grep -v ASCII | \
#   perl -ne 'print # $1."\n" if /^\..*\/([a-z]+):\s+.*$/' | sort | uniq -c
# which, as of June 2014, currently results in 
# 42 kernel
# 60 linux
# 8 vmlinux
# 180 vmlinuz
	"kernel"    , "application/octec-stream",
	"linux"    , "application/octec-stream",
	"vmlinux"    , "application/octec-stream",
	"vmlinuz"    , "application/octec-stream",
);

print <<EOF;

# other useful mappings

EOF

while (my ($ext, $mimetype) = splice(@useful, 0, 2)) {
	print_type($ext, $mimetype) unless $extensions{$ext};
}

print <<EOF;

# custom - put your own entries here (overwriting mappings above)

)
EOF
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.