@@ -15,8 +15,7 @@ class ResourceMapper {
15
15
includeHost = false ,
16
16
defaultContentType = 'application/octet-stream' ,
17
17
indexFilename = 'index.html' ,
18
- overrideTypes = { acl : 'text/turtle' , meta : 'text/turtle' } ,
19
- fileSuffixes = [ '.acl' , '.meta' ]
18
+ overrideTypes = { acl : 'text/turtle' , meta : 'text/turtle' }
20
19
} ) {
21
20
this . _rootUrl = this . _removeTrailingSlash ( rootUrl )
22
21
this . _rootPath = this . _removeTrailingSlash ( rootPath )
@@ -26,7 +25,6 @@ class ResourceMapper {
26
25
this . _types = { ...types , ...overrideTypes }
27
26
this . _indexFilename = indexFilename
28
27
this . _indexContentType = this . _getContentTypeByExtension ( indexFilename )
29
- this . _isControlFile = new RegExp ( `(?:${ fileSuffixes . map ( fs => fs . replace ( '.' , '\\.' ) ) . join ( '|' ) } )$` )
30
28
31
29
// If the host needs to be replaced on every call, pre-split the root URL
32
30
if ( includeHost ) {
@@ -49,7 +47,7 @@ class ResourceMapper {
49
47
: `${ this . _protocol } //${ hostname } ${ this . _port } ${ this . _rootUrl } ${ pathname } `
50
48
}
51
49
52
- // Gets the base file path for the given hostname
50
+ // Gets the base file path for the given host name
53
51
getBaseFilePath ( hostname ) {
54
52
return ! this . _includeHost ? this . _rootPath : `${ this . _rootPath } /${ hostname } `
55
53
}
@@ -68,44 +66,55 @@ class ResourceMapper {
68
66
}
69
67
70
68
// Maps the request for a given resource and representation format to a server file
71
- // When searchIndex is true and the URL ends with a '/', and contentType includes 'text/html' indexFilename will be matched.
69
+ // Will look for an index file if a folder is given and searchIndex is true
72
70
async mapUrlToFile ( { url, contentType, createIfNotExists, searchIndex = true } ) {
73
- let fullFilePath = this . _getFilePath ( url )
74
- let isIndex = searchIndex && fullFilePath . endsWith ( '/' )
75
- let path
76
-
77
- // Append index filename if the URL ends with a '/'
78
- if ( isIndex ) {
79
- if ( createIfNotExists && contentType !== this . _indexContentType ) {
80
- throw new Error ( `Index file needs to have ${ this . _indexContentType } as content type` )
81
- }
82
- if ( contentType && contentType . includes ( this . _indexContentType ) ) {
83
- fullFilePath += this . _indexFilename
84
- }
71
+ // Parse the URL and find the base file path
72
+ const { pathname, hostname } = this . _parseUrl ( url )
73
+ let filePath = `${ this . getBaseFilePath ( hostname ) } ${ decodeURIComponent ( pathname ) } `
74
+ if ( filePath . indexOf ( '/..' ) >= 0 ) {
75
+ throw new Error ( 'Disallowed /.. segment in URL' )
85
76
}
77
+ let isIndex = searchIndex && filePath . endsWith ( '/' )
78
+
86
79
// Create the path for a new file
80
+ let path
87
81
if ( createIfNotExists ) {
88
- path = fullFilePath
82
+ path = filePath
83
+ // Append index filename if needed
84
+ if ( isIndex ) {
85
+ if ( contentType !== this . _indexContentType ) {
86
+ throw new Error ( `Index file needs to have ${ this . _indexContentType } as content type` )
87
+ }
88
+ path += this . _indexFilename
89
+ }
89
90
// If the extension is not correct for the content type, append the correct extension
90
91
if ( searchIndex && this . _getContentTypeByExtension ( path ) !== contentType ) {
91
92
path += `$${ contentType in extensions ? `.${ extensions [ contentType ] [ 0 ] } ` : '.unknown' } `
92
93
}
93
94
// Determine the path of an existing file
94
95
} else {
95
96
// Read all files in the corresponding folder
96
- const filename = fullFilePath . substr ( fullFilePath . lastIndexOf ( '/' ) + 1 )
97
- const folder = fullFilePath . substr ( 0 , fullFilePath . length - filename . length )
97
+ const filename = filePath . substr ( filePath . lastIndexOf ( '/' ) + 1 )
98
+ const folder = filePath . substr ( 0 , filePath . length - filename . length )
98
99
99
100
// Find a file with the same name (minus the dollar extension)
100
- let match = searchIndex ? await this . _getMatchingFile ( folder , filename , isIndex , contentType ) : ''
101
+ let match = ''
102
+ if ( searchIndex ) {
103
+ const files = await this . _readdir ( folder )
104
+ // Search for files with the same name (disregarding a dollar extension)
105
+ if ( ! isIndex ) {
106
+ match = files . find ( f => this . _removeDollarExtension ( f ) === filename )
107
+ // Check if the index file exists
108
+ } else if ( files . includes ( this . _indexFilename ) ) {
109
+ match = this . _indexFilename
110
+ }
111
+ }
112
+ // Error if no match was found (unless URL ends with '/', then fall back to the folder)
101
113
if ( match === undefined ) {
102
- // Error if no match was found,
103
- // unless the URL ends with a '/',
104
- // in that case we fallback to the folder itself.
105
114
if ( isIndex ) {
106
115
match = ''
107
116
} else {
108
- throw new HTTPError ( 404 , `File not found: ${ fullFilePath } ` )
117
+ throw new HTTPError ( 404 , `Resource not found: ${ pathname } ` )
109
118
}
110
119
}
111
120
path = `${ folder } ${ match } `
@@ -115,28 +124,7 @@ class ResourceMapper {
115
124
return { path, contentType : contentType || this . _defaultContentType }
116
125
}
117
126
118
- async _getMatchingFile ( folder , filename , isIndex , contentType ) {
119
- const files = await this . _readdir ( folder )
120
- // Search for files with the same name (disregarding a dollar extension)
121
- if ( ! isIndex ) {
122
- return files . find ( f => this . _removeDollarExtension ( f ) === filename )
123
- // Check if the index file exists
124
- } else if ( files . includes ( this . _indexFilename ) && contentType && contentType . includes ( this . _indexContentType ) ) {
125
- return this . _indexFilename
126
- }
127
- }
128
-
129
- // Determine the full file path corresponding to a URL
130
- _getFilePath ( url ) {
131
- const { pathname, hostname } = this . _parseUrl ( url )
132
- const fullFilePath = `${ this . getBaseFilePath ( hostname ) } ${ decodeURIComponent ( pathname ) } `
133
- if ( fullFilePath . indexOf ( '/..' ) >= 0 ) {
134
- throw new Error ( 'Disallowed /.. segment in URL' )
135
- }
136
- return fullFilePath
137
- }
138
-
139
- // Parses a URL into a hostname and pathname
127
+ // Parses a URL into hostname and pathname
140
128
_parseUrl ( url ) {
141
129
// URL specified as string
142
130
if ( typeof url === 'string' ) {
@@ -157,16 +145,14 @@ class ResourceMapper {
157
145
return extension && this . _types [ extension [ 1 ] . toLowerCase ( ) ] || this . _defaultContentType
158
146
}
159
147
160
- // Removes a possible trailing slash from a path
148
+ // Removes possible trailing slashes from a path
161
149
_removeTrailingSlash ( path ) {
162
- const lastPos = path . length - 1
163
- return lastPos < 0 || path [ lastPos ] !== '/' ? path : path . substr ( 0 , lastPos )
150
+ return path . replace ( / \/ + $ / , '' )
164
151
}
165
152
166
- // Removes everything beyond the dollar sign from a path
153
+ // Removes dollar extensions from files (index$.html becomes index)
167
154
_removeDollarExtension ( path ) {
168
- const dollarPos = path . lastIndexOf ( '$' )
169
- return dollarPos < 0 ? path : path . substr ( 0 , dollarPos )
155
+ return path . replace ( / \$ \. [ ^ $ ] * $ / , '' )
170
156
}
171
157
}
172
158
0 commit comments