Skip to content

Commit fb56cee

Browse files
committed
Web extension resources return incorrect MIME type
Contributes to #468 Use Apache Tika to detect mimetype
1 parent 72706d1 commit fb56cee

File tree

4 files changed

+113
-12
lines changed

4 files changed

+113
-12
lines changed

server/build.gradle

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ def versions = [
2929
junit: '5.7.1',
3030
testcontainers: '1.15.2',
3131
jackson: '2.12.5',
32-
woodstox: '6.2.4'
32+
woodstox: '6.2.4',
33+
tika: '2.4.0'
3334
]
3435
ext['junit-jupiter.version'] = versions.junit
3536
sourceCompatibility = versions.java
@@ -66,6 +67,7 @@ dependencies {
6667
implementation "org.springframework.security:spring-security-oauth2-jose"
6768
implementation "org.springframework.session:spring-session-jdbc"
6869
implementation "org.flywaydb:flyway-core:${versions.flyway}"
70+
implementation "org.apache.tika:tika-core:${versions.tika}"
6971
implementation "com.google.cloud:google-cloud-storage:${versions.gcloud}"
7072
implementation "com.azure:azure-storage-blob:${versions.azure}"
7173
implementation "io.springfox:springfox-boot-starter:${versions.springfox}"

server/src/main/java/org/eclipse/openvsx/storage/StorageUtil.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
package org.eclipse.openvsx.storage;
1212

13+
import org.apache.tika.Tika;
1314
import org.springframework.http.CacheControl;
1415
import org.springframework.http.MediaType;
1516

@@ -20,7 +21,8 @@ class StorageUtil {
2021

2122
private StorageUtil(){}
2223

23-
static MediaType getFileType(String fileName) {
24+
@Deprecated
25+
static MediaType getFileTypeDeprecated(String fileName) {
2426
if (fileName.endsWith(".vsix"))
2527
return MediaType.APPLICATION_OCTET_STREAM;
2628
if (fileName.endsWith(".json"))
@@ -31,6 +33,12 @@ static MediaType getFileType(String fileName) {
3133
return MediaType.TEXT_PLAIN;
3234
}
3335

36+
static MediaType getFileType(String fileName) {
37+
var tika = new Tika();
38+
var contentType = tika.detect(fileName); // returns 'application/octet-stream' by default
39+
return MediaType.parseMediaType(contentType);
40+
}
41+
3442
static CacheControl getCacheControl(String fileName) {
3543
// Files are requested with a version string in the URL, so their content cannot change
3644
return CacheControl.maxAge(30, TimeUnit.DAYS).cachePublic();
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<mime-info>
3+
<mime-type type="application/vsix">
4+
<_comment>VSIX package</_comment>
5+
<glob pattern="*.vsix"/>
6+
</mime-type>
7+
</mime-info>

server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAdapterTest.java

Lines changed: 94 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
import com.fasterxml.jackson.core.JsonProcessingException;
2828
import com.fasterxml.jackson.databind.ObjectMapper;
29-
import com.google.common.base.Strings;
3029
import com.google.common.collect.Lists;
3130
import com.google.common.io.CharStreams;
3231

@@ -50,7 +49,6 @@
5049
import org.eclipse.openvsx.storage.GoogleCloudStorageService;
5150
import org.eclipse.openvsx.storage.StorageUtilService;
5251
import org.eclipse.openvsx.util.TargetPlatform;
53-
import org.eclipse.openvsx.util.VersionUtil;
5452
import org.elasticsearch.search.aggregations.Aggregations;
5553
import org.junit.jupiter.api.Test;
5654
import org.mockito.Mockito;
@@ -65,6 +63,7 @@
6563
import org.springframework.data.elasticsearch.core.SearchHitsImpl;
6664
import org.springframework.data.elasticsearch.core.TotalHitsRelation;
6765
import org.springframework.data.util.Streamable;
66+
import org.springframework.http.HttpHeaders;
6867
import org.springframework.http.MediaType;
6968
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
7069
import org.springframework.test.web.servlet.MockMvc;
@@ -223,6 +222,7 @@ public void testAsset() throws Exception {
223222
mockMvc.perform(get("/vscode/asset/{namespace}/{extensionName}/{version}/{assetType}",
224223
"redhat", "vscode-yaml", "0.5.2", "Microsoft.VisualStudio.Code.Manifest"))
225224
.andExpect(status().isOk())
225+
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))
226226
.andExpect(content().string("{\"foo\":\"bar\"}"));
227227
}
228228

@@ -233,6 +233,7 @@ public void testAssetMacOSX() throws Exception {
233233
mockMvc.perform(get("/vscode/asset/{namespace}/{extensionName}/{version}/{assetType}?targetPlatform={target}",
234234
"redhat", "vscode-yaml", "0.5.2", "Microsoft.VisualStudio.Code.Manifest", target))
235235
.andExpect(status().isOk())
236+
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))
236237
.andExpect(content().string("{\"foo\":\"bar\",\"target\":\"darwin-arm64\"}"));
237238
}
238239

@@ -246,6 +247,15 @@ public void testAssetNotFound() throws Exception {
246247
.andExpect(status().isNotFound());
247248
}
248249

250+
@Test
251+
public void testAssetVsixPackage() throws Exception {
252+
mockExtension();
253+
mockMvc.perform(get("/vscode/asset/{namespace}/{extensionName}/{version}/{assetType}",
254+
"redhat", "vscode-yaml", "0.5.2", "Microsoft.VisualStudio.Services.VSIXPackage"))
255+
.andExpect(status().isOk())
256+
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, "application/vsix"));
257+
}
258+
249259
@Test
250260
public void testGetItem() throws Exception {
251261
mockMvc.perform(get("/vscode/item?itemName={itemName}", "redhat.vscode-yaml"))
@@ -254,14 +264,55 @@ public void testGetItem() throws Exception {
254264
}
255265

256266
@Test
257-
public void testWebResourceAsset() throws Exception {
267+
public void testPngWebResourceAsset() throws Exception {
258268
mockExtension();
259269
mockMvc.perform(get("/vscode/asset/{namespace}/{extensionName}/{version}/{assetType}",
260270
"redhat", "vscode-yaml", "0.5.2", "Microsoft.VisualStudio.Code.WebResources/extension/img/logo.png"))
261271
.andExpect(status().isOk())
272+
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaType.IMAGE_PNG_VALUE))
262273
.andExpect(content().string("logo.png"));
263274
}
264275

