MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add node:http

+948 -58
+2
examples/demo/server.cjs
··· 1 + console.log('started on http://localhost:3000'); 2 + 1 3 require('node:http') 2 4 .createServer((_req, res) => res.end('ant!')) 3 5 .listen(3000);
+21
include/esm/builtin_bundle.h
··· 1 + #ifndef ESM_BUILTIN_BUNDLE_H 2 + #define ESM_BUILTIN_BUNDLE_H 3 + 4 + #include "esm/loader.h" 5 + 6 + #include <stdbool.h> 7 + #include <stddef.h> 8 + #include <stdint.h> 9 + 10 + typedef struct { 11 + const char *specifier; 12 + const char *source_name; 13 + const uint8_t *code; 14 + size_t code_len; 15 + ant_module_format_t format; 16 + } ant_builtin_bundle_entry_t; 17 + 18 + bool esm_is_builtin_specifier(const char *specifier); 19 + const ant_builtin_bundle_entry_t *esm_lookup_builtin_bundle(const char *specifier, size_t spec_len); 20 + 21 + #endif
+8
include/modules/http_metadata.h
··· 1 + #ifndef ANT_INTERNAL_HTTP_METADATA_MODULE_H 2 + #define ANT_INTERNAL_HTTP_METADATA_MODULE_H 3 + 4 + #include "types.h" 5 + 6 + ant_value_t internal_http_metadata_library(ant_t *js); 7 + 8 + #endif
+1
meson.build
··· 39 39 libant = static_library( 40 40 'ant', 41 41 lib_sources + [ 42 + builtin_bundle_h, 42 43 snapshot_h, 43 44 messages_h, 44 45 theme_h
+19
meson/builtins/meson.build
··· 1 + node = find_program('node', required: true) 2 + 3 + builtin_files = files(run_command( 4 + python, files('../sources.py'), src_root / 'sources.json', 'builtins', src_root, 5 + check: true 6 + ).stdout().strip().split()) 7 + 8 + builtin_bundle_h = custom_target( 9 + 'builtin_bundle', 10 + input: builtin_files, 11 + output: 'builtin_bundle_data.h', 12 + command: [ 13 + node, 14 + src_root / 'src' / 'tools' / 'gen_builtin_bundle.js', 15 + src_root / 'src' / 'builtins', 16 + '@OUTPUT@', 17 + '@INPUT@', 18 + ], 19 + )
+1
meson/meson.build
··· 31 31 subdir('deps') 32 32 subdir('version') 33 33 subdir('snapshot') 34 + subdir('builtins')
+34 -2
sources.json
··· 1 1 { 2 2 "engine": { 3 - "patterns": ["src/*.c", "src/gc/*.c", "src/highlight/*.c", "src/esm/*.c", "src/cli/*.c", "src/modules/*.c", "src/streams/*.c", "src/silver/*.c", "src/http/*.c", "src/net/*.c"], 3 + "patterns": [ 4 + "src/*.c", 5 + "src/gc/*.c", 6 + "src/highlight/*.c", 7 + "src/esm/*.c", 8 + "src/cli/*.c", 9 + "src/modules/*.c", 10 + "src/streams/*.c", 11 + "src/silver/*.c", 12 + "src/http/*.c", 13 + "src/net/*.c" 14 + ], 4 15 "exclude": ["src/main.c", "src/watch.c"] 5 16 }, 6 17 "library": { 7 - "patterns": ["src/*.c", "src/gc/*.c", "src/highlight/*.c", "src/esm/*.c", "src/modules/*.c", "src/streams/*.c", "src/silver/*.c", "src/http/*.c", "src/net/*.c"], 18 + "patterns": [ 19 + "src/*.c", 20 + "src/gc/*.c", 21 + "src/highlight/*.c", 22 + "src/esm/*.c", 23 + "src/modules/*.c", 24 + "src/streams/*.c", 25 + "src/silver/*.c", 26 + "src/http/*.c", 27 + "src/net/*.c" 28 + ], 8 29 "exclude": ["src/main.c", "src/watch.c"] 9 30 }, 10 31 "core": { 11 32 "patterns": ["src/core/**/*.ts"], 12 33 "exclude": ["src/core/index.ts"] 34 + }, 35 + "builtins": { 36 + "patterns": [ 37 + "src/builtins/**/*.cts", 38 + "src/builtins/**/*.mts", 39 + "src/builtins/**/*.ts", 40 + "src/builtins/**/*.cjs", 41 + "src/builtins/**/*.mjs", 42 + "src/builtins/**/*.js" 43 + ], 44 + "exclude": [] 13 45 } 14 46 }
+56 -30
src/ant.c
··· 24 24 25 25 #include "esm/remote.h" 26 26 #include "esm/loader.h" 27 + #include "esm/builtin_bundle.h" 27 28 28 29 #include "silver/lexer.h" 29 30 #include "silver/compiler.h" ··· 11208 11209 11209 11210 ant_value_t import_meta = js_get_current_import_meta(js); 11210 11211 if (vtype(import_meta) == T_OBJ) { 11211 - ant_value_t filename = js_get(js, import_meta, "filename"); 11212 - if (vtype(filename) == T_STR) { 11213 - ant_offset_t n = 0; ant_offset_t off = vstr(js, filename, &n); 11214 - return js_esm_resolve_specifier(js, args[0], (const char *)(uintptr_t)(off)); 11215 - } 11216 - } return js_esm_resolve_specifier(js, args[0], NULL); 11212 + ant_value_t filename = js_get(js, import_meta, "filename"); 11213 + 11214 + if (vtype(filename) == T_STR) { 11215 + ant_offset_t n = 0; ant_offset_t off = vstr(js, filename, &n); 11216 + return js_esm_resolve_specifier(js, args[0], (const char *)(uintptr_t)(off)); 11217 + }} 11218 + 11219 + return js_esm_resolve_specifier(js, args[0], NULL); 11220 + } 11221 + 11222 + static inline void js_set_import_meta_special_dirname( 11223 + ant_t *js, 11224 + ant_value_t import_meta, 11225 + const char *filename, 11226 + bool is_builtin 11227 + ) { 11228 + char *filename_copy = strdup(filename); 11229 + if (!filename_copy) return; 11230 + 11231 + char *last_slash = strrchr(filename_copy, '/'); 11232 + char *scheme_end = strstr(filename_copy, "://"); 11233 + 11234 + if ((is_builtin && last_slash) || (!is_builtin && last_slash && scheme_end && last_slash > scheme_end + 2)) { 11235 + *last_slash = '\0'; 11236 + ant_value_t dirname_val = js_mkstr(js, filename_copy, strlen(filename_copy)); 11237 + if (!is_err(dirname_val)) js_setprop(js, import_meta, js_mkstr(js, "dirname", 7), dirname_val); 11238 + } 11239 + 11240 + free(filename_copy); 11241 + } 11242 + 11243 + static inline void js_set_import_meta_path_dirname( 11244 + ant_t *js, 11245 + ant_value_t import_meta, 11246 + const char *filename 11247 + ) { 11248 + char *filename_copy = strdup(filename); 11249 + if (!filename_copy) return; 11250 + 11251 + char *dir = dirname(filename_copy); 11252 + if (dir) { 11253 + ant_value_t dirname_val = js_mkstr(js, dir, strlen(dir)); 11254 + if (!is_err(dirname_val)) js_setprop(js, import_meta, js_mkstr(js, "dirname", 7), dirname_val); 11255 + } 11256 + 11257 + free(filename_copy); 11217 11258 } 11218 11259 11219 11260 ant_value_t js_create_import_meta(ant_t *js, const char *filename, bool is_main) { ··· 11221 11262 11222 11263 ant_value_t import_meta = mkobj(js, 0); 11223 11264 if (is_err(import_meta)) return import_meta; 11265 + 11224 11266 bool is_url = esm_is_url(filename); 11267 + bool is_builtin = esm_is_builtin_specifier(filename); 11225 11268 11226 - ant_value_t url_val = is_url ? js_mkstr(js, filename, strlen(filename)) : js_esm_make_file_url(js, filename); 11269 + ant_value_t url_val = (is_url || is_builtin) 11270 + ? js_mkstr(js, filename, strlen(filename)) 11271 + : js_esm_make_file_url(js, filename); 11272 + 11227 11273 if (!is_err(url_val)) js_setprop(js, import_meta, js_mkstr(js, "url", 3), url_val); 11228 11274 11229 11275 ant_value_t filename_val = js_mkstr(js, filename, strlen(filename)); 11230 11276 if (!is_err(filename_val)) js_setprop(js, import_meta, js_mkstr(js, "filename", 8), filename_val); 11231 11277 11232 - if (is_url) { 11233 - char *filename_copy = strdup(filename); 11234 - if (filename_copy) { 11235 - char *last_slash = strrchr(filename_copy, '/'); 11236 - char *scheme_end = strstr(filename_copy, "://"); 11237 - if (last_slash && scheme_end && last_slash > scheme_end + 2) { 11238 - *last_slash = '\0'; 11239 - ant_value_t dirname_val = js_mkstr(js, filename_copy, strlen(filename_copy)); 11240 - if (!is_err(dirname_val)) js_setprop(js, import_meta, js_mkstr(js, "dirname", 7), dirname_val); 11241 - } 11242 - free(filename_copy); 11243 - } 11244 - } else { 11245 - char *filename_copy = strdup(filename); 11246 - if (filename_copy) { 11247 - char *dir = dirname(filename_copy); 11248 - if (dir) { 11249 - ant_value_t dirname_val = js_mkstr(js, dir, strlen(dir)); 11250 - if (!is_err(dirname_val)) js_setprop(js, import_meta, js_mkstr(js, "dirname", 7), dirname_val); 11251 - } 11252 - free(filename_copy); 11253 - } 11254 - } 11278 + if (is_url || is_builtin) js_set_import_meta_special_dirname(js, import_meta, filename, is_builtin); 11279 + else js_set_import_meta_path_dirname(js, import_meta, filename); 11255 11280 11256 11281 js_setprop(js, import_meta, js_mkstr(js, "main", 4), is_main ? js_true : js_false); 11257 11282 ant_value_t resolve_fn = js_mkfun(builtin_import_meta_resolve); 11258 11283 js_setprop(js, import_meta, js_mkstr(js, "resolve", 7), resolve_fn); 11284 + 11259 11285 return import_meta; 11260 11286 } 11261 11287
+409
src/builtins/node/http.cjs
··· 1 + const net = require('node:net'); 2 + const httpMetadata = require('ant:internal/http_metadata'); 3 + const httpParser = require('ant:internal/http_parser'); 4 + const httpWriter = require('ant:internal/http_writer'); 5 + 6 + const METHODS = httpMetadata.METHODS; 7 + const STATUS_CODES = httpMetadata.STATUS_CODES; 8 + 9 + function createHeadersObject() { 10 + return Object.create(null); 11 + } 12 + 13 + function normalizeHeaderName(name) { 14 + return String(name).toLowerCase(); 15 + } 16 + 17 + function appendHeaderValue(headers, key, value) { 18 + if (key === 'set-cookie') { 19 + if (!headers[key]) headers[key] = []; 20 + headers[key].push(value); 21 + return; 22 + } 23 + 24 + if (headers[key] === undefined) { 25 + headers[key] = value; 26 + return; 27 + } 28 + 29 + if (key === 'cookie') headers[key] += '; ' + value; 30 + else headers[key] += ', ' + value; 31 + } 32 + 33 + function buildHeaders(rawHeaders) { 34 + const headers = createHeadersObject(); 35 + let i = 0; 36 + 37 + for (i = 0; i + 1 < rawHeaders.length; i += 2) { 38 + appendHeaderValue(headers, normalizeHeaderName(rawHeaders[i]), String(rawHeaders[i + 1])); 39 + } 40 + 41 + return headers; 42 + } 43 + 44 + function bufferFrom(value, encoding) { 45 + if (value === undefined || value === null) return Buffer.alloc(0); 46 + if (Buffer.isBuffer(value)) return value; 47 + if (value && typeof value === 'object' && typeof value.byteLength === 'number') return Buffer.from(value); 48 + if (typeof value === 'string') return Buffer.from(value, encoding || 'utf8'); 49 + return Buffer.from(String(value), encoding || 'utf8'); 50 + } 51 + 52 + function appendRawHeader(rawHeaders, name, value) { 53 + if (Array.isArray(value)) { 54 + value.forEach(item => rawHeaders.push(name, String(item))); 55 + return; 56 + } 57 + 58 + rawHeaders.push(name, String(value)); 59 + } 60 + 61 + function hasResponseBody(method, statusCode) { 62 + if (method === 'HEAD') return false; 63 + if (statusCode === 204 || statusCode === 304) return false; 64 + return true; 65 + } 66 + 67 + function applyHeaderObject(target, headers) { 68 + if (!headers) return; 69 + 70 + if (Array.isArray(headers)) { 71 + for (let i = 0; i + 1 < headers.length; i += 2) { 72 + target.setHeader(headers[i], headers[i + 1]); 73 + } 74 + return; 75 + } 76 + 77 + Object.keys(headers).forEach(name => { 78 + target.setHeader(name, headers[name]); 79 + }); 80 + } 81 + 82 + function makeSocketState(server, socket) { 83 + return { 84 + server, 85 + socket, 86 + buffered: Buffer.alloc(0), 87 + activeResponse: null, 88 + closed: false 89 + }; 90 + } 91 + 92 + function appendSocketChunk(buffered, chunk) { 93 + const next = bufferFrom(chunk); 94 + if (buffered.length === 0) return next; 95 + return Buffer.concat([buffered, next]); 96 + } 97 + 98 + function handleClientError(server, socket, error) { 99 + if (server.listenerCount('clientError') > 0) { 100 + server.emit('clientError', error, socket); 101 + return; 102 + } 103 + 104 + if (socket && !socket.destroyed) { 105 + socket.end(httpWriter.writeBasicResponse(400, 'Bad Request', 'text/plain;charset=UTF-8', 'Bad Request', false)); 106 + } 107 + } 108 + 109 + class IncomingMessage extends EventEmitter { 110 + constructor(socket, parsed) { 111 + super(); 112 + this.socket = socket; 113 + this.connection = socket; 114 + this.method = parsed.method; 115 + this.url = parsed.target; 116 + this.headers = buildHeaders(parsed.rawHeaders || []); 117 + this.rawHeaders = (parsed.rawHeaders || []).slice(); 118 + this.httpVersion = parsed.httpVersion; 119 + this.httpVersionMajor = parsed.httpVersionMajor; 120 + this.httpVersionMinor = parsed.httpVersionMinor; 121 + this._keepAlive = !!parsed.keepAlive; 122 + this.complete = false; 123 + this.aborted = false; 124 + this.destroyed = false; 125 + this.readableEnded = false; 126 + this._body = bufferFrom(parsed.body); 127 + } 128 + 129 + _deliverBody() { 130 + if (this.destroyed || this.complete) return; 131 + 132 + if (this._body.length > 0) this.emit('data', this._body); 133 + this.complete = true; 134 + this.readableEnded = true; 135 + this.emit('end'); 136 + this.emit('close'); 137 + } 138 + 139 + destroy(error) { 140 + if (this.destroyed) return this; 141 + this.destroyed = true; 142 + if (error) this.emit('error', error); 143 + if (this.socket && !this.socket.destroyed) this.socket.destroy(error); 144 + return this; 145 + } 146 + 147 + setTimeout(msecs, callback) { 148 + if (this.socket && this.socket.setTimeout) this.socket.setTimeout(msecs, callback); 149 + return this; 150 + } 151 + } 152 + 153 + class ServerResponse extends EventEmitter { 154 + constructor(req, socket, socketState) { 155 + super(); 156 + this.req = req; 157 + this.socket = socket; 158 + this.connection = socket; 159 + this.statusCode = 200; 160 + this.statusMessage = undefined; 161 + this.sendDate = true; 162 + this.strictContentLength = false; 163 + this.headersSent = false; 164 + this.writableEnded = false; 165 + this.writableFinished = false; 166 + this.finished = false; 167 + this._headers = createHeadersObject(); 168 + this._headerNames = createHeadersObject(); 169 + this._socketState = socketState; 170 + this._streaming = false; 171 + } 172 + 173 + setHeader(name, value) { 174 + const headerName = String(name); 175 + const key = normalizeHeaderName(headerName); 176 + this._headers[key] = value; 177 + this._headerNames[key] = headerName; 178 + return this; 179 + } 180 + 181 + getHeader(name) { 182 + return this._headers[normalizeHeaderName(name)]; 183 + } 184 + 185 + getHeaders() { 186 + const copy = createHeadersObject(); 187 + Object.keys(this._headers).forEach(key => { 188 + copy[key] = this._headers[key]; 189 + }); 190 + return copy; 191 + } 192 + 193 + getHeaderNames() { 194 + return Object.keys(this._headers); 195 + } 196 + 197 + hasHeader(name) { 198 + return this._headers[normalizeHeaderName(name)] !== undefined; 199 + } 200 + 201 + removeHeader(name) { 202 + const key = normalizeHeaderName(name); 203 + delete this._headers[key]; 204 + delete this._headerNames[key]; 205 + return this; 206 + } 207 + 208 + writeHead(statusCode, statusMessage, headers) { 209 + this.statusCode = statusCode | 0; 210 + 211 + if (typeof statusMessage === 'string') { 212 + this.statusMessage = statusMessage; 213 + applyHeaderObject(this, headers); 214 + } else { 215 + applyHeaderObject(this, statusMessage); 216 + } 217 + 218 + return this; 219 + } 220 + 221 + _rawHeaders() { 222 + const rawHeaders = []; 223 + 224 + if (this.sendDate && !this.hasHeader('date')) { 225 + this.setHeader('Date', new Date().toUTCString()); 226 + } 227 + 228 + Object.keys(this._headers).forEach(key => { 229 + const name = this._headerNames[key] || key; 230 + appendRawHeader(rawHeaders, name, this._headers[key]); 231 + }); 232 + 233 + return rawHeaders; 234 + } 235 + 236 + _shouldKeepAlive() { 237 + const connection = this.getHeader('connection'); 238 + if (typeof connection === 'string') { 239 + if (connection.toLowerCase() === 'close') return false; 240 + if (connection.toLowerCase() === 'keep-alive') return true; 241 + } 242 + 243 + return !!(this.req && this.req._keepAlive); 244 + } 245 + 246 + _writeHead(bodyIsStream, bodySize) { 247 + if (this.headersSent) return; 248 + 249 + const statusText = this.statusMessage || STATUS_CODES[this.statusCode] || httpWriter.defaultStatusText(this.statusCode); 250 + const head = httpWriter.writeHead(this.statusCode, statusText, this._rawHeaders(), bodyIsStream, bodySize, this._shouldKeepAlive()); 251 + 252 + this.headersSent = true; 253 + this.socket.write(head); 254 + } 255 + 256 + write(chunk, encoding, callback) { 257 + const body = hasResponseBody(this.req && this.req.method, this.statusCode) 258 + ? bufferFrom(chunk, typeof encoding === 'string' ? encoding : undefined) 259 + : Buffer.alloc(0); 260 + 261 + if (typeof encoding === 'function') callback = encoding; 262 + if (!this.headersSent) this._writeHead(true, 0); 263 + this._streaming = true; 264 + 265 + if (body.length > 0) this.socket.write(httpWriter.writeChunk(body)); 266 + if (typeof callback === 'function') callback(); 267 + return true; 268 + } 269 + 270 + end(chunk, encoding, callback) { 271 + let body = Buffer.alloc(0); 272 + let keepAlive = false; 273 + 274 + if (this.writableEnded) return this; 275 + if (typeof encoding === 'function') { 276 + callback = encoding; 277 + encoding = undefined; 278 + } 279 + 280 + if (hasResponseBody(this.req && this.req.method, this.statusCode)) { 281 + body = bufferFrom(chunk, typeof encoding === 'string' ? encoding : undefined); 282 + } 283 + 284 + if (this._streaming) { 285 + if (!this.headersSent) this._writeHead(true, 0); 286 + if (body.length > 0) this.socket.write(httpWriter.writeChunk(body)); 287 + this.socket.write(httpWriter.writeFinalChunk()); 288 + } else { 289 + this._writeHead(false, body.length); 290 + if (body.length > 0) this.socket.write(body); 291 + } 292 + 293 + this.writableEnded = true; 294 + this.writableFinished = true; 295 + this.finished = true; 296 + this.emit('finish'); 297 + this.emit('close'); 298 + 299 + keepAlive = this._shouldKeepAlive(); 300 + this._socketState.activeResponse = null; 301 + if (typeof callback === 'function') callback(); 302 + 303 + if (!keepAlive) this.socket.end(); 304 + else if (this._socketState.buffered.length > 0) this._socketState.server._drainSocket(this.socket, this._socketState); 305 + return this; 306 + } 307 + 308 + setTimeout(msecs, callback) { 309 + if (this.socket && this.socket.setTimeout) this.socket.setTimeout(msecs, callback); 310 + return this; 311 + } 312 + } 313 + 314 + class Server extends net.Server { 315 + constructor(options, requestListener) { 316 + const serverOptions = typeof options === 'function' ? undefined : options; 317 + const onRequest = typeof options === 'function' ? options : requestListener; 318 + 319 + super(serverOptions); 320 + this.timeout = 0; 321 + this.headersTimeout = 60000; 322 + this.requestTimeout = 300000; 323 + this.keepAliveTimeout = 5000; 324 + this.maxHeadersCount = 2000; 325 + 326 + this.on('connection', socket => { 327 + this._attachSocket(socket); 328 + }); 329 + 330 + if (typeof onRequest === 'function') this.on('request', onRequest); 331 + } 332 + 333 + _attachSocket(socket) { 334 + const state = makeSocketState(this, socket); 335 + 336 + if (this.timeout > 0 && socket.setTimeout) { 337 + socket.setTimeout(this.timeout, () => { 338 + this.emit('timeout', socket); 339 + }); 340 + } 341 + 342 + socket.on('data', chunk => { 343 + state.buffered = appendSocketChunk(state.buffered, chunk); 344 + this._drainSocket(socket, state); 345 + }); 346 + 347 + socket.on('error', error => { 348 + handleClientError(this, socket, error); 349 + }); 350 + 351 + socket.on('close', () => { 352 + state.closed = true; 353 + }); 354 + } 355 + 356 + _drainSocket(socket, state) { 357 + while (!state.closed) { 358 + let parsed = null; 359 + 360 + if (state.activeResponse && !state.activeResponse.writableEnded) return; 361 + if (state.buffered.length === 0) return; 362 + 363 + try { 364 + parsed = httpParser.parseRequest(state.buffered); 365 + } catch (error) { 366 + state.closed = true; 367 + handleClientError(this, socket, error); 368 + return; 369 + } 370 + 371 + if (parsed === null) return; 372 + if (!parsed.consumed || parsed.consumed < 0) { 373 + state.closed = true; 374 + handleClientError(this, socket, new Error('Invalid HTTP parser state')); 375 + return; 376 + } 377 + 378 + state.buffered = parsed.consumed < state.buffered.length ? state.buffered.subarray(parsed.consumed) : Buffer.alloc(0); 379 + 380 + const req = new IncomingMessage(socket, parsed); 381 + const res = new ServerResponse(req, socket, state); 382 + state.activeResponse = res; 383 + 384 + this.emit('request', req, res); 385 + req._deliverBody(); 386 + 387 + if (!res.writableEnded) return; 388 + } 389 + } 390 + 391 + setTimeout(msecs, callback) { 392 + this.timeout = msecs | 0; 393 + if (typeof callback === 'function') this.on('timeout', callback); 394 + return this; 395 + } 396 + } 397 + 398 + function createServer(options, requestListener) { 399 + return new Server(options, requestListener); 400 + } 401 + 402 + module.exports = { 403 + METHODS, 404 + STATUS_CODES, 405 + IncomingMessage, 406 + Server, 407 + ServerResponse, 408 + createServer 409 + };
+24
src/esm/builtin_bundle.c
··· 1 + #include <string.h> 2 + 3 + #include "esm/builtin_bundle.h" 4 + #include "builtin_bundle_data.h" 5 + 6 + bool esm_is_builtin_specifier(const char *specifier) { 7 + if (!specifier) return false; 8 + return 9 + strncmp(specifier, "node:", 5) == 0 || 10 + strncmp(specifier, "ant:", 4) == 0; 11 + } 12 + 13 + const ant_builtin_bundle_entry_t *esm_lookup_builtin_bundle(const char *specifier, size_t spec_len) { 14 + size_t i = 0; 15 + if (!specifier) return NULL; 16 + 17 + for (i = 0; i < ant_builtin_bundle_data_count; i++) { 18 + const ant_builtin_bundle_entry_t *entry = &ant_builtin_bundle_data[i]; 19 + size_t entry_len = strlen(entry->specifier); 20 + if (entry_len == spec_len && memcmp(entry->specifier, specifier, spec_len) == 0) return entry; 21 + } 22 + 23 + return NULL; 24 + }
+115 -26
src/esm/loader.c
··· 4 4 #include "esm/commonjs.h" 5 5 #include "esm/library.h" 6 6 #include "esm/remote.h" 7 + #include "esm/builtin_bundle.h" 7 8 8 9 #include "modules/json.h" 9 10 #include "modules/napi.h" ··· 36 37 37 38 typedef struct esm_module { 38 39 char *path; 40 + char *cache_key; 39 41 char *resolved_path; 40 42 char *url_content; 43 + 41 44 size_t url_content_len; 45 + const uint8_t *embedded_code; 46 + size_t embedded_code_len; 47 + 42 48 ant_value_t namespace_obj; 43 49 ant_value_t default_export; 44 50 ant_value_t tla_promise; 45 51 UT_hash_handle hh; 46 52 esm_module_kind_t kind; 47 53 ant_module_format_t format; 54 + 48 55 bool is_loaded; 49 56 bool is_loading; 50 57 bool has_tla; ··· 62 69 63 70 static esm_module_cache_t global_module_cache = {NULL, 0}; 64 71 static int esm_dynamic_import_depth = 0; 72 + 65 73 static char *esm_resolve_node_module(const char *specifier, const char *base_path); 74 + static char *esm_canonicalize_path(const char *path); 66 75 67 76 static char *esm_file_url_to_path(const char *specifier) { 68 77 if (!specifier || strncmp(specifier, "file:", 5) != 0) return NULL; ··· 73 82 74 83 if (*p == '\0') return NULL; 75 84 return strdup(p); 85 + } 86 + 87 + static char *esm_make_cache_key(const char *module_key) { 88 + if (!module_key) return NULL; 89 + if (esm_is_builtin_specifier(module_key)) return strdup(module_key); 90 + if (esm_is_data_url(module_key)) return strdup(module_key); 91 + if (esm_is_url(module_key)) return strdup(module_key); 92 + return esm_canonicalize_path(module_key); 76 93 } 77 94 78 95 static char *esm_get_extension(const char *path) { ··· 832 849 return canonical; 833 850 } 834 851 835 - static esm_module_t *esm_find_module(const char *resolved_path) { 836 - char *canonical_path = esm_canonicalize_path(resolved_path); 837 - if (!canonical_path) return NULL; 852 + static esm_module_t *esm_find_module(const char *module_key) { 853 + char *cache_key = esm_make_cache_key(module_key); 854 + if (!cache_key) return NULL; 838 855 839 856 esm_module_t *mod = NULL; 840 - HASH_FIND_STR(global_module_cache.modules, canonical_path, mod); 857 + HASH_FIND_STR(global_module_cache.modules, cache_key, mod); 841 858 842 - free(canonical_path); 859 + free(cache_key); 843 860 return mod; 844 861 } 845 862 846 - static esm_module_t *esm_create_module(const char *path, const char *resolved_path) { 847 - bool is_url = esm_is_url(resolved_path) || esm_is_data_url(resolved_path); 848 - char *canonical_path = is_url ? strdup(resolved_path) : esm_canonicalize_path(resolved_path); 849 - if (!canonical_path) return NULL; 863 + static esm_module_t *esm_create_module( 864 + const char *path, 865 + const char *resolved_path, 866 + const char *module_key, 867 + ant_module_format_t format, 868 + const uint8_t *embedded_code, 869 + size_t embedded_code_len 870 + ) { 871 + char *cache_key = esm_make_cache_key(module_key); 872 + if (!cache_key) return NULL; 850 873 851 874 esm_module_t *existing_mod = NULL; 852 - HASH_FIND_STR(global_module_cache.modules, canonical_path, existing_mod); 875 + HASH_FIND_STR(global_module_cache.modules, cache_key, existing_mod); 853 876 if (existing_mod) { 854 - free(canonical_path); 877 + free(cache_key); 855 878 return existing_mod; 856 879 } 857 880 858 881 esm_module_t *mod = (esm_module_t *)malloc(sizeof(esm_module_t)); 859 882 if (!mod) { 860 - free(canonical_path); 883 + free(cache_key); 861 884 return NULL; 862 885 } 863 886 864 887 *mod = (esm_module_t){ 865 888 .path = strdup(path), 866 - .resolved_path = canonical_path, 889 + .cache_key = cache_key, 890 + .resolved_path = strdup(resolved_path), 867 891 .namespace_obj = js_mkundef(), 868 892 .default_export = js_mkundef(), 869 893 .is_loaded = false, 870 894 .is_loading = false, 871 895 .kind = esm_classify_module_kind(resolved_path), 872 - .format = MODULE_EVAL_FORMAT_UNKNOWN, 896 + .format = format, 873 897 .url_content = NULL, 874 898 .url_content_len = 0, 899 + .embedded_code = embedded_code, 900 + .embedded_code_len = embedded_code_len, 875 901 .tla_promise = js_mkundef(), 876 902 .has_tla = false, 877 903 }; 878 904 879 - HASH_ADD_STR(global_module_cache.modules, resolved_path, mod); 905 + if (!mod->path || !mod->resolved_path) { 906 + free(mod->path); 907 + free(mod->cache_key); 908 + free(mod->resolved_path); 909 + free(mod); 910 + return NULL; 911 + } 912 + 913 + HASH_ADD_STR(global_module_cache.modules, cache_key, mod); 880 914 global_module_cache.count++; 881 915 882 916 return mod; ··· 887 921 HASH_ITER(hh, global_module_cache.modules, current, tmp) { 888 922 HASH_DEL(global_module_cache.modules, current); 889 923 if (current->path) free(current->path); 924 + if (current->cache_key) free(current->cache_key); 890 925 if (current->resolved_path) free(current->resolved_path); 891 926 if (current->url_content) free(current->url_content); 892 927 free(current); ··· 997 1032 char *content = NULL; 998 1033 size_t size = 0; 999 1034 1000 - if (mod->kind == ESM_MODULE_KIND_URL && esm_is_data_url(mod->resolved_path)) { 1035 + if (mod->embedded_code) { 1036 + content = (char *)malloc(mod->embedded_code_len + 1); 1037 + if (!content) { 1038 + mod->is_loading = false; 1039 + return js_mkerr(js, "OOM loading bundled module"); 1040 + } 1041 + memcpy(content, mod->embedded_code, mod->embedded_code_len); 1042 + size = mod->embedded_code_len; 1043 + } else if (mod->kind == ESM_MODULE_KIND_URL && esm_is_data_url(mod->resolved_path)) { 1001 1044 content = esm_parse_data_url(mod->resolved_path, &size); 1002 1045 if (!content) { 1003 1046 mod->is_loading = false; ··· 1033 1076 1034 1077 size_t js_len = size; 1035 1078 const char *strip_detail = NULL; 1036 - 1079 + 1080 + if (!mod->embedded_code) { 1037 1081 int strip_result = strip_typescript_inplace( 1038 1082 &content, size, mod->resolved_path, &js_len, &strip_detail 1039 1083 ); 1040 - 1084 + 1041 1085 if (strip_result < 0) { 1042 1086 ant_value_t err = js_mkerr( 1043 1087 js, "TypeScript error: strip failed (%d): %s", 1044 1088 strip_result, strip_detail 1045 1089 ); 1046 - 1047 - free(content); 1090 + free(content); 1048 1091 mod->is_loading = false; 1049 - 1050 1092 return err; 1051 - } 1093 + }} 1052 1094 1053 1095 char *js_code = content; 1054 1096 ant_value_t ns = js_mkobj(js); ··· 1099 1141 return esm_complete_namespace_module(js, mod, ns); 1100 1142 } 1101 1143 1102 - static ant_value_t esm_get_or_load(ant_t *js, const char *specifier, const char *resolved_path) { 1103 - esm_module_t *mod = esm_find_module(resolved_path); 1144 + static ant_value_t esm_get_or_load( 1145 + ant_t *js, 1146 + const char *specifier, 1147 + const char *resolved_path, 1148 + const char *module_key, 1149 + ant_module_format_t format, 1150 + const uint8_t *embedded_code, 1151 + size_t embedded_code_len 1152 + ) { 1153 + esm_module_t *mod = esm_find_module(module_key); 1104 1154 if (!mod) { 1105 - mod = esm_create_module(specifier, resolved_path); 1155 + mod = esm_create_module( 1156 + specifier, 1157 + resolved_path, 1158 + module_key, 1159 + format, 1160 + embedded_code, 1161 + embedded_code_len 1162 + ); 1106 1163 if (!mod) return js_mkerr(js, "Cannot create module"); 1107 1164 } 1108 1165 return esm_load_module(js, mod); ··· 1119 1176 size_t spec_len, 1120 1177 const char *base_path 1121 1178 ) { 1179 + const ant_builtin_bundle_entry_t *bundle = NULL; 1122 1180 char *spec_copy = strndup(specifier, spec_len); 1123 1181 if (!spec_copy) return js_mkerr(js, "oom"); 1124 1182 ··· 1129 1187 spec_len = strlen(spec_copy); 1130 1188 } 1131 1189 1190 + bundle = esm_lookup_builtin_bundle(spec_copy, spec_len); 1191 + if (bundle) { 1192 + ant_value_t ns = esm_get_or_load( 1193 + js, 1194 + spec_copy, 1195 + bundle->source_name, 1196 + bundle->specifier, 1197 + bundle->format, 1198 + bundle->code, 1199 + bundle->code_len 1200 + ); 1201 + free(spec_copy); 1202 + return ns; 1203 + } 1204 + 1132 1205 bool loaded = false; 1133 1206 ant_value_t lib = js_esm_load_registered_library(js, spec_copy, spec_len, &loaded); 1134 1207 if (loaded) { ··· 1144 1217 return err; 1145 1218 } 1146 1219 1147 - ant_value_t ns = esm_get_or_load(js, spec_copy, resolved_path); 1220 + ant_value_t ns = esm_get_or_load( 1221 + js, 1222 + spec_copy, 1223 + resolved_path, 1224 + resolved_path, 1225 + MODULE_EVAL_FORMAT_UNKNOWN, 1226 + NULL, 1227 + 0 1228 + ); 1148 1229 free(resolved_path); 1149 1230 free(spec_copy); 1150 1231 return ns; ··· 1209 1290 }} 1210 1291 1211 1292 ant_value_t js_esm_resolve_specifier(ant_t *js, ant_value_t specifier, const char *base_path) { 1293 + const ant_builtin_bundle_entry_t *bundle = NULL; 1212 1294 if (vtype(specifier) != T_STR) { 1213 1295 return js_mkerr(js, "import.meta.resolve() requires a string specifier"); 1214 1296 } ··· 1218 1300 const char *spec_str = (const char *)(uintptr_t)(spec_off); 1219 1301 char *spec_copy = strndup(spec_str, (size_t)spec_len); 1220 1302 if (!spec_copy) return js_mkerr(js, "oom"); 1303 + 1304 + bundle = esm_lookup_builtin_bundle(spec_copy, (size_t)spec_len); 1305 + if (bundle) { 1306 + ant_value_t result = js_mkstr(js, bundle->specifier, strlen(bundle->specifier)); 1307 + free(spec_copy); 1308 + return result; 1309 + } 1221 1310 1222 1311 if (!base_path || !base_path[0]) base_path = esm_default_base_path(js); 1223 1312 char *resolved_path = esm_resolve(spec_copy, base_path, esm_resolve_path);
+6
src/http/http1_parser.c
··· 150 150 return ANT_HTTP1_PARSE_INCOMPLETE; 151 151 } 152 152 153 + if (out->consumed_len == 0) ctx.req.consumed_len = len; 154 + else ctx.req.consumed_len = out->consumed_len; 155 + 153 156 ctx.req.method = ant_http1_buffer_take(&ctx.method, NULL); 154 157 ctx.req.target = ant_http1_buffer_take(&ctx.target, NULL); 155 158 ctx.req.body = (uint8_t *)ant_http1_buffer_take(&ctx.body, &ctx.req.body_len); ··· 256 259 257 260 if (!cp->ctx.message_complete) 258 261 return ANT_HTTP1_PARSE_INCOMPLETE; 262 + 263 + if (consumed_out && *consumed_out == 0) *consumed_out = len; 264 + cp->ctx.req.consumed_len = consumed_out ? *consumed_out : len; 259 265 260 266 cp->ctx.req.method = ant_http1_buffer_take(&cp->ctx.method, NULL); 261 267 cp->ctx.req.target = ant_http1_buffer_take(&cp->ctx.target, NULL);
+2
src/main.c
··· 70 70 #include "modules/util.h" 71 71 #include "modules/async_hooks.h" 72 72 #include "modules/net.h" 73 + #include "modules/http_metadata.h" 73 74 #include "modules/http_parser.h" 74 75 #include "modules/http_writer.h" 75 76 #include "modules/dns.h" ··· 616 617 617 618 ant_register_library(internal_http_parser_library, "ant:internal/http_parser", NULL); 618 619 ant_register_library(internal_http_writer_library, "ant:internal/http_writer", NULL); 620 + ant_register_library(internal_http_metadata_library, "ant:internal/http_metadata", NULL); 619 621 620 622 ant_standard_library("util", util_library); 621 623 ant_standard_library("net", net_library);
+120
src/modules/http_metadata.c
··· 1 + #include <compat.h> // IWYU pragma: keep 2 + 3 + #include <stdio.h> 4 + #include <string.h> 5 + 6 + #include "ant.h" 7 + #include "modules/http_metadata.h" 8 + 9 + static const char *const http_methods[] = { 10 + "ACL", 11 + "BIND", 12 + "CHECKOUT", 13 + "CONNECT", 14 + "COPY", 15 + "DELETE", 16 + "GET", 17 + "HEAD", 18 + "LINK", 19 + "LOCK", 20 + "M-SEARCH", 21 + "MERGE", 22 + "MKACTIVITY", 23 + "MKCALENDAR", 24 + "MKCOL", 25 + "MOVE", 26 + "NOTIFY", 27 + "OPTIONS", 28 + "PATCH", 29 + "POST", 30 + "PROPFIND", 31 + "PROPPATCH", 32 + "PURGE", 33 + "PUT", 34 + "QUERY", 35 + "REBIND", 36 + "REPORT", 37 + "SEARCH", 38 + "SOURCE", 39 + "SUBSCRIBE", 40 + "TRACE", 41 + "UNBIND", 42 + "UNLINK", 43 + "UNLOCK", 44 + "UNSUBSCRIBE" 45 + }; 46 + 47 + typedef struct { 48 + int code; 49 + const char *text; 50 + } http_status_entry_t; 51 + 52 + static const http_status_entry_t http_status_codes[] = { 53 + {200, "OK"}, 54 + {201, "Created"}, 55 + {202, "Accepted"}, 56 + {204, "No Content"}, 57 + {301, "Moved Permanently"}, 58 + {302, "Found"}, 59 + {303, "See Other"}, 60 + {304, "Not Modified"}, 61 + {307, "Temporary Redirect"}, 62 + {308, "Permanent Redirect"}, 63 + {400, "Bad Request"}, 64 + {401, "Unauthorized"}, 65 + {403, "Forbidden"}, 66 + {404, "Not Found"}, 67 + {405, "Method Not Allowed"}, 68 + {408, "Request Timeout"}, 69 + {409, "Conflict"}, 70 + {410, "Gone"}, 71 + {413, "Payload Too Large"}, 72 + {415, "Unsupported Media Type"}, 73 + {421, "Misdirected Request"}, 74 + {429, "Too Many Requests"}, 75 + {500, "Internal Server Error"}, 76 + {501, "Not Implemented"}, 77 + {502, "Bad Gateway"}, 78 + {503, "Service Unavailable"}, 79 + {504, "Gateway Timeout"} 80 + }; 81 + 82 + static ant_value_t http_metadata_make_methods(ant_t *js) { 83 + ant_value_t methods = js_mkarr(js); 84 + size_t count = sizeof(http_methods) / sizeof(http_methods[0]); 85 + 86 + for (size_t i = 0; i < count; i++) { 87 + js_arr_push(js, methods, js_mkstr(js, http_methods[i], strlen(http_methods[i]))); 88 + } 89 + 90 + return methods; 91 + } 92 + 93 + static ant_value_t http_metadata_make_status_codes(ant_t *js) { 94 + ant_value_t status_codes = js_mkobj(js); 95 + 96 + size_t count = sizeof(http_status_codes) / sizeof(http_status_codes[0]); 97 + char code_buf[16]; 98 + 99 + for (size_t i = 0; i < count; i++) { 100 + int code = http_status_codes[i].code; 101 + size_t code_len = (size_t)snprintf(code_buf, sizeof(code_buf), "%d", code); 102 + 103 + js_setprop( 104 + js, status_codes, 105 + js_mkstr(js, code_buf, code_len), 106 + js_mkstr(js, http_status_codes[i].text, strlen(http_status_codes[i].text)) 107 + ); 108 + } 109 + 110 + return status_codes; 111 + } 112 + 113 + ant_value_t internal_http_metadata_library(ant_t *js) { 114 + ant_value_t lib = js_mkobj(js); 115 + 116 + js_set(js, lib, "METHODS", http_metadata_make_methods(js)); 117 + js_set(js, lib, "STATUS_CODES", http_metadata_make_status_codes(js)); 118 + 119 + return lib; 120 + }
+130
src/tools/gen_builtin_bundle.js
··· 1 + import * as esbuild from 'esbuild'; 2 + import path from 'node:path'; 3 + import { writeFileSync } from 'node:fs'; 4 + 5 + function cString(input) { 6 + return JSON.stringify(input); 7 + } 8 + 9 + function toSpecifier(rootDir, filePath) { 10 + const relativePath = path.relative(rootDir, filePath).replaceAll('\\', '/'); 11 + const withoutExt = relativePath.replace(/\.(cts|mts|ts|cjs|mjs|js)$/u, ''); 12 + 13 + if (withoutExt.startsWith('node/')) { 14 + return `node:${withoutExt.slice('node/'.length)}`; 15 + } 16 + 17 + if (withoutExt.startsWith('ant/')) { 18 + return `ant:${withoutExt.slice('ant/'.length)}`; 19 + } 20 + 21 + throw new Error(`Unsupported builtin module path: ${relativePath}`); 22 + } 23 + 24 + function toFormat(filePath) { 25 + if (/\.(cts|cjs)$/u.test(filePath)) return 'MODULE_EVAL_FORMAT_CJS'; 26 + if (/\.(mts|mjs)$/u.test(filePath)) return 'MODULE_EVAL_FORMAT_ESM'; 27 + return 'MODULE_EVAL_FORMAT_UNKNOWN'; 28 + } 29 + 30 + async function bundleBuiltin(entryPath, format) { 31 + const output = await esbuild.build({ 32 + entryPoints: [entryPath], 33 + bundle: true, 34 + write: false, 35 + minify: true, 36 + platform: 'neutral', 37 + format: format === 'MODULE_EVAL_FORMAT_ESM' ? 'esm' : 'cjs', 38 + target: ['es2020'], 39 + plugins: [ 40 + { 41 + name: 'builtin-externals', 42 + setup(build) { 43 + build.onResolve({ filter: /^(node:|ant:)/ }, args => ({ 44 + path: args.path, 45 + external: true 46 + })); 47 + } 48 + } 49 + ] 50 + }); 51 + 52 + if (output.outputFiles.length !== 1) { 53 + throw new Error(`Expected exactly one bundled output for ${entryPath}`); 54 + } 55 + 56 + return output.outputFiles[0].contents; 57 + } 58 + 59 + function generateHeader(rootDir, bundles) { 60 + const lines = []; 61 + 62 + lines.push('/* Auto-generated builtin bundle data. DO NOT EDIT. */'); 63 + lines.push(''); 64 + lines.push('#ifndef ANT_BUILTIN_BUNDLE_DATA_H'); 65 + lines.push('#define ANT_BUILTIN_BUNDLE_DATA_H'); 66 + lines.push(''); 67 + lines.push('#include <stddef.h>'); 68 + lines.push('#include <stdint.h>'); 69 + lines.push(''); 70 + 71 + bundles.forEach((bundle, index) => { 72 + const byteLines = []; 73 + for (let i = 0; i < bundle.bytes.length; i += 16) { 74 + byteLines.push(' ' + Array.from(bundle.bytes.slice(i, i + 16)).join(', ')); 75 + } 76 + 77 + lines.push(`/* ${bundle.specifier} <- ${path.relative(rootDir, bundle.entryPath).replaceAll('\\', '/')} */`); 78 + lines.push(`static const uint8_t ant_builtin_bundle_${index}[] = {`); 79 + lines.push(byteLines.join(',\n')); 80 + lines.push('};'); 81 + lines.push(''); 82 + }); 83 + 84 + lines.push('static const ant_builtin_bundle_entry_t ant_builtin_bundle_data[] = {'); 85 + bundles.forEach((bundle, index) => { 86 + lines.push( 87 + ` { ${cString(bundle.specifier)}, ${cString(bundle.specifier)}, ant_builtin_bundle_${index}, sizeof(ant_builtin_bundle_${index}), ${bundle.format} },` 88 + ); 89 + }); 90 + lines.push('};'); 91 + lines.push(''); 92 + lines.push('static const size_t ant_builtin_bundle_data_count ='); 93 + lines.push(' sizeof(ant_builtin_bundle_data) / sizeof(ant_builtin_bundle_data[0]);'); 94 + lines.push(''); 95 + lines.push('#endif'); 96 + 97 + return lines.join('\n') + '\n'; 98 + } 99 + 100 + async function main() { 101 + const args = process.argv.slice(2); 102 + if (args.length < 3) { 103 + console.error(`Usage: ${process.argv[1]} <builtins-root> <output.h> <entry...>`); 104 + process.exit(1); 105 + } 106 + 107 + const [builtinsRoot, outputFile, ...entryFiles] = args; 108 + const bundles = []; 109 + 110 + for (const entryPath of entryFiles) { 111 + const specifier = toSpecifier(builtinsRoot, entryPath); 112 + const format = toFormat(entryPath); 113 + const bytes = await bundleBuiltin(entryPath, format); 114 + bundles.push({ entryPath, specifier, format, bytes }); 115 + } 116 + 117 + const header = generateHeader(builtinsRoot, bundles); 118 + const totalBundledBytes = bundles.reduce((sum, bundle) => sum + bundle.bytes.length, 0); 119 + 120 + writeFileSync(outputFile, header); 121 + 122 + console.log(`builtin bundle generated successfully: ${outputFile}`); 123 + console.log(` bundled size: ${totalBundledBytes} bytes`); 124 + console.log(` modules: ${bundles.length}`); 125 + } 126 + 127 + main().catch(error => { 128 + console.error(error); 129 + process.exit(1); 130 + });