Adds net-listen to stdlib

This commit is contained in:
sloum 2022-11-05 08:25:29 -07:00
parent 8541bc66f8
commit d033985e69
3 changed files with 54 additions and 1 deletions

52
lib.go
View File

@ -2651,6 +2651,58 @@ var stdLibrary = vars{
return []expression{false, "'net-get' was given a URL for an unsupported scheme: " + u.Scheme + ". Consider using 'net-conn' instead"}
}
},
"net-listen": func(a ...expression) expression {
if len(a) < 3 {
return exception(`'net-listen' expects a string or symbol ("tcp", "tcp4", "tcp6", "unix", or "unixpacket"), an address string, and a procedure or macro that accepts an io-handle. Too few values were given`)
}
network := strings.ToLower(String(a[0], false))
switch network {
case "tcp", "tcp4", "tcp6", "unix", "unixpacket":
break
default:
return exception(`'net-listen' expected the first value given to be a string equal to "tcp", "tcp4", "tcp6", "unix", or "unixpacket". An invalid value was given`)
}
address := String(a[1], false)
server, err := net.Listen(network, address)
if err != nil {
return exception(`'net-listen' could not listen at the given address: ` + address)
}
defer server.Close()
var callback func(conn *IOHandle)
switch f := a[2].(type) {
case func(...expression) expression:
callback = func(conn *IOHandle) {
f(IOHandle{&conn, true, "net-conn"})
}
case proc:
callback = func(conn *IOHandle) {
apply(f, []expression{conn}, "net-listen callback")
}
case macro:
callback = func(conn *IOHandle) {
apply(f, []expression{conn}, "net-listen callback")
}
default:
return exception(`'net-listen' expected a built-in, proc, or macro as its third value; an invalid value was given`)
}
if len(a) > 3 {
switch f := a[3].(type) {
case func(...expression) expression:
f(network, address)
case proc:
apply(f, []expression{network, address}, "net-listen init callback")
case macro:
apply(f, []expression{network, address}, "net-listen init callback")
}
}
for {
conn, err := server.Accept()
if err != nil {
return exception("'net-listen' encountered an error with a connection: " + err.Error())
}
go callback(&IOHandle{&conn, true, "net-conn"})
}
},
"net-conn": func(a ...expression) expression {
// (net-conn host port use-tls timeout-seconds)
// (net-conn string string bool number)

View File

@ -18,7 +18,7 @@ import (
ln "github.com/peterh/liner"
)
const version = "1.2.7"
const version = "1.2.8"
const globalLibPath = "/usr/local/lib/slope/modules/"

View File

@ -119,6 +119,7 @@ var usageStrings = map[string]string{
"not": "(not [value]) => bool\n\n`not` will invert the truthiness of the value it is given",
"net-conn": "(net-conn [host: string] [port: string|number] [[use-tls: bool]] [[timeout-seconds: number]]) => io-handle|#f\n\n`net-conn` is the generic way of making a network request in slope. It returns an open io-handle or an exception. The returned io-handle represents a TCP connection to the given host at the given port. If the optional value use-tls is set to #t then TLS will be used. If the optional value timeout-seconds is set to a positive number the io-handle will be returned if a connection happens within the given number of seconds or #f will be returned. If timeout-seconds is not given or is less than 1 there is no timeout and an invalid connection attempt will hang indefinitely",
"net-get": "(net-get [url: string]) => list\n\nAccepts http(s), gopher, and gemini url schemes. `net-get` will request the resource found at the given url and return a list where the first item is a bool indicating that the request was successful or not. The second item in the list is either the response body or the error message (if the first value is false). Gemini requests will also have a third item in the list indicating the mime type of a successful request or an empty string for a failed request",
"net-listen": "(net-listen [network: string|symbol] [address: string|symbol] [callback: proc|macro] [[init-callback: proc|macro]]) => ()\n\nListens for incoming network requests at the given address, which should generally include a port (ex: `127.0.0.1:3000`). The network should be `tcp` (which will accept IPv4 or IPv6), `tcp4`, `tcp6`, `unix`, or `unixsocket`. Any other network will result in an error. The network is not case sensitive.\n\nThe callback should accept one argument: an io-handle. 'net-listen' automatically does an infinite loop to listen for requests, when one is received the callback will be called in its own concurrent coroutine passing the connection as an argument to the callback (as an open io-handle). It is the callback's responsibility to close the io-handle it is given.\n\nThe optional argument `init-callback` will be called once, before the infinite loop starts; it should accept two arguments: a network type string and an address string. The init callback is useful for printing information to the screen.\n\nIf there is an error accepting a connection the error will be returned, ending the infinite loop. Otherwise, 'net-listen' will go on forever and the caller will have to use Ctrl+C to exit the program",
"net-post": "(net-post [url: string] [data: io-handle|string] [[mime: string]]) => list\n\nAccepts http(s), gopher, and gemini url schemes. `net-post` will request the resource found at the given url and return a list where the first item is a bool indicating that the request was successful or not. The second item in the list is either the response body or the error message (if the first value is false). Gemini requests will also have a third item in the list indicating the mime type of a successful request or an empty string for a failed request.\n\nIn addition to making the request to the url the request body will include the data found in the io-handle or string. In the case of an http(s) this will be passed as post data. In the case of gemini or gopher this is just a shorthand for adding the data to the url as a query paramater. When making an http(s) post request via `net-post` the mime type will default to `text/plain` unless an alternate mime type is provided",
"newline": "(newline) => ()\n\nPrints a newline character to stdout",
"ns-create": "(ns-create [ns-name: string|symbol]) => true\n\nCreates an empty namespace with the given ns-name. The ns-name cannot contain an ascii space character, colon, apostrophe, open paren, closing paren, quote, or hash character as these are reserved for use elswhere in the slope system. Once values are defined within the namespace (via `ns-define`) they can by called the same as you would call code from a loaded module using the `namespace::symbol` syntax.\n\nNote: To reference other values within the namespace you must use the aforementioned calling convention. `ns->string` will automatically adjust the references so that its output will use the local global scope and allow the code to be loaded normally",