ajaxhaskellhaskell-warp

How to formulate the wai queryString properly?


I'm trying to formulate code, which would allow me to print something inside an html input field, and then the Haskell code will return what I have printed in the next div (to make an Ajax search engine in the long run). The connection between the two seems established, but in a really strange way. More than that, I do not quite understand how to make cases for the queryString req in the following sequence. Here is the Haskell code:

{-# LANGUAGE OverloadedStrings #-}

import Network.Wai
import Network.Wai.Handler.Warp
import Network.HTTP.Types
import Data.ByteString.Lazy

main :: IO ()
main = run 3000 app

app :: Application
app req resp = do
  case pathInfo req of
    ["main"] -> resp $ responseFile status200 [("Content-Type","text/html")] "search.html" Nothing
    _ -> resp $ responseLBS status404 [("Content-Type","text/plain")] "No such file."
  case queryString req of
    [("q=",Just stuff)] -> resp $ responseLBS
      status200
      [("Content-Type","text/html")]
      (fromStrict stuff)
    [("q=",Just "")] -> resp $ responseLBS
      status200
      [("Content-Type","text/html")]
      ""
    _ -> resp $ responseLBS
      status404
      [("Content-Type","text/html")]
      "sorry"

And here is my search page:

<!DOCTYPE html>
<html>
  <script>
  function getStuff(str) {
    if (str.length == 0) {
      document.getElementById("results").innerHTML = "";
      return;
    } else {
      var xmlhttp = new XMLHttpRequest();
      xmlhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
          document.getElementById("results").innerHTML = this.responseText;
        }
      };
      xmlhttp.open("GET", "http://localhost:3000/main?q=" + str, true);
      xmlhttp.send();
    }
  }
  </script>
  <body>
    <input type = "text" onkeyup = "getStuff(this.value)"/>
    <p><span id = "results"></span></p>
  </body>
</html>

Solution

  • Disclaimer: I have not tried to compile this code. I have no experience with WAI/Warp. This is just something to try to see if it works!

    app req resp = do
      case (pathInfo req, queryString req) of
        (["main"], [("q", Just stuff)]) ->
          resp $ responseLBS  status200 [("Content-Type","text/html")]  (fromStrict stuff)
        (["main"], _) ->
          resp $ responseFile status200 [("Content-Type","text/html")]  "search.html" Nothing
        _ ->
          resp $ responseLBS  status404 [("Content-Type","text/plain")] "No such file."
    

    To make this a slightly more generally useful answer (if it does indeed fix the problem): in your original code, you have two case blocks that will execute in succession, and all branches of each block call resp, so you are guaranteed to call resp twice, which, I assume, would produce (or try to produce) two responses for each request. Generally speaking, that can't happen in HTTP, so you're probably going to get the first response only, which happens not to use the query string at all. The fix is just to make sure you only call resp once, after you've inspected everything you need to inspect about your request! I'm showing you one way to do that by matching against both the path and the query string at the same time, but of course there are other ways. You could match against pathInfo req first, and then match on queryString req inside some of those cases where it's relevant. Just make sure that resp only gets called once for each possible code path.