mcl.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP (HTM) git clone git://bitreich.org/clic/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/clic/ (DIR) Log (DIR) Files (DIR) Refs (DIR) Tags (DIR) README (DIR) LICENSE --- mcl.lisp (11738B) --- 1 ;; MCL backend for USOCKET 0.4.1 2 ;; Terje Norderhaug <terje@in-progress.com>, January 1, 2009 3 4 (in-package :usocket) 5 6 (defun handle-condition (condition &optional socket (host-or-ip nil)) 7 ; incomplete, needs to handle additional conditions 8 (flet ((raise-error (&optional socket-condition host-or-ip) 9 (if socket-condition 10 (cond ((typep socket-condition ns-error) 11 (error socket-condition :socket socket :host-or-ip host-or-ip)) 12 (t 13 (error socket-condition :socket socket))) 14 (error 'unknown-error :socket socket :real-error condition)))) 15 (typecase condition 16 (ccl:host-stopped-responding 17 (raise-error 'host-down-error host-or-ip)) 18 (ccl:host-not-responding 19 (raise-error 'host-unreachable-error host-or-ip)) 20 (ccl:connection-reset 21 (raise-error 'connection-reset-error)) 22 (ccl:connection-timed-out 23 (raise-error 'timeout-error)) 24 (ccl:opentransport-protocol-error 25 (raise-error 'protocol-not-supported-error)) 26 (otherwise 27 (raise-error condition host-or-ip))))) 28 29 (defun socket-connect (host port &key (element-type 'character) timeout deadline nodelay 30 local-host local-port (protocol :stream)) 31 (when (eq nodelay :if-supported) 32 (setf nodelay t)) 33 (ecase protocol 34 (:stream 35 (with-mapped-conditions (nil host) 36 (let* ((socket 37 (make-instance 'active-socket 38 :remote-host (when host (host-to-hostname host)) 39 :remote-port port 40 :local-host (when local-host (host-to-hostname local-host)) 41 :local-port local-port 42 :deadline deadline 43 :nodelay nodelay 44 :connect-timeout (and timeout (round (* timeout 60))) 45 :element-type element-type)) 46 (stream (socket-open-stream socket))) 47 (make-stream-socket :socket socket :stream stream)))) 48 (:datagram 49 (with-mapped-conditions (nil (or host local-host)) 50 (make-datagram-socket 51 (ccl::open-udp-socket :local-address (and local-host (host-to-hbo local-host)) 52 :local-port local-port)))))) 53 54 (defun socket-listen (host port 55 &key reuseaddress 56 (reuse-address nil reuse-address-supplied-p) 57 (backlog 5) 58 (element-type 'character)) 59 (let* ((reuseaddress (if reuse-address-supplied-p reuse-address reuseaddress)) 60 (socket (with-mapped-conditions () 61 (make-instance 'passive-socket 62 :local-port port 63 :local-host (host-to-hbo host) 64 :reuse-address reuseaddress 65 :backlog backlog)))) 66 (make-stream-server-socket socket :element-type element-type))) 67 68 (defmethod socket-accept ((usocket stream-server-usocket) &key element-type) 69 (let* ((socket (socket usocket)) 70 (stream (with-mapped-conditions (usocket) 71 (socket-accept socket :element-type element-type)))) 72 (make-stream-socket :socket socket :stream stream))) 73 74 (defmethod socket-close ((usocket usocket)) 75 (with-mapped-conditions (usocket) 76 (socket-close (socket usocket)))) 77 78 (defmethod socket-shutdown ((usocket usocket) direction) 79 (declare (ignore usocket direction)) 80 ;; As far as I can tell there isn't a way to shutdown a socket in mcl. 81 (unsupported "shutdown" 'socket-shutdown)) 82 83 (defmethod ccl::stream-close ((usocket usocket)) 84 (socket-close usocket)) 85 86 (defun get-hosts-by-name (name) 87 (with-mapped-conditions (nil name) 88 (list (hbo-to-vector-quad (ccl::get-host-address 89 (host-to-hostname name)))))) 90 91 (defun get-host-by-address (address) 92 (with-mapped-conditions (nil address) 93 (ccl::inet-host-name (host-to-hbo address)))) 94 95 (defmethod get-local-name ((usocket usocket)) 96 (values (get-local-address usocket) 97 (get-local-port usocket))) 98 99 (defmethod get-peer-name ((usocket stream-usocket)) 100 (values (get-peer-address usocket) 101 (get-peer-port usocket))) 102 103 (defmethod get-local-address ((usocket usocket)) 104 (hbo-to-vector-quad (ccl::get-host-address (or (local-host (socket usocket)) "")))) 105 106 (defmethod get-local-port ((usocket usocket)) 107 (local-port (socket usocket))) 108 109 (defmethod get-peer-address ((usocket stream-usocket)) 110 (hbo-to-vector-quad (ccl::get-host-address (remote-host (socket usocket))))) 111 112 (defmethod get-peer-port ((usocket stream-usocket)) 113 (remote-port (socket usocket))) 114 115 (defun %setup-wait-list (wait-list) 116 (declare (ignore wait-list))) 117 118 (defun %add-waiter (wait-list waiter) 119 (declare (ignore wait-list waiter))) 120 121 (defun %remove-waiter (wait-list waiter) 122 (declare (ignore wait-list waiter))) 123 124 125 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 126 ;; BASIC MCL SOCKET IMPLEMENTATION 127 128 (defclass socket () 129 ((local-port :reader local-port :initarg :local-port) 130 (local-host :reader local-host :initarg :local-host) 131 (element-type :reader element-type :initform 'ccl::base-character :initarg :element-type))) 132 133 (defclass active-socket (socket) 134 ((remote-host :reader remote-host :initarg :remote-host) 135 (remote-port :reader remote-port :initarg :remote-port) 136 (deadline :initarg :deadline) 137 (nodelay :initarg :nodelay) 138 (connect-timeout :reader connect-timeout :initform NIL :initarg :connect-timeout 139 :type (or null fixnum) :documentation "ticks (60th of a second)"))) 140 141 (defmethod socket-open-stream ((socket active-socket)) 142 (ccl::open-tcp-stream (or (remote-host socket)(ccl::local-interface-ip-address)) (remote-port socket) 143 :element-type (if (subtypep (element-type socket) 'character) 'ccl::base-character 'unsigned-byte) 144 :connect-timeout (connect-timeout socket))) 145 146 (defmethod socket-close ((socket active-socket)) 147 NIL) 148 149 (defclass passive-socket (socket) 150 ((streams :accessor socket-streams :type list :initform NIL 151 :documentation "Circular list of streams with first element the next to open") 152 (reuse-address :reader reuse-address :initarg :reuse-address) 153 (lock :reader socket-lock :initform (ccl:make-lock "Socket")))) 154 155 (defmethod initialize-instance :after ((socket passive-socket) &key backlog) 156 (loop repeat backlog 157 collect (socket-open-listener socket) into streams 158 finally (setf (socket-streams socket) 159 (cdr (rplacd (last streams) streams)))) 160 (when (zerop (local-port socket)) 161 (setf (slot-value socket 'local-port) 162 (or (ccl::process-wait-with-timeout "binding port" (* 10 60) 163 #'ccl::stream-local-port (car (socket-streams socket))) 164 (error "timeout"))))) 165 166 (defmethod socket-accept ((socket passive-socket) &key element-type &aux (lock (socket-lock socket))) 167 (flet ((connection-established-p (stream) 168 (ccl::with-io-buffer-locked ((ccl::stream-io-buffer stream nil)) 169 (let ((state (ccl::opentransport-stream-connection-state stream))) 170 (not (eq :unbnd state)))))) 171 (with-mapped-conditions () 172 (ccl:with-lock-grabbed (lock nil "Socket Lock") 173 (let ((connection (shiftf (car (socket-streams socket)) 174 (socket-open-listener socket element-type)))) 175 (pop (socket-streams socket)) 176 (ccl:process-wait "Accepting" #'connection-established-p connection) 177 connection))))) 178 179 (defmethod socket-close ((socket passive-socket)) 180 (loop 181 with streams = (socket-streams socket) 182 for (stream tail) on streams 183 do (close stream :abort T) 184 until (eq tail streams) 185 finally (setf (socket-streams socket) NIL))) 186 187 (defmethod socket-open-listener (socket &optional element-type) 188 ; see http://code.google.com/p/mcl/issues/detail?id=28 189 (let* ((ccl::*passive-interface-address* (local-host socket)) 190 (new (ccl::open-tcp-stream NIL (or (local-port socket) #$kOTAnyInetAddress) 191 :reuse-local-port-p (reuse-address socket) 192 :element-type (if (subtypep (or element-type (element-type socket)) 193 'character) 194 'ccl::base-character 195 'unsigned-byte)))) 196 (declare (special ccl::*passive-interface-address*)) 197 new)) 198 199 (defmethod input-available-p ((stream ccl::opentransport-stream)) 200 (macrolet ((when-io-buffer-lock-grabbed ((lock &optional multiple-value-p) &body body) 201 "Evaluates the body if and only if the lock is successfully grabbed" 202 ;; like with-io-buffer-lock-grabbed but returns immediately instead of polling the lock 203 (let ((needs-unlocking-p (gensym)) 204 (lock-var (gensym))) 205 `(let* ((,lock-var ,lock) 206 (ccl::*grabbed-io-buffer-locks* (cons ,lock-var ccl::*grabbed-io-buffer-locks*)) 207 (,needs-unlocking-p (needs-unlocking-p ,lock-var))) 208 (declare (dynamic-extent ccl::*grabbed-io-buffer-locks*)) 209 (when ,needs-unlocking-p 210 (,(if multiple-value-p 'multiple-value-prog1 'prog1) 211 (progn ,@body) 212 (ccl::%release-io-buffer-lock ,lock-var))))))) 213 (labels ((needs-unlocking-p (lock) 214 (declare (type ccl::lock lock)) 215 ;; crucial - clears bogus lock.value as in grab-io-buffer-lock-out-of-line: 216 (ccl::%io-buffer-lock-really-grabbed-p lock) 217 (ccl:store-conditional lock nil ccl:*current-process*))) 218 "similar to stream-listen on buffered-input-stream-mixin but without waiting for lock" 219 (let ((io-buffer (ccl::stream-io-buffer stream))) 220 (or (not (eql 0 (ccl::io-buffer-incount io-buffer))) 221 (ccl::io-buffer-untyi-char io-buffer) 222 (locally (declare (optimize (speed 3) (safety 0))) 223 (when-io-buffer-lock-grabbed ((ccl::io-buffer-lock io-buffer)) 224 (funcall (ccl::io-buffer-listen-function io-buffer) stream io-buffer)))))))) 225 226 (defmethod connection-established-p ((stream ccl::opentransport-stream)) 227 (ccl::with-io-buffer-locked ((ccl::stream-io-buffer stream nil)) 228 (let ((state (ccl::opentransport-stream-connection-state stream))) 229 (not (eq :unbnd state))))) 230 231 (defun wait-for-input-internal (wait-list &key timeout &aux result) 232 (labels ((ready-sockets (sockets) 233 (dolist (sock sockets result) 234 (when (cond ((stream-usocket-p sock) 235 (input-available-p (socket-stream sock))) 236 ((stream-server-usocket-p sock) 237 (let ((ot-stream (first (socket-streams (socket sock))))) 238 (or (input-available-p ot-stream) 239 (connection-established-p ot-stream))))) 240 (push sock result))))) 241 (with-mapped-conditions () 242 (ccl:process-wait-with-timeout 243 "socket input" 244 (when timeout (truncate (* timeout 60))) 245 #'ready-sockets 246 (wait-list-waiters wait-list))) 247 (nreverse result))) 248 249 ;;; datagram socket methods 250 251 (defmethod initialize-instance :after ((usocket datagram-usocket) &key) 252 (with-slots (socket send-buffer recv-buffer) usocket 253 (setq send-buffer 254 (ccl::make-TUnitData (ccl::ot-conn-endpoint socket))) 255 (setq recv-buffer 256 (ccl::make-TUnitData (ccl::ot-conn-endpoint socket))))) 257 258 (defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0)) 259 (with-mapped-conditions (usocket host) 260 (with-slots (socket send-buffer) usocket 261 (unless (and host port) 262 (unsupported 'host 'socket-send)) 263 (ccl::send-message socket send-buffer buffer size host port offset)))) 264 265 (defmethod socket-receive ((usocket datagram-usocket) buffer length &key) 266 (with-mapped-conditions (usocket) 267 (with-slots (socket recv-buffer) usocket 268 (ccl::receive-message socket recv-buffer buffer length)))) 269 270 (defmethod socket-close ((socket datagram-usocket)) 271 nil) ; TODO