By | fr33domlover |
At | 2016-04-24 |
Title | Copy UploadHaves to HTTP for later use |
Description |
Add file src/Network/Git/Transport/HTTP/Fetch/UploadHaves.hs 0
Edit file src/Network/Git/Transport/HTTP/Fetch/UploadHaves.hs 0 → 0
+ 1 {- This file is part of hit-network.
+ 2 -
+ 3 - Written in 2016 by fr33domlover <fr33domlover@riseup.net>.
+ 4 -
+ 5 - ♡ Copying is an act of love. Please copy, reuse and share.
+ 6 -
+ 7 - The author(s) have dedicated all copyright and related and neighboring
+ 8 - rights to this software to the public domain worldwide. This software is
+ 9 - distributed without any warranty.
+ 10 -
+ 11 - You should have received a copy of the CC0 Public Domain Dedication along
+ 12 - with this software. If not, see
+ 13 - <http://creativecommons.org/publicdomain/zero/1.0/>.
+ 14 -}
+ 15 + 16 module Network.Git.Fetch.UploadHaves
+ 17 ( -- * Types
+ 18 Line (..)
+ 19 -- * Get
+ 20 , getHave
+ 21 , requireDone
+ 22 -- * Receive
+ 23 , recvLine
+ 24 , recvUploadHaves
+ 25 )
+ 26 where
+ 27 + 28 -------------------------------------------------------------------------------
+ 29 -- Types
+ 30 -------------------------------------------------------------------------------
+ 31 + 32 -- | A line received during the UploadHaves step. This step involves receiving
+ 33 -- several chunks of /have/ lines separated by /flush-pkt/ lines, and finally a
+ 34 -- /done/ line marks the end of the step.
+ 35 data Line = Have ObjId | Flush | Done
+ 36 + 37 -------------------------------------------------------------------------------
+ 38 -- Get
+ 39 -------------------------------------------------------------------------------
+ 40 + 41 getHave :: Get ObjId
+ 42 getHave = getTaggedObjId "have"
+ 43 + 44 requireDone :: Get ()
+ 45 requireDone = getDataPkt $ \ len ->
+ 46 if len < 4 || len > 5
+ 47 then fail "invalid pkt-len for a \"done\" line"
+ 48 else do
+ 49 requireByteString "done"
+ 50 when (len == 5) requireNewline
+ 51 + 52 -------------------------------------------------------------------------------
+ 53 -- Receive
+ 54 -------------------------------------------------------------------------------
+ 55 + 56 -- Now we should start getting /have/ lines. There will be
+ 57 -- FlushPkts in the middle, but what signals end-of-message is the
+ 58 -- "done" packet. Since we don't support any capabilities yet, we
+ 59 -- ack according to the default ack mode:
+ 60 --
+ 61 -- The first time we get a "have" of an ObjId we too have in the
+ 62 -- repo (better prepare a 'HashSet Ref' or similar for fast
+ 63 -- checking), we send an ACK. After that first time, we remain
+ 64 -- quiet until we get the "done" from the client.
+ 65 --
+ 66 -- Every time we get a flush-pkt and we didn't get a common commit
+ 67 -- yet (i.e. didn't send ACK yet), we send a NAK.
+ 68 --
+ 69 -- After we get the "done", we send a NAK (in all cases).
+ 70 + 71 -- | Read an UploadHaves line from the client.
+ 72 recvLine :: FetchT m Line
+ 73 recvLine = receive "UploadHaves Line" $
+ 74 Have <$> getHave
+ 75 <|> Flush <$ requireFlushPkt
+ 76 <|> Done <$ requireDone
+ 77 + 78 -- | Run the first part of the /have/ line receiver loop. In this part we
+ 79 -- receive commit IDs until we get one which we have on the server side too.
+ 80 -- Once we get it, we return it. Until then, we ignore the unknown commit IDs
+ 81 -- we get, and we send a NAK for each flush-pkt we get. If we get the /done/
+ 82 -- line before we see any commit ID we have too, we send a NAK and return
+ 83 -- 'Nothing'.
+ 84 waitCommon :: HashMap ObjId a -> FetchT m (Maybe ObjId)
+ 85 waitCommon objmap = go
+ 86 where
+ 87 go = do
+ 88 line <- recv
+ 89 case line of
+ 90 Have oid ->
+ 91 if oid `M.member` objmap
+ 92 then send (putAck oid) >> return (Just oid)
+ 93 else go
+ 94 Flush -> send putNak >> go
+ 95 Done -> send putNak >> return Nothing
+ 96 + 97 -- | Run the second part of the /have/ line receiver loop. This is needed only
+ 98 -- if we get a common commit ID in the first part. In the second part, we
+ 99 -- simply remain silent until the "done", and then we send a NAK. But we do
+ 100 -- collect the IDs and return a list of them.
+ 101 --
+ 102 -- The list is in reverse order. If needed, the implementation can be modified
+ 103 -- to use a DList instead, so that the list is constructed by appending.
+ 104 collectHaves :: FetchT m [ObjId]
+ 105 collectHaves = go []
+ 106 where
+ 107 go l = do
+ 108 line <- recv
+ 109 case line of
+ 110 Have oid -> go $ oid : l
+ 111 Flush -> go l
+ 112 Done -> send putNak >> return l
+ 113 + 114 recvUploadHaves :: HashMap ObjId a -> FetchT m [ObjId]
+ 115 recvUploadHaves objmap = do
+ 116 moid <- waitCommon objmap
+ 117 case moid of
+ 118 Nothing -> return []
+ 119 Just oid -> do
+ 120 oids <- collectHaves
+ 121 return $ oid : oids
… … … …