1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-21 04:52:26 -05:00

Compare commits

...

6 commits

Author SHA1 Message Date
snek
65ee67ae8e
Merge 74d705b35a into 0d3d4f5466 2025-01-20 23:27:15 +01:00
David Sherret
0d3d4f5466
fix(install/global): remove importMap field from specified config file (#27744)
Closes https://github.com/denoland/deno/issues/27734
2025-01-20 16:50:34 -05:00
Luca Casonato
5e9b3712de
feat(unstable): add basic support for otel trace links (#27727)
Currently only links with no attributes.
2025-01-20 15:39:59 +01:00
Bartek Iwańczuk
395628026f
fix(ext/os): pass SignalState to web worker (#27741)
Closes https://github.com/denoland/deno/issues/27717

Made a mistake in https://github.com/denoland/deno/pull/27655 and
didn't add the `SignalStore` for web worker.
2025-01-20 19:43:15 +05:30
Divy Srivastava
4f27d7cdc0
fix(ext/node): GCM auth tag check on DechiperIv#final (#27733) 2025-01-20 18:16:44 +05:30
snek
74d705b35a
feat(ext/node): support http information 2025-01-14 22:24:17 +01:00
23 changed files with 496 additions and 65 deletions

33
Cargo.lock generated
View file

@ -856,7 +856,7 @@ dependencies = [
"hickory-server", "hickory-server",
"http 1.1.0", "http 1.1.0",
"http-body-util", "http-body-util",
"hyper 1.4.1", "hyper 1.5.2",
"hyper-util", "hyper-util",
"nix", "nix",
"once_cell", "once_cell",
@ -1702,7 +1702,7 @@ dependencies = [
"hickory-resolver", "hickory-resolver",
"http 1.1.0", "http 1.1.0",
"http-body-util", "http-body-util",
"hyper 1.4.1", "hyper 1.5.2",
"hyper-rustls", "hyper-rustls",
"hyper-util", "hyper-util",
"ipnet", "ipnet",
@ -1820,7 +1820,7 @@ dependencies = [
"http-body-util", "http-body-util",
"httparse", "httparse",
"hyper 0.14.28", "hyper 0.14.28",
"hyper 1.4.1", "hyper 1.5.2",
"hyper-util", "hyper-util",
"itertools 0.10.5", "itertools 0.10.5",
"memmem", "memmem",
@ -2057,7 +2057,7 @@ dependencies = [
"hkdf", "hkdf",
"http 1.1.0", "http 1.1.0",
"http-body-util", "http-body-util",
"hyper 1.4.1", "hyper 1.5.2",
"hyper-util", "hyper-util",
"idna", "idna",
"indexmap 2.3.0", "indexmap 2.3.0",
@ -2345,7 +2345,7 @@ dependencies = [
"http 1.1.0", "http 1.1.0",
"http-body-util", "http-body-util",
"hyper 0.14.28", "hyper 0.14.28",
"hyper 1.4.1", "hyper 1.5.2",
"hyper-util", "hyper-util",
"libc", "libc",
"log", "log",
@ -2422,7 +2422,7 @@ dependencies = [
"deno_core", "deno_core",
"deno_error", "deno_error",
"http-body-util", "http-body-util",
"hyper 1.4.1", "hyper 1.5.2",
"hyper-util", "hyper-util",
"log", "log",
"once_cell", "once_cell",
@ -2580,7 +2580,7 @@ dependencies = [
"h2 0.4.4", "h2 0.4.4",
"http 1.1.0", "http 1.1.0",
"http-body-util", "http-body-util",
"hyper 1.4.1", "hyper 1.5.2",
"hyper-util", "hyper-util",
"once_cell", "once_cell",
"rustls-tokio-stream", "rustls-tokio-stream",
@ -3325,7 +3325,7 @@ dependencies = [
"base64 0.21.7", "base64 0.21.7",
"bytes", "bytes",
"http-body-util", "http-body-util",
"hyper 1.4.1", "hyper 1.5.2",
"hyper-util", "hyper-util",
"pin-project", "pin-project",
"rand", "rand",
@ -4210,9 +4210,8 @@ dependencies = [
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "1.4.1" version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/hyperium/hyper.git?rev=15227a3#15227a3006ae8b402394904ed4e1b86233f1bd65"
checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
@ -4237,7 +4236,7 @@ checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155"
dependencies = [ dependencies = [
"futures-util", "futures-util",
"http 1.1.0", "http 1.1.0",
"hyper 1.4.1", "hyper 1.5.2",
"hyper-util", "hyper-util",
"rustls", "rustls",
"rustls-pki-types", "rustls-pki-types",
@ -4253,7 +4252,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
dependencies = [ dependencies = [
"hyper 1.4.1", "hyper 1.5.2",
"hyper-util", "hyper-util",
"pin-project-lite", "pin-project-lite",
"tokio", "tokio",
@ -4271,7 +4270,7 @@ dependencies = [
"futures-util", "futures-util",
"http 1.1.0", "http 1.1.0",
"http-body 1.0.0", "http-body 1.0.0",
"hyper 1.4.1", "hyper 1.5.2",
"pin-project-lite", "pin-project-lite",
"socket2", "socket2",
"tokio", "tokio",
@ -6433,7 +6432,7 @@ dependencies = [
"http 1.1.0", "http 1.1.0",
"http-body 1.0.0", "http-body 1.0.0",
"http-body-util", "http-body-util",
"hyper 1.4.1", "hyper 1.5.2",
"hyper-rustls", "hyper-rustls",
"hyper-util", "hyper-util",
"ipnet", "ipnet",
@ -7973,7 +7972,7 @@ dependencies = [
"h2 0.4.4", "h2 0.4.4",
"http 1.1.0", "http 1.1.0",
"http-body-util", "http-body-util",
"hyper 1.4.1", "hyper 1.5.2",
"hyper-util", "hyper-util",
"jsonc-parser", "jsonc-parser",
"lazy-regex", "lazy-regex",
@ -8268,7 +8267,7 @@ dependencies = [
"http 1.1.0", "http 1.1.0",
"http-body 1.0.0", "http-body 1.0.0",
"http-body-util", "http-body-util",
"hyper 1.4.1", "hyper 1.5.2",
"hyper-timeout", "hyper-timeout",
"hyper-util", "hyper-util",
"percent-encoding", "percent-encoding",

View file

@ -350,3 +350,6 @@ opt-level = 3
opt-level = 3 opt-level = 3
[profile.release.package.zstd-sys] [profile.release.package.zstd-sys]
opt-level = 3 opt-level = 3
[patch.crates-io]
hyper = { git = "https://github.com/hyperium/hyper.git", rev = "15227a3" }

View file

@ -115,10 +115,16 @@ exec deno {} "$@"
Ok(()) Ok(())
} }
fn get_installer_root() -> Result<PathBuf, io::Error> { fn get_installer_root() -> Result<PathBuf, AnyError> {
if let Ok(env_dir) = env::var("DENO_INSTALL_ROOT") { if let Some(env_dir) = env::var_os("DENO_INSTALL_ROOT") {
if !env_dir.is_empty() { if !env_dir.is_empty() {
return canonicalize_path_maybe_not_exists(&PathBuf::from(env_dir)); let env_dir = PathBuf::from(env_dir);
return canonicalize_path_maybe_not_exists(&env_dir).with_context(|| {
format!(
"Canonicalizing DENO_INSTALL_ROOT ('{}').",
env_dir.display()
)
});
} }
} }
// Note: on Windows, the $HOME environment variable may be set by users or by // Note: on Windows, the $HOME environment variable may be set by users or by
@ -587,11 +593,22 @@ async fn resolve_shim_data(
let copy_path = get_hidden_file_with_ext(&file_path, "deno.json"); let copy_path = get_hidden_file_with_ext(&file_path, "deno.json");
executable_args.push("--config".to_string()); executable_args.push("--config".to_string());
executable_args.push(copy_path.to_str().unwrap().to_string()); executable_args.push(copy_path.to_str().unwrap().to_string());
extra_files.push(( let mut config_text = fs::read_to_string(config_path)
copy_path, .with_context(|| format!("error reading {config_path}"))?;
fs::read_to_string(config_path) // always remove the import map field because when someone specifies `--import-map` we
.with_context(|| format!("error reading {config_path}"))?, // don't want that file to be attempted to be loaded and when they don't specify that
)); // (which is just something we haven't implemented yet)
if let Some(new_text) = remove_import_map_field_from_text(&config_text) {
if flags.import_map_path.is_none() {
log::warn!(
"{} \"importMap\" field in the specified config file we be ignored. Use the --import-map flag instead.",
crate::colors::yellow("Warning"),
);
}
config_text = new_text;
}
extra_files.push((copy_path, config_text));
} else { } else {
executable_args.push("--no-config".to_string()); executable_args.push("--no-config".to_string());
} }
@ -631,6 +648,16 @@ async fn resolve_shim_data(
}) })
} }
fn remove_import_map_field_from_text(config_text: &str) -> Option<String> {
let value =
jsonc_parser::cst::CstRootNode::parse(config_text, &Default::default())
.ok()?;
let root_value = value.object_value()?;
let import_map_value = root_value.get("importMap")?;
import_map_value.remove();
Some(value.to_string())
}
fn get_hidden_file_with_ext(file_path: &Path, ext: &str) -> PathBuf { fn get_hidden_file_with_ext(file_path: &Path, ext: &str) -> PathBuf {
// use a dot file to prevent the file from showing up in some // use a dot file to prevent the file from showing up in some
// users shell auto-complete since this directory is on the PATH // users shell auto-complete since this directory is on the PATH
@ -1585,4 +1612,17 @@ mod tests {
assert!(!file_path.exists()); assert!(!file_path.exists());
} }
} }
#[test]
fn test_remove_import_map_field_from_text() {
assert_eq!(
remove_import_map_field_from_text(
r#"{
"importMap": "./value.json"
}"#,
)
.unwrap(),
"{}"
);
}
} }

View file

@ -226,7 +226,6 @@ deno_core::extension!(deno_node,
ops::crypto::op_node_decipheriv_decrypt, ops::crypto::op_node_decipheriv_decrypt,
ops::crypto::op_node_decipheriv_final, ops::crypto::op_node_decipheriv_final,
ops::crypto::op_node_decipheriv_set_aad, ops::crypto::op_node_decipheriv_set_aad,
ops::crypto::op_node_decipheriv_take,
ops::crypto::op_node_dh_compute_secret, ops::crypto::op_node_dh_compute_secret,
ops::crypto::op_node_diffie_hellman, ops::crypto::op_node_diffie_hellman,
ops::crypto::op_node_ecdh_compute_public_key, ops::crypto::op_node_ecdh_compute_public_key,
@ -373,6 +372,7 @@ deno_core::extension!(deno_node,
ops::zlib::brotli::op_brotli_decompress_stream_end, ops::zlib::brotli::op_brotli_decompress_stream_end,
ops::http::op_node_http_fetch_response_upgrade, ops::http::op_node_http_fetch_response_upgrade,
ops::http::op_node_http_request_with_conn<P>, ops::http::op_node_http_request_with_conn<P>,
ops::http::op_node_http_await_information,
ops::http::op_node_http_await_response, ops::http::op_node_http_await_response,
ops::http2::op_http2_connect, ops::http2::op_http2_connect,
ops::http2::op_http2_poll_client_connection, ops::http2::op_http2_poll_client_connection,

View file

@ -500,6 +500,11 @@ impl Decipher {
auth_tag: &[u8], auth_tag: &[u8],
) -> Result<(), DecipherError> { ) -> Result<(), DecipherError> {
use Decipher::*; use Decipher::*;
if input.is_empty() && !matches!(self, Aes128Gcm(_) | Aes256Gcm(_)) {
return Ok(());
}
match (self, auto_pad) { match (self, auto_pad) {
(Aes128Cbc(decryptor), true) => { (Aes128Cbc(decryptor), true) => {
assert!(input.len() == 16); assert!(input.len() == 16);

View file

@ -332,17 +332,6 @@ pub fn op_node_decipheriv_decrypt(
true true
} }
#[op2(fast)]
pub fn op_node_decipheriv_take(
state: &mut OpState,
#[smi] rid: u32,
) -> Result<(), cipher::DecipherContextError> {
let context = state.resource_table.take::<cipher::DecipherContext>(rid)?;
Rc::try_unwrap(context)
.map_err(|_| cipher::DecipherContextError::ContextInUse)?;
Ok(())
}
#[op2] #[op2]
pub fn op_node_decipheriv_final( pub fn op_node_decipheriv_final(
state: &mut OpState, state: &mut OpState,

View file

@ -11,6 +11,7 @@ use std::task::Poll;
use bytes::Bytes; use bytes::Bytes;
use deno_core::error::ResourceError; use deno_core::error::ResourceError;
use deno_core::futures::channel::mpsc;
use deno_core::futures::stream::Peekable; use deno_core::futures::stream::Peekable;
use deno_core::futures::Future; use deno_core::futures::Future;
use deno_core::futures::FutureExt; use deno_core::futures::FutureExt;
@ -70,9 +71,20 @@ pub struct NodeHttpResponse {
type CancelableResponseResult = type CancelableResponseResult =
Result<Result<http::Response<Incoming>, hyper::Error>, Canceled>; Result<Result<http::Response<Incoming>, hyper::Error>, Canceled>;
#[derive(Serialize, Debug)]
#[serde(rename_all = "camelCase")]
struct InformationalResponse {
status: u16,
status_text: String,
headers: Vec<(ByteString, ByteString)>,
version_major: u16,
version_minor: u16,
}
pub struct NodeHttpClientResponse { pub struct NodeHttpClientResponse {
response: Pin<Box<dyn Future<Output = CancelableResponseResult>>>, response: Pin<Box<dyn Future<Output = CancelableResponseResult>>>,
url: String, url: String,
informational_rx: RefCell<Option<mpsc::Receiver<InformationalResponse>>>,
} }
impl Debug for NodeHttpClientResponse { impl Debug for NodeHttpClientResponse {
@ -252,6 +264,36 @@ where
request.headers_mut().insert(CONTENT_LENGTH, len.into()); request.headers_mut().insert(CONTENT_LENGTH, len.into());
} }
let (tx, informational_rx) = mpsc::channel(1);
hyper::ext::on_informational(&mut request, move |res| {
let mut tx = tx.clone();
let _ = tx.try_send(InformationalResponse {
status: res.status().as_u16(),
status_text: res.status().canonical_reason().unwrap_or("").to_string(),
headers: res
.headers()
.iter()
.map(|(k, v)| (k.as_str().into(), v.as_bytes().into()))
.collect(),
version_major: match res.version() {
hyper::Version::HTTP_09 => 0,
hyper::Version::HTTP_10 => 1,
hyper::Version::HTTP_11 => 1,
hyper::Version::HTTP_2 => 2,
hyper::Version::HTTP_3 => 3,
_ => unreachable!(),
},
version_minor: match res.version() {
hyper::Version::HTTP_09 => 9,
hyper::Version::HTTP_10 => 0,
hyper::Version::HTTP_11 => 1,
hyper::Version::HTTP_2 => 0,
hyper::Version::HTTP_3 => 0,
_ => unreachable!(),
},
});
});
let cancel_handle = CancelHandle::new_rc(); let cancel_handle = CancelHandle::new_rc();
let cancel_handle_ = cancel_handle.clone(); let cancel_handle_ = cancel_handle.clone();
@ -264,6 +306,7 @@ where
.add(NodeHttpClientResponse { .add(NodeHttpClientResponse {
response: Box::pin(fut), response: Box::pin(fut),
url: url.clone(), url: url.clone(),
informational_rx: RefCell::new(Some(informational_rx)),
}); });
let cancel_handle_rid = state let cancel_handle_rid = state
@ -277,6 +320,27 @@ where
}) })
} }
#[op2(async)]
#[serde]
pub async fn op_node_http_await_information(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
) -> Option<InformationalResponse> {
let Ok(resource) = state
.borrow_mut()
.resource_table
.get::<NodeHttpClientResponse>(rid)
else {
return None;
};
let mut rx = resource.informational_rx.borrow_mut().take()?;
drop(resource);
rx.next().await
}
#[op2(async)] #[op2(async)]
#[serde] #[serde]
pub async fn op_node_http_await_response( pub async fn op_node_http_await_response(

View file

@ -5,6 +5,7 @@
import { core, primordials } from "ext:core/mod.js"; import { core, primordials } from "ext:core/mod.js";
import { import {
op_node_http_await_information,
op_node_http_await_response, op_node_http_await_response,
op_node_http_fetch_response_upgrade, op_node_http_fetch_response_upgrade,
op_node_http_request_with_conn, op_node_http_request_with_conn,
@ -479,6 +480,44 @@ class ClientRequest extends OutgoingMessage {
this._encrypted, this._encrypted,
); );
this._flushBuffer(); this._flushBuffer();
const infoPromise = op_node_http_await_information(
this._req!.requestRid,
);
core.unrefOpPromise(infoPromise);
infoPromise.then((info) => {
if (!info) return;
if (info.status === 100) this.emit("continue");
let headers;
let rawHeaders;
this.emit("information", {
statusCode: info.status,
statusMessage: info.statusText,
httpVersionMajor: info.versionMajor,
httpVersionMinor: info.versionMinor,
httpVersion: `${info.versionMajor}.${info.versionMinor}`,
get headers() {
if (!headers) {
headers = {};
for (let i = 0; i < info.headers.length; i++) {
const entry = info.headers[i];
headers[entry[0]] = entry[1];
}
}
return headers;
},
get rawHeaders() {
if (!rawHeaders) {
rawHeaders = info.headers.flat();
}
return rawHeaders;
},
});
});
const res = await op_node_http_await_response(this._req!.requestRid); const res = await op_node_http_await_response(this._req!.requestRid);
if (this._req.cancelHandleRid !== null) { if (this._req.cancelHandleRid !== null) {
core.tryClose(this._req.cancelHandleRid); core.tryClose(this._req.cancelHandleRid);
@ -1621,6 +1660,12 @@ ServerResponse.prototype.detachSocket = function (
this._socketOverride = null; this._socketOverride = null;
}; };
ServerResponse.prototype.writeContinue = function writeContinue(cb) {
if (cb) {
nextTick(cb);
}
};
Object.defineProperty(ServerResponse.prototype, "connection", { Object.defineProperty(ServerResponse.prototype, "connection", {
get: deprecate( get: deprecate(
function (this: ServerResponse) { function (this: ServerResponse) {
@ -1826,7 +1871,24 @@ export class ServerImpl extends EventEmitter {
} else { } else {
return new Promise<Response>((resolve): void => { return new Promise<Response>((resolve): void => {
const res = new ServerResponse(resolve, socket); const res = new ServerResponse(resolve, socket);
this.emit("request", req, res);
if (request.headers.has("expect")) {
if (/(?:^|\W)100-continue(?:$|\W)/i.test(req.headers.expect)) {
if (this.listenerCount("checkContinue") > 0) {
this.emit("checkContinue", req, res);
} else {
res.writeContinue();
this.emit("request", req, res);
}
} else if (this.listenerCount("checkExpectation") > 0) {
this.emit("checkExpectation", req, res);
} else {
res.writeHead(417);
res.end();
}
} else {
this.emit("request", req, res);
}
}); });
} }
}; };

View file

@ -18,7 +18,6 @@ import {
op_node_decipheriv_decrypt, op_node_decipheriv_decrypt,
op_node_decipheriv_final, op_node_decipheriv_final,
op_node_decipheriv_set_aad, op_node_decipheriv_set_aad,
op_node_decipheriv_take,
op_node_private_decrypt, op_node_private_decrypt,
op_node_private_encrypt, op_node_private_encrypt,
op_node_public_encrypt, op_node_public_encrypt,
@ -352,14 +351,6 @@ export class Decipheriv extends Transform implements Cipher {
} }
final(encoding: string = getDefaultEncoding()): Buffer | string { final(encoding: string = getDefaultEncoding()): Buffer | string {
if (!this.#needsBlockCache || this.#cache.cache.byteLength === 0) {
op_node_decipheriv_take(this.#context);
return encoding === "buffer" ? Buffer.from([]) : "";
}
if (this.#cache.cache.byteLength != 16) {
throw new Error("Invalid final block size");
}
let buf = new Buffer(16); let buf = new Buffer(16);
op_node_decipheriv_final( op_node_decipheriv_final(
this.#context, this.#context,
@ -369,6 +360,13 @@ export class Decipheriv extends Transform implements Cipher {
this.#authTag || NO_TAG, this.#authTag || NO_TAG,
); );
if (!this.#needsBlockCache || this.#cache.cache.byteLength === 0) {
return encoding === "buffer" ? Buffer.from([]) : "";
}
if (this.#cache.cache.byteLength != 16) {
throw new Error("Invalid final block size");
}
buf = buf.subarray(0, 16 - buf.at(-1)); // Padded in Pkcs7 mode buf = buf.subarray(0, 16 - buf.at(-1)); // Padded in Pkcs7 mode
return encoding === "buffer" ? buf : buf.toString(encoding); return encoding === "buffer" ? buf : buf.toString(encoding);
} }

View file

@ -116,6 +116,12 @@ deno_core::extension!(
"op_exit" | "op_set_exit_code" | "op_get_exit_code" => "op_exit" | "op_set_exit_code" | "op_get_exit_code" =>
op.with_implementation_from(&deno_core::op_void_sync()), op.with_implementation_from(&deno_core::op_void_sync()),
_ => op, _ => op,
},
state = |state| {
#[cfg(unix)]
{
state.put(ops::signal::SignalState::default());
}
} }
); );

View file

@ -42,6 +42,7 @@ use opentelemetry::metrics::InstrumentBuilder;
use opentelemetry::metrics::MeterProvider as _; use opentelemetry::metrics::MeterProvider as _;
use opentelemetry::otel_debug; use opentelemetry::otel_debug;
use opentelemetry::otel_error; use opentelemetry::otel_error;
use opentelemetry::trace::Link;
use opentelemetry::trace::SpanContext; use opentelemetry::trace::SpanContext;
use opentelemetry::trace::SpanId; use opentelemetry::trace::SpanId;
use opentelemetry::trace::SpanKind; use opentelemetry::trace::SpanKind;
@ -94,6 +95,7 @@ deno_core::extension!(
op_otel_span_attribute1, op_otel_span_attribute1,
op_otel_span_attribute2, op_otel_span_attribute2,
op_otel_span_attribute3, op_otel_span_attribute3,
op_otel_span_add_link,
op_otel_span_update_name, op_otel_span_update_name,
op_otel_metric_attribute3, op_otel_metric_attribute3,
op_otel_metric_record0, op_otel_metric_record0,
@ -1324,17 +1326,6 @@ impl OtelSpan {
} }
} }
#[fast]
fn drop_link(&self) {
let mut state = self.0.borrow_mut();
match &mut **state {
OtelSpanState::Recording(span) => {
span.links.dropped_count += 1;
}
OtelSpanState::Done(_) => {}
}
}
#[fast] #[fast]
fn end(&self, end_time: f64) { fn end(&self, end_time: f64) {
let end_time = if end_time.is_nan() { let end_time = if end_time.is_nan() {
@ -1448,6 +1439,48 @@ fn op_otel_span_update_name<'s>(
} }
} }
#[op2(fast)]
fn op_otel_span_add_link<'s>(
scope: &mut v8::HandleScope<'s>,
span: v8::Local<'s, v8::Value>,
trace_id: v8::Local<'s, v8::Value>,
span_id: v8::Local<'s, v8::Value>,
#[smi] trace_flags: u8,
is_remote: bool,
#[smi] dropped_attributes_count: u32,
) -> bool {
let trace_id = parse_trace_id(scope, trace_id);
if trace_id == TraceId::INVALID {
return false;
};
let span_id = parse_span_id(scope, span_id);
if span_id == SpanId::INVALID {
return false;
};
let span_context = SpanContext::new(
trace_id,
span_id,
TraceFlags::new(trace_flags),
is_remote,
TraceState::NONE,
);
let Some(span) =
deno_core::_ops::try_unwrap_cppgc_object::<OtelSpan>(scope, span)
else {
return true;
};
let mut state = span.0.borrow_mut();
if let OtelSpanState::Recording(span) = &mut **state {
span.links.links.push(Link::new(
span_context,
vec![],
dropped_attributes_count,
));
}
true
}
struct OtelMeter(opentelemetry::metrics::Meter); struct OtelMeter(opentelemetry::metrics::Meter);
impl deno_core::GarbageCollected for OtelMeter {} impl deno_core::GarbageCollected for OtelMeter {}

View file

@ -15,6 +15,7 @@ import {
op_otel_metric_record2, op_otel_metric_record2,
op_otel_metric_record3, op_otel_metric_record3,
op_otel_metric_wait_to_observe, op_otel_metric_wait_to_observe,
op_otel_span_add_link,
op_otel_span_attribute1, op_otel_span_attribute1,
op_otel_span_attribute2, op_otel_span_attribute2,
op_otel_span_attribute3, op_otel_span_attribute3,
@ -186,7 +187,6 @@ interface OtelSpan {
spanContext(): SpanContext; spanContext(): SpanContext;
setStatus(status: SpanStatusCode, errorDescription: string): void; setStatus(status: SpanStatusCode, errorDescription: string): void;
dropEvent(): void; dropEvent(): void;
dropLink(): void;
end(endTime: number): void; end(endTime: number): void;
} }
@ -359,14 +359,24 @@ class Span {
return this; return this;
} }
addLink(_link: Link): Span { addLink(link: Link): Span {
this.#otelSpan?.dropLink(); const droppedAttributeCount = (link.droppedAttributesCount ?? 0) +
(link.attributes ? ObjectKeys(link.attributes).length : 0);
const valid = op_otel_span_add_link(
this.#otelSpan,
link.context.traceId,
link.context.spanId,
link.context.traceFlags,
link.context.isRemote ?? false,
droppedAttributeCount,
);
if (!valid) return this;
return this; return this;
} }
addLinks(links: Link[]): Span { addLinks(links: Link[]): Span {
for (let i = 0; i < links.length; i++) { for (let i = 0; i < links.length; i++) {
this.#otelSpan?.dropLink(); this.addLink(links[i]);
} }
return this; return this;
} }

View file

@ -22,6 +22,10 @@
}, },
"args": "run -A main.ts metric.ts", "args": "run -A main.ts metric.ts",
"output": "metric.out" "output": "metric.out"
},
"links": {
"args": "run -A main.ts links.ts",
"output": "links.out"
} }
} }
} }

View file

@ -0,0 +1,96 @@
{
"spans": [
{
"traceId": "00000000000000000000000000000001",
"spanId": "0000000000000001",
"traceState": "",
"parentSpanId": "",
"flags": 1,
"name": "example span",
"kind": 1,
"startTimeUnixNano": "[WILDCARD]",
"endTimeUnixNano": "[WILDCARD]",
"attributes": [],
"droppedAttributesCount": 0,
"events": [],
"droppedEventsCount": 0,
"links": [
{
"traceId": "1234567890abcdef1234567890abcdef",
"spanId": "1234567890abcdef",
"traceState": "",
"attributes": [],
"droppedAttributesCount": 0,
"flags": 1
}
],
"droppedLinksCount": 0,
"status": {
"message": "",
"code": 0
}
},
{
"traceId": "00000000000000000000000000000002",
"spanId": "0000000000000002",
"traceState": "",
"parentSpanId": "",
"flags": 1,
"name": "example span",
"kind": 1,
"startTimeUnixNano": "[WILDCARD]",
"endTimeUnixNano": "[WILDCARD]",
"attributes": [],
"droppedAttributesCount": 0,
"events": [],
"droppedEventsCount": 0,
"links": [
{
"traceId": "1234567890abcdef1234567890abcdef",
"spanId": "1234567890abcdef",
"traceState": "",
"attributes": [],
"droppedAttributesCount": 0,
"flags": 1
}
],
"droppedLinksCount": 0,
"status": {
"message": "",
"code": 0
}
},
{
"traceId": "00000000000000000000000000000003",
"spanId": "0000000000000003",
"traceState": "",
"parentSpanId": "",
"flags": 1,
"name": "example span",
"kind": 1,
"startTimeUnixNano": "[WILDCARD]",
"endTimeUnixNano": "[WILDCARD]",
"attributes": [],
"droppedAttributesCount": 0,
"events": [],
"droppedEventsCount": 0,
"links": [
{
"traceId": "1234567890abcdef1234567890abcdef",
"spanId": "1234567890abcdef",
"traceState": "",
"attributes": [],
"droppedAttributesCount": 2,
"flags": 1
}
],
"droppedLinksCount": 0,
"status": {
"message": "",
"code": 0
}
}
],
"logs": [],
"metrics": []
}

View file

@ -0,0 +1,40 @@
// Copyright 2018-2025 the Deno authors. MIT license.
import { trace } from "npm:@opentelemetry/api@1.9.0";
const tracer = trace.getTracer("example-tracer");
const span1 = tracer.startSpan("example span", {
links: [{
context: {
traceId: "1234567890abcdef1234567890abcdef",
spanId: "1234567890abcdef",
traceFlags: 1,
},
}],
});
span1.end();
const span2 = tracer.startSpan("example span");
span2.addLink({
context: {
traceId: "1234567890abcdef1234567890abcdef",
spanId: "1234567890abcdef",
traceFlags: 1,
},
});
span2.end();
const span3 = tracer.startSpan("example span");
span3.addLink({
context: {
traceId: "1234567890abcdef1234567890abcdef",
spanId: "1234567890abcdef",
traceFlags: 1,
},
attributes: {
key: "value",
},
droppedAttributesCount: 1,
});
span3.end();

View file

@ -0,0 +1,5 @@
{
"tempDir": true,
"args": "install -g --root ./folder --config deno.json main.ts --name my-cli",
"output": "install.out"
}

View file

@ -0,0 +1,3 @@
{
"importMap": "./import_map.json"
}

View file

@ -0,0 +1,2 @@
{
}

View file

@ -0,0 +1,3 @@
Warning "importMap" field in the specified config file we be ignored. Use the --import-map flag instead.
✅ Successfully installed my-cli
[WILDCARD]

View file

@ -0,0 +1 @@
console.log(1);

View file

@ -0,0 +1,4 @@
{
"args": "run -A main.cjs",
"output": "ok\n"
}

View file

@ -0,0 +1,60 @@
"use strict";
const assert = require("assert");
const http = require("http");
const test_req_body = "some stuff...\n";
const test_res_body = "other stuff!\n";
let sent_continue = false;
let got_continue = false;
const server = http.createServer();
server.on("checkContinue", (req, res) => {
res.writeContinue();
sent_continue = true;
req.on("data", () => {});
req.on("end", () => {
res.writeHead(200, {
"Content-Type": "text/plain",
"ABCD": "1",
});
res.end(test_res_body);
});
});
server.listen(0);
server.on("listening", () => {
const req = http.request({
port: server.address().port,
method: "POST",
path: "/world",
headers: {
"Expect": "100-continue",
"Content-Length": test_req_body.length,
},
});
let body = "";
req.on("continue", () => {
assert.ok(sent_continue);
got_continue = true;
req.end(test_req_body);
});
req.on("response", (res) => {
assert.ok(got_continue, "Full response received before 100 Continue");
assert.strictEqual(
res.statusCode,
200,
`Final status code was ${res.statusCode}, not 200.`,
);
res.setEncoding("utf8");
res.on("data", function (chunk) {
body += chunk;
});
res.on("end", () => {
assert.strictEqual(body, test_res_body);
assert.ok("abcd" in res.headers, "Response headers missing.");
console.log("ok");
server.close();
});
});
});

View file

@ -4,7 +4,7 @@ import crypto from "node:crypto";
import { Buffer } from "node:buffer"; import { Buffer } from "node:buffer";
import testVectors128 from "./gcmEncryptExtIV128.json" with { type: "json" }; import testVectors128 from "./gcmEncryptExtIV128.json" with { type: "json" };
import testVectors256 from "./gcmEncryptExtIV256.json" with { type: "json" }; import testVectors256 from "./gcmEncryptExtIV256.json" with { type: "json" };
import { assertEquals } from "@std/assert"; import { assertEquals, assertThrows } from "@std/assert";
const aesGcm = (bits: string, key: Uint8Array) => { const aesGcm = (bits: string, key: Uint8Array) => {
const ALGO = bits == "128" ? `aes-128-gcm` : `aes-256-gcm`; const ALGO = bits == "128" ? `aes-128-gcm` : `aes-256-gcm`;
@ -123,7 +123,7 @@ Deno.test({
// Issue #27441 // Issue #27441
// https://github.com/denoland/deno/issues/27441 // https://github.com/denoland/deno/issues/27441
Deno.test({ Deno.test({
name: "aes-256-gcm supports IV of non standard length", name: "aes-256-gcm supports IV of non standard length and auth tag check",
fn() { fn() {
const decipher = crypto.createDecipheriv( const decipher = crypto.createDecipheriv(
"aes-256-gcm", "aes-256-gcm",
@ -136,6 +136,10 @@ Deno.test({
"utf-8", "utf-8",
); );
assertEquals(decrypted, "this is a secret"); assertEquals(decrypted, "this is a secret");
decipher.final(); assertThrows(
() => decipher.final(),
TypeError,
"Failed to authenticate data",
);
}, },
}); });