2024-07-07 16:59:12 +00:00
|
|
|
{- 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 #-}
|
2024-07-08 01:20:50 +00:00
|
|
|
{-# LANGUAGE TypeFamilies #-}
|
2024-07-10 20:06:39 +00:00
|
|
|
{-# LANGUAGE TypeApplications #-}
|
2024-07-07 16:59:12 +00:00
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
2024-07-11 15:42:32 +00:00
|
|
|
{-# LANGUAGE BangPatterns #-}
|
2024-07-07 16:59:12 +00:00
|
|
|
|
2024-07-09 01:11:01 +00:00
|
|
|
module P2P.Http (
|
|
|
|
module P2P.Http,
|
|
|
|
module P2P.Http.Types,
|
|
|
|
) where
|
2024-07-07 16:59:12 +00:00
|
|
|
|
2024-07-09 01:11:01 +00:00
|
|
|
import P2P.Http.Types
|
2024-07-07 16:59:12 +00:00
|
|
|
|
|
|
|
import Servant
|
|
|
|
import qualified Data.ByteString as B
|
|
|
|
|
2024-07-07 18:48:20 +00:00
|
|
|
type P2PHttpAPI
|
2024-07-11 15:19:20 +00:00
|
|
|
= "git-annex" :> SU :> PV3 :> "key" :> GetAPI
|
|
|
|
:<|> "git-annex" :> SU :> PV2 :> "key" :> GetAPI
|
|
|
|
:<|> "git-annex" :> SU :> PV1 :> "key" :> GetAPI
|
|
|
|
:<|> "git-annex" :> SU :> PV0 :> "key" :> GetAPI
|
|
|
|
:<|> "git-annex" :> SU :> PV3 :> "checkpresent" :> CheckPresentAPI
|
|
|
|
:<|> "git-annex" :> SU :> PV2 :> "checkpresent" :> CheckPresentAPI
|
|
|
|
:<|> "git-annex" :> SU :> PV1 :> "checkpresent" :> CheckPresentAPI
|
|
|
|
:<|> "git-annex" :> SU :> PV0 :> "checkpresent" :> CheckPresentAPI
|
|
|
|
:<|> "git-annex" :> SU :> PV3 :> "remove" :> RemoveAPI RemoveResultPlus
|
|
|
|
:<|> "git-annex" :> SU :> PV2 :> "remove" :> RemoveAPI RemoveResultPlus
|
|
|
|
:<|> "git-annex" :> SU :> PV1 :> "remove" :> RemoveAPI RemoveResult
|
|
|
|
:<|> "git-annex" :> SU :> PV0 :> "remove" :> RemoveAPI RemoveResult
|
|
|
|
:<|> "git-annex" :> SU :> PV3 :> "remove-before" :> RemoveBeforeAPI
|
|
|
|
:<|> "git-annex" :> SU :> PV3 :> "gettimestamp" :> GetTimestampAPI
|
2024-07-22 14:20:18 +00:00
|
|
|
:<|> "git-annex" :> SU :> PV3 :> "put" :> PutAPI PutResultPlus
|
|
|
|
:<|> "git-annex" :> SU :> PV2 :> "put" :> PutAPI PutResultPlus
|
|
|
|
:<|> "git-annex" :> SU :> PV1 :> "put" :> PutAPI PutResult
|
|
|
|
:<|> "git-annex" :> SU :> PV0 :> "put" :> PutAPI PutResult
|
2024-07-11 15:19:20 +00:00
|
|
|
:<|> "git-annex" :> SU :> PV3 :> "putoffset"
|
2024-07-07 16:59:12 +00:00
|
|
|
:> PutOffsetAPI PutOffsetResultPlus
|
2024-07-11 15:19:20 +00:00
|
|
|
:<|> "git-annex" :> SU :> PV2 :> "putoffset"
|
2024-07-07 16:59:12 +00:00
|
|
|
:> PutOffsetAPI PutOffsetResultPlus
|
2024-07-11 15:19:20 +00:00
|
|
|
:<|> "git-annex" :> SU :> PV1 :> "putoffset"
|
2024-07-07 16:59:12 +00:00
|
|
|
:> PutOffsetAPI PutOffsetResult
|
2024-07-11 15:19:20 +00:00
|
|
|
:<|> "git-annex" :> SU :> PV3 :> "lockcontent" :> LockContentAPI
|
|
|
|
:<|> "git-annex" :> SU :> PV2 :> "lockcontent" :> LockContentAPI
|
|
|
|
:<|> "git-annex" :> SU :> PV1 :> "lockcontent" :> LockContentAPI
|
|
|
|
:<|> "git-annex" :> SU :> PV0 :> "lockcontent" :> LockContentAPI
|
|
|
|
:<|> "git-annex" :> SU :> PV3 :> "keeplocked" :> KeepLockedAPI
|
|
|
|
:<|> "git-annex" :> SU :> PV2 :> "keeplocked" :> KeepLockedAPI
|
|
|
|
:<|> "git-annex" :> SU :> PV1 :> "keeplocked" :> KeepLockedAPI
|
|
|
|
:<|> "git-annex" :> SU :> PV0 :> "keeplocked" :> KeepLockedAPI
|
|
|
|
:<|> "git-annex" :> SU :> "key" :> GetGenericAPI
|
2024-07-07 16:59:12 +00:00
|
|
|
|
2024-07-07 18:48:20 +00:00
|
|
|
p2pHttpAPI :: Proxy P2PHttpAPI
|
|
|
|
p2pHttpAPI = Proxy
|
|
|
|
|
2024-07-11 15:19:20 +00:00
|
|
|
type GetGenericAPI
|
|
|
|
= CaptureKey
|
2024-07-11 15:26:03 +00:00
|
|
|
:> IsSecure
|
|
|
|
:> AuthHeader
|
|
|
|
:> StreamGet NoFraming OctetStream
|
|
|
|
(Headers '[DataLengthHeader] (SourceIO B.ByteString))
|
2024-07-07 18:48:20 +00:00
|
|
|
|
2024-07-08 17:26:02 +00:00
|
|
|
type GetAPI
|
2024-07-11 15:19:20 +00:00
|
|
|
= CaptureKey
|
|
|
|
:> CU Required
|
2024-07-07 18:48:20 +00:00
|
|
|
:> BypassUUIDs
|
2024-07-07 16:59:12 +00:00
|
|
|
:> AssociatedFileParam
|
|
|
|
:> OffsetParam
|
2024-07-10 16:19:47 +00:00
|
|
|
:> IsSecure
|
2024-07-09 21:30:55 +00:00
|
|
|
:> AuthHeader
|
2024-07-07 16:59:12 +00:00
|
|
|
:> StreamGet NoFraming OctetStream
|
2024-07-08 17:26:02 +00:00
|
|
|
(Headers '[DataLengthHeader] (SourceIO B.ByteString))
|
2024-07-07 16:59:12 +00:00
|
|
|
|
|
|
|
type CheckPresentAPI
|
|
|
|
= KeyParam
|
2024-07-11 15:19:20 +00:00
|
|
|
:> CU Required
|
2024-07-07 18:48:20 +00:00
|
|
|
:> BypassUUIDs
|
2024-07-10 03:44:40 +00:00
|
|
|
:> IsSecure
|
2024-07-10 00:52:56 +00:00
|
|
|
:> AuthHeader
|
2024-07-07 16:59:12 +00:00
|
|
|
:> Post '[JSON] CheckPresentResult
|
2024-07-07 18:48:20 +00:00
|
|
|
|
2024-07-07 16:59:12 +00:00
|
|
|
type RemoveAPI result
|
|
|
|
= KeyParam
|
2024-07-11 15:19:20 +00:00
|
|
|
:> CU Required
|
2024-07-07 18:48:20 +00:00
|
|
|
:> BypassUUIDs
|
2024-07-10 13:13:01 +00:00
|
|
|
:> IsSecure
|
2024-07-10 00:52:56 +00:00
|
|
|
:> AuthHeader
|
2024-07-07 16:59:12 +00:00
|
|
|
:> Post '[JSON] result
|
2024-07-07 18:48:20 +00:00
|
|
|
|
2024-07-07 16:59:12 +00:00
|
|
|
type RemoveBeforeAPI
|
|
|
|
= KeyParam
|
2024-07-11 15:19:20 +00:00
|
|
|
:> CU Required
|
2024-07-07 18:48:20 +00:00
|
|
|
:> BypassUUIDs
|
|
|
|
:> QueryParam' '[Required] "timestamp" Timestamp
|
2024-07-10 14:03:26 +00:00
|
|
|
:> IsSecure
|
|
|
|
:> AuthHeader
|
|
|
|
:> Post '[JSON] RemoveResultPlus
|
2024-07-07 18:48:20 +00:00
|
|
|
|
2024-07-07 16:59:12 +00:00
|
|
|
type GetTimestampAPI
|
2024-07-11 15:19:20 +00:00
|
|
|
= CU Required
|
2024-07-07 18:48:20 +00:00
|
|
|
:> BypassUUIDs
|
2024-07-10 14:23:10 +00:00
|
|
|
:> IsSecure
|
|
|
|
:> AuthHeader
|
2024-07-07 16:59:12 +00:00
|
|
|
:> Post '[JSON] GetTimestampResult
|
2024-07-07 18:48:20 +00:00
|
|
|
|
2024-07-07 16:59:12 +00:00
|
|
|
type PutAPI result
|
2024-07-22 14:20:18 +00:00
|
|
|
= DataLengthHeaderRequired
|
|
|
|
:> KeyParam
|
2024-07-11 15:19:20 +00:00
|
|
|
:> CU Required
|
2024-07-07 18:48:20 +00:00
|
|
|
:> BypassUUIDs
|
2024-07-07 16:59:12 +00:00
|
|
|
:> AssociatedFileParam
|
|
|
|
:> OffsetParam
|
|
|
|
:> StreamBody NoFraming OctetStream (SourceIO B.ByteString)
|
2024-07-11 16:20:07 +00:00
|
|
|
:> IsSecure
|
|
|
|
:> AuthHeader
|
2024-07-07 16:59:12 +00:00
|
|
|
:> Post '[JSON] result
|
|
|
|
|
|
|
|
type PutOffsetAPI result
|
|
|
|
= KeyParam
|
2024-07-11 15:19:20 +00:00
|
|
|
:> CU Required
|
2024-07-07 18:48:20 +00:00
|
|
|
:> BypassUUIDs
|
2024-07-22 19:02:08 +00:00
|
|
|
:> IsSecure
|
|
|
|
:> AuthHeader
|
2024-07-07 16:59:12 +00:00
|
|
|
:> Post '[JSON] result
|
|
|
|
|
2024-07-08 01:20:50 +00:00
|
|
|
type LockContentAPI
|
|
|
|
= KeyParam
|
2024-07-11 15:19:20 +00:00
|
|
|
:> CU Required
|
2024-07-08 01:20:50 +00:00
|
|
|
:> BypassUUIDs
|
2024-07-22 21:36:56 +00:00
|
|
|
:> IsSecure
|
|
|
|
:> AuthHeader
|
convert lockcontent api to http long polling
Websockets would work, but the problem with using them for this is that
each lockcontent call is a separate websocket connection. And that's an
actual TCP connection. One TCP connection per file dropped would be too
expensive. With http long polling, regular http pipelining can be used,
so it will reuse a TCP connection.
Unfortunately, at least with servant, bi-directional streams with long
polling don't result in true bidirectional full duplex communication.
Servant processes the whole client body stream before generating the server
body stream. I think it's entirely possible to do full bi-directional
communication over http, but it would need changes to servant.
And, there's no way for the client to tell if the server successfully
locked the content, since the server will keep processing the client
stream no matter what.:
So, added a new api endpoint, keeplocked. lockcontent will lock the key
for 10 minutes with retention lock, and then a call to keeplocked will
keep it locked for as long as needed. This does mean that there will
need to be a Map of locks by key, and I will probably want to add
some kind of lock identifier that lockcontent returns.
2024-07-08 14:40:38 +00:00
|
|
|
:> Post '[JSON] LockResult
|
2024-07-07 20:08:05 +00:00
|
|
|
|
convert lockcontent api to http long polling
Websockets would work, but the problem with using them for this is that
each lockcontent call is a separate websocket connection. And that's an
actual TCP connection. One TCP connection per file dropped would be too
expensive. With http long polling, regular http pipelining can be used,
so it will reuse a TCP connection.
Unfortunately, at least with servant, bi-directional streams with long
polling don't result in true bidirectional full duplex communication.
Servant processes the whole client body stream before generating the server
body stream. I think it's entirely possible to do full bi-directional
communication over http, but it would need changes to servant.
And, there's no way for the client to tell if the server successfully
locked the content, since the server will keep processing the client
stream no matter what.:
So, added a new api endpoint, keeplocked. lockcontent will lock the key
for 10 minutes with retention lock, and then a call to keeplocked will
keep it locked for as long as needed. This does mean that there will
need to be a Map of locks by key, and I will probably want to add
some kind of lock identifier that lockcontent returns.
2024-07-08 14:40:38 +00:00
|
|
|
type KeepLockedAPI
|
2024-07-09 00:18:55 +00:00
|
|
|
= LockIDParam
|
2024-07-22 23:48:54 +00:00
|
|
|
:> CU Optional
|
convert lockcontent api to http long polling
Websockets would work, but the problem with using them for this is that
each lockcontent call is a separate websocket connection. And that's an
actual TCP connection. One TCP connection per file dropped would be too
expensive. With http long polling, regular http pipelining can be used,
so it will reuse a TCP connection.
Unfortunately, at least with servant, bi-directional streams with long
polling don't result in true bidirectional full duplex communication.
Servant processes the whole client body stream before generating the server
body stream. I think it's entirely possible to do full bi-directional
communication over http, but it would need changes to servant.
And, there's no way for the client to tell if the server successfully
locked the content, since the server will keep processing the client
stream no matter what.:
So, added a new api endpoint, keeplocked. lockcontent will lock the key
for 10 minutes with retention lock, and then a call to keeplocked will
keep it locked for as long as needed. This does mean that there will
need to be a Map of locks by key, and I will probably want to add
some kind of lock identifier that lockcontent returns.
2024-07-08 14:40:38 +00:00
|
|
|
:> BypassUUIDs
|
2024-07-22 23:15:52 +00:00
|
|
|
:> IsSecure
|
|
|
|
:> AuthHeader
|
convert lockcontent api to http long polling
Websockets would work, but the problem with using them for this is that
each lockcontent call is a separate websocket connection. And that's an
actual TCP connection. One TCP connection per file dropped would be too
expensive. With http long polling, regular http pipelining can be used,
so it will reuse a TCP connection.
Unfortunately, at least with servant, bi-directional streams with long
polling don't result in true bidirectional full duplex communication.
Servant processes the whole client body stream before generating the server
body stream. I think it's entirely possible to do full bi-directional
communication over http, but it would need changes to servant.
And, there's no way for the client to tell if the server successfully
locked the content, since the server will keep processing the client
stream no matter what.:
So, added a new api endpoint, keeplocked. lockcontent will lock the key
for 10 minutes with retention lock, and then a call to keeplocked will
keep it locked for as long as needed. This does mean that there will
need to be a Map of locks by key, and I will probably want to add
some kind of lock identifier that lockcontent returns.
2024-07-08 14:40:38 +00:00
|
|
|
:> Header "Connection" ConnectionKeepAlive
|
|
|
|
:> Header "Keep-Alive" KeepAlive
|
|
|
|
:> StreamBody NewlineFraming JSON (SourceIO UnlockRequest)
|
|
|
|
:> Post '[JSON] LockResult
|
2024-07-07 18:48:20 +00:00
|
|
|
|
2024-07-23 18:12:03 +00:00
|
|
|
type SU = Capture "serveruuid" (B64UUID ServerSide)
|
|
|
|
|
|
|
|
type CU req = QueryParam' '[req] "clientuuid" (B64UUID ClientSide)
|
|
|
|
|
|
|
|
type BypassUUIDs = QueryParams "bypass" (B64UUID Bypass)
|
|
|
|
|
|
|
|
type CaptureKey = Capture "key" B64Key
|
|
|
|
|
|
|
|
type KeyParam = QueryParam' '[Required] "key" B64Key
|
|
|
|
|
|
|
|
type AssociatedFileParam = QueryParam "associatedfile" B64FilePath
|
|
|
|
|
|
|
|
type OffsetParam = QueryParam "offset" Offset
|
|
|
|
|
|
|
|
type DataLengthHeader = Header DataLengthHeader' DataLength
|
|
|
|
|
|
|
|
type DataLengthHeaderRequired = Header' '[Required] DataLengthHeader' DataLength
|
|
|
|
|
|
|
|
type DataLengthHeader' = "X-git-annex-data-length"
|
|
|
|
|
|
|
|
type LockIDParam = QueryParam' '[Required] "lockid" LockID
|
|
|
|
|
|
|
|
type AuthHeader = Header "Authorization" Auth
|
|
|
|
|
|
|
|
type PV3 = Capture "v3" V3
|
|
|
|
type PV2 = Capture "v2" V2
|
|
|
|
type PV1 = Capture "v1" V1
|
|
|
|
type PV0 = Capture "v0" V0
|
2024-07-08 18:20:30 +00:00
|
|
|
|