1{-# LANGUAGE OverloadedStrings #-}
25import Prelude hiding (zip)
26import qualified Codec.Archive.Zip as Zip
27import Control.Exception (SomeException, handle)
28import qualified Data.Binary as Binary
29import qualified Data.Binary.Get as Binary
30import qualified Data.ByteString.Builder as B
31import qualified Data.ByteString.Char8 as BS
32import qualified Data.Digest.Pure.SHA as SHA
33import qualified Data.String as String
34import Network.HTTP (urlEncodeVars)
35import Network.HTTP.Client
36import Network.HTTP.Client.TLS (tlsManagerSettings)
37import Network.HTTP.Types.Header (Header, hAccept, hAcceptEncoding, hUserAgent)
38import Network.HTTP.Types.Method (Method, methodGet, methodPost)
39import qualified Network.HTTP.Client as Multi (RequestBody(RequestBodyLBS))
40import qualified Network.HTTP.Client.MultipartFormData as Multi
42import qualified Json.Encode as Encode
43import qualified Elm.Version as V
50getManager :: IO Manager
52 newManager tlsManagerSettings
59toUrl :: String -> [(String,String)] -> String
63 _:_ -> url ++ "?" ++ urlEncodeVars params
70get :: Manager -> String -> [Header] -> (Error -> e) -> (BS.ByteString -> IO (Either e a)) -> IO (Either e a)
75post :: Manager -> String -> [Header] -> (Error -> e) -> (BS.ByteString -> IO (Either e a)) -> IO (Either e a)
80fetch :: Method -> Manager -> String -> [Header] -> (Error -> e) -> (BS.ByteString -> IO (Either e a)) -> IO (Either e a)
81fetch methodVerb manager url headers onError onSuccess =
82 handle (handleSomeException url onError) $
83 handle (handleHttpException url onError) $
84 do req0 <- parseUrlThrow url
88 , requestHeaders = addDefaultHeaders headers
90 withResponse req1 manager $ \response ->
91 do chunks <- brConsume (responseBody response)
92 onSuccess (BS.concat chunks)
95addDefaultHeaders :: [Header] -> [Header]
96addDefaultHeaders headers =
97 (hUserAgent, userAgent) : (hAcceptEncoding, "gzip") : headers
100{-# NOINLINE userAgent #-}
101userAgent :: BS.ByteString
103 BS.pack ("elm/" ++ V.toChars V.compiler)
106accept :: BS.ByteString -> Header
116 = BadUrl String String
117 | BadHttp String HttpExceptionContent
118 | BadMystery String SomeException
121handleHttpException :: String -> (Error -> e) -> HttpException -> IO (Either e a)
122handleHttpException url onError httpException =
123 case httpException of
124 InvalidUrlException _ reason ->
125 return (Left (onError (BadUrl url reason)))
127 HttpExceptionRequest _ content ->
128 return (Left (onError (BadHttp url content)))
131handleSomeException :: String -> (Error -> e) -> SomeException -> IO (Either e a)
132handleSomeException url onError exception =
133 return (Left (onError (BadMystery url exception)))
140type Sha = SHA.Digest SHA.SHA1State
143shaToChars :: Sha -> String
157 -> ((Sha, Zip.Archive) -> IO (Either e a))
159getArchive manager url onError err onSuccess =
160 handle (handleSomeException url onError) $
161 handle (handleHttpException url onError) $
162 do req0 <- parseUrlThrow url
166 , requestHeaders = addDefaultHeaders []
168 withResponse req1 manager $ \response ->
169 do result <- readArchive (responseBody response)
171 Nothing -> return (Left err)
172 Just shaAndArchive -> onSuccess shaAndArchive
175readArchive :: BodyReader -> IO (Maybe (Sha, Zip.Archive))
177 readArchiveHelp body $
178 AS 0 SHA.sha1Incremental (Binary.runGetIncremental Binary.get)
184 , _sha :: !(Binary.Decoder SHA.SHA1State)
185 , _zip :: !(Binary.Decoder Zip.Archive)
189readArchiveHelp :: BodyReader -> ArchiveState -> IO (Maybe (Sha, Zip.Archive))
190readArchiveHelp body (AS len sha zip) =
196 do chunk <- brRead body
197 readArchiveHelp body $
199 { _len = len + BS.length chunk
200 , _sha = Binary.pushChunk sha chunk
201 , _zip = k (if BS.null chunk then Nothing else Just chunk)
204 Binary.Done _ _ archive ->
205 return $ Just ( SHA.completeSha1Incremental sha len, archive )
212upload :: Manager -> String -> [Multi.Part] -> IO (Either Error ())
213upload manager url parts =
214 handle (handleSomeException url id) $
215 handle (handleHttpException url id) $
216 do req0 <- parseUrlThrow url
218 Multi.formDataBody parts $
220 { method = methodPost
221 , requestHeaders = addDefaultHeaders []
222 , responseTimeout = responseTimeoutNone
224 withResponse req1 manager $ \_ ->
228filePart :: String -> FilePath -> Multi.Part
229filePart name filePath =
230 Multi.partFileSource (String.fromString name) filePath
233jsonPart :: String -> FilePath -> Encode.Value -> Multi.Part
234jsonPart name filePath value =
237 Multi.RequestBodyLBS $ B.toLazyByteString $ Encode.encodeUgly value
239 Multi.partFileRequestBody (String.fromString name) filePath body
242stringPart :: String -> String -> Multi.Part
243stringPart name string =
244 Multi.partBS (String.fromString name) (BS.pack string)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list