source: josm/trunk/src/org/openstreetmap/josm/io/remotecontrol/RequestProcessor.java@ 6070

Last change on this file since 6070 was 6070, checked in by stoecker, 11 years ago

see #8853 remove tabs, trailing spaces, windows line ends, strange characters

  • Property svn:eol-style set to native
File size: 13.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io.remotecontrol;
3
4import java.io.BufferedOutputStream;
5import java.io.BufferedReader;
6import java.io.IOException;
7import java.io.InputStreamReader;
8import java.io.OutputStream;
9import java.io.OutputStreamWriter;
10import java.io.Writer;
11import java.net.Socket;
12import java.util.Arrays;
13import java.util.Date;
14import java.util.HashMap;
15import java.util.Map;
16import java.util.Map.Entry;
17import java.util.StringTokenizer;
18import java.util.TreeMap;
19import java.util.regex.Matcher;
20import java.util.regex.Pattern;
21
22import org.openstreetmap.josm.io.remotecontrol.handler.AddNodeHandler;
23import org.openstreetmap.josm.io.remotecontrol.handler.AddWayHandler;
24import org.openstreetmap.josm.io.remotecontrol.handler.ImageryHandler;
25import org.openstreetmap.josm.io.remotecontrol.handler.ImportHandler;
26import org.openstreetmap.josm.io.remotecontrol.handler.LoadAndZoomHandler;
27import org.openstreetmap.josm.io.remotecontrol.handler.LoadObjectHandler;
28import org.openstreetmap.josm.io.remotecontrol.handler.OpenFileHandler;
29import org.openstreetmap.josm.io.remotecontrol.handler.RequestHandler;
30import org.openstreetmap.josm.io.remotecontrol.handler.RequestHandler.RequestHandlerBadRequestException;
31import org.openstreetmap.josm.io.remotecontrol.handler.RequestHandler.RequestHandlerErrorException;
32import org.openstreetmap.josm.io.remotecontrol.handler.RequestHandler.RequestHandlerForbiddenException;
33import org.openstreetmap.josm.io.remotecontrol.handler.VersionHandler;
34import org.openstreetmap.josm.tools.Utils;
35
36/**
37 * Processes HTTP "remote control" requests.
38 */
39public class RequestProcessor extends Thread {
40 /**
41 * RemoteControl protocol version. Change minor number for compatible
42 * interface extensions. Change major number in case of incompatible
43 * changes.
44 */
45 public static final String PROTOCOLVERSION = "{\"protocolversion\": {\"major\": " +
46 RemoteControl.protocolMajorVersion + ", \"minor\": " +
47 RemoteControl.protocolMinorVersion +
48 "}, \"application\": \"JOSM RemoteControl\"}";
49
50 /** The socket this processor listens on */
51 private Socket request;
52
53 /**
54 * Collection of request handlers.
55 * Will be initialized with default handlers here. Other plug-ins
56 * can extend this list by using @see addRequestHandler
57 */
58 private static Map<String, Class<? extends RequestHandler>> handlers = new TreeMap<String, Class<? extends RequestHandler>>();
59
60 /**
61 * Constructor
62 *
63 * @param request A socket to read the request.
64 */
65 public RequestProcessor(Socket request) {
66 super("RemoteControl request processor");
67 this.setDaemon(true);
68 this.request = request;
69 }
70
71 /**
72 * Spawns a new thread for the request
73 */
74 public static void processRequest(Socket request) {
75 RequestProcessor processor = new RequestProcessor(request);
76 processor.start();
77 }
78
79 /**
80 * Add external request handler. Can be used by other plug-ins that
81 * want to use remote control.
82 *
83 * @param command The command to handle.
84 * @param handler The additional request handler.
85 */
86 static void addRequestHandlerClass(String command,
87 Class<? extends RequestHandler> handler) {
88 addRequestHandlerClass(command, handler, false);
89 }
90
91 /**
92 * Add external request handler. Message can be suppressed.
93 * (for internal use)
94 *
95 * @param command The command to handle.
96 * @param handler The additional request handler.
97 * @param silent Don't show message if true.
98 */
99 private static void addRequestHandlerClass(String command,
100 Class<? extends RequestHandler> handler, boolean silent) {
101 if(command.charAt(0) == '/')
102 {
103 command = command.substring(1);
104 }
105 String commandWithSlash = "/" + command;
106 if (handlers.get(commandWithSlash) != null) {
107 System.out.println("RemoteControl: ignoring duplicate command " + command
108 + " with handler " + handler.getName());
109 } else {
110 if (!silent) {
111 System.out.println("RemoteControl: adding command \"" +
112 command + "\" (handled by " + handler.getSimpleName() + ")");
113 }
114 handlers.put(commandWithSlash, handler);
115 }
116 }
117
118 /** Add default request handlers */
119 static {
120 addRequestHandlerClass(LoadAndZoomHandler.command, LoadAndZoomHandler.class, true);
121 addRequestHandlerClass(LoadAndZoomHandler.command2, LoadAndZoomHandler.class, true);
122 addRequestHandlerClass(ImageryHandler.command, ImageryHandler.class, true);
123 addRequestHandlerClass(AddNodeHandler.command, AddNodeHandler.class, true);
124 addRequestHandlerClass(AddWayHandler.command, AddWayHandler.class, true);
125 addRequestHandlerClass(ImportHandler.command, ImportHandler.class, true);
126 addRequestHandlerClass(VersionHandler.command, VersionHandler.class, true);
127 addRequestHandlerClass(LoadObjectHandler.command, LoadObjectHandler.class, true);
128 addRequestHandlerClass(OpenFileHandler.command, OpenFileHandler.class, true);
129 }
130
131 /**
132 * The work is done here.
133 */
134 public void run() {
135 Writer out = null;
136 try {
137 OutputStream raw = new BufferedOutputStream(request.getOutputStream());
138 out = new OutputStreamWriter(raw);
139 BufferedReader in = new BufferedReader(new InputStreamReader(request.getInputStream(), "ASCII"));
140
141 String get = in.readLine();
142 if (get == null) {
143 sendError(out);
144 return;
145 }
146 System.out.println("RemoteControl received: " + get);
147
148 StringTokenizer st = new StringTokenizer(get);
149 if (!st.hasMoreTokens()) {
150 sendError(out);
151 return;
152 }
153 String method = st.nextToken();
154 if (!st.hasMoreTokens()) {
155 sendError(out);
156 return;
157 }
158 String url = st.nextToken();
159
160 if (!method.equals("GET")) {
161 sendNotImplemented(out);
162 return;
163 }
164
165 int questionPos = url.indexOf('?');
166
167 String command = questionPos < 0 ? url : url.substring(0, questionPos);
168
169 Map <String,String> headers = new HashMap<String, String>();
170 int k=0, MAX_HEADERS=20;
171 while (k<MAX_HEADERS) {
172 get=in.readLine();
173 if (get==null) break;
174 k++;
175 String h[] = get.split(": ", 2);
176 if (h.length==2) {
177 headers.put(h[0], h[1]);
178 } else break;
179 }
180
181 // Who sent the request: trying our best to detect
182 // not from localhost => sender = IP
183 // from localhost: sender = referer header, if exists
184 String sender = null;
185
186 if (!request.getInetAddress().isLoopbackAddress()) {
187 sender = request.getInetAddress().getHostAddress();
188 } else {
189 String ref = headers.get("Referer");
190 Pattern r = Pattern.compile("(https?://)?([^/]*)");
191 if (ref!=null) {
192 Matcher m = r.matcher(ref);
193 if (m.find()) {
194 sender = m.group(2);
195 }
196 }
197 if (sender == null) {
198 sender = "localhost";
199 }
200 }
201
202 // find a handler for this command
203 Class<? extends RequestHandler> handlerClass = handlers.get(command);
204 if (handlerClass == null) {
205 // no handler found
206 StringBuilder usage = new StringBuilder(1024);
207 for (Entry<String, Class<? extends RequestHandler>> handler : handlers.entrySet()) {
208 String[] mandatory = handler.getValue().newInstance().getMandatoryParams();
209 usage.append("<li>");
210 usage.append(handler.getKey());
211 if (mandatory != null) {
212 usage.append("<br/>mandatory parameter: ").append(Utils.join(", ", Arrays.asList(mandatory)));
213 }
214 usage.append("</li>");
215 }
216 String websiteDoc = "http://josm.openstreetmap.de/wiki/Help/Preferences/RemoteControl";
217 String help = "No command specified! The following commands are available:<ul>"
218 + usage.toString()
219 + "</ul>" + "See <a href=\""+websiteDoc+"\">"+websiteDoc+"</a> for complete documentation.";
220 sendBadRequest(out, help);
221 } else {
222 // create handler object
223 RequestHandler handler = handlerClass.newInstance();
224 try {
225 handler.setCommand(command);
226 handler.setUrl(url);
227 handler.setSender(sender);
228 handler.handle();
229 sendHeader(out, "200 OK", handler.getContentType(), false);
230 out.write("Content-length: " + handler.getContent().length()
231 + "\r\n");
232 out.write("\r\n");
233 out.write(handler.getContent());
234 out.flush();
235 } catch (RequestHandlerErrorException ex) {
236 sendError(out);
237 } catch (RequestHandlerBadRequestException ex) {
238 sendBadRequest(out, ex.getMessage());
239 } catch (RequestHandlerForbiddenException ex) {
240 sendForbidden(out, ex.getMessage());
241 }
242 }
243
244 } catch (IOException ioe) {
245 } catch (Exception e) {
246 e.printStackTrace();
247 try {
248 sendError(out);
249 } catch (IOException e1) {
250 }
251 } finally {
252 try {
253 request.close();
254 } catch (IOException e) {
255 }
256 }
257 }
258
259 /**
260 * Sends a 500 error: server error
261 *
262 * @param out
263 * The writer where the error is written
264 * @throws IOException
265 * If the error can not be written
266 */
267 private void sendError(Writer out) throws IOException {
268 sendHeader(out, "500 Internal Server Error", "text/html", true);
269 out.write("<HTML>\r\n");
270 out.write("<HEAD><TITLE>Internal Error</TITLE>\r\n");
271 out.write("</HEAD>\r\n");
272 out.write("<BODY>");
273 out.write("<H1>HTTP Error 500: Internal Server Error</h2>\r\n");
274 out.write("</BODY></HTML>\r\n");
275 out.flush();
276 }
277
278 /**
279 * Sends a 501 error: not implemented
280 *
281 * @param out
282 * The writer where the error is written
283 * @throws IOException
284 * If the error can not be written
285 */
286 private void sendNotImplemented(Writer out) throws IOException {
287 sendHeader(out, "501 Not Implemented", "text/html", true);
288 out.write("<HTML>\r\n");
289 out.write("<HEAD><TITLE>Not Implemented</TITLE>\r\n");
290 out.write("</HEAD>\r\n");
291 out.write("<BODY>");
292 out.write("<H1>HTTP Error 501: Not Implemented</h2>\r\n");
293 out.write("</BODY></HTML>\r\n");
294 out.flush();
295 }
296
297 /**
298 * Sends a 403 error: forbidden
299 *
300 * @param out
301 * The writer where the error is written
302 * @throws IOException
303 * If the error can not be written
304 */
305 private void sendForbidden(Writer out, String help) throws IOException {
306 sendHeader(out, "403 Forbidden", "text/html", true);
307 out.write("<HTML>\r\n");
308 out.write("<HEAD><TITLE>Forbidden</TITLE>\r\n");
309 out.write("</HEAD>\r\n");
310 out.write("<BODY>");
311 out.write("<H1>HTTP Error 403: Forbidden</h2>\r\n");
312 if (help != null) {
313 out.write(help);
314 }
315 out.write("</BODY></HTML>\r\n");
316 out.flush();
317 }
318
319 /**
320 * Sends a 403 error: forbidden
321 *
322 * @param out
323 * The writer where the error is written
324 * @throws IOException
325 * If the error can not be written
326 */
327 private void sendBadRequest(Writer out, String help) throws IOException {
328 sendHeader(out, "400 Bad Request", "text/html", true);
329 out.write("<HTML>\r\n");
330 out.write("<HEAD><TITLE>Bad Request</TITLE>\r\n");
331 out.write("</HEAD>\r\n");
332 out.write("<BODY>");
333 out.write("<H1>HTTP Error 400: Bad Request</h2>\r\n");
334 if (help != null) {
335 out.write(help);
336 }
337 out.write("</BODY></HTML>\r\n");
338 out.flush();
339 }
340
341 /**
342 * Send common HTTP headers to the client.
343 *
344 * @param out
345 * The Writer
346 * @param status
347 * The status string ("200 OK", "500", etc)
348 * @param contentType
349 * The content type of the data sent
350 * @param endHeaders
351 * If true, adds a new line, ending the headers.
352 * @throws IOException
353 * When error
354 */
355 private void sendHeader(Writer out, String status, String contentType,
356 boolean endHeaders) throws IOException {
357 out.write("HTTP/1.1 " + status + "\r\n");
358 Date now = new Date();
359 out.write("Date: " + now + "\r\n");
360 out.write("Server: JOSM RemoteControl\r\n");
361 out.write("Content-type: " + contentType + "\r\n");
362 out.write("Access-Control-Allow-Origin: *\r\n");
363 if (endHeaders)
364 out.write("\r\n");
365 }
366}
Note: See TracBrowser for help on using the repository browser.