[ prog / sol / mona ]

prog


Running SchemeBBS using MIT Scheme 11.2

4 2022-05-26 14:07

Let's try to port SchemeBBS to MIT Scheme 11.2.

First, we need a patch to fix this error:

1 ]=> (listen server (string->number (car (command-line))))
;The object #f, passed as an argument to string-length, is not the correct type.

This is the patch:

--- bbs.scm.orig
+++ bbs.scm
@@ -414,4 +414,4 @@
                     (decode-formdata message)
                     (cdr validation)))))
 
-(listen server (string->number (car (command-line))))
+(listen server (string->number (car (command-line-arguments))))

There are no more error messages after applying the patch, but I am unable to view the webpage at localhost:8080. In Firefox, I get "The connection was reset". Does anyone know why?

9 2022-05-29 14:58

>>5
Thank you. To see the errors, I had to remove one dynamic-wind:

--- a/deps/server.scm
+++ b/deps/server.scm
@@ -44,10 +44,7 @@ Initializes our web server.
         (lambda ()
           (do () ((channel-closed? socket))
             (let ((port (tcp-server-connection-accept socket #t #f)))
-              (dynamic-wind
-                (lambda () unspecific)
-                (lambda () (ignore-errors (lambda () (serve-request port))))
-                (lambda () (ignore-errors (lambda () (close-port port))))))))
+              (serve-request port))))
         (lambda () (channel-close socket)))))
 
   ;;; Private helper procedures

It turns out that the hidden error was:

;Unbound variable: guarantee-http-token-string

Apparently, many things in MIT Scheme's runtime/httpio.scm file have changed (the file has been renamed to runtime/http-io.scm). Many variables are gone, and many new ones added. So, I removed runtime/httpio.scm from SchemeBBS:

--- a/bbs.scm
+++ b/bbs.scm
@@ -11,7 +11,6 @@
 (load "lib/utils")
 (load "deps/irregex")
 (load "deps/srfi-26")
-(load "deps/httpio")
 (load "deps/server")
 (load "lib/html")
 (load "lib/parameters")
@@ -414,4 +413,4 @@
                     (decode-formdata message)
                     (cdr validation)))))
 
-(listen server (string->number (car (command-line))))
+(listen server (string->number (car (command-line-arguments))))

At this point we have another error:

;The object #[textual-i/o-port 14 for channel: #[channel 15]], passed as an argument to #[compiled-procedure 16 ("binary-port" #x2) #x1c #x2f1f6f4], is not the correct type.

This error is caused by the use of read-http-request in deps/server.scm. read-http-request expects a binary port, but it is given a textual port created by tcp-server-connection-accept. Maybe we should replace tcp-server-connection-accept with tcp-server-binary-connection-accept, which creates a binary port:

