1 ;;; guile-websocket --- WebSocket client/server
2 ;;; Copyright © 2015 David Thompson <davet@gnu.org>
4 ;;; This file is part of guile-websocket.
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.
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.
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/>.
22 ;; WebSocket frame abstraction.
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)
73 (define-record-type <frame>
74 (make-frame final? type masking-key data)
78 (masking-key frame-masking-key)
81 (define (display-frame frame port)
82 (format port "#<frame final?: ~a type: ~a masking-key: ~a length: ~d>"
85 (frame-masking-key frame)
86 (frame-length frame)))
88 (set-record-type-printer! <frame> display-frame)
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
94 (make-frame #t 'ping masking-key bv))
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
100 (make-frame #t 'pong masking-key bv))
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
106 (make-frame #t 'close masking-key bv))
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)))
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
117 (make-frame #t 'binary masking-key bv))
119 (define (continuation-frame? frame)
120 "Return #t if FRAME is a continuation frame."
121 (eq? (frame-type frame) 'continuation))
123 (define (text-frame? frame)
124 "Return #t if FRAME is a text frame."
125 (eq? (frame-type frame) 'text))
127 (define (binary-frame? frame)
128 "Return #t if FRAME is a binary frame."
129 (eq? (frame-type frame) 'binary))
131 (define (close-frame? frame)
132 "Return #t if FRAME is a close frame."
133 (eq? (frame-type frame) 'close))
135 (define (ping-frame? frame)
136 "Return #t if FRAME is a ping frame."
137 (eq? (frame-type frame) 'ping))
139 (define (pong-frame? frame)
140 "Return #t if FRAME is a pong frame."
141 (eq? (frame-type frame) 'pong))
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))))
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)))
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)))
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)
164 (pong-frame? frame)))
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)))
172 (define (frame-length frame)
173 "Return the length of the data bytevector in FRAME."
174 (bytevector-length (frame-data frame)))
176 (define (text-frame->string frame)
177 "Convert FRAME, an unfragmented text frame, into a string."
178 (utf8->string (frame-data frame)))
180 (define (frame-concatenate frames)
181 "Concatenate the data in FRAMES, a list of fragmented frames, into a
183 (let ((bv (make-bytevector (reduce + 0 (map frame-length frames)))))
184 (let loop ((frames frames)
189 (let ((length (frame-length frame)))
190 (bytevector-copy! (frame-data frame) 0 bv offset length)
191 (loop rest (+ offset length))))))))
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)))
198 (define (call-with-input-bytevector bv proc)
199 "Call PROC with one argument: an open input port that reads from the
201 (let ((port (open-bytevector-input-port bv)))
207 (close-port port)))))
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)
216 ((and (or 1005 1006 1015) status)
217 (websocket-error "invalid use of reserved status code: " status))
220 (let ((length (frame-length frame)))
222 ((zero? length) ; unspecified closing status
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)
230 ;; Throw away the status code.
233 ;; Now read the reason.
234 (read-string port))))))))
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
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)
252 (define (websocket-error message . args)
253 (apply error message args))
255 ;; See section 5.2 - Base Framing Protocol
256 (define (read-frame port)
257 (define (opcode->frame-type opcode)
265 (else (websocket-error "invalid opcode: " opcode))))
267 (define (control-frame? type)
268 (memq type '(close ping pong)))
270 (define (parse-fin-bit octet)
271 ;; Test the first bit of the octet.
272 (not (zero? (logand #x80 octet))))
274 (define (parse-opcode octet final?)
275 ;; The opcode is stored in the least significant nibble of the
277 (let ((type (opcode->frame-type (logand #x0f octet))))
278 ;; Section 5.5 specifies that control frames must not be
280 (when (and (not final?) (control-frame? type))
281 (websocket-error "fragmented control frame: " type))
284 (define (parse-mask-bit octet)
285 (not (zero? (logand #x80 octet))))
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)
294 (bytevector-u16-ref (get-bytevector-n port 2) 0 (endianness big)))
296 (bytevector-u64-ref (get-bytevector-n port 8) 0 (endianness big)))
299 (define (parse-masking-key)
300 ;; Masking keys are always 32 bits.
301 (get-bytevector-n port 4))
303 (define (read-data type masking-key length)
304 ;; Section 5.5 specifies that control frame bodies may not exceed
306 (when (and (> length 125)
307 (control-frame? type))
308 (websocket-error "control frame too large: " type length))
310 (let ((bv (get-bytevector-n port length)))
312 (mask-bytevector! bv masking-key))
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)))
330 (define* (write-frame frame #:optional (port (current-output-port)))
331 ;; Packs an unsigned integer into a bytevector in network byte
333 (define (uint->bytevector n size)
334 (uint-list->bytevector (list n) (endianness big) size))
336 (define (masked-data mask data)
337 (let ((bv (bytevector-copy data)))
338 (mask-bytevector! bv mask)
341 (let ((length (frame-length frame))
342 (mask (frame-masking-key frame))
343 (data (frame-data frame)))
344 ;; Write FIN bit and opcode.
346 (logior (if (frame-final? frame) #x80 #x00)
347 (match (frame-type frame)
355 ;; Write mask bit and length.
357 (logior (if mask #x80 #x00)
359 ((< length 126) length)
360 ((< length (expt 2 16)) 126)
363 ;; Write true size when size is greater than 125.
366 ((< length (expt 2 16))
367 (put-bytevector port (uint->bytevector length 2)))
369 (put-bytevector port (uint->bytevector length 8))))
371 ;; Write masking key, if present.
372 (when mask (put-bytevector port mask))
374 ;; Write data, potentially masked.
375 (put-bytevector port (if mask (masked-data mask data) data))))