Skip to content

Commit

Permalink
include web stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
mattwparas committed Dec 31, 2024
1 parent 01d4637 commit 525af1c
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 0 deletions.
88 changes: 88 additions & 0 deletions cogs/web/client.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
(require "libs/steel-rustls/rustls.scm")
(require-builtin steel/http)

(provide request/get)

(define APPLICATION-JSON "application/json")
(define APPLICATION-FORM-URL-ENCODED "application/x-www-form-urlencoded")

(define (parse-url-into-parts url)
(~> (trim-start-matches url "https://") (split-once "/")))

(define (read-bytes-exact-into-buf port buf count)
(unless (= count 0)
(define byte (read-byte port))
(when (byte? byte)
(bytes-push! buf byte)
(read-bytes-exact-into-buf port buf (sub1 count)))))

(define (read-body-into-buffer->string content-length output-port buffer)
(cond
[(> content-length 0)
(read-bytes-exact-into-buf output-port buffer content-length)
(bytes->string/utf8 buffer)]
[else #f]))

(define (read-body-into-buffer content-length output-port buffer)
(cond
[(> content-length 0)
(read-bytes-exact-into-buf output-port buffer content-length)
buffer]
[else #f]))

(define (request/get url #:headers [headers '()] #:body [body void])
(define url-parts (parse-url-into-parts url))
(define host (car url-parts))
(define remaining-url (string-append "/" (cadr url-parts)))

(define tls-socket (tcp-connect (string-append host ":443")))

;; TODO: This is an issue - causes a panic
; (define client-connection (client-connection "www.rust-lang.org"))
(define connection (client-connection host))

(define stream (tls-stream connection tls-socket))

(define port (tls-writer stream))
(define reader (tls-reader stream))

(write-bytes (string->bytes (string-append "GET " remaining-url " HTTP/1.1\r\n")) port)
(write-bytes (string->bytes (string-append "Host: " host "\r\n")) port)

;; Go through the headers, add them here
(for-each (lambda (header) (write-bytes (string->bytes header) port)) headers)

(write-bytes (string->bytes "accept: application/json\r\n") port)
(write-bytes (string->bytes "\r\n") port)
(flush-output-port port)

(define buffer (bytevector))
(define (loop)
(define byte (read-byte reader))
(when (byte? byte)
(begin
(bytes-push! buffer byte)

;; If http-parse is not a bool
(let ([http-request (http-parse-response buffer)])
(if http-request
(begin
(define headers (http-response-headers http-request))
(define content-length
(string->int (bytes->string/utf8 (or (hash-try-get headers "Content-Length")
#(0)))))
(define content-type (bytes->string/utf8 (hash-try-get headers "Content-Type")))

;; Wipe out the buffer, since we've already parsed out the headers.
(bytes-clear! buffer)

;; Check the content type:
(define body (read-body-into-buffer content-length reader buffer))

(cond
[(equal? content-type "application/json")
(string->jsexpr (bytes->string/utf8 body))]
[else body]))

(loop))))))
(loop))
125 changes: 125 additions & 0 deletions cogs/web/server.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
(require-builtin steel/tcp)
(require-builtin steel/http)
(require-builtin steel/time)

(require "steel/sync")

(require "steel/logging/log.scm")

;;; Web server

(define (send-as-json port hashmap)
(define response-json (string->bytes (value->jsexpr-string hashmap)))
(define size (bytes-length response-json))
(write-bytes (string->bytes "HTTP/1.1 200 OK\r\n") port)
(write-bytes (string->bytes (to-string "Content-length:" size "\r\n")) port)
(write-bytes (string->bytes "Content-Type: application/json\r\n") port)
(write-bytes (string->bytes "\r\n") port)
(write-bytes response-json port)
(write-bytes (string->bytes "\r\n\r\n") port)
(flush-output-port port))

(define (read-bytes-exact-into-buf port buf count)
(unless (= count 0)
(define byte (read-byte port))
(when (byte? byte)
(bytes-push! buf byte)
(read-bytes-exact-into-buf port buf (sub1 count)))))

;; Vec of bytevectors, return cleared bytevector to the pool
(define (buffer-pool)
(error "TODO"))

(define (parse-keep-alive keep-alive-string)
(apply hash
(map trim
(flatten (map (lambda (p) (split-once p "=")) (split-once keep-alive-string ","))))))

(define EMPTY-KEEP-ALIVE (hash))

;; Router -> function that takes a path and returns a function

(define (read-body-into-buffer content-length output-port buffer)
(cond
[(> content-length 0)
(read-bytes-exact-into-buf output-port buffer content-length)
(bytes->string/utf8 buffer)]
[else #f]))

;; Router should be a function that takes a
;; method and a path, and returns a function
;; that takes the path, the body, and returns
;; a json or something

(define (serve addr thread-pool-size)
; (->/c string? int? void? (->/c string? string? (->/c string? string? hash?)))
(define listener (tcp-listen addr))
(define tp (make-thread-pool thread-pool-size))

(log/info! "Listening on 8080")

(while
#t
(define input-stream-and-addr (tcp-accept-with-addr listener))
(define input-stream (car input-stream-and-addr))
(define addr (cdr input-stream-and-addr))
(submit-task
tp
(lambda ()
(define now (instant/now))
(define output-port (tcp-stream-reader input-stream))
(define writer-port (tcp-stream-writer input-stream))
;; Lets just start with 100 bytes first, and then see what happens?
(define buffer (bytevector))
(define (loop)
(define byte (read-byte output-port))
(when (byte? byte)
(begin
(bytes-push! buffer byte)

;; If http-parse is not a bool
(let ([http-request (http-parse-request buffer)])
(if http-request
(begin
(define headers (http-request-headers http-request))
(define content-length
(string->int (bytes->string/utf8 (or (hash-try-get headers "Content-Length")
#(0)))))
(define content-type (bytes->string/utf8 (hash-try-get headers "Content-Type")))

;; Get the connection kind. If its
(define keep-alive-connection?
(if (hash-try-get headers "Connection")
(equal? (bytes->string/utf8 (hash-get headers "Connection")) "keep-alive")
#f))

(define keep-alive-params
(if (hash-try-get headers "Keep-Alive")
(~> (hash-get headers "Keep-Alive") bytes->string/utf8 parse-keep-alive)
EMPTY-KEEP-ALIVE))

(log/info! addr
(http-request-method http-request)
(http-request-path http-request)
(http-request-version http-request)
content-type
keep-alive-params)

;; Wipe out the buffer, since we've already parsed out the headers.
(bytes-clear! buffer)

(define body (read-body-into-buffer content-length output-port buffer))

(when (and body (equal? content-type "application/json"))
(send-as-json writer-port (string->jsexpr (bytes->string/utf8 buffer))))

(flush-output-port writer-port)

(displayln "finished sending response: "
(duration->string (instant/elapsed now))))

(loop))))))
(loop)))))

;; Actually start the server
(serve "0.0.0.0:8080" 10)

0 comments on commit 525af1c

Please sign in to comment.