WebAssembly InstantiateStreaming Wrong MIME type - javascript

I am attempting to get this tutorial (here: https://www.hellorust.com/demos/add/index.html) to work, and it seems that whatever I do, I cannot get the WebAssembly MDN reserved function to properly work.
So, I followed the instructions on the link above and got an add.wasm file. As far as I can tell this should be fairly simple and should work. After a little digging I found that the newest WebAssembly module is to instantiate streaming - the documentation for which can be found here: (https://developer.mozilla.org/en-US/docs/WebAssembly/Using_the_JavaScript_API).
The MDN example says to do the following:
var importObject = {
imports: { imported_func: arg => console.log(arg) }
};
then
WebAssembly.instantiateStreaming(fetch('simple.wasm'), importObject)
.then(obj => obj.instance.exports.exported_func());
According to MDN the importObject is to unwrap the nested argument. Weird, but OK.
To make this as simple as possible I put the add.wasm file and the js file that would import it in the same directory and then did then following (NOTE: I am using Vue.js, but for anyone familiar with SPA like libraries this should be similar):
window.WebAssembly.instantiateStreaming(fetch('./add.wasm', {
headers: {
"Content-Type": "application/wasm",
},
}), importObject)
.then(obj => {
console.log('inside return obj from WebAssembly initiateStreaming')
obj => obj.instance.exports.exported_func()
})
.catch(error=>{
console.log('there was some error; ', error)
});
The error I get back is:
there was some error; TypeError: "Response has unsupported MIME type"
I've tried not adding the header to the fetch request, using fetch(add.wasm), dropping the window., dropping the importObject entirely and simple logging obj to console. Nothing appears to work.
It may be that I have to add the application/wasm field to webpack somehow if it is not widely supported, but I'm not sure and I haven't seen any examples online.
Does anyone know how to get this to work?
EDIT:
Someone suggested that since this was a fetch request it had to be making the request from a backend server. This made sense to me, so I did the following:
WebAssembly.instantiateStreaming(fetch('http://localhost:8000/files/add.wasm'), importObject)
.then(obj => {
console.log('inside return obj from WebAssembly initiateStreaming')
obj => obj.instance.exports.exported_func()
})
.catch(error=>{
console.log('there was some error; ', error)
});
Where http://localhost:8000/files/{someFile} is a backend route that serves my files (which I made sure to put add.wasm in of course). Unfortunately, I get the same error (i.e. unrecognized MIME type) and I'm not sure why.

Considering you can't change the server to properly return application/wasm for .wasm file requests for any reason, you can work around the issue by changing the way you instantiate the WebAssembly module. Instead of doing this:
WebAssembly.instantiateStreaming(fetch("./add.wasm")).then(obj => /* ... */)
Do this:
const response = await fetch("add.wasm");
const buffer = await response.arrayBuffer();
const obj = await WebAssembly.instantiate(buffer);
obj.instance.exports.exported_func();
Or the equivalent using then() if you cannot use async/await.
In practice, what my workaround does is to avoid calling instantiateStreaming(), which must check the MIME type returned by the server before proceeding (according to this specification). Instead, I call instantiate() passing an ArrayBuffer and avoid the check altogether.

there was some error; TypeError: "Response has unsupported MIME type"
The Web server you are running does not understands/serves a MIME type application/wasm.
You can use a rust based http server, it knows about wasm MIME type.
Installation
Simply use curl
curl -SsL https://cdn.rawgit.com/thecoshman/http/master/install.sh | sh
and execute the downloaded script or you can explorer other ways to do the same at https://crates.io/crates/https.
Running
Please use the downloaded server to server your Web Application(index.html).
e.g
cd ${YOUR_APPS_PATH}
http

A snippet of code for a workaround has been published on the WebAssembly Git here. Unfortunately, this is a workaround, and this defeats the purpose of instantiateStreaming() which is told here to be "a lot more efficient", since the workaround needs an ArrayBuffer that instantiateStreaming() helps to avoid.

James Wilson's "wasm-fractal" project deals with the error, like this:
importScripts("wasm_fractal.js");
delete WebAssembly.instantiateStreaming;
wasmFractal("./wasm_fractal_bg.wasm").then((wasm) => {
// establish connection between wasm and javascript
});
I use the delete WebAssembly.instantiateStreaming; trick myself during development, since my editor's builtin server, serves wasm with the incorrect mime type.

Related

What can cause fetch() to return success, but subsequently accessing the body to throw TypeError or AbortError?

I use fetch for getting some resources from the server. I can see from logs that from time to time obtaining the JSON body fails on "TypeError: Failed to fetch", which is very interesting because this type of error should only happen when the request fails.
The simplified code I use:
const response = await fetch('https://something.com/api')
try {
// below throws TypeError: Failed to fetch in Chrome/Safari
// and AbortError in Firefox
await response.json();
} catch(error) {
console.log('error happened', error);
}
I cannot really find a case when this might happen. I tested a number of plausible scenarios and all failed on the first line of code, i.e. fetch('https:/something.com/api'). I have no idea when this might happen. I also should mention that it happens in modern browsers like Chrome 99, so it is not definitely something like Internet Explorer thing.
I found this useful example and it shows that requests are cancelled when you unload the document. Obviously, cancellation happens on the fetch line but I decided to stop logging these errors when the document is unloaded/hidden. Even when these cases are not logged I can see it happens on visible documents too.
https://github.com/shuding/request-cancellation-test
Cases tested:
Network error - user disconnected from the internet
CORS - missing CORS headers
Obviously, this does not prove anything but people in the comments think that I do something wrong in implementation and do not trust me when I say it indeed happens when obtaining the JSON body. This is the information I am able to log when I catch the error. Most properties are from the Response object.
This is the log captured from visitor using Chrome 100. Firefox does not throw TypeError instead it throws AbortError but it also happens when converting to json.
This looks like a network error that happened after the response HTTP headers have already been received. In such a situation, the fetch method returns success, but subsequent attempts to access the response body may fail.
I managed to trigger this kind of error with this simple server:
#!/usr/bin/env python3
import http.server
import time
class Handler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
self.wfile.write(b'HTTP/1.0 200 OK\r\n')
self.wfile.write(b'Content-type: text/html\r\n')
self.wfile.write(b'\r\n')
self.wfile.write(b"""\
<script type="module">
const f = await fetch("/j");
try {
await f.json();
} catch (e) {
alert(e);
}
</script>
""")
return
self.wfile.write(b'HTTP/1.0 200 OK\r\n')
self.wfile.write(b'Content-encoding: gzip\r\n') # bogus
self.wfile.write(b'Content-type: application/json\r\n')
self.wfile.write(b'\r\n')
self.wfile.write(b'[]')
server = http.server.HTTPServer(('127.0.0.1', 31337), Handler)
server.serve_forever()
It introduces a deliberate framing error when serving the JSON response; the headers indicate that gzip compression is employed, but no compression is actually used. When I open http://127.0.0.1:31337/ in Chromium 100.0.4896.127, it displays the following alert:
TypeError: Failed to fetch
Firefox ESR 91.8.0 displays a marginally more helpful:
TypeError: Decoding failed.
The particular framing error demonstrated above is rather contrived, and I doubt it is exactly the kind that the asker experienced. But the fact that it appeared in the middle of the response body is probably at the heart of the described problem.
The specific pair of error types from the question can be triggered by modifying the server thus:
self.wfile.write(b'HTTP/1.1 200 OK\r\n')
self.wfile.write(b'Content-type: application/json\r\n')
# sic: larger than the actual body length
self.wfile.write(b'Content-length: 2\r\n')
self.wfile.write(b'\r\n')
self.wfile.write(b'[')
This displays the same alert:
TypeError: Failed to fetch
in Chromium (same version), and
AbortError: The operation was aborted.
in Firefox (also same).
As such, a likely cause would be a flaky connection dropped in the middle of receiving the body. The browsers’ errors are not particularly detailed here (and sometimes are downright misleading), so we are resigned to speculate, but this seems like the best guess.
Your error from this line:
const response = await fetch('https://something.com/api')
I removed await response.json() to test, the error still exists.
Please look console tab in DevTool, make sure that you use this setting
These are 2 example errors Failed to fetch:
(async () => {
try {
const response = await fetch('http://my-api.com/example')
await response.json()
} catch (e) {
console.log('e')
console.log(e)
}
})()

Network error: Unexpected token < in JSON at position 0 at new ApolloError

const httpLink = createHttpLink({
uri: 'http://localhost:3090/'
})
const client = new ApolloClient({
link: httpLink,
cache: new InMemoryCache()
})
client.query({
query: gql`
query users {
email
}
`,
})
.then(data => console.log(data))
.catch(error => console.error(error));
This query gives an error when fetching from client-side code but when i execute this query in browser on http://localhost:3090/graphql it fetches data correctly
The graphql endpoint you are posting your queries to is missing the /graphql. So your server probably returns an html document containing the 404 error message that starts with < from <html.... Apollo tries to parse that as the query result and fails to do so.
Check that httpLink is actually localhost:3090/graphql.
Also the syntax of a query is either:
{
users {
email
}
}
or if you want to name the query:
query Users {
users {
email
}
}
For posterity in case someone finds this in the future, another reason you might get this error is if your API is returning something other than JSON.
https://medium.com/programmers-developers/one-simple-apollo-client-debugging-tip-youll-like-7877a97b9c16
I ran into this issue because the content type that was being returned from my API was text/plain rather than application/json. Apollo lets you specify a different body serializer in this case.
https://www.apollographql.com/docs/link/links/rest/
This happens when you do not define the path correctly in the queries files. This is a typechecking error when passing arguments
In my case this error was being thrown due to a trailing slash I had after the API URL. A possible fix would be checking your API URL properly and ensuring it is referencing the correct URL.
In my case, I only had to take out the trailing slash / after the URL.
As #Trixn posted: "So your server probably returns an html document containing the 404 error message that starts with < from <html.... Apollo tries to parse that as the query result and fails to do so."
In my case, I did not put "http://" in the beginning of my URL, and this returned a HTML web page with an error message.
Also check that usually, you should use "https://"
Make sure that you have configured the uri for httpLink and wsLink same.
I have seen this error when I had different uris as below.
httpLink had uri : "http://localhost:4000"
wsLink had uri: "ws://localhost:4000/graphql"
setting httpLink's uri to http://localhost:4000/graphql helped resolving the issue.
In my case, I recieved the same error, when I switched from a create-react-app to a next JS app pointing to port 3000.The react app had some values saved to local storage (tokens). In my case all I had to do, was to clear the local storage for localhost:3000, and I got rid of the error.
In my case, there was a typo in my graphql endpoint url. I had added a few characters to the end by accident. I think it would be good practice to add a catch rule for this situation so it does not break the site, just does not show the content and posts error to console log.

Read critical extensions from https certificate

I want to do custom validation for node https certificate verification. So, in https options I have set rejectUnauthorized property to false.
var httpsOptions = {
...
rejectUnauthorized: false,
...
};
Now even if certificate verification internally fails, request won't fail. I want to handle that part manually. I wanted to handle unhandled critical extension error. My code to do so is,
var req = https.request(httpsOptions, (res) => {
var data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log(data);
});
});
req.on("socket", function () {
req.socket.on('secureConnect', () => {
if(!req.socket.authorized){
if(req.socket.authorizationError === 'unhandled critical extension'){
// Place to verify extensions
}
process.nextTick(() => {req.abort();});
}
});
});
req.write(JSON.stringify(requestObj));
req.end();
The above code works as expected. I can say when unhandled critical extension error occurs. Inside the if condition(Place to verify extensions), I want to see what are all critical extensions that are unhandled. If it didn't match the list I have, I want to abort the request. req.socket has so many properties, so I could not paste here. There is no field in it, which holds those unhandled critical extensions. How to extract the unhandled critical extensions which caused the error?
Note: I have seen some npm packages which could parse ssl certificates like x509 and PKIjs. It gives lots of confusion and I could not find any working example which can solve my problem.
EDIT:
req.socket.getPeerCertificate().raw gives the DER certificate in Buffer format. How to decode it and view those extensions?
First we need to get the server certificate. We can use req.socket.getPeerCertificate() to get it. It returns a JSON object but it won't have extensions section. But, it will be having the entire certificate in buffer format in its raw property.
var certInBuf = req.socket.getPeerCertificate().raw;
There are a lot of packages available to convert this buffer format to pem format. One of them is pemtools
var certInPem = pemtools(certInBuf, 'CERTIFICATE').toString();
Then we can use node-forge to parse the certificate and extract the extensions.
var pki = require('node-forge').pki;
var extensions = pki.certificateFromPem(certInPem).extensions;
Then we can validate the extensions. If not satisfied we can abort the request by calling req.abort()

