guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Streaming responses with Guile's web modules


From: Roel Janssen
Subject: Re: Streaming responses with Guile's web modules
Date: Wed, 19 Sep 2018 17:50:20 +0200
User-agent: mu4e 1.0; emacs 26.1

Roel Janssen <address@hidden> writes:

> Amirouche Boubekki <address@hidden> writes:
>
>> On 2018-09-18 21:42, Roel Janssen wrote:
>>> Dear Guilers,
>>>
>>> I'd like to implement a web server using the (web server) module, but
>>> allow for “streaming” results.  The way I imagine this would look like,
>>> is something like this:
>>>
>>> (define (request-handler request body)
>>>   (values '((content-type . (text/plain)))
>>>           ;; This function can build its response by writing to
>>>           ;; ‘port’, rather than to return the whole body as a
>>>           ;; string.
>>>           (lambda (port)
>>>             (format port "Hello world!"))))
>>>
>>> (run-server request-handler)
>>>
>>> Is this possible with the (web server) module?  If so, how?
>>
>> What you describe is exactly how it works. The second value can
>> be a bytevector, #f or a procedure that takes a port as argument.
>>
>> Here is an example use [0] and here is the code [1]
>>
>> [0]
>> https://framagit.org/a-guile-mind/culturia.next/blob/master/culturia/web/helpers.scm#L34
>> [1]
>> https://git.savannah.gnu.org/cgit/guile.git/tree/module/web/server.scm#n198
>>
>> Regards
>
> Thanks for your quick and elaborate reply!  I didn't realize that in
> writing the example I had written a working example.
>
> Looking at memory usage, it looks as if it puts all bytes produced by
> that function into memory at once before sending the HTTP response over
> the network.  Is that observation correct?  If so, can it be avoided?

As an addition to the above, here's an example implementation:

(use-modules (web server)
             (web request)
             (web response)
             (web http)
             (ice-9 receive)
             (ice-9 rdelim))

(define (process-input input-port output-port)
  (unless (or (port-closed? input-port)
              (port-closed? output-port))
    (let ((line (read-line input-port)))
      (if (eof-object? line)
          (begin
            (close-port input-port)
            #t)
          (begin
            (format output-port "~a~%" line)
            (process-input input-port output-port))))))

(define (request-handler request body)
  (values '((content-type      . (text/plain))
            (transfer-encoding . ((chunked))))
          (lambda (port)
            (call-with-input-file "large-file.txt"
              (lambda (input-port) (process-input input-port port))))))

(run-server request-handler)


In the example, “large-file.txt” is a file of a few gigabytes, and the
Guile process grows to a few gigabytes as well.

Kind regards,
Roel Janssen



reply via email to

[Prev in Thread] Current Thread [Next in Thread]