Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CORS issue #15

Open
marcperrinp opened this issue Jun 24, 2020 · 13 comments
Open

CORS issue #15

marcperrinp opened this issue Jun 24, 2020 · 13 comments

Comments

@marcperrinp
Copy link

marcperrinp commented Jun 24, 2020

Trying to make an API request from the browser, but I'm getting a CORS error:

Access to fetch at 'https://app.sandbox.nash.io/api/graphql' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

I guess I need to fix this on my side*, but could you just confirm this is the right way to include a header at client login:

const client = new Client(EnvironmentConfiguration.sandbox, {
        headers: {
          "User-Agent": `nashcasino.com`
        }
      })

* : Not sure if there's any way to make this work locally since I'm in HTTP. Probably will just need to add local certificates to be in HTTPS, but will it be enough?

Any tips would be appreciated :)

@marcperrinp
Copy link
Author

Suppose the HTTP vs HTTPS issue was out of the way, unless I'm mistaken I don't see a Access-Control-Allow-Origin: * in the API response, so how can I make API requests from the frontend? I'm afraid the answer is "I can't", no? 😛

@jankjr
Copy link
Contributor

jankjr commented Jun 24, 2020

@marcperrinp it is possible. But you just need to set up a http proxy and direct it at the environment you want to interact with.

@marcperrinp
Copy link
Author

@jankjr I tried in production, which runs on HTTPS of course, and I'm getting the same error..

Are you saying there is a way to bypass the fact that Nash API's response doesn't contain a Access-Control-Allow-Origin: * header?

@jankjr
Copy link
Contributor

jankjr commented Jun 24, 2020

@marcperrinp

I think our CORS headers are restrictive for security reasons. So the browser probably won't let you call our service directly.

But what you can do is run an a small express server to act as a proxy for your browser app, which will remove the CORS restriction as you are calling your own service. It is essentially how our development setup works. Below is an example of now it can be configured for our production environment.

const app = express();
const proxy = require('http-proxy-middleware')

const PROXY_BACKEND = 'https://app.nash.io'

const proxyTimeout = 3600000
const cookieDomainRewrite = { '*': '' }
const onProxyRes = (proxyRes, req, res) => {
  const sc = proxyRes.headers['set-cookie']
  if (Array.isArray(sc)) {
    proxyRes.headers['set-cookie'] = sc.map(sc => {
      return sc
        .split(';')
        .filter(v => v.trim().toLowerCase() !== 'secure')
        .join('; ')
    })
  }
}

app.use(
	proxy('/api/socket', {
	  target: PROXY_BACKEND,
	  changeOrigin: true,
	  ws: true,
	  proxyTimeout,
	  cookieDomainRewrite,
	  onProxyRes,
	})
)

app.use(
	proxy('/api', {
	  target: PROXY_BACKEND,
	  changeOrigin: true,
	  cookieDomainRewrite,
	  onProxyRes,
	})
)
app.listen(3000);

If your proxy is hosted at localhost:3000 then you will need to initialize the SDK like so:

const nash = new Client({
      ...EnvironmentConfiguration.production,
      host: 'localhost:3000',
      isLocal: true
})

Once you deploy the service somewhere, say nashcasino.com/proxy, the SDK needs to be configured as follows:

const nash = new Client({
      ...EnvironmentConfiguration.production,
      host: 'nashcasino.com/proxy'
})

@marcperrinp
Copy link
Author

marcperrinp commented Jun 25, 2020

Thanks a lot @jankjr.

Unfortunately I cannot set up a custom express app, as I'm using Vercel which only provides serverless functions.
I did however successfully set up a rewrite rule specifically for API calls directed towards Nash (thanks to your explanation).

It works since I don't have the CORS issue anymore, but somehow it looks like I'm not getting a response. After some time, it appears I'm getting an "Invalid timestamp" error, but it may be due to some sort of timeout?

Anyway I just tried now (in production) at around 7:23:50 UTC if you have logs and time to check it out.

I'll continue to investigate, thanks again.

@jankjr
Copy link
Contributor

jankjr commented Jun 25, 2020

@marcperrinp I think you can run a proxy in a serverless setup as well. But of course my snippet can't be used without some modifications then :)

