GithubHelp home page GithubHelp logo

cl-http2-protocol's Introduction

CL-HTTP2-PROTOCOL

What This Is

This is HTTP/2 draft-14 (a.k.a. "h2-14") interopability test code written in Common Lisp. It has only been tested against SBCL 1.1.8.0 to 1.1.14 on Ubuntu Linux on x86, but it should be possible to make it work on other Common Lisp implementations, subject to some editing in util.lisp and possibly example.lisp.

The code offers a pure Common Lisp transport agnostic implementation of the HTTP/2 protocol at draft-14. An example client and server are included for a "Hello, World" style test, which employ TLS using CL+SSL and OpenSSL.

Networking examples have been based on the CL-ASYNC library. Some alterations to functions in CL+SSL and CL-ASYNC were necessary and are included in the source tree. (Prior versions of this library were based on homemade event loops based on USOCKET and SB-BSD-SOCKETS but this has now been removed.)

The current implementation is based on:

Support for:

Copyright

  • Copyright (c) 2014 Akamai Technologies, Inc. Published under MIT License.

  • Contains design and text from http-2 which contains this notice: "(MIT License) - Copyright (c) 2013 Ilya Grigorik"

  • Contains code from CL+SSL which contains this notice: "This library is a fork of SSL-CMUCL. The original SSL-CMUCL source code was written by Eric Marsden and includes contributions by Jochen Schmidt. Development into CL+SSL was done by David Lichteblau. License: MIT-style."

  • Contains code from CL-ASYNC which contains this notice: "As always, my code is MIT licenced. Do whatever the hell you want with it. Enjoy!"

Notes on Port

This code began life as a port from the draft-06 Ruby interopability code written by Ilya Grigorik released under MIT license. This code has since diverged to support subsequent working group drafts (up to draft-14 currently), to allow queueing and multiplexing support with a prioritization algorithm, and to move away from some Ruby idioms towards Lisp idioms over time.

The following are notes only of interest to folks who followed along since the beginning or have some interest in comparing the code revisions. Others may skip this section.

  • util.lisp defines several general purpose forms that give us some capabilities similar to calls in Ruby, as well as convenience calls that are stylistic Lisp choices.

  • buffer.lisp is much longer than buffer.rb in order to allow us to build up various primitives that Ruby offers in the String class for free.

  • ssl.lisp redefines some items in the CL+SSL package (based on the cl+ssl-20140316-git version) and tcp-ssl.lisp overrides some items in the CL-ASYNC-SSL package (based on the cl-async-20140616-git version), mostly to add support for NPN, SNI, and DH parameters, all of which are neecessary to establish the TLS connection as required by HTTP/2. See the comments in those files for more information.

  • tcp.lisp redefines some items in the CL-ASYNC package (based on the cl-async-20140616-git version) mostly to fix a couple issues encountered in corner cases.

  • The code in the example folder is contained in example.lisp in the form of functions. The examples in Lisp fire up CL-ASYNC event loops.

  • The Ruby code uses arrays and hashes which in the CL code are variously ported as alists, plists, and hashes depending on the specifics of access required.

  • The error conditions are largely the same, but are prefixed with HTTP2- and an additional one is added named HTTP2-NOT-STARTED as it is very convenient in debugging (when NPN fails, etc). In addition, the Lisp code defines various restarts in the normal manner, so that during debugging certain failures do not require the instant transaction to be aborted entirely.

  • Classes are matched one-to-one but module/package organization is different. Use the HTTP2 package for most functions and the HTTP2-EXAMPLE package for the examples.

This Common Lisp code was produced by Martin Flack, a Principal Architect on the Foundry team in the Web Experience business at Akamai.

Our team's mission is innovative applied R&D, and accordingly we explore new technologies close to the mission of excellent web experience. Note that this code is intended to be used against the other HTTP/2 interopability client/server code linked above, and not necessarily any part of the Akamai network; nor is any feature herein indicative of any planned or actual feature of Akamai products or services.

This system was named CL-HTTP2-PROTOCOL to avoid misunderstanding that it was related to CL-HTTP, a Common Lisp web server. Apologies for the redundant word.

Server Setup and Example

To run this on a fresh Ubuntu Linux 13.10 or 14.04 server, follow these instructions. A non-root user of "ubuntu" with sudo access is assumed.

sudo apt-get update && sudo apt-get dist-upgrade -y && sudo reboot
# ...if prompted about grub choose "install package maintainer's version"
#
# ...login again
sudo apt-get install -y git sbcl
git clone https://github.com/akamai/cl-http2-protocol.git
wget http://beta.quicklisp.org/quicklisp.lisp
sbcl --script <<EOF
(load "quicklisp.lisp")
(quicklisp-quickstart:install)
(ql:quickload :swank)
(ql:quickload :alexandria)
(ql:quickload :anaphora)
(ql:quickload :babel)
(ql:quickload :puri)
(ql:quickload :usocket)
(ql:quickload :cl+ssl)
(ql:quickload :cl-async)
(ql:quickload :cl-async-ssl)
EOF
#
# ...optionally run screen if you're familiar with it and you want to
# quickly test server/client on one system:
screen
#
# ...start the server:
sbcl --script <<EOF
(load "quicklisp/setup.lisp")
(load "cl-http2-protocol/cl-http2-protocol.asd")
(require :cl-http2-protocol)
(in-package :http2-example)
(example-server)
EOF
#
# ...now you have an HTTP/2 server, secured with TLS, on port 8080
# (note the server is ONLY HTTP/2; there is no HTTP/1.1 fallback)
# (note that port 8080 is chosen only to allow a non-root user to
# run the Lisp image and launch the server)
#
# ...to stop the server, CTRL+C followed by ABORT at the handler
#
# ...if you prefer to have less output and keep exceptions from
# stopping the program, break the server, set these globals and rerun
# the server:
(setf *verbose-mode* nil)
(setf *debug-mode* nil)
(setf *dump-bytes* nil)
(example-server)
#
# ...if you ran screen, press CTRL-A CTRL-C, else open a new terminal
# to the server in order to run the example client
#
# ...run a client:
sbcl --script <<EOF
(load "quicklisp/setup.lisp")
(load "cl-http2-protocol/cl-http2-protocol.asd")
(require :cl-http2-protocol)
(in-package :http2-example)
(example-client "https://localhost:8080/")
EOF

