Caution: my English is far from perfect. (Русский тоже не всегда хорош).
Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Monday, 26 September 2022

Only Persistent LogIn

Google login dialog does not have the "stay signed-in" checkbox anymore - you session is always persistent. Once logged in, this web browser will have access to your account even after reboot.

 

Monday, 15 August 2022

A robots.txt Problem

To prevent a part of our web application from being scanned by search engines and other web crawlers, we add a robots.txt like

    User-agent: *
    Disallow: /path


It's so simple, what can go wrong?

A real story happened to me.

Turns out, my cloud platform - Google App Engine - has a caching and compression layer between the application and the Internet. It can gzip content for one client, cache it, and then return the same gzipped responses to other clients, even if they haven't specified the Accept-Encoding: gzip header; or even explicitly requested uncompressed content.

This unwise, in my opinion, behaviour is documented here: https://cloud.google.com/appengine/docs/legacy/standard/java/how-requests-are-handled#response_caching

Example:

# Force a gzipped response

$ curl -v -H 'Accept-Encoding: gzip' -H 'User-Agent: gzip' https://yourapp.appspot.com/robots.txt
...
content-encoding: gzip
...
Warning: Binary output can mess up your terminal. Use "--output -" to tell
Warning: curl to output it to your terminal anyway, or consider "--output
Warning: <FILE>" to save to a file.


# Now explicitly request uncompressed robots.txt

$ curl -v -H 'Accept-Encoding: identity' https://yourapp.appspot.com/robots.txt
...
content-encoding: gzip
...
Warning: Binary output can mess up your terminal. Use "--output -" to tell
Warning: curl to output it to your terminal anyway, or consider "--output
Warning: <FILE>" to save to a file.


(BTW, despite the doc says the default caching duration is 10 minutes, I observed Google App Engine returning gzipped responses for at least 30 minutes).

A web crawler (Dotbot from moz.com) has encountered such a gzipped robots.txt response and was unable to parse it, so considers all the URLs in the app domain as allowed for crawling. Moreover, the crawler caches this gzipped response. All its subsequent requests to robots.txt are conditional (ETag based, I think), and result in 304 Not Modified, thus the crawler continues relying on the gzipped version it cannot parse, and regularly visits the unwanted URLs.

Luckily, the Dotbot clearly identifies itself in the User-Agent header, and they have a working support email, so after a five month communication in a ticket I discovered the reason.

Fixed the Google App Engine behaviour by adding an explicit configuration to the appengine-web.xml:

  <static-files>
    <include path="/**">
      <http-header name="Vary" value="Accept-Encoding"/>
    </include>
    <exclude path="/**.jsp"/>
  </static-files>

Also made a little modification to the robots.txt, to be sure the ETag changes.


Monday, 26 September 2016

Partitioned, Available and Consistent?

A database storing a bank account with balance $100 is replicated to two nodes. A network partition happens between the nodes. I am withdrawing $20 cash from an ATM which has access to a replica on one side of the partition. At the same moment I'm charged $10 for hosting and the payment gateway has access the replica on the other side of the partition.

That's OK, the DB nodes process the withdrawal as long as the amount is under a half of the balance at the time of their last synchronization. (Each node can spend up to $50).

Does it qualify as simultaneous availability, consistency and partition tolerance?

Account log says, depending on the node you're connected to, either:

4. final balance: between $30 and $80
3. spent $20
2. network partition 50 / 50
1. initial balance: $100

or

4. final balance: between $40 and $90
3. spent $10
2. network partition 50 / 50
1. initial balance: $100

Saturday, 5 September 2015

Imperative to Functional

How to automatically transform any imperative code into a functional code?

Think for a moment before opening the answer: >> <<

Copy all the data before invoking the imperative code.

That's not a joke. There are techniques (e.g. copy on write) and data structures which can make it efficient. I have no time to write-down all the existing analogies coming to my mind now. What I want to say, the imperative and functional approaches in many important aspects are not that different. We can think of imperative assignment to a variable as of pure function which computes new world where this variable has new value.

Wednesday, 12 August 2015

Javascript: LexicalEnvironment vs VariableEnvironment

One javascript feature I learned while working on POCL.

