map resolver 410 to NAME NOT_FOUND (a lapsed name is an answer, not a failure)

This commit is contained in:
Alain Brenzikofer
2026-09-04 09:09:31 +02:00
parent e4e3d0dc7c
commit 7e91f2e582
3 changed files with 26 additions and 2 deletions
+4
View File
@@ -76,6 +76,10 @@ fetch NamesEnv {resolverEnv} d =
mapResolverError :: ResolverError -> NameErrorType
mapResolverError = \case
HttpStatusErr 404 -> NOT_FOUND
-- 410 is a lapsed registration (past expiry, in grace or beyond): a correct
-- answer about the name, not a resolver failure, so it must not become
-- RESOLVER - that is reserved for the backing resolver/RPC breaking.
HttpStatusErr 410 -> NOT_FOUND
HttpStatusErr 400 -> NOT_FOUND
HttpStatusErr code -> RESOLVER ("HTTP " <> T.pack (show code))
HttpFailure _ -> RESOLVER "transport failure"
+13 -1
View File
@@ -19,7 +19,7 @@ import Data.List.NonEmpty (NonEmpty (..))
import Data.Text (Text)
import Data.Text.Encoding (encodeUtf8)
import Data.Time.Clock (getCurrentTime)
import Network.HTTP.Types (Status, status200, status404, status502)
import Network.HTTP.Types (Status, status200, status404, status410, status502)
import NamesResolverServer (memCfg, memCfg2, memProxyCfg, withNames)
import qualified NamesResolverServer as NRS
import SMPClient
@@ -74,6 +74,7 @@ rslvTests :: Spec
rslvTests = do
describe "RSLV direct (non-forwarded)" $ do
it "resolver replies 404 -> NAME NOT_FOUND (reached, not CMD PROHIBITED)" testRslvBackendNotFound
it "resolver replies 410 -> NAME NOT_FOUND (a lapsed name, not a resolver failure)" testRslvBackendGone
it "resolver replies 502 -> NAME (RESOLVER ..)" testRslvBackendHttpErr
it "no names config -> NAME NO_RESOLVER" testRslvDisabled
it "refuses to send RSLV on a session below namesSMPVersion" testRslvVersion
@@ -91,6 +92,17 @@ testRslvBackendNotFound =
corrId `shouldBe` CorrId "rs01"
resp `shouldBe` Right (ERR (NAME NOT_FOUND))
-- The resolver answers 410 for a registration that has lapsed (in grace or
-- past it). That is a correct answer about the name, so it has to arrive as
-- NOT_FOUND; RESOLVER would make the client treat it as a broken resolver and
-- abort domain verification instead of reporting the name as unverified.
testRslvBackendGone :: IO ()
testRslvBackendGone =
withResolverServer (status410, "{}") $
testSMPClient @TLS $ \h -> do
(_, _, resp) <- sendRslv h "rs08" (domain "lapsed.simplex")
resp `shouldBe` Right (ERR (NAME NOT_FOUND))
testRslvBackendHttpErr :: IO ()
testRslvBackendHttpErr =
withResolverServer (status502, "{}") $
+9 -1
View File
@@ -13,7 +13,7 @@ import Data.IORef (readIORef)
import Data.List (sort)
import qualified Data.Text as T
import Data.Text.Encoding (encodeUtf8)
import Network.HTTP.Types (status200, status400, status404, status500, status502)
import Network.HTTP.Types (status200, status400, status404, status410, status500, status502)
import NamesResolverServer (resolveResp, testNamesConfig, withResolverServer, withResolverServerDelayed)
import Simplex.Messaging.Encoding (smpDecode, smpEncode)
import Simplex.Messaging.Encoding.String (strDecode)
@@ -156,6 +156,14 @@ resolverSpec = do
env <- newNamesEnv (testNamesConfig port)
resolveName env aliceDomain `shouldReturn` Left NOT_FOUND
it "returns NOT_FOUND on 410 (registration lapsed)" $
-- A lapsed name is a correct answer, not a resolver failure: RESOLVER
-- would make the client abort domain verification instead of reporting
-- the name as unverified.
withResolverServer (resolveResp status410 "{}") $ \port _ -> do
env <- newNamesEnv (testNamesConfig port)
resolveName env aliceDomain `shouldReturn` Left NOT_FOUND
it "returns RESOLVER on 502 (upstream failure)" $
withResolverServer (resolveResp status502 "{}") $ \port _ -> do
env <- newNamesEnv (testNamesConfig port)