--- a/deps/server.scm
+++ b/deps/server.scm
@@ -43,11 +43,8 @@ Initializes our web server.
         (lambda () unspecific)
         (lambda ()
           (do () ((channel-closed? socket))
-            (let ((port (tcp-server-connection-accept socket #t #f)))
-              (dynamic-wind
-                (lambda () unspecific)
-                (lambda () (ignore-errors (lambda () (serve-request port))))
-                (lambda () (ignore-errors (lambda () (close-port port))))))))
+            (let ((port (tcp-server-binary-connection-accept socket #t #f)))
+              (serve-request port))))
         (lambda () (channel-close socket)))))
 
   ;;; Private helper procedures

Oops, another error:

;The object 3, passed as the second argument to vector-ref, is not in the correct range.

It turns out that we still need the modifications made to deps/httpio.scm. We can include those modifications by adding these lines to the beginning of bbs.scm using a technique similar to https://textboard.org/prog/140/88-89 :

(ge '(runtime http-i/o))

(load-option '*parser)

(define parse-request-line
  (*parser
    (seq (match (+ (char-set char-set:http-token)))
         " "
         (alt (map intern (match "*"))
              parse-uri
              parse-uri-authority)
         " "
         parse-http-version)))

(define (read-http-request port)
  (let ((line (read-ascii-line port)))
    (if (eof-object? line)
      line
      (receive (method uri version)
        (parse-line parse-request-line line "HTTP request line")
        (let ((headers (read-http-headers port)))
          (let ((b.t (or (%read-chunked-body headers port)
                         (%read-delimited-body headers port)
                         '())))
            (if (null? b.t)
              (make-http-request method uri version headers (bytevector))
              (make-http-request method uri version
                                 (append! headers (cdr b.t))
                                 (car b.t)))))))))

(ge '(user))

At this point, we get yet another error:

;Error while parsing RFC 2822 headers: Illegal character: peek-ascii-char

This error is caused by peek-ascii-char in runtime/rfc2822-headers.scm when it peeks at a carriage return character. peek-ascii-char only considers ASCII decimal 32 to 126 (inclusive) to be valid ASCII characters. So, let's try to patch it by adding this to the beginning of bbs.scm:

(ge '(runtime rfc2822-headers))

(define (peek-ascii-char port)
  (let ((byte (peek-u8 port)))
    (cond ((eof-object? byte)
           byte)
          ((and (fix:<= 0 byte) (fix:<= byte 127))
           (integer->char byte))
          (else (parse-error port "Illegal character:" 'peek-ascii-char)))))

Now, we get another error:

;The object "site root", passed as an argument to make-http-response, is not the correct type

This is easy to fix in bbs.scm:

     ;(pp (http-header 'host headers #f))
     (cond ((equal? method "GET")
 	   (match path
-	     (() () '(200 () "site root"))
+	     (() () `(200 () ,(string->utf8 "site root")))
 	     ((,board) () (view-index board))
 	     ((,board "list") () (view-list board))
 	     ((,board "preferences") () (set-preferences board query-string))

Then, we get another error:

;The object #[binary-i/o-port 35], passed as an argument to write-char, is not the correct type.

This error is raised in the string->tokens procedure in runtime/http-syntax.scm. write-char expects a textual port but we have a binary port.

I am going to take a break at this point. It seems that HTTP functionality is broken in MIT Scheme. For HTTP, some internal procedures expect a binary port while others expect a textual port.

10 2022-05-29 15:12

Addenda for >>9:
* To be clear, the errors appear after trying to access localhost:8080 through a web browser.
* The patched read-http-request procedure above differs from that in deps/httpio.scm. In deps/httpio.scm, we pass an empty string to make-http-request. Here, we pass it an empty bytevector.

12 2022-05-30 03:47

Errata for >>9:

So, I removed runtime/httpio.scm from SchemeBBS

This is supposed to be:
"So, I removed deps/httpio.scm from SchemeBBS"

13 2022-05-30 04:22

>>11

Perhaps a different write-char call is involved.

Yes, you are right. I was mistaken.
The error is raised in the write-http-response procedure in runtime/http-io.scm. The error is caused by the use of newline to write to a binary port. newline can only write to a textual port.

Fix:

--- src/runtime/http-io.scm.orig
+++ src/runtime/http-io.scm
@@ -188,7 +188,8 @@
 	(write-ascii (write-to-string (http-response-status response)) port)
 	(write-u8 (char->integer #\space) port)
 	(write-ascii (http-response-reason response) port)
-	(newline port)
+	(write-u8 (char->integer #\return) port)
+	(write-u8 (char->integer #\newline) port)
 	(write-http-headers (http-response-headers response) port)))
   (write-bytevector (http-response-body response) port)
   (flush-output-port port))
14 2022-05-30 04:50

>>13
Add that fix to the beginning of bbs.scm:

(ge '(runtime http-i/o))

(define (write-http-response response port)
  (if (http-response-version response)
    (begin
      (write-http-version (http-response-version response) port)
      (write-u8 (char->integer #\space) port)
      (write-ascii (write-to-string (http-response-status response)) port)
      (write-u8 (char->integer #\space) port)
      (write-ascii (http-response-reason response) port)
      (write-u8 (char->integer #\return) port)
      (write-u8 (char->integer #\newline) port)
      (write-http-headers (http-response-headers response) port)))
  (write-bytevector (http-response-body response) port)
  (flush-output-port port))

(ge '(user))

At this point, localhost:8080 should successfully display "site root"!

One more thing: I should have closed the port after serving each request.

--- a/deps/server.scm
+++ b/deps/server.scm
@@ -43,11 +43,9 @@ Initializes our web server.
         (lambda () unspecific)
         (lambda ()
           (do () ((channel-closed? socket))
-            (let ((port (tcp-server-connection-accept socket #t #f)))
-              (dynamic-wind
-                (lambda () unspecific)
-                (lambda () (ignore-errors (lambda () (serve-request port))))
-                (lambda () (ignore-errors (lambda () (close-port port))))))))
+            (let ((port (tcp-server-binary-connection-accept socket #t #f)))
+              (serve-request port)
+              (close-port port))))  ;; <- CLOSE PORT!
         (lambda () (channel-close socket)))))

Now that we have successfully displayed the site root, it's time to fix the board indexes, board lists, new thread creation, etc.

15 2022-05-30 06:15

Now, let's try to fix board indexes, board lists, and new thread creation.
All HTTP bodies are now represented using bytevectors, not strings. Fix:

--- a/deps/server.scm
+++ b/deps/server.scm
@@ -287,10 +285,10 @@ Initializes our web server.
 
 ;;; reads the string content at the given file path:
 (define (read-file filename)
-  (call-with-input-file filename
-                        (lambda (port)
-                          (read-string (char-set) port))))
-
+  (string->utf8
+    (call-with-input-file filename
+                          (lambda (port)
+                            (read-string (char-set) port)))))

(alternative fix: open file using open-binary-input-file, read file using call-with-port and read-bytevector)

and also:

--- a/bbs.scm
+++ b/bbs.scm
@@ -79,7 +79,7 @@
     ;(pp (http-header 'host headers #f))
     (cond ((equal? method "GET")
 	   (match path
-	     (() () '(200 () "site root"))
+	     (() () `(200 () ,(string->utf8 "site root")))
 	     ((,board) () (view-index board))
 	     ((,board "list") () (view-list board))
 	     ((,board "preferences") () (set-preferences board query-string))
@@ -102,11 +102,11 @@
 
 ;;; errors
 (define bad-request
-  `(400 () "Bad Request"))
+  `(400 () ,(string->utf8 "Bad Request")))
 (define not-found
-  `(404 () "Not found"))
+  `(404 () ,(string->utf8 "Not found")))
 (define method-not-allowed
-  '(405 () "Method not allowed"))
+  `(405 () ,(string->utf8 "Method not allowed")))
 
 (define (title board)
   (string-append "/" board "/ - SchemeBBS"))
@@ -215,7 +215,7 @@
            (let* ((t (call-with-input-file path read))
                   (posts (lookup-def 'posts t))
                   (post-number (+ 1 (car (last posts))))
-                  (body (http-request-body req))
+                  (body (utf8->string (http-request-body req)))
                   (params (parameters->alist body))
                   (frontpage (lookup-def 'frontpage params))
                   (message (decode-formdata (lookup-def 'epistula params)))
@@ -252,11 +252,11 @@
 		    (if newthread?
 			(add-query-string (string-append "/" board) query-string)
 			(string-append (add-query-string (string-append "/" board) query-string) "#t" thread "p" post))))
-	    "That was SICP quality!")
+	    ,(string->utf8 "That was SICP quality!"))
       `(303 ,(list (make-http-header
 		    'location
 		    (string-append  (add-query-string (string-append  "/" board "/" thread) query-string) "#t" thread "p" post)))
-	    "That was SICP quality")))
+	    ,(string->utf8 "That was SICP quality"))))
 
 (define (update-post-count board thread date post-count)
   (let ((cache (make-path *html* board "list")))
@@ -316,7 +316,7 @@
                 (threads (if (file-exists? list-path)
                              (call-with-input-file list-path read)
                              '()))
-                (body (http-request-body req))
+                (body (utf8->string (http-request-body req)))
                 (params (parameters->alist body))
                 (message (decode-formdata (lookup-def 'epistula params)))
                 (headline (decode-formdata (lookup-def 'titulus params)))

Then, to make redirections work correctly when creating new threads and messages, we need to add this fix to the beginning of bbs.scm: https://textboard.org/prog/140/88

SchemeBBS now works on MIT Scheme 11.2!

There's one problem left: multi-byte characters. If you try to submit "一二三" through the textarea, you will get something like "一二三". Not sure how to fix this.

56


VIP:

do not edit these