276+
@Test
277+
public void testCssWebResourceAsset() throws Exception {
278+
mockExtension();
279+
mockMvc.perform(get("/vscode/asset/{namespace}/{extensionName}/{version}/{assetType}",
280+
"redhat", "vscode-yaml", "0.5.2", "Microsoft.VisualStudio.Code.WebResources/extension/public/static/css/main.css"))
281+
.andExpect(status().isOk())
282+
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, "text/css"))
283+
.andExpect(content().string(".main { margin: 0 auto; }"));
284+
}
285+
286+
@Test
287+
public void testChunkCssWebResourceAsset() throws Exception {
288+
mockExtension();
289+
mockMvc.perform(get("/vscode/asset/{namespace}/{extensionName}/{version}/{assetType}",
290+
"redhat", "vscode-yaml", "0.5.2", "Microsoft.VisualStudio.Code.WebResources/extension/public/static/css/main.9cab4879.chunk.css"))
291+
.andExpect(status().isOk())
292+
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, "text/css"))
293+
.andExpect(content().string(".root { margin: 0 auto; }"));
294+
}
295+
296+
@Test
297+
public void testJsWebResourceAsset() throws Exception {
298+
mockExtension();
299+
mockMvc.perform(get("/vscode/asset/{namespace}/{extensionName}/{version}/{assetType}",
300+
"redhat", "vscode-yaml", "0.5.2", "Microsoft.VisualStudio.Code.WebResources/extension/public/static/js/main.js"))
301+
.andExpect(status().isOk())
302+
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, "application/javascript"))
303+
.andExpect(content().string("() => { console.log('main'); }"));
304+
}
305+
306+
@Test
307+
public void testChunkJsWebResourceAsset() throws Exception {
308+
mockExtension();
309+
mockMvc.perform(get("/vscode/asset/{namespace}/{extensionName}/{version}/{assetType}",
310+
"redhat", "vscode-yaml", "0.5.2", "Microsoft.VisualStudio.Code.WebResources/extension/public/static/js/main.34d01954.chunk.js"))
311+
.andExpect(status().isOk())
312+
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, "application/javascript"))
313+
.andExpect(content().string("() => { console.log('js'); }"));
314+
}
315+
265316
@Test
266317
public void testNotWebResourceAsset() throws Exception {
267318
mockExtension();
@@ -502,6 +553,7 @@ private ExtensionVersion mockExtension(String targetPlatform) throws JsonProcess
502553
extension.setAverageRating(3.0);
503554
extension.setNamespace(namespace);
504555
var extVersion = new ExtensionVersion();
556+
extVersion.setExtension(extension);
505557
extension.getVersions().add(extVersion);
506558
extVersion.setTargetPlatform(targetPlatform);
507559
extVersion.setVersion("0.5.2");
@@ -573,14 +625,46 @@ private ExtensionVersion mockExtension(String targetPlatform) throws JsonProcess
573625
iconFile.setStorageType(FileResource.STORAGE_DB);
574626
Mockito.when(repositories.findFileByType(extVersion, FileResource.ICON))
575627
.thenReturn(iconFile);
576-
var webResourceFile = new FileResource();
577-
webResourceFile.setExtension(extVersion);
578-
webResourceFile.setName("extension/img/logo.png");
579-
webResourceFile.setType(FileResource.RESOURCE);
580-
webResourceFile.setStorageType(STORAGE_DB);
581-
webResourceFile.setContent("logo.png".getBytes());
628+
var pngWebResourceFile = new FileResource();
629+
pngWebResourceFile.setExtension(extVersion);
630+
pngWebResourceFile.setName("extension/img/logo.png");
631+
pngWebResourceFile.setType(FileResource.RESOURCE);
632+
pngWebResourceFile.setStorageType(STORAGE_DB);
633+
pngWebResourceFile.setContent("logo.png".getBytes());
582634
Mockito.when(repositories.findFileByTypeAndName(extVersion, FileResource.RESOURCE, "extension/img/logo.png"))
583-
.thenReturn(webResourceFile);
635+
.thenReturn(pngWebResourceFile);
636+
var jsWebResourceFile = new FileResource();
637+
jsWebResourceFile.setExtension(extVersion);
638+
jsWebResourceFile.setName("extension/public/static/js/main.js");
639+
jsWebResourceFile.setType(FileResource.RESOURCE);
640+
jsWebResourceFile.setStorageType(STORAGE_DB);
641+
jsWebResourceFile.setContent("() => { console.log('main'); }".getBytes());
642+
Mockito.when(repositories.findFileByTypeAndName(extVersion, FileResource.RESOURCE, "extension/public/static/js/main.js"))
643+
.thenReturn(jsWebResourceFile);
644+
var jsChunkWebResourceFile = new FileResource();
645+
jsChunkWebResourceFile.setExtension(extVersion);
646+
jsChunkWebResourceFile.setName("extension/public/static/js/main.34d01954.chunk.js");
647+
jsChunkWebResourceFile.setType(FileResource.RESOURCE);
648+
jsChunkWebResourceFile.setStorageType(STORAGE_DB);
649+
jsChunkWebResourceFile.setContent("() => { console.log('js'); }".getBytes());
650+
Mockito.when(repositories.findFileByTypeAndName(extVersion, FileResource.RESOURCE, "extension/public/static/js/main.34d01954.chunk.js"))
651+
.thenReturn(jsChunkWebResourceFile);
652+
var cssWebResourceFile = new FileResource();
653+
cssWebResourceFile.setExtension(extVersion);
654+
cssWebResourceFile.setName("extension/public/static/css/main.css");
655+
cssWebResourceFile.setType(FileResource.RESOURCE);
656+
cssWebResourceFile.setStorageType(STORAGE_DB);
657+
cssWebResourceFile.setContent(".main { margin: 0 auto; }".getBytes());
658+
Mockito.when(repositories.findFileByTypeAndName(extVersion, FileResource.RESOURCE, "extension/public/static/css/main.css"))
659+
.thenReturn(cssWebResourceFile);
660+
var cssChunkWebResourceFile = new FileResource();
661+
cssChunkWebResourceFile.setExtension(extVersion);
662+
cssChunkWebResourceFile.setName("extension/public/static/css/main.9cab4879.chunk.css");
663+
cssChunkWebResourceFile.setType(FileResource.RESOURCE);
664+
cssChunkWebResourceFile.setStorageType(STORAGE_DB);
665+
cssChunkWebResourceFile.setContent(".root { margin: 0 auto; }".getBytes());
666+
Mockito.when(repositories.findFileByTypeAndName(extVersion, FileResource.RESOURCE, "extension/public/static/css/main.9cab4879.chunk.css"))
667+
.thenReturn(cssChunkWebResourceFile);
584668
Mockito.when(repositories.findFilesByType(extVersion, Arrays.asList(FileResource.MANIFEST, FileResource.README, FileResource.LICENSE, FileResource.ICON, FileResource.DOWNLOAD, FileResource.CHANGELOG)))
585669
.thenReturn(Streamable.of(manifestFile, readmeFile, licenseFile, iconFile, extensionFile, changelogFile));
586670
return extVersion;

0 commit comments

Comments
 (0)