-
Notifications
You must be signed in to change notification settings - Fork 3
/
server.clj
86 lines (76 loc) · 2.36 KB
/
server.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
(ns net.tcp.server
"Functions for creating a threaded TCP server."
(:require [clojure.java.io :as io])
(:import [java.net InetAddress ServerSocket Socket SocketException]))
(defn- server-socket [server]
(ServerSocket.
(:port server)
(:backlog server)
(InetAddress/getByName (:host server))))
(defn tcp-server
"Create a new TCP server. Takes the following keyword arguments:
:host - the host to bind to (defaults to 127.0.0.1)
:port - the port to bind to
:handler - a function to handle incoming connections, expects a socket as
an argument
:backlog - the maximum backlog of connections to keep (defaults to 50)"
[& {:as options}]
{:pre [(:port options)
(:handler options)]}
(merge
{:host "127.0.0.1"
:backlog 50
:socket (atom nil)
:connections (atom #{})}
options))
(defn close-socket [server socket]
(swap! (:connections server) disj socket)
(when-not (.isClosed socket)
(.close socket)))
(defn- open-server-socket [server]
(reset! (:socket server)
(server-socket server)))
(defn- accept-connection
[{:keys [handler connections socket] :as server}]
(let [conn (.accept @socket)]
(swap! connections conj conn)
(future
(try (handler conn)
(finally (close-socket server conn))))))
(defn running?
"True if the server is running."
[server]
(if-let [socket @(:socket server)]
(not (.isClosed socket))))
(defn start
"Start a TCP server going."
[server]
(open-server-socket server)
(future
(while (running? server)
(try
(accept-connection server)
(catch SocketException _)))))
(defn stop
"Stop the TCP server and close all open connections."
[server]
(doseq [socket @(:connections server)]
(close-socket server socket))
(.close @(:socket server)))
(defn wrap-streams
"Wrap a handler so that it expects an InputStream and an OutputStream
as arguments, rather than a raw Socket."
[handler]
(fn [socket]
(with-open [input (.getInputStream socket)
output (.getOutputStream socket)]
(handler input output))))
(defn wrap-io
"Wrap a handler so that it expects a Reader and Writer as arguments, rather
than a raw Socket."
[handler]
(wrap-streams
(fn [input output]
(with-open [reader (io/reader input)
writer (io/writer output)]
(handler reader writer)))))