There are two methods that I know of for making parsers that can also be unparsers. One is the approach used in Boomerang:
On Mon, Jun 30, 2014 at 2:20 PM, Nicolas Trangez <[email protected]> wrote: > On Sun, 2014-06-29 at 18:05 -0700, Gabriel Gonzalez wrote: >> So if I understand correctly, you only need a way to: >> >> A) Parse requests from a raw byte stream (i.e. your `Producer`) >> B) Create responses >> C) Serialize responses to a byte stream > > That's about it indeed. > >> `pipes-attoparsec` is the most idiomatic way to do (A). You can get the >> necessary parsers from `hweblib`, which provides RFC2616-compliant parsers. > > Aha, interesting. I didn't know that package, guess it could suit my > needs. > > Too bad the Warp parser can't be reused though. I'd expect it's more > battle-tested. On first sight, `hweblib` doesn't handle request bodies > (not to mention chunked encodings). I guess I could not support that for > now, maybe look into it later. > >> For (B), you can also use the `hweblib` library, which provides a >> `Response` type you can build. >> >> For (C), I'm not as sure. Ask Aycan (the maintainer of `hweblib`) if >> there is a way to serialize `Response`s to `ByteString`s. > > According to the package Haddocks, the `Response` constructor & > accessors are exported, so I can write `responseProducer` myself :-) > >> The main issue in general is that most web server libraries don't have a >> separate library just for parsing requests and serializing responses. >> `hweblib` is the closest library I've found for this purpose. > > Yeah. It's somewhat sad: HTTP seems deceivingly simple, but being able > to parse every valid HTTP request, including all possible quirks, is > harder than one might think, and writing a parser over and over again > seems rather useless. > > Maybe I should create a package, using `http-types`. > > On a side note: would it be hard to create a parser library (most likely > limited to an applicative interface, not monadic) which, given a parser > definition, can also create an 'unparser', up to an isomorphism (~ > ignored elements)? I'm aware of some research in this area, but given > Lens and its combination of getters & setters in one, there might be > some new opportunities. > > Nicolas > >> >> On 06/29/2014 05:00 PM, Nicolas Trangez wrote: >> > Gabriel, >> > >> > On Sun, 2014-06-29 at 12:37 -0700, Gabriel Gonzalez wrote: >> >> This is a simple "echo" server written using `pipes-wai`: >> >> >> >> {-# LANGUAGE OverloadedStrings #-} >> >> >> >> import Blaze.ByteString.Builder (fromByteString) >> >> import Pipes >> >> import qualified Pipes.Prelude as Pipes >> >> import Pipes.Wai >> >> import Network.Wai.Handler.Warp >> >> import Network.HTTP.Types (Status(..)) >> >> >> >> main = run 8000 $ \request onResponse -> do >> >> let p = do >> >> producerRequestBody request >-> Pipes.map (Chunk . >> >> fromByteString) >> >> yield Flush >> >> >> >> onResponse (responseProducer (Status 200 "") [] p) >> >> >> >> `producerRequestBody` turns the incoming client's request into a >> >> `Producer`. Then you can do whatever you want with that `Producer`, >> >> like read in its contents, discard it, or (in the above example) stream >> >> it directly to the response. >> >> >> >> If you give a more specific example of what you want to do, I can give a >> >> more specific example. >> > Thanks for taking time looking into this. It looks like I might have >> > been somewhat unclear before. >> > >> > Whilst your example above indeed turns a client request into a Producer >> > and turns a Producer into a response, this is all at another layer than >> > what I'm trying to accomplish. >> > >> > Here's the difference: in your example, the 'run' function (as provided >> > by Warp) takes care of setting up a listening socket, accepting client >> > connections, forking threads,... >> > >> > Whilst in my application, all this functionality is provided already, >> > all I'm left with are a ByteString Producer and ByteString Consumer >> > representing a client socket connection. Now a Request should be >> > read/parsed from the Producer, handled, and then a Response should be >> > yield'ed to the Consumer. All of this for this single client connection, >> > no need to accept connections, spawn threads,... >> > >> > This could boil down to, e.g. >> > >> > runHTTPInteraction :: Monad m => Pipe ByteString ByteString m r >> > runHTTPInteraction = do >> > req <- readRequest >> > let resp = ... >> > renderResponse resp >> > >> > Then, when my client handler function is called with a `Producer >> > ByteString m r` and `Consumer ByteString m r`, I can use the above using >> > something like >> > >> > -- Interact with a single client. This runs in its own thread etc. >> > myHandler :: Monad m => Producer ByteString IO () -> Consumer ByteString >> > IO () -> () >> > myHandler prod cons = >> > runEffect $ >> > {- for HTTP pipelining / connection re-use... -} forever $ >> > prod >-> runHTTPInteractoin >-> cons >> > >> > (or something along those lines, this is no real code so might not even >> > type-check). >> > >> > Thanks, >> > >> > Nicolas >> > >> > >> >> On 06/28/2014 02:20 PM, Nicolas Trangez wrote: >> >>> On Sat, 2014-06-28 at 14:12 -0700, Gabriel Gonzalez wrote: >> >>>> Have you tried the `pipes-wai` library? >> >>> I passed by it during my search, but its operations seem client-centric: >> >>> turning a `Request` into a `Producer ByteString m ()` (in order to >> >>> stream the data from that producer to some server?), or create a >> >>> `Response` out of a `Producer (Flush Builder) IO ()` (in order to stream >> >>> data from the producer to a client on some server?). >> >>> >> >>> I could be missing something very obvious, but I don't think that's what >> >>> I need. >> >>> >> >>> Thanks, >> >>> >> >>> Nicolas >> >>> >> >>>> On Jun 28, 2014 1:52 PM, "Nicolas Trangez" <[email protected]> wrote: >> >>>> >> >>>>> All, >> >>>>> >> >>>>> Has anyone ever written anything HTTP-server-like using Pipes? I looked >> >>>>> into Warp to check whether it could be integrated, but Warp seems to do >> >>>>> connection management, thread management, timeout handling, whatnot, >> >>>>> which is not what I need. >> >>>>> >> >>>>> I'm looking for something simpler: given a `Producer ByteString m r` >> >>>>> and >> >>>>> a `Consumer ByteString m r`, read a `Request` from the producer, then >> >>>>> do >> >>>>> some handling, and render a `Response` to the consumer. >> >>>>> >> >>>>> Are there any libraries I could look into, or is Warp easy to integrate >> >>>>> yet I'm failing to see how? >> >>>>> >> >>>>> Thanks, >> >>>>> >> >>>>> Nicolas >> >>>>> >> >>>>> -- >> >>>>> You received this message because you are subscribed to the Google >> >>>>> Groups >> >>>>> "Haskell Pipes" group. >> >>>>> To unsubscribe from this group and stop receiving emails from it, send >> >>>>> an >> >>>>> email to [email protected]. >> >>>>> To post to this group, send email to [email protected]. >> >>>>> >> > >> > > > -- > You received this message because you are subscribed to the Google Groups > "Haskell Pipes" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > To post to this group, send email to [email protected]. -- You received this message because you are subscribed to the Google Groups "Haskell Pipes" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected].
