request (empty) → 0.1.0.0
raw patch · 5 files changed
+254/−0 lines, 5 filesdep +basedep +bytestringdep +case-insensitivesetup-changed
Dependencies added: base, bytestring, case-insensitive, http-client, http-client-tls, http-types
Files
- LICENSE +30/−0
- README.md +100/−0
- Setup.hs +2/−0
- request.cabal +29/−0
- src/Network/HTTP/Request.hs +93/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright An Long (c) 2021++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Author name here nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,100 @@+# request++++HTTP client for haskell, inpired by [requests](https://requests.readthedocs.io/) and [http-dispatch](https://github.com/owainlewis/http-dispatch).++## Usage++You can try this in haskell REPL once you have `request` installed:++```haskell+import Network.HTTP.Request++resp <- get "https://api.leancloud.cn/1.1/date"+print $ requestStatus resp+```++## Core API++Request's API has three core concepts: `Request` record type, `Response` record type, `send` function.++### Request++`Request` is all about the information you will send to the target URL.++```haskell+data Request = Request+ { requestMethod :: Method+ , requestUrl :: String+ , requestHeaders :: Headers+ , requestBody :: Maybe Data.ByteString.ByteString+ } deriving (Show)+```++### send++Once you have constructed your own `Request` record, you can call the `send` function to send it to the server. The `send` function's type is:++```haskell+send :: Request -> IO Response+```++### Response++`Response` is what you got from the server URL.++```haskell+data Response = Response+ { responseStatus :: Int+ , responseHeaders :: Headers+ , responseBody :: Data.ByteString.ByteString+ } deriving (Show)+```++### Example++```haskell+:set -XOverloadedStrings++import Network.HTTP.Request++-- Construct a Request record.+let req = Request GET "https://api.leancloud.cn/1.1/date" [] Nothing+-- Send it.+res <- send req+-- access the fields on Response.+print $ requestStatus resp+```++## Shortcuts++As you expected, there are some shortcuts for the most used scenarios.++```haskell+get :: String -> IO Response+get url =+ send $ Request GET url [] Nothing++delete :: String -> IO Response+delete url =+ send $ Request DELETE url [] Nothing++post :: (String, Maybe Data.ByteString.ByteString) -> IO Response+post (url, body) =+ send $ Request POST url [] body++put :: (String, Maybe Data.ByteString.ByteString) -> IO Response+put (url, body) =+ send $ Request PUT url [] body+```++These shortcuts' definitions are simple and direct. You are encouraged to add your own if the built-in does not match your use cases, like add custom headers in every request.++## About the Project++Request is © 2020-2021 by [aisk](https://github.com/aisk).++### License++Request is distributed by a [BSD license](https://github.com/aisk/request/tree/master/LICENSE).
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ request.cabal view
@@ -0,0 +1,29 @@+name: request+version: 0.1.0.0+-- synopsis:+description: "HTTP client for haskell, inpired by requests and http-dispatch."+homepage: https://github.com/aisk/request#readme+license: BSD3+license-file: LICENSE+author: An Long+maintainer: aisk1988@gmail.com+copyright: 2020 An Long+category: Web+build-type: Simple+extra-source-files: README.md+cabal-version: >=1.10++library+ hs-source-dirs: src+ exposed-modules: Network.HTTP.Request+ build-depends: base >= 4.7 && < 5+ , bytestring >= 0.10.12 && < 0.11+ , case-insensitive >= 1.2.1 && < 1.3+ , http-client >= 0.7.7 && < 0.8+ , http-types >= 0.12.3 && < 0.13+ , http-client-tls >= 0.3.5 && < 0.4+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/aisk/request
+ src/Network/HTTP/Request.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE DatatypeContexts #-}+{-# LANGUAGE OverloadedStrings #-}++module Network.HTTP.Request+ ( Request+ , Response+ , Header+ , Headers+ ) where++import qualified Data.String as S+import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as C+import qualified Data.ByteString.Lazy as LBS+import qualified Data.CaseInsensitive as CI+import qualified Data.List as List+import qualified Network.HTTP.Client as LowLevelClient+import qualified Network.HTTP.Client.TLS as LowLevelTLSClient+import qualified Network.HTTP.Types.Status as LowLevelStatus++type Header = (BS.ByteString, BS.ByteString)++type Headers = [Header]++data Method+ = DELETE+ | GET+ | HEAD+ | OPTIONS+ | PATCH+ | POST+ | PUT+ | TRACE+ deriving (Eq, Show)++data (S.IsString a) => Request a = Request+ { requestMethod :: Method+ , requestUrl :: String+ , requestHeaders :: Headers+ , requestBody :: Maybe a+ } deriving (Show)++toLowlevelRequest :: (S.IsString a) => Request a -> IO LowLevelClient.Request+toLowlevelRequest req = do+ initReq <- LowLevelClient.parseRequest $ requestUrl req+ return $ initReq { LowLevelClient.method = C.pack . show $ requestMethod req+ , LowLevelClient.requestHeaders = map (\(k, v) -> (CI.mk k, v)) $ requestHeaders req+ }++data Response = Response+ { responseStatus :: Int+ , responseHeaders :: Headers+ , responseBody :: BS.ByteString+ } deriving (Show)++fromLowLevelRequest :: LowLevelClient.Response LBS.ByteString -> Response+fromLowLevelRequest res =+ let status = LowLevelStatus.statusCode . LowLevelClient.responseStatus $ res+ body = LBS.toStrict $ LowLevelClient.responseBody res+ headers = LowLevelClient.responseHeaders res+ in+ Response status (map (\(k,v) ->+ let hk = CI.original k+ in+ (hk, v)) headers) body++getManagerForUrl :: String -> IO LowLevelClient.Manager+getManagerForUrl url =+ if "https" `List.isPrefixOf` url then LowLevelClient.newManager LowLevelTLSClient.tlsManagerSettings+ else LowLevelClient.newManager LowLevelClient.defaultManagerSettings++send :: (S.IsString a) => Request a -> IO Response+send req = do+ manager <- getManagerForUrl $ requestUrl req+ llreq <- toLowlevelRequest req+ llres <- LowLevelClient.httpLbs llreq manager+ return $ fromLowLevelRequest llres++get :: String -> IO Response+get url =+ send $ Request GET url [] Nothing++delete :: String -> IO Response+delete url =+ send $ Request DELETE url [] Nothing++post :: (String, Maybe BS.ByteString) -> IO Response+post (url, body) =+ send $ Request POST url [] body++put :: (String, Maybe BS.ByteString) -> IO Response+put (url, body) =+ send $ Request PUT url [] body