// Path: src/org/httpkit/HttpUtils.java
// public static ByteBuffer[] HttpEncode(int status, HeaderMap headers, Object body) {
//     ByteBuffer bodyBuffer;
//     try {
//         bodyBuffer = bodyBuffer(body);
//         // only write length if not chunked
//         if (!CHUNKED.equals(headers.get("Transfer-Encoding"))) {
//             if (bodyBuffer != null) {
//                 headers.put(CL, Integer.toString(bodyBuffer.remaining()));
//             } 
//         }
//     } catch (IOException e) {
//         byte[] b = e.getMessage().getBytes(ASCII);
//         status = 500;
//         headers.clear();
//         headers.put(CL, Integer.toString(b.length));
//         bodyBuffer = ByteBuffer.wrap(b);
//     }
//     headers.put("Server", "http-kit");
//     headers.put("Date", DateFormatter.getDate()); // rfc says the Date is needed
// 
//     DynamicBytes bytes = new DynamicBytes(196);
//     byte[] bs = HttpStatus.valueOf(status).getInitialLineBytes();
//     bytes.append(bs, bs.length);
//     headers.encodeHeaders(bytes);
//     ByteBuffer headBuffer = ByteBuffer.wrap(bytes.get(), 0, bytes.length());
// 
//     if (bodyBuffer != null)
//         return new ByteBuffer[]{headBuffer, bodyBuffer};
//     else
//         return new ByteBuffer[]{headBuffer};
// }
// 
// Path: src/org/httpkit/HttpUtils.java
// public static ByteBuffer WsEncode(byte opcode, byte[] data, int length) {
//     byte b0 = 0;
//     b0 |= 1 << 7; // FIN
//     b0 |= opcode;
//     ByteBuffer buffer = ByteBuffer.allocate(length + 10); // max
//     buffer.put(b0);
// 
//     if (length <= 125) {
//         buffer.put((byte) (length));
//     } else if (length <= 0xFFFF) {
//         buffer.put((byte) 126);
//         buffer.putShort((short) length);
//     } else {
//         buffer.put((byte) 127);
//         buffer.putLong(length);
//     }
//     buffer.put(data, 0, length);
//     buffer.flip();
//     return buffer;
// }
// 
// Path: src/org/httpkit/server/Frame.java
//     public static class CloseFrame extends Frame {
// 
//         public final static short CLOSE_NORMAL = 1000;
//         // indicates that an endpoint is "going away", such as a server going down
//         // or a browser having navigated away from a page.
//         public final static short CLOSE_AWAY = 1001;
//         //        public final static short CLOSE_PROTOCOL_ERROR = 1002;
// //        public final static short CLOSE_NOT_IMPL = 1003;
//         // This is a generic status code that can be returned when there is no other
//         // more suitable status code (e.g., 1003 or 1009)
// //        public final static short CLOSE_VIOLATES_POLICY = 1008;
//         public final static short CLOSE_MESG_BIG = 1009;
// //        public final static short CLOSE_SEVER_ERROR = 1011;
// 
// //        private static byte[] bytes(short code) {
// //            return ByteBuffer.allocate(2).putShort(code).array();
// //        }
// 
// //        public static final CloseFrame NORMAL = new CloseFrame(bytes(CLOSE_NORMAL));
// //        public static final CloseFrame AWAY = new CloseFrame(bytes(CLOSE_AWAY));
// //        public static final CloseFrame MESG_BIG = new CloseFrame(bytes(CLOSE_MESG_BIG));
// //        public static final CloseFrame SERVER_ERROR = new CloseFrame(bytes(CLOSE_MESG_BIG));
// 
//         public CloseFrame(byte[] data) {
//             super(data);
//         }
// 
//         public int getStatus() {
//             if (data.length >= 2) {
//                 return ByteBuffer.wrap(data, 0, 2).getShort();
//             }
//             return CLOSE_NORMAL;
//         }
//     }


