@Override public NetCtx create(SocketChannel ch, BufHandler in, BufHandler out) { return new NetCtx(ch, in, out); } };
protected void connect(SelectionKey key) { NetCtx atta = NetCtx.fromKey(key); NetRequest req = (NetRequest)atta.getInput(); try { boolean connected = atta.getChannel().finishConnect(); if (connected) { // TODO key.interestOps(OP_WRITE|OP_READ); // TODO if TLS enabled, begin handshake here ... req.sendRequest(key, this); } } catch (IOException e) { // TODO notify client that connection failed, or retry log.error("Cannot finish connect for " + req, e); } }
private void scheduleWrites(boolean copyOnSchedule, NetCtx atta, ByteBuffer[] bufs) { // TODO when copyOnSchedule is true, coalesce everything into single buffer for (ByteBuffer b : bufs) { if (b.hasRemaining()) { if (copyOnSchedule) { ByteBuffer b1 = ByteBuffer.allocate(b.remaining()); b1.clear(); b1.put(b); b1.flip(); atta.addWrite(b1); } else { atta.addWrite(b); } } else if (b == NetCtx.CLOSE) { atta.addWrite(b); } } }
private void doWrite(SelectionKey key) { NetCtx atta = (NetCtx) key.attachment(); SocketChannel ch = (SocketChannel) key.channel(); try { // the sync is per socket (per client). virtually, no contention // 1. keep byte data order, 2. ensure visibility synchronized (atta) { if (atta.peekWrite() == NetCtx.CLOSE) { clientClose(key); } else if (atta.hasWrites()) { ByteBuffer[] bufs = atta.peekWrites(); ch.write(bufs, 0, bufs.length); // TODO handle CLOSE marks here ... if (atta.cleanWrites()) { key.interestOps(SelectionKey.OP_READ); } } } } catch (IOException e) { // the remote forcibly closed the connection clientClose(key); } }
@Override public RingHandler request(SelectionKey key, String httpVersion, HttpMethod method, String uri, String query) { NetCtx ctx = NetCtx.fromKey(key); request = PersistentArrayMap.EMPTY.asTransient() .assoc(SERVER_PORT, ctx.getServerPort()) .assoc(SERVER_NAME, ctx.getServerName()) .assoc(REMOTE_ADDR, ctx.getRemoteAddr()) .assoc(PROTOCOL, httpVersion) .assoc(URI, uri) .assoc(QUERY_STRING, query) .assoc(REQUEST_METHOD, getMethod(method)) .assoc(SCHEME, ctx.getTlsContext() != null ? HTTPS : HTTP); headers = PersistentArrayMap.EMPTY.asTransient(); // TODO SSL_CLIENT_CERT return this; }
@Override public HttpEncoder finish(SelectionKey key) { finishHeaders(key); ByteBuffer bb = ByteBuffer.wrap(buf.get(), 0, buf.length()); BufHandler out = output != null ? output : NetCtx.fromKey(key).getOutput(); out.submit(key, true, bb, body, keepAlive() ? NetCtx.FLUSH : NetCtx.CLOSE); state |= FINISHED; return this; }
@Override public boolean submit(final SelectionKey key, boolean copyOnSchedule, ByteBuffer... buffers) { NetCtx atta = (NetCtx) key.attachment(); TlsContext tctx = atta.getTlsContext(); ByteBuffer[] bufs = buffers; atta.addWrites(sbufs); if (hasClose) atta.addWrite(NetCtx.CLOSE); if (!atta.hasWrites() && !hasClose) { SocketChannel ch = (SocketChannel) key.channel(); try {
private void sslHandshake(final SelectionKey key) { NetCtx atta = atta(key); SocketChannel ch = (SocketChannel) key.channel(); TlsContext tctx = atta.getTlsContext(); SSLEngine engine = tctx.getSslEngine(); SSLEngineResult.HandshakeStatus handshakeStatus; case OK: tctx.getSslNetBuf().flip(); if (!atta.getOutput().submit(key, true, tctx.getSslNetBuf())) { return;
TlsContext tctx = atta.getTlsContext(); case OK: buffer.flip(); atta(key).getInput().submit(key, false, buffer); break; case BUFFER_OVERFLOW:
private void doRead(final SelectionKey key) { SocketChannel ch = (SocketChannel) key.channel(); BufHandler bufHandler = atta(key).getInput(); try { buffer.clear(); // clear for read int read = ch.read(buffer); if (read == -1) { // remote entity shut the socket down cleanly. clientClose(key); } else if (read > 0) { buffer.flip(); // flip for read bufHandler.submit(key, false, buffer); } } catch (IOException e) { // the remote forcibly closed the connection clientClose(key); } }
@Override public boolean submit(SelectionKey key, boolean copyOnSchedule, ByteBuffer... buffers) { // TODO make this thing fully streaming, do not use ByteArrayOutputStream, make it thread safe for (int i = 0; i < buffers.length; i++) { ByteBuffer buf = buffers[i]; while (buf.hasRemaining()) { byte b = buf.get(); os.write(b); if (b == '\n') { byte[] a = os.toByteArray(); os.reset(); ByteBuffer bb = ByteBuffer.wrap(a); BufHandler out = NetCtx.fromKey(key).getOutput(); //out.submit(key, true, bb, a[0] == 'q' ? NetAtta.CLOSE : NetAtta.NULL); if (isClose()) { out.submit(key, true, bb, NetCtx.CLOSE); } else { out.submit(key, true, bb); } return !(buf.hasRemaining() || i < buffers.length-1); } } } return true; }
private void forceClose(SelectionKey key, NetCtx atta, TlsContext tctx, SSLEngine engine) { try { if (!engine.isInboundDone()) { engine.closeInbound(); } if (!engine.isOutboundDone()) { engine.closeOutbound(); } atta.getInput().submit(key, false, NetCtx.CLOSE); } catch (Exception e) { log.debug("forcible close: " + e.getMessage()); } try { key.interestOps(0); key.channel().close(); } catch (Exception e) { log.debug("forcibly closing SSL connection: " + e.getMessage()); } tctx.setSslState(CLOSED); return; }
@Override public void submit(SelectionKey key, HttpMessage message) { reqs.add(message); NetCtx ctx = NetCtx.fromKey(key); assertNotNull("NetContext not passed", ctx); assertNotNull("Output not passed", ctx.getOutput()); if (!replies.isEmpty()) { HttpMessage msg = replies.pollFirst(); HttpMessageHandler mh = new HttpMessageHandler(config,null); mh.submit(key, msg); } }
@Override public NetCtx create(SocketChannel ch, BufHandler in, BufHandler out) { return new NetCtx(ch, svhFactory.create(ch), out); } }, sslContext);
private void initKeys() { assertNotNull("server not set", server); assertNotNull("client not set", client); if (clientKey == null) { clientKey = new TestSelectionKey("client"); clientKey.attach(new NetCtx(null, client, this)); } if (serverKey == null) { serverKey = new TestSelectionKey("server"); serverKey.attach(new NetCtx(null, server, this)); } }