server.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
       ---
       server.lisp (4900B)
       ---
            1 ;;;; See LICENSE for licensing information.
            2 
            3 (in-package :usocket)
            4 
            5 (defvar *server*)
            6 
            7 (defun socket-server (host port function &optional arguments
            8                       &key in-new-thread (protocol :stream)
            9                            ;; for udp
           10                            (timeout 1) (max-buffer-size +max-datagram-packet-size+)
           11                            ;; for tcp
           12                            element-type (reuse-address t) multi-threading
           13                            name)
           14   (let* ((real-host (or host *wildcard-host*))
           15          (socket (ecase protocol
           16                    (:stream
           17                     (apply #'socket-listen
           18                            `(,real-host ,port
           19                              ,@(when element-type `(:element-type ,element-type))
           20                              ,@(when reuse-address `(:reuse-address ,reuse-address)))))
           21                    (:datagram
           22                     (socket-connect nil nil :protocol :datagram
           23                                     :local-host real-host
           24                                     :local-port port)))))
           25     (labels ((real-call ()
           26                (ecase protocol
           27                  (:stream
           28                   (tcp-event-loop socket function arguments
           29                                   :element-type element-type
           30                                   :multi-threading multi-threading))
           31                  (:datagram
           32                   (udp-event-loop socket function arguments
           33                                   :timeout timeout
           34                                   :max-buffer-size max-buffer-size)))))
           35       (if in-new-thread
           36           (values (bt:make-thread #'real-call :name (or name "USOCKET Server")) socket)
           37         (progn
           38           (setq *server* socket)
           39           (real-call))))))
           40 
           41 (defvar *remote-host*)
           42 (defvar *remote-port*)
           43 
           44 (defun default-udp-handler (buffer) ; echo
           45   (declare (type (simple-array (unsigned-byte 8) *) buffer))
           46   buffer)
           47 
           48 (defun udp-event-loop (socket function &optional arguments
           49                        &key timeout max-buffer-size)
           50   (let ((buffer (make-array max-buffer-size :element-type '(unsigned-byte 8) :initial-element 0))
           51         (sockets (list socket)))
           52     (unwind-protect
           53         (loop do
           54           (multiple-value-bind (return-sockets real-time)
           55               (wait-for-input sockets :timeout timeout)
           56             (declare (ignore return-sockets))
           57             (when real-time
           58               (multiple-value-bind (recv n *remote-host* *remote-port*)
           59                   (socket-receive socket buffer max-buffer-size)
           60                 (declare (ignore recv))
           61                 (if (plusp n)
           62                     (progn
           63                       (let ((reply
           64                              (apply function (subseq buffer 0 n) arguments)))
           65                         (when reply
           66                           (replace buffer reply)
           67                           (let ((n (socket-send socket buffer (length reply)
           68                                                 :host *remote-host*
           69                                                 :port *remote-port*)))
           70                             (when (minusp n)
           71                               (error "send error: ~A~%" n))))))
           72                   (error "receive error: ~A" n))))
           73             #+scl (when thread:*quitting-lisp* (return))
           74             #+(and cmu mp) (mp:process-yield)))
           75       (socket-close socket)
           76       (values))))
           77 
           78 (defun default-tcp-handler (stream) ; null
           79   (declare (type stream stream))
           80   (format stream "Hello world!~%"))
           81 
           82 (defun echo-tcp-handler (stream)
           83   (loop
           84      (when (listen stream)
           85        (let ((line (read-line stream nil)))
           86          (write-line line stream)
           87          (force-output stream)))))
           88 
           89 (defun tcp-event-loop (socket function &optional arguments
           90                        &key element-type multi-threading)
           91   (let ((real-function #'(lambda (client-socket &rest arguments)
           92                            (unwind-protect
           93                                (multiple-value-bind (*remote-host* *remote-port*) (get-peer-name client-socket)
           94                                  (apply function (socket-stream client-socket) arguments))
           95                              (close (socket-stream client-socket))
           96                              (socket-close client-socket)
           97                              nil))))
           98     (unwind-protect
           99         (loop do
          100           (let* ((client-socket (apply #'socket-accept
          101                                        `(,socket ,@(when element-type `(:element-type ,element-type)))))
          102                  (client-stream (socket-stream client-socket)))
          103             (if multi-threading
          104                 (bt:make-thread (lambda () (apply real-function client-socket arguments))
          105                                 :name "USOCKET Client")
          106               (prog1 (apply real-function client-socket arguments)
          107                 (close client-stream)
          108                 (socket-close client-socket)))
          109             #+scl (when thread:*quitting-lisp* (return))
          110             #+(and cmu mp) (mp:process-yield)))
          111       (socket-close socket)
          112       (values))))