Consider:

function foo() {
    try {
        throw 'hello'
    } catch (e) {
        return function inner() {return e}
    }
}

foo()()
=> 'hello'

function foo2() {
    try {
        throw 'hello'
    } catch (e) {
        function inner() {return e}
        return inner;
    }
}

foo2()()
Uncaught ReferenceError: e is not defined

Why do the results differ?

In the first case INNER is a function expression, while in the second case INNER is a function declaration.

According to the standard:

Javascript execution context has two Lexical Environments. One is called LexicalEnvironment (he-he), and another is VariableEnvironment [§10.3].

The CATCH statement introduces new LexicalEnvironment where E is defined, and leaves the VariableEnvironment untouched.

Function expression receives current LexicalEnvironment as its scope, while function declaration receives current VariableEnvironment as its scope [§13], therefore E is invisible for the function declaration.

Why everything is so complicated, why two Lexical Environments exist?

My guess it's because we can call function before its declaration:

(function () {
    return inner();
    function inner() {return 1};
})()

=> 1

(function () {
    var x = inner();
    return x;
    function inner() {return 1};
})()

=> 1

(function () {
    var x = inner();
    try {
       throw 'error'
    } catch (unused) {
       function inner() {return 1};
    };
    return x;
})()

=> 1;

To support this behavior, the variable INNER is created and bound to the function object early, when control enters the surrounding function; before any CATCH is executed; and therefore it doesn't see the variable introduced by the CATCH clause.

Unlike normal variables, visible in the scope of the whole enclosing function, the variable introduced by CATCH is only visible inside the CATCH clause:

(function() {
    var x = 1;
    console.log(x);
    try {
        throw 2
    } catch (x) {
        console.log(x)
    }
    console.log(x)
})()

// log output:
1
2
1


That's why (I guess) javascript needs to distinguish LexicalEnvironment and VariableEnvironment.

During years of JS programming I never hit this difference in practice - never captured a catch variable by a closure. I only detected this strange thing when reading the standard in order to implement some code transformations needed for POCL, and was wondering, why does it need to distinguish LexicalEnvironment from VariableEnvironment.

BTW, another case when names are bound within a scope smaller that the enclosing function is the WITH statement. So it has the same problem regarding function expression / function declaration:

var o = {x: 1, y: 2}
(function() {with (o) {return function inner() {return x}}})()()
=> 1

(function() {with (o) {function inner() {return x}; return inner}})()()
Uncaught ReferenceError: x is not defined


Friday, 7 August 2015

Predictive Optimizing Code Loading

An idea I kept in mind for several years. Finally experimented with it: https://github.com/avodonosov/pocl.

I consider the experiment successful. It could be a useful technique of application acceleration.

Friday, 22 August 2014

Semantic Versioning is Not the Solution

People often think they can introduce incompatible changes in their library API, and just increase major version number, as semantic versioning proposes, to save the library clients from problems.

It is not true.

Consider a dependency tree:
my-application
  web-server 1.1.1
    commons-logging 1.1.1
  db-client 1.1.1
    commons-logging 1.1.1
  authentication 1.1.1
    commons-logging 1.1.1
Now commons-logging changes its API incompatibly and is released as commons-logging 2.0.1. Authentication adopts commons-logging 2.0.1 while other libraries still depend on 1.1.1:
my-application
  web-server 1.1.1
    commons-logging 1.1.1
  db-client 1.1.1
    commons-logging 1.1.1
  authentication 1.1.2
    commons-logging 2.0.1
Now my-application is broken, because the dependency tree includes two versions of commons-logging which share packages, class/functions names, and thus can not be loaded simultaneously.

When you release an incompatible API this way, you essentially split the world of dependent libraries into two parts: the ones depending on the old version, and ones depending in new version. Libraries from the first part can not be used together with libraries from the second part.

A better way to introduce incompatible API is to release it as a new library, for example commons-logging2, or new-logging. Make it possible to use the new library simultaneously with the old one, e.g. it should have new package name.
Doing so will protect clients in majority of cases.

If we are releasing new library for new API, there is no need for such a thing as "major version number".

