Write CLIPS code that can talk to the internet!
- This repository is for educational purposes only :)
- I have only tested this codebase in Ubuntu 23.10 so far
- the library is only built for linux-based systems for now
- Don't know CLIPS? Try the Tour of CLIPS I wrote to learn!
Create a server that listens, accepts, and reads a message from a client:
CLIPS (Forge Alpha 5/12/24)
CLIPS> (create-socket AF_INET SOCK_STREAM)
3
CLIPS> (bind-socket 3 127.0.0.1 8889)
127.0.0.1:8889
CLIPS> (listen 3) ; NOTE: 127.0.0.1:8889 would work here, too
TRUE
CLIPS> (accept 3) ; NOTE: 127.0.0.1:8889 would work here, too
4
CLIPS> (get-socket-logical-name 4)
127.0.0.1:42616
CLIPS> (readline 127.0.0.1:42616)
A client that would connect to that server would look like this:
CLIPS (Forge Alpha 5/12/24)
CLIPS> (create-socket AF_INET SOCK_STREAM)
3
CLIPS> (connect 3 127.0.0.1 8889)
127.0.0.1:8889
CLIPS> (printout 127.0.0.1:8889 "Hello, server :)" crlf)
CLIPS> (flush-connection 3) ; NOTE: 127.0.0.1:8889 would work here, too
TRUE
You should see the message from the client in the previously mentioned server's rules engine when the client flushes their connection!
Provide low-level networking functions to CLIPS environments via socket file descriptors. Allows CLIPS-based applications to send and receive messages to/from other computers on a network via I/O Router1-based reading and writing operations.
This library adds functions that let you create network servers and clients. That is: you can make CLIPS applications that receive network requests and make network requests.
This library also supports DNS resolution. To discover IP addresses
for a given url, use (resolve-domain-name ryjo.codes)
function.
Hint: Use (intersection$ (resolve-domain-name ryjo.codes))
if you see duplicate IP addresses returned.
Educational purposes for now. I would like to get the quality of code in this repo ready for upstream merging into CLIPS someday. For now, it's so I can make some fun web applications using CLIPS!
The project can be built from the root directory with make
:
make
Note that this requires image magick header files on your system.
If you do not want it or otherwise do not need to use the (mimetype
function
in your clips code, you can run:
make NO_IMAGE_MAGICK
This will create the binary clips
file in the root directory.
Use this to run the example server and client network applications
provided by the files in the examples
directory.
There are 4 example servers provided in this repository and 3 clients.
The simplest server
receives a single tcp connection and then exits.
The other can be used to serve multiple concurrent requests until it is
ctrl+z
and kill
ed.
./clips -f2 examples/server-simple.bat
or
./clips -f2 examples/server-complex.bat
./clips -f2 examples/client.bat
You should now also be able to connect to 127.0.0.1:8888 via telnet, curl, or a browser.
curl --http0.9 http://localhost:8888/
telnet localhost 8888
Visit http://localhost:8888 in the browser of your choice. The message displayed back is the first line of text that your browser sends to the webserver under the hood. This can be used to build web applications that let the users navigate to different "pages" in your web app. For example, if you try to go to http://localhost:8888/asdf-123, you'll see a slightly different message.
Looks up IP addresses given a domain name. Only tested with IPv4 and IPv6.
Accepts a connection on a socket file descriptor. Returns an integer representing the client's file descriptor or FALSE if it fails.
Binds a socket to a given IP/PORT or directory (in case of unix sockets). Returns an integer representing the client's file descriptor or FALSE if it fails.
Connects a socket to a given IP/PORT or directory (in case of unix sockets). Returns the logical name of the connection that can be read/written, or FALSE if it fails.
Closes a socket bound or connected on a given IP/PORT or directory (in case of unix sockets). Returns TRUE if connection closed successfully, FALSE if it fails.
Binds a socket to a given IP/PORT or directory (in case of unix sockets). Returns an integer representing the client's file descriptor or FALSE if it fails.
The following domains are currently supported:
AF_UNIX
: used for unix socketsAF_INET
: IPv4AF_INET6
: IPv6
The following types are currently supported:
SOCK_STREAM
: typically used for TCPSOCK_DGRAM
: typically used for UDP
Protocol is optional and can typically be left blank.
Use this to "empty" the buffer of data received from the client. WARNING: If this is run on a blocking request, you may block indefinitely.
Returns errno
, a global variable set when errors occur with some socket functions.
For example, if you try to bind twice on a socket:
$ ./clips
CLIPS (Forge Alpha 5/12/24)
CLIPS> (errno)
0
CLIPS> (errno-sym)
CLIPS> (create-socket AF_INET SOCK_STREAM)
3
CLIPS> (bind-socket 3 127.0.0.1 8889)
127.0.0.1:8889
CLIPS> (bind-socket 3 127.0.0.1 8887)
Could not bind 127.0.0.1
perror: Invalid argument
FALSE
CLIPS> (errno)
22
CLIPS> (errno-sym)
EINVAL
Add/remove certain flags on a socket as specified after the socket fd or logical name.
The following flags are currently supported:
O_NONBLOCK
O_APPEND
O_ASYNC
Flushes the buffer to the recipient.
Converts an integer representing a socket file descriptor to a symbol representing the name of the I/O router. Use this name to read and write to the socket.
Gets or Sets the timeout on the socket in microseconds.
Listens for connections on a socket. After running this,
you can now (accept
clients connecting to your server
via the socket.
Polls the socket for a number of milliseconds for any given number of flags.
Specify 0
as ?milliseconds
so that it returns immediately.
Possible flags:
POLLIN
POLLOUT
POLLERR
POLLHUP
POLLNVAL
POLLPRI
POLLRDNORM
POLLRDBAND
POLLWRNORM
POLLWRBAND
Returns TRUE if it got the FLAG, FALSE if it did not before timeout expires.
Gets or Sets options on the socket.
Possible values for ?level
currently supported:
SOL_SOCKET
IPPROTO_TCP
Possible values for ?optionName
currently supported:
SO_REUSEADDR
TCP_NODELAY
?value
is an integer to set the flag to.
Changes the buffering style for a connection. As a means of example, stderr is "not buffered," stdout via your terminal is probably "line buffered," and files are normally "block buffered."
Shut down part or all of a full-duplex connection.
Possible values of ?optionalHow
:
SHUT_RD
: further receptions will be disallowedSHUT_WR
: further transmissions will be disallowedSHUT_RDWR
: further receptions and transmissions will be disallowed
In order to watch all activity on your computer's port 8888
(which the example server and client use by default),
use tcpdump
. You may need to run as root
or with sudo
:
tcpdump -nn -i any port 8888
This codebase is based on
CLIPS 7.0.x
released on 5/12/24. I added a socketrtr.h
and socketrtr.c
to support reading/writing to sockets.
I add user defined functions (UDFs) to CLIPS environments compiled with this source code
in userfunctions.c
. I initialize the socket router in router.c
inside of the function InitializeDefaultRouters
.
Footnotes
-
See Section 9: I/O Routers of the CLIPS Advanced Programming Guide ↩