-
-
Notifications
You must be signed in to change notification settings - Fork 181
Description
We're having an issue where, especially under Windows, the browser uses very random mimetypes, depending on which application is registered to handle which file extensions. E.g., for a CSV file, the mime type can be text/plain if registered to a text editor, or application/vnd.ms-excel if it is registered to Excel. So in the javascript we correct these mimetypes based on the file extension. The filename itself is not sent to the server, so the server can't do the same logic.
We upload these files using XMLHttpRequest.send(File)
. We set the content type using xhr.setRequestHeader('Content-Type', contentType)
. This works fine in Firefox and Chrome, but not in HtmlUnit.
When doing an XMLHttpRequest.send(File)
request, any Content-Type
header set on the headers from the XMLHttpRequest
object from javascript is ignored and replaced by the ContentType from the BrowserVersion
.
HtmlUnit version: 2.50.0
HTML sample page:
<!DOCTYPE html>
<html>
<head>
<title>File upload test</title>
</head>
<body>
File: <input id="file" type="file"/><button id="submit">Submit</button><br/>
Result: <span id="result">Result goes here</span>
<script>
function onsubmit() {
const fileInput = document.querySelector('#file');
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function()
{
if(xhr.readyState === XMLHttpRequest.DONE) {
const result = document.querySelector('#result');
result.innerText = xhr.responseText;
}
}
xhr.open('PUT', '/doupload');
// We have analyzed the file(name), and we're sure it's actually a text/csv file
xhr.setRequestHeader("Content-type", 'text/csv');
xhr.send(fileInput.files[0]);
}
const submit = document.querySelector('#submit');
submit.addEventListener('click', onsubmit);
</script>
</body>
</html>
Spring Boot controller registered on /doupload
:
@PutMapping("/doupload")
public @ResponseBody String handleUpload(@RequestHeader("Content-type") String contentType) {
return contentType;
}
HtmlUnit test that fails:
@Test
void testUpload() throws FailingHttpStatusCodeException, MalformedURLException, IOException {
WebClient webClient = new WebClient();
// Register a dummy mime type, to make the problem obvious
webClient.getBrowserVersion().registerUploadMimeType("json", "foo/bar");
HtmlPage page = webClient.getPage("http://localhost:8080/upload.html");
HtmlFileInput file = page.querySelector("#file");
// This line doesn't actually do anything for AJAX requests, only for multipart/form-data form submissions
file.setContentType("application/json");
file.setFiles(new File("src/test/resources/test.json"));
HtmlButton submit = page.querySelector("#submit");
submit.click();
// Wait for all AJAX results to finish
webClient.waitForBackgroundJavaScriptStartingBefore(1000);
HtmlElement resultSpan = page.querySelector("#result");
// #result now contains the Content-Type header of the PUT request
// This fails with Expected: text/csv, actual: foo/bar
assertEquals("text/csv", resultSpan.asNormalizedText());
webClient.close();
}
Tracing the code, my guess would be that Blob.fillRequest(...)
should first check if there is already a Content-Type
header present. Although I'm not sure how actual browsers implement this.