1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Indent-tabs-mode: NIL -*-
3 ;;; io-channel.lisp --- Transport protocol.
5 ;;; Copyright (C) 2007, Stelian Ionescu <sionescu@common-lisp.net>
6 ;;; Copyright (C) 2007, Luis Oliveira <loliveira@common-lisp.net>
8 ;;; This code is free software; you can redistribute it and/or
9 ;;; modify it under the terms of the version 2.1 of
10 ;;; the GNU Lesser General Public License as published by
11 ;;; the Free Software Foundation, as clarified by the
12 ;;; preamble found here:
13 ;;; http://opensource.franz.com/preamble.html
15 ;;; This program is distributed in the hope that it will be useful,
16 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;;; GNU General Public License for more details.
20 ;;; You should have received a copy of the GNU Lesser General
21 ;;; Public License along with this library; if not, write to the
22 ;;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
23 ;;; Boston, MA 02110-1301, USA
25 (in-package :io.event
)
29 (defclass transport
()
30 ((event-base :initarg
:event-base
:accessor event-base-of
)
31 (protocol :initarg
:protocol
:accessor protocol-of
)
32 (read-handler :accessor read-handler-of
)
33 (write-handler :accessor write-handler-of
)
34 (error-handler :accessor error-handler-of
)))
36 (defgeneric on-transport-readable
(transport)
39 (defgeneric on-transport-writable
(transport)
42 (defgeneric on-transport-error
(transport)
45 (defgeneric write-data
(data transport
&key
&allow-other-keys
)
48 (defgeneric close-transport
(transport &key
&allow-other-keys
)
51 (defconstant +default-read-window-size
+ 8192
54 (defclass buffered-transport
(transport)
55 ((read-buffer :accessor read-buffer-of
)
56 (read-buffered-p :initarg
:read-buffered-p
57 :accessor read-buffered-p
59 (read-window-size :initarg
:read-window-size
60 :accessor read-window-size-of
62 (write-buffer :accessor write-buffer-of
)
63 (write-buffered-p :initarg
:write-buffered-p
64 :accessor write-buffered-p
65 :initform
+default-read-window-size
+))
68 (defmethod initialize-instance :after
((channel buffered-transport
) &key
69 read-buffer-size write-buffer-size
)
70 (when (read-buffered-p channel
)
71 (setf (read-buffer-of channel
)
72 (make-instance 'io-buffer
:size read-buffer-size
)))
73 (when (write-buffered-p channel
)
74 (setf (write-buffer-of channel
)
75 (make-instance 'io-buffer
:size write-buffer-size
))))
79 (defclass socket-transport
(transport)
80 ((socket :initarg
:socket
:accessor socket-of
))
83 (defmethod initialize-instance :after
((transport socket-transport
) &key
)
84 (macrolet ((handler (event callback
)
85 `(add-fd (event-base-of transport
) (fd-of (socket-of transport
))
86 ,event
(lambda (fd event
)
87 (declare (ignore fd event
))
88 (,callback transport
)))))
89 (setf (read-handler-of transport
) (handler :read on-transport-readable
)
90 (write-handler-of transport
) (handler :write on-transport-writable
)
91 (error-handler-of transport
) (handler :error on-transport-error
))))
93 (defmethod close-transport ((c socket-transport
) &key abort
)
94 (remove-event (event-base-of c
) (read-handler-of c
))
95 (remove-event (event-base-of c
) (write-handler-of c
))
96 (remove-event (event-base-of c
) (error-handler-of c
))
97 (close (socket-of c
) :abort abort
))
101 ;;; Unbuffered, for now.
102 (defclass tcp-transport
(#-
(and) buffered-transport socket-transport
)
103 ((status :initform
:unconnected
:accessor status-of
))
106 (defmethod on-transport-readable ((c tcp-transport
))
107 (unless (eq (status-of c
) :connected
)
108 (warn "ON-TRANSPORT-READABLE on non-connected socket")
109 (return-from on-transport-readable
))
110 (let ((buffer (make-array +default-read-window-size
+
111 :element-type
'(unsigned-byte 8)))
113 (declare (type unsigned-byte byte-num
))
115 (setf (values buffer byte-num
) (socket-receive buffer
(socket-of c
)))
118 (warn "Got a transport-readable event but recv() returned ~
120 ;; FIXME: perhaps we might be a little more sophisticated here
122 (setf (status-of c
) :disconnected
)
123 (on-connection-lost (protocol-of c
) c err
)))
127 (setf (status-of c
) :disconnected
)
128 (on-connection-end (protocol-of c
) c
))
131 (on-data-received (protocol-of c
) c
133 :element-type
(array-element-type buffer
)
135 :displaced-index-offset
0))))))
137 (defmethod write-data (data (c tcp-transport
) &key
)
139 (let ((count (socket-send data
(socket-of c
))))
140 (when (/= count
(length data
))
141 ;; here it should copy what it didn't manage to send,
142 ;; and then write it out ON-TRANSPORT-WRITABLE.
143 (warn "WRITE-DATA didn't send everything")))
145 (warn "WRITE-DATA EWOULDBLOCK"))))
147 ;;; FIXME: deal with full write kernel buffers
148 (defmethod on-transport-writable ((c tcp-transport
))
149 ;; not exactly complete: infact subsequent :WRITE
150 ;; events must be handled
151 (cond ((eq (status-of c
) :unconnected
)
152 (on-connection-made (protocol-of c
) c
)
153 (setf (status-of c
) :connected
))
154 (t (warn "tcp on-transport-writable: implement me"))))
156 (defmethod on-transport-error ((transport tcp-transport
))
157 ;; are we supposed to close the socket now?
158 (on-connection-lost (protocol-of transport
)
160 (sockets::lookup-socket-error
161 (get-socket-option (socket-of transport
) :error
))))
165 (defclass udp-transport
(socket-transport)
169 (defmethod on-transport-readable ((c udp-transport
))
171 (multiple-value-bind (buffer byte-num address port
)
172 (socket-receive (make-array +default-read-window-size
+
173 :element-type
'(unsigned-byte 8))
175 (on-datagram-received
178 :element-type
(array-element-type buffer
)
180 :displaced-index-offset
0)
185 (warn "Got a transport-readable event but recv() returned ~
187 ;; FIXME: perhaps we might be a little more sophisticated here
189 (warn "got error: ~S" err
))))
191 ;;; we can probably just use WRITE-DATA with :REMOTE-ADDRESS and
192 ;;; :REMOTE-PORT instead of this separate function.
193 (defgeneric write-datagram
(datagram address port transport
194 &key
&allow-other-keys
)
196 (:method
(datagram address port
(c udp-transport
) &key
)
198 (let ((count (socket-send datagram
(socket-of c
)
199 :remote-address address
201 (when (/= count
(length datagram
))
202 ;; here it should copy what it didn't manage to send,
203 ;; and then write it out ON-TRANSPORT-WRITABLE.
204 (warn "WRITE-DATA didn't send everything")))
206 (warn "WRITE-DATA EWOULDBLOCK"))
208 (warn "write-datagram: got ~S" err
)))))
210 ;;; FIXME: deal with full write kernel buffers
211 (defmethod on-transport-writable ((c udp-transport
))
212 (warn "udp on-transport-writable: implement me"))
214 ;;; FIXME: complete it
215 (defmethod on-transport-error ((c udp-transport
))
216 (let ((error-code (get-socket-option (socket-of c
) :error
)))
217 (warn "got socket error: ~A" error-code
)))