📄 webserver.java
字号:
/* * Copyright 1999,2005 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.apache.xmlrpc;import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.IOException;import java.io.InterruptedIOException;import java.io.UnsupportedEncodingException;import java.net.BindException;import java.net.InetAddress;import java.net.ServerSocket;import java.net.Socket;import java.net.SocketException;import java.util.EmptyStackException;import java.util.Stack;import java.util.StringTokenizer;import java.util.Vector;import org.apache.commons.codec.binary.Base64;/** * A minimal web server that exclusively handles XML-RPC requests. * * @author <a href="mailto:hannes@apache.org">Hannes Wallnoefer</a> * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a> * @author Daniel L. Rall */public class WebServer implements Runnable{ protected XmlRpcServer xmlrpc; protected ServerSocket serverSocket; protected Thread listener; protected Vector accept, deny; protected Stack threadpool; protected ThreadGroup runners; // Inputs to setupServerSocket() private InetAddress address; private int port; private boolean paranoid; protected static final byte[] ctype = toHTTPBytes("Content-Type: text/xml\r\n"); protected static final byte[] clength = toHTTPBytes("Content-Length: "); protected static final byte[] newline = toHTTPBytes("\r\n"); protected static final byte[] doubleNewline = toHTTPBytes("\r\n\r\n"); protected static final byte[] conkeep = toHTTPBytes("Connection: Keep-Alive\r\n"); protected static final byte[] conclose = toHTTPBytes("Connection: close\r\n"); protected static final byte[] ok = toHTTPBytes(" 200 OK\r\n"); protected static final byte[] server = toHTTPBytes("Server: Apache XML-RPC 1.0\r\n"); protected static final byte[] wwwAuthenticate = toHTTPBytes("WWW-Authenticate: Basic realm=XML-RPC\r\n"); private static final String HTTP_11 = "HTTP/1.1"; private static final String STAR = "*"; /** * This <em>can</em> be called from command line, but you'll have to edit * and recompile to change the server port or handler objects. By default, * it sets up the following responders: * <ul> * <li> A java.lang.String object </li> * <li> The java.lang.Math class (making its static methods callable via * XML-RPC) </li> * <li> An Echo handler that returns the argument array </li> * </ul> * * @see #addDefaultHandlers() */ public static void main(String[] argv) { int p = determinePort(argv, 8080); // XmlRpc.setDebug (true); XmlRpc.setKeepAlive(true); WebServer webserver = new WebServer(p); try { webserver.addDefaultHandlers(); webserver.start(); } catch (Exception e) { System.err.println("Error running web server"); e.printStackTrace(); System.exit(1); } } /** * Examines command line arguments from <code>argv</code>. If a * port may have been provided, parses that port (exiting with * error status if the port cannot be parsed). If no port is * specified, defaults to <code>defaultPort</code>. * * @param defaultPort The port to use if none was specified. */ protected static int determinePort(String[] argv, int defaultPort) { int port = defaultPort; if (argv.length > 0) { try { port = Integer.parseInt(argv[0]); } catch (NumberFormatException nfx) { System.err.println("Error parsing port number: " + argv[0]); System.err.println("Usage: java " + WebServer.class.getName() + " [port]"); System.exit(1); } } return port; } /** * Creates a web server at the specified port number. */ public WebServer(int port) { this(port, null); } /** * Creates a web server at the specified port number and IP address. */ public WebServer(int port, InetAddress addr) { this(port, addr, new XmlRpcServer()); } /** * Creates a web server at the specified port number and IP * address. */ public WebServer(int port, InetAddress addr, XmlRpcServer xmlrpc) { this.address = addr; this.port = port; this.xmlrpc = xmlrpc; accept = new Vector(); deny = new Vector(); threadpool = new Stack(); runners = new ThreadGroup("XML-RPC Runner"); } /** * Returns the US-ASCII encoded byte representation of text for * HTTP use (as per section 2.2 of RFC 2068). */ protected static final byte[] toHTTPBytes(String text) { try { return text.getBytes("US-ASCII"); } catch (UnsupportedEncodingException e) { throw new Error(e.getMessage() + ": HTTP requires US-ASCII encoding"); } } /** * Factory method to manufacture the server socket. Useful as a * hook method for subclasses to override when they desire * different flavor of socket (i.e. a <code>SSLServerSocket</code>). * * @param port * @param backlog * @param addr If <code>null</code>, binds to * <code>INADDR_ANY</code>, meaning that all network interfaces on * a multi-homed host will be listening. * @exception Exception Error creating listener socket. */ protected ServerSocket createServerSocket(int port, int backlog, InetAddress addr) throws Exception { return new ServerSocket(port, backlog, addr); } /** * Initializes this server's listener socket with the specified * attributes, assuring that a socket timeout has been set. The * {@link #createServerSocket(int, int, InetAddress)} method can * be overridden to change the flavor of socket used. * * @see #createServerSocket(int, int, InetAddress) */ private synchronized void setupServerSocket(int backlog) throws Exception { // Since we can't reliably set SO_REUSEADDR until JDK 1.4 is // the standard, try to (re-)open the server socket several // times. Some OSes (Linux and Solaris, for example), hold on // to listener sockets for a brief period of time for security // reasons before relinquishing their hold. int attempt = 1; while (serverSocket == null) { try { serverSocket = createServerSocket(port, backlog, address); } catch (BindException e) { if (attempt == 10) { throw e; } attempt++; Thread.sleep(1000); } } if (XmlRpc.debug) { StringBuffer msg = new StringBuffer(); msg.append("Opened XML-RPC server socket for "); msg.append(address != null ? address.getHostName() : "localhost"); msg.append(':').append(port); if (attempt > 1) { msg.append(" after ").append(attempt).append(" tries"); } System.out.println(msg.toString()); } // A socket timeout must be set. if (serverSocket.getSoTimeout() <= 0) { serverSocket.setSoTimeout(4096); } } /** * Spawns a new thread which binds this server to the port it's * configured to accept connections on. * * @see #run() */ public void start() { try { setupServerSocket(50); } catch (Exception e) { listener = null; e.printStackTrace(); throw new RuntimeException(e.getMessage()); } // The listener reference is released upon shutdown(). if (listener == null) { listener = new Thread(this, "XML-RPC Weblistener"); // Not marked as daemon thread since run directly via main(). listener.start(); } } /** * Register a handler object with this name. Methods of this objects will be * callable over XML-RPC as "name.method". */ public void addHandler(String name, Object target) { xmlrpc.addHandler(name, target); } /** * Adds the bundled handlers to the server. Called by {@link * #main(String[])}. */ protected void addDefaultHandlers() throws Exception { // webserver.setParanoid (true); // webserver.acceptClient ("192.168.*.*"); addHandler("string", "Welcome to XML-RPC!"); addHandler("math", Math.class); addHandler("auth", new AuthDemo()); addHandler("$default", new Echo()); // XmlRpcClients can be used as Proxies in XmlRpcServers which is a // cool feature for applets. String url = "http://www.mailtothefuture.com:80/RPC2"; addHandler("mttf", new XmlRpcClient(url)); SystemHandler system = new SystemHandler(); system.addDefaultSystemHandlers(); addHandler("system", system); } /** * Remove a handler object that was previously registered with this server. */ public void removeHandler(String name) { xmlrpc.removeHandler(name); } /** * Switch client filtering on/off. * @see #acceptClient(java.lang.String) * @see #denyClient(java.lang.String) */ public void setParanoid(boolean p) { paranoid = p; } /** * Add an IP address to the list of accepted clients. The parameter can * contain '*' as wildcard character, e.g. "192.168.*.*". You must call * setParanoid(true) in order for this to have any effect. * * @see #denyClient(java.lang.String) * @see #setParanoid(boolean) */ public void acceptClient(String address) throws IllegalArgumentException { try { AddressMatcher m = new AddressMatcher(address); accept.addElement(m); } catch (Exception x) { throw new IllegalArgumentException("\"" + address + "\" does not represent a valid IP address"); } } /** * Add an IP address to the list of denied clients. The parameter can * contain '*' as wildcard character, e.g. "192.168.*.*". You must call * setParanoid(true) in order for this to have any effect. * * @see #acceptClient(java.lang.String) * @see #setParanoid(boolean) */ public void denyClient(String address) throws IllegalArgumentException { try { AddressMatcher m = new AddressMatcher(address); deny.addElement(m); } catch (Exception x) { throw new IllegalArgumentException("\"" + address + "\" does not represent a valid IP address"); } } /** * Checks incoming connections to see if they should be allowed. * If not in paranoid mode, always returns true. * * @param s The socket to inspect. * @return Whether the connection should be allowed. */ protected boolean allowConnection(Socket s) { if (!paranoid) { return true; } int l = deny.size(); byte address[] = s.getInetAddress().getAddress(); for (int i = 0; i < l; i++) { AddressMatcher match = (AddressMatcher)deny.elementAt(i); if (match.matches(address)) { return false; } } l = accept.size(); for (int i = 0; i < l; i++) { AddressMatcher match = (AddressMatcher)accept.elementAt(i); if (match.matches(address)) { return true; } } return false; } /** * DEPRECATED: Do not use this method, it will be removed soon. * Use {@link #allowConnection(Socket)} instead. * * @deprecated Use allowConnection(Socket) instead. * @see #allowConnection(Socket) */ protected boolean checkSocket(Socket s) { return allowConnection(s); } /** * Listens for client requests until stopped. Call {@link * #start()} to invoke this method, and {@link #shutdown()} to * break out of it. * * @throws RuntimeException Generally caused by either an * <code>UnknownHostException</code> or <code>BindException</code> * with the vanilla web server. * * @see #start() * @see #shutdown() */ public void run() { try { while (listener != null) { try { Socket socket = serverSocket.accept(); try { socket.setTcpNoDelay(true); } catch (SocketException socketOptEx) { System.err.println(socketOptEx); } if (allowConnection(socket)) { Runner runner = getRunner(); runner.handle(socket); } else { socket.close(); } } catch (InterruptedIOException checkState)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -