servant API type is complete
This commit is contained in:
		
					parent
					
						
							
								4133063ab1
							
						
					
				
			
			
				commit
				
					
						1dbb5ec70d
					
				
			
		
					 4 changed files with 171 additions and 100 deletions
				
			
		| 
						 | 
					@ -12,14 +12,7 @@
 | 
				
			||||||
module Command.P2PHttp where
 | 
					module Command.P2PHttp where
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Command
 | 
					import Command
 | 
				
			||||||
import qualified P2P.Protocol as P2P
 | 
					import P2P.Http
 | 
				
			||||||
import Utility.Base64
 | 
					 | 
				
			||||||
import Utility.MonotonicClock
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import Servant
 | 
					 | 
				
			||||||
import Servant.API.WebSocket
 | 
					 | 
				
			||||||
import qualified Data.Text.Encoding as TE
 | 
					 | 
				
			||||||
import qualified Data.ByteString as B
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
cmd :: Command
 | 
					cmd :: Command
 | 
				
			||||||
cmd = command "p2phttp" SectionPlumbing
 | 
					cmd = command "p2phttp" SectionPlumbing
 | 
				
			||||||
| 
						 | 
					@ -28,93 +21,3 @@ cmd = command "p2phttp" SectionPlumbing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
seek :: CmdParams -> CommandSeek
 | 
					seek :: CmdParams -> CommandSeek
 | 
				
			||||||
seek = error "TODO"
 | 
					seek = error "TODO"
 | 
				
			||||||
 | 
					 | 
				
			||||||
type API
 | 
					 | 
				
			||||||
	= "git-annex" 
 | 
					 | 
				
			||||||
		:> (("key" :> CaptureKey) :<|> ("v3" :> "key" :> CaptureKey))
 | 
					 | 
				
			||||||
		:> CommonParams Optional
 | 
					 | 
				
			||||||
		:> AssociatedFileParam
 | 
					 | 
				
			||||||
		:> OffsetParam
 | 
					 | 
				
			||||||
		:> StreamGet NoFraming OctetStream (SourceIO B.ByteString)
 | 
					 | 
				
			||||||
	:<|> "git-annex" :> "v3" :> "checkpresent"
 | 
					 | 
				
			||||||
		:> KeyParam
 | 
					 | 
				
			||||||
		:> CommonParams Required
 | 
					 | 
				
			||||||
		:> Post '[JSON] CheckPresentResult
 | 
					 | 
				
			||||||
	:<|> "git-annex" :> "v3" :> "lockcontent"
 | 
					 | 
				
			||||||
		:> KeyParam
 | 
					 | 
				
			||||||
		:> CommonParams Required
 | 
					 | 
				
			||||||
		:> WebSocket
 | 
					 | 
				
			||||||
	:<|> "git-annex" :> "v3" :> "remove"
 | 
					 | 
				
			||||||
		:> KeyParam
 | 
					 | 
				
			||||||
		:> CommonParams Required
 | 
					 | 
				
			||||||
		:> Post '[JSON] RemoveResult
 | 
					 | 
				
			||||||
	:<|> "git-annex" :> "v3" :> "remove-before"
 | 
					 | 
				
			||||||
		:> KeyParam
 | 
					 | 
				
			||||||
		:> CommonParams Required
 | 
					 | 
				
			||||||
		:> QueryParam' '[Required] "timestamp" MonotonicTimestamp
 | 
					 | 
				
			||||||
		:> Post '[JSON] RemoveResult
 | 
					 | 
				
			||||||
	:<|> "git-annex" :> "v3" :> "gettimestamp"
 | 
					 | 
				
			||||||
		:> CommonParams Required
 | 
					 | 
				
			||||||
		:> Post '[JSON] GetTimestampResult
 | 
					 | 
				
			||||||
	:<|> "git-annex" :> "v3" :> "put"
 | 
					 | 
				
			||||||
		:> KeyParam
 | 
					 | 
				
			||||||
		:> AssociatedFileParam
 | 
					 | 
				
			||||||
		:> OffsetParam
 | 
					 | 
				
			||||||
		:> Header' '[Required] "X-git-annex-object-size" ObjectSize
 | 
					 | 
				
			||||||
		:> CommonParams Required
 | 
					 | 
				
			||||||
		:> StreamBody NoFraming OctetStream (SourceIO B.ByteString)
 | 
					 | 
				
			||||||
		:> Post '[JSON] PutResult
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type CommonParams req
 | 
					 | 
				
			||||||
	= QueryParam' '[req] "clientuuid" B64UUID 
 | 
					 | 
				
			||||||
	:> QueryParam' '[req] "serveruuid" B64UUID
 | 
					 | 
				
			||||||
	:> QueryParams "bypass" B64UUID
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type CaptureKey = Capture "key" B64Key
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type KeyParam = QueryParam' '[Required] "key"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type AssociatedFileParam = QueryParam "associatedfile" B64FilePath
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
type OffsetParam = QueryParam "offset" P2P.Offset
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type GetKey
 | 
					 | 
				
			||||||
	= Capture "key" B64Key
 | 
					 | 
				
			||||||
	:> CommonParams Optional
 | 
					 | 
				
			||||||
	:> AssociatedFileParam
 | 
					 | 
				
			||||||
	:> OffsetParam
 | 
					 | 
				
			||||||
	:> StreamGet NoFraming OctetStream (SourceIO B.ByteString)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newtype ObjectSize = ObjectSize Integer
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newtype CheckPresentResult = CheckPresentResult Bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newtype RemoveResult = RemoveResult Bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newtype GetTimestampResult = GetTimestmapResult MonotonicTimestamp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newtype PutResult = PutResult Bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-- Keys, UUIDs, and filenames are base64 encoded since Servant uses 
 | 
					 | 
				
			||||||
