Author: achiumenti Date: Sat Dec 13 14:44:21 2008 New Revision: 152
Log: manual update
Added: trunk/doc/chapters/claw-html.texinfo trunk/doc/chapters/installation.texinfo Modified: trunk/doc/chapters/intro.texinfo trunk/doc/chapters/lisplets.texinfo trunk/doc/chapters/server.texinfo
Added: trunk/doc/chapters/claw-html.texinfo ============================================================================== --- (empty file) +++ trunk/doc/chapters/claw-html.texinfo Sat Dec 13 14:44:21 2008 @@ -0,0 +1,283 @@ +@node The web framework +@comment node-name, next, previous, up +@chapter The web framework + +@code{CLAW-HTML} is one of the most advanced web aplication framework available, complitely object oriented (see @url{http://en.wikipedia.org/wiki/Common_Lisp_Object_System, CLOS}), +it is based on the concept of pages and components. + +Pages are containers for components that provide the request-response business logic. They pervent html elements from duplicating their id, that is very important in component based applications, +they are able to handle form requests, and to render components themselves of course. + +Components are highly reusable building blocks that make easy and fast the creation of a web application. +By using and creating new components, you, the developer, can create robust and consistent web application with the minimal effort. Each component may inject into a page its own set of stylesheet and javascript files, and may come with its own class or instance javascript. + +@section @code{CLAW-HTML} getting started + +With @code{CLAW-HTML} we'll begin to structure our samples in a more conformed way using the asdf definition system. + +If you don't have any idea of what asdf is, please read about it at the @url{http://constantly.at/lisp/asdf/, asdf Manual}. + +Create a directory named @code{clhtml-sample1} and begin writing the @code{clhtml-sample1.asd} file and create a symolic link +into the asdf system directory. + +@sp 2 +@smalllisp +;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*- +;;; $Header: clhtml-sample1.asd $ + +(asdf:defsystem :clhtml-sample1 + :name "clhtml-sample1" + :description "clhtml-sample1: CLAW-HTML sample application" + :depends-on (:claw-as :claw-hunchentoot-connector :claw-html) + :components ((:module src + :components ((:file "packages") + (:file "main" :depends-on ("packages")) + (:file "components" :depends-on ("packages")) + (:file "index" :depends-on ("main")))))) + +@end smalllisp +@sp 2 + +Now create the @code{clhtml-sample1/src} directory and the following empty files + +@itemize +@item @code{clhtml-sample1/src/packages.lisp} +@item @code{clhtml-sample1/src/main.lisp} +@item @code{clhtml-sample1/src/components.lisp} +@item @code{clhtml-sample1/src/index.lisp} +@end itemize + +@sp 1 + +Finally create the @code{clhtml-sample1/src/docroot} directory that will hold the static resources of the demo lisplet. + +Define now the package, writing into @code{clhtml-sample1/src/packages.lisp} the code below. + +@sp 2 +@smalllisp +;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*- +;;; $Header: src/package.lisp $ + +(in-package :cl-user) + +(defpackage :clhtml-sample1 + (:use :cl :claw-as :claw-hunchentoot-connector :claw-html) + (:documentation "clhtml-sample1: CLAW-HTML sample application") + (:export #:clhtml-sample1-start + #:clhtml-sample1-stop)) +@end smalllisp +@sp 2 + +The package exports the symbol functions @code{CLHTML-SAMPLE1-START} and @code{CLHTML-SAMPLE1-STOP}, used to +start and stop the application server. +This two function will be defined into the @code{main.lisp} file. + +Now it's time to write the code do launch the application server and register the lisplet. +Fill the @code{main.lisp} file with the following code + +@sp 2 +@smalllisp +;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*- +;;; $Header: src/main.lisp $ + +(in-package :clhtml-sample1) + +(defvar *main-file* (load-time-value + (or #.*compile-file-pathname* *load-pathname*))) + +(defvar *fs-realm* "frameworksample") + +(defvar *fs-lisplet* (make-instance 'lisplet :realm *fs-realm* + :base-path "/frameworksample")) + +(defvar *ht-connector* (make-instance 'hunchentoot-connector + :address "localhost" + :port 4242 + :sslport nil)) + +(defvar *sm* (make-instance 'default-session-manager)) + +(defvar *ht-log-manager* (make-instance 'hunchentoot-logger)) + +(defvar *the-server* (make-instance 'clawserver + :connector *ht-connector* + :log-manager *ht-log-manager* + :session-manager *sm* + :base-path "/claw")) + +(clawserver-register-lisplet *the-server* *fs-lisplet*) + +(defclass demo-configuration (configuration) + ((users :reader users + :initform '(("admin" "pwdadmin" ("admin-role" "user-role")) + ("user1" "pwduser" ("user-role")))))) + +(defmethod configuration-login ((configuration demo-configuration)) + (multiple-value-bind (user password) + (if (eq (lisplet-authentication-type *claw-current-lisplet*) :basic) + (claw-authorization) + (values (claw-parameter "username") + (claw-parameter "password"))) + (let ((maybe-principal (assoc user))) + (when (and maybe-principal (string= password (second maybe-principal))) + (make-instance 'principal + :name user + :roles (third maybe-principal)))))) + +(clawserver-register-configuration + *the-server* *fs-realm* (make-instance 'demo-configuration)) + +;;registering lisplet static resources +(let ((path (make-pathname :directory + (append (pathname-directory *main-file*) '("docroot"))))) + (lisplet-register-resource-location *fs-lisplet* + path + "docroot/")) + +(defun clhtml-sample1-start () + (clawserver-start *the-server*)) + +(defun clhtml-sample1-stop () + (clawserver-stop *the-server*)) +@end smalllisp +@sp 2 + +@section Your first @code{claw-html} page + +It's time to create your first page using @code{claw-html} this is done creating a @code{PAGE} subclass, override its +@code{page-content} mathod and finally registering it using the @code{make-page-renderer} function. + +In this very basic example we will not use any of the component feature of the framework, just basic tag functions. + +@sp 2 +@smalllisp +;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*- +;;; $Header: src/index.lisp $ + +(in-package :clhtml-sample1) + +(defclass index-page (page) + ((username :initform nil + :accessor index-page-username) + (passwd :initform nil + :accessor index-page-password))) + +(defmethod page-content ((o index-page)) + (html> + (head> + (title> "Home test page") + (link> :rel "stylesheet" :href "docroot/screen.css" :type "text/css")) + (body> :class "sampleBody" + (div> :class "topHeader" "top header") + (div> :class "mainContent" + (h1> "Hello world"))))) + +(lisplet-register-function-location *fs-lisplet* + (make-page-renderer 'index-page + #'claw-post-parameters + #'claw-get-parameters) + "index.html" + :welcome-page-p t) +@end smalllisp +@sp 2 + +Now create a very simple screen.css since the index page needs one. + +@sp 2 +@smallexample +/* $Header: src/docroot/screen.css */ + +.sampleBody @{ + width: 800px; + margin: 10px auto 0 auto; + background: #8B8DB9; +@} + +.sampleBody .topHeader @{ + min-height: 100px; + background: #D1D2E6; + padding: 10px; + border: 1px solid gray; + -moz-border-radius: 10px; /* only for firefox */ +@} + +.sampleBody .mainContent @{ + min-height: 500px; + background: white; + padding: 10px; + margin-top: 15px; + border: 1px solid gray; + -moz-border-radius: 10px; /* only for firefox */ +@} +@end smallexample +@sp 2 + +If you have done all properly inyour Slime REPL, after the following commands: + +@sp 2 +@smallexample +CL-USER> (asdf:oos 'asdf:load-op :clhtml-sample1) +.... +CL-USER> (clhtml-sample1:clhtml-sample1-start) +NIL +CL-USER> +@end smallexample +@sp 2 + +You should be able to see the following page: +@sp 1 +@image{images/claw-html-1,150mm,,,png} +@sp 2 + +@subsection Creating your first component + +Writing a page the way described before has been straightforward, but it doesn't show any of +the framework potentialities. Effectively this is nothing more then what other framework offers. + +So let's do what is usually done by component-based frameworks: create the first component. +This will be the site template component, shurely you don't want to write every page to be +different from each other, so you'll create the site template. + +This component will provide a tag function similar to any other standard like @code{html>, body>, div>} and +so on. + +The first thing you'll have to do is to create a class that subcleasses @code{WCOMPONENT} (the name stands for +web component) and with metaclass @code{METACOMPONENT}. Giving such metaclass to your component definition +makes the framework to automatically create a tag function for this component whose name will be the component name plus +a @code{>} at the end. + +A tag function is a function that may be placed inside a page, or another component, to render the referring component. + +@sp 2 +@smalllisp +(defclass site-template (wcomponent) + ((title :initarg :title + :reader site-template-title)) + (:metaclass metacomponent)) +@end smalllisp +@sp 2 + +Since you haven't provided any @code{DEFAULT-INITARGS}, if you forgot to provide the title attribute to your tag function, the +page won't render, and an error message will be logged. + +Surely the class does very little by itself without a proper template, so you'll define one for it now. + +@sp 2 +@smalllisp +(defmethod wcomponent-template ((obj site-template)) + (let ((title-value (site-template-title obj))) + (html> + (head> + (title> title-value)) + (body> (wcomponent-informal-parameters obj) + (htcomponent-body obj))))) +@end smalllisp +@sp 2 + +Here there are three points to highlight. Firstly the @code{HTCOMPONENT-BODY}, here we are informing the the component will render its child +at this position. Then the @code{WCOMPONENT-INFORMAL-PARAMETERS} is a method that informs the component to render tag parameters not directly specified into +the class specification, making it possible to specify any tag parameter to the tag function. + +The last thing to notice is the absence of the link to the css file. This was made because each componoent can inject into the page its own css or js definitions +both at class level and at instance level, that in @code{claw-html} are injected by the component directly into the containing page. +To add a css or a js file at class definition level you have to implement @code{HTCOMPONENT-SCRIPT-FILES} and @code{HTCOMPONENT-STYLESHEET-FILES} while
Added: trunk/doc/chapters/installation.texinfo ============================================================================== --- (empty file) +++ trunk/doc/chapters/installation.texinfo Sat Dec 13 14:44:21 2008 @@ -0,0 +1,88 @@ +@node Installation +@comment node-name, next, previous, up +@chapter Installation + +@value{claw} is a comprehensive set of software that helps you to easily and quickly build and run web applications. + +@section Getting the software + +You can find the latest stable release of @value{claw} at @indicateurl{ftp://common-lisp.net/pub/project/claw/stable}, +while the development code can be found at @indicateurl{ftp://common-lisp.net/pub/project/claw/development}. + +For brave coders it is possible to get it from subversion with the command +@code{svn checkout svn://common-lisp.net/project/claw/svn/trunk claw} + +Sure you can also use asdf-install that will resolve all dependencies you need. +Checkout @indicateurl{http://www.cliki.net/ASDF-Install%7D to know how asdf-install works. + + +@section Dependencies +For the application server, namely @emph{claw-as} you need the following packages: + +@itemize @minus +@item +closer-mop +@item +cl-ppcre +@item +cl-fad +@item +alexandria +@item +local-time +@item +split-sequence +@item +bordeaux-threads +@item +md5 +@end itemize + +For the http connector currently there is only the Hunchentoot connector. The connector is @emph{claw-hunchentoot-connector} and depends on: + +@itemize @minus +@item +claw-as +@item +hunchentoot +@end itemize + +@emph{claw-html} is a web framework independent from @emph{claw-as}, dependencies required are: + +@itemize @minus +@item +closer-mop +@item +local-time +@item +parenscript +@item +cl-ppcre +@item +split-sequence +@end itemize + +@emph{claw-html.dojo} is an extension of @emph{claw-html} but uses @emph{claw-as} to make auto library registration. +To get @emph{claw-html.dojo} working you also need to download dojo + dijit + dojox from @indicateurl{http://dojotoolkit.org/downloads%7D, +and unpack it under claw-html.dojo/src as dojotoolkit. + +@emph{claw-html.dojo} dependencies are: + +@itemize @minus +@item +claw-as +@item +claw-html +@item +parenscript +@end itemize + +@emph{claw.i18n} is a very simple library so its debendencies are limited to: + +@itemize @minus +@item +claw-as +@item +local-time +@end itemize +
Modified: trunk/doc/chapters/intro.texinfo ============================================================================== --- trunk/doc/chapters/intro.texinfo (original) +++ trunk/doc/chapters/intro.texinfo Sat Dec 13 14:44:21 2008 @@ -2,46 +2,32 @@ @comment node-name, next, previous, up @chapter Introduction
-@value{claw} is a comprehensive web application for Common Lisp programming language. +@value{claw} is a comprehensive set of software that helps you to easily and quickly build and run web applications.
-@section What is @value{claw} +@section What does @value{claw} provide?
-@value{claw} is a comprehensive web application framework for the Common Lisp programming language. -@value{claw} is based on components, highly reusable building blocks the make easy and fast the creation of a web application. -By using and creating new components, the developer can create robust and consistent web application with the minimal effort. - -Each component may inject into a page its own set of stylesheet and javascript files, and may come with its own class or instance javascript -directives (a class directive is inserted only once into the page, while this is not true for an instance script). This leads to -the creation of very sophisticated components with a very little effort. - -@value{claw} comes with its own authentication system that lets you create both basic and form based authentication systems. - -@value{claw} has the capability to force the page rendering through the HTTPS protocol of pages managing sensible data, using simple +@value{claw} is currently made of six subprojects +@itemize @minus +@item +@emph{claw-as} The application server itself. +The application server needs a connector to serve resources, such as web pages, through HTTP protocol. +The application server comes with its own authentication system that lets you create both basic and form based authentication systems. +It also has the capability to force the page rendering through the HTTPS protocol of pages that manage sensible data, using simple directives. +@item +@emph{connectors} Where http-service connectors are kept. Currently only @emph{HUNCHENTOOT} connector is implemented. @emph{HUNCHENTOOT} is a fast web application server written in +common lisp that supports both HTTP and HTTPS protocols. +@item +@emph{claw.i18n} A simple yet powerful internationalization/localization module. +@item +@emph{claw-html} An advanced, powerful and simple web framework. @emph{claw-html} is based on components, highly reusable building blocks that make easy and fast the creation of a web application. +By using and creating new components, the developer can create robust and consistent web application with the minimal effort. Each component may inject into a page its own set of stylesheet and javascript files, and may come with its own class or instance javascript +directives (a class directive is inserted only once into the page, while this is not true for an instance directive). This leads to +the creation of very sophisticated components with a very little effort. +@item +@emph{claw-html.dojo} An extension of @emph{claw-html} that integrates the @indicateurl{http://www.dojotoolki.org%7D ajax framework transparently. +The integration with dojo gives you the possibility to easily and quickly create full WEB 2.0 eye candy application with powerful and very user friendly UI. +@end itemize
-@value{claw} comes with its own extensible localization and validation system. - -The main aim of @value{claw} is @cite{`divide et impera'}, that means that dividing problems into small problems let programmers +The main aim of @value{claw} is @cite{`divide et impera'}, that means that dividing problems into small problems, let programmers work on different part of an application, creating ad hoc components for both generic and specific tasks. - -@value{claw} can easily handle all the request cycle,letting you to concentrate only in application business side problems, letting -@value{claw} automatically manage all the mechanism of the web layer, such as form submission and user interactions. - -@value{claw} comes integrated with the dojotoolkit, giving you the possibility to easily and quickly create full WEB 2.0 eye candy application, -with powerful and very user friendly UI. - -@subsection The request cycle - -When a user asks for a page the request is sent to the @code{CLAWSERVER} that dispatches the request to the registered lisplets. - -Lisplets are web resource containers that hold web pages and other resource files, such as javascript, image, css, etc. files, or even functions, under a common path. - -When a matching lisplet is then found, it dispatches the request to a registered resource that can be a page or a file or even a function. - -If the request is sent for a file, this is then sent back to the browser if found. - -If the request is sent for a page, usually mapped to a html URL, the dispatcher calls the page rendering function to display the page as an html resource. - -If no resource is found a 404 message page, is sent to the user as feedback. - -@image{figure1,15cm,,,png}
Modified: trunk/doc/chapters/lisplets.texinfo ============================================================================== --- trunk/doc/chapters/lisplets.texinfo (original) +++ trunk/doc/chapters/lisplets.texinfo Sat Dec 13 14:44:21 2008 @@ -1,160 +1,4 @@ -@node Lisplets +@node The web framewok @comment node-name, next, previous, up -@chapter Lisplets +@chapter The web framework
-Lisplets are @code{CLOS} objects that extend the functionalities of @code{CLAWSERVER}, dispatching requests that -come from this last one. - -Lisplets are so, the place where you put your web applications developed with @value{claw}. - -Lisplets return to the requesting user, pages, functions and resources mapped into them. - -Each Lisplet contains its own dispatch table and realm so that applications are not mixed together. - -@section Registering a lisplet into the server, crating a web application - -To create a web application you have to instantiate a @code{LISPLET} and then register it into the server. -@cartouche -@lisp -(defvar *clawserver* (make-instance 'clawserver :port 4242)) - -(defvar *test-lisplet* (make-instance 'lisplet :base-path "/test")) -(clawserver-register-lisplet *clawserver* *test-lisplet*) - -;;; you can now start the server -;;; with: -;;; (clawserver-start *clawserver*) -;;; and -;;; (clawserver-stop *clawserver*) -@end lisp -@end cartouche - -At this point you have defined a web application registered to the URL ``http://localhost:4242/test'' that -@value{claw} will be able to serve. - -All sessions and the authentication and authorization logic will be under the default realm ``claw'', -so if you register another lisplet into the server with the instruction: -@cartouche -@lisp -(defvar *test-lisplet2* (make-instance 'lisplet :base-path "/test2")) -(clawserver-register-lisplet *clawserver* *test-lisplet2*) -@end lisp -@end cartouche -any user session will be shared among @code{*test-lisplet*} and @code{*test-lisplet2*} and if a user is logged into -``/test'' application, he will be logged into ``/test2'' application too. - -To avoid this behaviour, you need to define a different realm for each of the two lisplet as the following example does: -@cartouche -@lisp -(defvar *clawserver* (make-instance 'clawserver :port 4242)) - -(defvar *test-lisplet* (make-instance 'lisplet :realm "test" - :base-path "/test")) -(clawserver-register-lisplet *clawserver* *test-lisplet*) - -(defvar *test-lisplet2* (make-instance 'lisplet :realm "test2" - :base-path "/test2")) -(clawserver-register-lisplet *clawserver* *test-lisplet2*) -@end lisp -@end cartouche - -The two lisplets will now have different realms, so a user session in @code{*test-lisplet*} will be -different from the one in @code{*test-lisplet2*}. So for the authentication and authorization module. -The same is for a user logged into the first application, he will not be automatically logged into the -other now. - -@section Adding resources into a @code{LISPLET} - -Lisplets alone don't do anything more then providing some error pages when something goes wrong. -To make a @code{LISPLET} a web application, you have to fill it with some application resource, and this -may be done in several ways. - -@subsection Adding files and folders to a @code{LISPLET} - -Suppose now you want to provide, thought your web application, a file present on your hard disk, for example: -``/opt/webresources/images/matrix.jpg''. - -This is made very simple with the following instructions -@cartouche -@lisp -(lisplet-register-resource-location *test-lisplet* - #P"/opt/webresources/images/matrix.jpg" - "images/matrix.jpg" "image/jpeg") -@end lisp -@end cartouche - -The jpeg file will now be available when accessing ``http://localhost:4242/test/images/matrix.jpg''. -The last argument specifies the mime-type, but it's optional. - -If you want to register an entire folder, the process is very similar -@cartouche -@lisp -(lisplet-register-resource-location *test-lisplet* - #P"/opt/webresources/images/" - "images2/") -@end lisp -@end cartouche - -Now you'll be able to access the same resource following the URL -``http://localhost:4242/test/images2/matrix.jpg'', easy, isn't it? - -@subsection Adding functions to a @code{LISPLET} - -Registering a function gives you more flexibility then registering a static resource as a file or a directory but the complexity -relies into the function that you want to register. - -For example, if you want to provide the same ``matrix.jpg'' file through a function, you'll have to do something of the kind: -@cartouche -@lisp -(lisplet-register-function-location *test-lisplet* - #'(lambda () - (let ((path #P"/opt/webresources/images/matrix.jpg")) - (setf (content-type) (mime-type path)) - (with-open-file (in path :element-type 'flex:octet) - (let ((image-data (make-array (file-length in) - :element-type 'flex:octet))) - (read-sequence image-data in) - image-data)))) - "images/matrix2.jpg" ) -@end lisp -@end cartouche -Now the image will be available at the URL ``http://localhost:4242/test/images/matrix2.jpg''. - -The method @code{lisplet-register-function-location} accepts two optional keys: -@itemize @minus -@item -@code{:WELCOME-PAGE-P} that -will redirect you to the registered location when you'll access your application with the URL -``http://localhost:4242/test'' -@item -@code{:LOGIN-PAGE-P} that will redirect an unregistered user to the resource when he tries to access -a protected resource to perform the login with a form based authentication. -@end itemize - -@subsection Adding pages to a @code{LISPLET} - -Pages are one of the key objects of @value{claw}, since they are sophisticated collectors of web components. -Pages are described in the next chapter, meanwhile to register a page that is a @code{CLOS} object, the procedure -is very similar to when you register a function. -@cartouche -@lisp -(defclass empty-page (page) ()) -(lisplet-register-page-location *test-lisplet* 'empty-page "index.html" - :welcome-page-p t) -@end lisp -@end cartouche - -This will provide an empty page at the URL ``http://localhost:4242/test/index.html'' and, since it -is defined as a welcome page when you'll access the URL ``http://localhost:4242/test'' you will redirected -to it. - -@section Sessions - -Sessions are common place where you sore stateful user data. Session handling is slightly different from the original one -implemented by @code{Hunchentoot}, so, to instantiate a session you have to use the method -@cartouche -@lisp -(lisplet-start-session) -@end lisp -@end cartouche -inside your code.
Modified: trunk/doc/chapters/server.texinfo ============================================================================== --- trunk/doc/chapters/server.texinfo (original) +++ trunk/doc/chapters/server.texinfo Sat Dec 13 14:44:21 2008 @@ -2,395 +2,779 @@ @comment node-name, next, previous, up @chapter The server
-@value{claw} wraps the Hunchentoot (see: @url{http://www.weitz.de/hunchentoot/, unchentoot}), a wonderful as powerful web server written in Common Lisp, -into the @code{CLAWSERVER} class. +The @value{claw} application server is hold into the @emph{claw-as} package. +To load it you simply have to use @emph{asdf} with the command +@sp 2 +@smalllisp +(asdf:oos 'asdf:load-op :claw-as) +@end smalllisp +@sp 2
-As an Hunchentoot wrapper @code{CLAWSERVER} ``provides facilities like automatic session handling (with and without cookies), logging -(to Apache's log files or to a file in the file system), customizable error handling, and easy access to GET and POST parameters sent by the client.'' - -@section Understanding the clawserver - -@code{CLAWSERVER} is not only a Hunchentoot wrapper, it is also the common place where you put your web applications -built with @value{claw} into lisplet that you can see as application resource containers and request dispatchers. - -@subsection @code{CLAWSERVER} instance initialization - -When you want to instantiate a @code{CLAWSERVER} class, remember that it accepts the following initialization arguments: -@itemize @minus -@item -@emph{port} The port the server will be listening on, default is 80 -@item -@emph{sslport} The SSL port the server will be listening on, default is 443 if the server has a certificate file defined. -@item -@emph{address} A string denoting an IP address, if provided then the server only receives connections for that address. -If address is NIL, then the server will receive connections to all IP addresses on the machine (default). -@item -@emph{name} Should be a symbol which can be used to name the server. -This name can utilized when defining easy handlers. The default name is an uninterned symbol as returned by GENSYM -@item -@emph{sslname} Should be a symbol which can be used to name the server running in SSL mode when a certificate file is provided. -This name can utilized when defining easy handlers. The default name is an uninterned symbol as returned by GENSYM -@item -@emph{mod-lisp-p} If true (the default is NIL), the server will act as a back-end for mod_lisp, otherwise it will be a stand-alone web server. -@item -@emph{use-apache-log-p} If true (which is the default), log messages will be written to the Apache log file - this parameter has no effect if @emph{mod-lisp-p} is NIL. -@item -@emph{input-chunking-p} If true (which is the default), the server will accept request bodies without a Content-Length header if the client uses chunked transfer encoding. -@item -@emph{read-timeout} Is the read timeout (in seconds) for the socket stream used by the server. -The default value is @url{http://www.weitz.de/hunchentoot/#*default-read-timeout*,HUNCHENTOOT:*DEFAULT... (20 seconds) -@item -@emph{write-timeout} Is the write timeout (in seconds) for the socket stream used by the server. -The default value is @url{http://www.weitz.de/hunchentoot/#*default-write-timeout*,HUNCHENTOOT:*DEFAUL... (20 seconds) -@item -@emph{setuid} On Unix systems, changes the UID of the process directly after the server has been started. -@item -@emph{setgid} On Unix systems, changes the GID of the process directly after the server has been started. -@item -@emph{ssl-certificate-file} If you want your server to use SSL, you must provide the pathname designator(s) for the certificate file (must be in PEM format). -@item -@emph{ssl-privatekey-file} the pathname designator(s) for the private key file (must be in PEM format). -@item -@emph{ssl-privatekey-password} If private key file needs a password set this parameter to the required password -@end itemize - -@subsection @code{CLAWSERVER} class methods - -@sp 1 -@fnindex clawserver-port -@noindent -@code{clawserver-port obj}@* -@code{(setf clawserver-port) val obj} -@indent - -@itemize -@item -@emph{obj} The @code{CLAWSERVER} instance -@item -@emph{val} The numeric value of listening port to assign -@end itemize -Returns and sets the port on which the server is listening to (default 80). -If the server is started and you try to change the listening value an error will be signaled - -@sp 1 -@fnindex clawserver-sslport -@noindent -@code{clawserver-sslport obj}@* -@code{(setf clawserver-sslport) val obj} -@indent - -@itemize -@item -@emph{obj} The @code{CLAWSERVER} instance -@item -@emph{val} The numeric value of listening port to assign for SSL connections -@end itemize -Returns and sets the port on which the server is listening to in SSL mode if a certificate file is provided (default 443). -If the server is started and you try to change the listening value an error will be signaled - -@sp 1 -@fnindex clawserver-address -@noindent -@code{clawserver-address obj}@* -@code{(setf clawserver-address) val obj} -@indent - -@itemize -@item -@emph{obj} The @code{CLAWSERVER} instance -@item -@emph{val} The string value denoting the IP address -@end itemize -Returns and sets the IP address where the server is bound to (default @code{NIL} @result{} any). -If the server is started and you try to change the listening value an error will be signaled - -@sp 1 -@fnindex clawserver-name -@noindent -@code{clawserver-name obj}@* -@code{(setf clawserver-name) val obj} -@indent - -@itemize -@item -@emph{obj} The @code{CLAWSERVER} instance -@item -@emph{val} The symbol value denoting the server name -@end itemize -Should be a symbol which can be used to name the server. -This name can utilized when defining easy handlers. The default name is an uninterned symbol as returned by GENSYM - -@sp 1 -@fnindex clawserver-sslname -@noindent -@code{clawserver-sslname obj}@* -@code{(setf clawserver-sslname) val obj} -@indent - -@itemize -@item -@emph{obj} The @code{CLAWSERVER} instance -@item -@emph{val} The symbol value denoting the server name running in SSL mode -@end itemize -Should be a symbol which can be used to name the server running in SSL mode, when a certificate file is provided. -This name can utilized when defining easy handlers. The default name is an uninterned symbol as returned by GENSYM - -@sp 1 -@fnindex clawserver-mod-lisp-p -@noindent -@code{clawserver-mod-lisp-p obj}@* -@code{(setf clawserver-mod-lisp-p) val obj} -@indent - -@itemize -@item -@emph{obj} The @code{CLAWSERVER} instance -@item -@emph{val} The boolean value denoting the use of mod_lisp. -@end itemize -Returns and sets the server startup modality . -If true (the default is @code{NIL}), the server will act as a back-end for mod_lisp, otherwise it will be a stand-alone web server. -If the server is started and you try to change the listening value an error will be signaled - -@sp 1 -@fnindex clawserver-use-apache-log-p -@noindent -@code{clawserver-use-apache-log-p obj}@* -@code{(setf clawserver-use-apache-log-p) val obj} -@indent - -@itemize -@item -@emph{obj} The @code{CLAWSERVER} instance -@item -@emph{val} The boolean value denoting the use of Apache log. -@end itemize -Returns and sets where the server should log messages. This parameter has no effects if clawserver-mod-lisp-p is set to @code{NIL}. (default @code{T} if @code{mod_lisp} -is activated. -If the server is started and you try to change the listening value an error will be signaled - -@sp 1 -@fnindex clawserver-input-chunking-p -@noindent -@code{clawserver-input-chunking-p obj}@* -@code{(setf clawserver-input-chunking-p) val obj} -@indent - -@itemize -@item -@emph{obj} The @code{CLAWSERVER} instance -@item -@emph{val} The boolean value denoting the ability to accept request bodies without a Content-Length header. -@end itemize -Returns and sets the ability to accept request bodies without a Content-Length header (default is @code{T}) -If the server is started and you try to change the listening value an error will be signaled - -@sp 1 -@fnindex clawserver-read-timeout -@noindent -@code{clawserver-read-timeout obj}@* -@code{(setf clawserver-read-timeout) val obj} -@indent - -@itemize -@item -@emph{obj} The @code{CLAWSERVER} instance -@item -@emph{val} The integer value denoting the server read timeout. -@end itemize -Returns and sets the server read timeout in seconds (default is @code{T}) -(default to @url{http://www.weitz.de/hunchentoot/#*default-read-timeout*,HUNCHENTOOT:*DEFAULT... [20 seconds]). -If the server is started and you try to change the listening value an error will be signaled - -@sp 1 -@fnindex clawserver-write-timeout -@noindent -@code{clawserver-write-timeout obj}@* -@code{(setf clawserver-write-timeout) val obj} -@indent - -@itemize -@item -@emph{obj} The @code{CLAWSERVER} instance -@item -@emph{val} The integer value denoting the server write timeout. -@end itemize -Returns and sets the server write timeout in seconds (default is @code{T}) -(default to @url{http://www.weitz.de/hunchentoot/#*default-read-timeout*,HUNCHENTOOT:*DEFAULT... [20 seconds]). -If the server is started and you try to change the listening value an error will be signaled - -@sp 1 -@fnindex clawserver-setuid -@noindent -@code{clawserver-setuid obj}@* -@code{(setf clawserver-setuid) val obj} -@indent - -@itemize -@item -@emph{obj} The @code{CLAWSERVER} instance -@item -@emph{val} The string or integer value of the UID with which the server instance will run. -@end itemize -Returns and sets the server instance UID (user id). -If the server is started and you try to change the listening value an error will be signaled - -@sp 1 -@fnindex clawserver-setgid -@noindent -@code{clawserver-setgid obj}@* -@code{(setf clawserver-setgid) val obj} -@indent - -@itemize -@item -@emph{obj} The @code{CLAWSERVER} instance -@item -@emph{val} The string or integer value of the GID with which the server instance will run. -@end itemize -Returns and sets the server instance GID (group id). -If the server is started and you try to change the listening value an error will be signaled - -@sp 1 -@fnindex clawserver-ssl-certificate-file -@noindent -@code{clawserver-ssl-certificate-file obj}@* -@code{(setf clawserver-ssl-certificate-file) val obj} -@indent +The application server is defined by the class @code{CLAWSERVER}.
-@itemize -@item -@emph{obj} The @code{CLAWSERVER} instance -@item -@emph{val} Pathname designator(s) for the certificate file -@end itemize -Returns and sets the pathname designator(s) for the certificate file if the @code{CLAWSERVER} is SSL enabled -If the server is started and you try to change the listening value an error will be signaled +A @code{CLAWSERVER} instance is able to start and stop services that are subclasses of the @code{CLAW-SERVICE} interface, +dispatch HTTP and HTTPS request and log messages through @code{CONNECTOR} and @code{LOGGER} implementations that are +@code{CLAW-SERVICE} implementations.
-@sp 1 -@fnindex clawserver-ssl-privatekey-file -@noindent -@code{clawserver-ssl-privatekey-file obj}@* -@code{(setf clawserver-ssl-privatekey-file) val obj} -@indent +A minimal server configuration needs an connector, a @code{SESSION-MANAGER} (another @code{CLAW-SERVICE} that handles users sessions) and a log manager to logs server messages.
-@itemize -@item -@emph{obj} The @code{CLAWSERVER} instance -@item -@emph{val} Pathname designator(s) for the private key file -@end itemize -Returns and sets the pathname designator(s) for the private key file if the @code{CLAWSERVER} is SSL enabled -If the server is started and you try to change the listening value an error will be signaled +Currently @value{claw} application server uses a connector to Hunchentoot (see: @url{http://www.weitz.de/hunchentoot/, unchentoot}), a wonderful as powerful web server written in Common Lisp.
-@sp 1 -@fnindex clawserver-ssl-privatekey-password -@noindent -@code{clawserver-ssl-privatekey-password obj}@* -@code{(setf clawserver-ssl-privatekey-password) val obj} -@indent +The log manager @code{HUNCHENTOOT-LOGGER} is also based on Hunchentoot logging system.
-@itemize -@item -@emph{obj} The @code{CLAWSERVER} instance -@item -@emph{val} Password for the private key file -@end itemize -Returns and sets the password for the private key file if the @code{CLAWSERVER} is SSL enabled -If the server is started and you try to change the listening value an error will be signaled +The connector,as an Hunchentoot wrapper @code{CLAWSERVER} ``provides facilities like automatic session handling (with and without cookies), logging +(to Apache's log files or to a file in the file system), customizable error handling, and easy access to GET and POST parameters sent by the client.''
-@sp 1 -@fnindex clawserver-start -@noindent -@code{clawserver-start obj} -@indent +@section Starting the application server +To stare the server you have to pass it a http connector that may be instantiated with
-@itemize -@item -@emph{obj} The @code{CLAWSERVER} instance -@end itemize -Make the @code{CLAWSERVER} begin to dispatch requests +@sp 2 +@smalllisp +(defvar *ht-connector* (make-instance 'hunchentoot-connector + :address "localhost" + :port 4242 + :sslport nil)) +@end smalllisp +@sp 2 + +This makes the connector to listen on port 4242 for http requests. + +Once created your connector you have to provide to the server a logger. Using @code{HUNCHENTOOT-LOGGER} service instantiated like + +@sp 2 +@smalllisp +(defvar *ht-log-manager* (make-instance 'hunchentoot-logger)) +@end smalllisp +@sp 2 + +You then have to instantiate a session manager service that will handle users sessions. + +@sp 2 +@smalllisp +(defvar *sm* (make-instance 'default-session-manager)) +@end smalllisp +@sp 2 + +Now you are ready to instantiate the application server. + +@sp 2 +@smalllisp +(defvar *demo-server* (make-instance 'clawserver + :connector *ht-connector* + :log-manager *ht-log-manager* + :session-manager *sm* + :base-path "/claw" + :reverse-proxy-path "/claw")) +@end smalllisp +@sp 2 + +@code{BASE-PATH} is the path where all your web applications will go under when directly accessing @value{claw} application server, while +@code{REVERSE-PROXY-PATH} is the path used when passing requests through Apache mod_proxy. + +The application server is tow ready to be started. + +@sp 2 +@smalllisp +(clawserver-start *demo-server*) +@end smalllisp +@sp 2 + +Test now with your browser the url http://localhost:4242/claw + +You can see a page reporting a 404 error, this is normal since we haven't already provided any @code{LISPLET} or resource to the server. + +you can stop the server with + +@sp 2 +@smalllisp +(clawserver-stop *demo-server*) +@end smalllisp +@sp 2 + +@section Lisplets + +@value{claw} has the notion of lisplets, components similar to java servlets, but written in Common Lisp. + +As the name suggests, a lisplet is defined by the @code{LISPLET} class. A lisplet is an envelop for your application, so +@code{CLAWSERVER}, being an application server, may hold more then one web application, each sharing or protecting its realm with +other web applications. This means that an application may or may not share user sessions among the others, configuring its own realm. + +A lisplet contains all the resources (dynamic pages, static files, and services) that your application may need. + +Registering a @code{LISPLET} into the application server is very easy, in fact you only need to instantiate a LISPLET object with its proper +realm defined, and tell the server to register it. +The default realm for a lisplet is @code{"claw"}, if not defined differently during its creation; when two or more lisplets share the same realm, +they will share users sessions too. + +@sp 2 +@smalllisp +(defvar *sample-lisplet* + (make-instance 'lisplet :realm "demo" + :base-path "/demo")) +@end smalllisp +@sp 2 + +When defining the lisplet @code{BASE-PATH} options @value{claw} application server will build a path to that requests that come, will have to match +the application server path plus the lisplet one. + +Let's now add an index page. We will not use any web framework yet, just plain text. + +@sp 2 +@smalllisp +(lisplet-register-function-location + *sample-lisplet* + #'(lambda () "<html> + <head> + <title>A very simple page</title> + <head> + <body> + <h1>hello world!</h1> + </body> + </html>") + "index.html" + :welcome-page-p t) +@end smalllisp +@sp 2 + +When setting the @code{WELCOME-PAGE-P} to @code{T}, it means that it will be your application welcome page, as the name suggests. +Of course there may be only one welcome page per application. +So, in the sample above, you can access the page browsing @indicateurl{http://localhost:4242/claw/demo/index.html%7D, but also +@indicateurl{http://localhost:4242/claw/demo%7D and @indicateurl{http://localhost:4242/claw/demo/%7D. + +We haven't defined a mime type, because @value{claw} application server is able to recognise it by uri extension. + +When an uri extension is not recognised, you have to tell the server what's the resource mime-type. +So we will slightly change the example above to test it. + +@sp 2 +@smalllisp +(lisplet-register-function-location + *sample-lisplet* + #'(lambda () "<html> + <head> + <title>A very simple page</title> + <head> + <body> + <h1><a href='info.do'>hello world!</a></h1> + </body> + </html>") + "index.html" + :welcome-page-p t) + +(lisplet-register-function-location + *sample-lisplet* + #'(lambda () + (setf (claw-content-type) "text/html") + "<html> + <head> + <title>A very simple info</title> + <head> + <body> + <h2>It's running!</h2> + </body> + </html>") + "info.do") +@end smalllisp +@sp 2 + +Finally you have to register the lisplet into the application server. + +@sp 2 +@smalllisp +(clawserver-register-lisplet *demo-server* *sample-lisplet*) +@end smalllisp +@sp 2 + +Now you are ready to put all together and try on your own your first claw application. + +@sp 2 +@smalllisp +;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*- +;;; $Header: claw-demo1.lisp $ +(in-package :cl-user) + +(eval-when (:compile-toplevel :load-toplevel) + (asdf:oos 'asdf:load-op :claw-as) + (asdf:oos 'asdf:load-op :claw-hunchentoot-connector)) + +(defpackage :claw-demo1 + (:use :cl :claw-as :claw-hunchentoot-connector)) + +(in-package :claw-demo1) + +(defvar *ht-connector* (make-instance 'hunchentoot-connector + :address "localhost" + :port 4242 + :sslport nil)) + +(defvar *ht-log-manager* (make-instance 'hunchentoot-logger)) + +(defvar *sm* (make-instance 'default-session-manager)) + +(defvar *demo-server* (make-instance 'clawserver + :connector *ht-connector* + :log-manager *ht-log-manager* + :session-manager *sm* + :base-path "/claw" + :reverse-proxy-path "/claw")) + +(defvar *sample-lisplet* + (make-instance 'lisplet :realm "demo" + :base-path "/demo")) + +(lisplet-register-function-location + *sample-lisplet* + #'(lambda () "<html> + <head> + <title>A very simple page</title> + <head> + <body> + <h1><a href='info.do'>hello world!</a></h1> + </body> + </html>") + "index.html" + :welcome-page-p t) + +(lisplet-register-function-location + *sample-lisplet* + #'(lambda () + (setf (claw-content-type) "text/html") + "<html> + <head> + <title>A very simple info</title> + <head> + <body> + <h2>It's running!</h2> + </body> + </html>") + "info.do") + +(clawserver-register-lisplet *demo-server* *sample-lisplet*) + +(clawserver-start *demo-server*) + +@end smalllisp +@sp 2 + +You now can brows your new mini application opening your browser at @indicateurl{http://localhost:4242/claw/demo/%7D. + +@section External resources + +When you want to make accessible you local file-system @value{claw} application server provides an easy way to register them into your application. + +For example in a Unix operating system you can access /tmp resources using the @code{LISPLET-REGISTER-RESOURCE-LOCATION} method. +So you can add to the example of the previous chapter the following code: + +@sp 2 +@smalllisp +(lisplet-register-resource-location *sample-lisplet* + #P"/tmp/" + "docroot/") +@end smalllisp +@sp 2 + +Now you can see the server log file pointing you browser to the following url @indicateurl{http://localhost:4242/claw/demo/docroot/hunchentoot.log%7D. + +Notice that you can add and remove resources to a lisplet while this is already running, that it's a great feature that may speed up project development and also let you to create +dynamic sites like @url{http://en.wikipedia.org/wiki/Content_management_system, CMS}. + +You could also register the single ``/tmp/hunchentoot.log'' file into your lisplet, this would protect other resources making them inaccessible by the users of you application. + +There are cases where you may need to stream resources that are not in the file system, for example when you have to generate pdf invoices or dynamic generated images. + +This again is another easy task, simply use @code{LISPLET-REGISTER-FUNCTION-LOCATION} as you did for the two pages in the example of the previous chapter. + +To get an example of this, download a jpg image and put it under the /tmp directory, naming it 'foo.jpg'. Then use the following code to register it +into your servlet. + +@sp 2 +@smalllisp +(lisplet-register-function-location + *sample-lisplet* + (lambda () + (setf (claw-content-type) "image/jpeg") + (with-open-file (in #P"/tmp/foo.jpg" :element-type '(unsigned-byte 8)) + (let ((image-data (make-array (file-length in) + :element-type '(unsigned-byte 8)))) + (read-sequence image-data in) + image-data))) + "images/foo.bar" ) +@end smalllisp +@sp 2 + +Now you can access your ``generated'' image pointing your browser at the url @indicateurl{http://localhost:4242/claw/demo/images/foo.bar%7D. +Though you have called the image ``foo.bar'', since you have provided the resource registration setting its content type with ``image/jpeg'', +it is rendered as a normal jpeg image even if its extension is ``.bar''. + +@section System wide resources + +One of the other great feature of @value{claw} are system wide resources. + +When you define a system wide resource, this will be available to all your registered lisplets. +This feature gives you the possibility to create @value{claw} libraries whose resources are available to all your lisplets. +An example of a @value{claw} library is the @emph{claw-html.dojo} library that extends the @emph{claw-html} web framework and +provides all the @url{http://www.dojotoolkit.org, dojo} resources to the server web applications. + +Defining a system wide resource is not so different from defining a local one. The following code gives you an example on how to do. + +@sp 2 +@smalllisp +(register-library-resource "tempdir/" #P"/tmp/") +@end smalllisp +@sp 2 + +A difference from a local and a system wide resource is that @code{REGISTER-LIBRARY-RESOURCE} registers the resource at the application server level +and not at the lisplet level. + +This implies that the resource in your example is available at the following url @indicateurl{http://localhost:4242/claw/tempdir/hunchentoot.log%7D to clarify. + +@section Authentication and authorization + +Sometimes you may have the need to protect particular resources because they might contain sensible data, or any other reason. + +@value{claw} application server provides such functionalities. + +Firstly you need to instantiate a subclass of @code{CONFIGURATION} class and successively register into @value{claw} application server. +You also need to implement the @code{CONFIGURATION-LOGIN} method that will perform the authentication logic. + +@value{claw} may handle both @emph{basic} an @emph{form based} authentication types, you'll begin to see a basic authentication and then will change +it into a form based one. + +Now begin defining a @code{CONFIGURATION} subclass implementing its @code{CONFIGURATION-LOGIN} method and add it to your previous sample sample. + +@sp 2 +@smalllisp +(defclass demo-configuration (configuration) + ((users :reader users + :initform '(("admin" "pwdadmin" ("admin-role" "user-role")) + ("user1" "pwduser" ("user-role")))))) + +(defmethod configuration-login ((configuration demo-configuration)) + (multiple-value-bind (user password) + (if (eq (lisplet-authentication-type *claw-current-lisplet*) :basic) + (claw-authorization) + (values (claw-parameter "username") + (claw-parameter "password"))) + (let ((maybe-principal (assoc user))) + (when (and maybe-principal (string= password (second maybe-principal))) + (make-instance 'principal + :name user + :roles (third maybe-principal)))))) +@end smalllisp +@sp 2 + +As for any other resource you have to register the @code{CONFIGURATION}, but not into your lisplet, instead the +@code{CONFIGURATION} must be registered into the @value{claw} application server because it is cross-application, +that means that more then one lisplet may share the same authentication configuration. + +@sp 2 +@smalllisp +(clawserver-register-configuration *demo-server* "demo" (make-instance 'demo-configuration)) +@end smalllisp +@sp 2 + +For your example, the @code{*SAMPLE-LISPLET*} lisplet instance has been registered into the @code{"demo"} realm. +This means that authentication is performed for a configuration that matches the same realm. For this reason the code +above sets the second parameter of the @code{CLAWSERVER-REGISTER-CONFIGURATION} to @code{"demo"}. + +The @code{*claw-current-lisplet*} is a locally scoped variable that the application server sets while it's performing +the request dispatching.
-@sp 1 -@fnindex clawserver-stop -@noindent -@code{clawserver-stop obj} -@indent +The @code{LISPLET-AUTHENTICATION-TYPE} method returns @code{:BASIC} when the authentication type is @emph{basic}, while +it returns @code{:FORM} when it is @emph{form based}.
-@itemize -@item -@emph{obj} The @code{CLAWSERVER} instance -@end itemize -Make the @code{CLAWSERVER} stop. +When the authentication type is @emph{basic} the @code{CLAW-AUTHORIZATION} function returns a value pair, containing the passed user name and password, while, when the authentication type is @emph{form based}, user name and password +are retrieved from the http request.
-@sp 1 -@fnindex clawserver-register-lisplet -@fnindex lisplet -@noindent -@code{clawserver-register-lisplet clawserver lisplet-obj}@* -@indent +To get a parameter from the http request claw provides three methods.
-@itemize -@item -@emph{obj} The @code{CLAWSERVER} instance +@itemize @minus @item -@emph{lisplet-obj} A @code{LISPLET} class instance -@end itemize -Registers a @code{LISPLET}, that is an `application container` for request dispatching. - -@sp 1 -@fnindex clawserver-unregister-lisplet -@noindent -@code{clawserver-unregister-lisplet clawserver lisplet-obj} -@indent - -@itemize +claw-get-parameter To get parameters when performing a get method. @item -@emph{obj} The @code{CLAWSERVER} instance +claw-post-parameter To get parameters when performing a post method. @item -@emph{lisplet-obj} A @code{LISPLET} class instance +claw-parameter Returns the value of the GET or POST parameter, when no GET one is found. @end itemize -Unregisters a @code{LISPLET}, that is an `application container`, an so all it's resources, from the @code{CLAWSERVER} instance. - -@section Starting the server - -Starting @value{claw} is very easy and requires a minimal effort. -@value{claw} supports both http and https protocols, thought enabling SSL connection for @value{claw} requires -a little more work then having it responding only to http calls.
-@subsection Making @value{claw} work on http protocol +Now you only need to protect some resource into your lisplet, so you'll now add a new one into your sample and protect it with the newly defined configuration.
-To simply start @value{claw} server, without enabling SSL requests handling, you just need few steps: +Firstly you'll try the @emph{basic} authentication mode, to witch then to the @emph{form based} one. + +@sp 2 +@smalllisp +(lisplet-register-function-location + *sample-lisplet* + #'(lambda () (format nil + "<html> + <head> + <title>protected resource</title> + <head> + <body> + <h2>Welcome ~a</h2> + ~@@[~a~] + </body> + </html>" + (principal-name (current-principal)) + (when (user-in-role-p '("admin-role")) + "<h3>You are an administrator now!</h3>"))) + "protected.html") + +(lisplet-protect *sample-lisplet* "protected.html" '("admin-role" "user-role")) +@end smalllisp +@sp 2 + +Here it is the full code: + +@sp 2 +@smalllisp +(in-package :cl-user) + +(eval-when (:compile-toplevel :load-toplevel) + (asdf:oos 'asdf:load-op :claw-as) + (asdf:oos 'asdf:load-op :claw-hunchentoot-connector)) + +(defpackage :claw-demo1 + (:use :cl :claw-as :claw-hunchentoot-connector)) + +(in-package :claw-demo1) + +(defvar *ht-connector* (make-instance 'hunchentoot-connector + :address "localhost" + :port 4242 + :sslport nil)) + +(defvar *ht-log-manager* (make-instance 'hunchentoot-logger)) + +(defvar *sm* (make-instance 'default-session-manager)) + +(defvar *demo-server* (make-instance 'clawserver + :connector *ht-connector* + :log-manager *ht-log-manager* + :session-manager *sm* + :base-path "/claw" + :reverse-proxy-path "/claw")) + +(defvar *sample-lisplet* + (make-instance 'lisplet :realm "demo" + :base-path "/demo")) + +(lisplet-register-function-location + *sample-lisplet* + #'(lambda () "<html> + <head> + <title>A very simple page</title> + <head> + <body> + <h1><a href='info.do'>hello world!</a></h1> + <h1><a href='protected.html'>protected!</a></h1> + </body> + </html>") + "index.html" + :welcome-page-p t) + +(lisplet-register-function-location + *sample-lisplet* + #'(lambda () + (setf (claw-content-type) "text/html") + "<html> + <head> + <title>A very simple info</title> + <head> + <body> + <h2>It's running!</h2> + </body> + </html>") + "info.do") + +(clawserver-register-lisplet *demo-server* *sample-lisplet*) + +(clawserver-start *demo-server*) + +(lisplet-register-resource-location *sample-lisplet* + #P"/tmp/" + "docroot/") + +(lisplet-register-function-location + *sample-lisplet* + (lambda () + (setf (claw-content-type) "image/jpeg") + (with-open-file (in #P"/tmp/foo.jpg" :element-type '(unsigned-byte 8)) + (let ((image-data (make-array (file-length in) + :element-type '(unsigned-byte 8)))) + (read-sequence image-data in) + image-data))) + "images/foo.bar" ) + +(register-library-resource "tempdir/" #P"/tmp/") + +(defclass demo-configuration (configuration) + ((users :reader users + :initform '(("admin" "pwdadmin" ("admin-role" "user-role")) + ("user1" "pwduser" ("user-role")))))) + +(defmethod configuration-login ((configuration demo-configuration)) + (multiple-value-bind (user password) + (if (eq (lisplet-authentication-type *claw-current-lisplet*) :basic) + (claw-authorization) + (values (claw-parameter "username") + (claw-parameter "password"))) + (let ((maybe-principal (assoc user (users configuration) :test #'equal))) + (when (and maybe-principal (string= password (second maybe-principal))) + (setf (current-principal) (make-instance 'principal + :name user + :roles (third maybe-principal))))))) + +(clawserver-register-configuration *demo-server* "demo" (make-instance 'demo-configuration)) + +(lisplet-register-function-location + *sample-lisplet* + #'(lambda () (format nil + "<html> + <head> + <title>protected resource</title> + <head> + <body> + <h2>Welcome ~a</h2> + ~@@[~a~] + </body> + </html>" + (principal-name (current-principal)) + (when (user-in-role-p '("admin-role")) + "<h3>You are an administrator now!</h3>"))) + "protected.html") + +(lisplet-protect *sample-lisplet* "protected.html" '("admin-role" "user-role")) +@end smalllisp +@sp 2 + +A note about @emph{(setf (current-principal) ...)} must be taken; this line in fact stores the newly created principal into the lisplet session. + +The method @code{LISPLET-PROTECT} protects a given resource, identified by its lisplet-relative uri, against a list of given roles. In the current +example the page ``protected.html'' will be available only to users that belong to the ``admin-role'' and ``user-role''. + +The function @code{USER-IN-ROLE-P} checks if the principal returned by the function @code{CURRENT-PRINCIPAL}, belongs to at least one of the given roles. +This method is useful to conditional filling you html page, as you just did in the sample above. + +Now it's time to move the demo lisplet to a form based authentication. + +The @emph{form based} authentication method is not much more difficult from the @emph{basic} one, you just have to crate +a form that calls the @code{CONFIGURATION-LOGIN} of the configuration retrieved by the @code{CURRENT-CONFIGURATION} function. + +You then have to register the page containing such form as a login page. + +The following example clarify what just described + +@sp 2 +@smalllisp +(lisplet-register-function-location + *sample-lisplet* + #'(lambda () + (configuration-login (current-config)) + (if (current-principal) + (claw-redirect "protected.html") + "<html> + <head> + <title>A very simple page</title> + <head> + <body> + <h1>User login</h1> + <form method='post'> + username <input type='text' name='username'> + password <input type='password' name='password'> + <input type='submit' name='submit' value='Login'> + </form> + </body> + </html>")) + "login.html" + :login-page-p t) +@end smalllisp +@sp 2 + +This is not much more then the effort for using the @emph{basic} authentication method. + +You have only to notice that the @code{name} tag attribute of the input boxes for username and password must match the parameters +expected into the @code{CONFIGURATION-LOGIN} method implementation. For this example they are in fact ``username'' and ``password''. + +The lo gout phase is even more simple, you only need to destroy the user session calling the function @code{claw-remove-session}. + +So putting all together + +@sp 2 +@smalllisp +(in-package :cl-user) + +(eval-when (:compile-toplevel :load-toplevel) + (asdf:oos 'asdf:load-op :claw-as) + (asdf:oos 'asdf:load-op :claw-hunchentoot-connector)) + +(defpackage :claw-demo1 + (:use :cl :claw-as :claw-hunchentoot-connector)) + +(in-package :claw-demo1) + +(defvar *ht-connector* (make-instance 'hunchentoot-connector + :address "localhost" + :port 4242 + :sslport nil)) + +(defvar *ht-log-manager* (make-instance 'hunchentoot-logger)) + +(defvar *sm* (make-instance 'default-session-manager)) + +(defvar *demo-server* (make-instance 'clawserver + :connector *ht-connector* + :log-manager *ht-log-manager* + :session-manager *sm* + :base-path "/claw" + :reverse-proxy-path "/claw")) + +(defvar *sample-lisplet* + (make-instance 'lisplet :realm "demo" + :base-path "/demo")) + +(lisplet-register-function-location + *sample-lisplet* + #'(lambda () "<html> + <head> + <title>A very simple page</title> + <head> + <body> + <h1><a href='info.do'>hello world!</a></h1> + <h1><a href='protected.html'>protected!</a></h1> + </body> + </html>") + "index.html" + :welcome-page-p t) + +(lisplet-register-function-location + *sample-lisplet* + #'(lambda () + (setf (claw-content-type) "text/html") + "<html> + <head> + <title>A very simple info</title> + <head> + <body> + <h2>It's running!</h2> + </body> + </html>") + "info.do") + +(clawserver-register-lisplet *demo-server* *sample-lisplet*) + +(clawserver-start *demo-server*) + +(lisplet-register-resource-location *sample-lisplet* + #P"/tmp/" + "docroot/") + +(lisplet-register-function-location + *sample-lisplet* + (lambda () + (setf (claw-content-type) "image/jpeg") + (with-open-file (in #P"/tmp/foo.jpg" :element-type '(unsigned-byte 8)) + (let ((image-data (make-array (file-length in) + :element-type '(unsigned-byte 8)))) + (read-sequence image-data in) + image-data))) + "images/foo.bar" ) + +(register-library-resource "tempdir/" #P"/tmp/") + +(defclass demo-configuration (configuration) + ((users :reader users + :initform '(("admin" "pwdadmin" ("admin-role" "user-role")) + ("user1" "pwduser" ("user-role")))))) + +(defmethod configuration-login ((configuration demo-configuration)) + (multiple-value-bind (user password) + (if (eq (lisplet-authentication-type *claw-current-lisplet*) :basic) + (claw-authorization) + (values (claw-parameter "username") + (claw-parameter "password"))) + (let ((maybe-principal (assoc user (users configuration) :test #'equal))) + (when (and maybe-principal (string= password (second maybe-principal))) + (setf (current-principal) (make-instance 'principal + :name user + :roles (third maybe-principal))))))) + +(clawserver-register-configuration *demo-server* "demo" (make-instance 'demo-configuration)) + +(lisplet-register-function-location + *sample-lisplet* + #'(lambda () (format nil + "<html> + <head> + <title>protected resource</title> + <head> + <body> + <h3><a href='logout.html'>logout</a></h3> + <h2>Welcome ~a</h2> + ~@@[~a~] + </body> + </html>" + (principal-name (current-principal)) + (when (user-in-role-p '("admin-role")) + "<h3>You are an administrator now!</h3>"))) + "protected.html") + +(lisplet-protect *sample-lisplet* "protected.html" '("admin-role" "user-role")) + +(lisplet-register-function-location + *sample-lisplet* + #'(lambda () + (configuration-login (current-config)) + (if (current-principal) + (claw-redirect "protected.html") + "<html> + <head> + <title>A very simple page</title> + <head> + <body> + <h1>User login</h1> + <form method='post'> + username <input type='text' name='username'> + password <input type='password' name='password'> + <input type='submit' name='submit' value='Login'> + </form> + </body> + </html>")) + "login.html" + :login-page-p t) + +(lisplet-register-function-location + *sample-lisplet* + #'(lambda () + (claw-remove-session) + (claw-redirect "index.html")) + "logout.html") +@end smalllisp +@sp 2 + +Of course you can protect entire branches of you applications, this is done protecting the uri location, for example you could protect all resources into +'private' forlder. + +@sp 2 +@smalllisp +(lisplet-protect *sample-lisplet* "private/" '("admin-role" "user-role")) +@end smalllisp +@sp 2 + +@section HTTPS connections
-@cartouche -@lisp -(defparameter *clawserver* (make-instance 'clawserver)) -(clawserver-start *clawserver*) -@end lisp -@end cartouche +@value{claw} is able to pass connection over the @code{HTTPS} protocol and to manage sessions and services between @code{HTTP} and @code{HTTPS}.
-This will start the web server on port 80 that is the default. +To make the server listen for https requests incoming you will have to provide it at least a @code{sslport}
+With the hunhcentoot connector if you don't pass through Apache mod_proxy you'll also have to pass it @code{ssl-certificate-file}, @code{ssl-privatekey-file} and , eventually, a @code{ssl-privatekey-password} parameter if the certificate is password protected. Of course you can also use Apache SSL certificate files.
-Of course you can create a parametrized version of @code{CLAWSERVER} instance for example specifying the listening port as the following: - -@cartouche -@lisp -(defparameter *clawserver* (make-instance 'clawserver :port 4242)) -(clawserver-start *clawserver*) -@end lisp -@end cartouche - -@subsection Making @value{claw} work on both http and https protocols - -To enable @value{claw} to https firt you need a certificate file. -A quick way to get one on a Linux system is to use openssl to generate the a certificate PEM file, the following example explains how to do. +To generate your own certificate files under Linux operating system, you can use openssl to get one in the way described below.
Firstly you'll generate the private key file:
-@cartouche -@example +@sp 2 +@smallexample #> openssl genrsa -out privkey.pem 2048 @sp 1 Generating RSA private key, 2048 bit long modulus @@ -399,13 +783,13 @@ e is 65537 (0x10001) @sp 1 #> -@end example -@end cartouche +@end smallexample +@sp 2
Then the certificate file:
-@cartouche -@example +@sp 2 +@smallexample #> openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095 @sp 1 You are about to be asked to enter information that will be incorporated @@ -424,32 +808,36 @@ Email Address []:admin@@mycompany.com @sp 1 #> -@end example -@end cartouche +@end smallexample +@sp 2 + +At this point you can instantiate the connector to directly handle HTTPS requests in the following way:
-Now you can start @code{CLAWSERVER} in both http and https mode: +@sp 2 +@smalllisp +(defvar *ht-connector* + (make-instance 'hunchentoot-connector + :address "localhost" + :port 4242 + :sslport 4343 + :ssl-certificate-file #P "/PATH_TO_CERT_FILES/cacert.pem" + :ssl-privatekey-file #P "/PATH_TO_CERT_FILES/privkey.pem")) +@end smalllisp +@sp 2 + +If you plan to pass connections through Apache mod_proxy there is no need to enable ssl at CLAW level so it is sufficient to only set @code{port} and +@code{sslport}. + +Another great feature of the @value{claw} application server is the ability to automatically redirect requests to protected resources through the @code{HTTPS} protocol. +This is made extremely easy setting to @emph{true} the @code{REDIRECT-PROTECTED-RESOURCES-P} lisplet parameter like in the following example + +@sp 2 +@smalllisp +(defvar *sample-lisplet* + (make-instance 'lisplet :realm "demo" + :redirect-protected-resources-p t + :base-path "/demo")) +@end smalllisp +@sp 2
-@cartouche -@lisp -(defparameter *clawserver* (make-instance 'clawserver :port 4242 - :sslport 4443 - :ssl-certificate-file #P"/path/to/certificate/cacert.pem" - :ssl-privatekey-file #P"/path/to/certificate/privkey.pem"))) -(clawserver-start *clawserver*) -@end lisp -@end cartouche - -@value{claw} is now up and you can browse it with your browser using address http://www.yourcompany.com:4242 and http://www.yourcompany.com:4443. -Of course you will have only a 404 response page! - -@subsection Making all applications to work under a common path - -You have the possibility to define a common path to mapp all @value{claw} applications registered into the server, -defining the global variable @code{*CLAWSERVER-BASE-PATH*}. This way, if you have two applcations mapped for example to -``/applicationA'' and ``/applicationB'', setting that variable to the common path ``/yourcompany'' with the instruction -@cartouche -@lisp -(setf *clawserver-base-path* "/yourcompany") -@end lisp -@end cartouche -you will have the two applications now mapped to ``/yourcompany/applicationA'' and ``/yourcompany/applicationB''. +So all the resources mapped with @code{LISPLET-PROTECT} (and the login page in a form based authentication) will be redirected to the @code{HTTPS} protocol.