Please note that the files mykey.pem, mycert.pem, and dhparams.2048.pem are used for the example server, so it is recommended that you regenerate them. You will then remove security warnings from real browsers. Please note that EXAMPLE-CLIENT does absolutely no sensible checking of the TLS certificate, etc.

openssl genrsa -out mykey.pem 2048
openssl req -new -key mykey.pem -out mycert.csr
# CN should be set to the hostname you will use to reach the server
openssl req -noout -text -in mycert.csr
openssl x509 -req -days 365 -in mycert.csr -signkey mykey.pem -out mycert.pem
openssl dhparam -outform pem -out dhparams.2048.pem 2048

At the time of writing (Sep 10, 2014), Firefox Nightly and Chrome Canary will be compatible with your HTTP/2 server started above as well. The address bar in Firefox Nightly should be https://HOST:8080/ where HOST is the hostname used to reach your server.

Getting Started

(load "cl-http2-protocol/cl-http2-protocol.asd")
(require :cl-http2-protocol)

(defvar socket #|  provide a transport...  |#)

(defvar conn (make-instance 'client))
(on conn :frame (lambda (bytes) #|  send the bytes...  |#))

(loop for bytes = #|  read some bytes...  |#
      if bytes (connection<< conn bytes) else (return))

Check out EXAMPLE-CLIENT and EXAMPLE-SERVER in HTTP2-EXAMPLE for basic examples that perform complete HTTP request/responses. These functions use CL+SSL for secure connections, or USOCKET / SB-BSD-SOCKETS for plain connections.

Connection lifecycle management

Depending on the role of the endpoint you must initialize either a CLIENT or a SERVER object. Doing so picks the appropriate header compression / decompression algorithms and stream management logic. From there, you can subscribe to connection level events, or invoke appropriate APIs to allocate new streams and manage the lifecycle. For example:

;;; server

(in-package :http2-example)

(defvar server (make-instance 'server))

(on server :stream (lambda (stream) ...))  ; process inbound stream
(on server :frame (lambda (bytes) ...))  ; encoded HTTP/2 frames

(ping server (lambda (payload) ...))  ; send ping, process pong

(goaway server)  ; send goaway frame to the client

;;; client

(in-package :http2-example)

(defvar client (make-instance 'client))

(on client :promise (lambda (stream) ...))  ; process push promise

(defparameter stream (new-stream client))  ; allocate new stream
(headers stream '((":method" . "post")) :end-stream: nil)
(data stream (buffer-simple "Hello") :end-stream t)

Events emitted by the CONNECTION object:

:promise client role only, fires once for each new push promise
:stream server role only, fires once for each new client stream
:frame fires once for every encoded HTTP/2 frame that needs to be sent to the peer

Stream lifecycle management

A single HTTP/2 connection can multiplex multiple streams in parallel: multiple requests and responses can be in flight simultaneously and stream data can be interleaved and prioritized. Further, the specification provides a well-defined lifecycle for each stream (see below).

The good news is, all of the stream management, and state transitions, and error checking is handled by the library. All you have to do is subscribe to appropriate events (marked with ":" prefix in diagram below) and provide your application logic to handle request and response processing.

                         +--------+
                    PP   |        |   PP
                ,--------|  idle  |--------.
               /         |        |         \
              v          +--------+          v
       +----------+          |           +----------+
       |          |          | H         |          |
   ,---|:reserved |          |           |:reserved |---.
   |   | (local)  |          v           | (remote) |   |
   |   +----------+      +--------+      +----------+   |
   |      | :active      |        |      :active |      |
   |      |      ,-------|:active |-------.      |      |
   |      | H   /   ES   |        |   ES   \   H |      |
   |      v    v         +--------+         v    v      |
   |   +-----------+          |          +-_---------+  |
   |   |:half_close|          |          |:half_close|  |
   |   |  (remote) |          |          |  (local)  |  |
   |   +-----------+          |          +-----------+  |
   |        |                 v                |        |
   |        |    ES/R    +--------+    ES/R    |        |
   |        `----------->|        |<-----------'        |
   | R                   | :close |                   R |
   `-------------------->|        |<--------------------'
                         +--------+

For sake of example, let's take a look at a simple server implementation:

(defvar conn (make-instance 'server))

; emits new streams opened by the client
(on conn :stream
  (lambda (stream)
    (on stream :active
      (lambda () ))  ; fires when stream transitions to open
    (on stream :close
      (lambda (err) ))  ; stream is closed by client and server

    (on stream :headers
      (lambda (head) ))  ; header callback
    (on stream :data
      (lambda (chunk) ))  ; body payload callback
	  
    (on stream :half-close
	  (lambda ()
	    ; ... generate response ...
		; send response
		(headers stream '((":status" . "200")
		                  ("content-type" . "text/plain")))
						  
        ; split response between multiple DATA frames
		(let ((chunk1 (buffer-simple "stuff "))
		      (chunk2 (buffer-simple "more stuff")))
		  (data stream chunk1 :end-stream nil)
		  (data stream chunk2))))))

Events emitted by the STREAM object:

:reserved fires exactly once when a push stream is initialized
:active fires exactly once when the stream become active and is counted towards the open stream limit
:headers fires once for each received header block (multi-frame blocks are reassembled before emitting this event)
:data fires once for every DATA frame (no buffering)
:half_close fires exactly once when the opposing peer closes its end of connection (e.g. client indicating that request is finished, or server indicating that response is finished)
:close fires exactly once when both peers close the stream, or if the stream is reset
:priority fires once for each received priority update (server only)

Flow control

Multiplexing multiple streams over the same TCP connection introduces contention for shared bandwidth resources. Stream priorities can help determine the relative order of delivery, but priorities alone are insufficient to control how the resource allocation is performed between multiple streams. To address this, HTTP/2 provides a simple mechanism for stream and connection flow control.

Connection and stream flow control is handled by the library: all streams are initialized with the default window size (64KB), and send/receive window updates are automatically processed - i.e. window is decremented on outgoing data transfers, and incremented on receipt of window frames. Similarly, if the window is exceeded, then data frames are automatically buffered until window is updated.

The only thing left is for your application to specify the logic as to when to emit window updates:

(buffered-amount conn)      ; check amount of buffered data
(conn-window conn)          ; check current window size
(window-update conn 1024)   ; increment connection window by 1024 bytes

(buffered-amount stream)    ; check amount of buffered data
(stream-window stream)      ; check current window size
(window-update stream 2048) ; increment stream window by 2048 bytes

Server push

An HTTP/2 server can send multiple replies to a single client request. To do so, first it emits a "push promise" frame which contains the headers of the promised resource, followed by the response to the original request, as well as promised resource payloads (which may be interleaved). A simple example is in order:

(defvar conn (make-instance 'server))

(on conn :stream
  (lambda (stream)
    (on stream :headers
	  (lambda (head) ... ))
    (on stream :data
	  (lambda (chunk) ... ))

    ; fires when client terminates its request (i.e. request finished)
    (on stream :half-close
	  (lambda ()
	    (let ((head '((":status" . "200")
		              (":path" . "/other_resource")
					  ("content-type" . "text/plain")))
              promise)
          (promise stream head
		    (lambda (push)
			  (headers push ...)
			  (setf promise push))))
			  
        (headers stream '((":status" . "200")
		                  ("content-type" . "text/plain")))
        (data stream response-chunk :end-stream nil)
		(data promise payload)
		(data stream last-chunk))))))

When a new push promise stream is sent by the server, the client is notified via the :promise event:

(defvar conn (make-instance 'client))
(on conn :promise
  (lambda (push)
    ; process push stream
	))

The client can cancel any given push stream (via STREAM-CLOSE), or disable server push entirely by sending the appropriate settings frame (note that below setting only impacts server > client direction):

(settings client :streams 0)  ; setting max limit to 0 disables server push

cl-http2-protocol's People

Contributors

martinflack avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cl-http2-protocol's Issues

name-conflicts in cl-http2-protocol-example between cl+ssl:+ssl-op-no-tlsv1-1+ and cl-async-ssl:+ssl-op-no-tlsv1-1+

Hi,

The problem described in this issue occurs with SBCL 1.5.4 using the versions of the dependencies that were installed by Quicklisp at the time of this writing on both platforms I tried it on:

  • FreeBSD 12.0-RELEASE-p7
  • macOS Mojave 10.14.5

I performed the setup and execution as per the instructions in the readme:

git clone https://github.com/akamai/cl-http2-protocol.git
wget http://beta.quicklisp.org/quicklisp.lisp
cat > deps.lisp <<EOF
(load "quicklisp.lisp")
(quicklisp-quickstart:install)
(ql:quickload :swank)
(ql:quickload :alexandria)
(ql:quickload :anaphora)
(ql:quickload :babel)
(ql:quickload :puri)
(ql:quickload :usocket)
(ql:quickload :cl+ssl)
(ql:quickload :cl-async)
(ql:quickload :cl-async-ssl)
EOF
sbcl --script deps.lisp
cat > server.lisp <<EOF
(load "quicklisp/setup.lisp")
(load "cl-http2-protocol/cl-http2-protocol.asd")
(require :cl-http2-protocol)
(in-package :http2-example)
(example-server)
EOF
sbcl --script server.lisp

Results in an error condition during compilation due to a naming conflict, and sbcl terminates:

[...]
; compiling (DEFPACKAGE :CL-HTTP2-PROTOCOL-EXAMPLE ...)ASDF could not load cl-http2-protocol because
USE-PACKAGE #<PACKAGE "CL+SSL"> causes name-conflicts in
#<PACKAGE "CL-HTTP2-PROTOCOL-EXAMPLE"> between the following symbols:
  CL+SSL:+SSL-OP-NO-TLSV1-1+, CL-ASYNC-SSL:+SSL-OP-NO-TLSV1-1+
See also:
  The ANSI Standard, Section 11.1.1.2.5.
Unhandled NAME-CONFLICT in thread #<SB-THREAD:THREAD "main thread" RUNNING
                                     {10005084F3}>:
  USE-PACKAGE #<PACKAGE "CL+SSL"> causes name-conflicts in
  #<PACKAGE "CL-HTTP2-PROTOCOL-EXAMPLE"> between the following symbols:
    CL+SSL:+SSL-OP-NO-TLSV1-1+, CL-ASYNC-SSL:+SSL-OP-NO-TLSV1-1+
See also:
  The ANSI Standard, Section 11.1.1.2.5

Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {10005084F3}>
0: (SB-DEBUG::DEBUGGER-DISABLED-HOOK #<NAME-CONFLICT {1001B35783}> #<unused argument> :QUIT T)
1: (SB-DEBUG::RUN-HOOK *INVOKE-DEBUGGER-HOOK* #<NAME-CONFLICT {1001B35783}>)
2: (INVOKE-DEBUGGER #<NAME-CONFLICT {1001B35783}>)
3: (ERROR #<NAME-CONFLICT {1001B35783}>)
4: (SB-KERNEL:WITH-SIMPLE-CONDITION-RESTARTS ERROR NIL NAME-CONFLICT :PACKAGE #<PACKAGE "CL-HTTP2-PROTOCOL-EXAMPLE"> :SYMBOLS (CL+SSL:+SSL-OP-NO-TLSV1-1+ CL-ASYNC-SSL:+SSL-OP-NO-TLSV1-1+) :FUNCTION USE-PACKAGE :DATUM #<PACKAGE "CL+SSL">)
5: (NAME-CONFLICT #<PACKAGE "CL-HTTP2-PROTOCOL-EXAMPLE"> USE-PACKAGE #<PACKAGE "CL+SSL"> CL+SSL:+SSL-OP-NO-TLSV1-1+ CL-ASYNC-SSL:+SSL-OP-NO-TLSV1-1+)
6: ((FLET SB-THREAD::WITH-RECURSIVE-LOCK-THUNK :IN USE-PACKAGE))
7: ((FLET "WITHOUT-INTERRUPTS-BODY-29" :IN SB-THREAD::CALL-WITH-RECURSIVE-LOCK))
8: (SB-THREAD::CALL-WITH-RECURSIVE-LOCK #<CLOSURE (FLET SB-THREAD::WITH-RECURSIVE-LOCK-THUNK :IN USE-PACKAGE) {800FFCBDB}> #<SB-THREAD:MUTEX "Package Graph Lock" owner: #<SB-THREAD:THREAD "main thread" RUNNING {10005084F3}>> T NIL)
9: (USE-PACKAGE (#<PACKAGE "COMMON-LISP"> #<PACKAGE "ALEXANDRIA.1.0.0"> #<PACKAGE "ANAPHORA"> #<PACKAGE "BABEL"> #<PACKAGE "CL-ASYNC"> #<PACKAGE "CL-ASYNC-SSL"> #<PACKAGE "PURI"> #<PACKAGE "CL+SSL"> #<PACKAGE "CL-HTTP2-PROTOCOL-UTIL"> #<PACKAGE "CL-HTTP2-PROTOCOL">) #<PACKAGE "CL-HTTP2-PROTOCOL-EXAMPLE">)
10: (SB-IMPL::UPDATE-PACKAGE #<PACKAGE "CL-HTTP2-PROTOCOL-EXAMPLE"> ("HTTP2-EXAMPLE") #S(SB-C:DEFINITION-SOURCE-LOCATION :NAMESTRING "/usr/home/erikn/cl-http2-protocol/packages.lisp" :INDICES 131073) NIL NIL (#<PACKAGE "COMMON-LISP"> #<PACKAGE "ALEXANDRIA.1.0.0"> #<PACKAGE "ANAPHORA"> #<PACKAGE "BABEL"> #<PACKAGE "CL-ASYNC"> #<PACKAGE "CL-ASYNC-SSL"> #<PACKAGE "PURI"> #<PACKAGE "CL+SSL"> #<PACKAGE "CL-HTTP2-PROTOCOL-UTIL"> #<PACKAGE "CL-HTTP2-PROTOCOL">) NIL NIL ("EXAMPLE-CLIENT" "EXAMPLE-SERVER" "*DUMP-BYTES*" "*DUMP-BYTES-STREAM*" "*DUMP-BYTES-HOOK*") ("CL-HTTP2-PROTOCOL-EXAMPLE") NIL NIL "HTTP/2 draft-14/hpack-09 (h2-14) simple CL-ASYNC example client/server.")
11: ((FLET "WITHOUT-INTERRUPTS-BODY-29" :IN SB-THREAD::CALL-WITH-RECURSIVE-LOCK))
12: (SB-THREAD::CALL-WITH-RECURSIVE-LOCK #<CLOSURE (FLET SB-THREAD::WITH-RECURSIVE-LOCK-THUNK :IN SB-IMPL::%DEFPACKAGE) {800FFCE9B}> #<SB-THREAD:MUTEX "Package Graph Lock" owner: #<SB-THREAD:THREAD "main thread" RUNNING {10005084F3}>> T NIL)
13: (SB-IMPL::%DEFPACKAGE "CL-HTTP2-PROTOCOL-EXAMPLE" ("HTTP2-EXAMPLE") NIL NIL NIL ("CL" "ALEXANDRIA" "ANAPHORA" "BABEL" "CL-ASYNC" "CL-ASYNC-SSL" "PURI" "CL+SSL" "HTTP2-UTIL" "HTTP2") NIL NIL ("EXAMPLE-CLIENT" "EXAMPLE-SERVER" "*DUMP-BYTES*" "*DUMP-BYTES-STREAM*" "*DUMP-BYTES-HOOK*") ("CL-HTTP2-PROTOCOL-EXAMPLE") NIL NIL #S(SB-C:DEFINITION-SOURCE-LOCATION :NAMESTRING "/usr/home/erikn/cl-http2-protocol/packages.lisp" :INDICES 131073) "HTTP/2 draft-14/hpack-09 (h2-14) simple CL-ASYNC example client/server.")
14: (SB-INT:SIMPLE-EVAL-IN-LEXENV (SB-IMPL::%DEFPACKAGE #1="CL-HTTP2-PROTOCOL-EXAMPLE" (QUOTE ("HTTP2-EXAMPLE")) (QUOTE NIL) (QUOTE NIL) (QUOTE NIL) (QUOTE ("CL" "ALEXANDRIA" "ANAPHORA" "BABEL" "CL-ASYNC" "CL-ASYNC-SSL" "PURI" "CL+SSL" "HTTP2-UTIL" "HTTP2")) (QUOTE NIL) (QUOTE NIL) (QUOTE ("EXAMPLE-CLIENT" "EXAMPLE-SERVER" "*DUMP-BYTES*" "*DUMP-BYTES-STREAM*" "*DUMP-BYTES-HOOK*")) (QUOTE (#1#)) (QUOTE NIL) ...) #<NULL-LEXENV>)
15: (SB-INT:SIMPLE-EVAL-IN-LEXENV (PROGN (SB-IMPL::%DEFPACKAGE #1="CL-HTTP2-PROTOCOL-EXAMPLE" (QUOTE ("HTTP2-EXAMPLE")) (QUOTE NIL) (QUOTE NIL) (QUOTE NIL) (QUOTE ("CL" "ALEXANDRIA" "ANAPHORA" "BABEL" "CL-ASYNC" "CL-ASYNC-SSL" "PURI" "CL+SSL" "HTTP2-UTIL" "HTTP2")) (QUOTE NIL) (QUOTE NIL) (QUOTE ("EXAMPLE-CLIENT" "EXAMPLE-SERVER" "*DUMP-BYTES*" "*DUMP-BYTES-STREAM*" "*DUMP-BYTES-HOOK*")) (QUOTE (#1#)) (QUOTE NIL) ...)) #<NULL-LEXENV>)
16: (EVAL-TLF (PROGN (SB-IMPL::%DEFPACKAGE #1="CL-HTTP2-PROTOCOL-EXAMPLE" (QUOTE ("HTTP2-EXAMPLE")) (QUOTE NIL) (QUOTE NIL) (QUOTE NIL) (QUOTE ("CL" "ALEXANDRIA" "ANAPHORA" "BABEL" "CL-ASYNC" "CL-ASYNC-SSL" "PURI" "CL+SSL" "HTTP2-UTIL" "HTTP2")) (QUOTE NIL) (QUOTE NIL) (QUOTE ("EXAMPLE-CLIENT" "EXAMPLE-SERVER" "*DUMP-BYTES*" "*DUMP-BYTES-STREAM*" "*DUMP-BYTES-HOOK*")) (QUOTE (#1#)) (QUOTE NIL) ...)) 3 #<NULL-LEXENV>)
17: ((FLET SB-C::FROB :IN SB-C::EVAL-COMPILE-TOPLEVEL))
18: (SB-C::EVAL-COMPILE-TOPLEVEL ((SB-IMPL::%DEFPACKAGE #1="CL-HTTP2-PROTOCOL-EXAMPLE" (QUOTE ("HTTP2-EXAMPLE")) (QUOTE NIL) (QUOTE NIL) (QUOTE NIL) (QUOTE ("CL" "ALEXANDRIA" "ANAPHORA" "BABEL" "CL-ASYNC" "CL-ASYNC-SSL" "PURI" "CL+SSL" "HTTP2-UTIL" "HTTP2")) (QUOTE NIL) (QUOTE NIL) (QUOTE ("EXAMPLE-CLIENT" "EXAMPLE-SERVER" "*DUMP-BYTES*" "*DUMP-BYTES-STREAM*" "*DUMP-BYTES-HOOK*")) (QUOTE (#1#)) (QUOTE NIL) ...)) (#1=(SB-IMPL::%DEFPACKAGE #2="CL-HTTP2-PROTOCOL-EXAMPLE" (QUOTE ("HTTP2-EXAMPLE")) (QUOTE NIL) (QUOTE NIL) (QUOTE NIL) (QUOTE ("CL" "ALEXANDRIA" "ANAPHORA" "BABEL" "CL-ASYNC" "CL-ASYNC-SSL" "PURI" "CL+SSL" "HTTP2-UTIL" "HTTP2")) (QUOTE NIL) (QUOTE NIL) (QUOTE ("EXAMPLE-CLIENT" "EXAMPLE-SERVER" "*DUMP-BYTES*" "*DUMP-BYTES-STREAM*" "*DUMP-BYTES-HOOK*")) (QUOTE (#2#)) (QUOTE NIL) ...) (EVAL-WHEN (:COMPILE-TOPLEVEL :LOAD-TOPLEVEL :EXECUTE) #1#) SB-C::ORIGINAL-SOURCE-START 0 3))
19: (SB-C::PROCESS-TOPLEVEL-FORM (SB-IMPL::%DEFPACKAGE #1="CL-HTTP2-PROTOCOL-EXAMPLE" (QUOTE ("HTTP2-EXAMPLE")) (QUOTE NIL) (QUOTE NIL) (QUOTE NIL) (QUOTE ("CL" "ALEXANDRIA" "ANAPHORA" "BABEL" "CL-ASYNC" "CL-ASYNC-SSL" "PURI" "CL+SSL" "HTTP2-UTIL" "HTTP2")) (QUOTE NIL) (QUOTE NIL) (QUOTE ("EXAMPLE-CLIENT" "EXAMPLE-SERVER" "*DUMP-BYTES*" "*DUMP-BYTES-STREAM*" "*DUMP-BYTES-HOOK*")) (QUOTE (#1#)) (QUOTE NIL) ...) ((EVAL-WHEN (:COMPILE-TOPLEVEL :LOAD-TOPLEVEL :EXECUTE) (SB-IMPL::%DEFPACKAGE #1="CL-HTTP2-PROTOCOL-EXAMPLE" (QUOTE ("HTTP2-EXAMPLE")) (QUOTE NIL) (QUOTE NIL) (QUOTE NIL) (QUOTE ("CL" "ALEXANDRIA" "ANAPHORA" "BABEL" "CL-ASYNC" "CL-ASYNC-SSL" "PURI" "CL+SSL" "HTTP2-UTIL" "HTTP2")) (QUOTE NIL) (QUOTE NIL) (QUOTE ("EXAMPLE-CLIENT" "EXAMPLE-SERVER" "*DUMP-BYTES*" "*DUMP-BYTES-STREAM*" "*DUMP-BYTES-HOOK*")) (QUOTE (#1#)) (QUOTE NIL) ...)) SB-C::ORIGINAL-SOURCE-START 0 3) (:COMPILE-TOPLEVEL))
20: (SB-C::PROCESS-TOPLEVEL-PROGN ((SB-IMPL::%DEFPACKAGE #1="CL-HTTP2-PROTOCOL-EXAMPLE" (QUOTE ("HTTP2-EXAMPLE")) (QUOTE NIL) (QUOTE NIL) (QUOTE NIL) (QUOTE ("CL" "ALEXANDRIA" "ANAPHORA" "BABEL" "CL-ASYNC" "CL-ASYNC-SSL" "PURI" "CL+SSL" "HTTP2-UTIL" "HTTP2")) (QUOTE NIL) (QUOTE NIL) (QUOTE ("EXAMPLE-CLIENT" "EXAMPLE-SERVER" "*DUMP-BYTES*" "*DUMP-BYTES-STREAM*" "*DUMP-BYTES-HOOK*")) (QUOTE (#1#)) (QUOTE NIL) ...)) ((EVAL-WHEN (:COMPILE-TOPLEVEL :LOAD-TOPLEVEL :EXECUTE) (SB-IMPL::%DEFPACKAGE #1="CL-HTTP2-PROTOCOL-EXAMPLE" (QUOTE ("HTTP2-EXAMPLE")) (QUOTE NIL) (QUOTE NIL) (QUOTE NIL) (QUOTE ("CL" "ALEXANDRIA" "ANAPHORA" "BABEL" "CL-ASYNC" "CL-ASYNC-SSL" "PURI" "CL+SSL" "HTTP2-UTIL" "HTTP2")) (QUOTE NIL) (QUOTE NIL) (QUOTE ("EXAMPLE-CLIENT" "EXAMPLE-SERVER" "*DUMP-BYTES*" "*DUMP-BYTES-STREAM*" "*DUMP-BYTES-HOOK*")) (QUOTE (#1#)) (QUOTE NIL) ...)) SB-C::ORIGINAL-SOURCE-START 0 3) (:COMPILE-TOPLEVEL))
21: (SB-C::PROCESS-TOPLEVEL-FORM (EVAL-WHEN (:COMPILE-TOPLEVEL :LOAD-TOPLEVEL :EXECUTE) (SB-IMPL::%DEFPACKAGE #1="CL-HTTP2-PROTOCOL-EXAMPLE" (QUOTE ("HTTP2-EXAMPLE")) (QUOTE NIL) (QUOTE NIL) (QUOTE NIL) (QUOTE ("CL" "ALEXANDRIA" "ANAPHORA" "BABEL" "CL-ASYNC" "CL-ASYNC-SSL" "PURI" "CL+SSL" "HTTP2-UTIL" "HTTP2")) (QUOTE NIL) (QUOTE NIL) (QUOTE ("EXAMPLE-CLIENT" "EXAMPLE-SERVER" "*DUMP-BYTES*" "*DUMP-BYTES-STREAM*" "*DUMP-BYTES-HOOK*")) (QUOTE (#1#)) (QUOTE NIL) ...)) (SB-C::ORIGINAL-SOURCE-START 0 3) NIL)
22: (SB-C::PROCESS-TOPLEVEL-FORM (DEFPACKAGE :CL-HTTP2-PROTOCOL-EXAMPLE (:NICKNAMES :HTTP2-EXAMPLE) (:DOCUMENTATION "HTTP/2 draft-14/hpack-09 (h2-14) simple CL-ASYNC example client/server.") (:USE :CL :ALEXANDRIA :ANAPHORA :BABEL :CL-ASYNC :CL-ASYNC-SSL :PURI :CL+SSL :HTTP2-UTIL :HTTP2) (:EXPORT #:EXAMPLE-CLIENT #:EXAMPLE-SERVER #:*DUMP-BYTES* #:*DUMP-BYTES-STREAM* #:*DUMP-BYTES-HOOK*)) (SB-C::ORIGINAL-SOURCE-START 0 3) NIL)
23: ((LAMBDA (SB-KERNEL:FORM &KEY :CURRENT-INDEX &ALLOW-OTHER-KEYS) :IN SB-C::SUB-COMPILE-FILE) (DEFPACKAGE :CL-HTTP2-PROTOCOL-EXAMPLE (:NICKNAMES :HTTP2-EXAMPLE) (:DOCUMENTATION "HTTP/2 draft-14/hpack-09 (h2-14) simple CL-ASYNC example client/server.") (:USE :CL :ALEXANDRIA :ANAPHORA :BABEL :CL-ASYNC :CL-ASYNC-SSL :PURI :CL+SSL :HTTP2-UTIL :HTTP2) (:EXPORT #:EXAMPLE-CLIENT #:EXAMPLE-SERVER #:*DUMP-BYTES* #:*DUMP-BYTES-STREAM* #:*DUMP-BYTES-HOOK*)) :CURRENT-INDEX 3)
24: (SB-C::%DO-FORMS-FROM-INFO #<FUNCTION (LAMBDA (SB-KERNEL:FORM &KEY :CURRENT-INDEX &ALLOW-OTHER-KEYS) :IN SB-C::SUB-COMPILE-FILE) {21D15C7B}> #<SB-C::SOURCE-INFO {10019443F3}> SB-C::INPUT-ERROR-IN-COMPILE-FILE)
25: ((FLET SB-THREAD::WITH-RECURSIVE-LOCK-THUNK :IN SB-C::SUB-COMPILE-FILE))
26: ((FLET "WITHOUT-INTERRUPTS-BODY-29" :IN SB-THREAD::CALL-WITH-RECURSIVE-LOCK))
27: (SB-THREAD::CALL-WITH-RECURSIVE-LOCK #<CLOSURE (FLET SB-THREAD::WITH-RECURSIVE-LOCK-THUNK :IN SB-C::SUB-COMPILE-FILE) {800FFD79B}> #<SB-THREAD:MUTEX "World Lock" owner: #<SB-THREAD:THREAD "main thread" RUNNING {10005084F3}>> T NIL)
28: ((FLET "LAMBDA0" :IN "SYS:SRC;COMPILER;MAIN.LISP"))
29: ((FLET SB-C::WITH-IT :IN SB-C::%WITH-COMPILATION-UNIT))
30: (SB-C::SUB-COMPILE-FILE #<SB-C::SOURCE-INFO {10019443F3}> NIL)
31: (COMPILE-FILE #P"/usr/home/erikn/cl-http2-protocol/packages.lisp" :OUTPUT-FILE #P"/usr/home/erikn/.cache/common-lisp/sbcl-1.5.4-bsd-x64/usr/home/erikn/cl-http2-protocol/packages-tmpGHU3ALSV.fasl" :VERBOSE NIL :PRINT NIL :EXTERNAL-FORMAT :UTF-8 :TRACE-FILE NIL :BLOCK-COMPILE NIL :EMIT-CFASL NIL)
32: (UIOP/UTILITY:CALL-WITH-MUFFLED-CONDITIONS #<CLOSURE (LAMBDA NIL :IN UIOP/LISP-BUILD:COMPILE-FILE*) {1001942D0B}> NIL)
33: (UIOP/PATHNAME:CALL-WITH-ENOUGH-PATHNAME #P"/usr/home/erikn/cl-http2-protocol/packages.lisp" NIL #<CLOSURE (LAMBDA (UIOP/LISP-BUILD::INPUT-FILE) :IN UIOP/LISP-BUILD:COMPILE-FILE*) {1001942C8B}>)
34: (UIOP/LISP-BUILD:COMPILE-FILE* #P"/usr/home/erikn/cl-http2-protocol/packages.lisp" :OUTPUT-FILE #P"/usr/home/erikn/.cache/common-lisp/sbcl-1.5.4-bsd-x64/usr/home/erikn/cl-http2-protocol/packages.fasl" :EXTERNAL-FORMAT :UTF-8 :WARNINGS-FILE NIL)
35: (ASDF/LISP-ACTION:PERFORM-LISP-COMPILATION #<ASDF/LISP-ACTION:COMPILE-OP > #<ASDF/LISP-ACTION:CL-SOURCE-FILE "cl-http2-protocol" "packages">)
36: ((SB-PCL::EMF ASDF/ACTION:PERFORM) #<unused argument> #<unused argument> #<ASDF/LISP-ACTION:COMPILE-OP > #<ASDF/LISP-ACTION:CL-SOURCE-FILE "cl-http2-protocol" "packages">)
37: ((LAMBDA NIL :IN ASDF/ACTION:CALL-WHILE-VISITING-ACTION))
38: ((:METHOD ASDF/ACTION:PERFORM-WITH-RESTARTS :AROUND (T T)) #<ASDF/LISP-ACTION:COMPILE-OP > #<ASDF/LISP-ACTION:CL-SOURCE-FILE "cl-http2-protocol" "packages">) [fast-method]
39: ((:METHOD ASDF/PLAN:PERFORM-PLAN (T)) #<ASDF/PLAN:SEQUENTIAL-PLAN {1002C93063}>) [fast-method]
40: ((FLET SB-C::WITH-IT :IN SB-C::%WITH-COMPILATION-UNIT))
41: ((:METHOD ASDF/PLAN:PERFORM-PLAN :AROUND (T)) #<ASDF/PLAN:SEQUENTIAL-PLAN {1002C93063}>) [fast-method]
42: ((:METHOD ASDF/OPERATE:OPERATE (ASDF/OPERATION:OPERATION ASDF/COMPONENT:COMPONENT)) #<ASDF/LISP-ACTION:LOAD-OP > #<ASDF/SYSTEM:SYSTEM "cl-http2-protocol"> :PLAN-CLASS NIL :PLAN-OPTIONS NIL) [fast-method]
43: ((SB-PCL::EMF ASDF/OPERATE:OPERATE) #<unused argument> #<unused argument> #<ASDF/LISP-ACTION:LOAD-OP > #<ASDF/SYSTEM:SYSTEM "cl-http2-protocol"> :VERBOSE NIL)
44: ((LAMBDA NIL :IN ASDF/OPERATE:OPERATE))
45: ((:METHOD ASDF/OPERATE:OPERATE :AROUND (T T)) #<ASDF/LISP-ACTION:LOAD-OP > #<ASDF/SYSTEM:SYSTEM "cl-http2-protocol"> :VERBOSE NIL) [fast-method]
46: ((SB-PCL::EMF ASDF/OPERATE:OPERATE) #<unused argument> #<unused argument> ASDF/LISP-ACTION:LOAD-OP ("cl-http2-protocol") :VERBOSE NIL)
47: ((LAMBDA NIL :IN ASDF/OPERATE:OPERATE))
48: ((:METHOD ASDF/OPERATE:OPERATE :AROUND (T T)) ASDF/LISP-ACTION:LOAD-OP ("cl-http2-protocol") :VERBOSE NIL) [fast-method]
49: (ASDF/SESSION:CALL-WITH-ASDF-SESSION #<CLOSURE (LAMBDA NIL :IN ASDF/OPERATE:OPERATE) {1002C786EB}> :OVERRIDE T :KEY NIL :OVERRIDE-CACHE T :OVERRIDE-FORCING NIL)
50: ((LAMBDA NIL :IN ASDF/OPERATE:OPERATE))
51: (ASDF/SESSION:CALL-WITH-ASDF-SESSION #<CLOSURE (LAMBDA NIL :IN ASDF/OPERATE:OPERATE) {1002C67B9B}> :OVERRIDE NIL :KEY NIL :OVERRIDE-CACHE NIL :OVERRIDE-FORCING NIL)
52: ((:METHOD ASDF/OPERATE:OPERATE :AROUND (T T)) ASDF/LISP-ACTION:LOAD-OP #<ASDF/SYSTEM:SYSTEM "cl-http2-protocol"> :VERBOSE NIL) [fast-method]
53: (ASDF/OPERATE:LOAD-SYSTEM #<ASDF/SYSTEM:SYSTEM "cl-http2-protocol"> :VERBOSE NIL)
54: (ASDF/OPERATE:MODULE-PROVIDE-ASDF :CL-HTTP2-PROTOCOL)
55: ((FLET "WRAPPER4" :IN REQUIRE) ASDF/OPERATE:MODULE-PROVIDE-ASDF)
56: (SB-KERNEL:%MAP-FOR-EFFECT-ARITY-1 #<CLOSURE (FLET "WRAPPER4" :IN REQUIRE) {800FFED9B}> (ASDF/OPERATE:MODULE-PROVIDE-ASDF SB-IMPL::MODULE-PROVIDE-CONTRIB))
57: (REQUIRE :CL-HTTP2-PROTOCOL NIL)
58: (SB-INT:SIMPLE-EVAL-IN-LEXENV (REQUIRE :CL-HTTP2-PROTOCOL) #<NULL-LEXENV>)
59: (EVAL-TLF (REQUIRE :CL-HTTP2-PROTOCOL) 2 NIL)
60: ((LABELS SB-FASL::EVAL-FORM :IN SB-INT:LOAD-AS-SOURCE) (REQUIRE :CL-HTTP2-PROTOCOL) 2)
61: ((LAMBDA (SB-KERNEL:FORM &KEY :CURRENT-INDEX &ALLOW-OTHER-KEYS) :IN SB-INT:LOAD-AS-SOURCE) (REQUIRE :CL-HTTP2-PROTOCOL) :CURRENT-INDEX 2)
62: (SB-C::%DO-FORMS-FROM-INFO #<CLOSURE (LAMBDA (SB-KERNEL:FORM &KEY :CURRENT-INDEX &ALLOW-OTHER-KEYS) :IN SB-INT:LOAD-AS-SOURCE) {10014FCFEB}> #<SB-C::SOURCE-INFO {10014FCFA3}> SB-C::INPUT-ERROR-IN-LOAD)
63: (SB-INT:LOAD-AS-SOURCE #<SB-SYS:FD-STREAM for "file /usr/home/erikn/server.lisp" {10014EE7F3}> :VERBOSE NIL :PRINT NIL :CONTEXT "loading")
64: ((FLET SB-FASL::THUNK :IN LOAD))
65: (SB-FASL::CALL-WITH-LOAD-BINDINGS #<CLOSURE (FLET SB-FASL::THUNK :IN LOAD) {800FFF69B}> #<SB-SYS:FD-STREAM for "file /usr/home/erikn/server.lisp" {10014EE7F3}>)
66: ((FLET SB-FASL::LOAD-STREAM :IN LOAD) #<SB-SYS:FD-STREAM for "file /usr/home/erikn/server.lisp" {10014EE7F3}> NIL)
67: (LOAD #<SB-SYS:FD-STREAM for "file /usr/home/erikn/server.lisp" {10014EE7F3}> :VERBOSE NIL :PRINT NIL :IF-DOES-NOT-EXIST T :EXTERNAL-FORMAT :DEFAULT)
68: ((FLET SB-IMPL::LOAD-SCRIPT :IN SB-IMPL::PROCESS-SCRIPT) #<SB-SYS:FD-STREAM for "file /usr/home/erikn/server.lisp" {10014EE7F3}>)
69: ((FLET SB-UNIX::BODY :IN SB-IMPL::PROCESS-SCRIPT))
70: ((FLET "WITHOUT-INTERRUPTS-BODY-2" :IN SB-IMPL::PROCESS-SCRIPT))
71: (SB-IMPL::PROCESS-SCRIPT "server.lisp")
72: (SB-IMPL::TOPLEVEL-INIT)
73: ((FLET SB-UNIX::BODY :IN SAVE-LISP-AND-DIE))
74: ((FLET "WITHOUT-INTERRUPTS-BODY-7" :IN SAVE-LISP-AND-DIE))
75: ((LABELS SB-IMPL::RESTART-LISP :IN SAVE-LISP-AND-DIE))

unhandled condition in --disable-debugger mode, quitting

; compilation aborted after 0:00:00.018
; 
; compilation unit aborted
;   caught 2 fatal ERROR conditions

Building cl-http2-protocol in 2021

I got the system to build, but it won't run and thought it would be useful to document how to get this far so that if someone else wants to pick up the ball and run with it they'll have a head start.

First, you'll want to review the contents of issue #4. Those are just symptoms of the fact that cl-async and cl+ssl both wrap the OpenSSL libs (why doesn't cl-async use cl+ssl?). @martinflack describes at a high level what remains to be done to finish off the project. I don't think any of this is insurmountable, but it will take a bit of time to organise all the pieces.

The immediate fixes to get the system building in this repo are:

  • Add :cl-libevent2-ssl to the list of dependencies in the ASDF file
  • Replace use with mix in the example package definition
  • Rename the second argument in the generic function emit

These changes, and a few others, are in the Symbolics fork. A pull request has been made to bring these into this repo.

The remaining issues are in the dependent libraries, mostly cl-async and its dependencies, which seems to have suffered some minor bit rot.

  1. If on MS Windows, the SSL libs won't load because CFFI can't locate the libraries. The fix has been submitted as a pull request orthecreedence/cl-async#187. If this patch hasn't made it into the main branch, you'll need to make the change yourself.
  2. cl-async in turn has a dependency on cl-libevent2, a CFFI wrapper over libevent. The wrapper code was generated with swig, which has removed support for Common Lisp in its latest version. cl-libevent2 uses an older version of libevent. There are a few options:
    • downgrade swig and regenerate the bindings with a newer libevent
    • downgrade libevent
    • do nothing and hope that the current bindings work with the new libevent
    • try generating the bindings with the CFFI groveler

Whilst this may get cl-async compiling on a developers workstation, it's not going to be a very nice user experience for others who want to use the library.

Now you can build cl-http2-protocol. The build will contain errors because of references to internal cl-async functions, now removed. See #4 for an explanation of why. The build errors aren't that great in number. Someone needs to look at each of the internal cl-async functions called and check for a public interface equivalent. Ideally do this working with the upstream maintainer to get any changes adopted there. It would probably be good for both projects.

There's some other tidying up that could be done in cl+ssl, but there don't appear to be any blockers there.

; file: s:/src/http2/tcp.lisp
; in: CL-ASYNC-UTIL:DEFINE-C-CALLBACK CL-ASYNC::TCP-EVENT-CB
;     (CL-ASYNC::GET-LAST-TCP-ERR)
; 
; caught STYLE-WARNING:
;   undefined function: CL-ASYNC::GET-LAST-TCP-ERR


; file: s:/src/http2/example.lisp
; in: DEFUN CL-HTTP2-PROTOCOL-EXAMPLE:EXAMPLE-CLIENT
;     (CL-ASYNC-SSL::INIT-SSL-CLIENT-CONTEXT CL+SSL::*SSL-GLOBAL-CONTEXT*)
; 
; caught STYLE-WARNING:
;   undefined function: CL-ASYNC-SSL::INIT-SSL-CLIENT-CONTEXT


; file: s:/src/http2/tcp-ssl.lisp
; in: DEFUN CL-ASYNC-SSL:TCP-SSL-SERVER
;     (CL-ASYNC-SSL::LAST-SSL-ERROR)
; 
; caught STYLE-WARNING:
;   undefined function: CL-ASYNC-SSL::LAST-SSL-ERROR


; file: s:/src/http2/tcp.lisp
; in: CL-ASYNC-UTIL:DEFINE-C-CALLBACK CL-ASYNC::TCP-EVENT-CB
;     (CL-ASYNC::RELEASE-DNS-BASE)
; 
; caught STYLE-WARNING:
;   undefined function: CL-ASYNC::RELEASE-DNS-BASE


; file: s:/src/http2/tcp-ssl.lisp
; in: DEFUN CL-ASYNC-SSL:TCP-SSL-SERVER
;     (CL-ASYNC-SSL::TCP-SERVER-C CL-ASYNC-SSL::SERVER)
; 
; caught STYLE-WARNING:
;   undefined function: CL-ASYNC-SSL::TCP-SERVER-C

;     (CL-ASYNC-SSL::TCP-SERVER-DATA-POINTER CL-ASYNC-SSL::SERVER)
; 
; caught STYLE-WARNING:
;   undefined function: CL-ASYNC-SSL::TCP-SERVER-DATA-POINTER


; file: s:/src/http2/tcp.lisp
; in: CL-ASYNC-UTIL:DEFINE-C-CALLBACK CL-ASYNC::TCP-EVENT-CB
;     (CL-ASYNC-UTIL:RUN-EVENT-CB CL-ASYNC::EVENT-CB CL-ASYNC:EVENT)
; --> HANDLER-BIND SB-KERNEL::%HANDLER-BIND LET SB-INT:DX-FLET FLET 
; --> #:H0 BLOCK SETF 
; ==>
;   (SETQ CL-ASYNC::_EVCB-ERR CL-ASYNC::E)
; 
; caught WARNING:
;   undefined variable: CL-ASYNC::_EVCB-ERR
; 
; compilation unit finished
;   Undefined functions:
;     CL-ASYNC::GET-LAST-TCP-ERR CL-ASYNC-SSL::INIT-SSL-CLIENT-CONTEXT CL-ASYNC-SSL::LAST-SSL-ERROR CL-ASYNC::RELEASE-DNS-BASE CL-ASYNC-SSL::TCP-SERVER-C CL-ASYNC-SSL::TCP-SERVER-DATA-POINTER
;   Undefined variable:
;     CL-ASYNC::_EVCB-ERR
;   caught 1 WARNING condition
;   caught 19 STYLE-WARNING conditions

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.