Added support for jagi.status "chain".
[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;
142de7b2
FT
106 case "chain":
107 break;
49ccd711
FT
108 default:
109 throw(new IllegalArgumentException(st));
110 }
111 } else if((st = (String)resp.get("http.status")) != null) {
112 respond(cl, st, resp);
113 break;
114 }
115 }
116 } catch(Throwable t) {
117 error = t;
118 throw(t);
119 } finally {
120 Collection cleanup = (Collection)env.get("jagi.cleanup");
121 RuntimeException ce = null;
122 for(Object obj : cleanup) {
123 if(obj instanceof AutoCloseable) {
124 try {
125 ((AutoCloseable)obj).close();
126 } catch(Exception e) {
127 if(error == null)
128 error = ce = new RuntimeException("error(s) occurred during cleanup");
129 error.addSuppressed(e);
130 }
131 }
132 }
133 if(ce != null)
134 throw(ce);
135 }
136 }
137
138 public void run() {
139 while(true) {
140 SocketChannel cl;
141 try {
142 cl = sk.accept();
143 } catch(IOException e) {
144 throw(new RuntimeException(e));
145 }
146 try {
147 serve(cl);
148 } catch(Exception e) {
149 e.printStackTrace();
150 } finally {
151 try {
152 cl.close();
153 } catch(IOException e) {
154 e.printStackTrace();
155 }
156 }
157 }
158 }
159}