5 import java.util.function.*;
8 import java.nio.channels.*;
10 public class SimpleServer implements Runnable {
11 private final ServerSocketChannel sk;
12 private final Function handler;
14 public SimpleServer(ServerSocketChannel sk, Function handler) {
16 this.handler = handler;
19 private void respond(SocketChannel cl, String status, Map resp) throws IOException {
20 Object output = resp.get("jagi.output");
22 BufferedWriter fm = new BufferedWriter(Channels.newWriter(cl, Utils.UTF8.newEncoder(), -1));
26 for(Iterator it = resp.entrySet().iterator(); it.hasNext();) {
27 Map.Entry ent = (Map.Entry)it.next();
28 Object val = ent.getValue();
29 if((ent.getKey() instanceof String) && (val != null)) {
30 String key = (String)ent.getKey();
31 if(key.startsWith("http.")) {
32 String head = key.substring(5);
33 if(head.equalsIgnoreCase("status"))
35 if(val instanceof Collection) {
36 for(Object part : (Collection)val) {
39 fm.write(part.toString());
45 fm.write(val.toString());
54 } else if(output instanceof byte[]) {
55 Utils.writeall(cl, ByteBuffer.wrap((byte[])output));
56 } else if(output instanceof ByteBuffer) {
57 Utils.writeall(cl, (ByteBuffer)output);
58 } else if(output instanceof String) {
59 Utils.writeall(cl, ByteBuffer.wrap(((String)output).getBytes(Utils.UTF8)));
60 } else if(output instanceof CharSequence) {
61 Utils.writeall(cl, Utils.UTF8.encode(CharBuffer.wrap((CharSequence)output)));
62 } else if(output instanceof InputStream) {
63 Utils.transfer(cl, Channels.newChannel((InputStream)output));
64 } else if(output instanceof ReadableByteChannel) {
65 Utils.transfer(cl, (ReadableByteChannel)output);
67 throw(new IllegalArgumentException("response-body: " + String.valueOf(output)));
70 if(output instanceof Closeable)
71 ((Closeable)output).close();
75 private void feedinput(SocketChannel cl, Map resp) throws IOException {
76 Object sink = resp.get("jagi.input-sink");
78 if(sink instanceof OutputStream) {
79 Utils.transfer(Channels.newChannel((OutputStream)sink), cl);
80 } else if(sink instanceof WritableByteChannel) {
81 Utils.transfer((WritableByteChannel)sink, cl);
83 throw(new IllegalArgumentException("input-sink: " + String.valueOf(sink)));
86 if(sink instanceof Closeable)
87 ((Closeable)sink).close();
91 @SuppressWarnings("unchecked")
92 private void serve(SocketChannel cl) throws IOException {
93 Function handler = this.handler;
94 Map<Object, Object> env = Jagi.mkenv(cl);
95 Throwable error = null;
98 Map resp = (Map)handler.apply(env);
100 if((st = (String)resp.get("jagi.status")) != null) {
101 handler = (Function)resp.get("jagi.next");
109 throw(new IllegalArgumentException(st));
111 } else if((st = (String)resp.get("http.status")) != null) {
112 respond(cl, st, resp);
116 } catch(Throwable t) {
120 Collection cleanup = (Collection)env.get("jagi.cleanup");
121 RuntimeException ce = null;
122 for(Object obj : cleanup) {
123 if(obj instanceof AutoCloseable) {
125 ((AutoCloseable)obj).close();
126 } catch(Exception e) {
128 error = ce = new RuntimeException("error(s) occurred during cleanup");
129 error.addSuppressed(e);
143 } catch(IOException e) {
144 throw(new RuntimeException(e));
148 } catch(Exception e) {
153 } catch(IOException e) {