This commit is contained in:
2026-03-08 22:49:39 +01:00
parent 5d29d6f3b6
commit c752553d2e
14 changed files with 255 additions and 52 deletions

View File

@@ -3,9 +3,12 @@
(require "racket-webview-qt.rkt"
"utils.rkt"
"mimetypes.rkt"
web-server/servlet
web-server/servlet-env
racket/async-channel
web-server/http
web-server/servlet-dispatch
web-server/web-server
web-server/servlet-env
(prefix-in c: net/cookies)
net/url
racket/runtime-path
racket/file
@@ -19,6 +22,8 @@
(prefix-in g: gregor)
(prefix-in g: gregor/time)
gregor-utils
lru-cache
racket-self-signed-cert
)
(provide webview-create
@@ -95,9 +100,6 @@
;; Web server
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define current-servlet-port 8083)
(define current-window-nr 1)
(define-runtime-path js-path "../js")
(define (default-boilerplate-js)
@@ -106,16 +108,20 @@
(define-struct wv
([handle #:mutable]
port
[port #:mutable]
[window-nr #:mutable]
[file-getter #:mutable]
[boilerplate-js #:mutable]
[webserver-thread #:mutable])
[webserver-thread #:mutable]
[request-count #:mutable]
[sec-token-cache #:mutable]
[cert-ou-token #:mutable]
)
#:transparent)
(define (process-html wv-handle path out)
(let ((html (file->string path))
(boilerplate-js (wv-boilerplate-js wv-handle)))
(boilerplate-js ((wv-boilerplate-js wv-handle))))
(set! html (string-replace html "<head>"
(string-append "<head>" "\n"
"<script>" "\n"
@@ -127,43 +133,120 @@
(let ((content (file->bytes path)))
(display content out)))
(define (web-serve wv-handle req)
(let* ((path (url->string (request-uri req)))
(file-getter (wv-file-getter wv-handle)))
(let* ((file-to-serve (build-path (file-getter path)))
(ext-bytes (path-get-extension file-to-serve))
(ext (if (eq? ext-bytes #f) #f
(string->symbol (string-downcase (substring (bytes->string/utf-8 ext-bytes) 1)))))
)
(if (file-exists? file-to-serve)
(response/output
#:mime-type (string->bytes/utf-8 (mimetype-for-ext ext))
(λ (out)
(if (or (eq? ext 'html) (eq? ext 'htm))
(process-html wv-handle file-to-serve out)
(process-file wv-handle ext file-to-serve out))
))
(response/output
#:code 404
(λ (out)
(displayln (format "~a not found" path) out)))
)
)
(define (make-security-token)
(letrec ((f (λ (n)
(if (= 0 n)
""
(string-append
(string (integer->char (+ 97 (random 26))))
(f (- n 1)))))))
(string->symbol (f 20))))
(define (get-security-token req)
(let* ((headers (request-headers/raw req))
(cookie-header (findf (λ (h)
(eq? (string->symbol
(format "~a" (header-field h)))
'Cookie))
headers)))
(if (eq? cookie-header #f)
#f
(let ((cookies (c:cookie-header->alist (header-value cookie-header))))
(displayln (format "Cookies: ~a" cookies))
(let ((sec-token (findf (λ (c)
(eq? (string->symbol
(format "~a" (car c)))
'rkt-webview-token))
cookies)))
(if (eq? sec-token #f)
#f
(string->symbol (format "~a" (cdr sec-token)))))))))
(define (make-sec-header sec-cache)
(let ((tok (make-security-token)))
(lru-add! sec-cache tok)
(displayln (format "new sec-token: ~a" tok))
(make-header #"Set-Cookie"
(string->bytes/utf-8
(format "rkt-webview-token=~a" tok)))
)
)
(define (start-web-server h)
(thread (λ ()
(serve/servlet
(λ (req) (web-serve h req))
#:listen-ip "127.0.0.1"
#:port (wv-port h)
#:command-line? #t
#:servlet-path ""
#:stateless? #t
;#:launch-browser #f
#:servlet-regexp #rx"")
)))
(define (web-serve wv-handle req)
(let* ((path (url->string (request-uri req)))
(file-getter (wv-file-getter wv-handle))
(token (get-security-token req))
(sec-cache (wv-sec-token-cache wv-handle))
(cache-empty? (lru-empty? sec-cache))
(token-in-cache? (not (or (eq? token #f)
(eq? (lru-has? sec-cache token) #f))))
)
(if (and (eq? token-in-cache? #f) (not cache-empty?))
(response/output
#:code 401
(λ (out)
#t))
(let* ((file-to-serve (build-path (file-getter path)))
(ext-bytes (path-get-extension file-to-serve))
(ext (if (eq? ext-bytes #f) #f
(string->symbol
(string-downcase
(substring (bytes->string/utf-8 ext-bytes) 1)))))
)
(if (file-exists? file-to-serve)
(response/output
#:mime-type (string->bytes/utf-8 (mimetype-for-ext ext))
#:headers (list (make-sec-header sec-cache))
(λ (out)
(if (or (eq? ext 'html) (eq? ext 'htm))
(process-html wv-handle file-to-serve out)
(process-file wv-handle ext file-to-serve out))
))
(response/output
#:code 404
#:headers (list (make-sec-header sec-cache))
(λ (out)
(displayln (format "~a not found" path) out)))
)
)
)
)
)
(define (start-web-server h channel cert)
(if (eq? cert #f)
(thread (λ ()
(serve
#:dispatch (dispatch/servlet
(λ (req) (web-serve h req)))
#:listen-ip "127.0.0.1"
#:port 0
#:confirmation-channel channel
)
)
)
(let* ((f1 "c:/tmp/my.crt")
(f2 "c:/tmp/my.key")
(fh1 (open-output-file f1 #:exists 'replace))
(fh2 (open-output-file f2 #:exists 'replace)))
(display (certificate cert) fh1)
(display (private-key cert) fh2)
(close-output-port fh1)
(close-output-port fh2)
(thread (λ ()
(serve
#:dispatch (dispatch/servlet
(λ (req) (web-serve h req)))
#:dispatch-server-connect@ (make-ssl-connect@ f1 f2)
#:listen-ip "127.0.0.1"
#:port 0
#:confirmation-channel channel
)
)
)
)
)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -271,25 +354,33 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (webview-create file-getter event-callback
#:boilerplate-js [bj (default-boilerplate-js)]
#:boilerplate-js [bj (λ () (default-boilerplate-js))]
#:parent [p #f])
(let* ((h (make-wv #f current-servlet-port -1 file-getter bj #f))
(server (let ((s (start-web-server h)))
(let* ((h (make-wv #f 0 -1 file-getter bj #f 0 (make-lru 250 #:cmp eq?)
(symbol->string (make-security-token))))
(cert (generate-self-signed-cert 2048 365 '("127.0.0.1" "localhost")
"NL" "Dijkema"
#:ou (wv-cert-ou-token h)))
(channel (make-async-channel))
(server (let ((s (start-web-server h channel cert)))
(sleep 1) ;;; TODO: Check if web server is up.
s))
(port-nr (let ((pn (async-channel-get channel)))
(set-wv-port! h pn)
pn))
(event-processor (λ (wv evt)
(event-callback h (util-parse-event evt))))
(ph (if (wv? p) (wv-handle p) #f))
(wv (rkt-webview-create ph event-processor))
(base-req (format "http://127.0.0.1:~a"
(wv-port h)))
(base-req (format "https://127.0.0.1:~a" (wv-port h)))
)
(set-wv-handle! h wv)
(set-wv-window-nr! h (rkt-wv-win wv))
(set-wv-webserver-thread! h server)
(rkt-webview-set-ou-token (wv-handle h) (wv-cert-ou-token h))
(rkt-webview-set-url! (wv-handle h) base-req)
(set! current-servlet-port (+ current-servlet-port 1))
h))
h)
)
(define/contract (webview-devtools wv)
(-> wv? symbol?)
@@ -680,7 +771,8 @@
(define (test)
(let* ((cb (λ (handle evt)
(displayln evt)))
(h (webview-create file-getter cb)))
(h (webview-create file-getter cb))
)
(webview-set-title! h "This is a test window")
(webview-resize h 800 600)
(webview-move h 350 220)