import org.httpkit.*;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import static java.nio.channels.SelectionKey.*;
import static org.httpkit.HttpUtils.HttpEncode;
import static org.httpkit.HttpUtils.WsEncode;
import static org.httpkit.server.Frame.CloseFrame.*;
                    } else {
                        atta.keepalive = request.isKeepAlive;
                    }
                    request.channel = channel;
                    request.remoteAddr = (InetSocketAddress) ch.socket().getRemoteSocketAddress();
                    handler.handle(request, new RespCallback(key, this));
                    // pipelining not supported : need queue to ensure order
                    atta.decoder.reset();
                }
            } while (buffer.hasRemaining()); // consume all
        } catch (ProtocolException e) {
            closeKey(key, -1);
        } catch (RequestTooLargeException e) {
            atta.keepalive = false;
            tryWrite(key, HttpEncode(413, new HeaderMap(), e.getMessage()));
        } catch (LineTooLargeException e) {
            atta.keepalive = false; // close after write
            tryWrite(key, HttpEncode(414, new HeaderMap(), e.getMessage()));
        }
    }

    private void decodeWs(WsAtta atta, SelectionKey key) {
        try {
            do {
                Frame frame = atta.decoder.decode(buffer);
                if (frame instanceof TextFrame || frame instanceof BinaryFrame) {
                    handler.handle(atta.channel, frame);
                    atta.decoder.reset();
                } else if (frame instanceof PingFrame) {
                    atta.decoder.reset();
// Path: src/org/httpkit/HttpUtils.java
// public static ByteBuffer[] HttpEncode(int status, HeaderMap headers, Object body) {
//     ByteBuffer bodyBuffer;
//     try {
//         bodyBuffer = bodyBuffer(body);
//         // only write length if not chunked
//         if (!CHUNKED.equals(headers.get("Transfer-Encoding"))) {
//             if (bodyBuffer != null) {
//                 headers.put(CL, Integer.toString(bodyBuffer.remaining()));
//             } 
//         }
//     } catch (IOException e) {
//         byte[] b = e.getMessage().getBytes(ASCII);
//         status = 500;
//         headers.clear();
//         headers.put(CL, Integer.toString(b.length));
//         bodyBuffer = ByteBuffer.wrap(b);
//     }
//     headers.put("Server", "http-kit");
//     headers.put("Date", DateFormatter.getDate()); // rfc says the Date is needed
// 
//     DynamicBytes bytes = new DynamicBytes(196);
//     byte[] bs = HttpStatus.valueOf(status).getInitialLineBytes();
//     bytes.append(bs, bs.length);
//     headers.encodeHeaders(bytes);
//     ByteBuffer headBuffer = ByteBuffer.wrap(bytes.get(), 0, bytes.length());
// 
//     if (bodyBuffer != null)
//         return new ByteBuffer[]{headBuffer, bodyBuffer};
//     else
//         return new ByteBuffer[]{headBuffer};
// }
// 
// Path: src/org/httpkit/HttpUtils.java
// public static ByteBuffer WsEncode(byte opcode, byte[] data, int length) {
//     byte b0 = 0;
//     b0 |= 1 << 7; // FIN
//     b0 |= opcode;
//     ByteBuffer buffer = ByteBuffer.allocate(length + 10); // max
//     buffer.put(b0);
// 
//     if (length <= 125) {
//         buffer.put((byte) (length));
//     } else if (length <= 0xFFFF) {
//         buffer.put((byte) 126);
//         buffer.putShort((short) length);
//     } else {
//         buffer.put((byte) 127);
//         buffer.putLong(length);
//     }
//     buffer.put(data, 0, length);
//     buffer.flip();
//     return buffer;
// }
// 
// Path: src/org/httpkit/server/Frame.java
//     public static class CloseFrame extends Frame {
// 
//         public final static short CLOSE_NORMAL = 1000;
//         // indicates that an endpoint is "going away", such as a server going down
//         // or a browser having navigated away from a page.
//         public final static short CLOSE_AWAY = 1001;
//         //        public final static short CLOSE_PROTOCOL_ERROR = 1002;
// //        public final static short CLOSE_NOT_IMPL = 1003;
//         // This is a generic status code that can be returned when there is no other
//         // more suitable status code (e.g., 1003 or 1009)
// //        public final static short CLOSE_VIOLATES_POLICY = 1008;
//         public final static short CLOSE_MESG_BIG = 1009;
// //        public final static short CLOSE_SEVER_ERROR = 1011;
// 
// //        private static byte[] bytes(short code) {
// //            return ByteBuffer.allocate(2).putShort(code).array();
// //        }
// 
// //        public static final CloseFrame NORMAL = new CloseFrame(bytes(CLOSE_NORMAL));
// //        public static final CloseFrame AWAY = new CloseFrame(bytes(CLOSE_AWAY));
// //        public static final CloseFrame MESG_BIG = new CloseFrame(bytes(CLOSE_MESG_BIG));
// //        public static final CloseFrame SERVER_ERROR = new CloseFrame(bytes(CLOSE_MESG_BIG));
// 
//         public CloseFrame(byte[] data) {
//             super(data);
//         }
// 
//         public int getStatus() {
//             if (data.length >= 2) {
//                 return ByteBuffer.wrap(data, 0, 2).getShort();
//             }
//             return CLOSE_NORMAL;
//         }
//     }


// Path: src/org/httpkit/server/HttpServer.java
import org.httpkit.*;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import static java.nio.channels.SelectionKey.*;
import static org.httpkit.HttpUtils.HttpEncode;
import static org.httpkit.HttpUtils.WsEncode;
import static org.httpkit.server.Frame.CloseFrame.*;

                    } else {
                        atta.keepalive = request.isKeepAlive;
                    }
                    request.channel = channel;
                    request.remoteAddr = (InetSocketAddress) ch.socket().getRemoteSocketAddress();
                    handler.handle(request, new RespCallback(key, this));
                    // pipelining not supported : need queue to ensure order
                    atta.decoder.reset();
                }
            } while (buffer.hasRemaining()); // consume all
        } catch (ProtocolException e) {
            closeKey(key, -1);
        } catch (RequestTooLargeException e) {
            atta.keepalive = false;
            tryWrite(key, HttpEncode(413, new HeaderMap(), e.getMessage()));
        } catch (LineTooLargeException e) {
            atta.keepalive = false; // close after write
            tryWrite(key, HttpEncode(414, new HeaderMap(), e.getMessage()));
        }
    }

    private void decodeWs(WsAtta atta, SelectionKey key) {
        try {
            do {
                Frame frame = atta.decoder.decode(buffer);
                if (frame instanceof TextFrame || frame instanceof BinaryFrame) {
                    handler.handle(atta.channel, frame);
                    atta.decoder.reset();
                } else if (frame instanceof PingFrame) {
                    atta.decoder.reset();
                    tryWrite(key, WsEncode(WSDecoder.OPCODE_PONG, frame.data));