/** * Copyright (C) Zhang,Yuexiang (xfeep) * */ package nginx.clojure.net; import java.io.Closeable; import java.io.IOException; import java.nio.ByteBuffer; import sun.nio.ch.DirectBuffer; import nginx.clojure.HackUtils; import nginx.clojure.MiniConstants; import nginx.clojure.NginxClojureRT; public class NginxClojureAsynSocket implements NginxClojureSocketRawHandler, Closeable { public static final long NGX_HTTP_CLOJURE_SOCKET_OK = 0; public static final long NGX_HTTP_CLOJURE_SOCKET_ERR = -16; public static final long NGX_HTTP_CLOJURE_SOCKET_ERR_RESOLVE = -17; public static final long NGX_HTTP_CLOJURE_SOCKET_ERR_CONNECT = -18; public static final long NGX_HTTP_CLOJURE_SOCKET_ERR_CONNECT_TIMEOUT = -19; /** * @deprecated please use either of NGX_HTTP_CLOJURE_SOCKET_ERR_CONNECT_TIMEOUT, NGX_HTTP_CLOJURE_SOCKET_ERR_READ_TIMEOUT or NGX_HTTP_CLOJURE_SOCKET_ERR_WRITE_TIMEOUT */ public static final long NGX_HTTP_CLOJURE_SOCKET_ERR_TIMEOUT = -20; public static final long NGX_HTTP_CLOJURE_SOCKET_ERR_READ = -21; public static final long NGX_HTTP_CLOJURE_SOCKET_ERR_READ_TIMEOUT = -22; public static final long NGX_HTTP_CLOJURE_SOCKET_ERR_WRITE = -23; public static final long NGX_HTTP_CLOJURE_SOCKET_ERR_WRITE_TIMEOUT = -24; public static final long NGX_HTTP_CLOJURE_SOCKET_ERR_RESET = -25; public static final long NGX_HTTP_CLOJURE_SOCKET_ERR_OUTOFMEMORY = -26; public static final long NGX_HTTP_CLOJURE_SOCKET_ERR_AGAIN = -27; public static final long NGX_HTTP_CLOJURE_SOCKET_ERR_BIND = -28; public static final long NGX_HTTP_CLOJURE_SOCKET_SHUTDOWN_READ = 0; public static final long NGX_HTTP_CLOJURE_SOCKET_SHUTDOWN_WRITE = 1; public static final long NGX_HTTP_CLOJURE_SOCKET_SHUTDOWN_BOTH = 2; public static final long NGX_HTTP_CLOJURE_SOCKET_SHUTDOWN_SOFT_FLAG = 4; public static final long NGX_HTTP_CLOJURE_SOCKET_SHUTDOWN_SOFT_READ = NGX_HTTP_CLOJURE_SOCKET_SHUTDOWN_READ | NGX_HTTP_CLOJURE_SOCKET_SHUTDOWN_SOFT_FLAG; public static final long NGX_HTTP_CLOJURE_SOCKET_SHUTDOWN_SOFT_WRITE = NGX_HTTP_CLOJURE_SOCKET_SHUTDOWN_WRITE | NGX_HTTP_CLOJURE_SOCKET_SHUTDOWN_SOFT_FLAG; public static final long NGX_HTTP_CLOJURE_SOCKET_SHUTDOWN_SOFT_BOTH = NGX_HTTP_CLOJURE_SOCKET_SHUTDOWN_BOTH | NGX_HTTP_CLOJURE_SOCKET_SHUTDOWN_SOFT_FLAG; public static final String[] NGX_HTTP_CLOJURE_SOCKET_ERROR_STRS = { "socket general error" , //NGX_HTTP_CLOJURE_SOCKET_ERR, "socket resolve error" ,//NGX_HTTP_CLOJURE_SOCKET_ERR_RESOLVE "socket connect error" , //NGX_HTTP_CLOJURE_SOCKET_ERR_CONNECT "socket connect timeout", //NGX_HTTP_CLOJURE_SOCKET_ERR_CONNECT_TIMEOUT "socket timeout", //NGX_HTTP_CLOJURE_SOCKET_ERR_TIMEOUT "socket read error", //NGX_HTTP_CLOJURE_SOCKET_ERR_READ "socket read timeout", //NGX_HTTP_CLOJURE_SOCKET_ERR_READ_TIMEOUT "socket write error" , //NGX_HTTP_CLOJURE_SOCKET_ERR_WRITE "socket write timeout" , //NGX_HTTP_CLOJURE_SOCKET_ERR_WRITE_TIMEOUT "socket reset", //NGX_HTTP_CLOJURE_SOCKET_ERR_RESET "socket out of memory" , //NGX_HTTP_CLOJURE_SOCKET_ERR_OUTOFMEMORY "socket try again" , //NGX_HTTP_CLOJURE_SOCKET_ERR_AGAIN "socket bind error" , //NGX_HTTP_CLOJURE_SOCKET_ERR_BIND }; protected long s; protected boolean connected; protected NginxClojureSocketHandler handler; protected Object context; protected String url; public NginxClojureAsynSocket() { if (Thread.currentThread() != NginxClojureRT.NGINX_MAIN_THREAD) { throw new IllegalAccessError("NginxClojureAsynSocket can only be operated in main thread"); } s = create(this); if (s == 0) { throw new OutOfMemoryError("no memory for create a native nginx clojure socket"); } } public NginxClojureAsynSocket(NginxClojureSocketHandler handler) { this(); this.handler = handler; } public int available() { return (int)available(s); } public long setTcpNoDelay(long tcpNoDelay) { return setTcpNoDelay(s, tcpNoDelay); } public long getTcpNoDelay() { return getTcpNoDelay(s); } public long setSoKeepAlive(long soKeepAlive) { return setSoKeepAlive(s, soKeepAlive); } public long getSoKeepAlive() { return getSoKeepAlive(s); } public boolean isClosed() { return s == 0; } public boolean isConnected() { return connected; } public final void checkConnected() { if (s <= 0 || !connected) { throw new RuntimeException("socket has been closed or not connected!"); } } public final void checkNotClosed() { if (s <= 0) { throw new RuntimeException("socket has been closed!"); } } public final static String errorCodeToString(long sc) { return NGX_HTTP_CLOJURE_SOCKET_ERROR_STRS[(int)(NGX_HTTP_CLOJURE_SOCKET_ERR - sc)]; } public final String buildError(long sc) { StringBuilder err = new StringBuilder(errorCodeToString(sc)); err.append(" On Server ").append(url); return err.toString(); } /** * if timeout is negative, it will be ignored. if timeout is 0, this means no timeout. */ public void setConnectTimeout(long timeout) { checkNotClosed(); setTimeout(s, timeout, -1, -1); } /** * if timeout is negative, it will be ignored. if timeout is 0, this means no timeout. */ public void setReadTimeout(long timeout) { checkNotClosed(); setTimeout(s, -1, timeout, -1); } /** * if timeout is negative, it will be ignored. if timeout is 0, this means no timeout. */ public void setWriteTimeout(long timeout) { checkNotClosed(); setTimeout(s, -1, -1, timeout); } /** * if timeout is negative, it will be ignored. if timeout is 0, this means no timeout. */ public void setTimeout(long connectTimeout, long readTimeout, long writeTimeout) { checkNotClosed(); setTimeout(s, connectTimeout, readTimeout, writeTimeout); } public long getReadTimeout() { checkNotClosed(); return getReadTimeout(s); } public long getWriteTimeout() { checkNotClosed(); return getWriteTimeout(s); } public long getConnectTimeout() { checkNotClosed(); return getConnectTimeout(s); } public long getReceiveBufferSize() { checkNotClosed(); return getReceiveBufferSize(s); } public void setReceiveBufferSize(long size) { checkNotClosed(); setReceiveBufferSize(s, size); } /** * * @param url * e.g. "192.168.2.34:80" , "www.bing.com:80", or unix domain socket "unix:/var/mytest/server.sock" * @return */ public long connect(String url) { this.url = url; ByteBuffer b = HackUtils.encode(url, MiniConstants.DEFAULT_ENCODING, NginxClojureRT.pickByteBuffer()); return connect(s, b.array(), MiniConstants.BYTE_ARRAY_OFFSET, b.remaining()); } public long bind(String addr) { ByteBuffer b = HackUtils.encode(addr, MiniConstants.DEFAULT_ENCODING, NginxClojureRT.pickByteBuffer()); return bind(s, b.array(), MiniConstants.BYTE_ARRAY_OFFSET, b.remaining()); } /** * * @param buf buffer * @param off offest of buf, * @param size the number of bytes returned at most * @return 0 : EOF, * NGX_HTTP_CLOJURE_SOCKET_ERR_AGAIN : try on next event, * NGX_HTTP_CLOJURE_SOCKET_ERR_READ : read error * > 0 : the number of bytes read */ public long read(byte[] buf, long off, long size) { checkConnected(); return read(s, buf, MiniConstants.BYTE_ARRAY_OFFSET + off, size); } /** * @param buf buffer * @return 0 : EOF, * NGX_HTTP_CLOJURE_SOCKET_ERR_AGAIN : try on next event, * NGX_HTTP_CLOJURE_SOCKET_ERR_READ : read error * > 0 : the number of bytes read */ public long read(ByteBuffer buf) { long rc; if (buf.isDirect()) { rc = read(s, null, ((DirectBuffer)buf).address()+buf.position(), buf.remaining()); }else { rc = read(s, buf.array(), MiniConstants.BYTE_ARRAY_OFFSET + buf.arrayOffset()+buf.position(), buf.remaining()); } if (rc > 0) { buf.position(buf.position() + (int)rc); } return rc; } /** * * @param buf buffer * @param off offest of buf * @param size the number of bytes sent at most * @return 0 : EOF, * NGX_HTTP_CLOJURE_SOCKET_ERR_AGAIN : try on next event, * NGX_HTTP_CLOJURE_SOCKET_ERR_WRITE : write error * > 0 : the number of bytes sent */ public long write(byte[] buf, long off, long size) { checkConnected(); return write(s, buf, MiniConstants.BYTE_ARRAY_OFFSET + off, size); } public long write(ByteBuffer buf) { long rc; if (buf.isDirect()) { rc = write(s, null, ((DirectBuffer)buf).address()+buf.position(), buf.remaining()); }else { rc = write(s, buf.array(), MiniConstants.BYTE_ARRAY_OFFSET + buf.arrayOffset()+buf.position(), buf.remaining()); } if (rc > 0) { buf.position(buf.position() + (int)rc); } return rc; } public long shutdown(long how) { checkConnected(); return shutdown(s, how); } public long cancelSoftShutdown(long how) { checkConnected(); return cancelSoftShutdown(s, how); } public void close() { if (s <= 0) { return; } s = -s; close(-s); } public <T> T getContext() { return (T)context; } public <T> void setContext(T context) { this.context = context; } public NginxClojureSocketHandler getHandler() { return handler; } public void setHandler(NginxClojureSocketHandler handler) { this.handler = handler; } @Override public void onConnect(long u, long sc) throws IOException { if (!connected && sc == NGX_HTTP_CLOJURE_SOCKET_OK) { connected = true; } handler.onConnect(this, sc); } @Override public void onRead(long u, long sc) throws IOException { handler.onRead(this, sc); } @Override public void onWrite(long u, long sc) throws IOException { handler.onWrite(this, sc); } @Override public void onRelease(long u, long sc) throws IOException { handler.onRelease(this, sc); } /** * * @param handler socket event handler * @return native socket handle created */ private static native long create(NginxClojureSocketRawHandler handler); /** * * @param s native socket handle * @return available bytes */ private static native long available(long s); /** * * @param s native socket handle * @param buf if buf is null, off is a native buffer address * @param off offest of buf, if buf is not null, offset must include Java Object Base Offset * @param size the number of bytes returned at most * @return 0 : EOF, * NGX_HTTP_CLOJURE_SOCKET_ERR_AGAIN : try on next event, * NGX_HTTP_CLOJURE_SOCKET_ERR_READ : read error */ private static native long read(long s, Object buf, long off, long size); /** * * @param s native socket handle * @param buf if buf is null, off is a native buffer address * @param off offest of buf, if buf is not null, offset must include Java Object Base Offset * @param size the number of bytes sent at most * @return 0 : EOF, * NGX_HTTP_CLOJURE_SOCKET_ERR_AGAIN : try on next event, * NGX_HTTP_CLOJURE_SOCKET_ERR_WRITE : write error */ private static native long write(long s, Object buf, long off, long size); private static native void setTimeout(long s, long ctimeout, long rtimeout, long wtimeout); private static native long setTcpNoDelay(long s, long tcpNoDelay); private static native long getTcpNoDelay(long s); private static native long setSoKeepAlive(long s, long soKeepAlive); private static native long getSoKeepAlive(long s); private static native long getReadTimeout(long s); private static native long getWriteTimeout(long s); private static native long getConnectTimeout(long s); private static native long getReceiveBufferSize(long s); private static native long setReceiveBufferSize(long s, long size); /** * @param url byte[], eg. "192.168.2.12:8080".getBytes(); * @param off eg. BYTE_ARRAY_OFFSET * @param len length of url bytes */ private static native long connect(long s, Object url, long off, long len); private static native long bind(long s, Object addr, long off, long len); private static native void close(long s); private static native long shutdown(long s, long how); private static native long cancelSoftShutdown(long s, long how); }