golf 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. data/.gitignore +10 -0
  2. data/Gemfile +6 -0
  3. data/Gemfile.lock +21 -0
  4. data/Rakefile +12 -0
  5. data/bin/golf +8 -0
  6. data/golf-java/README.markdown +41 -0
  7. data/golf-java/THANKS.markdown +14 -0
  8. data/golf-java/build/classes.zip +0 -0
  9. data/golf-java/build/com/thinkminimo/golf/GolfResource$MimeMapping$MimeMappingSingleton.class +0 -0
  10. data/golf-java/build/com/thinkminimo/golf/GolfResource$MimeMapping.class +0 -0
  11. data/golf-java/build/com/thinkminimo/golf/GolfResource.class +0 -0
  12. data/golf-java/build/com/thinkminimo/golf/GolfServlet$1.class +0 -0
  13. data/golf-java/build/com/thinkminimo/golf/GolfServlet$GolfContext.class +0 -0
  14. data/golf-java/build/com/thinkminimo/golf/GolfServlet$GolfParams.class +0 -0
  15. data/golf-java/build/com/thinkminimo/golf/GolfServlet$GolfSession.class +0 -0
  16. data/golf-java/build/com/thinkminimo/golf/GolfServlet$PermanentRedirectException.class +0 -0
  17. data/golf-java/build/com/thinkminimo/golf/GolfServlet$RedirectException.class +0 -0
  18. data/golf-java/build/com/thinkminimo/golf/GolfServlet$StoredJSVM.class +0 -0
  19. data/golf-java/build/com/thinkminimo/golf/GolfServlet.class +0 -0
  20. data/golf-java/build/com/thinkminimo/golf/JsonpTunnel.class +0 -0
  21. data/golf-java/build/com/thinkminimo/golf/Main$1.class +0 -0
  22. data/golf-java/build/com/thinkminimo/golf/Main$RingList.class +0 -0
  23. data/golf-java/build/com/thinkminimo/golf/Main.class +0 -0
  24. data/golf-java/build/com/thinkminimo/golf/ProxyServlet.class +0 -0
  25. data/golf-java/build/com/yahoo/platform/yui/compressor/CssCompressor.class +0 -0
  26. data/golf-java/build/com/yahoo/platform/yui/compressor/JarClassLoader.class +0 -0
  27. data/golf-java/build/com/yahoo/platform/yui/compressor/JavaScriptCompressor.class +0 -0
  28. data/golf-java/build/com/yahoo/platform/yui/compressor/JavaScriptIdentifier.class +0 -0
  29. data/golf-java/build/com/yahoo/platform/yui/compressor/JavaScriptToken.class +0 -0
  30. data/golf-java/build/com/yahoo/platform/yui/compressor/ScriptOrFnScope.class +0 -0
  31. data/golf-java/build/depends.zip +0 -0
  32. data/golf-java/build/golf +2 -0
  33. data/golf-java/build/org/json/JSONArray.class +0 -0
  34. data/golf-java/build/org/json/JSONException.class +0 -0
  35. data/golf-java/build/org/json/JSONObject$1.class +0 -0
  36. data/golf-java/build/org/json/JSONObject$Null.class +0 -0
  37. data/golf-java/build/org/json/JSONObject.class +0 -0
  38. data/golf-java/build/org/json/JSONString.class +0 -0
  39. data/golf-java/build/org/json/JSONStringer.class +0 -0
  40. data/golf-java/build/org/json/JSONTokener.class +0 -0
  41. data/golf-java/build/org/json/JSONWriter.class +0 -0
  42. data/golf-java/build/resources.zip +0 -0
  43. data/golf-java/build.xml +174 -0
  44. data/golf-java/dist/golf.zip +0 -0
  45. data/golf-java/lib/ant-launcher.jar +0 -0
  46. data/golf-java/lib/ant.jar +0 -0
  47. data/golf-java/lib/commons-codec-1.4.jar +0 -0
  48. data/golf-java/lib/commons-collections-3.2.1.jar +0 -0
  49. data/golf-java/lib/commons-fileupload-1.2.1.jar +0 -0
  50. data/golf-java/lib/commons-httpclient-3.1.jar +0 -0
  51. data/golf-java/lib/commons-io-1.4.jar +0 -0
  52. data/golf-java/lib/commons-lang-2.4.jar +0 -0
  53. data/golf-java/lib/commons-logging-1.1.1.jar +0 -0
  54. data/golf-java/lib/cssparser-0.9.5.jar +0 -0
  55. data/golf-java/lib/getopt-0.1-dev.jar +0 -0
  56. data/golf-java/lib/htmlunit-2.6.jar +0 -0
  57. data/golf-java/lib/htmlunit-core-js-2.6.jar +0 -0
  58. data/golf-java/lib/java-xmlbuilder-1.jar +0 -0
  59. data/golf-java/lib/jets3t-0.7.0.jar +0 -0
  60. data/golf-java/lib/jetty-6.1.15.jar +0 -0
  61. data/golf-java/lib/jetty-util-6.1.15.jar +0 -0
  62. data/golf-java/lib/log4j-1.2.15.jar +0 -0
  63. data/golf-java/lib/nekohtml-1.9.13.jar +0 -0
  64. data/golf-java/lib/sac-1.3.jar +0 -0
  65. data/golf-java/lib/serializer-2.7.1.jar +0 -0
  66. data/golf-java/lib/servlet-api-2.5-20081211.jar +0 -0
  67. data/golf-java/lib/xalan-2.7.1.jar +0 -0
  68. data/golf-java/lib/xercesImpl-2.9.1.jar +0 -0
  69. data/golf-java/lib/xml-apis-1.3.04.jar +0 -0
  70. data/golf-java/resources/app_error.html +60 -0
  71. data/golf-java/resources/error.html +29 -0
  72. data/golf-java/resources/forcebot.txt +0 -0
  73. data/golf-java/resources/forceclient.txt +0 -0
  74. data/golf-java/resources/forceproxy.txt +0 -0
  75. data/golf-java/resources/head.html +0 -0
  76. data/golf-java/resources/jquery.address.js +439 -0
  77. data/golf-java/resources/jquery.golf.js +945 -0
  78. data/golf-java/resources/jquery.js +4376 -0
  79. data/golf-java/resources/jsdetect.html +23 -0
  80. data/golf-java/resources/loading.gif +0 -0
  81. data/golf-java/resources/new.html +45 -0
  82. data/golf-java/resources/new.static.html +45 -0
  83. data/golf-java/resources/noscript.forceclient.html +11 -0
  84. data/golf-java/resources/noscript.html +18 -0
  85. data/golf-java/resources/noscript.static.html +15 -0
  86. data/golf-java/resources/project.xml +21 -0
  87. data/golf-java/resources/proxy_project.xml +11 -0
  88. data/golf-java/resources/proxy_web.xml +40 -0
  89. data/golf-java/resources/static_project.xml +37 -0
  90. data/golf-java/resources/version +1 -0
  91. data/golf-java/resources/web.xml +36 -0
  92. data/golf-java/resources/welcome2golf.html +50 -0
  93. data/golf-java/src/com/thinkminimo/golf/GolfResource.java +582 -0
  94. data/golf-java/src/com/thinkminimo/golf/GolfServlet.java +1055 -0
  95. data/golf-java/src/com/thinkminimo/golf/JsonpTunnel.java +86 -0
  96. data/golf-java/src/com/thinkminimo/golf/Main.java +1299 -0
  97. data/golf-java/src/com/thinkminimo/golf/ProxyServlet.java +577 -0
  98. data/golf-java/src/com/yahoo/platform/yui/compressor/CssCompressor.java +188 -0
  99. data/golf-java/src/com/yahoo/platform/yui/compressor/JarClassLoader.java +158 -0
  100. data/golf-java/src/com/yahoo/platform/yui/compressor/JavaScriptCompressor.java +1281 -0
  101. data/golf-java/src/com/yahoo/platform/yui/compressor/JavaScriptIdentifier.java +55 -0
  102. data/golf-java/src/com/yahoo/platform/yui/compressor/JavaScriptToken.java +28 -0
  103. data/golf-java/src/com/yahoo/platform/yui/compressor/ScriptOrFnScope.java +160 -0
  104. data/golf-java/src/org/json/JSONArray.java +934 -0
  105. data/golf-java/src/org/json/JSONException.java +27 -0
  106. data/golf-java/src/org/json/JSONObject.java +1550 -0
  107. data/golf-java/src/org/json/JSONString.java +18 -0
  108. data/golf-java/src/org/json/JSONStringer.java +78 -0
  109. data/golf-java/src/org/json/JSONTokener.java +422 -0
  110. data/golf-java/src/org/json/JSONWriter.java +323 -0
  111. data/golf-java/test/client/golftest/README.markdown +3 -0
  112. data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.css +114 -0
  113. data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.html +17 -0
  114. data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.js +81 -0
  115. data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.res/asc.gif +0 -0
  116. data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.res/bg.gif +0 -0
  117. data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.res/desc.gif +0 -0
  118. data/golf-java/test/client/golftest/components/com/thinkminimo/golf/test/Harness.res/test/test.html +1 -0
  119. data/golf-java/test/client/golftest/controller.js +131 -0
  120. data/golf-java/test/client/golftest/forceclient.txt +0 -0
  121. data/golf-java/test/client/golftest/forceproxy.txt +0 -0
  122. data/golf-java/test/client/golftest/head.html +1 -0
  123. data/golf-java/test/client/golftest/scripts/jquery.tablesort.js +75 -0
  124. data/golf-java/test/client/golftest/styles/style.css +18 -0
  125. data/golf-java/test/client/golftest/test/test.html +1 -0
  126. data/golf.gemspec +24 -0
  127. data/lib/golf/cli.rb +32 -0
  128. data/lib/golf/compiler.rb +12 -0
  129. data/lib/golf/rack.rb +17 -0
  130. data/lib/golf/version.rb +3 -0
  131. data/lib/golf.rb +6 -0
  132. data/template/Gemfile +5 -0
  133. data/template/config.ru +7 -0
  134. data/template/golfapp/components/Test/Test.html +22 -0
  135. data/template/golfapp/components/Test/Test.res/myfile.txt +1 -0
  136. data/template/golfapp/controller.js +10 -0
  137. data/template/golfapp/plugins/mylib.js +12 -0
  138. data/test/test_compiler.rb +10 -0
  139. data/test/test_helper.rb +6 -0
  140. data/test/test_rack.rb +17 -0
  141. metadata +208 -0
