{-# LANGUAGE FlexibleContexts #-}{-# LANGUAGE ConstraintKinds #-}{-# LANGUAGE FlexibleInstances #-}{-# LANGUAGE FunctionalDependencies #-}{-# LANGUAGE OverloadedStrings #-}{-# LANGUAGE QuasiQuotes #-}{-# LANGUAGE RecordWildCards #-}{-# LANGUAGE TupleSections #-}{-# LANGUAGE TypeFamilies #-}{-# LANGUAGE RankNTypes #-}------------------------------------------------------------- Module : Yesod.Handler-- Copyright : Michael Snoyman-- License : BSD3---- Maintainer : Michael Snoyman <michael@snoyman.com>-- Stability : stable-- Portability : portable---- Define Handler stuff.-----------------------------------------------------------moduleYesod.Core.Handler(-- * Handler monadHandlerT-- ** Read information from handler,getYesod,getUrlRender,getUrlRenderParams,getCurrentRoute,getRequest,waiRequest,runRequestBody,rawRequestBody-- ** Request information-- *** Request datatype,RequestBodyContents,YesodRequest(..),FileInfo,fileName,fileContentType,fileSource,fileMove-- *** Convenience functions,languages-- *** Lookup parameters,lookupGetParam,lookupPostParam,lookupCookie,lookupFile-- **** Multi-lookup,lookupGetParams,lookupPostParams,lookupCookies,lookupFiles-- * Responses-- ** Pure,respond-- ** Streaming,respondSource,sendChunk,sendFlush,sendChunkBS,sendChunkLBS,sendChunkText,sendChunkLazyText,sendChunkHtml-- ** Redirecting,RedirectUrl(..),redirect,redirectWith,redirectToPost-- ** Errors,notFound,badMethod,notAuthenticated,permissionDenied,permissionDeniedI,invalidArgs,invalidArgsI-- ** Short-circuit responses.,sendFile,sendFilePart,sendResponse,sendResponseStatus,sendResponseCreated,sendWaiResponse-- * Different representations-- $representations,selectRep,provideRep,provideRepType,ProvidedRep-- * Setting headers,setCookie,getExpires,deleteCookie,addHeader,setHeader,setLanguage-- ** Content caching and expiration,cacheSeconds,neverExpires,alreadyExpired,expiresAt-- * Session,SessionMap,lookupSession,lookupSessionBS,getSession,setSession,setSessionBS,deleteSession,clearSession-- ** Ultimate destination,setUltDest,setUltDestCurrent,setUltDestReferer,redirectUltDest,clearUltDest-- ** Messages,setMessage,setMessageI,getMessage-- * Helpers for specific content-- ** Hamlet,hamletToRepHtml,giveUrlRenderer-- ** Misc,newIdent-- * Lifting,handlerToIO-- * i18n,getMessageRender-- * Per-request caching,cached)whereimportData.Time(UTCTime,addUTCTime,getCurrentTime)importYesod.Core.Internal.Request(langKey,mkFileInfoFile,mkFileInfoLBS,mkFileInfoSource)importControl.Applicative((<$>),(<|>))importControl.Exception(evaluate)importControl.Monad(liftM)importqualifiedControl.Monad.Trans.WriterasWriterimportControl.Monad.IO.Class(MonadIO,liftIO)importControl.Monad.Trans.Resource(MonadResource,liftResourceT)importqualifiedNetwork.HTTP.TypesasHimportqualifiedNetwork.WaiasWimportControl.Monad.Trans.Class(lift)importData.Conduit(transPipe,Flush(Flush),yield,Producer)importqualifiedData.TextasTimportData.Text.Encoding(decodeUtf8With,encodeUtf8)importData.Text.Encoding.Error(lenientDecode)importqualifiedData.Text.LazyasTLimportqualifiedText.Blaze.Html.Renderer.TextasRenderTextimportText.Hamlet(Html,HtmlUrl,hamlet)importqualifiedData.ByteStringasSimportqualifiedData.ByteString.LazyasLimportqualifiedData.MapasMapimportData.Conduit(Source)importControl.Arrow((***))importqualifiedData.ByteString.Char8asS8importData.Maybe(mapMaybe)importData.Monoid(Endo(..),mappend,mempty)importData.Text(Text)importqualifiedNetwork.Wai.ParseasNWPimportText.Shakespeare.I18N(RenderMessage(..))importWeb.Cookie(SetCookie(..))importYesod.Core.Content(ToTypedContent(..),simpleContentType,contentTypeTypes,HasContentType(..),ToContent(..),ToFlushBuilder(..))importYesod.Core.Internal.Util(formatRFC1123)importText.Blaze.Html(preEscapedToMarkup,toHtml)importControl.Monad.Trans.Resource(ResourceT,runResourceT,withInternalState)importData.Dynamic(fromDynamic,toDyn)importqualifiedData.IORef.LiftedasIimportData.Maybe(listToMaybe)importData.Typeable(Typeable,typeOf)importYesod.Core.Class.HandlerimportYesod.Core.TypesimportYesod.Routes.Class(Route)importControl.Failure(failure)importBlaze.ByteString.Builder(Builder)importSafe(headMay)get::MonadHandlerm=>mGHStateget=liftHandlerT$HandlerT$I.readIORef.handlerStateput::MonadHandlerm=>GHState->m()putx=liftHandlerT$HandlerT$flipI.writeIORefx.handlerStatemodify::MonadHandlerm=>(GHState->GHState)->m()modifyf=liftHandlerT$HandlerT$flipI.modifyIOReff.handlerStatetell::MonadHandlerm=>Endo[Header]->m()tellhs=modify$\g->g{ghsHeaders=ghsHeadersg`mappend`hs}handlerError::MonadHandlerm=>HandlerContents->mahandlerError=liftHandlerT.failurehcError::MonadHandlerm=>ErrorResponse->mahcError=handlerError.HCErrorgetRequest::MonadHandlerm=>mYesodRequestgetRequest=liftHandlerT$HandlerT$return.handlerRequestrunRequestBody::MonadHandlerm=>mRequestBodyContentsrunRequestBody=doHandlerData{handlerEnv=RunHandlerEnv{..},handlerRequest=req}<-liftHandlerT$HandlerTreturnletlen=W.requestBodyLength$reqWaiRequestrequpload=rheUploadlenx<-getcaseghsRBCxofJustrbc->returnrbcNothing->dorr<-waiRequestrbc<-liftResourceT$rbHelperuploadrrputx{ghsRBC=Justrbc}returnrbcrbHelper::FileUpload->W.Request->ResourceTIORequestBodyContentsrbHelperupload=caseuploadofFileUploadMemorys->rbHelper'smkFileInfoLBSFileUploadDisks->rbHelper'smkFileInfoFileFileUploadSources->rbHelper'smkFileInfoSourcerbHelper'::NWP.BackEndx->(Text->Text->x->FileInfo)->W.Request->ResourceTIO([(Text,Text)],[(Text,FileInfo)])rbHelper'backendmkFIreq=(mapfix1***mapMaybefix2)<$>(NWP.parseRequestBodybackendreq)wherefix1=go***gofix2(x,NWP.FileInfoa'bc)|S.nulla=Nothing|otherwise=Just(gox,mkFI(goa)(gob)c)wherea|S.lengtha'<2=a'|S8.heada'=='"'&&S8.lasta'=='"'=S.tail$S.inita'|S8.heada'=='\''&&S8.lasta'=='\''=S.tail$S.inita'|otherwise=a'go=decodeUtf8WithlenientDecodeaskHandlerEnv::MonadHandlerm=>m(RunHandlerEnv(HandlerSitem))askHandlerEnv=liftHandlerT$HandlerT$return.handlerEnv-- | Get the master site appliation argument.getYesod::MonadHandlerm=>m(HandlerSitem)getYesod=rheSite`liftM`askHandlerEnv-- | Get the URL rendering function.getUrlRender::MonadHandlerm=>m(Route(HandlerSitem)->Text)getUrlRender=dox<-rheRender`liftM`askHandlerEnvreturn$flipx[]-- | The URL rendering function with query-string parameters.getUrlRenderParams::MonadHandlerm=>m(Route(HandlerSitem)->[(Text,Text)]->Text)getUrlRenderParams=rheRender`liftM`askHandlerEnv-- | Get the route requested by the user. If this is a 404 response- where the-- user requested an invalid route- this function will return 'Nothing'.getCurrentRoute::MonadHandlerm=>m(Maybe(Route(HandlerSitem)))getCurrentRoute=rheRoute`liftM`askHandlerEnv-- | Returns a function that runs 'HandlerT' actions inside @IO@.---- Sometimes you want to run an inner 'HandlerT' action outside-- the control flow of an HTTP request (on the outer 'HandlerT'-- action). For example, you may want to spawn a new thread:---- @-- getFooR :: Handler RepHtml-- getFooR = do-- runInnerHandler <- handlerToIO-- liftIO $ forkIO $ runInnerHandler $ do-- /Code here runs inside GHandler but on a new thread./-- /This is the inner GHandler./-- ...-- /Code here runs inside the request's control flow./-- /This is the outer GHandler./-- ...-- @---- Another use case for this function is creating a stream of-- server-sent events using 'GHandler' actions (see-- @yesod-eventsource@).---- Most of the environment from the outer 'GHandler' is preserved-- on the inner 'GHandler', however:---- * The request body is cleared (otherwise it would be very-- difficult to prevent huge memory leaks).---- * The cache is cleared (see 'CacheKey').---- Changes to the response made inside the inner 'GHandler' are-- ignored (e.g., session variables, cookies, response headers).-- This allows the inner 'GHandler' to outlive the outer-- 'GHandler' (e.g., on the @forkIO@ example above, a response-- may be sent to the client without killing the new thread).handlerToIO::(MonadIOm1,MonadIOm2)=>HandlerTsitem1(HandlerTsiteIOa->m2a)handlerToIO=HandlerT$\oldHandlerData->do-- Take just the bits we need from oldHandlerData.letnewReq=oldReq{reqWaiRequest=newWaiReq}whereoldReq=handlerRequestoldHandlerDataoldWaiReq=reqWaiRequestoldReqnewWaiReq=oldWaiReq{W.requestBody=mempty,W.requestBodyLength=W.KnownLength0}oldEnv=handlerEnvoldHandlerDatanewState<-liftIO$dooldState<-I.readIORef(handlerStateoldHandlerData)return$oldState{ghsRBC=Nothing,ghsIdent=1,ghsCache=mempty,ghsHeaders=mempty}-- xx From this point onwards, no references to oldHandlerData xxliftIO$evaluate(newReq`seq`oldEnv`seq`newState`seq`())-- Return GHandler running function.return$\(HandlerTf)->liftIO$runResourceT$withInternalState$\resState->do-- The state IORef needs to be created here, otherwise it-- will be shared by different invocations of this function.newStateIORef<-liftIO(I.newIORefnewState)letnewHandlerData=HandlerData{handlerRequest=newReq,handlerEnv=oldEnv,handlerState=newStateIORef,handlerToParent=const(),handlerResource=resState}liftIO(fnewHandlerData)-- | Redirect to the given route.-- HTTP status code 303 for HTTP 1.1 clients and 302 for HTTP 1.0-- This is the appropriate choice for a get-following-post-- technique, which should be the usual use case.---- If you want direct control of the final status code, or need a different-- status code, please use 'redirectWith'.redirect::(MonadHandlerm,RedirectUrl(HandlerSitem)url)=>url->maredirecturl=doreq<-waiRequestletstatus=ifW.httpVersionreq==H.http11thenH.status303elseH.status302redirectWithstatusurl-- | Redirect to the given URL with the specified status code.redirectWith::(MonadHandlerm,RedirectUrl(HandlerSitem)url)=>H.Status->url->maredirectWithstatusurl=dourlText<-toTextUrlurlhandlerError$HCRedirectstatusurlTextultDestKey::TextultDestKey="_ULT"-- | Sets the ultimate destination variable to the given route.---- An ultimate destination is stored in the user session and can be loaded-- later by 'redirectUltDest'.setUltDest::(MonadHandlerm,RedirectUrl(HandlerSitem)url)=>url->m()setUltDesturl=dourlText<-toTextUrlurlsetSessionultDestKeyurlText-- | Same as 'setUltDest', but uses the current page.---- If this is a 404 handler, there is no current page, and then this call does-- nothing.setUltDestCurrent::MonadHandlerm=>m()setUltDestCurrent=doroute<-getCurrentRoutecaserouteofNothing->return()Justr->dogets'<-reqGetParams`liftM`getRequestsetUltDest(r,gets')-- | Sets the ultimate destination to the referer request header, if present.---- This function will not overwrite an existing ultdest.setUltDestReferer::MonadHandlerm=>m()setUltDestReferer=domdest<-lookupSessionultDestKeymaybe(waiRequest>>=maybe(return())setUltDestBS.lookup"referer".W.requestHeaders)(const$return())mdestwheresetUltDestBS=setUltDest.T.pack.S8.unpack-- | Redirect to the ultimate destination in the user's session. Clear the-- value from the session.---- The ultimate destination is set with 'setUltDest'.---- This function uses 'redirect', and thus will perform a temporary redirect to-- a GET request.redirectUltDest::(RedirectUrl(HandlerSitem)url,MonadHandlerm)=>url-- ^ default destination if nothing in session->maredirectUltDestdef=domdest<-lookupSessionultDestKeydeleteSessionultDestKeymaybe(redirectdef)redirectmdest-- | Remove a previously set ultimate destination. See 'setUltDest'.clearUltDest::MonadHandlerm=>m()clearUltDest=deleteSessionultDestKeymsgKey::TextmsgKey="_MSG"-- | Sets a message in the user's session.---- See 'getMessage'.setMessage::MonadHandlerm=>Html->m()setMessage=setSessionmsgKey.T.concat.TL.toChunks.RenderText.renderHtml-- | Sets a message in the user's session.---- See 'getMessage'.setMessageI::(MonadHandlerm,RenderMessage(HandlerSitem)msg)=>msg->m()setMessageImsg=domr<-getMessageRendersetMessage$toHtml$mrmsg-- | Gets the message in the user's session, if available, and then clears the-- variable.---- See 'setMessage'.getMessage::MonadHandlerm=>m(MaybeHtml)getMessage=dommsg<-liftM(fmappreEscapedToMarkup)$lookupSessionmsgKeydeleteSessionmsgKeyreturnmmsg-- | Bypass remaining handler code and output the given file.---- For some backends, this is more efficient than reading in the file to-- memory, since they can optimize file sending via a system call to sendfile.sendFile::MonadHandlerm=>ContentType->FilePath->masendFilectfp=handlerError$HCSendFilectfpNothing-- | Same as 'sendFile', but only sends part of a file.sendFilePart::MonadHandlerm=>ContentType->FilePath->Integer-- ^ offset->Integer-- ^ count->masendFilePartctfpoffcount=handlerError$HCSendFilectfp$Just$W.FilePartoffcount-- | Bypass remaining handler code and output the given content with a 200-- status code.sendResponse::(MonadHandlerm,ToTypedContentc)=>c->masendResponse=handlerError.HCContentH.status200.toTypedContent-- | Bypass remaining handler code and output the given content with the given-- status code.sendResponseStatus::(MonadHandlerm,ToTypedContentc)=>H.Status->c->masendResponseStatuss=handlerError.HCContents.toTypedContent-- | Send a 201 "Created" response with the given route as the Location-- response header.sendResponseCreated::MonadHandlerm=>Route(HandlerSitem)->masendResponseCreatedurl=dor<-getUrlRenderhandlerError$HCCreated$rurl-- | Send a 'W.Response'. Please note: this function is rarely-- necessary, and will /disregard/ any changes to response headers and session-- that you have already specified. This function short-circuits. It should be-- considered only for very specific needs. If you are not sure if you need it,-- you don't.sendWaiResponse::MonadHandlerm=>W.Response->mbsendWaiResponse=handlerError.HCWai-- | Return a 404 not found page. Also denotes no handler available.notFound::MonadHandlerm=>manotFound=hcErrorNotFound-- | Return a 405 method not supported page.badMethod::MonadHandlerm=>mabadMethod=dow<-waiRequesthcError$BadMethod$W.requestMethodw-- | Return a 401 status codenotAuthenticated::MonadHandlerm=>manotAuthenticated=hcErrorNotAuthenticated-- | Return a 403 permission denied page.permissionDenied::MonadHandlerm=>Text->mapermissionDenied=hcError.PermissionDenied-- | Return a 403 permission denied page.permissionDeniedI::(RenderMessage(HandlerSitem)msg,MonadHandlerm)=>msg->mapermissionDeniedImsg=domr<-getMessageRenderpermissionDenied$mrmsg-- | Return a 400 invalid arguments page.invalidArgs::MonadHandlerm=>[Text]->mainvalidArgs=hcError.InvalidArgs-- | Return a 400 invalid arguments page.invalidArgsI::(MonadHandlerm,RenderMessage(HandlerSitem)msg)=>[msg]->mainvalidArgsImsg=domr<-getMessageRenderinvalidArgs$mapmrmsg------- Headers-- | Set the cookie on the client.setCookie::MonadHandlerm=>SetCookie->m()setCookie=addHeaderInternal.AddCookie-- | Helper function for setCookieExpires valuegetExpires::MonadIOm=>Int-- ^ minutes->mUTCTimegetExpiresm=donow<-liftIOgetCurrentTimereturn$fromIntegral(m*60)`addUTCTime`now-- | Unset the cookie on the client.---- Note: although the value used for key and path is 'Text', you should only-- use ASCII values to be HTTP compliant.deleteCookie::MonadHandlerm=>Text-- ^ key->Text-- ^ path->m()deleteCookiea=addHeaderInternal.DeleteCookie(encodeUtf8a).encodeUtf8-- | Set the language in the user session. Will show up in 'languages' on the-- next request.setLanguage::MonadHandlerm=>Text->m()setLanguage=setSessionlangKey-- | Set an arbitrary response header.---- Note that, while the data type used here is 'Text', you must provide only-- ASCII value to be HTTP compliant.---- Since 1.2.0addHeader::MonadHandlerm=>Text->Text->m()addHeadera=addHeaderInternal.Header(encodeUtf8a).encodeUtf8-- | Deprecated synonym for addHeader.setHeader::MonadHandlerm=>Text->Text->m()setHeader=addHeader{-# DEPRECATED setHeader "Please use addHeader instead" #-}-- | Set the Cache-Control header to indicate this response should be cached-- for the given number of seconds.cacheSeconds::MonadHandlerm=>Int->m()cacheSecondsi=setHeader"Cache-Control"$T.concat["max-age=",T.pack$showi,", public"]-- | Set the Expires header to some date in 2037. In other words, this content-- is never (realistically) expired.neverExpires::MonadHandlerm=>m()neverExpires=setHeader"Expires""Thu, 31 Dec 2037 23:55:55 GMT"-- | Set an Expires header in the past, meaning this content should not be-- cached.alreadyExpired::MonadHandlerm=>m()alreadyExpired=setHeader"Expires""Thu, 01 Jan 1970 05:05:05 GMT"-- | Set an Expires header to the given date.expiresAt::MonadHandlerm=>UTCTime->m()expiresAt=setHeader"Expires".formatRFC1123-- | Set a variable in the user's session.---- The session is handled by the clientsession package: it sets an encrypted-- and hashed cookie on the client. This ensures that all data is secure and-- not tampered with.setSession::MonadHandlerm=>Text-- ^ key->Text-- ^ value->m()setSessionk=setSessionBSk.encodeUtf8-- | Same as 'setSession', but uses binary data for the value.setSessionBS::MonadHandlerm=>Text->S.ByteString->m()setSessionBSk=modify.modSession.Map.insertk-- | Unsets a session variable. See 'setSession'.deleteSession::MonadHandlerm=>Text->m()deleteSession=modify.modSession.Map.delete-- | Clear all session variables.---- Since: 1.0.1clearSession::MonadHandlerm=>m()clearSession=modify$\x->x{ghsSession=Map.empty}modSession::(SessionMap->SessionMap)->GHState->GHStatemodSessionfx=x{ghsSession=f$ghsSessionx}-- | Internal use only, not to be confused with 'setHeader'.addHeaderInternal::MonadHandlerm=>Header->m()addHeaderInternal=tell.Endo.(:)-- | Some value which can be turned into a URL for redirects.classRedirectUrlmasterawhere-- | Converts the value to the URL and a list of query-string parameters.toTextUrl::(MonadHandlerm,HandlerSitem~master)=>a->mTextinstanceRedirectUrlmasterTextwheretoTextUrl=returninstanceRedirectUrlmasterStringwheretoTextUrl=toTextUrl.T.packinstanceRedirectUrlmaster(Routemaster)wheretoTextUrlurl=dor<-getUrlRenderreturn$rurlinstance(key~Text,val~Text)=>RedirectUrlmaster(Routemaster,[(key,val)])wheretoTextUrl(url,params)=dor<-getUrlRenderParamsreturn$rurlparamsinstance(key~Text,val~Text)=>RedirectUrlmaster(Routemaster,Map.Mapkeyval)wheretoTextUrl(url,params)=toTextUrl(url,Map.toListparams)-- | Lookup for session data.lookupSession::MonadHandlerm=>Text->m(MaybeText)lookupSession=(liftM.fmap)(decodeUtf8WithlenientDecode).lookupSessionBS-- | Lookup for session data in binary format.lookupSessionBS::MonadHandlerm=>Text->m(MaybeS.ByteString)lookupSessionBSn=dom<-liftMghsSessiongetreturn$Map.lookupnm-- | Get all session variables.getSession::MonadHandlerm=>mSessionMapgetSession=liftMghsSessionget-- | Get a unique identifier.newIdent::MonadHandlerm=>mTextnewIdent=dox<-getleti'=ghsIdentx+1putx{ghsIdent=i'}return$T.pack$'h':showi'-- | Redirect to a POST resource.---- This is not technically a redirect; instead, it returns an HTML page with a-- POST form, and some Javascript to automatically submit the form. This can be-- useful when you need to post a plain link somewhere that needs to cause-- changes on the server.redirectToPost::(MonadHandlerm,RedirectUrl(HandlerSitem)url)=>url->maredirectToPosturl=dourlText<-toTextUrlurlhamletToRepHtml[hamlet|$newlinenever$doctype5<html><head><title>Redirecting...<bodyonload="document.getElementById('form').submit()"><formid="form"method="post"action=#{urlText}><noscript><p>Javascripthasbeendisabled;pleaseclickonthebuttonbelowtoberedirected.<inputtype="submit"value="Continue">|]>>=sendResponse-- | Wraps the 'Content' generated by 'hamletToContent' in a 'RepHtml'.hamletToRepHtml::MonadHandlerm=>HtmlUrl(Route(HandlerSitem))->mHtmlhamletToRepHtml=giveUrlRenderer-- | Provide a URL rendering function to the given function and return the-- result. Useful for processing Shakespearean templates.---- Since 1.2.0giveUrlRenderer::MonadHandlerm=>((Route(HandlerSitem)->[(Text,Text)]->Text)->output)->moutputgiveUrlRendererf=dorender<-getUrlRenderParamsreturn$frender-- | Get the request\'s 'W.Request' value.waiRequest::MonadHandlerm=>mW.RequestwaiRequest=reqWaiRequest`liftM`getRequestgetMessageRender::(MonadHandlerm,RenderMessage(HandlerSitem)message)=>m(message->Text)getMessageRender=doenv<-askHandlerEnvl<-reqLangs`liftM`getRequestreturn$renderMessage(rheSiteenv)l-- | Use a per-request cache to avoid performing the same action multiple-- times. Note that values are stored by their type. Therefore, you should use-- newtype wrappers to distinguish logically different types.---- Since 1.2.0cached::(MonadHandlerm,Typeablea)=>ma->macachedf=dogs<-getletcache=ghsCachegscaseclookupcacheofJustval->returnvalNothing->doval<-fput$gs{ghsCache=cinsertvalcache}returnvalwhereclookup::Typeablea=>Cache->Maybeaclookup(Cachem)=reswhereres=Map.lookup(typeOf$fromJustres)m>>=fromDynamicfromJust::Maybea->afromJust=error"Yesod.Handler.cached.fromJust: Argument to typeOf was evaluated"cinsert::Typeablea=>a->Cache->Cachecinsertv(Cachem)=Cache(Map.insert(typeOfv)(toDynv)m)-- | Get the list of supported languages supplied by the user.---- Languages are determined based on the following three (in descending order-- of preference):---- * The _LANG get parameter.---- * The _LANG cookie.---- * The _LANG user session variable.---- * Accept-Language HTTP header.---- Yesod will seek the first language from the returned list matched with languages supporting by your application. This language will be used to render i18n templates.-- If a matching language is not found the default language will be used.---- This is handled by parseWaiRequest (not exposed).languages::MonadHandlerm=>m[Text]languages=reqLangs`liftM`getRequestlookup'::Eqa=>a->[(a,b)]->[b]lookup'a=mapsnd.filter(\x->a==fstx)-- | Lookup for GET parameters.lookupGetParams::MonadHandlerm=>Text->m[Text]lookupGetParamspn=dorr<-getRequestreturn$lookup'pn$reqGetParamsrr-- | Lookup for GET parameters.lookupGetParam::MonadHandlerm=>Text->m(MaybeText)lookupGetParam=liftMlistToMaybe.lookupGetParams-- | Lookup for POST parameters.lookupPostParams::(MonadResourcem,MonadHandlerm)=>Text->m[Text]lookupPostParamspn=do(pp,_)<-runRequestBodyreturn$lookup'pnpplookupPostParam::(MonadResourcem,MonadHandlerm)=>Text->m(MaybeText)lookupPostParam=liftMlistToMaybe.lookupPostParams-- | Lookup for POSTed files.lookupFile::(MonadHandlerm,MonadResourcem)=>Text->m(MaybeFileInfo)lookupFile=liftMlistToMaybe.lookupFiles-- | Lookup for POSTed files.lookupFiles::(MonadHandlerm,MonadResourcem)=>Text->m[FileInfo]lookupFilespn=do(_,files)<-runRequestBodyreturn$lookup'pnfiles-- | Lookup for cookie data.lookupCookie::MonadHandlerm=>Text->m(MaybeText)lookupCookie=liftMlistToMaybe.lookupCookies-- | Lookup for cookie data.lookupCookies::MonadHandlerm=>Text->m[Text]lookupCookiespn=dorr<-getRequestreturn$lookup'pn$reqCookiesrr-- $representations---- HTTP allows content negotation to determine what /representation/ of data-- you would like to use. The most common example of this is providing both a-- user-facing HTML page and an API facing JSON response from the same URL. The-- means of achieving this is the Accept HTTP header, which provides a list of-- content types the client will accept, sorted by preference.---- By using 'selectRep' and 'provideRep', you can provide a number of different-- representations, e.g.:---- > selectRep $ do-- > provideRep typeHtml $ produceHtmlOutput-- > provideRep typeJson $ produceJsonOutput---- The first provided representation will be used if no matches are found.-- | Select a representation to send to the client based on the representations-- provided inside this do-block. Should be used together with 'provideRep'.---- Since 1.2.0selectRep::MonadHandlerm=>Writer.Writer(Endo[ProvidedRepm])()->mTypedContentselectRepw=do-- the content types are already sorted by q values-- which have been strippedcts<-liftMreqAcceptgetRequestcasemapMaybetryAcceptctsof[]->caserepsof[]->sendResponseStatusH.status500("No reps provided to selectRep"::Text)rep:_->ifnullctsthenreturnReprepelsesendResponseStatusH.status406explainUnacceptedrep:_->returnReprepwhereexplainUnaccepted::TextexplainUnaccepted="no match found for accept header"returnRep(ProvidedRepctmcontent)=mcontent>>=return.TypedContentctreps=appEndo(Writer.execWriterw)[]repMap=Map.unions$map(\v@(ProvidedRepk_)->Map.fromList[(k,v),(noSpacek,v),(simpleContentTypek,v)])reps-- match on the type for sub-type wildcards.-- If the accept is text/* it should match a provided text/htmlmainTypeMap=Map.fromList$reverse$map(\v@(ProvidedRepct_)->(fst$contentTypeTypesct,v))repstryAcceptct=ifsubType=="*"thenifmainType=="*"thenheadMayrepselseMap.lookupmainTypemainTypeMapelselookupAcceptctwhere(mainType,subType)=contentTypeTypesctlookupAcceptct=Map.lookupctrepMap<|>Map.lookup(noSpacect)repMap<|>Map.lookup(simpleContentTypect)repMap-- Mime types such as "text/html; charset=foo" get converted to-- "text/html;charset=foo"noSpace=S8.filter(/=' ')-- | Internal representation of a single provided representation.---- Since 1.2.0dataProvidedRepm=ProvidedRep!ContentType!(mContent)-- | Provide a single representation to be used, based on the request of the-- client. Should be used together with 'selectRep'.---- Since 1.2.0provideRep::(Monadm,HasContentTypea)=>ma->Writer.Writer(Endo[ProvidedRepm])()provideRephandler=provideRepType(getContentTypehandler)handler-- | Same as 'provideRep', but instead of determining the content type from the-- type of the value itself, you provide the content type separately. This can-- be a convenience instead of creating newtype wrappers for uncommonly used-- content types.---- > provideRepType "application/x-special-format" "This is the content"---- Since 1.2.0provideRepType::(Monadm,ToContenta)=>ContentType->ma->Writer.Writer(Endo[ProvidedRepm])()provideRepTypecthandler=Writer.tell$Endo$(ProvidedRepct(liftMtoContenthandler):)-- | Stream in the raw request body without any parsing.---- Since 1.2.0rawRequestBody::MonadHandlerm=>SourcemS.ByteStringrawRequestBody=doreq<-liftwaiRequesttransPipeliftResourceT$W.requestBodyreq-- | Stream the data from the file. Since Yesod 1.2, this has been generalized-- to work in any @MonadResource@.fileSource::MonadResourcem=>FileInfo->SourcemS.ByteStringfileSource=transPipeliftResourceT.fileSourceRaw-- | Provide a pure value for the response body.---- > respond ct = return . TypedContent ct . toContent---- Since 1.2.0respond::(Monadm,ToContenta)=>ContentType->a->mTypedContentrespondct=return.TypedContentct.toContent-- | Use a @Source@ for the response body.---- Note that, for ease of use, the underlying monad is a @HandlerT@. This-- implies that you can run any @HandlerT@ action. However, since a streaming-- response occurs after the response headers have already been sent, some-- actions make no sense here. For example: short-circuit responses, setting-- headers, changing status codes, etc.---- Since 1.2.0respondSource::ContentType->Source(HandlerTsiteIO)(FlushBuilder)->HandlerTsiteIOTypedContentrespondSourcectypesrc=HandlerT$\hd->-- Note that this implementation relies on the fact that the ResourceT-- environment provided by the server is the same one used in HandlerT.-- This is a safe assumption assuming the HandlerT is run correctly.return$TypedContentctype$ContentSource$transPipe(lift.flipunHandlerThd)src-- | In a streaming response, send a single chunk of data. This function works-- on most datatypes, such as @ByteString@ and @Html@.---- Since 1.2.0sendChunk::Monadm=>ToFlushBuildera=>a->Producerm(FlushBuilder)sendChunk=yield.toFlushBuilder-- | In a streaming response, send a flush command, causing all buffered data-- to be immediately sent to the client.---- Since 1.2.0sendFlush::Monadm=>Producerm(FlushBuilder)sendFlush=yieldFlush-- | Type-specialized version of 'sendChunk' for strict @ByteString@s.---- Since 1.2.0sendChunkBS::Monadm=>S.ByteString->Producerm(FlushBuilder)sendChunkBS=sendChunk-- | Type-specialized version of 'sendChunk' for lazy @ByteString@s.---- Since 1.2.0sendChunkLBS::Monadm=>L.ByteString->Producerm(FlushBuilder)sendChunkLBS=sendChunk-- | Type-specialized version of 'sendChunk' for strict @Text@s.---- Since 1.2.0sendChunkText::Monadm=>T.Text->Producerm(FlushBuilder)sendChunkText=sendChunk-- | Type-specialized version of 'sendChunk' for lazy @Text@s.---- Since 1.2.0sendChunkLazyText::Monadm=>TL.Text->Producerm(FlushBuilder)sendChunkLazyText=sendChunk-- | Type-specialized version of 'sendChunk' for @Html@s.---- Since 1.2.0sendChunkHtml::Monadm=>Html->Producerm(FlushBuilder)sendChunkHtml=sendChunk