NB: in some module managers, most notably in javascript, there are no global package/class names on which different versions of a library can interfere. But in majority of programming languages that problem exists.

Monday, 3 September 2012

Linux memory over-commit.

Linux memory over-commit. Previously I've only read about it, but now I am bitten by it. When cl-test-grid agent (a CCL program) starts ECL and ECL starts gcc, linux on my VPS kills someone of them (usually CCL). Sometimes it just doesn't allow to fork new process. Why do CCL and other Lisps allocate so large heap at startup?

Tuesday, 27 September 2011

Private Members in Javascript

It is usually advised to use closures to emulate private class members in javascript. Like this:

function MyClass(param) {

   var privateVar = 1;

   function privateMethod(a) {
      return a + 2;
   }

   this.publicVar = param;

   this.publicMethod = function(x) {
     return x + this.publicVar - privateVar;
   }
}

var my = new MyClass();
my.publicMethod(1);

I find this approach inconvenient for various reasons. Right away I can remember at least 4:

  • Private members are not visible in debugger (FireBug)
  • You can't access private members programmatically (during development I often need to experiment and access private members)
  • Public methods which need access to private data should be defined inside of the closure, we can't add them to the class prototype.
  • In the code private and public members are referred differently: without this for private and with this for public. If I want to make some previously private method public, I need to find all the references and change method() to this.method(). And often the correct this is not available, because we are in some deeper function/closure, where this refers different object (for example we are in a callback of AJAX call). So, making a method public can not always be done mechanically.

We may try to workaround the last issue by defining even public methods as closure-local functions and then publishing them explicitly:

function MyClass(param) {

   // ...

   function publicMethod(x) {
     return x + publicVar - privateVar;
   }

   function publicMethod2(x, y) {
     return publicMethod(x) + y;
   }

  this.publicMethod = publicMethod;
  this.publicMethod2 = publicMethod2;
}

But that way descendant classes can't override the public methods. In short, the closure approach is unsatisfying.

I have an idea which looks for me better than using closures to emulate private members.

The class implementation may have everything accessible via this (i.e. public):

function MyClass(param) {
   this.privateVar = param;
}

MyClass.prototype.privateMethod = function (a) {
      return a + 2;
}

MyClass.prototype.publicMethod = function(x) {
     return x - this.privateVar;
}

var my = new MyClass();
my.privateMethod(2);
my.publicMethod(1);
Then, if I want to restrict class clients from using the private implementation details (and feel that code comments are not enough for that) I can provide them only public interface:
var my = {
   impl: new MyClass(1),
   publicMethod: function (x) {return this.imp.publicMethod(x);}
}

// This is how clients normally use it:
my.publicMethod(1);

// But it is also possible to check the private implementation for debugging and development purposes:
my.impl.privateMethod(2);

This separation of encapsulation from classes resembles Common Lisp.

And the difference between obj.method() and obj.impl.method() is similar to Common Lisp: package-name:func-name for exported symbols and package-name::func-name for not-exported symbols. Client can access private implementation if he wants, but the syntax difference ensures he knows what he is doing.

Depending on the situation, we may create such interface objects manually, or create a little function which wraps any object into an interface:


// Whole the code below is tested, you can run it e.g. in FireBug console.

function as(impl, interface) {
  var wrapper = {impl: impl}; 
  for (prop in interface) {
    wrapper[prop] = makeImplCaller(prop);
  }
  return wrapper;
}

function makeImplCaller(functionName) {
    return function() {
        return this.impl[functionName].apply(this.impl, arguments);
    }
}

// This is the public interface
var Interface = {
  funcA: null, // (a)
  funcB: null, // (a, b)
}

// The implementation of the interface
function Impl (x, y) {
    this.privX = x;
    this.privY = y;
}

Impl.prototype.funcA = function (a) {
    return this.privX + a;
}

Impl.prototype.funcB = function (a, b) {
    return this.privX - a + this.privY - b;
}

// How to provide the inteface to client
var impl = new Impl(5, 10);
var i = as(impl, Interface);

// And client uses it:
i.funcA(1);
i.funcB(7,5);
// inspecting private details
i.impl.privX;
i.impl.privY;