-- Text and so needs UTF-8.
 | 
					 | 
				
			||||||
newtype B64Key = B64Key Key
 | 
					 | 
				
			||||||
newtype B64UUID = B64UUID UUID
 | 
					 | 
				
			||||||
newtype B64FilePath = B64FilePath RawFilePath
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
instance FromHttpApiData B64Key where
 | 
					 | 
				
			||||||
	parseUrlPiece t = case fromB64Maybe (TE.encodeUtf8 t) of
 | 
					 | 
				
			||||||
		Nothing -> Left "unable to base64 decode key"
 | 
					 | 
				
			||||||
		Just b -> maybe (Left "key parse error") (Right . B64Key)
 | 
					 | 
				
			||||||
			(deserializeKey' b)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
instance FromHttpApiData B64UUID where
 | 
					 | 
				
			||||||
	parseUrlPiece t = case fromB64Maybe (TE.encodeUtf8 t) of
 | 
					 | 
				
			||||||
		Nothing -> Left "unable to base64 decode UUID"
 | 
					 | 
				
			||||||
		Just b -> case toUUID b of
 | 
					 | 
				
			||||||
			u@(UUID _) -> Right (B64UUID u)
 | 
					 | 
				
			||||||
			NoUUID -> Left "empty UUID"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
instance FromHttpApiData B64FilePath where
 | 
					 | 
				
			||||||
	parseUrlPiece t = case fromB64Maybe (TE.encodeUtf8 t) of
 | 
					 | 
				
			||||||
		Nothing -> Left "unable to base64 decode filename"
 | 
					 | 
				
			||||||
		Just b -> Right (B64FilePath b)
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										167
									
								
								P2P/Http.hs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								P2P/Http.hs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,167 @@
 | 
				
			||||||
 | 
					{- P2P protocol over HTTP
 | 
				
			||||||
 | 
					 -
 | 
				
			||||||
 | 
					 - https://git-annex.branchable.com/design/p2p_protocol_over_http/
 | 
				
			||||||
 | 
					 -
 | 
				
			||||||
 | 
					 - Copyright 2024 Joey Hess <id@joeyh.name>
 | 
				
			||||||
 | 
					 -
 | 
				
			||||||
 | 
					 - Licensed under the GNU AGPL version 3 or higher.
 | 
				
			||||||
 | 
					 -}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-# LANGUAGE DataKinds #-}
 | 
				
			||||||
 | 
					{-# LANGUAGE TypeOperators #-}
 | 
				
			||||||
 | 
					{-# LANGUAGE OverloadedStrings #-}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module P2P.Http where
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Annex.Common
 | 
				
			||||||
 | 
					import qualified P2P.Protocol as P2P
 | 
				
			||||||
 | 
					import Utility.Base64
 | 
				
			||||||
 | 
					import Utility.MonotonicClock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Servant
 | 
				
			||||||
 | 
					import Servant.API.WebSocket
 | 
				
			||||||
 | 
					import qualified Data.Text.Encoding as TE
 | 
				
			||||||
 | 
					import qualified Data.ByteString as B
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type P2PAPI
 | 
				
			||||||
 | 
						= "git-annex" :> "key" :> CaptureKey :> GetAPI '[]
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v3" :> "key" :> CaptureKey
 | 
				
			||||||
 | 
							:> GetAPI '[DataLengthHeader]
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v2" :> "key" :> CaptureKey
 | 
				
			||||||
 | 
							:> GetAPI '[DataLengthHeader]
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v1" :> "key" :> CaptureKey
 | 
				
			||||||
 | 
							:> GetAPI '[DataLengthHeader]
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v0" :> "key" :> CaptureKey
 | 
				
			||||||
 | 
							:> GetAPI '[]
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v3" :> "checkpresent" :> CheckPresentAPI
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v2" :> "checkpresent" :> CheckPresentAPI
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v1" :> "checkpresent" :> CheckPresentAPI
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v0" :> "checkpresent" :> CheckPresentAPI
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v3" :> "lockcontent" :> LockContentAPI
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v2" :> "lockcontent" :> LockContentAPI
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v1" :> "lockcontent" :> LockContentAPI
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v0" :> "lockcontent" :> LockContentAPI
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v3" :> "remove" :> RemoveAPI RemoveResultPlus
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v2" :> "remove" :> RemoveAPI RemoveResultPlus
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v1" :> "remove" :> RemoveAPI RemoveResult
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v0" :> "remove" :> RemoveAPI RemoveResult
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v3" :> "remove-before" :> RemoveBeforeAPI
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v3" :> "gettimestamp" :> GetTimestampAPI
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v3" :> "put" :> DataLengthHeader
 | 
				
			||||||
 | 
							:> PutAPI PutResultPlus
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v2" :> "put" :> DataLengthHeader
 | 
				
			||||||
 | 
							:> PutAPI PutResultPlus
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v1" :> "put" :> DataLengthHeader
 | 
				
			||||||
 | 
							:> PutAPI PutResult
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v0" :> "put"
 | 
				
			||||||
 | 
							:> PutAPI PutResult
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v3" :> "putoffset"
 | 
				
			||||||
 | 
							:> PutOffsetAPI PutOffsetResultPlus
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v2" :> "putoffset"
 | 
				
			||||||
 | 
							:> PutOffsetAPI PutOffsetResultPlus
 | 
				
			||||||
 | 
						:<|> "git-annex" :> "v1" :> "putoffset"
 | 
				
			||||||
 | 
							:> PutOffsetAPI PutOffsetResult
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GetAPI headers
 | 
				
			||||||
 | 
						= CommonParams Optional
 | 
				
			||||||
 | 
						:> AssociatedFileParam
 | 
				
			||||||
 | 
						:> OffsetParam
 | 
				
			||||||
 | 
						:> StreamGet NoFraming OctetStream
 | 
				
			||||||
 | 
							(Headers headers (SourceIO B.ByteString))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CheckPresentAPI
 | 
				
			||||||
 | 
						= KeyParam
 | 
				
			||||||
 | 
						:> CommonParams Required
 | 
				
			||||||
 | 
						:> Post '[JSON] CheckPresentResult
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
					type LockContentAPI
 | 
				
			||||||
 | 
						= KeyParam
 | 
				
			||||||
 | 
						:> CommonParams Required
 | 
				
			||||||
 | 
						:> WebSocket
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type RemoveAPI result
 | 
				
			||||||
 | 
						= KeyParam
 | 
				
			||||||
 | 
						:> CommonParams Required
 | 
				
			||||||
 | 
						:> Post '[JSON] result
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
					type RemoveBeforeAPI
 | 
				
			||||||
 | 
						= KeyParam
 | 
				
			||||||
 | 
						:> CommonParams Required
 | 
				
			||||||
 | 
						:> QueryParam' '[Required] "timestamp" MonotonicTimestamp
 | 
				
			||||||
 | 
						:> Post '[JSON] RemoveResult
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
					type GetTimestampAPI
 | 
				
			||||||
 | 
						= CommonParams Required
 | 
				
			||||||
 | 
						:> Post '[JSON] GetTimestampResult
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
					type PutAPI result
 | 
				
			||||||
 | 
						= KeyParam
 | 
				
			||||||
 | 
						:> CommonParams Required
 | 
				
			||||||
 | 
						:> AssociatedFileParam
 | 
				
			||||||
 | 
						:> OffsetParam
 | 
				
			||||||
 | 
						:> Header' '[Required] "X-git-annex-data-length" DataLength
 | 
				
			||||||
 | 
						:> CommonParams Required
 | 
				
			||||||
 | 
						:> StreamBody NoFraming OctetStream (SourceIO B.ByteString)
 | 
				
			||||||
 | 
						:> Post '[JSON] result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type PutOffsetAPI result
 | 
				
			||||||
 | 
						= KeyParam
 | 
				
			||||||
 | 
						:> CommonParams Required
 | 
				
			||||||
 | 
						:> Post '[JSON] result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CommonParams req
 | 
				
			||||||
 | 
						= QueryParam' '[req] "clientuuid" B64UUID 
 | 
				
			||||||
 | 
						:> QueryParam' '[req] "serveruuid" B64UUID
 | 
				
			||||||
 | 
						:> QueryParams "bypass" B64UUID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CaptureKey = Capture "key" B64Key
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type KeyParam = QueryParam' '[Required] "key"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AssociatedFileParam = QueryParam "associatedfile" B64FilePath
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					type OffsetParam = QueryParam "offset" P2P.Offset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DataLengthHeader = Header "X-git-annex-data-length" Integer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					newtype DataLength = DataLength Integer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					newtype CheckPresentResult = CheckPresentResult Bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					newtype RemoveResult = RemoveResult Bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					data RemoveResultPlus = RemoveResultPlus Bool [UUID]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					newtype GetTimestampResult = GetTimestmapResult MonotonicTimestamp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					newtype PutResult = PutResult Bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					data PutResultPlus = PutResultPlus Bool [UUID]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					newtype PutOffsetResult = PutOffsetResult P2P.Offset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					data PutOffsetResultPlus = PutOffsetResultPlus P2P.Offset [UUID]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- Keys, UUIDs, and filenames are base64 encoded since Servant uses 
 | 
				
			||||||
 | 
					-- Text and so needs UTF-8.
 | 
				
			||||||
 | 
					newtype B64Key = B64Key Key
 | 
				
			||||||
 | 
					newtype B64UUID = B64UUID UUID
 | 
				
			||||||
 | 
					newtype B64FilePath = B64FilePath RawFilePath
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					instance FromHttpApiData B64Key where
 | 
				
			||||||
 | 
						parseUrlPiece t = case fromB64Maybe (TE.encodeUtf8 t) of
 | 
				
			||||||
 | 
							Nothing -> Left "unable to base64 decode key"
 | 
				
			||||||
 | 
							Just b -> maybe (Left "key parse error") (Right . B64Key)
 | 
				
			||||||
 | 
								(deserializeKey' b)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					instance FromHttpApiData B64UUID where
 | 
				
			||||||
 | 
						parseUrlPiece t = case fromB64Maybe (TE.encodeUtf8 t) of
 | 
				
			||||||
 | 
							Nothing -> Left "unable to base64 decode UUID"
 | 
				
			||||||
 | 
							Just b -> case toUUID b of
 | 
				
			||||||
 | 
								u@(UUID _) -> Right (B64UUID u)
 | 
				
			||||||
 | 
								NoUUID -> Left "empty UUID"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					instance FromHttpApiData B64FilePath where
 | 
				
			||||||
 | 
						parseUrlPiece t = case fromB64Maybe (TE.encodeUtf8 t) of
 | 
				
			||||||
 | 
							Nothing -> Left "unable to base64 decode filename"
 | 
				
			||||||
 | 
							Just b -> Right (B64FilePath b)
 | 
				
			||||||
| 
						 | 
					@ -63,7 +63,7 @@ Any request may also optionally include these parameters:
 | 
				
			||||||
  This parameter can be given multiple times to list several cluster
 | 
					  This parameter can be given multiple times to list several cluster
 | 
				
			||||||
  gateway UUIDs.
 | 
					  gateway UUIDs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  This parameter is only available for v3 and above.
 | 
					  This parameter is only available for v2 and above.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Internally, git-annex can use these common parameters, plus the protocol
 | 
					[Internally, git-annex can use these common parameters, plus the protocol
 | 
				
			||||||
version, to create a P2P session. The P2P session is driven through
 | 
					version, to create a P2P session. The P2P session is driven through
 | 
				
			||||||
| 
						 | 
					@ -79,7 +79,7 @@ It is not part of the P2P protocol per se, but is provided to let
 | 
				
			||||||
other clients than git-annex easily download the content of keys from the
 | 
					other clients than git-annex easily download the content of keys from the
 | 
				
			||||||
http server.
 | 
					http server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This behaves almost the same as `GET /git-annex/v3/key/$key`, although its
 | 
					This behaves almost the same as `GET /git-annex/v0/key/$key`, although its
 | 
				
			||||||
behavior may change in later versions.
 | 
					behavior may change in later versions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
When the key is not present on the server, this returns a 404 Not Found.
 | 
					When the key is not present on the server, this returns a 404 Not Found.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -324,6 +324,7 @@ Executable git-annex
 | 
				
			||||||
    CPP-Options: -DWITH_SERVANT
 | 
					    CPP-Options: -DWITH_SERVANT
 | 
				
			||||||
    Other-Modules:
 | 
					    Other-Modules:
 | 
				
			||||||
      Command.P2PHttp
 | 
					      Command.P2PHttp
 | 
				
			||||||
 | 
					      P2P.Http
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (os(windows))
 | 
					  if (os(windows))
 | 
				
			||||||
    Build-Depends:
 | 
					    Build-Depends:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue