This is the first checked-in completely working version. It contains
nearly all concepts and simple implementations thereof needed to get a simple HTTP/0.9 "compliant" server working (there are some hacks needed that we don't yet provide, since the correct things will be added shortly, like complete HTTP/1.1 request parsing. The hacks needed are provided as part of the basic HTTP/0.9 server demo in src/test/basic-demo.cl). Further work is needed to clean up some things, Entity and Resource handling needs to be implemented right and less "naive" (the current implementations are just simple place-holders to get things up and running). Connections need to have knowledge of client identity (passed from the driver, this is implementation-specific stuff). Logging needs to be implemented (probably as server mixins). Condition handling needs to generate better responses for HTTP/0.9, and the division between condition handling and normal handling needs to be documented/rethought. Content generation is totally missing currently and needs to be implemented. If this is all in place, an HTTP/1.0 conforming server should be possible, and after porting the drivers to ACL and LW, we can make a first release.
This commit is contained in:
112
src/main/entity.cl
Normal file
112
src/main/entity.cl
Normal file
@ -0,0 +1,112 @@
|
||||
;;;; CLASH --- The Common Lisp Adaptable Simple HTTP server
|
||||
;;;; This is copyrighted software. See documentation for terms.
|
||||
;;;;
|
||||
;;;; entity.cl --- Entity Handling
|
||||
;;;;
|
||||
;;;; Checkout Tag: $Name$
|
||||
;;;; $Id$
|
||||
|
||||
(in-package :CLASH)
|
||||
|
||||
;;;; %File Description:
|
||||
;;;;
|
||||
;;;;
|
||||
;;;;
|
||||
|
||||
(defclass entity ()
|
||||
())
|
||||
|
||||
(defgeneric render-entity-headers (entity stream))
|
||||
|
||||
(defgeneric render-entity-body (entity stream))
|
||||
|
||||
(defclass entity-header-mixin/1.1 ()
|
||||
((allow :initarg :allow :reader entity-allow)
|
||||
(content-base :initarg :content-base :reader entity-content-base)
|
||||
(content-encoding :initarg :content-encoding
|
||||
:reader entity-content-encoding)
|
||||
(content-language :initarg :content-language
|
||||
:reader entity-content-language)
|
||||
(content-length :initarg :content-length :reader entity-content-length)
|
||||
(content-location :initarg :content-location
|
||||
:reader entity-content-location)
|
||||
(content-md5 :initarg :content-md5 :reader entity-content-md5)
|
||||
(content-range :initarg :content-range :reader entity-content-range)
|
||||
(content-type :initarg :content-type :reader entity-content-type)
|
||||
(etag :initarg :etag :reader entity-etag)
|
||||
(expires :initarg :expires :reader entity-expires)
|
||||
(last-modified :initarg :last-modified :reader entity-last-modified)))
|
||||
|
||||
(defmacro render-slots ((obj stream) &rest clauses)
|
||||
(loop with obj-sym = (gensym)
|
||||
with stream-sym = (gensym)
|
||||
for (slot-spec string . body) in clauses
|
||||
for slot = (if (consp slot-spec) (car slot-spec) slot-spec)
|
||||
for slot-var = (if (consp slot-spec) (cadr slot-spec) slot-spec)
|
||||
collect
|
||||
`(when (slot-boundp ,obj-sym (quote ,slot))
|
||||
(with-slots ((,slot-var ,slot)) ,obj-sym
|
||||
(write-string ,string ,stream-sym)
|
||||
(write-char #\: ,stream-sym)
|
||||
(write-char #\Space ,stream-sym)
|
||||
,@body
|
||||
(write-char #\Return ,stream-sym)
|
||||
(write-char #\Newline ,stream-sym)))
|
||||
into clause-list
|
||||
finally
|
||||
(return `(let ((,stream-sym ,stream)
|
||||
(,obj-sym ,obj))
|
||||
,@clause-list))))
|
||||
|
||||
(defmethod render-entity-headers ((e entity-header-mixin/1.1) stream)
|
||||
(render-slots (e stream)
|
||||
(allow "Allow"
|
||||
(write-http-element-list-1 allow stream))
|
||||
(content-base "Content-Base"
|
||||
(princ content-base stream))
|
||||
(content-encoding "Content-Encoding"
|
||||
(write-http-element-list-1 content-encoding stream))
|
||||
(content-language "Content-Language"
|
||||
(write-http-element-list-1 content-language stream))
|
||||
(content-length "Content-Length"
|
||||
(format stream "~D" content-length))
|
||||
(content-location "Content-Location"
|
||||
(princ content-location stream))
|
||||
#+NIL
|
||||
(content-md5 "Content-MD5"
|
||||
(write-string (base64-encode-string content-md5) stream))
|
||||
#+NIL
|
||||
(content-range "Content-Range"
|
||||
(princ content-range stream))
|
||||
#+NIL
|
||||
(content-type "Content-Type"
|
||||
(princ content-type stream))
|
||||
#+NIL
|
||||
(etag "ETag"
|
||||
(princ etag stream))
|
||||
#+NIL
|
||||
(expires "Expires"
|
||||
(princ expires stream))
|
||||
#+NIL
|
||||
(last-modified "Last-Modified"
|
||||
(princ last-modified stream))))
|
||||
|
||||
|
||||
|
||||
(defclass simple-entity (entity entity-header-mixin/1.1)
|
||||
((body :initarg :body :reader entity-body :initform nil)))
|
||||
|
||||
(defmethod render-entity-body ((entity simple-entity) stream)
|
||||
(etypecase (entity-body entity)
|
||||
(string
|
||||
(with-input-from-string (in-stream (entity-body entity))
|
||||
(loop for line = (read-line in-stream nil nil)
|
||||
while line
|
||||
do (write-http-line line stream))))
|
||||
(pathname
|
||||
(with-open-file (in-stream (entity-body entity))
|
||||
(let ((buffer (make-string (file-length in-stream))))
|
||||
(+ (read-sequence buffer in-stream)
|
||||
(length (write-sequence buffer stream))))))
|
||||
(function
|
||||
(funcall (entity-body entity) stream))))
|
||||
Reference in New Issue
Block a user