001 // Copyright (c) 2001 Hursh Jain (http://www.mollypages.org)
002 // The Molly framework is freely distributable under the terms of an
003 // MIT-style license. For details, see the molly pages web site at:
004 // http://www.mollypages.org/. Use, modify, have fun !
005
006 package fc.web.servlet;
007
008 import javax.servlet.*;
009 import javax.servlet.http.*;
010 import java.io.*;
011 import java.util.*;
012 import fc.web.servlet.*;
013 import fc.io.*;
014
015 /**
016 Sends the specified file as a binary stream after setting the
017 ContenEncoding to be gzip. (the specified file should have
018 a .gz extension, this is mandated because it helps catch
019 user errors, for example forgetting to gzip the file).
020 <p>
021 <b>Note, this servlet does not Gzip data, it only serves data that
022 has already been gzip'ed.</b>
023 <p>
024 To use, add something like the following to web.xml:
025 <blockquote>
026 <pre>
027 <servlet>
028 <servlet-name>gzip</servlet-name>
029 <servlet-class>fc.web.servlet.GzipFileServlet</servlet-class>
030 </servlet>
031 ...
032 <!-- an example mapping, your taste may vary -->
033 <servlet-mapping>
034 <servlet-name>gzip</servlet-name>
035 <url-pattern>/gzip</url-pattern>
036 </servlet-mapping>
037 </pre>
038 </blockquote>
039 Then, in this example, one can say, in your HTML document:
040 <blockquote>
041 <pre>
042 <html>
043 <head>
044 <title>Untitled</title>
045 <font color=blue><script src="/gzip?js=myscript.js"></script></font>
046 </head>
047 <body>
048 hello
049 </body>
050 </html>
051 </pre>
052 </blockquote>
053 That will load the <i><code>myscript.js.gz</code></i>
054 file (and if myscript.js.gz is not present, a servlet exception
055 will be logged). Note, the ".gz" extension is added automatically
056 if not specified in the filename.
057 <p>
058 There are two modes in serving files. In both cases, the file
059 to be retrieved <u>must already exist as a gzip file on
060 the server</u>.
061 <ol>
062 <li>
063 Retrieve a arbitrary gzip'ed file from the server. There are 3
064 required parameters:
065 <dl>
066 <dt>file</dt>
067 <dd>the filename (if the specified name does not have a
068 .gz extension, ".gz" will be added automatically to the
069 specified name). [see <a href="#path"> path note below</a>]
070 </dd>
071 <dt>mimetype</dt>
072 <dd>The mimetype of the retrieved file (<i>after</i> it has been
073 gunzipped). This will commonly be <code>text/html</code> or
074 <code>text/javascript</code></dd>
075 <dt>encoding</dt>
076 <dd>The text encoding of the specified file. Commonly, it is
077 US-ASCII, ISO-8859-1 or UTF-8</dd>
078 </dl>
079 Example: <code>
080 /gzip?file=foo.html.gz&mimetype=text/html&encoding=ISO-8859-1
081 </code>
082 </li>
083
084 <li>
085 Retrieve a Javascript file (convenience method): This will automatically
086 set the content-type to be <i>text/javascript; charset=utf-8</i>
087 (so the mime or charset do not have to be specified). One need
088 only specify:
089 <dd>
090 <dt>js</dt>
091 <dl>path to the gzip'ed file. [see <a href="#path"> path note below</a>]</dl>
092 </dd>
093 </li>
094 </ol>
095 <hr>
096 <h3>Path name for the specified file</h3>
097
098 This path to the included file must be in absolute (start with a
099 '/') or relative form.
100 <p>
101 Absolute path names are like HTML absolute names and
102 start from the document root directory of the web server.
103 <p>
104 Relative names (relative to the invoking page) are <b>not</b>
105 supported by this servlet.
106
107 @author hursh jain
108 */
109 public final class GzipFileServlet extends javax.servlet.http.HttpServlet
110 {
111 /*
112 context page
113 /WEBAPP/ /bar/baz/foo.js
114 /WEBAPP/ /bar/img/zap.img
115
116 from foo.js ->
117 a) "../img/zap.img" [relative]
118 b) "/WEBAPP/bar/img/zap.img" [absolute #1]
119 c) "/bar/img/zap.img" [absolute #2, WEBAPP added by us]
120
121 we really want (a) and (b), (c) is not-intuitive.
122 (a) and (b) correspond to tradional relative, absolute paths
123 (where absolute is always from the web server document root]
124
125 (a) is too tricky, we don't know the path to the invoking
126 page in this servlet (so cannot do relative to that
127 unknown path).
128 */
129
130 File docroot;
131
132 String d_charset = "text/html; charset=ISO-8859-1";
133
134 public void init(ServletConfig conf) throws ServletException
135 {
136 super.init();
137 ServletContext context = conf.getServletContext();
138 ServletContext root_context = context.getContext("/");
139 docroot = new File(root_context.getRealPath("/"));
140 }
141
142 /**
143 Returns the specified gzip file. If the specified name
144 does not end with ".gz", ".gz" will automatically
145 be tacked on to the specified name (so files served by
146 this method must always end with ".gz"
147 */
148 public void doGet(final HttpServletRequest req, final HttpServletResponse res)
149 throws ServletException, IOException
150 {
151 final String file = req.getParameter("file");
152 if (file != null) {
153 sendFile(file, req, res);
154 return;
155 }
156
157 final String js = req.getParameter("js");
158 if (js != null) {
159 sendJSFile(js, req, res);
160 return;
161 }
162
163 throw new ServletException("Bad request. I need either the \"file\" or the \"jsfile\" parameter.");
164 }
165
166 void sendFile(final String file, final HttpServletRequest req, final HttpServletResponse res)
167 throws ServletException, IOException
168 {
169 final String mime = WebUtil.getRequiredParam(req, "mimetype");
170 final String encoding = WebUtil.getRequiredParam(req, "encoding");
171 final String content_type = mime + "; charset=" + encoding;
172 res.setContentType(content_type);
173 res.setHeader("Content-Encoding", "gzip");
174 ServletOutputStream out = res.getOutputStream();
175 push(out, file);
176 }
177
178 String js_content_type = "text/javascript; charset=utf-8";
179
180 void sendJSFile(final String file, final HttpServletRequest req, final HttpServletResponse res)
181 throws ServletException, IOException
182 {
183 res.setContentType(js_content_type);
184 res.setHeader("Content-Encoding", "gzip");
185 final ServletOutputStream out = res.getOutputStream();
186 push(out, file);
187 }
188
189 void push(final ServletOutputStream out, final String file)
190 throws IOException
191 {
192 final File f = makeFile(file);
193 final byte[] buf = IOUtil.fileToByteArray(f);
194 final BufferedOutputStream bout = new BufferedOutputStream(out);
195 bout.write(buf, 0, buf.length);
196 bout.flush();
197 bout.close();
198 }
199
200 File makeFile(String path) throws IOException
201 {
202 if (! path.endsWith(".gz")) {
203 path += ".gz";
204 }
205
206 File f = new File(docroot, path);
207
208 if (! f.exists() || ! f.isFile()) {
209 throw new IOException("\nERROR: file: [" + path + "] not found");
210 }
211 return f;
212 }
213 }