changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / lisp/lib/net/srv.lisp

changeset 698: 96958d3eb5b0
parent: 7a5d6e45de7a
author: Richard Westhaver <ellis@rwest.io>
date: Fri, 04 Oct 2024 22:04:59 -0400
permissions: -rw-r--r--
description: fixes
1 ;;; net/srv.lisp --- Lisp Web Services
2 
3 ;; This library contains provides a Web Server abstraction a la Hunchentoot or
4 ;; Woo.
5 
6 ;;; Commentary:
7 
8 ;; The code in this file is meant to be small. We want to leverage the core
9 ;; ecosystem and internal NET/* packages to build high-level abstractions that
10 ;; are still useful with minimal boilerplate.
11 
12 ;; In other words we want to support both these use-cases in the least amount
13 ;; of code:
14 #|
15 (srv:start (srv:file-server)) ;; start a simple HTTP file server in current
16  ;; directory with all default values
17 
18 (srv:define-service my-homepage (:port 8080
19  :auth (auth settings ...)
20  :routes (routes ...)
21  &more ...)
22  (with-ws (ws 'my-homepage)
23  (srv:start ws)))
24 |#
25 
26 ;; not considering SSL currently - not a core object type but perhaps subclass
27 
28 ;; mostly following the implementation of hunchentoot with attempts at
29 ;; simplification.
30 
31 ;;; Code:
32 (in-package :net/srv)
33 
34 ;;; Vars
35 (defvar *router*)
36 (defvar *service*)
37 (defvar *handlers*)
38 (defvar-unbound *request*)
39 (defvar-unbound *response*)
40 (defvar *global-session-db* nil)
41 (defvar *global-session-db-lock* (make-mutex :name "global-session-db"))
42 (defvar *default-connection-timeout* 20)
43 (defvar *default-service-port* 8000)
44 #+ssl (defvar *default-ssl-service-port* 8000)
45 (defvar *default-session-timeout* #.(* 30 60)) ;; 30m
46 (defvar *default-content-type* "text/html")
47 ;;; Conditions
48 ;; from hunchentoot
49 (define-condition srv-condition (condition) ())
50 (deferror srv-error (srv-condition error) () ())
51 (deferror srv-simple-error (srv-error simple-condition) () (:auto t))
52 
53 (define-condition srv-warning (srv-condition warning) ())
54 (define-condition srv-simple-warning (srv-warning simple-condition) ())
55 
56 (deferror bad-request (srv-error) ())
57 
58 ;;; Utils
59 (eval-when (:load-toplevel :compile-toplevel :execute)
60  (defun default-web-directory (&optional sub-directory)
61  (let ((source-directory #.(or *compile-file-truename* *load-truename*)))
62  (merge-pathnames (make-pathname :directory (append (pathname-directory source-directory)
63  (list "www")
64  (when sub-directory
65  (list sub-directory)))
66  :name nil
67  :type nil
68  :defaults source-directory)))))
69 
70 ;;; Protocol
71 (defgeneric start-service (self)
72  (:documentation "Start a service."))
73 
74 (defgeneric stop-service (self)
75  (:documentation "Stop a service."))
76 
77 (defgeneric restart-service (self)
78  (:documentation "Restart a service.")
79  (:method ((self t))
80  (stop-service self)
81  (start-service self)))
82 
83 (defgeneric add-route (self uri handler &key &allow-other-keys))
84 (defgeneric delete-route (self uri &key &allow-other-keys))
85 (defgeneric handle-request (self request))
86 (defgeneric service-name (self)
87  (:method ((self t))
88  (obj/id:id self)))
89 
90 ;;; Response
91 (defclass service-response () ())
92 (defclass http-service-response (service-response) ((response :type http-response)))
93 
94 ;;; Request
95 (defclass service-request ()
96  ((origin :initarg :origin
97  :reader request-origin)
98  (session :initform nil
99  :accessor session)))
100 
101 (defclass http-service-request (service-request)
102  ((request :type http-request)))
103 
104 ;;; Session
105 
106 ;; HACK 2024-07-18: currently not storing the SESSION-STRING directly in this
107 ;; class as a slot. may need to change but I would rather have the string
108 ;; cached/displaced to some other location.. depends how often we need that
109 ;; string.
110 (defclass session (obj/id:id)
111  ((id :type integer)
112  (user-agent)
113  (remote-addr)
114  (session-start)
115  (last-click)
116  (data)
117  (max-time :type fixnum))
118  (:default-initargs
119  :session-start (get-universal-time)
120  :last-click (get-universal-time)
121  :max-time *default-session-timeout*))
122 
123 ;;; Headers
124 
125 ;;; Router
126 (defclass router () ())
127 
128 ;;; Engine
129 ;; Multithreaded runtime for services
130 (define-task-kernel service-task-kernel () ()
131  "Default task kernel for service-based tasks.")
132 
133 (defclass service-engine ()
134  ((task-pool :type task-pool)))
135 
136 ;; supervisor, worker, task, kernel
137 ;;; Service
138 (defclass service (obj/id:id)
139  ((port)
140  (address)
141  ;; HACK 2024-07-18: will this only accept class names? are structures classes? (http-request, http-response)
142  (request-class :type symbol)
143  (response-class :type symbol)
144  (engine :type service-engine)
145  (read-timeout :type fixnum)
146  (write-timeout :type fixnum)
147  (connection-max :type fixnum)
148  ;; RESEARCH 2024-07-18:
149  ;; may need to start dealing with this
150  ;; https://datatracker.ietf.org/doc/html/rfc2616#section-3.6.1
151  (chunk-output-p :type boolean)
152  (chunk-input-p :type boolean)
153  (socket :type socket)
154  (request-count :type integer)
155  (shutdown-lock :type mutex)
156  (shutdown-queue :type waitqueue))
157  (:default-initargs
158  :id (symbol-name (gensym "srv"))
159  :port *default-service-port*
160  :address nil
161  :request-class 'service-request
162  :response-class 'service-response
163  :chunk-output-p t
164  :chunk-input-p t
165  :read-timeout *default-connection-timeout*
166  :write-timeout *default-connection-timeout*
167  :request-count 0
168  :shutdown-lock (sb-thread:make-mutex :name "shutdown-lock")
169  :shutdown-queue (sb-thread:make-waitqueue :name "shutdown-queue"))
170  (:documentation "The service class is designed primarily for webservers and functionally
171 similar to HUNCHENTOOT:ACCEPTOR."))
172 
173 #+ssl
174 (defclass ssl-service (service)
175  ((certificate-file :initarg :certificate-file
176  :reader service-certificate-file)
177  (privatekey-file :initarg :privatekey-file
178  :reader service-privatekey-file)
179  (privatekey-password :initarg :privatekey-password
180  :reader service-privatekey-password))
181  (:default-initargs
182  :password nil
183  :port 443))
184 
185 ;;; Macros
186 (defmacro define-service (name &rest initargs)
187  "Define a subclass of NET/SRV:SERVICE."
188  `(defclass ,name ,@initargs))