internal pronto
This commit is contained in:
108
http.cm
108
http.cm
@@ -1,5 +1,6 @@
|
||||
var socket = use('socket')
|
||||
var tls = use('net/tls')
|
||||
var Blob = use('blob')
|
||||
|
||||
def CRLF = "\r\n"
|
||||
|
||||
@@ -625,20 +626,106 @@ var receive_response = function(callback, state) {
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// fetch — composed requestor pipeline
|
||||
// fetch — synchronous HTTP(S) GET, returns response body (stoned blob)
|
||||
// ============================================================
|
||||
|
||||
var fetch = function(callback, value) {
|
||||
def pipeline = runtime_env.sequence([
|
||||
parse_url,
|
||||
resolve_dns,
|
||||
open_connection,
|
||||
send_request,
|
||||
receive_response
|
||||
])
|
||||
return pipeline(callback, value)
|
||||
var fetch = function(url) {
|
||||
var scheme = "http"
|
||||
var rest = url
|
||||
var scheme_end = search(url, "://")
|
||||
var slash = null
|
||||
var host_port = null
|
||||
var path = "/"
|
||||
var hp = null
|
||||
var host = null
|
||||
var port = null
|
||||
var fd = null
|
||||
var ctx = null
|
||||
var buf = Blob()
|
||||
var raw_text = null
|
||||
var hdr_end = null
|
||||
var header_text = null
|
||||
var body_start_bits = null
|
||||
var body = null
|
||||
var addrs = null
|
||||
var address = null
|
||||
var ok = true
|
||||
|
||||
if (scheme_end != null) {
|
||||
scheme = lower(text(url, 0, scheme_end))
|
||||
rest = text(url, scheme_end + 3, length(url))
|
||||
}
|
||||
slash = search(rest, "/")
|
||||
host_port = rest
|
||||
if (slash != null) {
|
||||
host_port = text(rest, 0, slash)
|
||||
path = text(rest, slash, length(rest))
|
||||
}
|
||||
hp = array(host_port, ":")
|
||||
host = hp[0]
|
||||
port = length(hp) > 1 ? number(hp[1]) : (scheme == "https" ? 443 : 80)
|
||||
|
||||
addrs = socket.getaddrinfo(host, text(port))
|
||||
if (addrs == null || length(addrs) == 0) return null
|
||||
address = addrs[0].address
|
||||
|
||||
fd = socket.socket("AF_INET", "SOCK_STREAM")
|
||||
var _do = function() {
|
||||
var req = null
|
||||
var chunk = null
|
||||
socket.connect(fd, {address: address, port: port})
|
||||
if (scheme == "https") ctx = tls.wrap(fd, host)
|
||||
req = "GET " + path + " HTTP/1.1" + CRLF
|
||||
req = req + "Host: " + host_port + CRLF
|
||||
req = req + "Connection: close" + CRLF
|
||||
req = req + "User-Agent: cell/1.0" + CRLF
|
||||
req = req + "Accept: */*" + CRLF + CRLF
|
||||
if (ctx != null) tls.send(ctx, req)
|
||||
else socket.send(fd, req)
|
||||
while (true) {
|
||||
if (ctx != null) chunk = tls.recv(ctx, 16384)
|
||||
else chunk = socket.recv(fd, 16384)
|
||||
if (chunk == null) break
|
||||
stone(chunk)
|
||||
if (length(chunk) == 0) break
|
||||
buf.write_blob(chunk)
|
||||
}
|
||||
} disruption {
|
||||
ok = false
|
||||
}
|
||||
_do()
|
||||
var _cleanup = function() {
|
||||
if (ctx != null) tls.close(ctx)
|
||||
else socket.close(fd)
|
||||
} disruption {}
|
||||
_cleanup()
|
||||
if (!ok) return null
|
||||
stone(buf)
|
||||
raw_text = text(buf)
|
||||
hdr_end = search(raw_text, CRLF + CRLF)
|
||||
if (hdr_end == null) return null
|
||||
header_text = text(raw_text, 0, hdr_end)
|
||||
if (search(lower(header_text), "transfer-encoding: chunked") != null)
|
||||
return decode_chunked(text(raw_text, hdr_end + 4))
|
||||
// Headers are ASCII so char offset = byte offset
|
||||
body_start_bits = (hdr_end + 4) * 8
|
||||
body = buf.read_blob(body_start_bits, length(buf))
|
||||
stone(body)
|
||||
return body
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// fetch_requestor — async requestor pipeline for fetch
|
||||
// ============================================================
|
||||
|
||||
var fetch_requestor = sequence([
|
||||
parse_url,
|
||||
resolve_dns,
|
||||
open_connection,
|
||||
send_request,
|
||||
receive_response
|
||||
])
|
||||
|
||||
function close(fd) {
|
||||
socket.close(fd)
|
||||
}
|
||||
@@ -650,5 +737,6 @@ return {
|
||||
sse_open: sse_open, sse_event: sse_event, sse_close: sse_close,
|
||||
// client
|
||||
fetch: fetch,
|
||||
fetch_requestor: fetch_requestor,
|
||||
request: request
|
||||
}
|
||||
|
||||
@@ -822,9 +822,8 @@ var $_ = {}
|
||||
|
||||
use_cache['core/json'] = json
|
||||
|
||||
// Create runtime_env early (empty) -- filled after pronto loads.
|
||||
// Shop accesses it lazily (in inject_env, called at module-use time, not load time)
|
||||
// so it sees the filled version.
|
||||
// Runtime env: passed to package modules via shop's inject_env.
|
||||
// Requestor functions are added immediately below; actor/log/send added later.
|
||||
var runtime_env = {}
|
||||
|
||||
// Populate core_extras with everything shop (and other core modules) need
|
||||
@@ -848,6 +847,18 @@ core_extras.ensure_build_dir = ensure_build_dir
|
||||
core_extras.compile_to_blob = compile_to_blob
|
||||
core_extras.native_mode = native_mode
|
||||
|
||||
// Load pronto early so requestor functions (sequence, parallel, etc.) are
|
||||
// available to core modules loaded below (http, shop, etc.)
|
||||
var pronto = use_core('internal/pronto')
|
||||
var fallback = pronto.fallback
|
||||
var parallel = pronto.parallel
|
||||
var race = pronto.race
|
||||
var sequence = pronto.sequence
|
||||
core_extras.fallback = fallback
|
||||
core_extras.parallel = parallel
|
||||
core_extras.race = race
|
||||
core_extras.sequence = sequence
|
||||
|
||||
// NOW load shop -- it receives all of the above via env
|
||||
var shop = use_core('internal/shop')
|
||||
use_core('build')
|
||||
@@ -1074,12 +1085,6 @@ actor_mod.set_log(log)
|
||||
// (before the full log was ready) captured the bootstrap function reference.
|
||||
_log_full = log
|
||||
|
||||
var pronto = use_core('pronto')
|
||||
var fallback = pronto.fallback
|
||||
var parallel = pronto.parallel
|
||||
var race = pronto.race
|
||||
var sequence = pronto.sequence
|
||||
|
||||
runtime_env.actor = actor
|
||||
runtime_env.log = log
|
||||
runtime_env.send = send
|
||||
|
||||
Reference in New Issue
Block a user