1 16 17 18 package org.apache.xmlrpc; 19 20 import java.io.BufferedInputStream ; 21 import java.io.BufferedOutputStream ; 22 import java.io.IOException ; 23 import java.io.InterruptedIOException ; 24 import java.io.UnsupportedEncodingException ; 25 import java.net.BindException ; 26 import java.net.InetAddress ; 27 import java.net.ServerSocket ; 28 import java.net.Socket ; 29 import java.net.SocketException ; 30 import java.util.EmptyStackException ; 31 import java.util.Stack ; 32 import java.util.StringTokenizer ; 33 import java.util.Vector ; 34 import org.apache.commons.codec.binary.Base64; 35 36 43 public class WebServer implements Runnable 44 { 45 protected XmlRpcServer xmlrpc; 46 47 protected ServerSocket serverSocket; 48 protected Thread listener; 49 protected Vector accept, deny; 50 protected Stack threadpool; 51 protected ThreadGroup runners; 52 53 private InetAddress address; 55 private int port; 56 57 private boolean paranoid; 58 59 protected static final byte[] ctype = 60 toHTTPBytes("Content-Type: text/xml\r\n"); 61 protected static final byte[] clength = 62 toHTTPBytes("Content-Length: "); 63 protected static final byte[] newline = toHTTPBytes("\r\n"); 64 protected static final byte[] doubleNewline = toHTTPBytes("\r\n\r\n"); 65 protected static final byte[] conkeep = 66 toHTTPBytes("Connection: Keep-Alive\r\n"); 67 protected static final byte[] conclose = 68 toHTTPBytes("Connection: close\r\n"); 69 protected static final byte[] ok = toHTTPBytes(" 200 OK\r\n"); 70 protected static final byte[] server = 71 toHTTPBytes("Server: Apache XML-RPC 1.0\r\n"); 72 protected static final byte[] wwwAuthenticate = 73 toHTTPBytes("WWW-Authenticate: Basic realm=XML-RPC\r\n"); 74 75 private static final String HTTP_11 = "HTTP/1.1"; 76 private static final String STAR = "*"; 77 78 91 public static void main(String [] argv) 92 { 93 int p = determinePort(argv, 8080); 94 XmlRpc.setKeepAlive(true); 96 WebServer webserver = new WebServer(p); 97 98 try 99 { 100 webserver.addDefaultHandlers(); 101 webserver.start(); 102 } 103 catch (Exception e) 104 { 105 System.err.println("Error running web server"); 106 e.printStackTrace(); 107 System.exit(1); 108 } 109 } 110 111 119 protected static int determinePort(String [] argv, int defaultPort) 120 { 121 int port = defaultPort; 122 if (argv.length > 0) 123 { 124 try 125 { 126 port = Integer.parseInt(argv[0]); 127 } 128 catch (NumberFormatException nfx) 129 { 130 System.err.println("Error parsing port number: " + argv[0]); 131 System.err.println("Usage: java " + WebServer.class.getName() 132 + " [port]"); 133 System.exit(1); 134 } 135 } 136 return port; 137 } 138 139 142 public WebServer(int port) 143 { 144 this(port, null); 145 } 146 147 150 public WebServer(int port, InetAddress addr) 151 { 152 this(port, addr, new XmlRpcServer()); 153 } 154 155 159 public WebServer(int port, InetAddress addr, XmlRpcServer xmlrpc) 160 { 161 this.address = addr; 162 this.port = port; 163 this.xmlrpc = xmlrpc; 164 accept = new Vector (); 165 deny = new Vector (); 166 threadpool = new Stack (); 167 runners = new ThreadGroup ("XML-RPC Runner"); 168 } 169 170 174 protected static final byte[] toHTTPBytes(String text) 175 { 176 try 177 { 178 return text.getBytes("US-ASCII"); 179 } 180 catch (UnsupportedEncodingException e) 181 { 182 throw new Error (e.getMessage() + 183 ": HTTP requires US-ASCII encoding"); 184 } 185 } 186 187 199 protected ServerSocket createServerSocket(int port, int backlog, 200 InetAddress addr) 201 throws Exception 202 { 203 return new ServerSocket (port, backlog, addr); 204 } 205 206 214 private synchronized void setupServerSocket(int backlog) 215 throws Exception 216 { 217 int attempt = 1; 223 while (serverSocket == null) 224 { 225 try 226 { 227 serverSocket = createServerSocket(port, backlog, address); 228 } 229 catch (BindException e) 230 { 231 if (attempt == 10) 232 { 233 throw e; 234 } 235 236 attempt++; 237 Thread.sleep(1000); 238 } 239 } 240 241 if (XmlRpc.debug) 242 { 243 StringBuffer msg = new StringBuffer (); 244 msg.append("Opened XML-RPC server socket for "); 245 msg.append(address != null ? address.getHostName() : "localhost"); 246 msg.append(':').append(port); 247 if (attempt > 1) 248 { 249 msg.append(" after ").append(attempt).append(" tries"); 250 } 251 System.out.println(msg.toString()); 252 } 253 254 if (serverSocket.getSoTimeout() <= 0) 256 { 257 serverSocket.setSoTimeout(4096); 258 } 259 } 260 261 267 public void start() 268 { 269 try 270 { 271 setupServerSocket(50); 272 } 273 catch (Exception e) 274 { 275 listener = null; 276 e.printStackTrace(); 277 throw new RuntimeException (e.getMessage()); 278 } 279 280 if (listener == null) 282 { 283 listener = new Thread (this, "XML-RPC Weblistener"); 284 listener.start(); 286 } 287 } 288 289 293 public void addHandler(String name, Object target) 294 { 295 xmlrpc.addHandler(name, target); 296 } 297 298 302 protected void addDefaultHandlers() 303 throws Exception 304 { 305 addHandler("string", "Welcome to XML-RPC!"); 308 addHandler("math", Math .class); 309 addHandler("auth", new AuthDemo()); 310 addHandler("$default", new Echo()); 311 String url = "http://www.mailtothefuture.com:80/RPC2"; 314 addHandler("mttf", new XmlRpcClient(url)); 315 SystemHandler system = new SystemHandler(); 316 system.addDefaultSystemHandlers(); 317 addHandler("system", system); 318 } 319 320 323 public void removeHandler(String name) 324 { 325 xmlrpc.removeHandler(name); 326 } 327 328 333 public void setParanoid(boolean p) 334 { 335 paranoid = p; 336 } 337 338 346 public void acceptClient(String address) throws IllegalArgumentException 347 { 348 try 349 { 350 AddressMatcher m = new AddressMatcher(address); 351 accept.addElement(m); 352 } 353 catch (Exception x) 354 { 355 throw new IllegalArgumentException ("\"" + address 356 + "\" does not represent a valid IP address"); 357 } 358 } 359 360 368 public void denyClient(String address) throws IllegalArgumentException 369 { 370 try 371 { 372 AddressMatcher m = new AddressMatcher(address); 373 deny.addElement(m); 374 } 375 catch (Exception x) 376 { 377 throw new IllegalArgumentException ("\"" + address 378 + "\" does not represent a valid IP address"); 379 } 380 } 381 382 389 protected boolean allowConnection(Socket s) 390 { 391 if (!paranoid) 392 { 393 return true; 394 } 395 396 int l = deny.size(); 397 byte address[] = s.getInetAddress().getAddress(); 398 for (int i = 0; i < l; i++) 399 { 400 AddressMatcher match = (AddressMatcher)deny.elementAt(i); 401 if (match.matches(address)) 402 { 403 return false; 404 } 405 } 406 l = accept.size(); 407 for (int i = 0; i < l; i++) 408 { 409 AddressMatcher match = (AddressMatcher)accept.elementAt(i); 410 if (match.matches(address)) 411 { 412 return true; 413 } 414 } 415 return false; 416 } 417 418 425 protected boolean checkSocket(Socket s) 426 { 427 return allowConnection(s); 428 } 429 430 442 public void run() 443 { 444 try 445 { 446 while (listener != null) 447 { 448 try 449 { 450 Socket socket = serverSocket.accept(); 451 try 452 { 453 socket.setTcpNoDelay(true); 454 } 455 catch (SocketException socketOptEx) 456 { 457 System.err.println(socketOptEx); 458 } 459 460 if (allowConnection(socket)) 461 { 462 Runner runner = getRunner(); 463 runner.handle(socket); 464 } 465 else 466 { 467 socket.close(); 468 } 469 } 470 catch (InterruptedIOException checkState) 471 { 472 } 475 catch (Exception ex) 476 { 477 System.err.println("Exception in XML-RPC listener loop (" 478 + ex + ")."); 479 if (XmlRpc.debug) 480 { 481 ex.printStackTrace(); 482 } 483 } 484 catch (Error err) 485 { 486 System.err.println("Error in XML-RPC listener loop (" 487 + err + ")."); 488 err.printStackTrace(); 489 } 490 } 491 } 492 catch (Exception exception) 493 { 494 System.err.println("Error accepting XML-RPC connections (" 495 + exception + ")."); 496 if (XmlRpc.debug) 497 { 498 exception.printStackTrace(); 499 } 500 } 501 finally 502 { 503 if (serverSocket != null) 504 { 505 try 506 { 507 serverSocket.close(); 508 if (XmlRpc.debug) 509 { 510 System.out.print("Closed XML-RPC server socket"); 511 } 512 serverSocket = null; 513 } 514 catch (IOException e) 515 { 516 e.printStackTrace(); 517 } 518 } 519 520 if (runners != null) 522 { 523 ThreadGroup g = runners; 524 runners = null; 525 try 526 { 527 g.interrupt(); 528 } 529 catch (Exception e) 530 { 531 System.err.println(e); 532 e.printStackTrace(); 533 } 534 } 535 } 536 } 537 538 545 public synchronized void shutdown() 546 { 547 if (listener != null) 549 { 550 Thread l = listener; 551 listener = null; 552 l.interrupt(); 553 } 554 } 555 556 560 protected Runner getRunner() 561 { 562 try 563 { 564 return (Runner)threadpool.pop(); 565 } 566 catch (EmptyStackException empty) 567 { 568 int maxRequests = XmlRpc.getMaxThreads(); 569 if (runners.activeCount() > XmlRpc.getMaxThreads()) 570 { 571 throw new RuntimeException ("System overload: Maximum number " + 572 "of concurrent requests (" + 573 maxRequests + ") exceeded"); 574 } 575 return new Runner(); 576 } 577 } 578 579 584 void repoolRunner(Runner runner) 585 { 586 threadpool.push(runner); 587 } 588 589 592 class Runner implements Runnable 593 { 594 Thread thread; 595 Connection con; 596 int count; 597 598 603 public synchronized void handle(Socket socket) throws IOException 604 { 605 con = new Connection(socket); 606 count = 0; 607 if (thread == null || !thread.isAlive()) 608 { 609 thread = new Thread (runners, this); 610 thread.start(); 611 } 612 else 613 { 614 this.notify(); 616 } 617 } 618 619 622 public void run() 623 { 624 while (con != null && Thread.currentThread() == thread) 625 { 626 con.run(); 627 count++; 628 con = null; 629 630 if (count > 200 || threadpool.size() > 20) 631 { 632 return; 635 } 636 synchronized(this) 637 { 638 repoolRunner(this); 639 try 640 { 641 this.wait(); 642 } 643 catch (InterruptedException ir) 644 { 645 Thread.currentThread().interrupt(); 646 } 647 } 648 } 649 } 650 } 651 652 655 class Connection implements Runnable 656 { 657 private Socket socket; 658 private BufferedInputStream input; 659 private BufferedOutputStream output; 660 private String user, password; 661 private Base64 base64Codec; 662 byte[] buffer; 663 664 669 public Connection (Socket socket) throws IOException 670 { 671 socket.setSoTimeout (30000); 673 674 this.socket = socket; 675 input = new BufferedInputStream (socket.getInputStream()); 676 output = new BufferedOutputStream (socket.getOutputStream()); 677 } 678 679 682 public void run() 683 { 684 try 685 { 686 boolean keepAlive = false; 687 688 do 689 { 690 user = null; 692 password = null; 693 String line = readLine(); 694 if (line != null && line.length() == 0) 696 { 697 line = readLine(); 698 } 699 if (XmlRpc.debug) 700 { 701 System.out.println(line); 702 } 703 int contentLength = -1; 704 705 StringTokenizer tokens = new StringTokenizer (line); 707 String method = tokens.nextToken(); 708 String uri = tokens.nextToken(); 709 String httpVersion = tokens.nextToken(); 710 keepAlive = XmlRpc.getKeepAlive() 711 && HTTP_11.equals(httpVersion); 712 do 713 { 714 line = readLine(); 715 if (line != null) 716 { 717 if (XmlRpc.debug) 718 { 719 System.out.println(line); 720 } 721 String lineLower = line.toLowerCase(); 722 if (lineLower.startsWith("content-length:")) 723 { 724 contentLength = Integer.parseInt( 725 line.substring(15).trim()); 726 } 727 if (lineLower.startsWith("connection:")) 728 { 729 keepAlive = XmlRpc.getKeepAlive() && 730 lineLower.indexOf("keep-alive") > -1; 731 } 732 if (lineLower.startsWith("authorization: basic ")) 733 { 734 parseAuth (line); 735 } 736 } 737 } 738 while (line != null && line.length() != 0); 739 740 if ("POST".equalsIgnoreCase(method)) 741 { 742 ServerInputStream sin = new ServerInputStream(input, 743 contentLength); 744 try 745 { 746 byte[] result = xmlrpc.execute(sin, user, password); 747 writeResponse(result, httpVersion, keepAlive); 748 } 749 catch (AuthenticationFailed unauthorized) 750 { 751 keepAlive = false; 752 writeUnauthorized(httpVersion, method); 753 } 754 } 755 else 756 { 757 keepAlive = false; 758 writeBadRequest(httpVersion, method); 759 } 760 output.flush(); 761 } 762 while (keepAlive); 763 } 764 catch (Exception exception) 765 { 766 if (XmlRpc.debug) 767 { 768 exception.printStackTrace(); 769 } 770 else 771 { 772 System.err.println(exception); 773 } 774 } 775 finally 776 { 777 try 778 { 779 if (socket != null) 780 { 781 socket.close(); 782 } 783 } 784 catch (IOException ignore) 785 { 786 } 787 } 788 } 789 790 795 private String readLine() throws IOException 796 { 797 if (buffer == null) 798 { 799 buffer = new byte[2048]; 800 } 801 int next; 802 int count = 0; 803 for (;;) 804 { 805 next = input.read(); 806 if (next < 0 || next == '\n') 807 { 808 break; 809 } 810 if (next != '\r') 811 { 812 buffer[count++] = (byte) next; 813 } 814 if (count >= buffer.length) 815 { 816 throw new IOException ("HTTP Header too long"); 817 } 818 } 819 return new String (buffer, 0, count); 820 } 821 822 826 private void parseAuth(String line) 827 { 828 try 829 { 830 byte[] c = base64Codec.decode(toHTTPBytes(line.substring(21))); 831 String str = new String (c); 832 int col = str.indexOf(':'); 833 user = str.substring(0, col); 834 password = str.substring(col + 1); 835 } 836 catch (Throwable ignore) 837 { 838 } 839 } 840 841 private void writeResponse(byte[] payload, String httpVersion, 842 boolean keepAlive) 843 throws IOException 844 { 845 output.write(toHTTPBytes(httpVersion)); 846 output.write(ok); 847 output.write(server); 848 output.write(keepAlive ? conkeep : conclose); 849 output.write(ctype); 850 output.write(clength); 851 output.write(toHTTPBytes(Integer.toString(payload.length))); 852 output.write(doubleNewline); 853 output.write(payload); 854 } 855 856 private void writeBadRequest(String httpVersion, String httpMethod) 857 throws IOException 858 { 859 output.write(toHTTPBytes(httpVersion)); 860 output.write(toHTTPBytes(" 400 Bad Request")); 861 output.write(newline); 862 output.write(server); 863 output.write(newline); 864 output.write(toHTTPBytes("Method " + httpMethod + 865 " not implemented (try POST)")); 866 } 867 868 private void writeUnauthorized(String httpVersion, String httpMethod) 869 throws IOException 870 { 871 output.write(toHTTPBytes(httpVersion)); 872 output.write(toHTTPBytes(" 401 Unauthorized")); 873 output.write(newline); 874 output.write(server); 875 output.write(wwwAuthenticate); 876 output.write(newline); 877 output.write(toHTTPBytes("Method " + httpMethod + " requires a " + 878 "valid user name and password")); 879 } 880 } 881 882 885 class AddressMatcher 886 { 887 int pattern[]; 888 889 894 public AddressMatcher(String address) throws Exception 895 { 896 pattern = new int[4]; 897 StringTokenizer st = new StringTokenizer (address, "."); 898 if (st.countTokens() != 4) 899 { 900 throw new Exception ("\"" + address 901 + "\" does not represent a valid IP address"); 902 } 903 for (int i = 0; i < 4; i++) 904 { 905 String next = st.nextToken(); 906 if (STAR.equals(next)) 907 { 908 pattern[i] = 256; 909 } 910 else 911 { 912 pattern[i] = (byte) Integer.parseInt(next); 913 } 914 } 915 } 916 917 922 public boolean matches (byte address[]) 923 { 924 for (int i = 0; i < 4; i++) 925 { 926 if (pattern[i] > 255) { 928 continue; 929 } 930 if (pattern[i] != address[i]) 931 { 932 return false; 933 } 934 } 935 return true; 936 } 937 } 938 } 939 | Popular Tags |