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