| 1 | package jagi.scgi; |
| 2 | |
| 3 | import jagi.*; |
| 4 | import java.lang.reflect.*; |
| 5 | import java.util.*; |
| 6 | import java.util.function.*; |
| 7 | import java.io.*; |
| 8 | import java.net.*; |
| 9 | import java.nio.channels.*; |
| 10 | |
| 11 | public class Bootstrap { |
| 12 | private static InetSocketAddress resolveinaddr(String spec) { |
| 13 | int p = spec.indexOf(':'); |
| 14 | SocketAddress bind; |
| 15 | if(p >= 0) { |
| 16 | InetAddress host; |
| 17 | try { |
| 18 | if(spec.charAt(0) == '[') { |
| 19 | p = spec.indexOf(']'); |
| 20 | if((p < 0) || (spec.charAt(p + 1) != ':')) |
| 21 | throw(new IllegalArgumentException("invalid address syntax: " + spec)); |
| 22 | host = InetAddress.getByName(spec.substring(1, p)); |
| 23 | p++; |
| 24 | } else { |
| 25 | host = InetAddress.getByName(spec.substring(0, p)); |
| 26 | } |
| 27 | } catch(UnknownHostException e) { |
| 28 | throw(new IllegalArgumentException("could not resolve inet host: " + spec, e)); |
| 29 | } |
| 30 | try { |
| 31 | return(new InetSocketAddress(host, Integer.parseInt(spec.substring(p + 1)))); |
| 32 | } catch(NumberFormatException e) { |
| 33 | throw(new IllegalArgumentException("not a valid port number: " + spec.substring(p + 1), e)); |
| 34 | } |
| 35 | } else { |
| 36 | try { |
| 37 | return(new InetSocketAddress(Integer.parseInt(spec))); |
| 38 | } catch(NumberFormatException e) { |
| 39 | throw(new IllegalArgumentException("not a valid port number: " + spec, e)); |
| 40 | } |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | private static ServerSocketChannel tcplisten(String spec) { |
| 45 | SocketAddress bind; |
| 46 | try { |
| 47 | bind = resolveinaddr(spec); |
| 48 | } catch(IllegalArgumentException e) { |
| 49 | System.err.println("scgi-jagi: " + e.getMessage()); |
| 50 | System.exit(1); |
| 51 | return(null); |
| 52 | } |
| 53 | try { |
| 54 | ServerSocketChannel sk = ServerSocketChannel.open(); |
| 55 | sk.bind(bind); |
| 56 | return(sk); |
| 57 | } catch(IOException e) { |
| 58 | System.err.println("scgi-jagi: could not create TCP socket: " + e.getMessage()); |
| 59 | System.exit(1); |
| 60 | return(null); |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | private static ServerSocketChannel getstdin() { |
| 65 | Channel stdin; |
| 66 | try { |
| 67 | stdin = System.inheritedChannel(); |
| 68 | } catch(IOException e) { |
| 69 | System.err.println("scgi-jagi: could not get stdin channel: " + e.getMessage()); |
| 70 | System.exit(1); |
| 71 | return(null); |
| 72 | } |
| 73 | if(!(stdin instanceof ServerSocketChannel)) { |
| 74 | System.err.println("scgi-jagi: stdin is not a listening socket"); |
| 75 | System.exit(1); |
| 76 | return(null); |
| 77 | } |
| 78 | return((ServerSocketChannel)stdin); |
| 79 | } |
| 80 | |
| 81 | private static Function gethandler(ClassLoader loader, String nm, String... args) { |
| 82 | Class<?> cl; |
| 83 | try { |
| 84 | cl = loader.loadClass(nm); |
| 85 | } catch(ClassNotFoundException e) { |
| 86 | try { |
| 87 | cl = loader.loadClass(nm + ".Bootstrap"); |
| 88 | } catch(ClassNotFoundException e2) { |
| 89 | System.err.println("scgi-jagi: could not find handler class or package: " + nm); |
| 90 | System.exit(1); |
| 91 | return(null); |
| 92 | } |
| 93 | } |
| 94 | Method wmain; |
| 95 | try { |
| 96 | wmain = cl.getDeclaredMethod("wmain", String[].class); |
| 97 | int mod = wmain.getModifiers(); |
| 98 | if(((mod & Modifier.STATIC) == 0) || ((mod & Modifier.PUBLIC) == 0)) |
| 99 | throw(new NoSuchMethodException()); |
| 100 | } catch(NoSuchMethodException e) { |
| 101 | System.err.println("scgi-jagi: could not find wmain method in " + cl.getName()); |
| 102 | System.exit(1); |
| 103 | return(null); |
| 104 | } |
| 105 | Object handler; |
| 106 | try { |
| 107 | handler = wmain.invoke(null, new Object[] {args}); |
| 108 | } catch(IllegalAccessException e) { |
| 109 | System.err.println("scgi-jagi: could not call wmain in " + cl.getName()); |
| 110 | System.exit(1); |
| 111 | return(null); |
| 112 | } catch(InvocationTargetException e) { |
| 113 | System.err.println("scgi-jagi: wmain in " + cl.getName() + " failed"); |
| 114 | e.printStackTrace(System.err); |
| 115 | System.exit(1); |
| 116 | return(null); |
| 117 | } |
| 118 | if(!(handler instanceof Function)) { |
| 119 | System.err.println("scgi-jagi: wmain in " + cl.getName() + " returned " + ((handler == null) ? "null" : ("a " + handler.getClass()))); |
| 120 | System.exit(1); |
| 121 | return(null); |
| 122 | } |
| 123 | return((Function)handler); |
| 124 | } |
| 125 | |
| 126 | private static void usage(PrintStream out) { |
| 127 | out.println("usage: jagi.jar [-h] [-T [HOST:]PORT] HANDLER-CLASS [ARGS...]"); |
| 128 | } |
| 129 | |
| 130 | public static void main(String[] args) { |
| 131 | PosixArgs opt = PosixArgs.getopt(args, "hT:"); |
| 132 | if(opt == null) { |
| 133 | usage(System.err); |
| 134 | System.exit(1); |
| 135 | return; |
| 136 | } |
| 137 | String tcpspec = null; |
| 138 | ClassLoader loader = Bootstrap.class.getClassLoader(); |
| 139 | for(char c : opt.parsed()) { |
| 140 | switch(c) { |
| 141 | case 'h': |
| 142 | usage(System.out); |
| 143 | System.exit(0); |
| 144 | break; |
| 145 | case 'T': |
| 146 | tcpspec = opt.arg; |
| 147 | break; |
| 148 | } |
| 149 | } |
| 150 | if(opt.rest.length < 1) { |
| 151 | usage(System.err); |
| 152 | System.exit(1); |
| 153 | return; |
| 154 | } |
| 155 | Function handler = gethandler(loader, opt.rest[0], Arrays.copyOfRange(opt.rest, 1, opt.rest.length)); |
| 156 | ServerSocketChannel sk; |
| 157 | if(tcpspec != null) { |
| 158 | sk = tcplisten(tcpspec); |
| 159 | } else { |
| 160 | sk = getstdin(); |
| 161 | } |
| 162 | Runnable server = new SimpleServer(sk, handler); |
| 163 | try { |
| 164 | server.run(); |
| 165 | } catch(Throwable e) { |
| 166 | System.err.println("scgi-jagi: server exited abnormally"); |
| 167 | e.printStackTrace(); |
| 168 | System.exit(1); |
| 169 | } |
| 170 | System.exit(0); |
| 171 | } |
| 172 | } |