Move use of WITH-PTY inside WITH-REDIRECTIONS
[iolib.git] / examples / ex7-buffer.lisp
blob15d660ead2215c7fa95c2a36731577bd86ea3cc4
1 (in-package :iolib.examples)
3 ;;;; This file was originally written by Peter Keller (psilord@cs.wisc.edu)
4 ;;;; and this code is released under the same license as IOLib.
6 ;;;; This function returns a closure from which I can ask for a
7 ;;;; reader/writer function that the event-dispatcher can use. When
8 ;;;; constructing the buffer, which consists of a queue of lines, I
9 ;;;; specify the socket upon which the buffer is responsible and how
10 ;;;; bytes I'm willing to buffer. Obviously, if a line is very much
11 ;;;; bigger than the max-bytes I have, I have to read the whole thing
12 ;;;; due to read-line and the blocking i/o requirement.
14 ;; The event dispatcher
15 (defvar *ex7-event-base*)
17 ;; ex-0b
18 (defun make-ex7-io-buffer (socket who port disconnector &key (max-bytes 4096))
19 (let ((line-queue (make-queue))
20 (bytes-left-to-write 0)
21 (read-handler-registered nil)
22 (write-handler-registered nil)
23 (eof-seen nil))
24 ;; ex-0e
25 ;; ex-1b
26 (labels
27 ;; If this function notices that there is data to write, it will
28 ;; set the io-handler on the socket for the write handler.
29 ;; If the function notices it has read >= than the max-bytes
30 ;; it will remove itself from the handler *after* ensuring the
31 ;; write handler is set up properly.
32 ((read-a-line (fd event exception)
33 (handler-case
34 (let ((line (format nil "~A~%" (read-line socket)))) ; add a \n
35 (format t "Read from ~A:~A: ~A" who port line)
36 (enqueue line line-queue)
37 (incf bytes-left-to-write (length line))
39 (when (> bytes-left-to-write 0)
40 ;; If the write handler isn't registered, then do
41 ;; it now since I have data to write.
42 (unless write-handler-registered
43 (set-io-handler *ex7-event-base*
44 (socket-os-fd socket)
45 :write
46 #'write-a-line)
47 (setf write-handler-registered t)))
49 ;; Now, if there is more data than I should be
50 ;; reading, remove myself from the io handler. When
51 ;; the write handler notices that, after writing some
52 ;; data, more of it can be read, it will reregister
53 ;; the io handler for the read socket.
54 (when (>= bytes-left-to-write max-bytes)
55 (funcall disconnector who port :read)
56 (setf read-handler-registered nil)))
58 (socket-connection-reset-error ()
59 ;; If the client resets its connection, we close
60 ;; everything down.
61 (format t "Client ~A:~A: Connection reset by peer~%" who port)
62 (funcall disconnector who port :close))
64 (end-of-file ()
65 ;; When we get an end of file, that doesn't necessarily
66 ;; mean the client went away, it could just mean that
67 ;; the client performed a shutdown on the write end of
68 ;; its socket and it is expecting the data stored in
69 ;; the server to be written to it. However, if there
70 ;; is nothing left to write and our read end is close,
71 ;; we shall consider it that the client went away and
72 ;; close the connection.
73 (format t "Client ~A:~A produced end-of-file on a read.~%"
74 who port)
75 (if (zerop bytes-left-to-write)
76 (funcall disconnector who port :close)
77 (progn
78 (funcall disconnector who port :read)
79 (setf read-handler-registered nil)
80 (setf eof-seen t))))))
81 ;; ex-1e
83 ;; ex-2b
84 ;; This function will notice that if it has written enough bytes to
85 ;; bring the bytes-left-to-write under max-bytes, it will re-register
86 ;; the reader io handler. If there is no data to write, it will,
87 ;; after ensuring the read handler is registered, unregister itself
88 ;; as to not be called constantly on a write ready socket with no
89 ;; data to write.
90 (write-a-line (fd event exception)
91 (handler-case
92 (progn
93 ;; If we have something to write to the client, do so.
94 (when (> bytes-left-to-write 0)
95 (let ((line (dequeue line-queue)))
96 (format socket "~A" line) ;; newline is in the string.
97 (finish-output socket)
98 (format t "Wrote to ~A:~A: ~A" who port line)
99 (decf bytes-left-to-write (length line))))
101 ;; If we see we've fallen below the max-bytes mark,
102 ;; re-register the read handler to get more data for
103 ;; us. However, don't reregister the read handler if
104 ;; we've seen that the client closed our read end of
105 ;; our socket.
106 (when (< bytes-left-to-write max-bytes)
107 (unless (or eof-seen read-handler-registered)
108 (set-io-handler *ex7-event-base*
109 (socket-os-fd socket)
110 :read
111 #'read-a-line)
112 (setf read-handler-registered t)))
114 ;; If we notice that we don't have any data to write
115 ;; AND have seen the end of file from the client,
116 ;; then we close the connection to the client since
117 ;; it will never speak to us again and we're done
118 ;; speaking to it.
120 ;; If notice we've written all of our data and there
121 ;; might be more to do later, then unregister the
122 ;; write handler so we don't get called
123 ;; unnecesarily. This might mean that sometimes we'll
124 ;; have to make an extra trip through the
125 ;; event-dispatcher to perform the write if we read
126 ;; more from the client and it reregisters us.
127 (when (zerop bytes-left-to-write)
128 (if eof-seen
129 (funcall disconnector who port :close)
130 (progn
131 (funcall disconnector who port :write)
132 (setf write-handler-registered nil)))))
134 (socket-connection-reset-error ()
135 ;; If I happen to get a reset, make sure the connection
136 ;; is closed. I shouldn't get this here, but if you
137 ;; tinker with the flow of this example, it is a good
138 ;; guard to have.
139 (format t "Client ~A:~A: connection reset by peer.~%" who port)
140 (funcall disconnector who port :close))
142 (hangup ()
143 ;; In this server, if the client doesn't accept data,
144 ;; it also means it will never send us data again. So
145 ;; close the connection for good.
146 (format t "Client ~A:~A got hangup on write.~%" who port)
147 (funcall disconnector who port :close)))))
148 ;; ex-2e
150 ;; ex-3b
151 ;; This is the actual function returned from make-ex7-io-buffer
152 ;; which allows us access to the read/writer in the scope of the
153 ;; closure. We will ask for the correct functions when setting
154 ;; up the io handlers. NOTE: By simply asking for the handler,
155 ;; I've assumed it is to be immediately put into an iolib event
156 ;; handler. This is why they are considered registered at this point.
157 (lambda (msg)
158 (cond
159 ((equalp msg :read-a-line)
160 (setf read-handler-registered t)
161 #'read-a-line)
162 ((equalp msg :write-a-line)
163 (setf write-handler-registered t)
164 #'write-a-line)
166 (error "make-ex7-buffer: Please supply :read-a-line or :write-a-line~%")))))))
167 ;; ex-3e