Beware, in some cases the fact that implementation is wrapped into a wrapper might affect the program. If we compare o1 === o2, it might happen that o1 is impl, and 02 is a wrapper, and we get false when true is expected. This is the kind of problem many Java programmers face sometimes (in Java ecosystem various decorators, AOP, proxies, etc. are often used).

I had no time to think further about this approach. For example, in the above code making i instanceof Interface returning true.

But in general I like the idea.

Saturday, 22 August 2009

Extending IntelliJ IDEA using Common Lisp

If you have nothing to do, you can embed ABCL into IntelliJ IDEA and extend and control IDEA interactively from SLIME.

I created a little plugin that starts ABCL inside of IDEA:


Note: both videos for better resolution are opened in new window at their Vimeo URLs

How it works: "ABCL Start" command creates ABCL compiler and attaches it's REPL to a special "ABCL Console" tool window. Also it executes start.lisp file, which is supposed to be edited by user. User may do whatever he needs: start SLIME server or load other lisp files.

SLIME fuzzy-completion works for Java classes/methods because I use jfli (I found an ABCL port by Andras Simon). I wish fuzzy-completion work faster with ABCL (the latency may be up to half a second). But I believe ABCL will be improved in this area in the near future.

Hello Word -like examples in the video above are contained in the fun.lisp file from the plugin distribution.

Another example I prepared is implementation of Emacs-like "yanking" in IDEA (Ctrl-Y pastes an item from clipboard, and Alt-Y replaces just inserted item by next clipboard item). The code is contained in the yanking.lisp. Here it is in action:


To summarize, it is possible, and with some practice quite convenient, to use ABCL for IDEA extension.

If you have in mind some useful extension that may be implemented in Lisp, you can use my code as a boilerplate.

The sources: http://github.com/avodonosov/abcl-idea/
Binary distro: http://github.com/avodonosov/abcl-idea/downloads

Some details are provided in the README file.

Friday, 5 December 2008

Hunchentoot on ECL

It was reported that all Hunchentoot dependencies are already ported to ECL and a patch exists for Hunchentoot itself to make it running on ECL.

I am glad to hear this, because ECL is the only free Common Lisp on Windows with multithreading support (at the same time an active work on multithreading for CLISP is in progress, Clozure CL has beta port for Windows, and a lot of work was also done for threading in SBCL Windows port, although the work is not finished).

