sourceextensions/web/lib/Web.xtl

1⍝# Web.xtl -- the facade of the web extension: HTTP serving, where the 2⍝# program is the request loop. axum serves on 127.0.0.1 in the 3⍝# background and queues each request; the program takes the next one, 4⍝# reads its parts, and replies. 5⍝# Import: "wb:" u_se< "Web" 6⍝# Run with xetal-x (xetal-x --ext DIR puts this directory on XETAL_PATH). 7⍝# Each signature line is written out by the binding macro (lib/Ffi.xtlm). 8 9ᶠᶠⁱ⁼u̲se< "Ffi" 10 11⍝# wb:s_erve! port: serve on 127.0.0.1 at the port (8470 is this 12⍝# repository's; 0 takes any free one); the port it serves on. 13"s_erve! : int -> int" ᶠᶠⁱb̲ind< "web/serve"
ᶠᶠⁱb̲ind< expands to
ˡs̲erve! ← { a →
  pa ← (f̲ormat r̲avel f̲loor a) ⎕N̲PUT "ext:web/serve?int=" c̲at f̲ormat s̲hape a
  f̲loor f̲irst n̲umbers ⎕N̲GET "ext:web/serve"
}
14⍝# wb:n_ext! seconds: wait up to that long for a request; "GET /path" 15⍝# (the method and path), or "none". A request taken before and not 16⍝# replied to is answered 500. 17"n_ext! : float -> text" ᶠᶠⁱb̲ind< "web/next"
ᶠᶠⁱb̲ind< expands to
ˡn̲ext! ← { a →
  pa ← (f̲ormat r̲avel f̲loat a) ⎕N̲PUT "ext:web/next?float=" c̲at f̲ormat s̲hape a
  ⎕N̲GET "ext:web/next"
}
18⍝## The request being answered 19 20⍝# wb:m_ethod @: its method (GET, POST, ...). 21"m_ethod : unit -> text" ᶠᶠⁱb̲ind< "web/method"
ᶠᶠⁱb̲ind< expands to
ˡm̲ethod ← { @ →
  ⎕N̲GET "ext:web/method"
}
22⍝# wb:p_ath @: its path, without the query. 23"p_ath : unit -> text" ᶠᶠⁱb̲ind< "web/path"
ᶠᶠⁱb̲ind< expands to
ˡp̲ath ← { @ →
  ⎕N̲GET "ext:web/path"
}
24⍝# wb:q_uery @: its query, as sent (still encoded). 25"q_uery : unit -> text" ᶠᶠⁱb̲ind< "web/query"
ᶠᶠⁱb̲ind< expands to
ˡq̲uery ← { @ →
  ⎕N̲GET "ext:web/query"
}
26⍝# wb:b_ody @: its body, as text. 27"b_ody : unit -> text" ᶠᶠⁱb̲ind< "web/body"
ᶠᶠⁱb̲ind< expands to
ˡb̲ody ← { @ →
  ⎕N̲GET "ext:web/body"
}
28⍝# wb:p_aram "name": a query or form field, decoded ("" when absent). 29"p_aram : text -> text" ᶠᶠⁱb̲ind< "web/param"
ᶠᶠⁱb̲ind< expands to
ˡp̲aram ← { a →
  pa ← a ⎕N̲PUT "ext:web/param?text"
  ⎕N̲GET "ext:web/param"
}
30⍝# wb:c_ontent! "text/csv": the reply's content type (else it is guessed 31⍝# from the body: SVG, HTML, JSON or plain text). 32"c_ontent! : text -> int" ᶠᶠⁱb̲ind< "web/content"
ᶠᶠⁱb̲ind< expands to
ˡc̲ontent! ← { a →
  pa ← a ⎕N̲PUT "ext:web/content?text"
  f̲loor f̲irst n̲umbers ⎕N̲GET "ext:web/content"
}
33⍝# wb:h_eader! "Location: /": a header on the reply (a redirect's 34⍝# Location, Cache-Control, ...); how many the reply has. 35"h_eader! : text -> int" ᶠᶠⁱb̲ind< "web/header"
ᶠᶠⁱb̲ind< expands to
ˡh̲eader! ← { a →
  pa ← a ⎕N̲PUT "ext:web/header?text"
  f̲loor f̲irst n̲umbers ⎕N̲GET "ext:web/header"
}
36⍝# status wb:r_eply! body: answer the request; the body's length in bytes. 37"r_eply! : int text -> int" ᶠᶠⁱb̲ind< "web/reply"
ᶠᶠⁱb̲ind< expands to
ˡr̲eply! ← { a b →
  pa ← (f̲ormat r̲avel f̲loor a) ⎕N̲PUT "ext:web/reply?int=" c̲at f̲ormat s̲hape a
  pb ← b ⎕N̲PUT "ext:web/reply?text"
  f̲loor f̲irst n̲umbers ⎕N̲GET "ext:web/reply"
}
38⍝# wb:f_iles! "dir": serve the files under dir (relative, no ..) directly, 39⍝# without the program; "" stops. How many files it holds. 40"f_iles! : text -> int" ᶠᶠⁱb̲ind< "web/files"
ᶠᶠⁱb̲ind< expands to
ˡf̲iles! ← { a →
  pa ← a ⎕N̲PUT "ext:web/files?text"
  f̲loor f̲irst n̲umbers ⎕N̲GET "ext:web/files"
}
41⍝# wb:s_top! @: stop serving; the port (0 if none was serving). 42"s_top! : unit -> int" ᶠᶠⁱb̲ind< "web/stop"
ᶠᶠⁱb̲ind< expands to
ˡs̲top! ← { @ →
  f̲loor f̲irst n̲umbers ⎕N̲GET "ext:web/stop"
}
43 44⍝# The native package behind these functions. 45ˡpackage ← "web"