@@ -34,10 +34,9 @@ Rebol [
34
34
10-May-2020 "Oldes" {Implemented directory listing, logging and multipart POST processing}
35
35
02-Jul-2020 "Oldes" {Added possibility to stop server and return data from client (useful for OAuth2)}
36
36
]
37
+ needs: [mime-types]
37
38
]
38
39
39
- import 'mime-types
40
-
41
40
append system/options/log [httpd: 1 ]
42
41
43
42
;------------------------------------------------------------------------
@@ -343,6 +342,21 @@ sys/make-scheme [
343
342
]
344
343
]
345
344
345
+ On-Read-Websocket : func [
346
+ "Process READ action on client's port using websocket"
347
+ ctx [object! ]
348
+ final? [logic! ] "Indicates that this is the final fragment in a message."
349
+ opcode [integer! ] "Defines the interpretation of the 'Payload data'."
350
+ ] [
351
+ ;@@ this is just a placeholder!
352
+ ]
353
+ On-Close-Websocket : func [
354
+ "Process READ action on client's port using websocket"
355
+ ctx [object! ] code [integer! ]
356
+ ] [
357
+ ;@@ this is just a placeholder!
358
+ ]
359
+
346
360
On-List-Dir : func [
347
361
ctx [object! ] target [object! ]
348
362
/local path dir out size date files dirs
@@ -399,10 +413,25 @@ sys/make-scheme [
399
413
sys/log/more 'HTTPD ["Target not found:^[ [1m" mold target/file ]
400
414
ctx/out/status: 404
401
415
]
416
+
417
+ WS-handshake : func [ ctx /local key] [
418
+ if all [
419
+ "websocket" = select ctx/inp/header 'Upgrade
420
+ key: select ctx/inp/header 'Sec-WebSocket-Key
421
+ ][
422
+ ctx/out/status: 101
423
+ ctx/out/header/Upgrade: "websocket"
424
+ ctx/out/header/Connection: "Upgrade"
425
+ ctx/out/header/Sec-WebSocket-Accept: enbase checksum join key "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 'sha1 64
426
+ ;? ctx/out/header
427
+ ;ctx/out/content: ""
428
+ ]
429
+ ]
402
430
]
403
431
404
432
Status-Codes: make map! [
405
433
100 "Continue"
434
+ 101 "Switching Protocols"
406
435
200 "OK"
407
436
201 "Created"
408
437
202 "Accepted"
@@ -471,38 +500,44 @@ sys/make-scheme [
471
500
buffer: make binary! 1024
472
501
append buffer ajoin ["HTTP/" ctx/inp/version #" " out/status #" " status-codes/ (out/status ) CRLF]
473
502
474
- unless out/header/Content-Type [
475
- if out/target [
476
- out/header/Content-Type: mime-type? out/target
477
- ]
478
- if all [
479
- none? out/header/Content-Type ; no mime found above
480
- string? out/content
481
- ][
482
- out/header/Content-Type: "text/html; charset=UTF-8"
483
- ]
484
- ]
503
+ either "websocket" = out/header/upgrade [
504
+ ctx/inp/method: "websocket"
505
+ try [ctx/inp/version: to integer! ctx/inp/header/Sec-WebSocket-Version ]
506
+ port/awake: :Awake-Websocket
507
+ ][
508
+ unless out/header/Content-Type [
509
+ if out/target [
510
+ out/header/Content-Type: mime-type? out/target
511
+ ]
512
+ if all [
513
+ none? out/header/Content-Type ; no mime found above
514
+ string? out/content
515
+ ][
516
+ out/header/Content-Type: "text/html; charset=UTF-8"
517
+ ]
518
+ ]
485
519
486
- out/header/Content-Length: either out/content [
487
- if string? out/content [
488
- ; must be converted to binary to have proper length if not ascii
489
- out/content: to binary! out/content
520
+ out/header/Content-Length: either out/content [
521
+ if string? out/content [
522
+ ; must be converted to binary to have proper length if not ascii
523
+ out/content: to binary! out/content
524
+ ]
525
+ length? out/content
526
+ ][
527
+ 0
490
528
]
491
- length? out/content
492
- ][
493
- 0
494
- ]
495
529
496
- if keep-alive: ctx/config/keep-alive [
497
- if logic? keep-alive [
498
- ; using defaults
499
- ctx/config/keep-alive:
500
- keep-alive: [15 100 ] ; [timeout max-requests]
530
+ if keep-alive: ctx/config/keep-alive [
531
+ if logic? keep-alive [
532
+ ; using defaults
533
+ ctx/config/keep-alive:
534
+ keep-alive: [15 100 ] ; [timeout max-requests]
535
+ ]
536
+ ctx/out/header/Connection: "keep-alive"
537
+ ctx/out/header/Keep-Alive: ajoin ["timeout=" keep-alive/1 ", max=" keep-alive/2 ]
501
538
]
502
- ctx/out/header/Connection: "keep-alive"
503
- ctx/out/header/Keep-Alive: ajoin ["timeout=" keep-alive/1 ", max=" keep-alive/2 ]
539
+ out/header/Server: ctx/config/server-name
504
540
]
505
- out/header/Server: ctx/config/server-name
506
541
507
542
;probe out/header
508
543
foreach [name value] out/header [
@@ -680,6 +715,111 @@ sys/make-scheme [
680
715
true
681
716
]
682
717
718
+ Awake-Websocket : function [
719
+ event [event! ]
720
+ ] [
721
+ port: event/port
722
+ ctx: port/extra
723
+
724
+ sys/log/more 'HTTPD ["Awake Websocket:^[ [1m" ctx/remote "^[ [22m" event/type ]
725
+
726
+ ctx/timeout: now + 0:0:30
727
+
728
+ switch event/type [
729
+ READ [
730
+ ready?: false
731
+ data: head port/data
732
+ sys/log/more 'HTTPD ["bytes:^[ [1m" length? data]
733
+ try /except [
734
+ while [2 < length? data][
735
+ final?: data/1 & 128 = 128
736
+ opcode: data/1 & 15
737
+ mask?: data/2 & 128 = 128
738
+ len: data/2 & 127
739
+ data: skip data 2
740
+ ;? final? ? opcode ? len
741
+ case [
742
+ len = 126 [
743
+ if 2 >= length? data [break]
744
+ len: binary/read data 'UI16
745
+ data: skip data 2
746
+ ]
747
+ len = 127 [
748
+ if 8 >= length? data [break]
749
+ len: binary/read data 'UI64
750
+ data: skip data 8
751
+ ]
752
+ ]
753
+ if (4 + length? data) < len [break]
754
+ remove/part head data data
755
+ data: head data
756
+ either mask? [
757
+ request-data: make binary! len
758
+ masks: take/part data 4
759
+ payload: take/part data len
760
+ while [not tail? payload][
761
+ append request-data masks xor take/part payload 4
762
+ ]
763
+ ][
764
+ request-data: take/part data len
765
+ ]
766
+ ready?: true
767
+ clear skip request-data len
768
+ ctx/inp/content: request-data
769
+ if opcode = 8 [
770
+ sys/log/more 'HTTPD "WS Connection Close Frame!"
771
+ code: 0
772
+ if all [
773
+ 2 <= len
774
+ 2 <= length? request-data
775
+ ][
776
+ code: to integer! take/part request-data 2
777
+ sys/log/more 'HTTPD ["WS Close reason:" as-red code]
778
+ ]
779
+ actor/On-Close-Websocket ctx code
780
+ event/type: 'CLOSE
781
+ Awake-Websocket event
782
+ exit
783
+ ]
784
+ actor/On-Read-Websocket ctx final? opcode
785
+ ]
786
+ ][
787
+ print system/state/last-error
788
+ ]
789
+ either ready? [
790
+ ;; there was complete input...
791
+ write port either all [
792
+ series? content: ctx/out/content
793
+ not empty? content
794
+ ][
795
+ content: to binary! content
796
+ clear ctx/out/content
797
+ len: length? content
798
+ ;print len
799
+ ;prin "out: " ? content
800
+ bin: binary len
801
+ binary/write bin case [
802
+ len < 127 [ [UI8 129 UI8 :len :content ] ]
803
+ all [ len > 126 len <= 65535 ][ [UI8 129 UI8 126 UI16 :len :content ] ]
804
+ len > 65535 [ [UI8 129 UI8 127 UI64 :len :content ] ]
805
+ ]
806
+ head bin/buffer
807
+ ][ "" ]
808
+ ][
809
+ ;; needs more data!
810
+ read port
811
+ ]
812
+ ]
813
+ WROTE [
814
+ read port
815
+ ]
816
+ CLOSE [
817
+ sys/log/info 'HTTPD ["Closing:^[ [22m" ctx/remote ]
818
+ if pos: find ctx/parent/extra/clients port [ remove pos ]
819
+ close port
820
+ ]
821
+ ]
822
+ ]
683
823
684
824
685
825
New-Client : func [ port [port! ] /local client info err] [
0 commit comments