only base64 non-utf8
This commit is contained in:
parent
b290a72025
commit
eb4fb388bd
3 changed files with 51 additions and 19 deletions
|
@ -41,7 +41,7 @@ instance APIVersion V2 where protocolVersion _ = P2P.ProtocolVersion 2
|
||||||
instance APIVersion V1 where protocolVersion _ = P2P.ProtocolVersion 1
|
instance APIVersion V1 where protocolVersion _ = P2P.ProtocolVersion 1
|
||||||
instance APIVersion V0 where protocolVersion _ = P2P.ProtocolVersion 0
|
instance APIVersion V0 where protocolVersion _ = P2P.ProtocolVersion 0
|
||||||
|
|
||||||
-- Keys, UUIDs, and filenames are base64 encoded since Servant uses
|
-- Keys, UUIDs, and filenames can be base64 encoded since Servant uses
|
||||||
-- Text and so needs UTF-8.
|
-- Text and so needs UTF-8.
|
||||||
newtype B64Key = B64Key Key
|
newtype B64Key = B64Key Key
|
||||||
deriving (Show)
|
deriving (Show)
|
||||||
|
@ -52,7 +52,32 @@ newtype B64FilePath = B64FilePath RawFilePath
|
||||||
newtype B64UUID t = B64UUID { fromB64UUID :: UUID }
|
newtype B64UUID t = B64UUID { fromB64UUID :: UUID }
|
||||||
deriving (Show, Ord, Eq, Generic, NFData)
|
deriving (Show, Ord, Eq, Generic, NFData)
|
||||||
|
|
||||||
-- Phantom types for B64UIID
|
encodeB64Text :: B.ByteString -> T.Text
|
||||||
|
encodeB64Text b = case TE.decodeUtf8' b of
|
||||||
|
Right t
|
||||||
|
| (snd <$> B.unsnoc b) == Just closebracket
|
||||||
|
&& (fst <$> B.uncons b) == Just openbracket ->
|
||||||
|
b64wrapped
|
||||||
|
| otherwise -> t
|
||||||
|
Left _ -> b64wrapped
|
||||||
|
where
|
||||||
|
b64wrapped = TE.decodeUtf8Lenient $ "[" <> B64.encode b <> "]"
|
||||||
|
openbracket = fromIntegral (ord '[')
|
||||||
|
closebracket = fromIntegral (ord ']')
|
||||||
|
|
||||||
|
decodeB64Text :: T.Text -> Either T.Text B.ByteString
|
||||||
|
decodeB64Text t =
|
||||||
|
case T.unsnoc t of
|
||||||
|
Just (t', lastc) | lastc == ']' ->
|
||||||
|
case T.uncons t' of
|
||||||
|
Just (firstc, t'') | firstc == '[' ->
|
||||||
|
case B64.decode (TE.encodeUtf8 t'') of
|
||||||
|
Right b -> Right b
|
||||||
|
Left _ -> Left "unable to base64 decode [] wrapped value"
|
||||||
|
_ -> Right (TE.encodeUtf8 t)
|
||||||
|
_ -> Right (TE.encodeUtf8 t)
|
||||||
|
|
||||||
|
-- Phantom types.
|
||||||
data ClientSide
|
data ClientSide
|
||||||
data ServerSide
|
data ServerSide
|
||||||
data Bypass
|
data Bypass
|
||||||
|
@ -163,33 +188,31 @@ parseAPIVersion v need t
|
||||||
| otherwise = Left "bad version"
|
| otherwise = Left "bad version"
|
||||||
|
|
||||||
instance ToHttpApiData B64Key where
|
instance ToHttpApiData B64Key where
|
||||||
toUrlPiece (B64Key k) = TE.decodeUtf8Lenient $
|
toUrlPiece (B64Key k) = encodeB64Text (serializeKey' k)
|
||||||
B64.encode (serializeKey' k)
|
|
||||||
|
|
||||||
instance FromHttpApiData B64Key where
|
instance FromHttpApiData B64Key where
|
||||||
parseUrlPiece t = case B64.decode (TE.encodeUtf8 t) of
|
parseUrlPiece t = case decodeB64Text t of
|
||||||
Left _ -> Left "unable to base64 decode key"
|
|
||||||
Right b -> maybe (Left "key parse error") (Right . B64Key)
|
Right b -> maybe (Left "key parse error") (Right . B64Key)
|
||||||
(deserializeKey' b)
|
(deserializeKey' b)
|
||||||
|
Left err -> Left err
|
||||||
|
|
||||||
instance ToHttpApiData (B64UUID t) where
|
instance ToHttpApiData (B64UUID t) where
|
||||||
toUrlPiece (B64UUID u) = TE.decodeUtf8Lenient $
|
toUrlPiece (B64UUID u) = encodeB64Text (fromUUID u)
|
||||||
B64.encode (fromUUID u)
|
|
||||||
|
|
||||||
instance FromHttpApiData (B64UUID t) where
|
instance FromHttpApiData (B64UUID t) where
|
||||||
parseUrlPiece t = case B64.decode (TE.encodeUtf8 t) of
|
parseUrlPiece t = case decodeB64Text t of
|
||||||
Left _ -> Left "unable to base64 decode UUID"
|
|
||||||
Right b -> case toUUID b of
|
Right b -> case toUUID b of
|
||||||
u@(UUID _) -> Right (B64UUID u)
|
u@(UUID _) -> Right (B64UUID u)
|
||||||
NoUUID -> Left "empty UUID"
|
NoUUID -> Left "empty UUID"
|
||||||
|
Left err -> Left err
|
||||||
|
|
||||||
instance ToHttpApiData B64FilePath where
|
instance ToHttpApiData B64FilePath where
|
||||||
toUrlPiece (B64FilePath f) = TE.decodeUtf8Lenient $ B64.encode f
|
toUrlPiece (B64FilePath f) = encodeB64Text f
|
||||||
|
|
||||||
instance FromHttpApiData B64FilePath where
|
instance FromHttpApiData B64FilePath where
|
||||||
parseUrlPiece t = case B64.decode (TE.encodeUtf8 t) of
|
parseUrlPiece t = case decodeB64Text t of
|
||||||
Left _ -> Left "unable to base64 decode filename"
|
|
||||||
Right b -> Right (B64FilePath b)
|
Right b -> Right (B64FilePath b)
|
||||||
|
Left err -> Left err
|
||||||
|
|
||||||
instance ToHttpApiData Offset where
|
instance ToHttpApiData Offset where
|
||||||
toUrlPiece (Offset (P2P.Offset n)) = T.pack (show n)
|
toUrlPiece (Offset (P2P.Offset n)) = T.pack (show n)
|
||||||
|
@ -292,7 +315,7 @@ instance FromJSON PutOffsetResultPlus where
|
||||||
<*> v .: "plusuuids"
|
<*> v .: "plusuuids"
|
||||||
|
|
||||||
instance FromJSON (B64UUID t) where
|
instance FromJSON (B64UUID t) where
|
||||||
parseJSON (String t) = case B64.decode (TE.encodeUtf8 t) of
|
parseJSON (String t) = case decodeB64Text t of
|
||||||
Right s -> pure (B64UUID (toUUID s))
|
Right s -> pure (B64UUID (toUUID s))
|
||||||
Left _ -> mempty
|
Left _ -> mempty
|
||||||
parseJSON _ = mempty
|
parseJSON _ = mempty
|
||||||
|
@ -300,7 +323,7 @@ instance FromJSON (B64UUID t) where
|
||||||
instance ToJSON LockResult where
|
instance ToJSON LockResult where
|
||||||
toJSON (LockResult v (Just (B64UUID lck))) = object
|
toJSON (LockResult v (Just (B64UUID lck))) = object
|
||||||
[ "locked" .= v
|
[ "locked" .= v
|
||||||
, "lockid" .= TE.decodeUtf8Lenient (B64.encode (fromUUID lck))
|
, "lockid" .= encodeB64Text (fromUUID lck)
|
||||||
]
|
]
|
||||||
toJSON (LockResult v Nothing) = object
|
toJSON (LockResult v Nothing) = object
|
||||||
[ "locked" .= v
|
[ "locked" .= v
|
||||||
|
|
|
@ -11,11 +11,13 @@ repository might.
|
||||||
But this protocol requires that UTF-8 be used throughout, except
|
But this protocol requires that UTF-8 be used throughout, except
|
||||||
where bodies use `Content-Type: application/octet-stream`.
|
where bodies use `Content-Type: application/octet-stream`.
|
||||||
|
|
||||||
So, all git-annex keys, uuids, and filenames in this protocol are
|
So this protocol allows using
|
||||||
[base64url](https://datatracker.ietf.org/doc/html/rfc4648#section-5) encoded.
|
[base64url](https://datatracker.ietf.org/doc/html/rfc4648#section-5)
|
||||||
|
encoding for such values. Any key, filename, or UUID wrapped in square
|
||||||
|
brackets is a base64url encoded value.
|
||||||
|
For example, "[Zm9v]" is the same as "foo".
|
||||||
|
|
||||||
Examples in this document use non-base64url-encoded values to show the
|
A filename like "[foo]" will need to itself be encoded that way: "[W2Zvb10=]"
|
||||||
underlying data.
|
|
||||||
|
|
||||||
## authentication
|
## authentication
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,13 @@ This allows a git-annex repository to be accessed over HTTP.
|
||||||
It is the git-annex equivilant of git-http-backend(1), for serving
|
It is the git-annex equivilant of git-http-backend(1), for serving
|
||||||
a repository over HTTP with write access for authenticated users.
|
a repository over HTTP with write access for authenticated users.
|
||||||
|
|
||||||
|
As well as serving the git-annex HTTP API, this server provides a
|
||||||
|
convenient way to download the content of any key, by using the path
|
||||||
|
"/git-annex/$uuid/$key". For example:
|
||||||
|
|
||||||
|
$ curl http://localhost:8080/git-annex/f11773f0-11e1-45b2-9805-06db16768efe/key/SHA256E-s6--5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03
|
||||||
|
hello
|
||||||
|
|
||||||
# OPTIONS
|
# OPTIONS
|
||||||
|
|
||||||
* `--jobs=N`
|
* `--jobs=N`
|
||||||
|
|
Loading…
Reference in a new issue