websockets: Initial websocket support.
[8sync.git] / 8sync / systems / websocket / frame.scm
1 ;;; guile-websocket --- WebSocket client/server
2 ;;; Copyright © 2015 David Thompson <davet@gnu.org>
3 ;;;
4 ;;; This file is part of guile-websocket.
5 ;;;
6 ;;; Guile-websocket is free software; you can redistribute it and/or modify
7 ;;; it under the terms of the GNU Lesser General Public License as
8 ;;; published by the Free Software Foundation; either version 3 of the
9 ;;; License, or (at your option) any later version.
10 ;;;
11 ;;; Guile-websocket is distributed in the hope that it will be useful,
12 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 ;;; Lesser General Public License for more details.
15 ;;;
16 ;;; You should have received a copy of the GNU Lesser General Public
17 ;;; License along with guile-websocket.  If not, see
18 ;;; <http://www.gnu.org/licenses/>.
19
20 ;;; Commentary:
21 ;;
22 ;; WebSocket frame abstraction.
23 ;;
24 ;;; Code:
25
26 (define-module (8sync systems websocket frame)
27   #:use-module (rnrs bytevectors)
28   #:use-module (rnrs io ports)
29   #:use-module (ice-9 format)
30   #:use-module (ice-9 match)
31   #:use-module (ice-9 rdelim)
32   #:use-module (srfi srfi-1)
33   #:use-module (srfi srfi-9)
34   #:use-module (srfi srfi-9 gnu)
35   #:use-module (srfi srfi-26)
36   #:export (make-frame
37             frame?
38             frame-final?
39             frame-type
40             frame-masking-key
41             frame-data
42
43             make-ping-frame
44             make-pong-frame
45             make-close-frame
46             make-text-frame
47             make-binary-frame
48
49             continuation-frame?
50             text-frame?
51             binary-frame?
52             close-frame?
53             ping-frame?
54             pong-frame?
55             fragment-frame?
56             first-fragment-frame?
57             final-fragment-frame?
58             control-frame?
59             data-frame?
60
61             frame-length
62             frame-concatenate
63             text-frame->string
64             text-frames->string
65
66             read-frame
67             write-frame))
68
69 ;;;
70 ;;; WebSocket frames
71 ;;;
72
73 (define-record-type <frame>
74   (make-frame final? type masking-key data)
75   frame?
76   (final? frame-final?)
77   (type frame-type)
78   (masking-key frame-masking-key)
79   (data frame-data))
80
81 (define (display-frame frame port)
82   (format port "#<frame final?: ~a type: ~a masking-key: ~a length: ~d>"
83           (frame-final? frame)
84           (frame-type frame)
85           (frame-masking-key frame)
86           (frame-length frame)))
87
88 (set-record-type-printer! <frame> display-frame)
89
90 (define* (make-ping-frame bv #:optional masking-key)
91   "Return a \"ping\" control frame containing the contents of the
92 bytevector BV, masked with MASKING-KEY.  By default, the data is
93 unmasked."
94   (make-frame #t 'ping masking-key bv))
95
96 (define* (make-pong-frame bv #:optional masking-key)
97   "Return a \"pong\" control frame containing the contents of the
98 bytevector BV, masked with MASKING-KEY.  By default, the data is
99 unmasked."
100   (make-frame #t 'pong masking-key bv))
101
102 (define* (make-close-frame bv #:optional masking-key)
103   "Return a \"close\" control frame containing the contents of the
104 bytevector BV, masked with MASKING-KEY.  By default, the data is
105 unmasked."
106   (make-frame #t 'close masking-key bv))
107
108 (define* (make-text-frame text #:optional masking-key)
109   "Return a text data frame containing the string TEXT, masked with MASKING-KEY.
110 By default, the text is unmasked."
111   (make-frame #t 'text masking-key (string->utf8 text)))
112
113 (define* (make-binary-frame bv #:optional masking-key)
114   "Return a binary data frame containing the contents of the
115 bytevector BV, masked with MASKING-KEY.  By default, the data is
116 unmasked."
117   (make-frame #t 'binary masking-key bv))
118
119 (define (continuation-frame? frame)
120   "Return #t if FRAME is a continuation frame."
121   (eq? (frame-type frame) 'continuation))
122
123 (define (text-frame? frame)
124   "Return #t if FRAME is a text frame."
125   (eq? (frame-type frame) 'text))
126
127 (define (binary-frame? frame)
128   "Return #t if FRAME is a binary frame."
129   (eq? (frame-type frame) 'binary))
130
131 (define (close-frame? frame)
132   "Return #t if FRAME is a close frame."
133   (eq? (frame-type frame) 'close))
134
135 (define (ping-frame? frame)
136   "Return #t if FRAME is a ping frame."
137   (eq? (frame-type frame) 'ping))
138
139 (define (pong-frame? frame)
140   "Return #t if FRAME is a pong frame."
141   (eq? (frame-type frame) 'pong))
142
143 ;; See section 5.4 - Fragmentation
144 (define (fragment-frame? frame)
145   "Return #t if FRAME is an incomplete message."
146   (or (continuation-frame? frame)
147       (not (frame-final? frame))))
148
149 (define (first-fragment-frame? frame)
150   "Return #t if FRAME is the first piece of a fragmented message."
151   (and (not (frame-final? frame))
152        (data-frame? frame)))
153
154 (define (final-fragment-frame? frame)
155   "Return #t if FRAME is the final piece of a fragmented message."
156   (and (frame-final? frame)
157        (continuation-frame? frame)))
158
159 ;; See section 5.5 - Control Frames
160 (define (control-frame? frame)
161   "Return #t if FRAME is a control frame."
162   (or (close-frame? frame)
163       (ping-frame? frame)
164       (pong-frame? frame)))
165
166 ;; See section 5.6 - Data Frames
167 (define (data-frame? frame)
168   "Return #t if FRAME is a data frame."
169   (or (text-frame? frame)
170       (binary-frame? frame)))
171
172 (define (frame-length frame)
173   "Return the length of the data bytevector in FRAME."
174   (bytevector-length (frame-data frame)))
175
176 (define (text-frame->string frame)
177   "Convert FRAME, an unfragmented text frame, into a string."
178   (utf8->string (frame-data frame)))
179
180 (define (frame-concatenate frames)
181   "Concatenate the data in FRAMES, a list of fragmented frames, into a
182 single bytevector."
183   (let ((bv (make-bytevector (reduce + 0 (map frame-length frames)))))
184     (let loop ((frames frames)
185                (offset 0))
186       (match frames
187         (() bv)
188         ((frame . rest)
189          (let ((length (frame-length frame)))
190            (bytevector-copy! (frame-data frame) 0 bv offset length)
191            (loop rest (+ offset length))))))))
192
193 (define (text-frames->string frames)
194   "Convert FRAMES, a list of fragmented text frames, into a single
195 concatenated string."
196   (utf8->string (frame-concatenate frames)))
197
198 (define (call-with-input-bytevector bv proc)
199   "Call PROC with one argument: an open input port that reads from the
200 bytevector BV."
201   (let ((port (open-bytevector-input-port bv)))
202     (dynamic-wind
203       (const #t)
204       (lambda ()
205         (proc port))
206       (lambda ()
207         (close-port port)))))
208
209 (define (close-frame->status frame)
210   "Convert FRAME, a close frame, into a pair.  The \"car\" of the pair
211 is a positive integer status code, and the \"cdr\" is a string
212 containing the explanation, if present."
213   (define (read-status-code)
214     (match (bytevector-sint-ref (frame-data frame) 0 (endianness big) 2)
215       ;; See section 7.4
216       ((and (or 1005 1006 1015) status)
217        (websocket-error "invalid use of reserved status code: " status))
218       (status status)))
219
220   (let ((length (frame-length frame)))
221     (cond
222      ((zero? length) ; unspecified closing status
223       '(1005 . ""))
224      ((= length 2) ; status code only
225       (cons (read-status-code) ""))
226      (else ; status + reason
227       (cons (read-status-code)
228             (call-with-input-bytevector (frame-data frame)
229               (lambda (port)
230                 ;; Throw away the status code.
231                 (get-u8 port)
232                 (get-u8 port)
233                 ;; Now read the reason.
234                 (read-string port))))))))
235
236 \f
237 ;;;
238 ;;; Frame reader
239 ;;;
240
241 ;; See section 5.3 - Client-to-Server Masking
242 (define (mask-bytevector! bv masking-key)
243   "Apply the WebSocket masking algorithm to the bytevector BV using
244 MASKING-KEY."
245   (let loop ((i 0))
246     (when (< i (bytevector-length bv))
247       (let ((masked (logxor (u8vector-ref bv i)
248                             (u8vector-ref masking-key (modulo i 4)))))
249         (u8vector-set! bv i masked)
250         (loop (1+ i))))))
251
252 (define (websocket-error message . args)
253   (apply error message args))
254
255 ;; See section 5.2 - Base Framing Protocol
256 (define (read-frame port)
257   (define (opcode->frame-type opcode)
258     (match opcode
259       (#x0 'continuation)
260       (#x1 'text)
261       (#x2 'binary)
262       (#x8 'close)
263       (#x9 'ping)
264       (#xA 'pong)
265       (else (websocket-error "invalid opcode: " opcode))))
266
267   (define (control-frame? type)
268     (memq type '(close ping pong)))
269
270   (define (parse-fin-bit octet)
271     ;; Test the first bit of the octet.
272     (not (zero? (logand #x80 octet))))
273
274   (define (parse-opcode octet final?)
275     ;; The opcode is stored in the least significant nibble of the
276     ;; octet.
277     (let ((type (opcode->frame-type (logand #x0f octet))))
278       ;; Section 5.5 specifies that control frames must not be
279       ;; fragmented.
280       (when (and (not final?) (control-frame? type))
281         (websocket-error "fragmented control frame: " type))
282       type))
283
284   (define (parse-mask-bit octet)
285     (not (zero? (logand #x80 octet))))
286
287   (define (parse-length octet)
288     ;; For lengths <= 125, the frame length is encoded in the last 7
289     ;; bits of the octet.  If this number is 126, then the true length
290     ;; is encoded in the following 16 bits.  If the number is 127,
291     ;; then the true length is encoded in the following 64 bits.
292     (match (logand #x7f octet)
293       (126
294        (bytevector-u16-ref (get-bytevector-n port 2) 0 (endianness big)))
295       (127
296        (bytevector-u64-ref (get-bytevector-n port 8) 0 (endianness big)))
297       (length length)))
298
299   (define (parse-masking-key)
300     ;; Masking keys are always 32 bits.
301     (get-bytevector-n port 4))
302
303   (define (read-data type masking-key length)
304     ;; Section 5.5 specifies that control frame bodies may not exceed
305     ;; 125 bytes.
306     (when (and (> length 125)
307                (control-frame? type))
308       (websocket-error "control frame too large: " type length))
309
310     (let ((bv (get-bytevector-n port length)))
311       (when masking-key
312         (mask-bytevector! bv masking-key))
313       bv))
314
315   (let* ((type-byte (get-u8 port))
316          (length-byte (get-u8 port))
317          (final? (parse-fin-bit type-byte))
318          (type (parse-opcode type-byte final?))
319          (mask? (parse-mask-bit length-byte))
320          (length (parse-length length-byte))
321          (masking-key (and mask? (parse-masking-key)))
322          (data (read-data type masking-key length)))
323     (make-frame final? type masking-key data)))
324
325 \f
326 ;;;
327 ;;; Frame writer
328 ;;;
329
330 (define* (write-frame frame #:optional (port (current-output-port)))
331   ;; Packs an unsigned integer into a bytevector in network byte
332   ;; order.
333   (define (uint->bytevector n size)
334     (uint-list->bytevector (list n) (endianness big) size))
335
336   (define (masked-data mask data)
337     (let ((bv (bytevector-copy data)))
338       (mask-bytevector! bv mask)
339       bv))
340
341   (let ((length (frame-length frame))
342         (mask   (frame-masking-key frame))
343         (data   (frame-data frame)))
344     ;; Write FIN bit and opcode.
345     (put-u8 port
346             (logior (if (frame-final? frame) #x80 #x00)
347                     (match (frame-type frame)
348                       ('continuation #x00)
349                       ('text         #x01)
350                       ('binary       #x02)
351                       ('close        #x08)
352                       ('ping         #x09)
353                       ('pong         #x0A))))
354
355     ;; Write mask bit and length.
356     (put-u8 port
357             (logior (if mask #x80 #x00)
358                     (cond
359                      ((< length 126) length)
360                      ((< length (expt 2 16)) 126)
361                      (else 127))))
362
363     ;; Write true size when size is greater than 125.
364     (cond
365      ((< length 126) #f)
366      ((< length (expt 2 16))
367       (put-bytevector port (uint->bytevector length 2)))
368      (else
369       (put-bytevector port (uint->bytevector length 8))))
370
371     ;; Write masking key, if present.
372     (when mask (put-bytevector port mask))
373
374     ;; Write data, potentially masked.
375     (put-bytevector port (if mask (masked-data mask data) data))))