1 //
2 // Copyright (c) 2007, Brian Frank and Andy Frank
3 // Licensed under the Academic Free License version 3.0
4 //
5 // History:
6 // 27 Jun 07 Brian Frank Creation
7 //
8
9 using web
10
11 **
12 ** WispRes
13 **
14 class WispRes : WebRes
15 {
16
17 //////////////////////////////////////////////////////////////////////////
18 // Construction
19 //////////////////////////////////////////////////////////////////////////
20
21 new make(WispService service, OutStream o)
22 {
23 this.service = service
24 @out = WebOutStream.make(o)
25 headers.caseInsensitive = true
26 }
27
28 //////////////////////////////////////////////////////////////////////////
29 // WebRes
30 //////////////////////////////////////////////////////////////////////////
31
32 **
33 ** WispService.
34 **
35 override WispService service
36
37 **
38 ** Get or set the HTTP status code for this response. Status code
39 ** defaults to 200. If response has already been committed, throws Err.
40 ** If status code passed in is not recognized, throws Err.
41 **
42 override Int statusCode := 200
43 {
44 set
45 {
46 checkUncommitted
47 if (statusMsg[val] == null) throw Err.make("Unknown status code: $val")
48 @statusCode = val
49 }
50 }
51
52 **
53 ** Map of HTTP response headers. You must set all headers before
54 ** you access out() for the first time, which commits the response.
55 ** After the response is commited this map becomes read only.
56 **
override Str:Str headers := Str:Str[:]
58
59 **
60 ** Return true if this response has been commmited. A committed
61 ** response has written its response headers, and can no longer
62 ** modify its status code or headers. A response is committed the
63 ** first time that `out` is called.
64 **
65 override readonly Bool isCommitted := false
66
67 **
68 ** Return the WebOutStream for this response. The first time this
69 ** method is accessed the response is committed: all headers
70 ** currently set will be written to the stream, and can no longer
71 ** be modified.
72 **
73 override WebOutStream out
74 {
75 get
76 {
77 commit
78 return @out
79 }
80 }
81
82 **
83 ** Send a redirect response to the client using the specified status
84 ** code and url. If this response has already been committed this
85 ** method throws an Err.
86 **
87 override Void redirect(Int statusCode, Uri uri)
88 {
89 checkUncommitted
90 this.statusCode = statusCode
91 headers["Location"] = uri.toStr
92 headers["Content-Length"] = "0"
93 commit
94 }
95
96 **
97 ** Send an error response to client using the specified status and
98 ** HTML formatted message. If this response has already been committed
99 ** this method throws an Err. If the server has a preconfigured page
100 ** for this error code, it will trump the message passed in.
101 **
102 override Void sendError(Int statusCode, Str msg := null)
103 {
104 checkUncommitted
105 this.statusCode = statusCode
106 headers["Content-Type"] = "text/html"
107
108 out.docType
109 out.html
110 out.head.title("$statusCode ${statusMsg[statusCode]}").headEnd
111 out.body
112 out.h1(statusMsg[statusCode])
113 if (msg != null) out.w(msg).nl
114 out.bodyEnd
115 out.htmlEnd
116 }
117
118 //////////////////////////////////////////////////////////////////////////
119 // Impl
120 //////////////////////////////////////////////////////////////////////////
121
122 **
123 ** If the response has already been committed, then throw an Err.
124 **
125 Void checkUncommitted()
126 {
127 if (isCommitted) throw Err.make("WebRes already committed")
128 }
129
130 **
131 ** If we haven't committed yet, then write the response header.
132 **
133 Void commit()
134 {
135 if (isCommitted) return
136 isCommitted = true
137 headers = headers.ro
138 @out.w("HTTP/1.1 ").w(statusCode).w(" ").w(statusMsg[statusCode]).w("\r\n")
139 headers.each |Str v, Str k| { @out.w(k).w(": ").w(v).w("\r\n") }
140 @out.w("\r\n")
141 }
142
143 }