1

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>
Bob Dalgleish
  • 8,167
  • 4
  • 32
  • 42
Heligo
  • 165
  • 9
  • 1
    Perhaps it's obvious to a warp expert, I don't know; but it's not obvious to me: what is your question? – Daniel Wagner May 01 '19 at 14:11
  • The question is: how would you construct a wai/warp code, which would receive user input from an html page (like when you print stuff inside the google search bar), and give out what you've just written within a new div inside the same html page? The code I'm showing is my humble attempt of doing so, but most likely there is something wrong at its core. – Heligo May 01 '19 at 14:22
  • 1
    I don't know WAI/Warp at all, but is it possible that you want your `case pathInfo req` block to replace the `_` case of your `case queryString req` block? As is, the code reads to me like it's going to issue two responses to each request, so you're probably only ever seeing the first response, which ignores the query string and just serves another copy of `search.html`. – user11228628 May 01 '19 at 16:46
  • 1
    Or for a more robust handler, maybe you want to match both at the same time with `case (pathInfo req, queryString req)`. – user11228628 May 01 '19 at 16:48
  • Would you mind giving your own version of how you see it? This is actually what happens, and I do not get why is the the output behaving like this. – Heligo May 01 '19 at 21:32

1 Answers1

1

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.

user11228628
  • 1,526
  • 1
  • 6
  • 17