reflex-frp / reflex-dom

Web applications without callbacks or side-effects. Reflex-DOM brings the power of functional reactive programming (FRP) to the web. Build HTML and other Document Object Model (DOM) data with a pure functional interface.
https://reflex-frp.org
BSD 3-Clause "New" or "Revised" License
357 stars 145 forks source link

XHR: Refused connection causes runtime error #334

Open johannesgerer opened 5 years ago

johannesgerer commented 5 years ago

Thanks for your work on this great package!

I encountered the following problem when using the ArrayBuffer result type in the performRequestsAsync call.

If I try to query a non existing host/port, the whole app crashes when I try to access the _xhrResponse_response and I see the following console log:

GET http://localhost:800/ net::ERR_CONNECTION_REFUSED

h$wrapBuffer: not an ArrayBuffer

There seems to be something wrong with the way Reflex.Dom.Xhr.Foreign parses the body into an ArrayBuffer.

This happens in Chrome and Firefox with both GHCJS and jsaddle-warp.

johannesgerer commented 5 years ago

The following code seems to work instead of the current version:

    Just XhrResponseType_ArrayBuffer -> do
       isn <- liftJSM $ ghcjsPure $ isNull mr
       if isn then return Nothing else do
         ab <- liftJSM $ mutableArrayBufferFromJSVal mr
         Just . XhrResponseBody_ArrayBuffer <$> bsFromMutableArrayBuffer ab

I am very new to GHCJS, so please tell me whether this is the best way to check for null?

johannesgerer commented 5 years ago

... at least in GHCJS. In jsaddle-warp, I still get the same error stangely.

jmininger commented 4 years ago

Hi @johannesgerer . Thank you very much for taking the time to file an issue! Do you think you could provide the version or hash of reflex-dom that you were using? I am unable to reproduce the issue, as the resulting event from performRequestAsync does not fire when an xhr exception is thrown (in this case, I get net::ERR_CONNECTION_REFUSED in the console). On the other hand, I am able to "access" the exception (proving that an exception is indeed being thrown) when I use performRequestAsyncWithError. I am not sure how you were able to access the XhrResponse at all if you are using a "nonexistent host/port"? Am I misunderstanding something ? I will post a snippet below.

frontend = Frontend
  { _frontend_head = el "title" $ text "Obelisk Minimal Example"
  , _frontend_body = void $ prerender (pure ()) $ do
      ev <- button "press me"
      let 
        url = "http://localhost:800/"
        reqConfig = def
          {
            _xhrRequestConfig_responseType = (Just XhrResponseType_ArrayBuffer) 
          , _xhrRequestConfig_headers = "Access-Control-Allow-Origin" =: "*"
          , _xhrRequestConfig_responseHeaders = AllHeaders
          }
        reqEv = xhrRequest "GET" url reqConfig <$ ev
       -- print a msg to the terminal each time the response event from performRequestAsync fires 
       resEv <- fmap (traceEventWith (const "Response Event has fired")) $ performRequestAsync reqEv
      dTxt <- holdDyn "init" $ ffor resEv $ \res ->
        case (_xhrResponse_response res) of
          Nothing -> "empty body"
          Just body -> 
            case body of
              XhrResponseBody_ArrayBuffer arr -> (T.pack $ show $ _xhrResponse_statusText res) <> "   array"
              _ -> "other body"
      dynText dTxt
      pure ()
    }
malte-v commented 4 years ago

I'm able to reproduce this issue with jsaddle-warp and the current version of reflex and reflex-dom, so I think this should be reopened.

edit: unfortunate wording error

malte-v commented 4 years ago

Not sure if that helps, but here is where the error originates: https://github.com/ghcjs/jsaddle/blob/d569be43f92b9b8c01dc3ee4c41401ab406a2076/jsaddle/src/Language/Javascript/JSaddle/Run/Files.hs#L431

ibizaman commented 4 years ago

This could be a CORS issue. See https://stackoverflow.com/questions/8456538/origin-null-is-not-allowed-by-access-control-allow-origin and https://stackoverflow.com/a/37690646/1013628 for some suggestions on how to workaround the issue.

In my specific case, I could get a more informative error message by setting useWarp = false in default.nix and then I could see this error:

CONSOLE ERROR Origin null is not allowed by Access-Control-Allow-Origin.
CONSOLE ERROR XMLHttpRequest cannot load http://localhost:8080 due to access control checks.
file:///.../index.html:386:20: CONSOLE LOG h$wrapBuffer: not an ArrayBuffer
frontend: JSException
A JavaScript exception was thrown! (may not reach Haskell code)
h$wrapBuffer: not an ArrayBuffer

I fixed it by following the answer here https://stackoverflow.com/a/42150084/1013628