I tried Hunchentoot on ECL and for those who are interested too, I reproduce here the steps I took. My environment: Windows XP + Microsoft Visual Studio.

  1. You need the latest versions of Hunchentoot and all the dependencies. If you are lazy, you may get all them at once from this archive I prepared.
  2. Apply the patch from this post (already done in my archive): save the port-ecl.lisp to the hunchentoot directory and add #+:ecl (:file "port-ecl") to hunchentoot.asd as here.
  3. Checkout fresh ECL from CVS:
      cvs -z3 -d:pserver:anonymous@ecls.cvs.sourceforge.net:/cvsroot/ecls checkout ecl
    
    I recommend fresh checkout because I had problems building ECL from sources already existing on my computer after update with cvs update -dAP.
  4. Configure ECL. Open ecl\msvc\Makefile and set
    ECL_THREADS  = 1
    ECL_UNICODE  = 1
    ECL_ASDF     = 1
    ECL_SOCKETS  = 1
    
  5. Build ECL. Run Visual Studio command prompt: menu Start->All Programs -> Microsoft Visual Studio 2005 -> Visual Studio Tools -> Visual Studio 2005 Command Prompt. This script sets up environment variables so that C compiler cl.exe, nmake.exe and other things are in your PATH, all includes and libraries are available, etc. In this console window type:
     > cd <DIR_WITH_ECL_SOURCES>\ecl\msvc
     > nmake
    
    After the build completes, copy resulting binaries somewhere:
      > nmake install prefix=c:\somewhere\ecl
    Now you have working ECL, lets test it:
    > c:\somewhere\ecl\ecl.exe
    ECL (Embeddable Common-Lisp) 0.9l (CVS 2008-07-12 18:54)
    Copyright (C) 1984 Taiichi Yuasa and Masami Hagiya
    Copyright (C) 1993 Giuseppe Attardi
    Copyright (C) 2000 Juan J. Garcia-Ripoll
    ECL is free software, and you are welcome to redistribute it
    under certain conditions; see file 'Copyright' for details.
    Type :h for Help.  Top level.
    > (+ 2 2)
    4
    > (quit)
    
    Works.

    Do not close the Visual Studio command prompt, we will need it soon.

  6. As Hunchentoot uses ASDF, we need Hunchentoot with all its dependencies to be registered in ASDF:*CENTRAL-REGISTY*. For this I have a special lisp file, setup-asdf-registry.lisp (already included in the archive from the step 1):
    ;; this file must be stored in the same directory
    ;; where all the lisp libraries are stored 
    ;; (but libraries, of course, have their own subdirectories)
    
    (require 'asdf)
    
    (flet
        ((reg (relative-lib-dir)
           (let ((lib-dir
                  (directory-namestring 
                   (merge-pathnames relative-lib-dir
                                    (load-time-value
                                     (or #.*compile-file-pathname* 
                                         *load-pathname*))))))
             (pushnew lib-dir
                      asdf:*central-registry*
                      :test #'equalp))))
      (reg "alexandria\\")
      (reg "asdf-binary-locations\\")
      (reg "babel_0.2.0\\")
      (reg "cffi-080926\\")
      (reg "chunga-0.4.3\\")
      (reg "cl-base64-3.3.2\\")
      (reg "cl-fad-0.6.2\\") 
      (reg "cl-ppcre-2.0.1\\")
      (reg "cl-who-0.11.0\\")
      (reg "cl+ssl\\")
      (reg "flexi-streams-1.0.7\\")
      (reg "hunchentoot-0.15.7\\")
      (reg "md5-1.8.5\\")
      (reg "rfc2388\\")
      (reg "trivial-features_0.1\\")
      (reg "trivial-gray-streams\\")
      (reg "url-rewrite-0.1.1\\"))
    
    (asdf:operate 'asdf:load-op :asdf-binary-locations) 
  7. Compile and load Hunchentoot. For this go back to the Visual Studio command prompt.
    > c:\somewhere\ecl\ecl.exe  
    > (load "c:/archive-from-the-step-1/setup-asdf-registry.lisp") 
    > (pushnew :hunchentoot-no-ssl *features*)
    > (asdf:operate 'asdf:load-op :hunchentoot-test)
    
    ECL must be started from Visual Studio command prompt because it uses C compiler when compiling Lisp. (pushnew :hunchentoot-no-ssl *features*) is only necessary if you do not have OpenSSL installed (get it here for Win32).
  8. Start Hunchentoot:
    > (hunchentoot:start-server :port 4242) 
    

Now you may open http://localhost:4242/hunchentoot/test and see Hunchentoot test site.

At the moment of this post writing, almost all examples from the test site work, except for two: "UTF-8 demo (writing UTF-8 characters directly to the stream)" and "UTF-8 demo (returning a string)". The reason is already found and I am sure it will be fixed very soon.

Thursday, 23 October 2008

Thursday, 26 June 2008

Fix Oracle Output

Customers recently sent us for analysis results of several queries from their Oracle database. The output produced by SQL*Plus or TOAD with the default value of the LINESIZE system variable is quite nasty. Similar to the following:

SQL> select * from users;

        ID
----------
NAME                                                                            
--------------------------------------------------------------------------------
SURNAME                                                                         
--------------------------------------------------------------------------------
LOGIN                                                                           
--------------------------------------------------------------------------------
PASSWORD                                                                        
--------------------------------------------------------------------------------
REGISTRATION_TIME            
-----------------------------
         1
ivan                                                                            
ivanov                                                                          
vanya                                                                           
123                                                                             
26.06.08 21:16:12,000000     
                                                                                
         2
petr                                                                            
petrov                                                                          
petya                                                                           

        ID
----------
NAME                                                                            
--------------------------------------------------------------------------------
SURNAME                                                                         
--------------------------------------------------------------------------------
LOGIN                                                                           
--------------------------------------------------------------------------------
PASSWORD                                                                        
--------------------------------------------------------------------------------
REGISTRATION_TIME            
-----------------------------
qwerty                                                                          
26.06.08 21:16:12,000000     
                                                                                
         3
sidor                                                                           
sidorov                                                                         
sid                                                                             
password                                                                        
26.06.08 21:16:12,000000     
                                                                                


3 rows selected.

SQL> spool off;

 
I created a little tool to convert such an output to a pretty thing like this:
ID         NAME                                                                             SURNAME                                                                          LOGIN                                                                            PASSWORD                                                                         REGISTRATION_TIME             
---------- -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- ----------------------------- 
         1 ivan                                                                             ivanov                                                                           vanya                                                                            123                                                                              26.06.08 21:16:12,000000      
         2 petr                                                                             petrov                                                                           petya                                                                            qwerty                                                                           26.06.08 21:16:12,000000      
         3 sidor                                                                            sidorov                                                                          sid                                                                              password                                                                         26.06.08 21:16:12,000000      

Lisp source is here. Usage instructions are at the top of the file.

Monday, 28 April 2008

Learning Flex

I'm learning Adobe Flex now. Surprisingly, I have much better impression than I expected about it (my expectations are derived from one or two evening experience with Flash).
I dislike ajax/comet etc, because IMHO web browsers are not suitable for desktop-like UI and it is high time for a new cross-platform client technology.
Flex Builder resembles old good Borland Delphi or MS VisualStudio. Flex libraries and tools have "open design" (my intuitive definition for this: if there is no standard TreeView class, we could write it ourselves because all base classes/interactions are understandably defined and the sources are available).
BTW, interesting facts about the language. Adobe claims that ActionScript is based on ECMAScript Edition 4. The ECMAScript 4 language is in some sense progressive, has interesting (in particular lisp-like) features: http://www.ecmascript.org/es4/spec/overview.pdf. Also funny is that the reference implementation of ECMAScript is created on Standard ML.

Thursday, 6 March 2008

Junit 4.4 new assertion mechanism is a very clear demonstration of java limitation comparing to lisp.

It is obvious that ASSERT A > 2 OR B < 3 when failed should print a message "assertion failed: a > 2 or b < 3. This is trivial to implement with lisp macros, but when the language does not provide programmatic access to the program source code, our ASSERT receives only result of expression calculation - a boolean.

Therefore people must create new expression specification language and duplicate expression evaluation that is already present in java compiler.

Tuesday, 1 January 2008

Web development in single threaded lisp with slime and hunchentoot

It is problematic at first sight to use SLIME for web development in a single threaded lisp with swank:*communication-style* = nil (as it happens with CLISP and SBCL on Windows). The problem is the impossibility to modify application while it is running.

In very informal pseudo code this configuration of swank may be simplified represented as:

(loop
   (read-slime-request)
   (exec-slime-request))
When our next SLIME request starts hunchentoot, hunchentoot enters its own request handling loop (for HTTP requests):
(loop
   (read-slime-request)
   (exec-slime-request :
      (loop
         (read-http-request)
         (handle-http-request)))
This means exec-slime-request will not exit and subsequent slime requests will not be handled until the web server is stopped. SLIME isn't functioning.

The solution is to use web server to invoke SLIME. When in the emacs we are going to talk to swank, send special HTTP request and its handler will enforce swank to handle emacs request.

(loop
   (read-slime-request)
   (exec-slime-request :
      (loop
         (read-http-request)
         (handle-http-request :
            (read-slime-request)
            (exec-slime-request))))
It requires only few lines of code in emacs lisp and in common lisp. The code is here, it's simple, see it for more details.

One note is needed to help fully understand the code. When emacs side sends a request to swank, swank is not always blocked by hunchentoot. As far as I understand it, one slime request may lead to a sequence of communications between swank and emacs.

01: (loop
02:   (read-slime-request)
03:   (exec-slime-request :
04:      (loop
05:         (read-http-request)
06:         (handle-http-request :
07:            (read-slime-request)
08:            (exec-slime-request :
09:               ...
10:               (read-slime-request)
11:               ...))))
In the emacs we should not make an HTTP request before sending the slime request that is to be read on the line 10.

To distinguish this situation on the emacs side I check whether the socket of HTTP request read by swank on the line 05 is still connected. In this case new HTTP request isn't performed.

This solution isn't 100% correct (the socket may be still not closed, but swank isn't going to read anything). But it works almost good. Sometimes a glitch appears - when you execute an expression, you get result of previous expression instead of the just executed. If you execute expressions en, en+1, en+2, you get results of executing expressions en-1, en, en+1. This "phase displacement" is most likely caused by the fact that we not performed HTTP request when it was necessary and the slime request remained not read by swank. When next time we do HTTP request for the next slime request, the previous unread request is read and executed by swank.

In principle, I know how to fix this problem (we may annotate HTTP requests and slime requests with some IDs and do synchronization - ignore unnecessary HTTP requests - on the common lisp side; socket checking on the emacs side will became unnecessary). But I do not want to spend time on it, it is easier to enforce reading enqueued expressions by the "special HTTP request" directly from browser: fetch http://localhost:4343/slime-http-send-handler needed count of times.

How to use it.

Very easy.

Add the following line into your .emacs file:

(load "c:/some/path/slime-over-http.el")
The line should follow the normal slime setup.

Here is an example of common lisp web site:

;; this line may vary in you case; just make
;; sure that asdf is loaded and all the needed
;; libraries are available
(load "c:/usr/unpacked/lisp-libs/setup-asdf-registry.lisp")

(asdf:operate 'asdf:load-op :hunchentoot)
(use-package :hunchentoot)

(defun want-slime-over-http-p ()
  "This predicate indicates whether we
are in the development environment and have no
threading"
  #+:win32 t)

(when (want-slime-over-http-p)
  (load "C:/usr/projects/slime-over-http/slime-over-http.lisp")
  (defun hunchentoot::keep-alive-p ()))

(setq *dispatch-table*
      `(dispatch-easy-handlers
        ,@(when (want-slime-over-http-p)
                (list (create-prefix-dispatcher
                           "/slime-http-send-handler"
                           (find-symbol "SLIME-HTTP-SEND-HANDLER"
                                        "SLIME-HTTP"))))
        default-dispatcher))

(define-easy-handler (hello :uri "/hello")
    ()
  "Hello, change me!")
After executing the above code run emacs command slime-start-hunchentoot. Now you may open http://localhost:4343/hello in the browser and see the phrase "Hello, change me!".

You may change it because you have fully functioning slime with repl, debugger, etc.

To stop the server execute common lisp function (slime-http::stop-instance).

Note how we redefined hunchentoot::keep-alive-p to return nil. Single threaded server can't provide keep-alive, because while it is handling alive connection it can't accept any other connections (in particular from slime; also, IE is often unable to fully open a page with several images or frames in this case; looks like it opens several connections and schedules downloading different parts of the page to different connections; connections other than "kept alive" are not handled by hunchentoot).

In addition to redefining keep-alive-p I also use reverse proxying apache for sure.

Resume

So we see quite simple way (despite the long explanation) to do web development with hunchentoot in free lisps available on windows.

It isn't the only way to develop web applications with common lisp if you are on windows. For example you may install linux virtual machine. Edi Weitz - the hunchentoot author - uses this approach.

PS

My code is called "slime-over-http" because initially I delivered slime requests over the TCP connection used by the HTTP request and only some time later I realized that it is unnecessary. I'd like another name (preferably short), but can't contrive it.

If you have any questions about this configuration - ask, I'll be glad to help.

Wednesday, 12 December 2007

ECL - multi threaded, free lisp, supporting Windows

Until yesterday I thought that there are no free multithreaded Common Lisp implementations for Windows.

But it turned out that ECL supports multithreading.

Yesterday I downloaded, build it and tried simple test: (mp:process-run-function 'zu (lambda () (loop (format t "zu~%") (sleep 3))))

Seems working.

Also ECL may be build with Unicode support. Although it doesn't support external formats in streams, strings and characters are represented as 24-bit values. It should be sufficient to have support for various external formats in IO using flexi-streams.

Good news for me. I pin great hopes on ECL.

P.S.

I discovered ECL via Common Lisp Survey linked from the xach's post. Thanks ... all.

Blog Archive