-
Notifications
You must be signed in to change notification settings - Fork 4
adding client and nas architecture #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
WalkthroughAdds atomic state management and NAS context structures; introduces a new atomic-handle crate; refactors UE/NAS handling to a state-machine trait with registration handling; switches NGAP context scheduling to a SeqLock-based queue; adds service discovery and OAuth layers to the client; updates NRF token handling; adjusts module paths and workspace dependencies. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor R as RAN/UE
participant NGAP as NGAP Controller
participant CtxMgr as ContextManager<UE>
participant UE as UeContext (SeqLock)
participant NAS as NasMessageHandle (Registration)
rect rgba(230,240,255,0.5)
R->>NGAP: InitialUEMessage (NAS PDU, FiveGSTmsi?)
NGAP->>CtxMgr: create_new_context(...)
CtxMgr-->>NGAP: Arc<SeqLock<UeContext>>
NGAP->>UE: schedule closure with NAS PDU
note right of UE: Unsafe SeqLock access within single-threaded queue
UE->>NAS: state_transition(from_state, request)
NAS-->>UE: (next_state, Optional<NgapPdu>)
end
sequenceDiagram
autonumber
participant Client as NFClient<T, APP, TARGET>
participant SD as ServiceDiscoveryLayer
participant OA as OAuthTokenLayer
participant HTTP as HttpClient
participant NRF as NrfClient
rect rgba(240,255,240,0.5)
Client->>SD: request
alt cache invalid
SD->>NRF: discover TARGET services
NRF-->>SD: NfProfile / error
SD->>SD: update cache / error
end
SD->>OA: rewritten request (base URL)
OA->>NRF: get/set OAuth token (headers)
OA->>HTTP: forward request
HTTP-->>Client: response
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 23
🧹 Nitpick comments (25)
Cargo.toml (1)
76-76: Fix the typo in this comment."Depenedency" should be spelled "Dependency" to keep the note clear.
-socket2 = "0.4" # Depenedency of tokio-sctp +socket2 = "0.4" # Dependency of tokio-sctplightning-nf/omnipath/app/src/nas_old/ue_actions.rs (1)
1-1: Drop the unusedUeContextimportLine 1 pulls in
UeContext, but every reference remains commented out, so the compiler emits an unused-import warning. Unless we plan to resurrect that block immediately, please remove the import (or add an allow) to keep the module warning-free.lightning-nf/omnipath/app/src/context/ue_context.rs (1)
17-19: Restrictstateandnas_contextvisibility to avoid unsound external mutationBoth fields are public, enabling mutation outside the scheduling/serialization guarantees of the context manager. Make them private or
pub(crate)and expose intentional accessors/methods.pub struct UeContext { pub ran_ue_ngap_id: RanUeNgapId, pub amf_ue_ngap_id: AmfUeNgapId, pub gnb_context: Arc<GnbContext>, - pub state: AtomicGmmState, - pub nas_context: NasContext, + pub(crate) state: AtomicGmmState, + pub(crate) nas_context: NasContext, }lightning-nf/omnipath/app/src/nas_old/mod.rs (2)
1-2: Remove unusedArcimport
Arcis not used in this module.-use std::{future::Future, sync::Arc}; +use std::future::Future;
4-4: Explicitly import the intendedNasContext
Change the import to reference the canonical type and avoid shadowing:-use nas_context::NasContext; +use crate::context::NasContext;Confirm all usages in
nas_old/mod.rsnow refer tocrate::context::NasContext, not the localnas_old::nas_context::NasContext.lightning-nf/omnipath/app/src/ngap/core/initial_ue_message.rs (2)
5-5: Drop unused RwLock imports
OwnedRwLockWriteGuardandRwLockare no longer used after switching toSeqLock.-use tokio::sync::{OwnedRwLockWriteGuard, RwLock}; +// (imports removed)
30-51: Existing‑context path returns an NGAP failure by design — confirm this matches expected behaviorPreviously, implementations often schedule onto the existing context. If failing fast here is intentional, confirm AMF behavior/spec alignment for re‑entrant InitialUE when registration is in progress.
lightning-nf/omnipath/app/src/context/state/gmm_state.rs (2)
7-8: Derive Copy/Clone for ergonomic value semantics.These states are small repr(u8) values and frequently copied; deriving Copy, Clone reduces churn and borrow issues.
Apply:
-#[derive(Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Eq, PartialEq, Hash)]
94-107: next_state for Registered: clarify or encode the common-procedure hop.Comment says “Final state or can go to CommonProcedureInitiated” but next_state returns None. Consider returning Some(CommonProcedureInitiated) or clarifying that next_state encodes only the registration linear flow.
lightning-nf/omnipath/app/src/nas/registration_req/mod.rs (2)
35-37: Emit structured log for NoIdentity.Convert TODO into a warning so operators can see malformed/empty IDs.
Apply:
- MobileIdentity::NoIdentity(_no_identity) => { - // Todo push some logging here - } + MobileIdentity::NoIdentity(_no_identity) => { + tracing::warn!("NAS RegistrationRequest without a mobile identity"); + }
26-63: Persist UE security capability from the request.Prior handler logic populated ue_security_capability or errored; this impl omits it. Persisting this is necessary for subsequent security mode decisions.
Apply near the end of the function (before Ok(...)):
+ if let Some(sec) = &req.nas_ue_security_capability { + self.nas_context.ue_security_capability = Some(sec.clone()); + } else { + tracing::warn!("Missing UE security capability in RegistrationRequest"); + }lightning-nf/omnipath/app/src/ngap/manager/context_queue.rs (3)
17-26: Docs mention RwLock—update to SeqLock.The module docs still reference RwLock; adjust to reflect the SeqLock‑based design to prevent confusion.
202-209: Avoid unwrap on oneshot receive.A drop/panic in the worker will panic callers. Return Result or map the error.
Apply:
- rx.await.unwrap() + rx.await.expect("ContextQueue: operation task dropped or panicked")Or better, change the API to return
Result<O, oneshot::error::RecvError>.
136-140: Unnecessary Sync bound on FnOnce closures.Requiring Sync for FnOnce is superfluous and restricts callers; remove it unless there’s a concrete need.
Apply:
- + Send - + Sync + + SendDo the same in schedule_and_wait.
Also applies to: 192-196
utils/client/src/nf_clients/oauth_service.rs (3)
19-23: Use Arc<[ServiceName]> instead of Vec to avoid per-call clones and shrink capturesReplace Vec with Arc<[ServiceName]> in the service and layer; accept Into<Arc<[ServiceName]>> in constructors to keep call sites ergonomic. This removes an allocation and makes .clone() cheap in call().
@@ -pub struct OAuthTokenService<S, const TARGET_NF_TYPE: NfType> { +pub struct OAuthTokenService<S, const TARGET_NF_TYPE: NfType> { inner: S, nrf_client: Arc<NrfClient>, - service_names: Vec<ServiceName>, + service_names: Arc<[ServiceName]>, } @@ - pub fn new( + pub fn new( inner: S, nrf_client: Arc<NrfClient>, - service_names: Vec<ServiceName>, + service_names: impl Into<Arc<[ServiceName]>>, ) -> Self { Self { inner, nrf_client, - service_names, + service_names: service_names.into(), } } @@ - pub fn service_names(&self) -> &[ServiceName] { + pub fn service_names(&self) -> &[ServiceName] { &self.service_names } @@ -pub struct OAuthTokenLayer<const TARGET_NF_TYPE: NfType> { +pub struct OAuthTokenLayer<const TARGET_NF_TYPE: NfType> { nrf_client: Arc<NrfClient>, - service_names: Vec<ServiceName>, + service_names: Arc<[ServiceName]>, } @@ - pub fn new( + pub fn new( nrf_client: Arc<NrfClient>, - service_names: Vec<ServiceName>, + service_names: impl Into<Arc<[ServiceName]>>, ) -> Self { Self { nrf_client, - service_names, + service_names: service_names.into(), } } @@ - pub fn with_service( + pub fn with_service( nrf_client: Arc<NrfClient>, service_name: ServiceName, ) -> Self { Self { nrf_client, - service_names: vec![service_name], + service_names: vec![service_name].into(), } } @@ - fn layer( + fn layer( &self, inner: S, ) -> Self::Service { - OAuthTokenService::new(inner, self.nrf_client.clone(), self.service_names.clone()) + OAuthTokenService::new(inner, self.nrf_client.clone(), self.service_names.clone()) }Also applies to: 30-37, 55-57, 112-114, 119-137, 150-159, 89-92
150-159: Minor: make the layer self-sufficient wrt sensitive headersToday, SetSensitiveRequestHeadersLayer is applied by the caller (see utils/client/src/nf_clients/mod.rs). Consider documenting this requirement here, or optionally adding a tiny layer wrapper that always marks AUTHORIZATION as sensitive to prevent accidental leakage in other stacks.
175-180: Unit test is a stub; provide a real test via a mock NrfClientAdd a lightweight NrfClient test-double that sets a fixed Authorization header so we can assert header injection.
I can scaffold a minimal trait/adapter around set_auth_token for easy mocking and add a test if helpful.
utils/atomic-handle/src/lib.rs (2)
1-5: Prefer NonNull over raw pointer; derive Clone; tighten debug load orderingUsing NonNull avoids accidental nulls and makes unsafe blocks smaller; deriving Clone simplifies code; Relaxed is enough for debug printing.
@@ -use std::{ - marker::PhantomData, ops::Deref, sync::{ - atomic::{AtomicUsize, Ordering}, Arc - } -}; +use std::{ + marker::PhantomData, + ops::Deref, + ptr::NonNull, + sync::{atomic::{AtomicUsize, Ordering}, Arc}, +}; @@ -pub struct AtomicHandle<S, Field = ()> +#[derive(Clone)] +pub struct AtomicHandle<S, Field = ()> where S: AtomicOperation<Field> + Send + Sync , { /// Keeps the original Arc<S> alive to prevent memory deallocation _owner: Arc<S>, /// Raw pointer to the specific atomic field within the struct - atomic_ptr: *const AtomicUsize, + atomic_ptr: NonNull<AtomicUsize>, /// Zero-sized marker to track which field this handle represents _phantom: PhantomData<Field>, } @@ - pub fn new(owner: Arc<S>) -> Self { - let atomic_ptr = owner.get_atomic() as *const AtomicUsize; + pub fn new(owner: Arc<S>) -> Self { + let atomic_ptr = NonNull::from(owner.get_atomic()); Self { _owner: owner, atomic_ptr, _phantom: PhantomData, } } @@ - pub fn get(&self) -> &AtomicUsize { - unsafe { &*self.atomic_ptr } + pub fn get(&self) -> &AtomicUsize { + // Safety: NonNull points at a field inside `_owner`, whose lifetime we hold via Arc. + unsafe { self.atomic_ptr.as_ref() } } @@ -// Manual implementation of Clone. -impl<S, Field> Clone for AtomicHandle<S, Field> -where - S: AtomicOperation<Field> + Send + Sync, -{ - fn clone(&self) -> Self { - Self { - _owner: Arc::clone(&self._owner), - atomic_ptr: self.atomic_ptr, - _phantom: PhantomData, - } - } -} +// (Clone derived above) @@ f.debug_struct("AtomicHandle") - .field("current_value", &self.load(Ordering::SeqCst)) + .field("current_value", &self.load(Ordering::Relaxed)) .field("field_type", &std::any::type_name::<Field>()) .field("struct_type", &std::any::type_name::<S>()) .finish() }Also applies to: 32-42, 95-103, 108-111, 114-123, 125-137
8-15: Document safety invariants for implementors of AtomicOperationCallers must ensure the returned &AtomicUsize has a stable address for the lifetime of Arc
and is not inside buffers that may reallocate/move. Add explicit safety notes to prevent unsound impls.@@ /// The generic parameter `Field` allows the same type to implement /// this trait multiple times for different atomic fields. /// pub trait AtomicOperation<Field = ()> { - /// Returns a reference to the atomic field identified by the Field type - /// parameter + /// Returns a reference to the atomic field identified by the `Field` type parameter. + /// + /// Safety contract: + /// - The returned reference must point to a field whose memory address remains + /// stable for as long as any `Arc<S>` clones exist (e.g., the field is inside `S`, + /// not inside a relocatable buffer). + /// - It must not be freed while any `AtomicHandle<S, Field>` derived from the same + /// owner `Arc<S>` is alive. fn get_atomic(&self) -> &AtomicUsize; }lightning-nf/omnipath/app/src/nas/interface.rs (2)
17-21: Avoid logging full request Debug in error message (PII risk)Printing the entire request on invalid transitions may leak identifiers. Remove it from Display; keep fields on the struct for programmatic access.
-#[error("Invalid state transition from {state:?} with request: {request:?}")] +#[error("Invalid state transition from {state:?}")] pub struct InvalidStateTransition<R: Debug> {
212-226: Test covers spawn bounds; expand with a case that mutates selfAdd a handler that updates internal state inside an async block to ensure the relaxed (no Sync) bounds compile in real scenarios.
utils/client/src/nf_clients/mod.rs (1)
162-171: Remove unused generics fromrequestsignature (H, Q, B).They’re unused and create noise. Keep only
Resp.-pub async fn request<H, Q, B, Resp>( +pub async fn request<Resp>( &self, req: HttpRequest<Body>, ) -> Result<(StatusCode, Resp), GenericClientError> - where - Q: Serialize, - H: Serialize, - B: Serialize, - Resp: DeserResponse, + where + Resp: DeserResponse,utils/client/src/nf_clients/service_discovery/mod.rs (3)
84-93: Guard against negative validity periods to avoid cast pitfalls.Casting negative
i64tou64can mis-evaluate freshness. Treat non-positive as invalid.pub fn is_valid(&self) -> bool { if let Some(validity_seconds) = self.validity_period { - SystemTime::now() + if validity_seconds <= 0 { + return false; + } + SystemTime::now() .duration_since(self.cached_at) .unwrap_or(Duration::MAX) - .as_secs() < validity_seconds as u64 + .as_secs() < validity_seconds as u64 } else { self.nf_profile.is_some() // Valid if we have a profile and no expiry } }
95-99: Prefer service-specific URL selection using configuredservice_names.
get_base_url()picks an arbitrary URL from a HashMap. If multiple NF services exist, useservice_namespriority order to choose a base URL deterministically.Minimal change approach:
- Pass
&[ServiceName]intoupdate_request_url.- Choose the first matching service URL; fallback to
get_base_url().- fn update_request_url( - request: &mut HttpRequest<Body>, - cached_profile: &CachedNfProfile, - ) -> Result<(), ServiceDiscoveryError> { - match cached_profile.get_base_url() { + fn update_request_url( + request: &mut HttpRequest<Body>, + cached_profile: &CachedNfProfile, + service_names: &[ServiceName], + ) -> Result<(), ServiceDiscoveryError> { + let preferred = service_names + .iter() + .find_map(|n| cached_profile.get_first_service_url(n)); + match preferred.or_else(|| cached_profile.get_base_url()) { Some(base_url) => {And call site:
- match Self::update_request_url(&mut request, &cached_profile) { + match Self::update_request_url(&mut request, &cached_profile, &self.layer.service_names) {Also applies to: 347-373, 377-397
200-218: Cache reset on discovery failure is fine; consider exponential backoff later.Current strategy invalidates cache to retry on next request. Looks good for now.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
lightning-nf/omnipath/app/src/nas_old/gmm_state_machine.pngis excluded by!**/*.png
📒 Files selected for processing (35)
Cargo.toml(1 hunks)lightning-nf/omnipath/app/Cargo.toml(1 hunks)lightning-nf/omnipath/app/src/context/gnb_context.rs(2 hunks)lightning-nf/omnipath/app/src/context/mod.rs(1 hunks)lightning-nf/omnipath/app/src/context/nas_context.rs(1 hunks)lightning-nf/omnipath/app/src/context/state/atomic_gmm_state.rs(1 hunks)lightning-nf/omnipath/app/src/context/state/gmm_state.rs(1 hunks)lightning-nf/omnipath/app/src/context/state/mod.rs(1 hunks)lightning-nf/omnipath/app/src/context/ue_context.rs(2 hunks)lightning-nf/omnipath/app/src/lib.rs(1 hunks)lightning-nf/omnipath/app/src/nas/interface.rs(1 hunks)lightning-nf/omnipath/app/src/nas/mod.rs(1 hunks)lightning-nf/omnipath/app/src/nas/registration_req/error.rs(1 hunks)lightning-nf/omnipath/app/src/nas/registration_req/mod.rs(1 hunks)lightning-nf/omnipath/app/src/nas/ue_actions.rs(0 hunks)lightning-nf/omnipath/app/src/nas_old/builders/authentication_request.rs(1 hunks)lightning-nf/omnipath/app/src/nas_old/handlers/authentication_response.rs(1 hunks)lightning-nf/omnipath/app/src/nas_old/handlers/registration_request.rs(1 hunks)lightning-nf/omnipath/app/src/nas_old/mod.rs(1 hunks)lightning-nf/omnipath/app/src/nas_old/ue_actions.rs(1 hunks)lightning-nf/omnipath/app/src/ngap/core/initial_ue_message.rs(2 hunks)lightning-nf/omnipath/app/src/ngap/engine/controller.rs(1 hunks)lightning-nf/omnipath/app/src/ngap/engine/mod.rs(1 hunks)lightning-nf/omnipath/app/src/ngap/manager/context_manager.rs(2 hunks)lightning-nf/omnipath/app/src/ngap/manager/context_queue.rs(5 hunks)lightning-nf/omnipath/app/src/utils/mod.rs(1 hunks)lightning-nf/omnipath/app/src/utils/seq_lock.rs(1 hunks)utils/atomic-handle/Cargo.toml(1 hunks)utils/atomic-handle/src/lib.rs(1 hunks)utils/client/src/lib.rs(1 hunks)utils/client/src/nf_clients/mod.rs(5 hunks)utils/client/src/nf_clients/oauth_service.rs(1 hunks)utils/client/src/nf_clients/service_discovery/future.rs(1 hunks)utils/client/src/nf_clients/service_discovery/mod.rs(1 hunks)utils/client/src/nrf_client.rs(4 hunks)
💤 Files with no reviewable changes (1)
- lightning-nf/omnipath/app/src/nas/ue_actions.rs
🧰 Additional context used
🧬 Code graph analysis (24)
lightning-nf/omnipath/app/src/nas_old/builders/authentication_request.rs (4)
lightning-nf/omnipath/app/src/nas/builders/mod.rs (1)
authentication_request(2-2)lightning-nf/omnipath/app/src/nas/builders/authentication_request.rs (2)
NasBuilder(7-16)build(9-15)lightning-nf/omnipath/app/src/nas/gmm.rs (1)
NasContext(24-97)lightning-nf/omnipath/app/src/nas/nas_context.rs (1)
NasContext(8-13)
lightning-nf/omnipath/app/src/nas_old/ue_actions.rs (2)
lightning-nf/omnipath/app/src/nas/ue_actions.rs (2)
UeContext(3-25)handle_nas(4-24)lightning-nf/omnipath/app/src/nas/nas_context.rs (1)
NasContext(8-13)
lightning-nf/omnipath/app/src/nas/registration_req/error.rs (2)
lightning-nf/omnipath/app/src/nas/handlers/registration_request.rs (1)
NasHandler(54-72)lightning-nf/omnipath/app/src/nas/nas_context.rs (1)
NasContext(8-13)
lightning-nf/omnipath/app/Cargo.toml (1)
lightning-nf/omnipath/app/src/context/app_context.rs (1)
update_config(126-133)
lightning-nf/omnipath/app/src/utils/seq_lock.rs (1)
lightning-nf/omnipath/app/src/ngap/manager/context_queue.rs (2)
new(43-49)into_inner(54-57)
utils/client/src/nf_clients/service_discovery/future.rs (1)
utils/client/src/nf_clients/service_discovery/mod.rs (3)
new(46-82)new(146-160)new(270-279)
lightning-nf/omnipath/app/src/context/nas_context.rs (2)
lightning-nf/omnipath/app/src/nas/nas_context.rs (1)
NasContext(8-13)lightning-nf/omnipath/app/src/nas/ue_actions.rs (1)
UeContext(3-25)
lightning-nf/omnipath/app/src/context/gnb_context.rs (3)
lightning-nf/omnipath/app/src/ngap/manager/context_manager.rs (1)
new(54-58)utils/atomic-handle/src/lib.rs (1)
new(95-102)lightning-nf/omnipath/app/src/context/ngap_context.rs (2)
new(28-44)NgapContext(16-25)
lightning-nf/omnipath/app/src/nas/interface.rs (2)
lightning-nf/omnipath/app/src/context/state/gmm_state.rs (3)
fmt(168-184)fmt(188-193)from(154-156)lightning-nf/omnipath/app/src/nas/registration_req/mod.rs (1)
pre_comp_state_change(17-24)
lightning-nf/omnipath/app/src/context/state/atomic_gmm_state.rs (3)
utils/atomic-handle/src/lib.rs (6)
std(149-149)std(150-150)fmt(143-152)get(108-110)deref(120-122)clone(130-136)lightning-nf/omnipath/app/src/context/state/gmm_state.rs (5)
fmt(168-184)fmt(188-193)can_access_services(77-79)is_transitional(82-92)default(148-150)lightning-nf/omnipath/app/src/context/ue_context.rs (1)
fmt(30-39)
lightning-nf/omnipath/app/src/context/state/gmm_state.rs (3)
lightning-nf/omnipath/app/src/context/state/atomic_gmm_state.rs (5)
fmt(211-217)can_access_services(170-172)is_transitional(176-178)default(183-185)from(205-207)lightning-nf/omnipath/app/src/context/ue_context.rs (1)
fmt(30-39)lightning-nf/omnipath/app/src/nas/nas_context.rs (1)
GmmState(21-24)
lightning-nf/omnipath/app/src/context/mod.rs (1)
lightning-nf/omnipath/app/src/nas/handlers/mod.rs (1)
gmm_status(3-3)
lightning-nf/omnipath/app/src/nas_old/mod.rs (3)
lightning-nf/omnipath/app/src/nas_old/handlers/authentication_response.rs (1)
handle(8-15)lightning-nf/omnipath/app/src/nas_old/handlers/registration_request.rs (1)
handle(56-70)lightning-nf/omnipath/app/src/nas_old/builders/authentication_request.rs (1)
build(9-15)
utils/client/src/nf_clients/oauth_service.rs (3)
utils/client/src/nf_clients/mod.rs (1)
new(127-160)utils/client/src/nf_clients/service_discovery/mod.rs (7)
new(46-82)new(146-160)new(270-279)into_inner(292-294)poll_ready(308-345)call(347-374)layer(233-242)utils/client/src/nrf_client.rs (2)
new(127-140)from(549-557)
lightning-nf/omnipath/app/src/nas/registration_req/mod.rs (3)
lightning-nf/omnipath/app/src/nas/interface.rs (3)
pre_comp_state_change(51-57)state_transition_deregistered(90-95)new(24-29)lightning-nf/omnipath/app/src/nas/handlers/registration_request.rs (2)
initial_registration_handler(13-52)NasHandler(54-72)lightning-nf/omnipath/app/src/nas/gmm.rs (4)
NasContext(24-97)deregistered(68-96)registration_initiated(53-65)unauthenticated(28-39)
lightning-nf/omnipath/app/src/ngap/core/initial_ue_message.rs (5)
lightning-nf/omnipath/app/src/context/state/atomic_gmm_state.rs (2)
new(20-24)from(205-207)lightning-nf/omnipath/app/src/ngap/manager/context_manager.rs (1)
new(54-58)lightning-nf/omnipath/app/src/ngap/manager/context_queue.rs (1)
new(43-49)lightning-nf/omnipath/app/src/utils/seq_lock.rs (1)
new(11-15)lightning-nf/omnipath/app/src/nas/ue_actions.rs (2)
UeContext(3-25)handle_nas(4-24)
lightning-nf/omnipath/app/src/context/ue_context.rs (3)
utils/atomic-handle/src/lib.rs (8)
std(149-149)std(150-150)new(95-102)get_atomic(14-14)get_atomic(177-179)get_atomic(183-185)get_atomic(189-191)get_atomic(196-198)lightning-nf/omnipath/app/src/context/state/atomic_gmm_state.rs (1)
new(20-24)lightning-nf/omnipath/app/src/nas/nas_context.rs (2)
GmmState(21-24)NasContext(8-13)
utils/client/src/nrf_client.rs (1)
utils/client/src/nf_clients/mod.rs (1)
request(162-180)
lightning-nf/omnipath/app/src/ngap/engine/mod.rs (1)
lightning-nf/omnipath/app/src/ngap/mod.rs (4)
models(8-10)core(4-4)engine(3-3)network(6-6)
lightning-nf/omnipath/app/src/nas/mod.rs (2)
lightning-nf/omnipath/app/src/nas/handlers/registration_request.rs (1)
NasHandler(54-72)lightning-nf/omnipath/app/src/nas/handlers/authentication_response.rs (1)
NasHandler(6-16)
Cargo.toml (1)
lightning-nf/omnipath/app/src/config/mod.rs (1)
SCTP(178-183)
utils/atomic-handle/src/lib.rs (2)
lightning-nf/omnipath/app/src/context/ue_context.rs (1)
get_atomic(24-26)lightning-nf/omnipath/app/src/context/state/atomic_gmm_state.rs (5)
new(20-24)get(146-148)deref(191-193)clone(199-201)load(28-44)
utils/client/src/nf_clients/service_discovery/mod.rs (4)
utils/client/src/nf_clients/oauth_service.rs (11)
nrf_client(60-62)nrf_client(95-96)nrf_client(145-147)new(27-37)new(118-126)service_names(55-57)service_names(140-142)layer(153-158)inner(40-42)poll_ready(78-83)call(85-106)utils/client/src/nf_clients/mod.rs (3)
new(127-160)request(162-180)base_url(67-67)utils/client/src/nf_clients/service_discovery/future.rs (2)
new(33-37)error(40-44)utils/client/src/nrf_client.rs (2)
new(127-140)from(549-557)
utils/client/src/nf_clients/mod.rs (3)
utils/client/src/nf_clients/oauth_service.rs (5)
new(27-37)new(118-126)nrf_client(60-62)nrf_client(95-96)nrf_client(145-147)utils/client/src/nf_clients/service_discovery/future.rs (1)
new(33-37)utils/client/src/nf_clients/service_discovery/mod.rs (3)
new(46-82)new(146-160)new(270-279)
🔇 Additional comments (13)
lightning-nf/omnipath/app/src/ngap/engine/controller.rs (1)
10-14: Import path aligns with module rename.Confirming the interface rename is reflected here; everything builds against the new module structure cleanly.
lightning-nf/omnipath/app/src/ngap/engine/mod.rs (1)
2-7: Interface module rename looks good.
mod interface;plus the re-export keeps downstream imports stable after the rename.lightning-nf/omnipath/app/src/context/ue_context.rs (1)
37-37: Debug output is concise and safePrinting
gmmviastate.get()is a good, stable choice for logs.lightning-nf/omnipath/app/src/nas_old/mod.rs (1)
25-30: Builder trait looks fineThe
NasBuildersignature is clear and composable.lightning-nf/omnipath/app/src/ngap/core/initial_ue_message.rs (1)
61-77: No change needed forGmmStatevariant
TheGmmStatein this file is imported fromcrate::context, whose enum definesDeregistered(notGmmDeregistered), soAtomicGmmState::new(GmmState::Deregistered)is correct.lightning-nf/omnipath/app/src/context/state/gmm_state.rs (2)
138-140: Self→self transitions: confirm intentional.Allowing identity transitions can mask no‑ops where callers expect a change. If not required, disallow to catch bugs; otherwise add a short comment stating why it’s allowed.
5-35: Ignore legacy GmmState in nas_old—only one active GmmState remains
The only GmmState used by the build is the enum in context/state/gmm_state.rs; the one in src/nas_old/nas_context.rs lives in an unused directory and has no imports in active code.Likely an incorrect or invalid review comment.
utils/client/src/nf_clients/oauth_service.rs (2)
93-101: Fail-open behavior when OAuth is disabled or temporarily unavailableIf OAuth is disabled or token retrieval is transiently failing, returning Err here will make all requests fail. Confirm NrfClient::set_auth_token returns Ok(()) when disabled and uses cached tokens to avoid per-request NRF calls.
65-73: Verify ADT const-generic setup for NfType
- Confirm the crate root (e.g. lib.rs or main.rs) includes
#![feature(adt_const_params)]to enableconst TARGET_NF_TYPE: NfType.- Ensure the
NfTypeenum derives all traits required for a const‐generic parameter (e.g.Copy,Eq).Applies to oauth_service.rs lines 65–73 and 85–106
lightning-nf/omnipath/app/src/nas/interface.rs (2)
32-37: Trait bounds look goodReasonable defaults, explicit decode hook, and dispatcher structure are clear.
1-1: No+ Syncbounds found in anyNasMessageHandleimpl return types. All existing implementations rely on the trait’s defaulthandle(which still includes+ Send + Sync), and none override it—therefore there are no+ Syncbounds in impls to remove.utils/client/src/nf_clients/service_discovery/mod.rs (1)
227-244: Layer implementation is correct and clone-safe.
Layer<S> for ServiceDiscoveryLayerreturns a freshServiceDiscoverywith no shared future; safe and idiomatic.utils/client/src/nf_clients/mod.rs (1)
146-154: Layer order confirmed; approving.
Pipeline matches TowerReqwestClient alias (ServiceDiscovery → OAuth → SetSensitive → Trace → HttpClient).
| impl Deref for AtomicGmmState { | ||
| type Target = AtomicUsize; | ||
|
|
||
| fn deref(&self) -> &Self::Target { | ||
| &self.state | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Deref leaks the raw AtomicUsize and breaks your invariants
By exposing &AtomicUsize, callers can invoke operations like fetch_add or store with arbitrary values, producing invalid discriminants and triggering UB once load() transmutes them. This defeats the entire safety contract of AtomicGmmState. Please drop the Deref impl (or expose only constrained helpers) so the raw atomic ops remain encapsulated.
-impl Deref for AtomicGmmState {
- type Target = AtomicUsize;
-
- fn deref(&self) -> &Self::Target {
- &self.state
- }
-}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| impl Deref for AtomicGmmState { | |
| type Target = AtomicUsize; | |
| fn deref(&self) -> &Self::Target { | |
| &self.state | |
| } | |
| } |
🤖 Prompt for AI Agents
In lightning-nf/omnipath/app/src/context/state/atomic_gmm_state.rs around lines
188-194, the Deref impl returns &AtomicUsize which leaks the raw atomic and
allows callers to call fetch_add/store with arbitrary values (violating
invariants and risking UB when you transmute load results); remove the Deref
implementation and instead keep the AtomicUsize private, then add controlled
accessor/mutator methods on AtomicGmmState (e.g., safe load(),
compare_exchange/validated_store(), increment_if_valid(), etc.) that enforce
valid discriminants and encapsulate all atomic operations so external code
cannot perform raw atomic ops.
| impl AtomicOperation<GmmStateField> for UeContext { | ||
| fn get_atomic(&self) -> &AtomicUsize { | ||
| &self.state | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
get_atomic relies on implicit Deref — make the dependency explicit (or add AsRef)
Returning &self.state assumes AtomicGmmState: Deref<Target = AtomicUsize>. Prefer an explicit projection to the inner AtomicUsize to avoid brittle coupling.
impl AtomicOperation<GmmStateField> for UeContext {
- fn get_atomic(&self) -> &AtomicUsize {
- &self.state
- }
+ fn get_atomic(&self) -> &AtomicUsize {
+ // Prefer an explicit projection, e.g., via AsRef or a dedicated accessor
+ self.state.as_ref()
+ }
}If AsRef<AtomicUsize> is not implemented for AtomicGmmState, add:
// in context/state/atomic_gmm_state.rs
impl AsRef<std::sync::atomic::AtomicUsize> for AtomicGmmState {
fn as_ref(&self) -> &std::sync::atomic::AtomicUsize { &self.state }
}Run to verify Deref/AsRef existence and call sites:
🏁 Script executed:
#!/bin/bash
rg -nP 'struct\s+AtomicGmmState\b|impl\s+Deref<\s*Target\s*=\s*AtomicUsize\s*>\s+for\s+AtomicGmmState|impl\s+AsRef<\s*AtomicUsize\s*>\s+for\s+AtomicGmmState' -C2Length of output: 478
Make the AtomicUsize projection explicit — implement AsRef or return the inner AtomicUsize
AtomicGmmState wraps an AtomicUsize and no Deref/AsRef exists; returning &self.state depends on an implicit conversion. Replace the implicit projection with an explicit one.
-
Change in lightning-nf/omnipath/app/src/context/ue_context.rs:
fn get_atomic(&self) -> &AtomicUsize { self.state.as_ref() } -
If AsRef is missing, add in lightning-nf/omnipath/app/src/context/state/atomic_gmm_state.rs:
impl AsRefstd::sync::atomic::AtomicUsize for AtomicGmmState {
fn as_ref(&self) -> &std::sync::atomic::AtomicUsize { &self.state }
}
🤖 Prompt for AI Agents
In lightning-nf/omnipath/app/src/context/ue_context.rs around lines 23 to 26,
the method get_atomic currently returns &self.state relying on an implicit
projection; update it to call the explicit AsRef conversion (e.g.
self.state.as_ref()) or otherwise return the inner AtomicUsize reference
explicitly. If AsRef is not yet implemented, add impl
AsRef<std::sync::atomic::AtomicUsize> for AtomicGmmState in
lightning-nf/omnipath/app/src/context/state/atomic_gmm_state.rs with as_ref
returning &self.state, then change fn get_atomic(&self) -> &AtomicUsize {
self.state.as_ref() } to use that explicit projection.
| pub trait NasHandler { | ||
| fn handle( | ||
| &self, | ||
| nas_context: &mut NasContext, | ||
| ue_context: &mut UeContext, | ||
| ) -> impl Future<Output = Result<(), NasHandlerError>> + Send; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
impl Future in trait is not stable here — switch to GAT associated future or async-trait
Returning impl Future in trait methods (RPITIT) is not usable in stable Rust across broad MSRV ranges. Use a GAT associated type or async_trait.
GAT option (zero allocation, idiomatic on stable):
-pub trait NasHandler {
- fn handle(
- &self,
- nas_context: &mut NasContext,
- ue_context: &mut UeContext,
- ) -> impl Future<Output = Result<(), NasHandlerError>> + Send;
-}
+pub trait NasHandler {
+ type Fut<'a>: Future<Output = Result<(), NasHandlerError>> + Send
+ where
+ Self: 'a;
+
+ fn handle<'a>(
+ &'a self,
+ nas_context: &'a mut NasContext,
+ ue_context: &'a mut UeContext,
+ ) -> Self::Fut<'a>;
+}Alternatively:
use async_trait::async_trait;
#[async_trait]
pub trait NasHandler {
async fn handle(
&self,
nas_context: &mut NasContext,
ue_context: &mut UeContext,
) -> Result<(), NasHandlerError>;
}Update implementors accordingly (builders/handlers).
🤖 Prompt for AI Agents
In lightning-nf/omnipath/app/src/nas_old/mod.rs around lines 16–22, the trait
currently returns an `impl Future` which is unstable; replace it by either (A) a
GAT-style associated future: add an associated generic Future type on the trait
(e.g. Future<'a> that implements Future<Output = Result<(), NasHandlerError>> +
Send with the appropriate lifetime bound) and change `handle` to take
lifetime-parameterized references and return that associated Future type, then
update all implementors to return that concrete future type; or (B) use the
async-trait crate: annotate the trait with the async_trait attribute and make
`handle` an async fn returning `Result<(), NasHandlerError>`, then update all
implementors to use async functions — pick one approach and update all
implementations and imports accordingly.
| ) -> impl Future<Output = Result<(State, Option<NgapPdu>), Self::Error>> + Send + Sync { | ||
| async move { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overly strict Sync bound on returned futures blocks common implementations
Requiring Future + Sync prevents handlers that mutate self inside async blocks (capturing &mut self makes futures !Sync). Drop Sync and keep Send; tokio::spawn only needs Send.
- ) -> impl Future<Output = Result<(State, Option<NgapPdu>), Self::Error>> + Send + Sync {
+ ) -> impl Future<Output = Result<(State, Option<NgapPdu>), Self::Error>> + Send {
@@
- ) -> impl Future<Output = Result<(State, Option<NgapPdu>), Self::Error>> + Send + Sync {
+ ) -> impl Future<Output = Result<(State, Option<NgapPdu>), Self::Error>> + Send {
@@
- ) -> impl std::future::Future<Output = Result<(GmmState, Option<NgapPdu>), Self::Error>>
- + Send
- + Sync {
+ ) -> impl std::future::Future<Output = Result<(GmmState, Option<NgapPdu>), Self::Error>> + Send {Apply the same -Sync change to each per-state handler signature.
Also applies to: 93-96, 100-103, 107-110, 114-117, 121-124, 126-130, 132-136, 138-143, 145-150, 191-199
🤖 Prompt for AI Agents
In lightning-nf/omnipath/app/src/nas/interface.rs around lines 64-65 (and also
update the same change at ranges 93-96, 100-103, 107-110, 114-117, 121-124,
126-130, 132-136, 138-143, 145-150, 191-199), the handler signatures currently
return futures bounded by Send + Sync which prevents common implementations that
capture &mut self (making futures !Sync); remove the Sync bound and keep only
Send on the returned Future types so the signatures become impl Future<...> +
Send (not + Sync), and apply this exact -Sync change consistently to each
per-state handler signature listed.
| fn state_transition_irrecoverable( | ||
| &mut self, | ||
| req: Self::Request, | ||
| ) -> impl Future<Output = Result<(State, Option<NgapPdu>), Self::Error>> + Send + Sync { | ||
| async move { Err(InvalidStateTransition::new(GmmState::CommonProcedureInitiated, req).into()) } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: wrong state reported for Irrecoverable default transition
Should use GmmState::Irrecoverable, not CommonProcedureInitiated.
- async move { Err(InvalidStateTransition::new(GmmState::CommonProcedureInitiated, req).into()) }
+ async move { Err(InvalidStateTransition::new(GmmState::Irrecoverable, req).into()) }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| fn state_transition_irrecoverable( | |
| &mut self, | |
| req: Self::Request, | |
| ) -> impl Future<Output = Result<(State, Option<NgapPdu>), Self::Error>> + Send + Sync { | |
| async move { Err(InvalidStateTransition::new(GmmState::CommonProcedureInitiated, req).into()) } | |
| } | |
| fn state_transition_irrecoverable( | |
| &mut self, | |
| req: Self::Request, | |
| ) -> impl Future<Output = Result<(State, Option<NgapPdu>), Self::Error>> + Send + Sync { | |
| async move { Err(InvalidStateTransition::new(GmmState::Irrecoverable, req).into()) } | |
| } |
🤖 Prompt for AI Agents
In lightning-nf/omnipath/app/src/nas/interface.rs around lines 145 to 150, the
default irrecoverable state transition currently constructs an
InvalidStateTransition with GmmState::CommonProcedureInitiated; change that to
GmmState::Irrecoverable so the error reports the correct target state. Update
the InvalidStateTransition::new call to pass GmmState::Irrecoverable and keep
the rest of the async return signature and Err(...) wrapping unchanged.
| let url_str = format!("{}://{:?}:{}", scheme, host, port); | ||
| Url::parse(&url_str).ok() | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Broken URL construction: using {:?} for host yields invalid URLs.
Debug formatting inserts quotes; parsing may fail. Use {}.
- let url_str = format!("{}://{:?}:{}", scheme, host, port);
+ let url_str = format!("{}://{}:{}", scheme, host, port);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| let url_str = format!("{}://{:?}:{}", scheme, host, port); | |
| Url::parse(&url_str).ok() | |
| }) | |
| let url_str = format!("{}://{}:{}", scheme, host, port); | |
| Url::parse(&url_str).ok() | |
| }) |
🤖 Prompt for AI Agents
In utils/client/src/nf_clients/service_discovery/mod.rs around lines 63–65, the
URL is being built with debug formatting "{:?}" for the host which inserts
quotes and produces invalid URLs; change the formatter to use "{}", ensuring the
host is formatted via Display (e.g., replace the "{:?}" usage with "{}"),
rebuild and run tests to confirm Url::parse succeeds with the corrected string.
| self: Self | ||
| ) -> Result<Arc<CachedNfProfile>, ServiceDiscoveryError> { | ||
| // Acquire a permit. This will pause if another task is already updating. | ||
| let _permit = self.discovery_semaphore.acquire().await.unwrap(); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use acquire_owned() to keep the future Send; avoid unwrap().
Semaphore::acquire() returns a non-Send permit, making the async future !Send, but you store it in a Pin<Box<dyn Future + Send>>. Also, unwrap() can panic.
- let _permit = self.discovery_semaphore.acquire().await.unwrap();
+ let _permit = self
+ .discovery_semaphore
+ .clone()
+ .acquire_owned()
+ .await
+ .map_err(|_| ServiceDiscoveryError::SemaphoreClosed)?;Add an error variant:
#[derive(thiserror::Error, Debug)]
pub enum ServiceDiscoveryError {
+ #[error("Discovery semaphore closed")]
+ SemaphoreClosed,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| self: Self | |
| ) -> Result<Arc<CachedNfProfile>, ServiceDiscoveryError> { | |
| // Acquire a permit. This will pause if another task is already updating. | |
| let _permit = self.discovery_semaphore.acquire().await.unwrap(); | |
| ++ b/utils/client/src/nf_clients/service_discovery/mod.rs | |
| @@ -181,7 +181,12 @@ impl ServiceDiscoveryClient { | |
| // Acquire a permit. This will pause if another task is already updating. | |
| let _permit = self | |
| .discovery_semaphore | |
| .clone() | |
| .acquire_owned() | |
| .await | |
| .map_err(|_| ServiceDiscoveryError::SemaphoreClosed)?; |
| self: Self | |
| ) -> Result<Arc<CachedNfProfile>, ServiceDiscoveryError> { | |
| // Acquire a permit. This will pause if another task is already updating. | |
| let _permit = self.discovery_semaphore.acquire().await.unwrap(); | |
| ++ b/utils/client/src/nf_clients/service_discovery/mod.rs | |
| @@ -10,6 +10,9 @@ | |
| #[derive(thiserror::Error, Debug)] | |
| pub enum ServiceDiscoveryError { | |
| #[error("Discovery semaphore closed")] | |
| SemaphoreClosed, | |
| // ... existing variants ... | |
| } |
🤖 Prompt for AI Agents
In utils/client/src/nf_clients/service_discovery/mod.rs around lines 181 to 185,
the code uses self.discovery_semaphore.acquire().await.unwrap(), which yields a
non-Send permit and can panic; change this to use
self.discovery_semaphore.acquire_owned().await and map the error into a new
ServiceDiscoveryError variant instead of unwrap-ing, keep the owned permit bound
for the duration of the update to ensure it remains Send, and add an appropriate
enum variant (e.g., SemaphoreClosed or PermitAcquisitionFailed) to
ServiceDiscoveryError and return that variant on acquisition failure.
| fn poll_ready( | ||
| &mut self, | ||
| cx: &mut Context<'_>, | ||
| ) -> Poll<Result<(), Self::Error>> { | ||
| // First check if we have a valid cache (fast path) | ||
| if self.layer.is_cache_valid() { | ||
| // Cache is valid, just check if inner service is ready | ||
| return self.inner.poll_ready(cx).map_err(Into::into); | ||
| } | ||
|
|
||
| // Cache is stale, we need to discover | ||
| loop { | ||
| if let Some(ref mut discovery_fut) = self.discovery_future { | ||
| // We have an ongoing discovery, poll it | ||
| match discovery_fut.as_mut().poll(cx) { | ||
| Poll::Ready(Ok(_cached_profile)) => { | ||
| // Discovery completed successfully | ||
| self.discovery_future = None; | ||
| // Now check if inner service is ready | ||
| return self.inner.poll_ready(cx).map_err(Into::into); | ||
| } | ||
| Poll::Ready(Err(e)) => { | ||
| // Discovery failed | ||
| self.discovery_future = None; | ||
| return Poll::Ready(Err(Box::new(e))); | ||
| } | ||
| Poll::Pending => { | ||
| // Discovery is still in progress | ||
| return Poll::Pending; | ||
| } | ||
| } | ||
| } else { | ||
| // No ongoing discovery, start one | ||
| self.discovery_future = Some(Box::pin(ServiceDiscoveryLayer::discover_and_update_cache(self.layer.clone()))); | ||
| // Continue the loop to poll the new future | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
poll_ready state machine is solid; single in-flight discovery per service.
The double-check after acquiring and proper error propagation are good.
Run quick greps to ensure no non-Send permits remain and bad URL formatting is gone:
🏁 Script executed:
#!/bin/bash
rg -nP 'acquire\(' utils/client/src/nf_clients/service_discovery
rg -nP 'format!\([^)]*{:?\}' utils/client/src/nf_clients/service_discoveryLength of output: 245
🏁 Script executed:
#!/bin/bash
sed -n '150,200p' utils/client/src/nf_clients/service_discovery/mod.rsLength of output: 1573
🏁 Script executed:
#!/bin/bash
sed -n '280,380p' utils/client/src/nf_clients/service_discovery/mod.rsLength of output: 2949
poll_ready logic is solid and semaphore permits are Send. Fix the URL formatting
Line 63 still uses a debug specifier—replace
let url_str = format!("{}://{:?}:{}", scheme, host, port);with
let url_str = format!("{}://{}:{}", scheme, host, port);🤖 Prompt for AI Agents
In utils/client/src/nf_clients/service_discovery/mod.rs around lines 308 to 345,
the URL string is being built with a debug formatter for the host which produces
extra quotes/braces; replace the debug specifier with the display specifier so
the host is formatted as a plain string (i.e., change the format call to use {}
for host instead of {:?}); ensure the surrounding variables (scheme, host, port)
remain in the same order and types implement Display.
| AccessTokenRspTokenType::Bearer => { | ||
| let mut string = "Bearer ".to_owned(); | ||
| let mut string = "bearer ".to_owned(); | ||
| string.push_str(token); | ||
| string | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Authorization scheme must use canonical “Bearer” casing
OAuth 2.0 access tokens require the Authorization header to start with Bearer (RFC 6750). Emitting bearer <token> breaks interoperability with servers that enforce the scheme’s case. Please restore the capitalized form.
- let mut string = "bearer ".to_owned();
+ let mut string = "Bearer ".to_owned();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| AccessTokenRspTokenType::Bearer => { | |
| let mut string = "Bearer ".to_owned(); | |
| let mut string = "bearer ".to_owned(); | |
| string.push_str(token); | |
| string | |
| } | |
| AccessTokenRspTokenType::Bearer => { | |
| let mut string = "Bearer ".to_owned(); | |
| string.push_str(token); | |
| string | |
| } |
🤖 Prompt for AI Agents
In utils/client/src/nrf_client.rs around lines 501 to 505, the Authorization
header value is constructed using lowercase "bearer " which violates RFC 6750;
change the prefix to "Bearer " (capital B) when building the string so the
header is emitted as "Bearer <token>" to ensure correct OAuth2 scheme casing and
interoperability.
| fn is_token_expired(token: &AccessTokenRsp) -> bool { | ||
| // Get current time | ||
| let now = SystemTime::now() | ||
| .duration_since(UNIX_EPOCH) | ||
| // SAFETY: As now system time should not be before UNIX_EPOCH | ||
| .unwrap_or_default() | ||
| .as_secs(); | ||
| // Check if token has expires_in field | ||
| if let Some(expires_in) = token.expires_in { | ||
| // Calculate expiry time (assuming the token was issued recently) | ||
| // In a real implementation, you might want to store the issue time | ||
| // For now, we assume the token was just issued | ||
| let buffer_seconds = 30; // 30 second buffer before expiry | ||
| let expires_at = now + expires_in as u64; | ||
| let expires_with_buffer = expires_at.saturating_sub(buffer_seconds); | ||
|
|
||
| now >= expires_with_buffer | ||
| } else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Token expiry check never invalidates cached tokens
is_token_expired adds expires_in to the current timestamp, so the comparison becomes now >= now + expires_in - buffer, which never trips (unless expires_in ≤ buffer). As a result, cached tokens are treated as valid forever and we keep sending expired credentials to NRF once the real TTL elapses. Please derive the expiry using the moment the token was issued/stored (e.g., issued_at + expires_in) or track an absolute expiry at insertion time so stale tokens are evicted before reuse.
🤖 Prompt for AI Agents
In utils/client/src/nrf_client.rs around lines 521 to 538, the expiry check
incorrectly adds expires_in to the current time (now) which makes the token
never appear expired; change the logic to compute expiry from the token's
issuance time or from an absolute expiry stored when caching: either (a)
add/require an issued_at (unix seconds) on AccessTokenRsp and compute expires_at
= issued_at + expires_in then compare if now >=
expires_at.saturating_sub(buffer) or (b) when inserting the token into the cache
compute and store expires_at = now + expires_in and then compare now >=
expires_at.saturating_sub(buffer); ensure buffer is applied by subtracting it
from expires_at and handle missing fields gracefully.
Summary by CodeRabbit