Conversation
Client-side SDKs are moving to the FDv2 endpoints, which the dev server did
not serve at all: an SDK upgraded to an FDv2-capable version and pointed at
the dev server asked for routes that were not there.
Adds POST/REPORT /sdk/poll/eval, GET /sdk/poll/eval/{context},
POST/REPORT /sdk/stream/eval and GET /sdk/stream/eval/{context}. FDv2
unifies the browser and mobile endpoints, so this one pair of routes covers
what /eval/{envId}, /meval, /sdk/evalx/{envId} and /msdk/evalx did in FDv1.
The protocol event builders are parameterized over the put-object kind and
object shape rather than forked, so the two protocols differ only in that
client-side put-objects carry a pre-evaluated flag-eval result where
server-side ones carry the flag configuration.
Co-authored-by: Ramon Niebla <nieblara@users.noreply.github.com>
Covers the flag-eval object shape, base64 context decoding across the url-safe and standard variants, the credential and context matrices for both the path and body forms, CORS preflight on all four routes, and an end-to-end SSE stream carrying an initial transfer plus an override. Co-authored-by: Ramon Niebla <nieblara@users.noreply.github.com>
Co-authored-by: Ramon Niebla <nieblara@users.noreply.github.com>
The two handlers loaded the project, applied overrides, and built the initial response identically. Co-authored-by: Ramon Niebla <nieblara@users.noreply.github.com>
Keep main's per-route CORS method lists and this branch's FDv2 client routes. FDv1 eval routes use CorsHeadersForMethods; the new client-side FDv2 routes keep ClientFdv2CorsHeaders so Authorization stays allowed. Co-authored-by: Ramon Niebla <nieblara@users.noreply.github.com>
The FDv2 client-side protocol takes the evaluation context in a POST body or a GET path; REPORT is not part of it, and the JS SDK never sends it. POST /sdk/poll/eval and POST /sdk/stream/eval now answer REPORT with 405, and their CORS configuration no longer allows it. The FDv1 client-side routes still accept REPORT. Co-authored-by: Ramon Niebla <nieblara@users.noreply.github.com>
The dev server serves the same flag values to everyone, so it never uses the evaluation context. Parsing it as a full LaunchDarkly context only added a way to reject a request the dev server could have served. Requests are still rejected with 400 when the context is not base64 or not well-formed JSON. A well-formed context missing fields such as key is now accepted. Co-authored-by: Ramon Niebla <nieblara@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requirements
Related issues
This PR uses the same pattern as the earlier server-side work in #699, #701, and #703.
Describe the solution you've provided
Summary
ldcli dev-serverruns a local copy of LaunchDarkly. Your app can then get flag values from your computer instead of from the LaunchDarkly service.An SDK is the LaunchDarkly library that your app uses to get flag values. The newer client-side SDKs (the SDKs for browsers and mobile apps) use a newer protocol, FDv2. FDv2 uses different endpoints that an SDK calls. The dev server did not have these endpoints. As a result, an upgraded client-side SDK got no flag values from the dev server.
This PR adds the missing endpoints. Older SDKs and server-side SDKs will get the same responses as before.
New endpoints
The context is the user or device that the SDK gets flag values for. The SDK sends the context with each request.
/sdk/poll/eval/sdk/poll/eval/{context}/sdk/stream/eval/sdk/stream/eval/{context}All four endpoints also accept the
OPTIONSrequests that browsers send for CORS. CORS is the set of browser rules that controls which websites can call a server. Client-side SDKs for browsers and for mobile apps use the same four endpoints. These endpoints replace four older groups of endpoints. The older endpoints stay available for SDKs that did not upgrade.How the dev server handles a request
The dev server does these steps in this order:
Authorizationheader. This step comes first because a preflight request does not include a credential.Authorizationheader. Browser SDKs send it in theauthquery parameter. If both are present, the dev server uses the header. If neither is present, the dev server returns401.400.The dev server gives the same flag values to all users. As a result, it does not use the context after step 4.
Each flag in the response looks like this:
{"version":1,"kind":"flag-eval","key":"bool-flag","object":{"flagVersion":3,"value":true,"variation":0,"trackEvents":true}}Other details
Server-side SDKs get the full rules for each flag, and they calculate the value themselves. Client-side SDKs get only the result, with the kind
flag-eval. The kind isflag-evalwith a hyphen. It is notflag_eval. The JS SDK reads onlyflag-eval(flagEvalMapper.ts). It ignores all other kinds and does not show an error.The server-side responses and the client-side responses use the same code. This code takes an encoder, which is a small value that sets the object kind and the object fields. The two responses are different only in these two parts. The server-side output did not change. It is the same as before, byte for byte.
The older client-side endpoints accept the HTTP method REPORT. FDv2 does not use REPORT. The JS SDK sends only GET or POST (
FDv2Requestor.ts). As a result, the new endpoints return405for REPORT.If an SDK connects again with an older version of the flags, the dev server sends all the flag values again. It does not send only the changes. The payloads are small, and the server-side endpoints do the same thing.
If the SDK asks for evaluation reasons (
withReasons=true), each flag has the reasonFALLTHROUGH. If no targeting rule matches, a flag gives its fallthrough value. The dev server has no targeting rules, so each value is the fallthrough value.Tests
The unit tests and handler tests cover these areas:
We also ran the dev server with a project imported from a file. This test does not need a LaunchDarkly account. The results were:
405.401.400.We also ran this branch and
mainat the same time, with the same project. All existing endpoints returned the same responses, byte for byte. This includes the server-side endpoints and the older client-side endpoints.Describe alternatives you've considered
We did not write separate code for the client-side responses. The two protocols are different only in the object kind and the object fields. With one shared implementation, there is only one copy of the shared parts.
We did not accept REPORT on the new endpoints, as the older endpoints do. FDv2 SDKs use POST instead.
We did not require a complete context, for example a context with a
key. The dev server does not use the context. A stricter rule can only reject a request that the dev server can serve. The dev server still rejects JSON that is not valid.We did not send only the changes to an SDK that connects again. The reasons are in "Other details".
Additional context
FDv2 SDKs must accept a
pingmessage on the stream. The dev server does not sendping. As a result, this PR does not need to do anything for it.If a response includes the
x-ld-fd-fallbackheader, an FDv2 SDK goes back to the older protocol. The dev server supports FDv2 fully, so it never sends this header.At intervals, the stream sends an SSE comment, which is a line with no event data. The comment keeps the connection open. The server-side FDv2 stream does the same thing.
The package documentation in
internal/dev_server/sdk/docs.gonow lists all the FDv2 endpoints. This includes two server-side endpoints that were missing.