------------------------------------------------------------------------------- |-- Module : Network.HTTP-- Copyright : See LICENSE file-- License : BSD-- -- Maintainer : Ganesh Sittampalam <ganesh@earth.li>-- Stability : experimental-- Portability : non-portable (not tested)---- The 'Network.HTTP' module provides a simple interface for sending and-- receiving content over HTTP in Haskell. Here's how to fetch a document from-- a URL and return it as a String:---- >-- > simpleHTTP (getRequest "http://www.haskell.org/") >>= fmap (take 100) . getResponseBody-- > -- fetch document and return it (as a 'String'.)---- Other functions let you control the submission and transfer of HTTP-- 'Request's and 'Response's more carefully, letting you integrate the use-- of 'Network.HTTP' functionality into your application.---- The module also exports the main types of the package, 'Request' and 'Response',-- along with 'Header' and functions for working with these.---- The actual functionality is implemented by modules in the @Network.HTTP.*@-- namespace, letting you either use the default implementation here-- by importing @Network.HTTP@ or, for more specific uses, selectively-- import the modules in @Network.HTTP.*@. To wit, more than one kind of-- representation of the bulk data that flows across a HTTP connection is -- supported. (see "Network.HTTP.HandleStream".)-- -- /NOTE:/ The 'Request' send actions will normalize the @Request@ prior to transmission.-- Normalization such as having the request path be in the expected form and, possibly,-- introduce a default @Host:@ header if one isn't already present.-- Normalization also takes the @"user:pass\@"@ portion out of the the URI,-- if it was supplied, and converts it into @Authorization: Basic$ header.-- If you do not -- want the requests tampered with, but sent as-is, please import and use the-- the "Network.HTTP.HandleStream" or "Network.HTTP.Stream" modules instead. They-- export the same functions, but leaves construction and any normalization of -- @Request@s to the user.---- /NOTE:/ This package only supports HTTP; it does not support HTTPS.-- Attempts to use HTTPS result in an error.-----------------------------------------------------------------------------moduleNetwork.HTTP(moduleNetwork.HTTP.Base ,moduleNetwork.HTTP.Headers {- the functionality that the implementation modules, Network.HTTP.HandleStream and Network.HTTP.Stream, exposes: -},simpleHTTP -- :: Request -> IO (Result Response),simpleHTTP_ -- :: Stream s => s -> Request -> IO (Result Response),sendHTTP -- :: Stream s => s -> Request -> IO (Result Response),sendHTTP_notify -- :: Stream s => s -> Request -> IO () -> IO (Result Response),receiveHTTP -- :: Stream s => s -> IO (Result Request),respondHTTP -- :: Stream s => s -> Response -> IO (),moduleNetwork.TCP ,getRequest -- :: String -> Request_String,headRequest -- :: String -> Request_String,postRequest -- :: String -> Request_String,postRequestWithBody -- :: String -> String -> String -> Request_String,getResponseBody -- :: Result (Request ty) -> IO ty,getResponseCode -- :: Result (Request ty) -> IO ResponseCode)where----------------------------------------------------------------------------------- Imports -------------------------------------------------------------------------------------------------------importNetwork.HTTP.Headers importNetwork.HTTP.Base importqualifiedNetwork.HTTP.HandleStream asS-- old implementation: import Network.HTTP.StreamimportNetwork.TCP importNetwork.Stream (Result )importNetwork.URI(parseURI)importData.Maybe(fromMaybe){- Note: if you switch over/back to using Network.HTTP.Stream here, you'll have to wrap the results from 'openStream' as Connections via 'hstreamToConnection' prior to delegating to the Network.HTTP.Stream functions. -}-- | @simpleHTTP req@ transmits the 'Request' @req@ by opening a /direct/, non-persistent-- connection to the HTTP server that @req@ is destined for, followed by transmitting-- it and gathering up the response as a 'Result'. Prior to sending the request,-- it is normalized (via 'normalizeRequest'). If you have to mediate the request-- via an HTTP proxy, you will have to normalize the request yourself. Or switch to-- using 'Network.Browser' instead.---- Examples:---- > simpleHTTP (getRequest "http://hackage.haskell.org/")-- > simpleHTTP (getRequest "http://hackage.haskell.org:8012/")simpleHTTP::(HStream ty )=>Request ty ->IO(Result (Response ty ))simpleHTTP r =doauth <-getAuth r failHTTPS (rqURIr )c <-openStream (hostauth )(fromMaybe80(portauth ))letnorm_r =normalizeRequest defaultNormalizeRequestOptions {normDoClose=True}r simpleHTTP_ c norm_r -- | Identical to 'simpleHTTP', but acting on an already opened stream.simpleHTTP_::HStream ty =>HandleStream ty ->Request ty ->IO(Result (Response ty ))simpleHTTP_ s r =doletnorm_r =normalizeRequest defaultNormalizeRequestOptions {normDoClose=True}r S.sendHTTP s norm_r -- | @sendHTTP hStream httpRequest@ transmits @httpRequest@ (after normalization) over-- @hStream@, but does not alter the status of the connection, nor request it to be-- closed upon receiving the response.sendHTTP::HStream ty =>HandleStream ty ->Request ty ->IO(Result (Response ty ))sendHTTP conn rq =doletnorm_r =normalizeRequest defaultNormalizeRequestOptions rq S.sendHTTP conn norm_r -- | @sendHTTP_notify hStream httpRequest action@ behaves like 'sendHTTP', but-- lets you supply an IO @action@ to execute once the request has been successfully-- transmitted over the connection. Useful when you want to set up tracing of-- request transmission and its performance.sendHTTP_notify::HStream ty =>HandleStream ty ->Request ty ->IO()->IO(Result (Response ty ))sendHTTP_notify conn rq onSendComplete =doletnorm_r =normalizeRequest defaultNormalizeRequestOptions rq S.sendHTTP_notify conn norm_r onSendComplete -- | @receiveHTTP hStream@ reads a 'Request' from the 'HandleStream' @hStream@receiveHTTP::HStream ty =>HandleStream ty ->IO(Result (Request ty ))receiveHTTP conn =S.receiveHTTP conn -- | @respondHTTP hStream httpResponse@ transmits an HTTP 'Response' over-- the 'HandleStream' @hStream@. It could be used to implement simple web-- server interactions, performing the dual role to 'sendHTTP'.respondHTTP::HStream ty =>HandleStream ty ->Response ty ->IO()respondHTTP conn rsp =S.respondHTTP conn rsp -- | A convenience constructor for a GET 'Request'.---- If the URL isn\'t syntactically valid, the function raises an error.getRequest::String-- ^URL to fetch->Request_String -- ^The constructed requestgetRequest urlString =caseparseURIurlString ofNothing->error("getRequest: Not a valid URL - "++urlString )Justu ->mkRequest GET u -- | A convenience constructor for a HEAD 'Request'.---- If the URL isn\'t syntactically valid, the function raises an error.headRequest::String-- ^URL to fetch->Request_String -- ^The constructed requestheadRequest urlString =caseparseURIurlString ofNothing->error("headRequest: Not a valid URL - "++urlString )Justu ->mkRequest HEAD u -- | A convenience constructor for a POST 'Request'.---- If the URL isn\'t syntactically valid, the function raises an error.postRequest::String-- ^URL to POST to->Request_String -- ^The constructed requestpostRequest urlString =caseparseURIurlString ofNothing->error("postRequest: Not a valid URL - "++urlString )Justu ->mkRequest POST u -- | A convenience constructor for a POST 'Request'.---- It constructs a request and sets the body as well as-- the Content-Type and Content-Length headers. The contents of the body-- are forced to calculate the value for the Content-Length header.---- If the URL isn\'t syntactically valid, the function raises an error.postRequestWithBody::String-- ^URL to POST to->String-- ^Content-Type of body->String-- ^The body of the request->Request_String -- ^The constructed requestpostRequestWithBody urlString typ body =caseparseURIurlString ofNothing->error("postRequestWithBody: Not a valid URL - "++urlString )Justu ->setRequestBody (mkRequest POST u )(typ ,body )-- | @getResponseBody response@ takes the response of a HTTP requesting action and-- tries to extricate the body of the 'Response' @response@. If the request action-- returned an error, an IO exception is raised.getResponseBody::Result (Response ty )->IOty getResponseBody (Lefterr )=fail(showerr )getResponseBody(Rightr )=return(rspBodyr )-- | @getResponseBody response@ takes the response of a HTTP requesting action and-- tries to extricate the status code of the 'Response' @response@. If the request action-- returned an error, an IO exception is raised.getResponseCode::Result (Response ty )->IOResponseCode getResponseCode (Lefterr )=fail(showerr )getResponseCode(Rightr )=return(rspCoder )---- * TODO-- - request pipelining-- - https upgrade (includes full TLS, i.e. SSL, implementation)-- - use of Stream classes will pay off-- - consider C implementation of encryption\/decryption-- - comm timeouts-- - MIME & entity stuff (happening in separate module)-- - support \"*\" uri-request-string for OPTIONS request method-- -- -- * Header notes:---- [@Host@]-- Required by HTTP\/1.1, if not supplied as part-- of a request a default Host value is extracted-- from the request-uri.-- -- [@Connection@] -- If this header is present in any request or-- response, and it's value is "close", then-- the current request\/response is the last -- to be allowed on that connection.-- -- [@Expect@]-- Should a request contain a body, an Expect-- header will be added to the request. The added-- header has the value \"100-continue\". After-- a 417 \"Expectation Failed\" response the request-- is attempted again without this added Expect-- header.-- -- [@TransferEncoding,ContentLength,...@]-- if request is inconsistent with any of these-- header values then you may not receive any response-- or will generate an error response (probably 4xx).------ * Response code notes-- Some response codes induce special behaviour:---- [@1xx@] \"100 Continue\" will cause any unsent request body to be sent.-- \"101 Upgrade\" will be returned.-- Other 1xx responses are ignored.-- -- [@417@] The reason for this code is \"Expectation failed\", indicating-- that the server did not like the Expect \"100-continue\" header-- added to a request. Receipt of 417 will induce another-- request attempt (without Expect header), unless no Expect header-- had been added (in which case 417 response is returned).