GPXファイルのMIMEタイプ検出サポートを追加
GPS Exchange Format(GPX)ファイルを application/xml ではなく application/gpx+xml として正しく識別できるようになりました。拡張子とファイル内容の両面でのマッチングにより、コンテンツタイプバリデーションが期待通りに動作します。
背景
GPXファイルはXMLベースのフォーマットであるため、Marcelは従来これらを汎用の application/xml として識別していました。GPXファイルをアップロードするアプリケーションで application/gpx+xml を期待するバリデーションを実装していた場合、汎用MIMEタイプが返ることでバリデーションが失敗するという問題が生じていました。
GPXは .gpx という明確な拡張子と、ファイル先頭付近に <gpx というルート要素を持つ構造的な特徴を備えており、専用MIMEタイプとしての検出が技術的に成立します。
技術的な変更
3つのファイルにまたがる変更により、拡張子・MIMEタイプマッピング・マジックバイト検出・サブクラス関係の4つの側面でGPXサポートが追加されました。
data/custom.xml にMIMEタイプ定義が追加されました。<sub-class-of> により application/xml のサブタイプとして宣言され、*.gpx グロブと <gpx 文字列マジックマッチが定義されています。
<mime-type type="application/gpx+xml">
<_comment>GPS Exchange Format (GPX)</_comment>
<sub-class-of type="application/xml" />
<glob pattern="*.gpx" />
<magic priority="50">
<match value="<gpx" type="string" offset="0:4096" />
</magic>
</mime-type>
lib/marcel/tables.rb には3箇所の追記が行われました。
- 拡張子→MIMEタイプの正引きテーブル:
'gpx' => 'application/gpx+xml' - MIMEタイプ→拡張子の逆引きテーブル:
'application/gpx+xml' => %w(gpx) - サブクラス関係テーブル:
'application/gpx+xml' => %w(application/xml) - マジックバイト検出テーブル:
['application/gpx+xml', [[0..4096, b['<gpx']]]]
マジックバイト検出では、ファイル先頭から4096バイト以内に <gpx という文字列が存在するかを確認します。GPXファイルは <?xml ... ?> 宣言とXML名前空間属性を持つ <gpx> ルート要素から始まるため、このオフセット範囲が設定されています。
テストフィクスチャとして test/fixtures/name/application/gpx+xml/gpx.gpx が追加され、ウェイポイント(<wpt>)を含む実際のGPXファイル構造が使用されています。
設計判断
sub-class-ofによるサブタイプ宣言 が採用された点が重要な設計判断です。application/gpx+xml を application/xml のサブクラスとして登録することで、Marcelの型階層においてGPXは「よりspecificなXML」として扱われます。これにより、application/xml を許可するバリデーションはGPXファイルも受け入れ、application/gpx+xml を指定するバリデーションはGPXファイルのみを受け入れるという一貫した動作が実現します。
マジックバイト検出の優先度が priority="50" に設定されていることも注目に値します。これはMarcelの検出優先度スケールにおいて中程度の値であり、より確実なシグネチャを持つフォーマット(EPUBやFITSなど)よりも低く、汎用XMLよりも高い位置付けを意図した設定と読み取れます。
まとめ
この変更は、既存のXML検出フレームワークにGPXを適切に組み込んだ追加です。拡張子・マジックバイト・サブクラス関係の3層での登録により、GPXファイルは application/xml に誤検出されることなく、正確な application/gpx+xml として識別されます。