Need a way to systematically handle errors/exceptions/rejections in a NodeJS project

Background Info
I have a new project I'm working on that will provide multiple different (optional) packages that can be installed, all of which are in addition to the core package (only manual package). The other packages just interact with the core.
The project is just meant to keep track of lists of data (not very specific, I know, but these details aren't needed). The add-on packages determine HOW the lists of data are interacted with. The core package just consists of all the main JS functionality and database models, and authentication. The other packages tie into those.
Lets say you want to just have it as a standard web page, you can install the webui package, which will tie into the core, and create a web app for it
If you want to create an API, you can install the restapi package, which creates the RESTful interface; You can also install the spaui package which will interact with the RESTful interface, which gets the data from the core
These addon packages I will call "facade" packages. All you really need to extrapolate from the above is that the core is a separate package from the facade packages, and it handles the core functionality (Database stuff, authentication, authorization, etc)
Problem
The core can use promises or callbacks, and it returns exceptions for failures, then whatever facade package is used to interact with the core will handle the exceptions/errors (showing an HTTP error page, returning a RESTful error result, etc).
Since the package that handles the errors is different than the package that returns the errors, there needs to be a systematic way of knowing what type of error was returned, so it can be dealt with properly (EG: The webui/restui packages should know if it needs to show a HTTP 500, a HTTP 403, HTTP 409, etc). Obviously of the core just returns new Error('Something broke'), then the facade packages don't really know what type of error it is, unless they have the text saved somewhere and can match it up with an error code.
Question
Whats the best way to handle this? I haven't been able to find anything that accomplishes this exactly how I want..
I eventually started working on my own attempt.. (below)
My Possible Solution (If this is sufficient, just confirm)
I created a new AppError exception type, and instead of returning AppError exceptions with simple strings, you provide an error code which will associate that exception with the error message, error type, etc.
Here is an example usage of the AppError exception:
exports.createThing = ( name, data ) => {
return new Promise( ( res, rej ) => {
if( doesItExist( name ) )
return rej( new AppError( 'document.create.duplicateName' ) )
// Other stuff...
})
}
Now inside the AppError exception method, it takes the code and looks inside a list of exceptions (the code should be the key inside an object of exception data).
Heres an example of what the exception data object for the above exception would contain:
module.exports = {
'document.create.duplicateName': {
type: 'DocumentConflict',
message: 'Failed to create new document',
detail: 'The document name specified already exists, try another one'
}
}
Example Usage: Lets say we try to execute createThing with an already existing name (From within the webui package):
CorePackage.createThing( 'foobar', 'some data' )
.catch( err => {
/*
The err is now an instance of AppError
err.type -> DocumentConflict
err.message -> Failed to create new document
err.detail -> The document name specified already exists, try another one
*/
})
From here, it's as simple as associating the err.type value with a suitable HTTP error code! (which would probably be HTTP 409 Conflict). Obviously these associations can be kept in an object, making it easy to just retrieve the correct error code for any of the error type values returned. Then the text for the error code is right there in err.message and err.detail
This also makes it easy to introduce some type of locale into the application, as the error, as all that needs to be done is to edit the exception data object.
End of post
So if you think my solution above is a sufficient one, and you cant think of any problems, then please say so. Id like to know if it is or if it isn't. Even if you can't think of a proper solution, but you just know the one I created wont work, share that as well.
If you have an alternative solution, then that would work just as well!
Thanks
I think there are two basic ways to approach this:
code property: Create a new \Error object and assign the code property with information about the error. For example:
var err = new Error('Message');
err.code = "DocumentConflict";
Custom error objects. You could have a seperate Error object per error type that you have. For example, rather than having just AppError, you can have DocumentConflict error.
For projects where I am creating a RESTful API, I like to think in terms of error codes. For most projects, the endpoints will return one of the following codes:
400 (Bad Request)
401 (Credentials Error)
403 (Forbidden)
404 (Not Found).
500 (Internal Server Error).
These then become 'standard' types of Error that I pass around the application. A normal Error object is interpretated as an internal server error, so this will always pass 500 to the endpoint.
For example,
CredentialsError = function (message) {
Error.call(this, arguments);
Error.captureStackTrace(this, this.constructor);
this.message = message;
};
util.inherits(CredentialsError, Error);
CredentialsError.prototype.name = "CredentialsError";
And then just return/throw a new CredentialsError("Invalid password") object as necessary. To check the type of object, you can use instanceof. With Express, for example, you can have an error handler similar to the following:
app.use(function(err, req, res, next) {
var status;
if (err instanceof error.FieldError) {
status = 400;
} else if (err instanceof error.CredentialsError) {
status = 401;
/* etc */
} else {
status = 500;
}
if (status !== 500) {
res.status(status).send(JSON.stringify(
err,
null,
4
));
} else {
// for 500, do not output the error!
console.error(err.stack);
res.status(500).send({
message: "Internal Server Error"
});
}
});
It is also worth noting that you can defined your custom error object constructors to take more than just strings. For example, you can pass objects into a BadRequestError constructor to provide field-level error detail.
Now, in most cases, you can just propagate the errors and the response to the endpoint will make sense. However, there are cases where you want to transmute the type of error. For example, if you have a login endpoint, you might do a request to findUserByEmailAddress(). This could return a NotFoundError object, but you want to capture this in the signIn() function and transmute it to a CredentialsError.

Relative URL as hostname in Nock

I need to mock client side HTTP requests. I'm using isomorphic-fetch in the client side and I'm using mocha and nock for testing and mocking. All my client requests are based on relative path. Due to this I'm unable to provide host name for the nock. Is there a work around.
Client side:
fetch('/foo') //hostname: http://localhost:8080
.then(res => res.json())
.then(data => console.log(data))
.catch(e => console.log(e))
Test suite
nock('/')
.get('/foo')
.reply(200, {data: "hello"})
This is failing as I'm not giving the proper hostname for the nock. Am I doing something wrong?
For anyone interested: In my react/webpack project I solved this by prepending the fetch url with 'http://localhost' when NODE_ENV is set to 'test'.
example:
const testing = process.env.NODE_ENV === 'test';
const apiUrl = `${testing ? 'http://localhost' : ''}/api`;
function getStuffFromApi() {
return fetch(apiUrl, ...)
}
This way, in my test I can always use nock like so:
nock('http://localhost')
.get('/api')
.reply(200, {data: 'hello'})
NOTE: When running my tests, NODE_ENV gets set to 'test'
Found a workaround for this. Have a _ajax-setup.js in your test folder
import $ from 'jquery'
$(document).ajaxSend((event, jqxhr, settings) => {
settings.url = 'http://0.0.0.0' + settings.url;
})
The thing to note is that this file has to run First and Only Once. Having it as a file runs only once and _ before it makes it alphabetically first.
Later you can test your code with
nock('http://0.0.0.0')
.get('/foo')
.reply(200, {data: "hello"})
I just asked a similar question on Nock's repository. Apparently this is not supported: https://github.com/pgte/nock/issues/431
For axios I added following to my test file. This helped overcome this limitation.
axios.defaults.baseURL='http://localhost:4000/';
Please note that this is a global variable.
With this the actual code can live with relative URL and there is no need to check testing flag in it.
kudresov's reply to liorbauer's issue describes a Jest-specific workaround.
Put this into __mocks__/isomorphic-fetch.js:
const fetch = require.requireActual('isomorphic-fetch');
module.exports = (url, config) => {
return fetch('https://localhost' + url, config);
};
Jest then automatically replaces all requires (or imports) for this node module.
In other test frameworks you could use something like rewire

Categories

Resources