sourceafBedSheet::HttpResponse.fan

using afIoc::Inject
using web::Cookie
using web::WebRes
using concurrent::Actor
using web::WebUtil

** (Service) - An injectable 'const' version of [WebRes]`web::WebRes`.
** 
** This will always refers to the current web response.
const mixin HttpResponse {

    ** Get / set the HTTP status code for this response.
    ** Setter throws Err if response is already committed.
    ** 
    ** @see `web::WebRes.statusCode`
    abstract Int statusCode
    
    ** Map of HTTP response headers.  You must set all headers before you access out() for the 
    ** first time, which commits the response.  
    ** 
    ** @see 
    **  - `web::WebRes.headers`
    **  - `http://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Responses`
    abstract HttpResponseHeaders headers()
    
    ** Return true if this response has been committed.  A committed response has written its 
    ** response headers, and can no longer modify its status code or headers.  A response is 
    ** committed the first time that `out` is called.
    ** 
    ** @see `web::WebRes.isCommitted`
    abstract Bool isCommitted()
    
    ** Returns the 'OutStream' for this response. 
    ** Should current settings allow, the 'OutStream' is automatically gzipped.
    ** 
    ** @see `web::WebRes.out`
    abstract OutStream out()
    
    ** Set to 'true' to disable gzip compression for this response.
    abstract Bool disableGzip
    
    ** Set to 'true' to disable buffering for this response.
    ** 
    ** Buffered responses contain a 'Content-Length' header and are easier to process by clients. 
    ** Non buffered responses are streamed straight out to the client.
    abstract Bool disableBuffering

    ** Directs the client to display a 'save as' dialog by setting the 'Content-Disposition' HTTP 
    ** response header. 
    ** 
    ** The 'Content-Type' HTTP response header is set to the MimeType derived from the fileName's 
    ** extension.
    ** 
    ** @see `HttpResponseHeaders.contentDisposition`
    abstract Void saveAsAttachment(Str fileName)
    
    ** Adds an event handler that gets called just before a session is committed.
    ** Use to make last minute changes to header values. 
    ** 
    ** Callbacks may be mutable, do not need to be cleaned up, but should be added at the start of *every* HTTP request. 
    abstract Void onCommit(|HttpResponse| fn)
    
    ** Clears all response headers and sets the status code back to '200'.
    ** 
    ** Called by BedSheet before processing an error handler.
    ** 
    ** Throws an Err should the response already be committed.
    @NoDoc  // advanced use only!
    abstract Void reset()

    ** Map of HTTP status codes to status messages.
    ** 
    ** See [WebRes.statusMsg]`web::WebRes.statusMsg`.
    static const Int:Str statusMsg := WebRes.statusMsg.rw.setAll([
        // @see http://fantom.org/forum/topic/2683#c1
        203 : "Non-Authoritative Information",
        308 : "Permanent Redirect",
        418 : "I'm a teapot",
    ])
}

internal const class HttpResponseImpl : HttpResponse {
    @Inject  const |->RequestState|     reqState
    new make(|This|in) { 
        in(this)
    } 
    override HttpResponseHeaders headers() {
        reqState().responseHeaders
    }
    override Bool disableGzip {
        get { reqState().disableGzip ?: false }
        set { reqState().disableGzip = it}
    }
    override Bool disableBuffering {
        get { reqState().disableBuffering ?: false }
        set { reqState().disableBuffering = it}
    }
    override Int statusCode {
        get { reqState().webRes.statusCode }
        set { reqState().webRes.statusCode = it }
    }   
    override Bool isCommitted() {
        reqState().webRes.isCommitted
    }
    override OutStream out() {
        reqState().responseBody
    }
    override Void saveAsAttachment(Str fileName) {
        headers.contentDisposition = "attachment; filename=" + WebUtil.toQuotedStr(fileName)
        headers.contentType = fileName.toUri.mimeType
    }
    override Void onCommit(|HttpResponse| fn) {
        reqState().addResponseCommitFn(fn)
    }
    override Void reset() {
        headers.clear
        statusCode = 200
        disableGzip = false
        disableBuffering = false
    }
    override Str toStr() {
        "$statusCode ${statusMsg[statusCode]}"
    }
}