Have you updated to the very latest SDK? It is the only thing I can think of that is causing the issue.

@marcperrinp
Copy link
Author

marcperrinp commented Jun 25, 2020

@jankjr Upgrading to 5.0.35 (from 5.0.31) solved it!... for a minute only 🙀 It was working on my staging (which is connected to Nash's Production), but when I released to Production shortly after it wasn't anymore. Now back on my staging , it's not working anymore.

Those were the successful trades, so it was at around 11:30PM (UTC+2 time):

image

Did anything change on Nash side?

@jankjr
Copy link
Contributor

jankjr commented Jun 25, 2020

Not sure @marcperrinp

Can you double check that the time is set correctly on your computer? I've seen that sometime the browser is reporting incorrect time.

@marcperrinp
Copy link
Author

marcperrinp commented Jun 25, 2020

@jankjr Your answer helped me identify the issue. It is two-fold:

  • using my Mac which is connected on wifi, it's likely that my clock was slightly off, even though I thoroughly followed this tutorial. I actually sometimes have the "Sync your clock" warning on this Mac when trading on the exchange. Restarting my Mac usually solves the problem. Also, this explains why it was working on my staging, then not in production.

  • now to the real issue: when I trigger the very first spin, Webpack fetches a few simple chunks of code. The operation takes around 50ms, but apparently it's enough to trigger the "Invalid timestamp" error from the API..

image

On the next spins, the chunks don't need to be fetched again so passing the order via the API goes smooth.
When I was executing the order from the server, this didn't happen obviously, which is why I'm only having the issue now.

To solve this, I guess I'll have to dig into Webpack to understand how to have it pre-download those chunks.

Thanks for reading, I'm explaining in details hoping it could help others in the future 😄

@marcperrinp
Copy link
Author

Hi @jankjr, I'm writing to you again after having tried many things, but I couldn't get it to work.

I narrowed it down to the fact that those 3 chunks are downloaded precisely when calling placeLimitOrder (other authenticated method of the API have the same behavior).

Do you have any idea what they could be?

@jankjr
Copy link
Contributor

jankjr commented Jul 2, 2020

@marcperrinp

I know I suggested this before in a different context, I am wondering if you could retry the call if it fails?

try {
	// try placing the call first time
	return client.placeLimitOrder(...)
} catch(e){
	// if it fails because of timing issues because it is loading modules. Then calling it again should succeed?
	return client.placeLimitOrder(...)
        /// if it fails here again then maybe there is something else that is wrong
}

I am not sure I know how to coerce Webpack into behaving.

@marcperrinp
Copy link
Author

@jankjr Thanks for the reply!

I tried it but it does not work.

What's happening actually is that the placeLimitOrder method calls the following mutation:

query: "mutation dhFillRPool($blockchain: Blockchain!, $dhPublics: [Base16]!) {↵  dhFillPool(dhPublics: $dhPublics, blockchain: $blockchain)↵}↵"

after 30+ seconds a response is returned (30s feels like it might have timed out, but weirdly I do get a response..):

image

then only is the actual placeLimitOrder mutation called:

image

and unsurprisingly, the return is an "Invalid timestamp" error:

image

As a result the try // catch you suggested doesn't actually catch an error until 30 seconds after the spin started. Not only is this not acceptable in terms of playing experience, but the fallback placeLimitOrder method in the catch statement doesn't seem to trigger anyway.

My questions are the following:

  • Do you know why the dhFillRPool mutation takes so much time to respond?
  • If this is expected behavior, is there any way to manually timeout the placeLimitOrder method if the promise isn't fulfilled after X milliseconds?

Thanks again for your time, Jan!

@Marcpepe
Copy link

This is partly solved thanks to one of @jankjr's PRs.

Adding this snippet to your code will prevent the delay/timeout:

import {
  configurePoolSettings
} from '@neon-exchange/nash-protocol'

// Do not go lower than 4. The lower, the more work is done during the trade. (Default is 100)
configurePoolSettings(4)

I'm keeping this thread open because I'm still having a freeze issue. It's due to the browser doing some calculations which throttles the RAM, apparently. I'll be this thread updated with any additional info.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants