Initial commit.
[jagi.git] / src / jagi / scgi / SimpleServer.java
CommitLineData
49ccd711
FT
1package jagi.scgi;
2
3import jagi.*;
4import java.util.*;
5import java.util.function.*;
6import java.io.*;
7import java.nio.*;
8import java.nio.channels.*;
9
10public class SimpleServer implements Runnable {
11 private final ServerSocketChannel sk;
12 private final Function handler;
13
14 public SimpleServer(ServerSocketChannel sk, Function handler) {
15 this.sk = sk;
16 this.handler = handler;
17 }
18
19 private void respond(SocketChannel cl, String status, Map resp) throws IOException {
20 Object output = resp.get("jagi.output");
21 try {
22 BufferedWriter fm = new BufferedWriter(Channels.newWriter(cl, Utils.UTF8.newEncoder(), -1));
23 fm.write("Status: ");
24 fm.write(status);
25 fm.write("\n");
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"))
34 continue;
35 if(val instanceof Collection) {
36 for(Object part : (Collection)val) {
37 fm.write(head);
38 fm.write(": ");
39 fm.write(part.toString());
40 fm.write("\n");
41 }
42 } else {
43 fm.write(head);
44 fm.write(": ");
45 fm.write(val.toString());
46 fm.write("\n");
47 }
48 }
49 }
50 }
51 fm.write("\n");
52 fm.flush();
53 if(output == null) {
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);
66 } else {
67 throw(new IllegalArgumentException("response-body: " + String.valueOf(output)));
68 }
69 } finally {
70 if(output instanceof Closeable)
71 ((Closeable)output).close();
72 }
73 }
74
75 private void feedinput(SocketChannel cl, Map resp) throws IOException {
76 Object sink = resp.get("jagi.input-sink");
77 try {
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);
82 } else {
83 throw(new IllegalArgumentException("input-sink: " + String.valueOf(sink)));
84 }
85 } finally {
86 if(sink instanceof Closeable)
87 ((Closeable)sink).close();
88 }
89 }
90
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;
96 try {
97 while(true) {
98 Map resp = (Map)handler.apply(env);
99 String st;
100 if((st = (String)resp.get("jagi.status")) != null) {
101 handler = (Function)resp.get("jagi.next");
102 switch(st) {
103 case "feed-input":
104 feedinput(cl, resp);
105 break;
106 default:
107 throw(new IllegalArgumentException(st));
108 }
109 } else if((st = (String)resp.get("http.status")) != null) {
110 respond(cl, st, resp);
111 break;
112 }
113 }
114 } catch(Throwable t) {
115 error = t;
116 throw(t);
117 } finally {
118 Collection cleanup = (Collection)env.get("jagi.cleanup");
119 RuntimeException ce = null;
120 for(Object obj : cleanup) {
121 if(obj instanceof AutoCloseable) {
122 try {
123 ((AutoCloseable)obj).close();
124 } catch(Exception e) {
125 if(error == null)
126 error = ce = new RuntimeException("error(s) occurred during cleanup");
127 error.addSuppressed(e);
128 }
129 }
130 }
131 if(ce != null)
132 throw(ce);
133 }
134 }
135
136 public void run() {
137 while(true) {
138 SocketChannel cl;
139 try {
140 cl = sk.accept();
141 } catch(IOException e) {
142 throw(new RuntimeException(e));
143 }
144 try {
145 serve(cl);
146 } catch(Exception e) {
147 e.printStackTrace();
148 } finally {
149 try {
150 cl.close();
151 } catch(IOException e) {
152 e.printStackTrace();
153 }
154 }
155 }
156 }
157}