https://github.com/akkartik/mu1/blob/master/092socket.mu
1
2
3
4 scenario example-server-test [
5 local-scope
6
7
8
9 make-random-nondeterministic
10 port:num <- random-in-range null/real-random-numbers, 8000, 8100
11 run [
12 socket:num <- $open-server-socket port
13 assert socket, [
14 F - example-server-test: $open-server-socket failed]
15 handler-routine:number <- start-running serve-one-request socket, example-handler
16 ]
17 source:&:source:char <- start-reading-from-network null/real-resources, [localhost/], port
18 response:text <- drain source
19 10:@:char/raw <- copy *response
20 memory-should-contain [
21 10:array:character <- [abc]
22 ]
23 socket <- $close-socket socket
24 ]
25
26 def example-handler query:text -> response:text [
27 local-scope
28 load-inputs
29 return [abc]
30 ]
31
32
33
34
35 scenario example-client-test [
36 local-scope
37 assume-resources [
38 [example.com/] <- [
39 |abc|
40 ]
41 ]
42 run [
43 source:&:source:char <- start-reading-from-network resources, [example.com/]
44 ]
45 contents:text <- drain source
46 10:@:char/raw <- copy *contents
47 memory-should-contain [
48 10:array:character <- [abc
49 ]
50 ]
51 ]
52
53 type request-handler = (recipe text -> text)
54
55 def serve-one-request socket:num, request-handler:request-handler -> socket:num [
56 local-scope
57 load-inputs
58 session:num <- $accept socket
59 assert session, [
60 F - example-server-test: $accept failed]
61 contents:&:source:char, sink:&:sink:char <- new-channel 30
62 start-running receive-from-socket session, sink
63 query:text <- drain contents
64 response:text <- call request-handler, query
65 write-to-socket session, response
66 session <- $close-socket session
67 ]
68
69 def start-reading-from-network resources:&:resources, uri:text -> contents:&:source:char [
70 local-scope
71 load-inputs
72 {
73 port:num, port-found?:boolean <- next-input
74 break-if port-found?
75 port <- copy 80/http-port
76 }
77 {
78 break-unless resources
79
80 contents <- start-reading-from-fake-resource resources, uri
81 return
82 }
83
84 host:text, path:text <- split-at uri, 47/slash
85 socket:num <- $open-client-socket host, port
86 assert socket, [contents]
87 req:text <- interpolate [GET _ HTTP/1.1], path
88 request-socket socket, req
89 contents:&:source:char, sink:&:sink:char <- new-channel 10000
90 start-running receive-from-client-socket-and-close socket, sink
91 ]
92
93 def request-socket socket:num, s:text -> socket:num [
94 local-scope
95 load-inputs
96 write-to-socket socket, s
97 $write-to-socket socket, 13/cr
98 $write-to-socket socket, 10/lf
99
100 $write-to-socket socket, 13/cr
101 $write-to-socket socket, 10/lf
102 ]
103
104 def receive-from-socket socket:num, sink:&:sink:char -> sink:&:sink:char, socket:num [
105 local-scope
106 load-inputs
107 {
108 +next-attempt
109 c:char, found?:bool, eof?:bool, error:num <- $read-from-socket socket
110 break-if eof?
111 break-if error
112 {
113 break-unless found?
114 sink <- write sink, c
115 }
116 {
117 break-if found?
118 switch
119 }
120 loop
121 }
122 sink <- close sink
123 ]
124
125 def receive-from-client-socket-and-close socket:num, sink:&:sink:char -> sink:&:sink:char, socket:num [
126 local-scope
127 load-inputs
128 sink <- receive-from-socket socket, sink
129 socket <- $close-socket socket
130 ]
131
132 def write-to-socket socket:num, s:text [
133 local-scope
134 load-inputs
135 len:num <- length *s
136 i:num <- copy 0
137 {
138 done?:bool <- greater-or-equal i, len
139 break-if done?
140 c:char <- index *s, i
141 $write-to-socket socket, c
142 i <- add i, 1
143 loop
144 }
145 ]
146
147
148 def split-at text:text, delim:char -> x:text, y:text [
149 local-scope
150 load-inputs
151
152 len:num <- length *text
153 {
154 empty?:bool <- equal len, 0
155 break-unless empty?
156 x:text <- new []
157 y:text <- new []
158 return
159 }
160 idx:num <- find-next text, delim, 0
161 x:text <- copy-range text, 0, idx
162 y:text <- copy-range text, idx, len
163 ]
164
165 scenario text-split-at [
166 local-scope
167 x:text <- new [a/b]
168 run [
169 y:text, z:text <- split-at x, 47/slash
170 10:@:char/raw <- copy *y
171 20:@:char/raw <- copy *z
172 ]
173 memory-should-contain [
174 10:array:character <- [a]
175 20:array:character <- [/b]
176 ]
177 ]