aboutsummaryrefslogtreecommitdiffstats
path: root/swarm
diff options
context:
space:
mode:
authorAlexey Sharov <www.pismeco@gmail.com>2018-10-01 19:39:39 +0800
committerAnton Evangelatov <anton.evangelatov@gmail.com>2018-10-01 19:39:39 +0800
commitdc5d643bb59812cda578fac941c2f1da316bc9d7 (patch)
tree7405f387672f0548eb4734a93581780c96cee7a9 /swarm
parentb69942befeb9f1af55cad0f91953bdaea2ea3efb (diff)
downloaddexon-dc5d643bb59812cda578fac941c2f1da316bc9d7.tar.gz
dexon-dc5d643bb59812cda578fac941c2f1da316bc9d7.tar.zst
dexon-dc5d643bb59812cda578fac941c2f1da316bc9d7.zip
cmd/swarm, swarm: cross-platform Content-Type detection (#17782)
- Mime types generator (Standard "mime" package rely on system-settings, see mime.osInitMime) - Changed swarm/api.Upload: - simplify I/O throttling by semaphore primitive and use file name where possible - f.Close() must be called in Defer - otherwise panic or future added early return will cause leak of file descriptors - one error was suppressed
Diffstat (limited to 'swarm')
-rw-r--r--swarm/api/api.go51
-rw-r--r--swarm/api/api_test.go67
-rw-r--r--swarm/api/client/client.go9
-rw-r--r--swarm/api/filesystem.go81
-rw-r--r--swarm/api/filesystem_test.go4
-rw-r--r--swarm/api/gen_mime.go1201
-rw-r--r--swarm/api/http/server.go26
-rw-r--r--swarm/api/http/server_test.go47
8 files changed, 1419 insertions, 67 deletions
diff --git a/swarm/api/api.go b/swarm/api/api.go
index 70c12a757..7b8f04c13 100644
--- a/swarm/api/api.go
+++ b/swarm/api/api.go
@@ -16,6 +16,9 @@
package api
+//go:generate mimegen --types=./../../cmd/swarm/mimegen/mime.types --package=api --out=gen_mime.go
+//go:generate gofmt -s -w gen_mime.go
+
import (
"archive/tar"
"context"
@@ -29,8 +32,6 @@ import (
"path"
"strings"
- "github.com/ethereum/go-ethereum/swarm/storage/mru/lookup"
-
"bytes"
"mime"
"path/filepath"
@@ -45,7 +46,8 @@ import (
"github.com/ethereum/go-ethereum/swarm/spancontext"
"github.com/ethereum/go-ethereum/swarm/storage"
"github.com/ethereum/go-ethereum/swarm/storage/mru"
- opentracing "github.com/opentracing/opentracing-go"
+ "github.com/ethereum/go-ethereum/swarm/storage/mru/lookup"
+ "github.com/opentracing/opentracing-go"
)
var (
@@ -757,9 +759,14 @@ func (a *API) UploadTar(ctx context.Context, bodyReader io.ReadCloser, manifestP
// add the entry under the path from the request
manifestPath := path.Join(manifestPath, hdr.Name)
+ contentType := hdr.Xattrs["user.swarm.content-type"]
+ if contentType == "" {
+ contentType = mime.TypeByExtension(filepath.Ext(hdr.Name))
+ }
+ //DetectContentType("")
entry := &ManifestEntry{
Path: manifestPath,
- ContentType: hdr.Xattrs["user.swarm.content-type"],
+ ContentType: contentType,
Mode: hdr.Mode,
Size: hdr.Size,
ModTime: hdr.ModTime,
@@ -770,10 +777,15 @@ func (a *API) UploadTar(ctx context.Context, bodyReader io.ReadCloser, manifestP
return nil, fmt.Errorf("error adding manifest entry from tar stream: %s", err)
}
if hdr.Name == defaultPath {
+ contentType := hdr.Xattrs["user.swarm.content-type"]
+ if contentType == "" {
+ contentType = mime.TypeByExtension(filepath.Ext(hdr.Name))
+ }
+
entry := &ManifestEntry{
Hash: contentKey.Hex(),
Path: "", // default entry
- ContentType: hdr.Xattrs["user.swarm.content-type"],
+ ContentType: contentType,
Mode: hdr.Mode,
Size: hdr.Size,
ModTime: hdr.ModTime,
@@ -1033,3 +1045,32 @@ func (a *API) ResolveResourceView(ctx context.Context, uri *URI, values mru.Valu
}
return view, nil
}
+
+// MimeOctetStream default value of http Content-Type header
+const MimeOctetStream = "application/octet-stream"
+
+// DetectContentType by file file extension, or fallback to content sniff
+func DetectContentType(fileName string, f io.ReadSeeker) (string, error) {
+ ctype := mime.TypeByExtension(filepath.Ext(fileName))
+ if ctype != "" {
+ return ctype, nil
+ }
+
+ // save/rollback to get content probe from begin of file
+ currentPosition, err := f.Seek(0, io.SeekCurrent)
+ if err != nil {
+ return MimeOctetStream, fmt.Errorf("seeker can't seek, %s", err)
+ }
+
+ // read a chunk to decide between utf-8 text and binary
+ var buf [512]byte
+ n, _ := f.Read(buf[:])
+ ctype = http.DetectContentType(buf[:n])
+
+ _, err = f.Seek(currentPosition, io.SeekStart) // rewind to output whole file
+ if err != nil {
+ return MimeOctetStream, fmt.Errorf("seeker can't seek, %s", err)
+ }
+
+ return ctype, nil
+}
diff --git a/swarm/api/api_test.go b/swarm/api/api_test.go
index a65bf07e2..eb896f32a 100644
--- a/swarm/api/api_test.go
+++ b/swarm/api/api_test.go
@@ -17,6 +17,7 @@
package api
import (
+ "bytes"
"context"
"errors"
"flag"
@@ -433,3 +434,69 @@ func TestDecryptOrigin(t *testing.T) {
}
}
}
+
+func TestDetectContentType(t *testing.T) {
+ for _, tc := range []struct {
+ file string
+ content string
+ expectedContentType string
+ }{
+ {
+ file: "file-with-correct-css.css",
+ content: "body {background-color: orange}",
+ expectedContentType: "text/css; charset=utf-8",
+ },
+ {
+ file: "empty-file.css",
+ content: "",
+ expectedContentType: "text/css; charset=utf-8",
+ },
+ {
+ file: "empty-file.pdf",
+ content: "",
+ expectedContentType: "application/pdf",
+ },
+ {
+ file: "empty-file.md",
+ content: "",
+ expectedContentType: "text/markdown; charset=utf-8",
+ },
+ {
+ file: "empty-file-with-unknown-content.strangeext",
+ content: "",
+ expectedContentType: "text/plain; charset=utf-8",
+ },
+ {
+ file: "file-with-unknown-extension-and-content.strangeext",
+ content: "Lorem Ipsum",
+ expectedContentType: "text/plain; charset=utf-8",
+ },
+ {
+ file: "file-no-extension",
+ content: "Lorem Ipsum",
+ expectedContentType: "text/plain; charset=utf-8",
+ },
+ {
+ file: "file-no-extension-no-content",
+ content: "",
+ expectedContentType: "text/plain; charset=utf-8",
+ },
+ {
+ file: "css-file-with-html-inside.css",
+ content: "<!doctype html><html><head></head><body></body></html>",
+ expectedContentType: "text/css; charset=utf-8",
+ },
+ } {
+ t.Run(tc.file, func(t *testing.T) {
+ detected, err := DetectContentType(tc.file, bytes.NewReader([]byte(tc.content)))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if detected != tc.expectedContentType {
+ t.Fatalf("File: %s, Expected mime type %s, got %s", tc.file, tc.expectedContentType, detected)
+ }
+
+ })
+ }
+}
diff --git a/swarm/api/client/client.go b/swarm/api/client/client.go
index a6666144a..47a6980de 100644
--- a/swarm/api/client/client.go
+++ b/swarm/api/client/client.go
@@ -24,7 +24,6 @@ import (
"fmt"
"io"
"io/ioutil"
- "mime"
"mime/multipart"
"net/http"
"net/textproto"
@@ -124,10 +123,16 @@ func Open(path string) (*File, error) {
f.Close()
return nil, err
}
+
+ contentType, err := api.DetectContentType(f.Name(), f)
+ if err != nil {
+ return nil, err
+ }
+
return &File{
ReadCloser: f,
ManifestEntry: api.ManifestEntry{
- ContentType: mime.TypeByExtension(filepath.Ext(path)),
+ ContentType: contentType,
Mode: int64(stat.Mode()),
Size: stat.Size(),
ModTime: stat.ModTime(),
diff --git a/swarm/api/filesystem.go b/swarm/api/filesystem.go
index 8251ebc4d..43695efc1 100644
--- a/swarm/api/filesystem.go
+++ b/swarm/api/filesystem.go
@@ -21,7 +21,6 @@ import (
"context"
"fmt"
"io"
- "net/http"
"os"
"path"
"path/filepath"
@@ -97,51 +96,50 @@ func (fs *FileSystem) Upload(lpath, index string, toEncrypt bool) (string, error
list = append(list, entry)
}
- cnt := len(list)
- errors := make([]error, cnt)
- done := make(chan bool, maxParallelFiles)
- dcnt := 0
- awg := &sync.WaitGroup{}
+ errors := make([]error, len(list))
+ sem := make(chan bool, maxParallelFiles)
+ defer close(sem)
for i, entry := range list {
- if i >= dcnt+maxParallelFiles {
- <-done
- dcnt++
- }
- awg.Add(1)
- go func(i int, entry *manifestTrieEntry, done chan bool) {
+ sem <- true
+ go func(i int, entry *manifestTrieEntry) {
+ defer func() { <-sem }()
+
f, err := os.Open(entry.Path)
- if err == nil {
- stat, _ := f.Stat()
- var hash storage.Address
- var wait func(context.Context) error
- ctx := context.TODO()
- hash, wait, err = fs.api.fileStore.Store(ctx, f, stat.Size(), toEncrypt)
- if hash != nil {
- list[i].Hash = hash.Hex()
- }
- err = wait(ctx)
- awg.Done()
- if err == nil {
- first512 := make([]byte, 512)
- fread, _ := f.ReadAt(first512, 0)
- if fread > 0 {
- mimeType := http.DetectContentType(first512[:fread])
- if filepath.Ext(entry.Path) == ".css" {
- mimeType = "text/css"
- }
- list[i].ContentType = mimeType
- }
- }
- f.Close()
+ if err != nil {
+ errors[i] = err
+ return
+ }
+ defer f.Close()
+
+ stat, err := f.Stat()
+ if err != nil {
+ errors[i] = err
+ return
+ }
+
+ var hash storage.Address
+ var wait func(context.Context) error
+ ctx := context.TODO()
+ hash, wait, err = fs.api.fileStore.Store(ctx, f, stat.Size(), toEncrypt)
+ if hash != nil {
+ list[i].Hash = hash.Hex()
}
- errors[i] = err
- done <- true
- }(i, entry, done)
+ if err := wait(ctx); err != nil {
+ errors[i] = err
+ return
+ }
+
+ list[i].ContentType, err = DetectContentType(f.Name(), f)
+ if err != nil {
+ errors[i] = err
+ return
+ }
+
+ }(i, entry)
}
- for dcnt < cnt {
- <-done
- dcnt++
+ for i := 0; i < cap(sem); i++ {
+ sem <- true
}
trie := &manifestTrie{
@@ -168,7 +166,6 @@ func (fs *FileSystem) Upload(lpath, index string, toEncrypt bool) (string, error
if err2 == nil {
hs = trie.ref.Hex()
}
- awg.Wait()
return hs, err2
}
diff --git a/swarm/api/filesystem_test.go b/swarm/api/filesystem_test.go
index fe7527b1f..02f5bff65 100644
--- a/swarm/api/filesystem_test.go
+++ b/swarm/api/filesystem_test.go
@@ -60,7 +60,7 @@ func TestApiDirUpload0(t *testing.T) {
content = readPath(t, "testdata", "test0", "index.css")
resp = testGet(t, api, bzzhash, "index.css")
- exp = expResponse(content, "text/css", 0)
+ exp = expResponse(content, "text/css; charset=utf-8", 0)
checkResponse(t, resp, exp)
addr := storage.Address(common.Hex2Bytes(bzzhash))
@@ -140,7 +140,7 @@ func TestApiDirUploadModify(t *testing.T) {
content = readPath(t, "testdata", "test0", "index.css")
resp = testGet(t, api, bzzhash, "index.css")
- exp = expResponse(content, "text/css", 0)
+ exp = expResponse(content, "text/css; charset=utf-8", 0)
checkResponse(t, resp, exp)
_, _, _, _, err = api.Get(context.TODO(), nil, addr, "")
diff --git a/swarm/api/gen_mime.go b/swarm/api/gen_mime.go
new file mode 100644
index 000000000..109edeb50
--- /dev/null
+++ b/swarm/api/gen_mime.go
@@ -0,0 +1,1201 @@
+// Code generated by github.com/ethereum/go-ethereum/cmd/swarm/mimegen. DO NOT EDIT.
+
+package api
+
+import "mime"
+
+func init() {
+ var mimeTypes = map[string]string{
+ ".a2l": "application/A2L",
+ ".aml": "application/AML",
+ ".ez": "application/andrew-inset",
+ ".atf": "application/ATF",
+ ".atfx": "application/ATFX",
+ ".atxml": "application/ATXML",
+ ".atom": "application/atom+xml",
+ ".atomcat": "application/atomcat+xml",
+ ".atomdeleted": "application/atomdeleted+xml",
+ ".atomsvc": "application/atomsvc+xml",
+ ".apxml": "application/auth-policy+xml",
+ ".xdd": "application/bacnet-xdd+zip",
+ ".xcs": "application/calendar+xml",
+ ".cbor": "application/cbor",
+ ".ccmp": "application/ccmp+xml",
+ ".ccxml": "application/ccxml+xml",
+ ".cdfx": "application/CDFX+XML",
+ ".cdmia": "application/cdmi-capability",
+ ".cdmic": "application/cdmi-container",
+ ".cdmid": "application/cdmi-domain",
+ ".cdmio": "application/cdmi-object",
+ ".cdmiq": "application/cdmi-queue",
+ ".cea": "application/CEA",
+ ".cellml": "application/cellml+xml",
+ ".cml": "application/cellml+xml",
+ ".clue": "application/clue_info+xml",
+ ".cmsc": "application/cms",
+ ".cpl": "application/cpl+xml",
+ ".csrattrs": "application/csrattrs",
+ ".mpd": "application/dash+xml",
+ ".mpdd": "application/dashdelta",
+ ".davmount": "application/davmount+xml",
+ ".dcd": "application/DCD",
+ ".dcm": "application/dicom",
+ ".dii": "application/DII",
+ ".dit": "application/DIT",
+ ".xmls": "application/dskpp+xml",
+ ".dssc": "application/dssc+der",
+ ".xdssc": "application/dssc+xml",
+ ".dvc": "application/dvcs",
+ ".es": "application/ecmascript",
+ ".efi": "application/efi",
+ ".emma": "application/emma+xml",
+ ".emotionml": "application/emotionml+xml",
+ ".epub": "application/epub+zip",
+ ".exi": "application/exi",
+ ".finf": "application/fastinfoset",
+ ".fdt": "application/fdt+xml",
+ ".pfr": "application/font-tdpfr",
+ ".geojson": "application/geo+json",
+ ".gml": "application/gml+xml",
+ ".gz": "application/gzip",
+ ".tgz": "application/gzip",
+ ".stk": "application/hyperstudio",
+ ".ink": "application/inkml+xml",
+ ".inkml": "application/inkml+xml",
+ ".ipfix": "application/ipfix",
+ ".its": "application/its+xml",
+ ".js": "application/javascript",
+ ".jrd": "application/jrd+json",
+ ".json": "application/json",
+ ".json-patch": "application/json-patch+json",
+ ".jsonld": "application/ld+json",
+ ".lgr": "application/lgr+xml",
+ ".wlnk": "application/link-format",
+ ".lostxml": "application/lost+xml",
+ ".lostsyncxml": "application/lostsync+xml",
+ ".lxf": "application/LXF",
+ ".hqx": "application/mac-binhex40",
+ ".mads": "application/mads+xml",
+ ".mrc": "application/marc",
+ ".mrcx": "application/marcxml+xml",
+ ".nb": "application/mathematica",
+ ".ma": "application/mathematica",
+ ".mb": "application/mathematica",
+ ".mml": "application/mathml+xml",
+ ".mbox": "application/mbox",
+ ".meta4": "application/metalink4+xml",
+ ".mets": "application/mets+xml",
+ ".mf4": "application/MF4",
+ ".mods": "application/mods+xml",
+ ".m21": "application/mp21",
+ ".mp21": "application/mp21",
+ ".doc": "application/msword",
+ ".mxf": "application/mxf",
+ ".nq": "application/n-quads",
+ ".nt": "application/n-triples",
+ ".orq": "application/ocsp-request",
+ ".ors": "application/ocsp-response",
+ ".bin": "application/octet-stream",
+ ".lha": "application/octet-stream",
+ ".lzh": "application/octet-stream",
+ ".exe": "application/octet-stream",
+ ".class": "application/octet-stream",
+ ".so": "application/octet-stream",
+ ".dll": "application/octet-stream",
+ ".img": "application/octet-stream",
+ ".iso": "application/octet-stream",
+ ".oda": "application/oda",
+ ".odx": "application/ODX",
+ ".opf": "application/oebps-package+xml",
+ ".ogx": "application/ogg",
+ ".oxps": "application/oxps",
+ ".relo": "application/p2p-overlay+xml",
+ ".pdf": "application/pdf",
+ ".pdx": "application/PDX",
+ ".pgp": "application/pgp-encrypted",
+ ".sig": "application/pgp-signature",
+ ".p10": "application/pkcs10",
+ ".p12": "application/pkcs12",
+ ".pfx": "application/pkcs12",
+ ".p7m": "application/pkcs7-mime",
+ ".p7c": "application/pkcs7-mime",
+ ".p7s": "application/pkcs7-signature",
+ ".p8": "application/pkcs8",
+ ".cer": "application/pkix-cert",
+ ".crl": "application/pkix-crl",
+ ".pkipath": "application/pkix-pkipath",
+ ".pki": "application/pkixcmp",
+ ".pls": "application/pls+xml",
+ ".ps": "application/postscript",
+ ".eps": "application/postscript",
+ ".ai": "application/postscript",
+ ".provx": "application/provenance+xml",
+ ".cw": "application/prs.cww",
+ ".cww": "application/prs.cww",
+ ".hpub": "application/prs.hpub+zip",
+ ".rnd": "application/prs.nprend",
+ ".rct": "application/prs.nprend",
+ ".rdf-crypt": "application/prs.rdf-xml-crypt",
+ ".xsf": "application/prs.xsf+xml",
+ ".pskcxml": "application/pskc+xml",
+ ".rdf": "application/rdf+xml",
+ ".rif": "application/reginfo+xml",
+ ".rnc": "application/relax-ng-compact-syntax",
+ ".rld": "application/resource-lists-diff+xml",
+ ".rl": "application/resource-lists+xml",
+ ".rfcxml": "application/rfc+xml",
+ ".rs": "application/rls-services+xml",
+ ".gbr": "application/rpki-ghostbusters",
+ ".mft": "application/rpki-manifest",
+ ".roa": "application/rpki-roa",
+ ".rtf": "application/rtf",
+ ".scim": "application/scim+json",
+ ".scq": "application/scvp-cv-request",
+ ".scs": "application/scvp-cv-response",
+ ".spq": "application/scvp-vp-request",
+ ".spp": "application/scvp-vp-response",
+ ".sdp": "application/sdp",
+ ".soc": "application/sgml-open-catalog",
+ ".shf": "application/shf+xml",
+ ".siv": "application/sieve",
+ ".sieve": "application/sieve",
+ ".cl": "application/simple-filter+xml",
+ ".smil": "application/smil+xml",
+ ".smi": "application/smil+xml",
+ ".sml": "application/smil+xml",
+ ".rq": "application/sparql-query",
+ ".srx": "application/sparql-results+xml",
+ ".sql": "application/sql",
+ ".gram": "application/srgs",
+ ".grxml": "application/srgs+xml",
+ ".sru": "application/sru+xml",
+ ".ssml": "application/ssml+xml",
+ ".tau": "application/tamp-apex-update",
+ ".auc": "application/tamp-apex-update-confirm",
+ ".tcu": "application/tamp-community-update",
+ ".cuc": "application/tamp-community-update-confirm",
+ ".ter": "application/tamp-error",
+ ".tsa": "application/tamp-sequence-adjust",
+ ".sac": "application/tamp-sequence-adjust-confirm",
+ ".tur": "application/tamp-update",
+ ".tuc": "application/tamp-update-confirm",
+ ".tei": "application/tei+xml",
+ ".teiCorpus": "application/tei+xml",
+ ".odd": "application/tei+xml",
+ ".tfi": "application/thraud+xml",
+ ".tsq": "application/timestamp-query",
+ ".tsr": "application/timestamp-reply",
+ ".tsd": "application/timestamped-data",
+ ".trig": "application/trig",
+ ".ttml": "application/ttml+xml",
+ ".gsheet": "application/urc-grpsheet+xml",
+ ".rsheet": "application/urc-ressheet+xml",
+ ".td": "application/urc-targetdesc+xml",
+ ".uis": "application/urc-uisocketdesc+xml",
+ ".plb": "application/vnd.3gpp.pic-bw-large",
+ ".psb": "application/vnd.3gpp.pic-bw-small",
+ ".pvb": "application/vnd.3gpp.pic-bw-var",
+ ".sms": "application/vnd.3gpp2.sms",
+ ".tcap": "application/vnd.3gpp2.tcap",
+ ".imgcal": "application/vnd.3lightssoftware.imagescal",
+ ".pwn": "application/vnd.3M.Post-it-Notes",
+ ".aso": "application/vnd.accpac.simply.aso",
+ ".imp": "application/vnd.accpac.simply.imp",
+ ".acu": "application/vnd.acucobol",
+ ".atc": "application/vnd.acucorp",
+ ".acutc": "application/vnd.acucorp",
+ ".swf": "application/vnd.adobe.flash.movie",
+ ".fcdt": "application/vnd.adobe.formscentral.fcdt",
+ ".fxp": "application/vnd.adobe.fxp",
+ ".fxpl": "application/vnd.adobe.fxp",
+ ".xdp": "application/vnd.adobe.xdp+xml",
+ ".xfdf": "application/vnd.adobe.xfdf",
+ ".ahead": "application/vnd.ahead.space",
+ ".azf": "application/vnd.airzip.filesecure.azf",
+ ".azs": "application/vnd.airzip.filesecure.azs",
+ ".azw3": "application/vnd.amazon.mobi8-ebook",
+ ".acc": "application/vnd.americandynamics.acc",
+ ".ami": "application/vnd.amiga.ami",
+ ".apkg": "application/vnd.anki",
+ ".cii": "application/vnd.anser-web-certificate-issue-initiation",
+ ".fti": "application/vnd.anser-web-funds-transfer-initiation",
+ ".dist": "application/vnd.apple.installer+xml",
+ ".distz": "application/vnd.apple.installer+xml",
+ ".pkg": "application/vnd.apple.installer+xml",
+ ".mpkg": "application/vnd.apple.installer+xml",
+ ".m3u8": "application/vnd.apple.mpegurl",
+ ".swi": "application/vnd.aristanetworks.swi",
+ ".iota": "application/vnd.astraea-software.iota",
+ ".aep": "application/vnd.audiograph",
+ ".package": "application/vnd.autopackage",
+ ".bmml": "application/vnd.balsamiq.bmml+xml",
+ ".bmpr": "application/vnd.balsamiq.bmpr",
+ ".mpm": "application/vnd.blueice.multipass",
+ ".ep": "application/vnd.bluetooth.ep.oob",
+ ".le": "application/vnd.bluetooth.le.oob",
+ ".bmi": "application/vnd.bmi",
+ ".rep": "application/vnd.businessobjects",
+ ".tlclient": "application/vnd.cendio.thinlinc.clientconf",
+ ".cdxml": "application/vnd.chemdraw+xml",
+ ".pgn": "application/vnd.chess-pgn",
+ ".mmd": "application/vnd.chipnuts.karaoke-mmd",
+ ".cdy": "application/vnd.cinderella",
+ ".csl": "application/vnd.citationstyles.style+xml",
+ ".cla": "application/vnd.claymore",
+ ".rp9": "application/vnd.cloanto.rp9",
+ ".c4g": "application/vnd.clonk.c4group",
+ ".c4d": "application/vnd.clonk.c4group",
+ ".c4f": "application/vnd.clonk.c4group",
+ ".c4p": "application/vnd.clonk.c4group",
+ ".c4u": "application/vnd.clonk.c4group",
+ ".c11amc": "application/vnd.cluetrust.cartomobile-config",
+ ".c11amz": "application/vnd.cluetrust.cartomobile-config-pkg",
+ ".coffee": "application/vnd.coffeescript",
+ ".cbz": "application/vnd.comicbook+zip",
+ ".ica": "application/vnd.commerce-battelle",
+ ".icf": "application/vnd.commerce-battelle",
+ ".icd": "application/vnd.commerce-battelle",
+ ".ic0": "application/vnd.commerce-battelle",
+ ".ic1": "application/vnd.commerce-battelle",
+ ".ic2": "application/vnd.commerce-battelle",
+ ".ic3": "application/vnd.commerce-battelle",
+ ".ic4": "application/vnd.commerce-battelle",
+ ".ic5": "application/vnd.commerce-battelle",
+ ".ic6": "application/vnd.commerce-battelle",
+ ".ic7": "application/vnd.commerce-battelle",
+ ".ic8": "application/vnd.commerce-battelle",
+ ".csp": "application/vnd.commonspace",
+ ".cst": "application/vnd.commonspace",
+ ".cdbcmsg": "application/vnd.contact.cmsg",
+ ".ign": "application/vnd.coreos.ignition+json",
+ ".ignition": "application/vnd.coreos.ignition+json",
+ ".cmc": "application/vnd.cosmocaller",
+ ".clkx": "application/vnd.crick.clicker",
+ ".clkk": "application/vnd.crick.clicker.keyboard",
+ ".clkp": "application/vnd.crick.clicker.palette",
+ ".clkt": "application/vnd.crick.clicker.template",
+ ".clkw": "application/vnd.crick.clicker.wordbank",
+ ".wbs": "application/vnd.criticaltools.wbs+xml",
+ ".pml": "application/vnd.ctc-posml",
+ ".ppd": "application/vnd.cups-ppd",
+ ".curl": "application/vnd.curl",
+ ".dart": "application/vnd.dart",
+ ".rdz": "application/vnd.data-vision.rdz",
+ ".deb": "application/vnd.debian.binary-package",
+ ".udeb": "application/vnd.debian.binary-package",
+ ".uvf": "application/vnd.dece.data",
+ ".uvvf": "application/vnd.dece.data",
+ ".uvd": "application/vnd.dece.data",
+ ".uvvd": "application/vnd.dece.data",
+ ".uvt": "application/vnd.dece.ttml+xml",
+ ".uvvt": "application/vnd.dece.ttml+xml",
+ ".uvx": "application/vnd.dece.unspecified",
+ ".uvvx": "application/vnd.dece.unspecified",
+ ".uvz": "application/vnd.dece.zip",
+ ".uvvz": "application/vnd.dece.zip",
+ ".fe_launch": "application/vnd.denovo.fcselayout-link",
+ ".dsm": "application/vnd.desmume.movie",
+ ".dna": "application/vnd.dna",
+ ".docjson": "application/vnd.document+json",
+ ".scld": "application/vnd.doremir.scorecloud-binary-document",
+ ".dpg": "application/vnd.dpgraph",
+ ".mwc": "application/vnd.dpgraph",
+ ".dpgraph": "application/vnd.dpgraph",
+ ".dfac": "application/vnd.dreamfactory",
+ ".fla": "application/vnd.dtg.local.flash",
+ ".ait": "application/vnd.dvb.ait",
+ ".svc": "application/vnd.dvb.service",
+ ".geo": "application/vnd.dynageo",
+ ".dzr": "application/vnd.dzr",
+ ".mag": "application/vnd.ecowin.chart",
+ ".nml": "application/vnd.enliven",
+ ".esf": "application/vnd.epson.esf",
+ ".msf": "application/vnd.epson.msf",
+ ".qam": "application/vnd.epson.quickanime",
+ ".slt": "application/vnd.epson.salt",
+ ".ssf": "application/vnd.epson.ssf",
+ ".qcall": "application/vnd.ericsson.quickcall",
+ ".qca": "application/vnd.ericsson.quickcall",
+ ".espass": "application/vnd.espass-espass+zip",
+ ".es3": "application/vnd.eszigno3+xml",
+ ".et3": "application/vnd.eszigno3+xml",
+ ".asice": "application/vnd.etsi.asic-e+zip",
+ ".sce": "application/vnd.etsi.asic-e+zip",
+ ".asics": "application/vnd.etsi.asic-s+zip",
+ ".tst": "application/vnd.etsi.timestamp-token",
+ ".ez2": "application/vnd.ezpix-album",
+ ".ez3": "application/vnd.ezpix-package",
+ ".dim": "application/vnd.fastcopy-disk-image",
+ ".fdf": "application/vnd.fdf",
+ ".msd": "application/vnd.fdsn.mseed",
+ ".mseed": "application/vnd.fdsn.mseed",
+ ".seed": "application/vnd.fdsn.seed",
+ ".dataless": "application/vnd.fdsn.seed",
+ ".zfc": "application/vnd.filmit.zfc",
+ ".gph": "application/vnd.FloGraphIt",
+ ".ftc": "application/vnd.fluxtime.clip",
+ ".sfd": "application/vnd.font-fontforge-sfd",
+ ".fm": "application/vnd.framemaker",
+ ".fnc": "application/vnd.frogans.fnc",
+ ".ltf": "application/vnd.frogans.ltf",
+ ".fsc": "application/vnd.fsc.weblaunch",
+ ".oas": "application/vnd.fujitsu.oasys",
+ ".oa2": "application/vnd.fujitsu.oasys2",
+ ".oa3": "application/vnd.fujitsu.oasys3",
+ ".fg5": "application/vnd.fujitsu.oasysgp",
+ ".bh2": "application/vnd.fujitsu.oasysprs",
+ ".ddd": "application/vnd.fujixerox.ddd",
+ ".xdw": "application/vnd.fujixerox.docuworks",
+ ".xbd": "application/vnd.fujixerox.docuworks.binder",
+ ".xct": "application/vnd.fujixerox.docuworks.container",
+ ".fzs": "application/vnd.fuzzysheet",
+ ".txd": "application/vnd.genomatix.tuxedo",
+ ".g3": "application/vnd.geocube+xml",
+ ".g³": "application/vnd.geocube+xml",
+ ".ggb": "application/vnd.geogebra.file",
+ ".ggt": "application/vnd.geogebra.tool",
+ ".gex": "application/vnd.geometry-explorer",
+ ".gre": "application/vnd.geometry-explorer",
+ ".gxt": "application/vnd.geonext",
+ ".g2w": "application/vnd.geoplan",
+ ".g3w": "application/vnd.geospace",
+ ".gmx": "application/vnd.gmx",
+ ".kml": "application/vnd.google-earth.kml+xml",
+ ".kmz": "application/vnd.google-earth.kmz",
+ ".gqf": "application/vnd.grafeq",
+ ".gqs": "application/vnd.grafeq",
+ ".gac": "application/vnd.groove-account",
+ ".ghf": "application/vnd.groove-help",
+ ".gim": "application/vnd.groove-identity-message",
+ ".grv": "application/vnd.groove-injector",
+ ".gtm": "application/vnd.groove-tool-message",
+ ".tpl": "application/vnd.groove-tool-template",
+ ".vcg": "application/vnd.groove-vcard",
+ ".hal": "application/vnd.hal+xml",
+ ".zmm": "application/vnd.HandHeld-Entertainment+xml",
+ ".hbci": "application/vnd.hbci",
+ ".hbc": "application/vnd.hbci",
+ ".kom": "application/vnd.hbci",
+ ".upa": "application/vnd.hbci",
+ ".pkd": "application/vnd.hbci",
+ ".bpd": "application/vnd.hbci",
+ ".hdt": "application/vnd.hdt",
+ ".les": "application/vnd.hhe.lesson-player",
+ ".hpgl": "application/vnd.hp-HPGL",
+ ".hpi": "application/vnd.hp-hpid",
+ ".hpid": "application/vnd.hp-hpid",
+ ".hps": "application/vnd.hp-hps",
+ ".jlt": "application/vnd.hp-jlyt",
+ ".pcl": "application/vnd.hp-PCL",
+ ".sfd-hdstx": "application/vnd.hydrostatix.sof-data",
+ ".x3d": "application/vnd.hzn-3d-crossword",
+ ".emm": "application/vnd.ibm.electronic-media",
+ ".mpy": "application/vnd.ibm.MiniPay",
+ ".list3820": "application/vnd.ibm.modcap",
+ ".listafp": "application/vnd.ibm.modcap",
+ ".afp": "application/vnd.ibm.modcap",
+ ".pseg3820": "application/vnd.ibm.modcap",
+ ".irm": "application/vnd.ibm.rights-management",
+ ".sc": "application/vnd.ibm.secure-container",
+ ".icc": "application/vnd.iccprofile",
+ ".icm": "application/vnd.iccprofile",
+ ".1905.1": "application/vnd.ieee.1905",
+ ".igl": "application/vnd.igloader",
+ ".imf": "application/vnd.imagemeter.folder+zip",
+ ".imi": "application/vnd.imagemeter.image+zip",
+ ".ivp": "application/vnd.immervision-ivp",
+ ".ivu": "application/vnd.immervision-ivu",
+ ".imscc": "application/vnd.ims.imsccv1p1",
+ ".igm": "application/vnd.insors.igm",
+ ".xpw": "application/vnd.intercon.formnet",
+ ".xpx": "application/vnd.intercon.formnet",
+ ".i2g": "application/vnd.intergeo",
+ ".qbo": "application/vnd.intu.qbo",
+ ".qfx": "application/vnd.intu.qfx",
+ ".rcprofile": "application/vnd.ipunplugged.rcprofile",
+ ".irp": "application/vnd.irepository.package+xml",
+ ".xpr": "application/vnd.is-xpr",
+ ".fcs": "application/vnd.isac.fcs",
+ ".jam": "application/vnd.jam",
+ ".rms": "application/vnd.jcp.javame.midlet-rms",
+ ".jisp": "application/vnd.jisp",
+ ".joda": "application/vnd.joost.joda-archive",
+ ".ktz": "application/vnd.kahootz",
+ ".ktr": "application/vnd.kahootz",
+ ".karbon": "application/vnd.kde.karbon",
+ ".chrt": "application/vnd.kde.kchart",
+ ".kfo": "application/vnd.kde.kformula",
+ ".flw": "application/vnd.kde.kivio",
+ ".kon": "application/vnd.kde.kontour",
+ ".kpr": "application/vnd.kde.kpresenter",
+ ".kpt": "application/vnd.kde.kpresenter",
+ ".ksp": "application/vnd.kde.kspread",
+ ".kwd": "application/vnd.kde.kword",
+ ".kwt": "application/vnd.kde.kword",
+ ".htke": "application/vnd.kenameaapp",
+ ".kia": "application/vnd.kidspiration",
+ ".kne": "application/vnd.Kinar",
+ ".knp": "application/vnd.Kinar",
+ ".sdf": "application/vnd.Kinar",
+ ".skp": "application/vnd.koan",
+ ".skd": "application/vnd.koan",
+ ".skm": "application/vnd.koan",
+ ".skt": "application/vnd.koan",
+ ".sse": "application/vnd.kodak-descriptor",
+ ".lasjson": "application/vnd.las.las+json",
+ ".lasxml": "application/vnd.las.las+xml",
+ ".lbd": "application/vnd.llamagraphics.life-balance.desktop",
+ ".lbe": "application/vnd.llamagraphics.life-balance.exchange+xml",
+ ".123": "application/vnd.lotus-1-2-3",
+ ".wk4": "application/vnd.lotus-1-2-3",
+ ".wk3": "application/vnd.lotus-1-2-3",
+ ".wk1": "application/vnd.lotus-1-2-3",
+ ".apr": "application/vnd.lotus-approach",
+ ".vew": "application/vnd.lotus-approach",
+ ".prz": "application/vnd.lotus-freelance",
+ ".pre": "application/vnd.lotus-freelance",
+ ".nsf": "application/vnd.lotus-notes",
+ ".ntf": "application/vnd.lotus-notes",
+ ".ndl": "application/vnd.lotus-notes",
+ ".ns4": "application/vnd.lotus-notes",
+ ".ns3": "application/vnd.lotus-notes",
+ ".ns2": "application/vnd.lotus-notes",
+ ".nsh": "application/vnd.lotus-notes",
+ ".nsg": "application/vnd.lotus-notes",
+ ".or3": "application/vnd.lotus-organizer",
+ ".or2": "application/vnd.lotus-organizer",
+ ".org": "application/vnd.lotus-organizer",
+ ".scm": "application/vnd.lotus-screencam",
+ ".lwp": "application/vnd.lotus-wordpro",
+ ".sam": "application/vnd.lotus-wordpro",
+ ".portpkg": "application/vnd.macports.portpkg",
+ ".mvt": "application/vnd.mapbox-vector-tile",
+ ".mdc": "application/vnd.marlin.drm.mdcf",
+ ".mmdb": "application/vnd.maxmind.maxmind-db",
+ ".mcd": "application/vnd.mcd",
+ ".mc1": "application/vnd.medcalcdata",
+ ".cdkey": "application/vnd.mediastation.cdkey",
+ ".mwf": "application/vnd.MFER",
+ ".mfm": "application/vnd.mfmp",
+ ".flo": "application/vnd.micrografx.flo",
+ ".igx": "application/vnd.micrografx.igx",
+ ".mif": "application/vnd.mif",
+ ".daf": "application/vnd.Mobius.DAF",
+ ".dis": "application/vnd.Mobius.DIS",
+ ".mbk": "application/vnd.Mobius.MBK",
+ ".mqy": "application/vnd.Mobius.MQY",
+ ".msl": "application/vnd.Mobius.MSL",
+ ".plc": "application/vnd.Mobius.PLC",
+ ".txf": "application/vnd.Mobius.TXF",
+ ".mpn": "application/vnd.mophun.application",
+ ".mpc": "application/vnd.mophun.certificate",
+ ".xul": "application/vnd.mozilla.xul+xml",
+ ".3mf": "application/vnd.ms-3mfdocument",
+ ".cil": "application/vnd.ms-artgalry",
+ ".asf": "application/vnd.ms-asf",
+ ".cab": "application/vnd.ms-cab-compressed",
+ ".xls": "application/vnd.ms-excel",
+ ".xlm": "application/vnd.ms-excel",
+ ".xla": "application/vnd.ms-excel",
+ ".xlc": "application/vnd.ms-excel",
+ ".xlt": "application/vnd.ms-excel",
+ ".xlw": "application/vnd.ms-excel",
+ ".xltm": "application/vnd.ms-excel.template.macroEnabled.12",
+ ".xlam": "application/vnd.ms-excel.addin.macroEnabled.12",
+ ".xlsb": "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
+ ".xlsm": "application/vnd.ms-excel.sheet.macroEnabled.12",
+ ".eot": "application/vnd.ms-fontobject",
+ ".chm": "application/vnd.ms-htmlhelp",
+ ".ims": "application/vnd.ms-ims",
+ ".lrm": "application/vnd.ms-lrm",
+ ".thmx": "application/vnd.ms-officetheme",
+ ".ppt": "application/vnd.ms-powerpoint",
+ ".pps": "application/vnd.ms-powerpoint",
+ ".pot": "application/vnd.ms-powerpoint",
+ ".ppam": "application/vnd.ms-powerpoint.addin.macroEnabled.12",
+ ".pptm": "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
+ ".sldm": "application/vnd.ms-powerpoint.slide.macroEnabled.12",
+ ".ppsm": "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
+ ".potm": "application/vnd.ms-powerpoint.template.macroEnabled.12",
+ ".mpp": "application/vnd.ms-project",
+ ".mpt": "application/vnd.ms-project",
+ ".tnef": "application/vnd.ms-tnef",
+ ".tnf": "application/vnd.ms-tnef",
+ ".docm": "application/vnd.ms-word.document.macroEnabled.12",
+ ".dotm": "application/vnd.ms-word.template.macroEnabled.12",
+ ".wcm": "application/vnd.ms-works",
+ ".wdb": "application/vnd.ms-works",
+ ".wks": "application/vnd.ms-works",
+ ".wps": "application/vnd.ms-works",
+ ".wpl": "application/vnd.ms-wpl",
+ ".xps": "application/vnd.ms-xpsdocument",
+ ".msa": "application/vnd.msa-disk-image",
+ ".mseq": "application/vnd.mseq",
+ ".crtr": "application/vnd.multiad.creator",
+ ".cif": "application/vnd.multiad.creator.cif",
+ ".mus": "application/vnd.musician",
+ ".msty": "application/vnd.muvee.style",
+ ".taglet": "application/vnd.mynfc",
+ ".entity": "application/vnd.nervana",
+ ".request": "application/vnd.nervana",
+ ".bkm": "application/vnd.nervana",
+ ".kcm": "application/vnd.nervana",
+ ".nitf": "application/vnd.nitf",
+ ".nlu": "application/vnd.neurolanguage.nlu",
+ ".nds": "application/vnd.nintendo.nitro.rom",
+ ".sfc": "application/vnd.nintendo.snes.rom",
+ ".smc": "application/vnd.nintendo.snes.rom",
+ ".nnd": "application/vnd.noblenet-directory",
+ ".nns": "application/vnd.noblenet-sealer",
+ ".nnw": "application/vnd.noblenet-web",
+ ".ac": "application/vnd.nokia.n-gage.ac+xml",
+ ".ngdat": "application/vnd.nokia.n-gage.data",
+ ".n-gage": "application/vnd.nokia.n-gage.symbian.install",
+ ".rpst": "application/vnd.nokia.radio-preset",
+ ".rpss": "application/vnd.nokia.radio-presets",
+ ".edm": "application/vnd.novadigm.EDM",
+ ".edx": "application/vnd.novadigm.EDX",
+ ".ext": "application/vnd.novadigm.EXT",
+ ".odc": "application/vnd.oasis.opendocument.chart",
+ ".otc": "application/vnd.oasis.opendocument.chart-template",
+ ".odb": "application/vnd.oasis.opendocument.database",
+ ".odf": "application/vnd.oasis.opendocument.formula",
+ ".odg": "application/vnd.oasis.opendocument.graphics",
+ ".otg": "application/vnd.oasis.opendocument.graphics-template",
+ ".odi": "application/vnd.oasis.opendocument.image",
+ ".oti": "application/vnd.oasis.opendocument.image-template",
+ ".odp": "application/vnd.oasis.opendocument.presentation",
+ ".otp": "application/vnd.oasis.opendocument.presentation-template",
+ ".ods": "application/vnd.oasis.opendocument.spreadsheet",
+ ".ots": "application/vnd.oasis.opendocument.spreadsheet-template",
+ ".odt": "application/vnd.oasis.opendocument.text",
+ ".odm": "application/vnd.oasis.opendocument.text-master",
+ ".ott": "application/vnd.oasis.opendocument.text-template",
+ ".oth": "application/vnd.oasis.opendocument.text-web",
+ ".xo": "application/vnd.olpc-sugar",
+ ".dd2": "application/vnd.oma.dd2+xml",
+ ".tam": "application/vnd.onepager",
+ ".tamp": "application/vnd.onepagertamp",
+ ".tamx": "application/vnd.onepagertamx",
+ ".tat": "application/vnd.onepagertat",
+ ".tatp": "application/vnd.onepagertatp",
+ ".tatx": "application/vnd.onepagertatx",
+ ".obgx": "application/vnd.openblox.game+xml",
+ ".obg": "application/vnd.openblox.game-binary",
+ ".oeb": "application/vnd.openeye.oeb",
+ ".oxt": "application/vnd.openofficeorg.extension",
+ ".osm": "application/vnd.openstreetmap.data+xml",
+ ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
+ ".sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide",
+ ".ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
+ ".potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
+ ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ ".xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ ".dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
+ ".ndc": "application/vnd.osa.netdeploy",
+ ".mgp": "application/vnd.osgeo.mapguide.package",
+ ".dp": "application/vnd.osgi.dp",
+ ".esa": "application/vnd.osgi.subsystem",
+ ".oxlicg": "application/vnd.oxli.countgraph",
+ ".prc": "application/vnd.palm",
+ ".pdb": "application/vnd.palm",
+ ".pqa": "application/vnd.palm",
+ ".oprc": "application/vnd.palm",
+ ".plp": "application/vnd.panoply",
+ ".paw": "application/vnd.pawaafile",
+ ".str": "application/vnd.pg.format",
+ ".ei6": "application/vnd.pg.osasli",
+ ".pil": "application/vnd.piaccess.application-license",
+ ".efif": "application/vnd.picsel",
+ ".wg": "application/vnd.pmi.widget",
+ ".plf": "application/vnd.pocketlearn",
+ ".pbd": "application/vnd.powerbuilder6",
+ ".preminet": "application/vnd.preminet",
+ ".box": "application/vnd.previewsystems.box",
+ ".vbox": "application/vnd.previewsystems.box",
+ ".mgz": "application/vnd.proteus.magazine",
+ ".qps": "application/vnd.publishare-delta-tree",
+ ".ptid": "application/vnd.pvi.ptid1",
+ ".bar": "application/vnd.qualcomm.brew-app-res",
+ ".qxd": "application/vnd.Quark.QuarkXPress",
+ ".qxt": "application/vnd.Quark.QuarkXPress",
+ ".qwd": "application/vnd.Quark.QuarkXPress",
+ ".qwt": "application/vnd.Quark.QuarkXPress",
+ ".qxl": "application/vnd.Quark.QuarkXPress",
+ ".qxb": "application/vnd.Quark.QuarkXPress",
+ ".quox": "application/vnd.quobject-quoxdocument",
+ ".quiz": "application/vnd.quobject-quoxdocument",
+ ".tree": "application/vnd.rainstor.data",
+ ".rar": "application/vnd.rar",
+ ".bed": "application/vnd.realvnc.bed",
+ ".mxl": "application/vnd.recordare.musicxml",
+ ".cryptonote": "application/vnd.rig.cryptonote",
+ ".link66": "application/vnd.route66.link66+xml",
+ ".st": "application/vnd.sailingtracker.track",
+ ".scd": "application/vnd.scribus",
+ ".sla": "application/vnd.scribus",
+ ".slaz": "application/vnd.scribus",
+ ".s3df": "application/vnd.sealed.3df",
+ ".scsf": "application/vnd.sealed.csf",
+ ".sdoc": "application/vnd.sealed.doc",
+ ".sdo": "application/vnd.sealed.doc",
+ ".s1w": "application/vnd.sealed.doc",
+ ".seml": "application/vnd.sealed.eml",
+ ".sem": "application/vnd.sealed.eml",
+ ".smht": "application/vnd.sealed.mht",
+ ".smh": "application/vnd.sealed.mht",
+ ".sppt": "application/vnd.sealed.ppt",
+ ".s1p": "application/vnd.sealed.ppt",
+ ".stif": "application/vnd.sealed.tiff",
+ ".sxls": "application/vnd.sealed.xls",
+ ".sxl": "application/vnd.sealed.xls",
+ ".s1e": "application/vnd.sealed.xls",
+ ".stml": "application/vnd.sealedmedia.softseal.html",
+ ".s1h": "application/vnd.sealedmedia.softseal.html",
+ ".spdf": "application/vnd.sealedmedia.softseal.pdf",
+ ".spd": "application/vnd.sealedmedia.softseal.pdf",
+ ".s1a": "application/vnd.sealedmedia.softseal.pdf",
+ ".see": "application/vnd.seemail",
+ ".sema": "application/vnd.sema",
+ ".semd": "application/vnd.semd",
+ ".semf": "application/vnd.semf",
+ ".ifm": "application/vnd.shana.informed.formdata",
+ ".itp": "application/vnd.shana.informed.formtemplate",
+ ".iif": "application/vnd.shana.informed.interchange",
+ ".ipk": "application/vnd.shana.informed.package",
+ ".twd": "application/vnd.SimTech-MindMapper",
+ ".twds": "application/vnd.SimTech-MindMapper",
+ ".mmf": "application/vnd.smaf",
+ ".notebook": "application/vnd.smart.notebook",
+ ".teacher": "application/vnd.smart.teacher",
+ ".fo": "application/vnd.software602.filler.form+xml",
+ ".zfo": "application/vnd.software602.filler.form-xml-zip",
+ ".sdkm": "application/vnd.solent.sdkm+xml",
+ ".sdkd": "application/vnd.solent.sdkm+xml",
+ ".dxp": "application/vnd.spotfire.dxp",
+ ".sfs": "application/vnd.spotfire.sfs",
+ ".smzip": "application/vnd.stepmania.package",
+ ".sm": "application/vnd.stepmania.stepchart",
+ ".wadl": "application/vnd.sun.wadl+xml",
+ ".sus": "application/vnd.sus-calendar",
+ ".susp": "application/vnd.sus-calendar",
+ ".xsm": "application/vnd.syncml+xml",
+ ".bdm": "application/vnd.syncml.dm+wbxml",
+ ".xdm": "application/vnd.syncml.dm+xml",
+ ".ddf": "application/vnd.syncml.dmddf+xml",
+ ".tao": "application/vnd.tao.intent-module-archive",
+ ".pcap": "application/vnd.tcpdump.pcap",
+ ".cap": "application/vnd.tcpdump.pcap",
+ ".dmp": "application/vnd.tcpdump.pcap",
+ ".qvd": "application/vnd.theqvd",
+ ".vfr": "application/vnd.tml",
+ ".viaframe": "application/vnd.tml",
+ ".tmo": "application/vnd.tmobile-livetv",
+ ".tpt": "application/vnd.trid.tpt",
+ ".mxs": "application/vnd.triscape.mxs",
+ ".tra": "application/vnd.trueapp",
+ ".ufdl": "application/vnd.ufdl",
+ ".ufd": "application/vnd.ufdl",
+ ".frm": "application/vnd.ufdl",
+ ".utz": "application/vnd.uiq.theme",
+ ".umj": "application/vnd.umajin",
+ ".unityweb": "application/vnd.unity",
+ ".uoml": "application/vnd.uoml+xml",
+ ".uo": "application/vnd.uoml+xml",
+ ".urim": "application/vnd.uri-map",
+ ".urimap": "application/vnd.uri-map",
+ ".vmt": "application/vnd.valve.source.material",
+ ".vcx": "application/vnd.vcx",
+ ".mxi": "application/vnd.vd-study",
+ ".study-inter": "application/vnd.vd-study",
+ ".model-inter": "application/vnd.vd-study",
+ ".vwx": "application/vnd.vectorworks",
+ ".vsc": "application/vnd.vidsoft.vidconference",
+ ".vsd": "application/vnd.visio",
+ ".vst": "application/vnd.visio",
+ ".vsw": "application/vnd.visio",
+ ".vss": "application/vnd.visio",
+ ".vis": "application/vnd.visionary",
+ ".vsf": "application/vnd.vsf",
+ ".sic": "application/vnd.wap.sic",
+ ".slc": "application/vnd.wap.slc",
+ ".wbxml": "application/vnd.wap.wbxml",
+ ".wmlc": "application/vnd.wap.wmlc",
+ ".wmlsc": "application/vnd.wap.wmlscriptc",
+ ".wtb": "application/vnd.webturbo",
+ ".p2p": "application/vnd.wfa.p2p",
+ ".wsc": "application/vnd.wfa.wsc",
+ ".wmc": "application/vnd.wmc",
+ ".m": "application/vnd.wolfram.mathematica.package",
+ ".nbp": "application/vnd.wolfram.player",
+ ".wpd": "application/vnd.wordperfect",
+ ".wqd": "application/vnd.wqd",
+ ".stf": "application/vnd.wt.stf",
+ ".wv": "application/vnd.wv.csp+wbxml",
+ ".xar": "application/vnd.xara",
+ ".xfdl": "application/vnd.xfdl",
+ ".xfd": "application/vnd.xfdl",
+ ".cpkg": "application/vnd.xmpie.cpkg",
+ ".dpkg": "application/vnd.xmpie.dpkg",
+ ".ppkg": "application/vnd.xmpie.ppkg",
+ ".xlim": "application/vnd.xmpie.xlim",
+ ".hvd": "application/vnd.yamaha.hv-dic",
+ ".hvs": "application/vnd.yamaha.hv-script",
+ ".hvp": "application/vnd.yamaha.hv-voice",
+ ".osf": "application/vnd.yamaha.openscoreformat",
+ ".saf": "application/vnd.yamaha.smaf-audio",
+ ".spf": "application/vnd.yamaha.smaf-phrase",
+ ".yme": "application/vnd.yaoweme",
+ ".cmp": "application/vnd.yellowriver-custom-menu",
+ ".zir": "application/vnd.zul",
+ ".zirz": "application/vnd.zul",
+ ".zaz": "application/vnd.zzazz.deck+xml",
+ ".vxml": "application/voicexml+xml",
+ ".wif": "application/watcherinfo+xml",
+ ".wgt": "application/widget",
+ ".wsdl": "application/wsdl+xml",
+ ".wspolicy": "application/wspolicy+xml",
+ ".xav": "application/xcap-att+xml",
+ ".xca": "application/xcap-caps+xml",
+ ".xdf": "application/xcap-diff+xml",
+ ".xel": "application/xcap-el+xml",
+ ".xer": "application/xcap-error+xml",
+ ".xns": "application/xcap-ns+xml",
+ ".xhtml": "application/xhtml+xml",
+ ".xhtm": "application/xhtml+xml",
+ ".xht": "application/xhtml+xml",
+ ".dtd": "application/xml-dtd",
+ ".xop": "application/xop+xml",
+ ".xsl": "application/xslt+xml",
+ ".xslt": "application/xslt+xml",
+ ".mxml": "application/xv+xml",
+ ".xhvml": "application/xv+xml",
+ ".xvml": "application/xv+xml",
+ ".xvm": "application/xv+xml",
+ ".yang": "application/yang",
+ ".yin": "application/yin+xml",
+ ".zip": "application/zip",
+ ".726": "audio/32kadpcm",
+ ".ac3": "audio/ac3",
+ ".amr": "audio/AMR",
+ ".awb": "audio/AMR-WB",
+ ".acn": "audio/asc",
+ ".aal": "audio/ATRAC-ADVANCED-LOSSLESS",
+ ".atx": "audio/ATRAC-X",
+ ".at3": "audio/ATRAC3",
+ ".aa3": "audio/ATRAC3",
+ ".omg": "audio/ATRAC3",
+ ".au": "audio/basic",
+ ".snd": "audio/basic",
+ ".dls": "audio/dls",
+ ".evc": "audio/EVRC",
+ ".evb": "audio/EVRCB",
+ ".enw": "audio/EVRCNW",
+ ".evw": "audio/EVRCWB",
+ ".lbc": "audio/iLBC",
+ ".l16": "audio/L16",
+ ".mxmf": "audio/mobile-xmf",
+ ".m4a": "audio/mp4",
+ ".mp3": "audio/mpeg",
+ ".mpga": "audio/mpeg",
+ ".mp1": "audio/mpeg",
+ ".mp2": "audio/mpeg",
+ ".oga": "audio/ogg",
+ ".ogg": "audio/ogg",
+ ".opus": "audio/ogg",
+ ".spx": "audio/ogg",
+ ".sid": "audio/prs.sid",
+ ".psid": "audio/prs.sid",
+ ".qcp": "audio/qcelp",
+ ".smv": "audio/SMV",
+ ".koz": "audio/vnd.audikoz",
+ ".uva": "audio/vnd.dece.audio",
+ ".uvva": "audio/vnd.dece.audio",
+ ".eol": "audio/vnd.digital-winds",
+ ".mlp": "audio/vnd.dolby.mlp",
+ ".dts": "audio/vnd.dts",
+ ".dtshd": "audio/vnd.dts.hd",
+ ".plj": "audio/vnd.everad.plj",
+ ".lvp": "audio/vnd.lucent.voice",
+ ".pya": "audio/vnd.ms-playready.media.pya",
+ ".vbk": "audio/vnd.nortel.vbk",
+ ".ecelp4800": "audio/vnd.nuera.ecelp4800",
+ ".ecelp7470": "audio/vnd.nuera.ecelp7470",
+ ".ecelp9600": "audio/vnd.nuera.ecelp9600",
+ ".rip": "audio/vnd.rip",
+ ".smp3": "audio/vnd.sealedmedia.softseal.mpeg",
+ ".smp": "audio/vnd.sealedmedia.softseal.mpeg",
+ ".s1m": "audio/vnd.sealedmedia.softseal.mpeg",
+ ".ttc": "font/collection",
+ ".otf": "font/otf",
+ ".ttf": "font/ttf",
+ ".woff": "font/woff",
+ ".woff2": "font/woff2",
+ ".bmp": "image/bmp",
+ ".dib": "image/bmp",
+ ".cgm": "image/cgm",
+ ".drle": "image/dicom-rle",
+ ".emf": "image/emf",
+ ".fits": "image/fits",
+ ".fit": "image/fits",
+ ".fts": "image/fits",
+ ".gif": "image/gif",
+ ".ief": "image/ief",
+ ".jls": "image/jls",
+ ".jp2": "image/jp2",
+ ".jpg2": "image/jp2",
+ ".jpg": "image/jpeg",
+ ".jpeg": "image/jpeg",
+ ".jpe": "image/jpeg",
+ ".jfif": "image/jpeg",
+ ".jpm": "image/jpm",
+ ".jpgm": "image/jpm",
+ ".jpx": "image/jpx",
+ ".jpf": "image/jpx",
+ ".ktx": "image/ktx",
+ ".png": "image/png",
+ ".btif": "image/prs.btif",
+ ".btf": "image/prs.btif",
+ ".pti": "image/prs.pti",
+ ".svg": "image/svg+xml",
+ ".svgz": "image/svg+xml",
+ ".t38": "image/t38",
+ ".tiff": "image/tiff",
+ ".tif": "image/tiff",
+ ".tfx": "image/tiff-fx",
+ ".psd": "image/vnd.adobe.photoshop",
+ ".azv": "image/vnd.airzip.accelerator.azv",
+ ".uvi": "image/vnd.dece.graphic",
+ ".uvvi": "image/vnd.dece.graphic",
+ ".uvg": "image/vnd.dece.graphic",
+ ".uvvg": "image/vnd.dece.graphic",
+ ".djvu": "image/vnd.djvu",
+ ".djv": "image/vnd.djvu",
+ ".dwg": "image/vnd.dwg",
+ ".dxf": "image/vnd.dxf",
+ ".fbs": "image/vnd.fastbidsheet",
+ ".fpx": "image/vnd.fpx",
+ ".fst": "image/vnd.fst",
+ ".mmr": "image/vnd.fujixerox.edmics-mmr",
+ ".rlc": "image/vnd.fujixerox.edmics-rlc",
+ ".pgb": "image/vnd.globalgraphics.pgb",
+ ".ico": "image/vnd.microsoft.icon",
+ ".apng": "image/vnd.mozilla.apng",
+ ".mdi": "image/vnd.ms-modi",
+ ".hdr": "image/vnd.radiance",
+ ".rgbe": "image/vnd.radiance",
+ ".xyze": "image/vnd.radiance",
+ ".spng": "image/vnd.sealed.png",
+ ".spn": "image/vnd.sealed.png",
+ ".s1n": "image/vnd.sealed.png",
+ ".sgif": "image/vnd.sealedmedia.softseal.gif",
+ ".sgi": "image/vnd.sealedmedia.softseal.gif",
+ ".s1g": "image/vnd.sealedmedia.softseal.gif",
+ ".sjpg": "image/vnd.sealedmedia.softseal.jpg",
+ ".sjp": "image/vnd.sealedmedia.softseal.jpg",
+ ".s1j": "image/vnd.sealedmedia.softseal.jpg",
+ ".tap": "image/vnd.tencent.tap",
+ ".vtf": "image/vnd.valve.source.texture",
+ ".wbmp": "image/vnd.wap.wbmp",
+ ".xif": "image/vnd.xiff",
+ ".pcx": "image/vnd.zbrush.pcx",
+ ".wmf": "image/wmf",
+ ".u8msg": "message/global",
+ ".u8dsn": "message/global-delivery-status",
+ ".u8mdn": "message/global-disposition-notification",
+ ".u8hdr": "message/global-headers",
+ ".eml": "message/rfc822",
+ ".mail": "message/rfc822",
+ ".art": "message/rfc822",
+ ".gltf": "model/gltf+json",
+ ".igs": "model/iges",
+ ".iges": "model/iges",
+ ".msh": "model/mesh",
+ ".mesh": "model/mesh",
+ ".silo": "model/mesh",
+ ".dae": "model/vnd.collada+xml",
+ ".dwf": "model/vnd.dwf",
+ ".gdl": "model/vnd.gdl",
+ ".gsm": "model/vnd.gdl",
+ ".win": "model/vnd.gdl",
+ ".dor": "model/vnd.gdl",
+ ".lmp": "model/vnd.gdl",
+ ".rsm": "model/vnd.gdl",
+ ".msm": "model/vnd.gdl",
+ ".ism": "model/vnd.gdl",
+ ".gtw": "model/vnd.gtw",
+ ".moml": "model/vnd.moml+xml",
+ ".mts": "model/vnd.mts",
+ ".ogex": "model/vnd.opengex",
+ ".x_b": "model/vnd.parasolid.transmit.binary",
+ ".xmt_bin": "model/vnd.parasolid.transmit.binary",
+ ".x_t": "model/vnd.parasolid.transmit.text",
+ ".xmt_txt": "model/vnd.parasolid.transmit.text",
+ ".bsp": "model/vnd.valve.source.compiled-map",
+ ".vtu": "model/vnd.vtu",
+ ".wrl": "model/vrml",
+ ".vrml": "model/vrml",
+ ".x3db": "model/x3d+xml",
+ ".x3dv": "model/x3d-vrml",
+ ".x3dvz": "model/x3d-vrml",
+ ".bmed": "multipart/vnd.bint.med-plus",
+ ".vpm": "multipart/voice-message",
+ ".appcache": "text/cache-manifest",
+ ".manifest": "text/cache-manifest",
+ ".ics": "text/calendar",
+ ".ifb": "text/calendar",
+ ".css": "text/css",
+ ".csv": "text/csv",
+ ".csvs": "text/csv-schema",
+ ".soa": "text/dns",
+ ".zone": "text/dns",
+ ".html": "text/html",
+ ".htm": "text/html",
+ ".cnd": "text/jcr-cnd",
+ ".markdown": "text/markdown",
+ ".md": "text/markdown",
+ ".miz": "text/mizar",
+ ".n3": "text/n3",
+ ".txt": "text/plain",
+ ".asc": "text/plain",
+ ".text": "text/plain",
+ ".pm": "text/plain",
+ ".el": "text/plain",
+ ".c": "text/plain",
+ ".h": "text/plain",
+ ".cc": "text/plain",
+ ".hh": "text/plain",
+ ".cxx": "text/plain",
+ ".hxx": "text/plain",
+ ".f90": "text/plain",
+ ".conf": "text/plain",
+ ".log": "text/plain",
+ ".provn": "text/provenance-notation",
+ ".rst": "text/prs.fallenstein.rst",
+ ".tag": "text/prs.lines.tag",
+ ".dsc": "text/prs.lines.tag",
+ ".rtx": "text/richtext",
+ ".sgml": "text/sgml",
+ ".sgm": "text/sgml",
+ ".tsv": "text/tab-separated-values",
+ ".t": "text/troff",
+ ".tr": "text/troff",
+ ".roff": "text/troff",
+ ".ttl": "text/turtle",
+ ".uris": "text/uri-list",
+ ".uri": "text/uri-list",
+ ".vcf": "text/vcard",
+ ".vcard": "text/vcard",
+ ".a": "text/vnd.a",
+ ".abc": "text/vnd.abc",
+ ".ascii": "text/vnd.ascii-art",
+ ".copyright": "text/vnd.debian.copyright",
+ ".dms": "text/vnd.DMClientScript",
+ ".sub": "text/vnd.dvb.subtitle",
+ ".jtd": "text/vnd.esmertec.theme-descriptor",
+ ".fly": "text/vnd.fly",
+ ".flx": "text/vnd.fmi.flexstor",
+ ".gv": "text/vnd.graphviz",
+ ".dot": "text/vnd.graphviz",
+ ".3dml": "text/vnd.in3d.3dml",
+ ".3dm": "text/vnd.in3d.3dml",
+ ".spot": "text/vnd.in3d.spot",
+ ".spo": "text/vnd.in3d.spot",
+ ".mpf": "text/vnd.ms-mediapackage",
+ ".ccc": "text/vnd.net2phone.commcenter.command",
+ ".uric": "text/vnd.si.uricatalogue",
+ ".jad": "text/vnd.sun.j2me.app-descriptor",
+ ".ts": "text/vnd.trolltech.linguist",
+ ".si": "text/vnd.wap.si",
+ ".sl": "text/vnd.wap.sl",
+ ".wml": "text/vnd.wap.wml",
+ ".wmls": "text/vnd.wap.wmlscript",
+ ".xml": "text/xml",
+ ".xsd": "text/xml",
+ ".rng": "text/xml",
+ ".ent": "text/xml-external-parsed-entity",
+ ".3gp": "video/3gpp",
+ ".3gpp": "video/3gpp",
+ ".3g2": "video/3gpp2",
+ ".3gpp2": "video/3gpp2",
+ ".m4s": "video/iso.segment",
+ ".mj2": "video/mj2",
+ ".mjp2": "video/mj2",
+ ".mp4": "video/mp4",
+ ".mpg4": "video/mp4",
+ ".m4v": "video/mp4",
+ ".mpeg": "video/mpeg",
+ ".mpg": "video/mpeg",
+ ".mpe": "video/mpeg",
+ ".m1v": "video/mpeg",
+ ".m2v": "video/mpeg",
+ ".ogv": "video/ogg",
+ ".mov": "video/quicktime",
+ ".qt": "video/quicktime",
+ ".uvh": "video/vnd.dece.hd",
+ ".uvvh": "video/vnd.dece.hd",
+ ".uvm": "video/vnd.dece.mobile",
+ ".uvvm": "video/vnd.dece.mobile",
+ ".uvu": "video/vnd.dece.mp4",
+ ".uvvu": "video/vnd.dece.mp4",
+ ".uvp": "video/vnd.dece.pd",
+ ".uvvp": "video/vnd.dece.pd",
+ ".uvs": "video/vnd.dece.sd",
+ ".uvvs": "video/vnd.dece.sd",
+ ".uvv": "video/vnd.dece.video",
+ ".uvvv": "video/vnd.dece.video",
+ ".dvb": "video/vnd.dvb.file",
+ ".fvt": "video/vnd.fvt",
+ ".mxu": "video/vnd.mpegurl",
+ ".m4u": "video/vnd.mpegurl",
+ ".pyv": "video/vnd.ms-playready.media.pyv",
+ ".nim": "video/vnd.nokia.interleaved-multimedia",
+ ".bik": "video/vnd.radgamettools.bink",
+ ".bk2": "video/vnd.radgamettools.bink",
+ ".smk": "video/vnd.radgamettools.smacker",
+ ".smpg": "video/vnd.sealed.mpeg1",
+ ".s11": "video/vnd.sealed.mpeg1",
+ ".s14": "video/vnd.sealed.mpeg4",
+ ".sswf": "video/vnd.sealed.swf",
+ ".ssw": "video/vnd.sealed.swf",
+ ".smov": "video/vnd.sealedmedia.softseal.mov",
+ ".smo": "video/vnd.sealedmedia.softseal.mov",
+ ".s1q": "video/vnd.sealedmedia.softseal.mov",
+ ".viv": "video/vnd.vivo",
+ ".cpt": "application/mac-compactpro",
+ ".metalink": "application/metalink+xml",
+ ".owx": "application/owl+xml",
+ ".rss": "application/rss+xml",
+ ".apk": "application/vnd.android.package-archive",
+ ".dd": "application/vnd.oma.dd+xml",
+ ".dcf": "application/vnd.oma.drm.content",
+ ".o4a": "application/vnd.oma.drm.dcf",
+ ".o4v": "application/vnd.oma.drm.dcf",
+ ".dm": "application/vnd.oma.drm.message",
+ ".drc": "application/vnd.oma.drm.rights+wbxml",
+ ".dr": "application/vnd.oma.drm.rights+xml",
+ ".sxc": "application/vnd.sun.xml.calc",
+ ".stc": "application/vnd.sun.xml.calc.template",
+ ".sxd": "application/vnd.sun.xml.draw",
+ ".std": "application/vnd.sun.xml.draw.template",
+ ".sxi": "application/vnd.sun.xml.impress",
+ ".sti": "application/vnd.sun.xml.impress.template",
+ ".sxm": "application/vnd.sun.xml.math",
+ ".sxw": "application/vnd.sun.xml.writer",
+ ".sxg": "application/vnd.sun.xml.writer.global",
+ ".stw": "application/vnd.sun.xml.writer.template",
+ ".sis": "application/vnd.symbian.install",
+ ".mms": "application/vnd.wap.mms-message",
+ ".anx": "application/x-annodex",
+ ".bcpio": "application/x-bcpio",
+ ".torrent": "application/x-bittorrent",
+ ".bz2": "application/x-bzip2",
+ ".vcd": "application/x-cdlink",
+ ".crx": "application/x-chrome-extension",
+ ".cpio": "application/x-cpio",
+ ".csh": "application/x-csh",
+ ".dcr": "application/x-director",
+ ".dir": "application/x-director",
+ ".dxr": "application/x-director",
+ ".dvi": "application/x-dvi",
+ ".spl": "application/x-futuresplash",
+ ".gtar": "application/x-gtar",
+ ".hdf": "application/x-hdf",
+ ".jar": "application/x-java-archive",
+ ".jnlp": "application/x-java-jnlp-file",
+ ".pack": "application/x-java-pack200",
+ ".kil": "application/x-killustrator",
+ ".latex": "application/x-latex",
+ ".nc": "application/x-netcdf",
+ ".cdf": "application/x-netcdf",
+ ".pl": "application/x-perl",
+ ".rpm": "application/x-rpm",
+ ".sh": "application/x-sh",
+ ".shar": "application/x-shar",
+ ".sit": "application/x-stuffit",
+ ".sv4cpio": "application/x-sv4cpio",
+ ".sv4crc": "application/x-sv4crc",
+ ".tar": "application/x-tar",
+ ".tcl": "application/x-tcl",
+ ".tex": "application/x-tex",
+ ".texinfo": "application/x-texinfo",
+ ".texi": "application/x-texinfo",
+ ".man": "application/x-troff-man",
+ ".1": "application/x-troff-man",
+ ".2": "application/x-troff-man",
+ ".3": "application/x-troff-man",
+ ".4": "application/x-troff-man",
+ ".5": "application/x-troff-man",
+ ".6": "application/x-troff-man",
+ ".7": "application/x-troff-man",
+ ".8": "application/x-troff-man",
+ ".me": "application/x-troff-me",
+ ".ms": "application/x-troff-ms",
+ ".ustar": "application/x-ustar",
+ ".src": "application/x-wais-source",
+ ".xpi": "application/x-xpinstall",
+ ".xspf": "application/x-xspf+xml",
+ ".xz": "application/x-xz",
+ ".mid": "audio/midi",
+ ".midi": "audio/midi",
+ ".kar": "audio/midi",
+ ".aif": "audio/x-aiff",
+ ".aiff": "audio/x-aiff",
+ ".aifc": "audio/x-aiff",
+ ".axa": "audio/x-annodex",
+ ".flac": "audio/x-flac",
+ ".mka": "audio/x-matroska",
+ ".mod": "audio/x-mod",
+ ".ult": "audio/x-mod",
+ ".uni": "audio/x-mod",
+ ".m15": "audio/x-mod",
+ ".mtm": "audio/x-mod",
+ ".669": "audio/x-mod",
+ ".med": "audio/x-mod",
+ ".m3u": "audio/x-mpegurl",
+ ".wax": "audio/x-ms-wax",
+ ".wma": "audio/x-ms-wma",
+ ".ram": "audio/x-pn-realaudio",
+ ".rm": "audio/x-pn-realaudio",
+ ".ra": "audio/x-realaudio",
+ ".s3m": "audio/x-s3m",
+ ".stm": "audio/x-stm",
+ ".wav": "audio/x-wav",
+ ".xyz": "chemical/x-xyz",
+ ".webp": "image/webp",
+ ".ras": "image/x-cmu-raster",
+ ".pnm": "image/x-portable-anymap",
+ ".pbm": "image/x-portable-bitmap",
+ ".pgm": "image/x-portable-graymap",
+ ".ppm": "image/x-portable-pixmap",
+ ".rgb": "image/x-rgb",
+ ".tga": "image/x-targa",
+ ".xbm": "image/x-xbitmap",
+ ".xpm": "image/x-xpixmap",
+ ".xwd": "image/x-xwindowdump",
+ ".sandboxed": "text/html-sandboxed",
+ ".pod": "text/x-pod",
+ ".etx": "text/x-setext",
+ ".webm": "video/webm",
+ ".axv": "video/x-annodex",
+ ".flv": "video/x-flv",
+ ".fxm": "video/x-javafx",
+ ".mkv": "video/x-matroska",
+ ".mk3d": "video/x-matroska-3d",
+ ".asx": "video/x-ms-asf",
+ ".wm": "video/x-ms-wm",
+ ".wmv": "video/x-ms-wmv",
+ ".wmx": "video/x-ms-wmx",
+ ".wvx": "video/x-ms-wvx",
+ ".avi": "video/x-msvideo",
+ ".movie": "video/x-sgi-movie",
+ ".ice": "x-conference/x-cooltalk",
+ ".sisx": "x-epoc/x-sisx-app",
+ }
+ for ext, name := range mimeTypes {
+ if err := mime.AddExtensionType(ext, name); err != nil {
+ panic(err)
+ }
+ }
+}
diff --git a/swarm/api/http/server.go b/swarm/api/http/server.go
index 87ef05baa..5ec69373d 100644
--- a/swarm/api/http/server.go
+++ b/swarm/api/http/server.go
@@ -201,6 +201,13 @@ func (s *Server) HandleBzzGet(w http.ResponseWriter, r *http.Request) {
defer reader.Close()
w.Header().Set("Content-Type", "application/x-tar")
+
+ fileName := uri.Addr
+ if found := path.Base(uri.Path); found != "" && found != "." && found != "/" {
+ fileName = found
+ }
+ w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s.tar\"", fileName))
+
w.WriteHeader(http.StatusOK)
io.Copy(w, reader)
return
@@ -616,7 +623,7 @@ func (s *Server) HandleGetResource(w http.ResponseWriter, r *http.Request) {
// All ok, serve the retrieved update
log.Debug("Found update", "view", view.Hex(), "ruid", ruid)
- w.Header().Set("Content-Type", "application/octet-stream")
+ w.Header().Set("Content-Type", api.MimeOctetStream)
http.ServeContent(w, r, "", time.Now(), bytes.NewReader(data))
}
@@ -690,11 +697,9 @@ func (s *Server) HandleGet(w http.ResponseWriter, r *http.Request) {
case uri.Raw():
// allow the request to overwrite the content type using a query
// parameter
- contentType := "application/octet-stream"
if typ := r.URL.Query().Get("content_type"); typ != "" {
- contentType = typ
+ w.Header().Set("Content-Type", typ)
}
- w.Header().Set("Content-Type", contentType)
http.ServeContent(w, r, "", time.Now(), reader)
case uri.Hash():
w.Header().Set("Content-Type", "text/plain")
@@ -850,8 +855,17 @@ func (s *Server) HandleGetFile(w http.ResponseWriter, r *http.Request) {
return
}
- w.Header().Set("Content-Type", contentType)
- http.ServeContent(w, r, "", time.Now(), newBufferedReadSeeker(reader, getFileBufferSize))
+ if contentType != "" {
+ w.Header().Set("Content-Type", contentType)
+ }
+
+ fileName := uri.Addr
+ if found := path.Base(uri.Path); found != "" && found != "." && found != "/" {
+ fileName = found
+ }
+ w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s\"", fileName))
+
+ http.ServeContent(w, r, fileName, time.Now(), newBufferedReadSeeker(reader, getFileBufferSize))
}
// The size of buffer used for bufio.Reader on LazyChunkReader passed to
diff --git a/swarm/api/http/server_test.go b/swarm/api/http/server_test.go
index 8ba4e55c3..817519a30 100644
--- a/swarm/api/http/server_test.go
+++ b/swarm/api/http/server_test.go
@@ -32,6 +32,7 @@ import (
"net/http"
"net/url"
"os"
+ "path"
"strconv"
"strings"
"testing"
@@ -764,6 +765,16 @@ func testBzzTar(encrypted bool, t *testing.T) {
}
defer resp2.Body.Close()
+ if h := resp2.Header.Get("Content-Type"); h != "application/x-tar" {
+ t.Fatalf("Content-Type header expected: application/x-tar, got: %s", h)
+ }
+
+ expectedFileName := string(swarmHash) + ".tar"
+ expectedContentDisposition := fmt.Sprintf("inline; filename=\"%s\"", expectedFileName)
+ if h := resp2.Header.Get("Content-Disposition"); h != expectedContentDisposition {
+ t.Fatalf("Content-Disposition header expected: %s, got: %s", expectedContentDisposition, h)
+ }
+
file, err := ioutil.TempFile("", "swarm-downloaded-tarball")
if err != nil {
t.Fatal(err)
@@ -1099,7 +1110,7 @@ func TestModify(t *testing.T) {
res, body := httpDo(testCase.method, testCase.uri, reqBody, testCase.headers, testCase.verbose, t)
if res.StatusCode != testCase.expectedStatusCode {
- t.Fatalf("expected status code %d but got %d", testCase.expectedStatusCode, res.StatusCode)
+ t.Fatalf("expected status code %d but got %d, %s", testCase.expectedStatusCode, res.StatusCode, body)
}
if testCase.assertResponseBody != "" && !strings.Contains(body, testCase.assertResponseBody) {
t.Log(body)
@@ -1210,19 +1221,25 @@ func TestBzzGetFileWithResolver(t *testing.T) {
hash := common.HexToHash(string(swarmHash))
resolver.hash = &hash
for _, v := range []struct {
- addr string
- path string
- expectedStatusCode int
+ addr string
+ path string
+ expectedStatusCode int
+ expectedContentType string
+ expectedFileName string
}{
{
- addr: string(swarmHash),
- path: fileNames[0],
- expectedStatusCode: http.StatusOK,
+ addr: string(swarmHash),
+ path: fileNames[0],
+ expectedStatusCode: http.StatusOK,
+ expectedContentType: "text/plain",
+ expectedFileName: path.Base(fileNames[0]),
},
{
- addr: "somebogusensname",
- path: fileNames[0],
- expectedStatusCode: http.StatusOK,
+ addr: "somebogusensname",
+ path: fileNames[0],
+ expectedStatusCode: http.StatusOK,
+ expectedContentType: "text/plain",
+ expectedFileName: path.Base(fileNames[0]),
},
} {
req, err := http.NewRequest("GET", fmt.Sprintf(srv.URL+"/bzz:/%s/%s", v.addr, v.path), nil)
@@ -1237,6 +1254,16 @@ func TestBzzGetFileWithResolver(t *testing.T) {
if serverResponse.StatusCode != v.expectedStatusCode {
t.Fatalf("expected %d, got %d", v.expectedStatusCode, serverResponse.StatusCode)
}
+
+ if h := serverResponse.Header.Get("Content-Type"); h != v.expectedContentType {
+ t.Fatalf("Content-Type header expected: %s, got %s", v.expectedContentType, h)
+ }
+
+ expectedContentDisposition := fmt.Sprintf("inline; filename=\"%s\"", v.expectedFileName)
+ if h := serverResponse.Header.Get("Content-Disposition"); h != expectedContentDisposition {
+ t.Fatalf("Content-Disposition header expected: %s, got: %s", expectedContentDisposition, h)
+ }
+
}
}