@@ -0,0 +1,577 @@
1
+ package com.thinkminimo.golf;
2
+
3
+ /*
4
+ * Based on ProxyServlet from Jason Edwards. Thanks, man!
5
+ * http://edwardstx.net/wiki/Wiki.jsp?page=HttpProxyServlet
6
+ */
7
+
8
+
9
+ import java.io.BufferedInputStream;
10
+ import java.io.File;
11
+ import java.io.IOException;
12
+ import java.io.InputStream;
13
+ import java.io.OutputStream;
14
+ import java.util.ArrayList;
15
+ import java.util.Enumeration;
16
+ import java.util.List;
17
+ import java.util.Map;
18
+
19
+ import javax.servlet.ServletConfig;
20
+ import javax.servlet.ServletException;
21
+ import javax.servlet.http.HttpServlet;
22
+ import javax.servlet.http.HttpServletRequest;
23
+ import javax.servlet.http.HttpServletResponse;
24
+
25
+ import org.apache.commons.fileupload.FileItem;
26
+ import org.apache.commons.fileupload.FileUploadException;
27
+ import org.apache.commons.fileupload.disk.DiskFileItemFactory;
28
+ import org.apache.commons.fileupload.servlet.ServletFileUpload;
29
+ import org.apache.commons.httpclient.Header;
30
+ import org.apache.commons.httpclient.HttpClient;
31
+ import org.apache.commons.httpclient.HttpMethod;
32
+ import org.apache.commons.httpclient.NameValuePair;
33
+ import org.apache.commons.httpclient.methods.GetMethod;
34
+ import org.apache.commons.httpclient.methods.PostMethod;
35
+ import org.apache.commons.httpclient.methods.PutMethod;
36
+ import org.apache.commons.httpclient.methods.DeleteMethod;
37
+ import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
38
+ import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource;
39
+ import org.apache.commons.httpclient.methods.multipart.FilePart;
40
+ import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
41
+ import org.apache.commons.httpclient.methods.multipart.Part;
42
+ import org.apache.commons.httpclient.methods.multipart.StringPart;
43
+
44
+
45
+ public class ProxyServlet extends HttpServlet {
46
+ /**
47
+ * Serialization UID.
48
+ */
49
+ private static final long serialVersionUID = 1L;
50
+ /**
51
+ * Key for redirect location header.
52
+ */
53
+ private static final String STRING_LOCATION_HEADER = "Location";
54
+ /**
55
+ * Key for content type header.
56
+ */
57
+ private static final String STRING_CONTENT_TYPE_HEADER_NAME = "Content-Type";
58
+
59
+ /**
60
+ * Key for content length header.
61
+ */
62
+ private static final String STRING_CONTENT_LENGTH_HEADER_NAME = "Content-Length";
63
+ /**
64
+ * Key for host header
65
+ */
66
+ private static final String STRING_HOST_HEADER_NAME = "Host";
67
+ /**
68
+ * The directory to use to temporarily store uploaded files
69
+ */
70
+ private static final File FILE_UPLOAD_TEMP_DIRECTORY = new File(System.getProperty("java.io.tmpdir"));
71
+
72
+ // Proxy host params
73
+ /**
74
+ * The host to which we are proxying requests
75
+ */
76
+ private String stringProxyHost;
77
+ /**
78
+ * The port on the proxy host to wihch we are proxying requests. Default value is 80.
79
+ */
80
+ private int intProxyPort = 80;
81
+ /**
82
+ * The (optional) path on the proxy host to wihch we are proxying requests. Default value is "".
83
+ */
84
+ private String stringProxyPath = "";
85
+ /**
86
+ * The (optional) extra query string parameters to add onto any request for proxy. Default value is "".
87
+ */
88
+ private String stringProxyQuery = "";
89
+ /**
90
+ * The maximum size for uploaded files in bytes. Default value is 5MB.
91
+ */
92
+ private int intMaxFileUploadSize = 5 * 1024 * 1024;
93
+
94
+ /**
95
+ * Initialize the <code>ProxyServlet</code>
96
+ * @param servletConfig The Servlet configuration passed in by the servlet conatiner
97
+ */
98
+ public void init(ServletConfig servletConfig) {
99
+ // Get the proxy host
100
+ String stringProxyHostNew = servletConfig.getInitParameter("proxyHost");
101
+ if(stringProxyHostNew == null || stringProxyHostNew.length() == 0) {
102
+ throw new IllegalArgumentException("Proxy host not set, please set init-param 'proxyHost' in web.xml");
103
+ }
104
+ this.setProxyHost(stringProxyHostNew);
105
+ // Get the proxy port if specified
106
+ String stringProxyPortNew = servletConfig.getInitParameter("proxyPort");
107
+ if(stringProxyPortNew != null && stringProxyPortNew.length() > 0) {
108
+ this.setProxyPort(Integer.parseInt(stringProxyPortNew));
109
+ }
110
+ // Get the proxy path if specified
111
+ String stringProxyPathNew = servletConfig.getInitParameter("proxyPath");
112
+ if(stringProxyPathNew != null && stringProxyPathNew.length() > 0) {
113
+ this.setProxyPath(stringProxyPathNew);
114
+ }
115
+ // Get the extra query params if specified
116
+ String stringProxyQueryNew = servletConfig.getInitParameter("proxyQuery");
117
+ if(stringProxyQueryNew != null && stringProxyQueryNew.length() > 0) {
118
+ this.setProxyQuery(stringProxyQueryNew);
119
+ }
120
+ // Get the maximum file upload size if specified
121
+ String stringMaxFileUploadSize = servletConfig.getInitParameter("maxFileUploadSize");
122
+ if(stringMaxFileUploadSize != null && stringMaxFileUploadSize.length() > 0) {
123
+ this.setMaxFileUploadSize(Integer.parseInt(stringMaxFileUploadSize));
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Performs an HTTP GET request
129
+ * @param httpServletRequest The {@link HttpServletRequest} object passed
130
+ * in by the servlet engine representing the
131
+ * client request to be proxied
132
+ * @param httpServletResponse The {@link HttpServletResponse} object by which
133
+ * we can send a proxied response to the client
134
+ */
135
+ public void doGet (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
136
+ throws IOException, ServletException {
137
+ // Create a GET request
138
+ GetMethod getMethodProxyRequest = new GetMethod(this.getProxyURL(httpServletRequest));
139
+ // Forward the request headers
140
+ setProxyRequestHeaders(httpServletRequest, getMethodProxyRequest);
141
+ // Execute the proxy request
142
+ this.executeProxyRequest(getMethodProxyRequest, httpServletRequest, httpServletResponse);
143
+ }
144
+
145
+ /**
146
+ * Performs an HTTP DELETE request
147
+ * @param httpServletRequest The {@link HttpServletRequest} object passed
148
+ * in by the servlet engine representing the
149
+ * client request to be proxied
150
+ * @param httpServletResponse The {@link HttpServletResponse} object by which
151
+ * we can send a proxied response to the client
152
+ */
153
+ public void doDelete (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
154
+ throws IOException, ServletException {
155
+ // Create a DELETE request
156
+ DeleteMethod delMethodProxyRequest = new DeleteMethod(this.getProxyURL(httpServletRequest));
157
+ // Forward the request headers
158
+ setProxyRequestHeaders(httpServletRequest, delMethodProxyRequest);
159
+ // Execute the proxy request
160
+ this.executeProxyRequest(delMethodProxyRequest, httpServletRequest, httpServletResponse);
161
+ }
162
+
163
+ /**
164
+ * Performs an HTTP PUT request
165
+ * @param httpServletRequest The {@link HttpServletRequest} object passed
166
+ * in by the servlet engine representing the
167
+ * client request to be proxied
168
+ * @param httpServletResponse The {@link HttpServletResponse} object by which
169
+ * we can send a proxied response to the client
170
+ */
171
+ public void doPut(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
172
+ throws IOException, ServletException {
173
+ // Create a standard PUT request
174
+ PutMethod putMethodProxyRequest = new PutMethod(this.getProxyURL(httpServletRequest));
175
+ // Forward the request headers
176
+ setProxyRequestHeaders(httpServletRequest, putMethodProxyRequest);
177
+ // Check if this is a mulitpart (file upload) PUT
178
+ if(ServletFileUpload.isMultipartContent(httpServletRequest)) {
179
+ this.handleMultipartPut(putMethodProxyRequest, httpServletRequest);
180
+ } else {
181
+ this.handleStandardPut(putMethodProxyRequest, httpServletRequest);
182
+ }
183
+ // Execute the proxy request
184
+ this.executeProxyRequest(putMethodProxyRequest, httpServletRequest, httpServletResponse);
185
+ }
186
+
187
+ /**
188
+ * Sets up the given {@link PutMethod} to send the same multipart PUT
189
+ * data as was sent in the given {@link HttpServletRequest}
190
+ * @param putMethodProxyRequest The {@link PutMethod} that we are
191
+ * configuring to send a multipart PUT request
192
+ * @param httpServletRequest The {@link HttpServletRequest} that contains
193
+ * the mutlipart PUT data to be sent via the {@link PutMethod}
194
+ */
195
+ @SuppressWarnings("unchecked")
196
+ private void handleMultipartPut(PutMethod putMethodProxyRequest, HttpServletRequest httpServletRequest)
197
+ throws ServletException {
198
+ // Create a factory for disk-based file items
199
+ DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
200
+ // Set factory constraints
201
+ diskFileItemFactory.setSizeThreshold(this.getMaxFileUploadSize());
202
+ diskFileItemFactory.setRepository(FILE_UPLOAD_TEMP_DIRECTORY);
203
+ // Create a new file upload handler
204
+ ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
205
+ // Parse the request
206
+ try {
207
+ // Get the multipart items as a list
208
+ List<FileItem> listFileItems = (List<FileItem>) servletFileUpload.parseRequest(httpServletRequest);
209
+ // Create a list to hold all of the parts
210
+ List<Part> listParts = new ArrayList<Part>();
211
+ // Iterate the multipart items list
212
+ for(FileItem fileItemCurrent : listFileItems) {
213
+ // If the current item is a form field, then create a string part
214
+ if (fileItemCurrent.isFormField()) {
215
+ StringPart stringPart = new StringPart(
216
+ fileItemCurrent.getFieldName(), // The field name
217
+ fileItemCurrent.getString() // The field value
218
+ );
219
+ // Add the part to the list
220
+ listParts.add(stringPart);
221
+ } else {
222
+ // The item is a file upload, so we create a FilePart
223
+ FilePart filePart = new FilePart(
224
+ fileItemCurrent.getFieldName(), // The field name
225
+ new ByteArrayPartSource(
226
+ fileItemCurrent.getName(), // The uploaded file name
227
+ fileItemCurrent.get() // The uploaded file contents
228
+ )
229
+ );
230
+ // Add the part to the list
231
+ listParts.add(filePart);
232
+ }
233
+ }
234
+ MultipartRequestEntity multipartRequestEntity = new MultipartRequestEntity(
235
+ listParts.toArray(new Part[] {}),
236
+ putMethodProxyRequest.getParams()
237
+ );
238
+ putMethodProxyRequest.setRequestEntity(multipartRequestEntity);
239
+ // The current content-type header (received from the client) IS of
240
+ // type "multipart/form-data", but the content-type header also
241
+ // contains the chunk boundary string of the chunks. Currently, this
242
+ // header is using the boundary of the client request, since we
243
+ // blindly copied all headers from the client request to the proxy
244
+ // request. However, we are creating a new request with a new chunk
245
+ // boundary string, so it is necessary that we re-set the
246
+ // content-type string to reflect the new chunk boundary string
247
+ putMethodProxyRequest.setRequestHeader(STRING_CONTENT_TYPE_HEADER_NAME, multipartRequestEntity.getContentType());
248
+ } catch (FileUploadException fileUploadException) {
249
+ throw new ServletException(fileUploadException);
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Sets up the given {@link PutMethod} to send the same standard PUT
255
+ * data as was sent in the given {@link HttpServletRequest}
256
+ * @param putMethodProxyRequest The {@link PutMethod} that we are
257
+ * configuring to send a standard PUT request
258
+ * @param httpServletRequest The {@link HttpServletRequest} that contains
259
+ * the PUT data to be sent via the {@link PutMethod}
260
+ */
261
+ @SuppressWarnings("unchecked")
262
+ private void handleStandardPut(PutMethod putMethodProxyRequest, HttpServletRequest httpServletRequest)
263
+ throws IOException {
264
+ // Get the client PUT data as a Map
265
+ Map<String, String[]> mapPutParameters = (Map<String,String[]>) httpServletRequest.getParameterMap();
266
+ // Create a List to hold the NameValuePairs to be passed to the PutMethod
267
+ List<NameValuePair> listNameValuePairs = new ArrayList<NameValuePair>();
268
+ // Iterate the parameter names
269
+ for(String stringParameterName : mapPutParameters.keySet()) {
270
+ // Iterate the values for each parameter name
271
+ String[] stringArrayParameterValues = mapPutParameters.get(stringParameterName);
272
+ for(String stringParamterValue : stringArrayParameterValues) {
273
+ // Create a NameValuePair and store in list
274
+ NameValuePair nameValuePair = new NameValuePair(stringParameterName, stringParamterValue);
275
+ listNameValuePairs.add(nameValuePair);
276
+ }
277
+ }
278
+ // Set the proxy request PUT data
279
+ putMethodProxyRequest.setRequestEntity(new InputStreamRequestEntity(httpServletRequest.getInputStream()));
280
+ }
281
+
282
+ /**
283
+ * Performs an HTTP POST request
284
+ * @param httpServletRequest The {@link HttpServletRequest} object passed
285
+ * in by the servlet engine representing the
286
+ * client request to be proxied
287
+ * @param httpServletResponse The {@link HttpServletResponse} object by which
288
+ * we can send a proxied response to the client
289
+ */
290
+ public void doPost(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
291
+ throws IOException, ServletException {
292
+ // Create a standard POST request
293
+ PostMethod postMethodProxyRequest = new PostMethod(this.getProxyURL(httpServletRequest));
294
+ // Forward the request headers
295
+ setProxyRequestHeaders(httpServletRequest, postMethodProxyRequest);
296
+ // Check if this is a mulitpart (file upload) POST
297
+ if(ServletFileUpload.isMultipartContent(httpServletRequest)) {
298
+ this.handleMultipartPost(postMethodProxyRequest, httpServletRequest);
299
+ } else {
300
+ this.handleStandardPost(postMethodProxyRequest, httpServletRequest);
301
+ }
302
+ // Execute the proxy request
303
+ this.executeProxyRequest(postMethodProxyRequest, httpServletRequest, httpServletResponse);
304
+ }
305
+
306
+ /**
307
+ * Sets up the given {@link PostMethod} to send the same multipart POST
308
+ * data as was sent in the given {@link HttpServletRequest}
309
+ * @param postMethodProxyRequest The {@link PostMethod} that we are
310
+ * configuring to send a multipart POST request
311
+ * @param httpServletRequest The {@link HttpServletRequest} that contains
312
+ * the mutlipart POST data to be sent via the {@link PostMethod}
313
+ */
314
+ @SuppressWarnings("unchecked")
315
+ private void handleMultipartPost(PostMethod postMethodProxyRequest, HttpServletRequest httpServletRequest)
316
+ throws ServletException {
317
+ // Create a factory for disk-based file items
318
+ DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
319
+ // Set factory constraints
320
+ diskFileItemFactory.setSizeThreshold(this.getMaxFileUploadSize());
321
+ diskFileItemFactory.setRepository(FILE_UPLOAD_TEMP_DIRECTORY);
322
+ // Create a new file upload handler
323
+ ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
324
+ // Parse the request
325
+ try {
326
+ // Get the multipart items as a list
327
+ List<FileItem> listFileItems = (List<FileItem>) servletFileUpload.parseRequest(httpServletRequest);
328
+ // Create a list to hold all of the parts
329
+ List<Part> listParts = new ArrayList<Part>();
330
+ // Iterate the multipart items list
331
+ for(FileItem fileItemCurrent : listFileItems) {
332
+ // If the current item is a form field, then create a string part
333
+ if (fileItemCurrent.isFormField()) {
334
+ StringPart stringPart = new StringPart(
335
+ fileItemCurrent.getFieldName(), // The field name
336
+ fileItemCurrent.getString() // The field value
337
+ );
338
+ // Add the part to the list
339
+ listParts.add(stringPart);
340
+ } else {
341
+ // The item is a file upload, so we create a FilePart
342
+ FilePart filePart = new FilePart(
343
+ fileItemCurrent.getFieldName(), // The field name
344
+ new ByteArrayPartSource(
345
+ fileItemCurrent.getName(), // The uploaded file name
346
+ fileItemCurrent.get() // The uploaded file contents
347
+ )
348
+ );
349
+ // Add the part to the list
350
+ listParts.add(filePart);
351
+ }
352
+ }
353
+ MultipartRequestEntity multipartRequestEntity = new MultipartRequestEntity(
354
+ listParts.toArray(new Part[] {}),
355
+ postMethodProxyRequest.getParams()
356
+ );
357
+ postMethodProxyRequest.setRequestEntity(multipartRequestEntity);
358
+ // The current content-type header (received from the client) IS of
359
+ // type "multipart/form-data", but the content-type header also
360
+ // contains the chunk boundary string of the chunks. Currently, this
361
+ // header is using the boundary of the client request, since we
362
+ // blindly copied all headers from the client request to the proxy
363
+ // request. However, we are creating a new request with a new chunk
364
+ // boundary string, so it is necessary that we re-set the
365
+ // content-type string to reflect the new chunk boundary string
366
+ postMethodProxyRequest.setRequestHeader(STRING_CONTENT_TYPE_HEADER_NAME, multipartRequestEntity.getContentType());
367
+ } catch (FileUploadException fileUploadException) {
368
+ throw new ServletException(fileUploadException);
369
+ }
370
+ }
371
+
372
+ /**
373
+ * Sets up the given {@link PostMethod} to send the same standard POST
374
+ * data as was sent in the given {@link HttpServletRequest}
375
+ * @param postMethodProxyRequest The {@link PostMethod} that we are
376
+ * configuring to send a standard POST request
377
+ * @param httpServletRequest The {@link HttpServletRequest} that contains
378
+ * the POST data to be sent via the {@link PostMethod}
379
+ */
380
+ @SuppressWarnings("unchecked")
381
+ private void handleStandardPost(PostMethod postMethodProxyRequest, HttpServletRequest httpServletRequest) throws IOException {
382
+ /*
383
+ // Get the client POST data as a Map
384
+ Map<String, String[]> mapPostParameters = (Map<String,String[]>) httpServletRequest.getParameterMap();
385
+ // Create a List to hold the NameValuePairs to be passed to the PostMethod
386
+ List<NameValuePair> listNameValuePairs = new ArrayList<NameValuePair>();
387
+ // Iterate the parameter names
388
+ for(String stringParameterName : mapPostParameters.keySet()) {
389
+ // Iterate the values for each parameter name
390
+ String[] stringArrayParameterValues = mapPostParameters.get(stringParameterName);
391
+ for(String stringParamterValue : stringArrayParameterValues) {
392
+ // Create a NameValuePair and store in list
393
+ NameValuePair nameValuePair = new NameValuePair(stringParameterName, stringParamterValue);
394
+ listNameValuePairs.add(nameValuePair);
395
+ }
396
+ }
397
+ // Set the proxy request POST data
398
+ postMethodProxyRequest.setRequestBody(listNameValuePairs.toArray(new NameValuePair[] { }));
399
+ */
400
+ postMethodProxyRequest.setRequestEntity(new InputStreamRequestEntity(httpServletRequest.getInputStream()));
401
+ }
402
+
403
+ /**
404
+ * Executes the {@link HttpMethod} passed in and sends the proxy response
405
+ * back to the client via the given {@link HttpServletResponse}
406
+ * @param httpMethodProxyRequest An object representing the proxy request to be made
407
+ * @param httpServletResponse An object by which we can send the proxied
408
+ * response back to the client
409
+ * @throws IOException Can be thrown by the {@link HttpClient}.executeMethod
410
+ * @throws ServletException Can be thrown to indicate that another error has occurred
411
+ */
412
+ private void executeProxyRequest(
413
+ HttpMethod httpMethodProxyRequest,
414
+ HttpServletRequest httpServletRequest,
415
+ HttpServletResponse httpServletResponse)
416
+ throws IOException, ServletException {
417
+ // Create a default HttpClient
418
+ HttpClient httpClient = new HttpClient();
419
+ httpMethodProxyRequest.setFollowRedirects(false);
420
+ // Execute the request
421
+ int intProxyResponseCode = httpClient.executeMethod(httpMethodProxyRequest);
422
+
423
+ // Check if the proxy response is a redirect
424
+ // The following code is adapted from org.tigris.noodle.filters.CheckForRedirect
425
+ // Hooray for open source software
426
+ if(intProxyResponseCode >= HttpServletResponse.SC_MULTIPLE_CHOICES /* 300 */
427
+ && intProxyResponseCode < HttpServletResponse.SC_NOT_MODIFIED /* 304 */) {
428
+ String stringStatusCode = Integer.toString(intProxyResponseCode);
429
+ String stringLocation = httpMethodProxyRequest.getResponseHeader(STRING_LOCATION_HEADER).getValue();
430
+ if(stringLocation == null) {
431
+ throw new ServletException("Recieved status code: " + stringStatusCode
432
+ + " but no " + STRING_LOCATION_HEADER + " header was found in the response");
433
+ }
434
+ // Modify the redirect to go to this proxy servlet rather that the proxied host
435
+ String stringMyHostName = httpServletRequest.getServerName();
436
+ if(httpServletRequest.getServerPort() != 80) {
437
+ stringMyHostName += ":" + httpServletRequest.getServerPort();
438
+ }
439
+ stringMyHostName += httpServletRequest.getContextPath();
440
+ httpServletResponse.sendRedirect(stringLocation.replace(getProxyHostAndPort() + this.getProxyPath(), stringMyHostName));
441
+ return;
442
+ } else if(intProxyResponseCode == HttpServletResponse.SC_NOT_MODIFIED) {
443
+ // 304 needs special handling. See:
444
+ // http://www.ics.uci.edu/pub/ietf/http/rfc1945.html#Code304
445
+ // We get a 304 whenever passed an 'If-Modified-Since'
446
+ // header and the data on disk has not changed; server
447
+ // responds w/ a 304 saying I'm not going to send the
448
+ // body because the file has not changed.
449
+ httpServletResponse.setIntHeader(STRING_CONTENT_LENGTH_HEADER_NAME, 0);
450
+ httpServletResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
451
+ return;
452
+ }
453
+
454
+ // Pass the response code back to the client
455
+ httpServletResponse.setStatus(intProxyResponseCode);
456
+
457
+ // Pass response headers back to the client
458
+ Header[] headerArrayResponse = httpMethodProxyRequest.getResponseHeaders();
459
+ for(Header header : headerArrayResponse) {
460
+ httpServletResponse.setHeader(header.getName(), header.getValue());
461
+ }
462
+
463
+ // Send the content to the client
464
+ InputStream inputStreamProxyResponse = httpMethodProxyRequest.getResponseBodyAsStream();
465
+ BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStreamProxyResponse);
466
+ OutputStream outputStreamClientResponse = httpServletResponse.getOutputStream();
467
+ int intNextByte;
468
+ while ( ( intNextByte = bufferedInputStream.read() ) != -1 ) {
469
+ outputStreamClientResponse.write(intNextByte);
470
+ }
471
+ }
472
+
473
+ public String getServletInfo() {
474
+ return "Jason's Proxy Servlet";
475
+ }
476
+
477
+ /**
478
+ * Retreives all of the headers from the servlet request and sets them on
479
+ * the proxy request
480
+ *
481
+ * @param httpServletRequest The request object representing the client's
482
+ * request to the servlet engine
483
+ * @param httpMethodProxyRequest The request that we are about to send to
484
+ * the proxy host
485
+ */
486
+ @SuppressWarnings("unchecked")
487
+ private void setProxyRequestHeaders(HttpServletRequest httpServletRequest, HttpMethod httpMethodProxyRequest) {
488
+ // Get an Enumeration of all of the header names sent by the client
489
+ Enumeration enumerationOfHeaderNames = httpServletRequest.getHeaderNames();
490
+ while(enumerationOfHeaderNames.hasMoreElements()) {
491
+ String stringHeaderName = (String) enumerationOfHeaderNames.nextElement();
492
+ if(stringHeaderName.equalsIgnoreCase(STRING_CONTENT_LENGTH_HEADER_NAME))
493
+ continue;
494
+ // As per the Java Servlet API 2.5 documentation:
495
+ // Some headers, such as Accept-Language can be sent by clients
496
+ // as several headers each with a different value rather than
497
+ // sending the header as a comma separated list.
498
+ // Thus, we get an Enumeration of the header values sent by the client
499
+ Enumeration enumerationOfHeaderValues = httpServletRequest.getHeaders(stringHeaderName);
500
+ while(enumerationOfHeaderValues.hasMoreElements()) {
501
+ String stringHeaderValue = (String) enumerationOfHeaderValues.nextElement();
502
+ // In case the proxy host is running multiple virtual servers,
503
+ // rewrite the Host header to ensure that we get content from
504
+ // the correct virtual server
505
+ if(stringHeaderName.equalsIgnoreCase(STRING_HOST_HEADER_NAME)){
506
+ stringHeaderValue = getProxyHostAndPort();
507
+ }
508
+ Header header = new Header(stringHeaderName, stringHeaderValue);
509
+ // Set the same header on the proxy request
510
+ httpMethodProxyRequest.setRequestHeader(header);
511
+ }
512
+ }
513
+ }
514
+
515
+ // Accessors
516
+ private String getProxyURL(HttpServletRequest httpServletRequest) {
517
+ // Set the protocol to HTTP
518
+ String stringProxyURL = "http://" + this.getProxyHostAndPort();
519
+ // Check if we are proxying to a path other that the document root
520
+ if(!this.getProxyPath().equalsIgnoreCase("")){
521
+ stringProxyURL += this.getProxyPath();
522
+ }
523
+ // Handle the path given to the servlet
524
+ stringProxyURL += httpServletRequest.getRequestURI()
525
+ .substring(httpServletRequest.getContextPath().length());
526
+ System.err.println("PROXY: '"+stringProxyURL+"'");
527
+ // Handle the query string
528
+ String query1 = (httpServletRequest.getQueryString() == null ? "" : httpServletRequest.getQueryString());
529
+ String query2 = this.getProxyQuery();
530
+ if (query1.length() > 0 && query2.length() > 0)
531
+ stringProxyURL += "?" + query1 + "&" + query2;
532
+ else if (query1.length() > 0)
533
+ stringProxyURL += "?" + query1;
534
+ else if (query2.length() > 0)
535
+ stringProxyURL += "?" + query2;
536
+ return stringProxyURL;
537
+ }
538
+
539
+ private String getProxyHostAndPort() {
540
+ if(this.getProxyPort() == 80) {
541
+ return this.getProxyHost();
542
+ } else {
543
+ return this.getProxyHost() + ":" + this.getProxyPort();
544
+ }
545
+ }
546
+
547
+ private String getProxyHost() {
548
+ return this.stringProxyHost;
549
+ }
550
+ private void setProxyHost(String stringProxyHostNew) {
551
+ this.stringProxyHost = stringProxyHostNew;
552
+ }
553
+ private int getProxyPort() {
554
+ return this.intProxyPort;
555
+ }
556
+ private void setProxyPort(int intProxyPortNew) {
557
+ this.intProxyPort = intProxyPortNew;
558
+ }
559
+ private String getProxyPath() {
560
+ return this.stringProxyPath;
561
+ }
562
+ private void setProxyPath(String stringProxyPathNew) {
563
+ this.stringProxyPath = stringProxyPathNew;
564
+ }
565
+ private String getProxyQuery() {
566
+ return this.stringProxyQuery;
567
+ }
568
+ private void setProxyQuery(String stringProxyQueryNew) {
569
+ this.stringProxyQuery = stringProxyQueryNew;
570
+ }
571
+ private int getMaxFileUploadSize() {
572
+ return this.intMaxFileUploadSize;
573
+ }
574
+ private void setMaxFileUploadSize(int intMaxFileUploadSizeNew) {
575
+ this.intMaxFileUploadSize = intMaxFileUploadSizeNew;
576
+ }
577
+ }