/** * Copyright (C) Zhang,Yuexiang (xfeep) * */ package nginx.clojure; import java.io.IOException; import java.lang.management.ManagementFactory; import java.lang.reflect.Field; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.Callable; import java.util.concurrent.CompletionService; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import nginx.clojure.java.ArrayMap; import nginx.clojure.logger.LoggerService; import nginx.clojure.logger.TinyLogService; import nginx.clojure.net.NginxClojureSocketFactory; import nginx.clojure.net.NginxClojureSocketImpl; import nginx.clojure.wave.JavaAgent; import sun.misc.Unsafe; public class NginxClojureRT extends MiniConstants { public static long[] MEM_INDEX; public static Thread NGINX_MAIN_THREAD; /*use it carefully!!*/ public static Unsafe UNSAFE = HackUtils.UNSAFE; private static List<NginxHandler> HANDLERS = new ArrayList<NginxHandler>(); //mapping clojure code pointer address to clojure code id // private static Map<Long, Integer> CODE_MAP = new HashMap<Long, Integer>(); public static int MODE = MODE_DEFAULT; public static ConcurrentHashMap<Long, Object> POSTED_EVENTS_DATA = new ConcurrentHashMap<Long, Object>(); private static ExecutorService eventDispather; public static CompletionService<WorkerResponseContext> workers; public static ExecutorService workerExecutorService; //only for testing, e.g. with lein-ring where no coroutine support public static ExecutorService threadPoolOnlyForTestingUsage; public static boolean coroutineEnabled = false; public static LoggerService log; public static String processId="-1"; public native static long ngx_palloc(long pool, long size); public native static long ngx_pcalloc(long pool, long size); public native static long ngx_array_create(long pool, long n, long size); public native static long ngx_array_init(long array, long pool, long n, long size); public native static void ngx_array_destory(long array); public native static long ngx_array_push_n(long array, long n); public native static long ngx_list_create(long pool, long n, long size); public native static long ngx_list_init(long list, long pool, long n, long size); public native static long ngx_list_push(long list); public native static long ngx_create_temp_buf(long r, long size); public native static long ngx_create_temp_buf_by_jstring(long r, String s, int last_buf); public native static long ngx_create_temp_buf_by_obj(long r, Object obj, long offset, long len, int last_buf); public native static long ngx_create_file_buf(long r, long file, long name_len, int last_buf); public native static long ngx_http_set_content_type(long r); public native static long ngx_http_send_header(long r); public native static void ngx_http_clear_header_and_reset_ctx_phase(long r, long phase, boolean clearHeader); public static void ngx_http_clear_header_and_reset_ctx_phase(long r, long phase) { ngx_http_clear_header_and_reset_ctx_phase(r, phase, true); } public native static void ngx_http_ignore_next_response(long r); public native static long ngx_http_output_filter(long r, long chain); public native static void ngx_http_finalize_request(long r, long rc); public native static void ngx_http_filter_finalize_request(long r, long rc); /** * * @param r nginx http request * @param chain -1 means continue next header filter otherwise continue next body filter * @return */ public native static long ngx_http_filter_continue_next(long r, long chain); /** * last_buf can be either of {@link MiniConstants#NGX_CLOJURE_BUF_LAST_OF_NONE} {@link MiniConstants#NGX_CLOJURE_BUF_LAST_OF_CHAIN}, {@link MiniConstants#NGX_CLOJURE_BUF_LAST_OF_RESPONSE} */ public native static long ngx_http_clojure_mem_init_ngx_buf(long buf, Object obj, long offset, long len, int last_buf); public native static long ngx_http_clojure_mem_build_temp_chain(long req, long preChain, Object obj, long offset, long len); public native static long ngx_http_clojure_mem_build_file_chain(long req, long preChain, Object path, long offset, long len, boolean safe); public native static long ngx_http_clojure_mem_get_chain_info(long chain, Object buf, long offset, long len); public native static long ngx_http_clojure_mem_get_obj_addr(Object obj); public native static long ngx_http_clojure_mem_get_list_size(long l); public native static long ngx_http_clojure_mem_get_list_item(long l, long i); public native static long ngx_http_clojure_mem_get_headers_size(long header, int flag); public native static long ngx_http_clojure_mem_get_headers_items(long header, long i, int flag, Object buf, long off, long maxoff); public native static void ngx_http_clojure_mem_copy_to_obj(long src, Object obj, long offset, long len); public native static void ngx_http_clojure_mem_copy_to_addr(Object obj, long offset, long dest, long len); public native static void ngx_http_clojure_mem_shadow_copy_ngx_str(long s, long t); public native static long ngx_http_clojure_mem_copy_header_buf(long r, Object buf, long offset, long len); public native static long ngx_http_clojure_mem_get_header(long headers, Object buf, long nameOffset, long nameLen, long valueOffset, long bufMaxOffset); /** * It will return 0 if there's no request body . * It will return a value < 0 if there's request body file, -value is the length of the file path, and addr(buf, offset) is stored with the path data * It will return a value > 0 if there's a in-memory request body, value is the length of the body and addr(buf, offset) is stored with a address of the body data */ public native static long ngx_http_clojure_mem_get_request_body(long r, Object buf, long offset, long limit); public native static long ngx_http_clojure_mem_get_variable(long r, long name, long varlenPtr); public native static long ngx_http_clojure_mem_set_variable(long r, long name, long val, long vlen); /** * return the old value of r->count or error code (< 0) */ public native static long ngx_http_clojure_mem_inc_req_count(long r, long detal); public native static void ngx_http_clojure_mem_continue_current_phase(long r, long rc); public native static long ngx_http_clojure_mem_get_module_ctx_phase(long r); public native static long ngx_http_clojure_mem_get_module_ctx_upgrade(long r); public native static long ngx_http_clojure_mem_post_event(long e, Object data, long offset); public native static long ngx_http_clojure_mem_broadcast_event(long e, Object data, long offset, long hasSelf); public native static long ngx_http_clojure_mem_read_raw_pipe(long p, Object buf, long offset, long len); /** * @deprecated */ public static long ngx_http_cleanup_add(long r, final ChannelListener listener, Object data) { return ngx_http_clojure_add_listener(r, new ChannelCloseAdapter<Object>() { @Override public void onClose(Object data) throws IOException { listener.onClose(data); } }, data, 0); } private native static long ngx_http_clojure_add_listener(long r, ChannelListener listener, Object data, int replace); public static void addListener(NginxRequest r, ChannelListener listener, Object data, int replace) { addListener(r.nativeRequest(), listener, data, replace); } public static void addListener(long r, ChannelListener listener, Object data, int replace) { if ( ngx_http_clojure_add_listener(r, listener, data, replace) != 0) { throw new IllegalStateException("invalid request which is cleaned!"); } } public static long ngx_http_clojure_websocket_upgrade(long req) { return ngx_http_clojure_websocket_upgrade(req, 1); } /** * flag can be either of * 0 do nothing for non-websocket request * 1 error for non-websocket request */ public native static long ngx_http_clojure_websocket_upgrade(long req, int flag); /** * flag can be either of or combined of * 0 * NGX_HTTP_CLOJURE_EVENT_HANDLER_FLAG_READ 1 * NGX_HTTP_CLOJURE_EVENT_HANDLER_FLAG_WRITE 2 */ public native static void ngx_http_hijack_turn_on_event_handler(long req, int flag); public native static long ngx_http_hijack_read(long req, Object buf, long offset, long len); public native static long ngx_http_hijack_write(long req, Object buf, long offset, long len); /** * flag can be either of {@link MiniConstants#NGX_CLOJURE_BUF_FLUSH_FLAG} {@link MiniConstants#NGX_CLOJURE_BUF_LAST_FLAG} */ public native static long ngx_http_hijack_send(long req, Object buf, long offset, long len, int flag); /** * flag can be either of {@link MiniConstants#NGX_CLOJURE_BUF_FLUSH_FLAG} {@link MiniConstants#NGX_CLOJURE_BUF_LAST_FLAG} */ public native static long ngx_http_hijack_send_header(long req, int flag); public native static long ngx_http_hijack_send_header(long req, Object buf, long offset, long len, int flag); public native static long ngx_http_hijack_send_chain(long req, long chain, int flag); public native static void ngx_http_hijack_set_async_timeout(long req, long timeout); // public native static long ngx_http_clojure_mem_get_body_tmp_file(long r); private static AppEventListenerManager appEventListenerManager; // //for default or coroutine mode // private static ByteBuffer defaultByteBuffer; // private static CharBuffer defaultCharBuffer; //It was only for thread pool mode //But now we unify temp bufferes for thread pool mode & default & coroutine mode because maybe user can invoke some api in their own thread private final static ThreadLocal<ByteBuffer> threadLocalByteBuffers = new ThreadLocal<ByteBuffer>(); private final static ThreadLocal<CharBuffer> threadLocalCharBuffers = new ThreadLocal<CharBuffer>(); private final static ConcurrentLinkedQueue<HijackEvent> pooledEvents = new ConcurrentLinkedQueue<NginxClojureRT.HijackEvent>(); static { //be friendly to lein ring testing getLog(); initUnsafe(); appEventListenerManager = new AppEventListenerManager(); processId = ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; } public static AppEventListenerManager getAppEventListenerManager() { return appEventListenerManager; } public static void setAppEventListenerManager(AppEventListenerManager appEventListenerManager) { NginxClojureRT.appEventListenerManager = appEventListenerManager; } public static String formatVer(long ver) { long f = ver / 1000000; long s = ver / 1000 - f * 1000; long t = ver - s * 1000 - f * 1000000; return f + "." + s + "." + t; } public static final class WorkerResponseContext { public final NginxResponse response; public final NginxRequest request; public long chain; public WorkerResponseContext(NginxResponse resp, NginxRequest req) { super(); this.response = resp; this.request = req; if (resp.type() >= 0) { if (req.isReleased()) { chain = 0; }else { chain = req.handler().buildOutputChain(resp); } }else { if (resp.type() == NginxResponse.TYPE_FAKE_BODY_FILTER_TAG) { // chain = req.handler().buildOutputChain(resp); chain = 0; }else { chain = 0; } } } } public static class HijackEvent { protected NginxHttpServerChannel channel; protected Object message; //maybe NginxResponse or for complex return value protected volatile long offset; //maybe chain of response or also as simple return value protected int len; protected int flag; protected Semaphore semaphore; public HijackEvent() { semaphore = new Semaphore(0); } public HijackEvent reset(NginxHttpServerChannel channel, Object message, long off, int len, int flag) { this.channel = channel; this.message = message; this.offset = off; this.len = len; this.flag = flag; return this; } public HijackEvent reset(NginxHttpServerChannel channel, NginxResponse response, long chain) { this.channel = channel; this.message = response; this.offset = chain; return this; } public boolean awaitForFinish(long timeout) throws InterruptedException { return semaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS); } public void awaitForFinish() throws InterruptedException { semaphore.acquire(); } public void complete(long v) { this.offset = v; semaphore.release(); } public void complete(Object v) { this.message = v; semaphore.release(); } public void recycle() { channel = null; message = null; semaphore.drainPermits(); } } public static final class EventDispatherRunnable implements Runnable { final CompletionService<WorkerResponseContext> workers; public EventDispatherRunnable(final CompletionService<WorkerResponseContext> workers) { this.workers = workers; } @Override public void run() { while (true) { try { Future<WorkerResponseContext> respFuture = workers.take(); WorkerResponseContext ctx = respFuture.get(); if (ctx.response.type() == NginxResponse.TYPE_FAKE_ASYNC_TAG) { continue; } long r = ctx.response.request().nativeRequest(); savePostEventData(r, ctx); ngx_http_clojure_mem_post_event(r, null, 0); } catch (InterruptedException e) { log.warn("jvm workers dispather has been interrupted!"); break; } catch (ExecutionException e) { log.error("unexpected ExecutionException!", e); }catch (Throwable e) { log.error("unexpected Error!", e); } } } } public static void savePostEventData(long id, Object o) { while (POSTED_EVENTS_DATA.putIfAbsent(id, o) != null) { try { Thread.sleep(0); } catch (InterruptedException e) { Thread.currentThread().interrupt(); log.warn("savePostEventData interrupted!"); return; } } } private static void initWorkers(int n) { if (JavaAgent.db != null) { if (JavaAgent.db.isDoNothing()) { coroutineEnabled = false; log.warn("java agent disabled so we turn off coroutine support!"); if (n == 0) { n = -1; } }else if (JavaAgent.db.isRunTool()) { coroutineEnabled = false; log.warn("we just run for generatation of coroutine waving configuration NOT for general cases!!!"); /* * Because sometimes we need to access services provide by the same nginx instance, * e.g. proxyed external http service, so when turn on run tool mode we need thread * pool to make worker not blocked otherwise we can not continue the process of generatation * of coroutine waving configuration.*/ if (n < 0) { log.warn("enable thread pool mode for run tool mode so that %s", "worker won't be blocked when access services provide by the same nginx instance"); n = Runtime.getRuntime().availableProcessors() * 2; } }else { log.info("java agent configured so we turn on coroutine support!"); if (n > 0) { log.warn("found jvm_workers = %d, and not = 0 we just ignored!", n); } n = 0; } } if (n == 0) { if (JavaAgent.db == null) { log.warn("java agent NOT configured so we turn off coroutine support!"); coroutineEnabled = false; }else { coroutineEnabled = true; MODE = MODE_COROUTINE; try { Socket.setSocketImplFactory(new NginxClojureSocketFactory()); } catch (IOException e) { throw new RuntimeException("can not init NginxClojureSocketFactory!", e); } } // defaultByteBuffer = ByteBuffer.allocate(NGINX_CLOJURE_CORE_CLIENT_HEADER_MAX_SIZE); // defaultCharBuffer = CharBuffer.allocate(NGINX_CLOJURE_CORE_CLIENT_HEADER_MAX_SIZE); return; } if (n < 0) { // defaultByteBuffer = ByteBuffer.allocate(NGINX_CLOJURE_CORE_CLIENT_HEADER_MAX_SIZE); // defaultCharBuffer = CharBuffer.allocate(NGINX_CLOJURE_CORE_CLIENT_HEADER_MAX_SIZE); return; } log.info("nginx-clojure run on thread pool mode, coroutineEnabled=false"); MODE = MODE_THREAD; // threadLocalByteBuffers = new ThreadLocal<ByteBuffer>(); // threadLocalCharBuffers = new ThreadLocal<CharBuffer>(); eventDispather = Executors.newSingleThreadExecutor(new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, "nginx-clojure-eventDispather"); } }); workers = new ExecutorCompletionService<WorkerResponseContext>(workerExecutorService = Executors.newFixedThreadPool(n, new ThreadFactory() { final AtomicLong counter = new AtomicLong(0); public Thread newThread(Runnable r) { return new Thread(r, "nginx-clojure-worker-" + counter.getAndIncrement()); } })); eventDispather.submit(new EventDispatherRunnable(workers)); } private static void destoryWorkers() { if (workerExecutorService != null) { workerExecutorService.shutdown(); try { workerExecutorService.awaitTermination(1000, TimeUnit.SECONDS); } catch (InterruptedException e) { getLog().error("shutdown workerExecutorService error", e); } } if (eventDispather != null) { eventDispather.shutdownNow(); } if (threadPoolOnlyForTestingUsage != null) { threadPoolOnlyForTestingUsage.shutdownNow(); } workerExecutorService = null; eventDispather = null; threadPoolOnlyForTestingUsage = null; } public static synchronized ExecutorService initThreadPoolOnlyForTestingUsage() { if (threadPoolOnlyForTestingUsage == null) { threadPoolOnlyForTestingUsage = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()+2, new ThreadFactory() { final AtomicLong counter = new AtomicLong(0); public Thread newThread(Runnable r) { return new Thread(r, "nginx-clojure-only4test-thread" + counter.getAndIncrement()); } }); } return threadPoolOnlyForTestingUsage; } private static NginxHeaderHolder safeBuildKnownTableEltHeaderHolder(String name, long offset, long headersOffset) { if (offset >= 0) { return new TableEltHeaderHolder(name, offset, headersOffset); } return new UnknownHeaderHolder(name, headersOffset); } public static void initStringAddrMapsByNativeAddr(Map<String, Long> map, long addr) { while (true) { String var = fetchNGXString(addr, DEFAULT_ENCODING); if (var == null) { break; } map.put(var, addr); addr += NGX_HTTP_CLOJURE_STR_SIZE; } } private static synchronized void initMemIndex(long idxpt) { getLog(); initUnsafe(); //hack mysql jdbc driver to keep from creating connections by reflective invoking the constructor try { Class mysqljdbcUtilClz = Thread.currentThread().getContextClassLoader().loadClass("com.mysql.jdbc.Util"); Field isJdbc4Field = mysqljdbcUtilClz.getDeclaredField("isJdbc4"); isJdbc4Field.setAccessible(true); isJdbc4Field.set(null, false); } catch (Throwable e) { } NGINX_MAIN_THREAD = Thread.currentThread(); BYTE_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); try { STRING_CHAR_ARRAY_OFFSET = UNSAFE.objectFieldOffset(String.class.getDeclaredField("value")); } catch (Throwable e) { // never happen! UNSAFE.throwException(e); } long[] index = new long[NGX_HTTP_CLOJURE_MEM_IDX_END + 1]; for (int i = 0; i < NGX_HTTP_CLOJURE_MEM_IDX_END + 1; i++) { index[i] = UNSAFE.getLong(idxpt + i * 8); } MEM_INDEX = index; NGX_HTTP_CLOJURE_UINT_SIZE = MEM_INDEX[NGX_HTTP_CLOJURE_UINT_SIZE_IDX]; NGX_HTTP_CLOJURE_PTR_SIZE = MEM_INDEX[NGX_HTTP_CLOJURE_PTR_SIZE_IDX]; NGX_HTTP_CLOJURE_STR_SIZE = MEM_INDEX[NGX_HTTP_CLOJURE_STR_SIZE_IDX]; NGX_HTTP_CLOJURE_STR_LEN_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_STR_LEN_IDX]; NGX_HTTP_CLOJURE_STR_DATA_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_STR_DATA_IDX]; NGX_HTTP_CLOJURE_SIZET_SIZE = MEM_INDEX[NGX_HTTP_CLOJURE_SIZET_SIZE_IDX]; NGX_HTTP_CLOJURE_OFFT_SIZE = MEM_INDEX[NGX_HTTP_CLOJURE_OFFT_SIZE_IDX]; NGX_HTTP_CLOJURE_BUFFER_SIZE = MEM_INDEX[NGX_HTTP_CLOJURE_BUFFER_SIZE_IDX]; NGINX_CLOJURE_CORE_CLIENT_HEADER_MAX_SIZE = (int) NGX_HTTP_CLOJURE_BUFFER_SIZE; NGINX_CLOJURE_CORE_CLIENT_HEADER_MAX_LINE_SIZE = Math.max(NGINX_CLOJURE_CORE_CLIENT_HEADER_MAX_SIZE/2, NGINX_CLOJURE_CORE_CLIENT_HEADER_MAX_SIZE-1024); NGX_HTTP_CLOJURE_TELT_SIZE = MEM_INDEX[NGX_HTTP_CLOJURE_TELT_SIZE_IDX]; NGX_HTTP_CLOJURE_TEL_HASH_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_TEL_HASH_IDX]; NGX_HTTP_CLOJURE_TEL_KEY_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_TEL_KEY_IDX]; NGX_HTTP_CLOJURE_TEL_VALUE_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_TEL_VALUE_IDX]; NGX_HTTP_CLOJURE_TEL_LOWCASE_KEY_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_TEL_LOWCASE_KEY_IDX]; NGX_HTTP_CLOJURE_REQT_SIZE = MEM_INDEX[NGX_HTTP_CLOJURE_REQT_SIZE_IDX]; NGX_HTTP_CLOJURE_REQ_METHOD_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_REQ_METHOD_IDX]; NGX_HTTP_CLOJURE_REQ_URI_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_REQ_URI_IDX]; NGX_HTTP_CLOJURE_REQ_ARGS_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_REQ_ARGS_IDX]; NGX_HTTP_CLOJURE_REQ_HEADERS_IN_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_REQ_HEADERS_IN_IDX]; NGX_HTTP_CLOJURE_REQ_POOL_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_REQ_POOL_IDX]; NGX_HTTP_CLOJURE_REQ_HEADERS_OUT_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_REQ_HEADERS_OUT_IDX]; NGX_HTTP_CLOJURE_CHAINT_SIZE = MEM_INDEX[NGX_HTTP_CLOJURE_CHAINT_SIZE_IDX]; NGX_HTTP_CLOJURE_CHAIN_BUF_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_CHAIN_BUF_IDX]; NGX_HTTP_CLOJURE_CHAIN_NEXT_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_CHAIN_NEXT_IDX]; NGX_HTTP_CLOJURE_VARIABLET_SIZE = MEM_INDEX[NGX_HTTP_CLOJURE_VARIABLET_SIZE_IDX]; NGX_HTTP_CLOJURE_CORE_VARIABLES_ADDR = MEM_INDEX[NGX_HTTP_CLOJURE_CORE_VARIABLES_ADDR_IDX]; NGX_HTTP_CLOJURE_HEADERS_NAMES_ADDR = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERS_NAMES_ADDR_IDX]; NGX_HTTP_CLOJURE_ARRAYT_SIZE = MEM_INDEX[NGX_HTTP_CLOJURE_ARRAYT_SIZE_IDX]; NGX_HTTP_CLOJURE_ARRAY_ELTS_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_ARRAY_ELTS_IDX]; NGX_HTTP_CLOJURE_ARRAY_NELTS_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_ARRAY_NELTS_IDX]; NGX_HTTP_CLOJURE_ARRAY_SIZE_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_ARRAY_SIZE_IDX]; NGX_HTTP_CLOJURE_ARRAY_NALLOC_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_ARRAY_NALLOC_IDX]; NGX_HTTP_CLOJURE_ARRAY_POOL_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_ARRAY_POOL_IDX]; NGX_HTTP_CLOJURE_KEYVALT_SIZE = MEM_INDEX[NGX_HTTP_CLOJURE_KEYVALT_SIZE_IDX]; NGX_HTTP_CLOJURE_KEYVALT_KEY_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_KEYVALT_KEY_IDX]; NGX_HTTP_CLOJURE_KEYVALT_VALUE_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_KEYVALT_VALUE_IDX]; NGX_HTTP_CLOJURE_MIME_TYPES_ADDR = MEM_INDEX[NGX_HTTP_CLOJURE_MIME_TYPES_ADDR_IDX]; NGX_HTTP_CLOJURE_HEADERSIT_SIZE = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSIT_SIZE_IDX]; NGX_HTTP_CLOJURE_HEADERSI_HOST_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_HOST_IDX]; NGX_HTTP_CLOJURE_HEADERSI_CONNECTION_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_CONNECTION_IDX]; NGX_HTTP_CLOJURE_HEADERSI_IF_MODIFIED_SINCE_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_IF_MODIFIED_SINCE_IDX]; NGX_HTTP_CLOJURE_HEADERSI_IF_UNMODIFIED_SINCE_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_IF_UNMODIFIED_SINCE_IDX]; NGX_HTTP_CLOJURE_HEADERSI_USER_AGENT_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_USER_AGENT_IDX]; NGX_HTTP_CLOJURE_HEADERSI_REFERER_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_REFERER_IDX]; NGX_HTTP_CLOJURE_HEADERSI_CONTENT_LENGTH_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_CONTENT_LENGTH_IDX]; NGX_HTTP_CLOJURE_HEADERSI_CONTENT_TYPE_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_CONTENT_TYPE_IDX]; NGX_HTTP_CLOJURE_HEADERSI_RANGE_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_RANGE_IDX]; NGX_HTTP_CLOJURE_HEADERSI_IF_RANGE_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_IF_RANGE_IDX]; NGX_HTTP_CLOJURE_HEADERSI_TRANSFER_ENCODING_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_TRANSFER_ENCODING_IDX]; NGX_HTTP_CLOJURE_HEADERSI_EXPECT_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_EXPECT_IDX]; //#if (NGX_HTTP_GZIP) NGX_HTTP_CLOJURE_HEADERSI_ACCEPT_ENCODING_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_ACCEPT_ENCODING_IDX]; NGX_HTTP_CLOJURE_HEADERSI_VIA_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_VIA_IDX]; //#endif NGX_HTTP_CLOJURE_HEADERSI_AUTHORIZATION_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_AUTHORIZATION_IDX]; NGX_HTTP_CLOJURE_HEADERSI_KEEP_ALIVE_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_KEEP_ALIVE_IDX]; //#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP || NGX_HTTP_GEO) NGX_HTTP_CLOJURE_HEADERSI_X_FORWARDED_FOR_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_X_FORWARDED_FOR_IDX]; //#endif //#if (NGX_HTTP_REALIP) NGX_HTTP_CLOJURE_HEADERSI_X_REAL_IP_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_X_REAL_IP_IDX]; //#endif //#if (NGX_HTTP_HEADERS) NGX_HTTP_CLOJURE_HEADERSI_ACCEPT_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_ACCEPT_IDX]; NGX_HTTP_CLOJURE_HEADERSI_ACCEPT_LANGUAGE_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_ACCEPT_LANGUAGE_IDX]; //#endif //#if (NGX_HTTP_DAV) NGX_HTTP_CLOJURE_HEADERSI_DEPTH_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_DEPTH_IDX]; NGX_HTTP_CLOJURE_HEADERSI_DESTINATION_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_DESTINATION_IDX]; NGX_HTTP_CLOJURE_HEADERSI_OVERWRITE_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_OVERWRITE_IDX]; NGX_HTTP_CLOJURE_HEADERSI_DATE_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_DATE_IDX]; //#endif NGX_HTTP_CLOJURE_HEADERSI_USER_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_USER_IDX]; NGX_HTTP_CLOJURE_HEADERSI_PASSWD_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_PASSWD_IDX]; NGX_HTTP_CLOJURE_HEADERSI_COOKIE_OFFSET =MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_COOKIE_IDX]; NGX_HTTP_CLOJURE_HEADERSI_SERVER_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_SERVER_IDX]; NGX_HTTP_CLOJURE_HEADERSI_CONTENT_LENGTH_N_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_CONTENT_LENGTH_N_IDX]; NGX_HTTP_CLOJURE_HEADERSI_KEEP_ALIVE_N_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_KEEP_ALIVE_N_IDX]; NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSI_HEADERS_IDX]; /*index for size of ngx_http_headers_out_t */ NGX_HTTP_CLOJURE_HEADERSOT_SIZE = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSOT_SIZE_IDX]; /*field offset index for ngx_http_headers_out_t*/ NGX_HTTP_CLOJURE_HEADERSO_STATUS_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_STATUS_IDX]; NGX_HTTP_CLOJURE_HEADERSO_STATUS_LINE_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_STATUS_LINE_IDX]; NGX_HTTP_CLOJURE_HEADERSO_SERVER_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_SERVER_IDX]; NGX_HTTP_CLOJURE_HEADERSO_DATE_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_DATE_IDX]; NGX_HTTP_CLOJURE_HEADERSO_CONTENT_LENGTH_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_CONTENT_LENGTH_IDX]; NGX_HTTP_CLOJURE_HEADERSO_CONTENT_ENCODING_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_CONTENT_ENCODING_IDX]; NGX_HTTP_CLOJURE_HEADERSO_LOCATION_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_LOCATION_IDX]; NGX_HTTP_CLOJURE_HEADERSO_REFRESH_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_REFRESH_IDX]; NGX_HTTP_CLOJURE_HEADERSO_LAST_MODIFIED_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_LAST_MODIFIED_IDX]; NGX_HTTP_CLOJURE_HEADERSO_CONTENT_RANGE_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_CONTENT_RANGE_IDX]; NGX_HTTP_CLOJURE_HEADERSO_ACCEPT_RANGES_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_ACCEPT_RANGES_IDX]; NGX_HTTP_CLOJURE_HEADERSO_WWW_AUTHENTICATE_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_WWW_AUTHENTICATE_IDX]; NGX_HTTP_CLOJURE_HEADERSO_EXPIRES_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_EXPIRES_IDX]; NGX_HTTP_CLOJURE_HEADERSO_ETAG_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_ETAG_IDX]; NGX_HTTP_CLOJURE_HEADERSO_OVERRIDE_CHARSET_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_OVERRIDE_CHARSET_IDX]; NGX_HTTP_CLOJURE_HEADERSO_CONTENT_TYPE_LEN_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_CONTENT_TYPE_LEN_IDX]; NGX_HTTP_CLOJURE_HEADERSO_CONTENT_TYPE_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_CONTENT_TYPE_IDX]; NGX_HTTP_CLOJURE_HEADERSO_CHARSET_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_CHARSET_IDX]; NGX_HTTP_CLOJURE_HEADERSO_CONTENT_TYPE_LOWCASE_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_CONTENT_TYPE_LOWCASE_IDX]; NGX_HTTP_CLOJURE_HEADERSO_CONTENT_TYPE_HASH_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_CONTENT_TYPE_HASH_IDX]; NGX_HTTP_CLOJURE_HEADERSO_CACHE_CONTROL_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_CACHE_CONTROL_IDX]; NGX_HTTP_CLOJURE_HEADERSO_CONTENT_LENGTH_N_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_CONTENT_LENGTH_N_IDX]; NGX_HTTP_CLOJURE_HEADERSO_DATE_TIME_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_DATE_TIME_IDX]; NGX_HTTP_CLOJURE_HEADERSO_LAST_MODIFIED_TIME_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_LAST_MODIFIED_TIME_IDX]; NGX_HTTP_CLOJURE_HEADERSO_HEADERS_OFFSET = MEM_INDEX[NGX_HTTP_CLOJURE_HEADERSO_HEADERS_IDX]; // NGINX_CLOJURE_MODULE_CTX_PHRASE_ID_OFFSET = MEM_INDEX[NGINX_CLOJURE_MODULE_CTX_PHRASE_ID]; NGX_WORKER_PROCESSORS_NUM = MEM_INDEX[NGX_WORKER_PROCESSORS_NUM_ID]; NGINX_CLOJURE_RT_WORKERS = MEM_INDEX[NGINX_CLOJURE_RT_WORKERS_ID]; NGINX_CLOJURE_VER = MEM_INDEX[NGINX_CLOJURE_VER_ID]; NGINX_VER = MEM_INDEX[NGINX_VER_ID]; //now we not use final static to keep it from optimizing to constant integer if (NGINX_CLOJURE_RT_REQUIRED_LVER > NGINX_CLOJURE_VER) { throw new IllegalStateException("NginxClojureRT required version is >=" + formatVer(NGINX_CLOJURE_RT_REQUIRED_LVER) + ", but here is " + formatVer(NGINX_CLOJURE_VER)); } NGINX_CLOJURE_FULL_VER = "nginx-clojure/" + formatVer(NGINX_VER) + "-" + formatVer(NGINX_CLOJURE_RT_VER); KNOWN_REQ_HEADERS.put("Host", safeBuildKnownTableEltHeaderHolder("Host", NGX_HTTP_CLOJURE_HEADERSI_HOST_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("Connection", safeBuildKnownTableEltHeaderHolder("Connection", NGX_HTTP_CLOJURE_HEADERSI_CONNECTION_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("If-Modified-Since",safeBuildKnownTableEltHeaderHolder("If-Modified-Since", NGX_HTTP_CLOJURE_HEADERSI_IF_MODIFIED_SINCE_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("If-Unmodified-Since", safeBuildKnownTableEltHeaderHolder("If-Unmodified-Since", NGX_HTTP_CLOJURE_HEADERSI_IF_UNMODIFIED_SINCE_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("User-Agent", safeBuildKnownTableEltHeaderHolder("User-Agent", NGX_HTTP_CLOJURE_HEADERSI_USER_AGENT_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("Referer", safeBuildKnownTableEltHeaderHolder("Referer", NGX_HTTP_CLOJURE_HEADERSI_REFERER_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("Content-Length", new OffsetHeaderHolder("Content-Length", NGX_HTTP_CLOJURE_HEADERSI_CONTENT_LENGTH_N_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("Content-Type", safeBuildKnownTableEltHeaderHolder("Content-Type", NGX_HTTP_CLOJURE_HEADERSI_CONTENT_TYPE_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("Range", safeBuildKnownTableEltHeaderHolder("Range", NGX_HTTP_CLOJURE_HEADERSI_RANGE_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("If-Range", safeBuildKnownTableEltHeaderHolder("If-Range", NGX_HTTP_CLOJURE_HEADERSI_IF_RANGE_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("Transfer-Encoding", safeBuildKnownTableEltHeaderHolder("Transfer-Encoding", NGX_HTTP_CLOJURE_HEADERSI_TRANSFER_ENCODING_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("Expect", safeBuildKnownTableEltHeaderHolder("Expect", NGX_HTTP_CLOJURE_HEADERSI_EXPECT_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("Accept-Encoding", safeBuildKnownTableEltHeaderHolder("Accept-Encoding", NGX_HTTP_CLOJURE_HEADERSI_ACCEPT_ENCODING_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("Via", safeBuildKnownTableEltHeaderHolder("Via", NGX_HTTP_CLOJURE_HEADERSI_VIA_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("Authorization", safeBuildKnownTableEltHeaderHolder("Authorization", NGX_HTTP_CLOJURE_HEADERSI_AUTHORIZATION_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("Keep-Alive", safeBuildKnownTableEltHeaderHolder("Keep-Alive", NGX_HTTP_CLOJURE_HEADERSI_KEEP_ALIVE_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("X-Forwarded-For", new ArrayHeaderHolder("X-Forwarded-For", NGX_HTTP_CLOJURE_HEADERSI_X_FORWARDED_FOR_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("X-Real-Ip", safeBuildKnownTableEltHeaderHolder("X-Real-Ip", NGX_HTTP_CLOJURE_HEADERSI_X_REAL_IP_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("Accept", safeBuildKnownTableEltHeaderHolder("Accept", NGX_HTTP_CLOJURE_HEADERSI_ACCEPT_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("Accept-Language", safeBuildKnownTableEltHeaderHolder("Accept-Language", NGX_HTTP_CLOJURE_HEADERSI_ACCEPT_LANGUAGE_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("Depth", safeBuildKnownTableEltHeaderHolder("Depth", NGX_HTTP_CLOJURE_HEADERSI_DEPTH_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("Destination", safeBuildKnownTableEltHeaderHolder("Destination", NGX_HTTP_CLOJURE_HEADERSI_DESTINATION_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("Overwrite", safeBuildKnownTableEltHeaderHolder("Overwrite", NGX_HTTP_CLOJURE_HEADERSI_OVERWRITE_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("Date", safeBuildKnownTableEltHeaderHolder("Date", NGX_HTTP_CLOJURE_HEADERSI_DATE_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); KNOWN_REQ_HEADERS.put("Cookie", new ArrayHeaderHolder("Cookie", NGX_HTTP_CLOJURE_HEADERSI_COOKIE_OFFSET, NGX_HTTP_CLOJURE_HEADERSI_HEADERS_OFFSET)); /*temp setting only for CORE_VARS initialization*/ // defaultByteBuffer = ByteBuffer.allocate(NGINX_CLOJURE_CORE_CLIENT_HEADER_MAX_SIZE); // defaultCharBuffer = CharBuffer.allocate(NGINX_CLOJURE_CORE_CLIENT_HEADER_MAX_SIZE); initStringAddrMapsByNativeAddr(CORE_VARS, NGX_HTTP_CLOJURE_CORE_VARIABLES_ADDR); initStringAddrMapsByNativeAddr(HEADERS_NAMES, NGX_HTTP_CLOJURE_HEADERS_NAMES_ADDR); initStringAddrMapsByNativeAddr(MIME_TYPES, NGX_HTTP_CLOJURE_MIME_TYPES_ADDR); SERVER_PORT_FETCHER = new RequestKnownNameVarFetcher("server_port"); SERVER_NAME_FETCHER = new RequestKnownNameVarFetcher("server_name"); REMOTE_ADDR_FETCHER = new RequestKnownNameVarFetcher("remote_addr"); URI_FETCHER = new RequestKnownOffsetVarFetcher(NGX_HTTP_CLOJURE_REQ_URI_OFFSET); QUERY_STRING_FETCHER = new RequestKnownOffsetVarFetcher(NGX_HTTP_CLOJURE_REQ_ARGS_OFFSET); SCHEME_FETCHER = new RequestKnownNameVarFetcher("scheme"); REQUEST_METHOD_FETCHER = new RequestMethodStrFetcher(); CONTENT_TYPE_FETCHER = new RequestKnownHeaderFetcher("Content-Type"); CHARACTER_ENCODING_FETCHER = new RequestCharacterEncodingFetcher(); // HEADER_FETCHER = new RequestHeadersFetcher(); BODY_FETCHER = new RequestBodyFetcher(); KNOWN_RESP_HEADERS.put("Server", safeBuildKnownTableEltHeaderHolder("Server", NGX_HTTP_CLOJURE_HEADERSO_SERVER_OFFSET, NGX_HTTP_CLOJURE_HEADERSO_HEADERS_OFFSET)); KNOWN_RESP_HEADERS.put("Date", safeBuildKnownTableEltHeaderHolder("Date", NGX_HTTP_CLOJURE_HEADERSO_DATE_OFFSET, NGX_HTTP_CLOJURE_HEADERSO_HEADERS_OFFSET)); KNOWN_RESP_HEADERS.put("Content-Encoding", safeBuildKnownTableEltHeaderHolder("Content-Encoding", NGX_HTTP_CLOJURE_HEADERSO_CONTENT_ENCODING_OFFSET, NGX_HTTP_CLOJURE_HEADERSO_HEADERS_OFFSET)); KNOWN_RESP_HEADERS.put("Location", safeBuildKnownTableEltHeaderHolder("Location", NGX_HTTP_CLOJURE_HEADERSO_LOCATION_OFFSET, NGX_HTTP_CLOJURE_HEADERSO_HEADERS_OFFSET)); KNOWN_RESP_HEADERS.put("Refresh", safeBuildKnownTableEltHeaderHolder("Refresh", NGX_HTTP_CLOJURE_HEADERSO_REFRESH_OFFSET, NGX_HTTP_CLOJURE_HEADERSO_HEADERS_OFFSET)); KNOWN_RESP_HEADERS.put("Last-Modified", safeBuildKnownTableEltHeaderHolder("Last-Modified", NGX_HTTP_CLOJURE_HEADERSO_LAST_MODIFIED_OFFSET, NGX_HTTP_CLOJURE_HEADERSO_HEADERS_OFFSET)); KNOWN_RESP_HEADERS.put("Content-Range", safeBuildKnownTableEltHeaderHolder("Content-Range", NGX_HTTP_CLOJURE_HEADERSO_CONTENT_RANGE_OFFSET, NGX_HTTP_CLOJURE_HEADERSO_HEADERS_OFFSET)); KNOWN_RESP_HEADERS.put("Accept-Ranges", safeBuildKnownTableEltHeaderHolder("Accept-Ranges", NGX_HTTP_CLOJURE_HEADERSO_ACCEPT_RANGES_OFFSET, NGX_HTTP_CLOJURE_HEADERSO_HEADERS_OFFSET)); KNOWN_RESP_HEADERS.put("WWW-Authenticate", safeBuildKnownTableEltHeaderHolder("WWW-Authenticate", NGX_HTTP_CLOJURE_HEADERSO_WWW_AUTHENTICATE_OFFSET, NGX_HTTP_CLOJURE_HEADERSO_HEADERS_OFFSET)); KNOWN_RESP_HEADERS.put("Expires", safeBuildKnownTableEltHeaderHolder("Expires", NGX_HTTP_CLOJURE_HEADERSO_EXPIRES_OFFSET, NGX_HTTP_CLOJURE_HEADERSO_HEADERS_OFFSET)); KNOWN_RESP_HEADERS.put("Etag", safeBuildKnownTableEltHeaderHolder("Etag", NGX_HTTP_CLOJURE_HEADERSO_ETAG_OFFSET, NGX_HTTP_CLOJURE_HEADERSO_HEADERS_OFFSET)); KNOWN_RESP_HEADERS.put("Cache-Control", new ArrayHeaderHolder("Cache-Control", NGX_HTTP_CLOJURE_HEADERSO_CACHE_CONTROL_OFFSET, NGX_HTTP_CLOJURE_HEADERSO_HEADERS_OFFSET)); KNOWN_RESP_HEADERS.put("Content-Type", RESP_CONTENT_TYPE_HOLDER = new ResponseContentTypeHolder()); KNOWN_RESP_HEADERS.put("Content-Length", new OffsetHeaderHolder("Content-Length", NGX_HTTP_CLOJURE_HEADERSO_CONTENT_LENGTH_N_OFFSET, NGX_HTTP_CLOJURE_HEADERSO_HEADERS_OFFSET) ); /*clear all to let initWorkers initializing them correctly*/ // defaultByteBuffer = null; // defaultCharBuffer = null; initWorkers((int)NGINX_CLOJURE_RT_WORKERS); //set system properties for build-in nginx handler factories System.setProperty(NginxHandlerFactory.NGINX_CLOJURE_HANDLER_FACTORY_SYSTEM_PROPERTY_PREFIX + "java", "nginx.clojure.java.NginxJavaHandlerFactory"); System.setProperty(NginxHandlerFactory.NGINX_CLOJURE_HANDLER_FACTORY_SYSTEM_PROPERTY_PREFIX + "clojure", "nginx.clojure.clj.NginxClojureHandlerFactory"); System.setProperty(NginxHandlerFactory.NGINX_CLOJURE_HANDLER_FACTORY_SYSTEM_PROPERTY_PREFIX + "groovy", "nginx.clojure.groovy.NginxGroovyHandlerFactory"); } private static synchronized void destoryMemIndex() { destoryWorkers(); MEM_INDEX = null; } public static void initUnsafe() { if (UNSAFE != null) { return; } UNSAFE = HackUtils.UNSAFE; } /** * DO NOT use this method for frequent invoking because it is slow and not optimized. */ public static String evalSimpleExp(String v, Map<String, String> vars) { int p = v.indexOf("#{"); if (p > -1) { int s = 0; StringBuilder sb = new StringBuilder(); while (p > -1) { if (p != s) { sb.append(v.substring(s, p)); } s = v.indexOf('}', p); if (s < 0) { sb.append(v.substring(p)); break; } String ek = v.substring(p+2, s); String ev = vars.get(ek); if (ev == null) { ev = vars.get("system." + ek); if (ev == null) { ev = System.getProperty(ek); } } sb.append(ev); s++; p = v.indexOf("#{", s); } if (p < 0 && s != v.length()) { sb.append(v.substring(s)); } return sb.toString(); } return v; } public static synchronized int registerCode(int phase, long typeNStr, long nameNStr, long codeNStr, long pros) { // if (CODE_MAP.containsKey(codeNStr)) { // return CODE_MAP.get(codeNStr); // } // // if (CODE_MAP.containsKey(nameNStr)) { // return CODE_MAP.get(nameNStr); // } String type = fetchNGXString(typeNStr, DEFAULT_ENCODING); String name = fetchNGXString(nameNStr, DEFAULT_ENCODING); String code = fetchNGXString(codeNStr, DEFAULT_ENCODING); NginxHandler handler = NginxHandlerFactory.fetchHandler(phase, type, name, code); HANDLERS.add(handler); if (pros != 0) { Map<String, String> properties = new ArrayMap<String, String>(); int size = fetchNGXInt(pros + NGX_HTTP_CLOJURE_ARRAY_NELTS_OFFSET); long ele = UNSAFE.getAddress(pros + NGX_HTTP_CLOJURE_ARRAY_ELTS_OFFSET); for (int i = 0; i < size; i++) { long kv = ele + i * NGX_HTTP_CLOJURE_KEYVALT_SIZE; properties.put(fetchNGXString(kv + NGX_HTTP_CLOJURE_KEYVALT_KEY_OFFSET, DEFAULT_ENCODING), fetchNGXString(kv + NGX_HTTP_CLOJURE_KEYVALT_VALUE_OFFSET, DEFAULT_ENCODING)); } for (Entry<String, String> en : properties.entrySet()) { en.setValue(evalSimpleExp(en.getValue(), properties)); } if (handler instanceof Configurable) { Configurable cr = (Configurable) handler; cr.config(properties); }else { log.warn("%s is not an instance of nginx.clojure.Configurable, so properties will be ignored!", handler.getClass()); } } return HANDLERS.size() - 1; } /** * convert ngx_str_t to java String */ public static final String fetchNGXString(long address, Charset encoding) { if (address == 0){ return null; } long lenAddr = address + NGX_HTTP_CLOJURE_STR_LEN_OFFSET; int len = fetchNGXInt(lenAddr); if (len <= 0){ return null; } return fetchString(address + NGX_HTTP_CLOJURE_STR_DATA_OFFSET, len, encoding); } /** * convert ngx_str_t to java String */ public static final String fetchNGXString(long address, Charset encoding, ByteBuffer bb, CharBuffer cb) { if (address == 0){ return null; } long lenAddr = address + NGX_HTTP_CLOJURE_STR_LEN_OFFSET; int len = fetchNGXInt(lenAddr); if (len <= 0){ return null; } return fetchString(address + NGX_HTTP_CLOJURE_STR_DATA_OFFSET, len, encoding, bb, cb); } public static final int pushNGXString(long address, String val, Charset encoding, long pool){ long lenAddr = address + NGX_HTTP_CLOJURE_STR_LEN_OFFSET; long dataAddr = address + NGX_HTTP_CLOJURE_STR_DATA_OFFSET; if (val == null) { UNSAFE.putAddress(dataAddr, 0); pushNGXInt(lenAddr, 0); return 0; }else { int len = pushString(dataAddr, val, encoding, pool); pushNGXInt(lenAddr, len); return len; } } public static final int pushNGXLowcaseString(long address, String val, Charset encoding, long pool){ long lenAddr = address + NGX_HTTP_CLOJURE_STR_LEN_OFFSET; long dataAddr = address + NGX_HTTP_CLOJURE_STR_DATA_OFFSET; int len = pushLowcaseString(dataAddr, val, encoding, pool); pushNGXInt(lenAddr, len); return len; } public static final int fetchNGXInt(long address){ return NGX_HTTP_CLOJURE_UINT_SIZE == 4 ? UNSAFE.getInt(address) : (int)UNSAFE.getLong(address); } public static final void pushNGXInt(long address, int val){ if (NGX_HTTP_CLOJURE_UINT_SIZE == 4){ UNSAFE.putInt(address, val); }else { UNSAFE.putLong(address, val); } } public static final long fetchNGXOfft(long address){ return NGX_HTTP_CLOJURE_OFFT_SIZE == 4 ? UNSAFE.getInt(address) : UNSAFE.getLong(address); } public static final void pushNGXOfft(long address, long val){ if (NGX_HTTP_CLOJURE_OFFT_SIZE == 4){ UNSAFE.putInt(address, (int)val); }else { UNSAFE.putLong(address, val); } } public static final void pushNGXSizet(long address, int val){ if (NGX_HTTP_CLOJURE_SIZET_SIZE == 4){ UNSAFE.putInt(address, val); }else { UNSAFE.putLong(address, val); } } public final static String fetchDString(long address, int size) { ByteBuffer bb = pickByteBuffer(); CharBuffer cb = pickCharBuffer(); if (size > bb.capacity()) { bb = ByteBuffer.allocate(size); } ngx_http_clojure_mem_copy_to_obj(address, bb.array(), BYTE_ARRAY_OFFSET, size); bb.limit(size); return HackUtils.decode(bb, DEFAULT_ENCODING, cb); } public final static String fetchString(long paddress, int size) { return fetchString(paddress, size, DEFAULT_ENCODING); } public static final String fetchString(long paddress, int size, Charset encoding, ByteBuffer bb, CharBuffer cb) { if (size > bb.limit()) { size = bb.limit(); } ngx_http_clojure_mem_copy_to_obj(UNSAFE.getAddress(paddress), bb.array(), BYTE_ARRAY_OFFSET, size); bb.limit(size); return HackUtils.decode(bb, encoding, cb); } public static final String fetchString(long paddress, int size, Charset encoding) { ByteBuffer bb = pickByteBuffer(); CharBuffer cb = pickCharBuffer(); if (size > bb.capacity()) { bb = ByteBuffer.allocate(size); } ngx_http_clojure_mem_copy_to_obj(UNSAFE.getAddress(paddress), bb.array(), BYTE_ARRAY_OFFSET, size); bb.limit(size); return HackUtils.decode(bb, encoding, cb); } public static final String fetchStringValidPart(long paddress, int off, int size, Charset encoding, ByteBuffer bb, CharBuffer cb) { ByteBuffer lb = null; if (size > bb.remaining()) { lb = ByteBuffer.allocate(size); ngx_http_clojure_mem_copy_to_obj(UNSAFE.getAddress(paddress) + off, lb.array(), BYTE_ARRAY_OFFSET, size); lb.limit(size); cb = HackUtils.decodeValid(lb, encoding, cb); if (lb.remaining() == 0) { bb.position(bb.limit()); }else if (lb.remaining() < bb.remaining()){ bb.position(bb.position() + lb.remaining()); } return cb.toString(); } ngx_http_clojure_mem_copy_to_obj(UNSAFE.getAddress(paddress) + off, bb.array(), bb.arrayOffset() + bb.position() + BYTE_ARRAY_OFFSET, size); bb.limit(size); cb = HackUtils.decodeValid(bb, encoding, cb); return cb.toString(); } public static final int pushLowcaseString(long paddress, String val, Charset encoding, long pool) { ByteBuffer bb = pickByteBuffer(); bb = HackUtils.encodeLowcase(val, encoding, bb); int len = bb.remaining(); long strAddr = ngx_palloc(pool, len); UNSAFE.putAddress(paddress, strAddr); ngx_http_clojure_mem_copy_to_addr(bb.array(), BYTE_ARRAY_OFFSET , strAddr, len); return len; } public static final int pushString(long paddress, String val, Charset encoding, long pool) { ByteBuffer bb = pickByteBuffer(); bb = HackUtils.encode(val, encoding, bb); int len = bb.remaining(); long strAddr = ngx_palloc(pool, len); UNSAFE.putAddress(paddress, strAddr); ngx_http_clojure_mem_copy_to_addr(bb.array(), BYTE_ARRAY_OFFSET , strAddr, len); return len; } public static final String getNGXVariable(final long r, final String name) { if (r == 0) { throw new RuntimeException("invalid request which address is 0!"); } if (Thread.currentThread() != NGINX_MAIN_THREAD) { FutureTask<String> task = new FutureTask<String>(new Callable<String>() { @Override public String call() throws Exception { return unsafeGetNGXVariable(r, name); } }); postPollTaskEvent(task); try { return task.get(); } catch (InterruptedException e) { throw new RuntimeException("getNGXVariable " + name + " error", e); } catch (ExecutionException e) { throw new RuntimeException("getNGXVariable " + name + " error", e.getCause()); } }else { return unsafeGetNGXVariable(r, name); } } public static final String unsafeGetNGXVariable(long r, String name) { if (CORE_VARS.containsKey(name)) { return (String) new RequestKnownNameVarFetcher(name).fetch(r, DEFAULT_ENCODING); } return (String) new RequestUnknownNameVarFetcher(name).fetch(r, DEFAULT_ENCODING); } public static final int setNGXVariable(final long r, final String name, final String val) { if (r == 0) { throw new RuntimeException("invalid request which address is 0!"); } if (Thread.currentThread() != NGINX_MAIN_THREAD) { FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>() { @Override public Integer call() throws Exception { return unsafeSetNginxVariable(r, name, val); } }); postPollTaskEvent(task); try { return task.get(); } catch (InterruptedException e) { throw new RuntimeException("setNGXVariable " + name + " error", e); } catch (ExecutionException e) { throw new RuntimeException("setNGXVariable " + name + " error", e.getCause()); } }else { return unsafeSetNginxVariable(r, name, val); } } protected static int unsafeSetNginxVariable(long r, String name, String val) throws OutOfMemoryError { long np = CORE_VARS.containsKey(name) ? CORE_VARS.get(name) : 0; long pool = UNSAFE.getAddress(r + NGX_HTTP_CLOJURE_REQ_POOL_OFFSET); if (pool == 0) { throw new RuntimeException("pool is null, maybe request is finished by wrong coroutine configuration!"); } if (np == 0) { np = ngx_palloc(pool, NGX_HTTP_CLOJURE_STR_SIZE); pushNGXLowcaseString(np, name, DEFAULT_ENCODING, pool); } ByteBuffer vbb = HackUtils.encode(val, DEFAULT_ENCODING, pickByteBuffer()); int vlen = vbb.remaining(); long strAddr = ngx_palloc(pool, vbb.remaining()); if (strAddr == 0) { throw new OutOfMemoryError("nginx OutOfMemoryError"); } ngx_http_clojure_mem_copy_to_addr(vbb.array(), BYTE_ARRAY_OFFSET, strAddr, vlen); return (int)ngx_http_clojure_mem_set_variable(r, np, strAddr, vlen); } public static int eval(final int codeId, final long r, final long c) { return HANDLERS.get(codeId).execute(r, c); } public static LoggerService getLog() { //be friendly to junit test if (log == null) { //standard error stream is redirect to the nginx error log file, so we just use System.err as output stream. log = TinyLogService.createDefaultTinyLogService(); } return log; } public static void setLog(LoggerService log) { NginxClojureRT.log = log; } public final static long makeEventAndSaveIt(long type, Object o) { long id = ngx_http_clojure_mem_get_obj_addr(o); long event = type << 56 | id; savePostEventData(id, o); return event; } public static void postCloseSocketEvent(NginxClojureSocketImpl s) { ngx_http_clojure_mem_post_event(makeEventAndSaveIt(POST_EVENT_TYPE_CLOSE_SOCKET, s), null, 0); } public static HijackEvent pickHijackEvent() { HijackEvent e = pooledEvents.poll(); if (e == null) { return new HijackEvent(); } return e; } public static void returnHijackEvent(HijackEvent e) { e.recycle(); pooledEvents.add(e); } public static void postHijackSendEvent(NginxHttpServerChannel channel, Object message, long off, int len, int flag) { HijackEvent hijackEvent = pickHijackEvent().reset(channel, message, off, len , flag); ngx_http_clojure_mem_post_event( makeEventAndSaveIt(POST_EVENT_TYPE_HIJACK_SEND, hijackEvent), null, 0); } public static long postHijackWriteEvent(NginxHttpServerChannel channel, Object message, long off, int len) throws IOException { HijackEvent hijackEvent = pickHijackEvent().reset(channel, message, off, len, 0); ngx_http_clojure_mem_post_event( makeEventAndSaveIt(POST_EVENT_TYPE_HIJACK_WRITE, hijackEvent), null, 0); try { hijackEvent.awaitForFinish(); long rc = hijackEvent.offset; returnHijackEvent(hijackEvent); return rc; } catch (InterruptedException e) { throw new IOException("write await be interrupted", e); } } public static void postHijackSendHeaderEvent(NginxHttpServerChannel channel, int flag) { HijackEvent hijackEvent = pickHijackEvent().reset(channel, null, 0, 0, flag); ngx_http_clojure_mem_post_event( makeEventAndSaveIt(POST_EVENT_TYPE_HIJACK_SEND_HEADER, hijackEvent), null, 0); } public static void postHijackSendHeaderEvent(NginxHttpServerChannel channel, Object buf, int pos, int len, int flag) { HijackEvent hijackEvent = pickHijackEvent().reset(channel, buf, pos, len, flag); ngx_http_clojure_mem_post_event( makeEventAndSaveIt(POST_EVENT_TYPE_HIJACK_SEND_HEADER, hijackEvent), null, 0); } public static void postHijackSendResponseEvent(NginxHttpServerChannel channel, NginxResponse resp, long chain) { HijackEvent hijackEvent = pickHijackEvent().reset(channel, resp, chain); ngx_http_clojure_mem_post_event( makeEventAndSaveIt(POST_EVENT_TYPE_HIJACK_SEND_RESPONSE, hijackEvent), null, 0); } private final static byte[] POST_EVENT_BUF = new byte[4096]; public static int handlePostEvent(long event, byte[] body, long off) { if (event == 0) { //event loop wake up event return NGX_OK; } int tag = (int)((0xff00000000000000L & event) >>> 56); long data = event & 0x00ffffffffffffffL; if (tag <= POST_EVENT_TYPE_SYSTEM_EVENT_IDX_END) { switch (tag) { case POST_EVENT_TYPE_HANDLE_RESPONSE: return handlePostedResponse(data); case POST_EVENT_TYPE_CLOSE_SOCKET: try { NginxClojureSocketImpl s = (NginxClojureSocketImpl) POSTED_EVENTS_DATA.remove(data); s.closeByPostEvent(); return NGX_OK; }catch (Throwable e) { log.error("handle post close event error", e); return NGX_HTTP_INTERNAL_SERVER_ERROR; } case POST_EVENT_TYPE_HIJACK_SEND : { HijackEvent hijackEvent = (HijackEvent)POSTED_EVENTS_DATA.remove(data); try{ if (hijackEvent.channel.request.isReleased()) { if (hijackEvent.message == null) { return NGX_OK; } log.error("#%d: NginxHttpServerChannel released, request=%s", hijackEvent.channel.request.nativeRequest(), hijackEvent.channel.request); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (hijackEvent.message instanceof ByteBuffer) { hijackEvent.channel.send((ByteBuffer)hijackEvent.message, hijackEvent.flag); }else { hijackEvent.channel.send((byte[])hijackEvent.message, hijackEvent.offset, hijackEvent.len, hijackEvent.flag); } }finally{ returnHijackEvent(hijackEvent); } return NGX_OK; } case POST_EVENT_TYPE_HIJACK_SEND_HEADER : { HijackEvent hijackHeaderEvent = (HijackEvent)POSTED_EVENTS_DATA.remove(data); try{ if (hijackHeaderEvent.channel.request.isReleased()) { log.error("#%d: send header on released NginxHttpServerChannel , request=%s", hijackHeaderEvent.channel.request.nativeRequest(), hijackHeaderEvent.channel.request); returnHijackEvent(hijackHeaderEvent); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (hijackHeaderEvent.message != null) { if (hijackHeaderEvent.message instanceof ByteBuffer) { hijackHeaderEvent.channel.sendHeader((ByteBuffer)hijackHeaderEvent.message, hijackHeaderEvent.flag); }else { hijackHeaderEvent.channel.sendHeader((byte[])hijackHeaderEvent.message, hijackHeaderEvent.offset, hijackHeaderEvent.len, hijackHeaderEvent.flag); } }else { hijackHeaderEvent.channel.sendHeader(hijackHeaderEvent.flag); } }finally{ returnHijackEvent(hijackHeaderEvent); } return NGX_OK; } case POST_EVENT_TYPE_HIJACK_SEND_RESPONSE : { HijackEvent hijackResponseEvent = (HijackEvent)POSTED_EVENTS_DATA.remove(data); try { if (hijackResponseEvent.channel.request.isReleased()) { log.error("#%d: send response on released NginxHttpServerChannel, request=%s", hijackResponseEvent.channel.request.nativeRequest(), hijackResponseEvent.channel.request); returnHijackEvent(hijackResponseEvent); return NGX_HTTP_INTERNAL_SERVER_ERROR; } NginxRequest request = hijackResponseEvent.channel.request; request.channel().sendResponseHelp((NginxResponse) hijackResponseEvent.message, hijackResponseEvent.offset); }finally { returnHijackEvent(hijackResponseEvent); } return NGX_OK; } case POST_EVENT_TYPE_HIJACK_WRITE : { HijackEvent hijackEvent = (HijackEvent)POSTED_EVENTS_DATA.remove(data); try{ if (hijackEvent.channel.request.isReleased()) { log.error("#%d: send response on released NginxHttpServerChannel, request=%s", hijackEvent.channel.request.nativeRequest(), hijackEvent.channel.request); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (hijackEvent.message instanceof ByteBuffer) { hijackEvent.complete(hijackEvent.channel.unsafeWrite((ByteBuffer) hijackEvent.message)); } else { hijackEvent.complete(hijackEvent.channel.unsafeWrite((byte[]) hijackEvent.message, hijackEvent.offset, hijackEvent.len)); } }finally{ /*it will be released in the method postHijackWriteEvent */ // returnHijackEvent(hijackEvent); } return NGX_OK; } case POST_EVENT_TYPE_POLL_TASK : { Runnable task = (Runnable) POSTED_EVENTS_DATA.remove(data); try { task.run(); return NGX_OK; }catch(Throwable e) { log.error("handle post poll task event error", e); return NGX_HTTP_INTERNAL_SERVER_ERROR; } } case POST_EVENT_TYPE_PUB : { appEventListenerManager.onBroadcastedEvent(tag, data); return NGX_OK; } default: log.error("handlePostEvent:unknown event tag :%d", tag); return NGX_HTTP_INTERNAL_SERVER_ERROR; } } else { if (tag < POST_EVENT_TYPE_COMPLEX_EVENT_IDX_START) { appEventListenerManager.onBroadcastedEvent(tag, data); return NGX_OK; }else { appEventListenerManager.onBroadcastedEvent(tag, body, (int)off, (int)data); return NGX_OK; } } } /** * called by native code */ private static int handlePostEvent(long event, long pipe) { int tag = (int)((0xff00000000000000L & event) >>> 56); long data = event & 0x00ffffffffffffffL; if (log.isDebugEnabled()) { log.debug("handlePostEvent tag=%d, len/data=%d", tag, data); } if (tag < POST_EVENT_TYPE_COMPLEX_EVENT_IDX_START) { return handlePostEvent(event, null, 0); } else { long rc = ngx_http_clojure_mem_read_raw_pipe(pipe, POST_EVENT_BUF, BYTE_ARRAY_OFFSET, data); if (rc != data) { log.error("ngx_http_clojure_mem_read_raw_pipe error, return %d, expect %d", rc, data); return NGX_HTTP_INTERNAL_SERVER_ERROR; } return handlePostEvent(event, POST_EVENT_BUF, 0); } } private static void handleChannelEvent(int type, long status, Object data, ChannelListener<Object> listener) { try { switch(type) { case NGX_HTTP_CLOJURE_CHANNEL_EVENT_CLOSE: listener.onClose(data); break; case NGX_HTTP_CLOJURE_CHANNEL_EVENT_CONNECT : listener.onConnect(status, data); break; case NGX_HTTP_CLOJURE_CHANNEL_EVENT_READ: listener.onRead(status, data); break; case NGX_HTTP_CLOJURE_CHANNEL_EVENT_WRITE: listener.onWrite(status, data); break; default: if (listener instanceof RawMessageListener) { RawMessageListener rawListener = (RawMessageListener) listener; if ( (type & NGX_HTTP_CLOJURE_CHANNEL_EVENT_MSGTEXT) != 0) { rawListener.onTextMessage(data, status, (type & NGX_HTTP_CLOJURE_CHANNEL_EVENT_MSGREMAIN) != 0, (type & NGX_HTTP_CLOJURE_CHANNEL_EVENT_MSGFIRST) != 0); }else if ( (type & NGX_HTTP_CLOJURE_CHANNEL_EVENT_MSGBIN) != 0) { rawListener.onBinaryMessage(data, status, (type & NGX_HTTP_CLOJURE_CHANNEL_EVENT_MSGREMAIN) != 0, (type & NGX_HTTP_CLOJURE_CHANNEL_EVENT_MSGFIRST) != 0); }else if ( (type & NGX_HTTP_CLOJURE_CHANNEL_EVENT_MSGCLOSE) != 0) { rawListener.onClose(data, status);//(data, status, (type & NGX_HTTP_CLOJURE_CHANNEL_EVENT_MSGREMAIN) != 0); } } } }catch(Throwable e) { log.error("handleChannelEvent error", e); } } public static int handlePostedResponse(long r) { WorkerResponseContext ctx = (WorkerResponseContext) POSTED_EVENTS_DATA.remove(r); NginxResponse resp = ctx.response; NginxRequest req = ctx.request; long rc = NGX_OK; if (ctx.request.isReleased()) { if (resp.type() > 0) { log.error("#%d: request is release! and we alos meet an unhandled exception! %s", req.nativeRequest(), resp.fetchBody()); }else { log.error("#%d: request is release! ", req.nativeRequest()); } return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (resp.type() == NginxResponse.TYPE_FAKE_PHASE_DONE) { if (ctx.request.phase() == NGX_HTTP_HEADER_FILTER_PHASE) { rc = ngx_http_filter_continue_next(r, -1); ngx_http_finalize_request(r, rc); return NGX_OK; }else if (ctx.request.phase() == NGX_HTTP_BODY_FILTER_PHASE) { ctx.chain = req.handler().buildOutputChain(resp); rc = ngx_http_filter_continue_next(r, ctx.chain); if (resp.isLast()) { ngx_http_finalize_request(r, rc); } return NGX_OK; } ngx_http_clojure_mem_continue_current_phase(r, NGX_DECLINED); return NGX_OK; }else if (ctx.request.phase() == NGX_HTTP_BODY_FILTER_PHASE) { ctx.chain = req.handler().buildOutputChain(resp); rc = ngx_http_filter_continue_next(r, ctx.chain); if (resp.isLast()) { ngx_http_finalize_request(r, rc); } else { ngx_http_clojure_mem_inc_req_count(r, -1); } return NGX_OK; } long chain = ctx.chain; int phase = req.phase(); long nr = req.nativeRequest(); if (chain < 0) { req.handler().prepareHeaders(req, -(int)chain, resp.fetchHeaders()); rc = -chain; }else if (chain == 0) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; } else { int status = ctx.response.fetchStatus(NGX_HTTP_OK); if (phase == NGX_HTTP_HEADER_FILTER_PHASE || phase == NGX_HTTP_BODY_FILTER_PHASE) { ngx_http_clear_header_and_reset_ctx_phase(nr, ~phase); } req.handler().prepareHeaders(req, status, resp.fetchHeaders()); rc = ngx_http_send_header(nr); if (rc == NGX_ERROR || rc > NGX_OK) { }else { rc = ngx_http_output_filter(r, chain); if (rc == NGX_OK && phase != -1) { ngx_http_ignore_next_response(nr); } if (phase != -1) { if (phase == NGX_HTTP_ACCESS_PHASE || phase == NGX_HTTP_REWRITE_PHASE ) { rc = handleReturnCodeFromHandler(nr, phase, rc, status); }else { handleReturnCodeFromHandler(nr, phase, rc, status); } } } } if (phase == -1 || phase == NGX_HTTP_HEADER_FILTER_PHASE) { ngx_http_finalize_request(r, rc); }else if (rc != NGX_DONE) { ngx_http_clojure_mem_continue_current_phase(r, rc); } return NGX_OK; } protected static long handleReturnCodeFromHandler(long r, int phase, long rc, int status) { if (phase == -1 || rc == NGX_ERROR ) { return rc; } if (phase == NGX_HTTP_HEADER_FILTER_PHASE) { //header filter want to hajick all the response ,e.g some exception happends return NGX_ERROR; } ngx_http_finalize_request(r, rc); if (phase == NGX_HTTP_ACCESS_PHASE || phase == NGX_HTTP_REWRITE_PHASE ) { return NGX_DONE; } return rc; } public static int handleResponse(NginxRequest r, final NginxResponse resp) { if (Thread.currentThread() != NGINX_MAIN_THREAD) { throw new RuntimeException("handleResponse can not be called out of nginx clojure main thread!"); } if (resp == null) { return NGX_HTTP_NOT_FOUND; } int phase = r.phase(); if (resp.type() == NginxResponse.TYPE_FAKE_PHASE_DONE) { if (phase == NGX_HTTP_REWRITE_PHASE || phase == NGX_HTTP_ACCESS_PHASE) { return NGX_DECLINED; } //header filter return (int)ngx_http_filter_continue_next(r.nativeRequest(), -1); } NginxHandler handler = r.handler(); int status = resp.fetchStatus(NGX_HTTP_OK); long chain = handler.buildOutputChain(resp); if (chain < 0) { status = -(int)chain; handler.prepareHeaders(r, status, resp.fetchHeaders()); return status; } long nr = r.nativeRequest(); if (phase == NGX_HTTP_HEADER_FILTER_PHASE) { ngx_http_clear_header_and_reset_ctx_phase(nr, ~phase); }else if (phase == NGX_HTTP_BODY_FILTER_PHASE) { ngx_http_clear_header_and_reset_ctx_phase(nr, ~phase, false); return (int)ngx_http_filter_continue_next(r.nativeRequest(), chain); } handler.prepareHeaders(r, status, resp.fetchHeaders()); long rc = ngx_http_send_header(r.nativeRequest()); if (rc == NGX_ERROR || rc > NGX_OK) { return (int) rc; } rc = ngx_http_output_filter(r.nativeRequest(), chain); if (rc == NGX_OK && phase != -1) { ngx_http_ignore_next_response(nr); } return (int)handleReturnCodeFromHandler(nr, phase, rc, status); } public static void completeAsyncResponse(NginxRequest req, final NginxResponse resp) { if (req == null) { return; } long r = req.nativeRequest(); if (r == 0) { return; } if (req.isReleased()) { if (resp.type() > 0) { log.error("#%d: request is release! and we alos meet an unhandled exception! %s", req.nativeRequest(), resp.fetchBody()); }else { log.error("#%d: request is release! ", req.nativeRequest()); } return; } long rc; int phase = req.phase(); if (resp.type() == NginxResponse.TYPE_FAKE_PHASE_DONE) { if (phase == NGX_HTTP_HEADER_FILTER_PHASE) { rc = ngx_http_filter_continue_next(r, -1); ngx_http_finalize_request(r, rc); return; } ngx_http_clojure_mem_continue_current_phase(r, NGX_DECLINED); return; } rc = handleResponse(req, resp); if (phase == -1 || phase == NGX_HTTP_HEADER_FILTER_PHASE) { ngx_http_finalize_request(r, rc); }else if (rc != MiniConstants.NGX_DONE) { ngx_http_clojure_mem_continue_current_phase(r, rc); } } public static void completeAsyncResponse(NginxRequest r, int rc) { if (r == null) { return; } completeAsyncResponse(r.nativeRequest(), rc); } public static void completeAsyncResponse(long r, int rc) { if (r == 0) { return; } ngx_http_finalize_request(r, rc); } /** * When called in the main thread it will be handled directly otherwise it will post a event by pipe let * main thread get a chance to handle this response. */ public static void postResponseEvent(NginxRequest req, NginxResponse resp) { if (Thread.currentThread() == NGINX_MAIN_THREAD) { int phase = req.phase(); int rc = handleResponse(req, resp); if (phase == -1 || phase == NGX_HTTP_HEADER_FILTER_PHASE) { ngx_http_finalize_request(req.nativeRequest(), rc); }else if (rc != MiniConstants.NGX_DONE){ ngx_http_clojure_mem_continue_current_phase(req.nativeRequest(), rc); } }else { long r = req.nativeRequest(); WorkerResponseContext ctx = new WorkerResponseContext(resp, req); savePostEventData(r, ctx); ngx_http_clojure_mem_post_event(r, null, 0); } } public static void postPollTaskEvent(Runnable task) { ngx_http_clojure_mem_post_event(makeEventAndSaveIt(POST_EVENT_TYPE_POLL_TASK,task), null, 0); } /** * broadcast simple event to all nginx workers * @param tag must be less than POST_EVENT_TYPE_COMPLEX_EVENT_IDX_START * @param id event id, must be less than 0x0100000000000000L * @param */ public static int broadcastEvent(long tag, long id) { if (tag >= POST_EVENT_TYPE_COMPLEX_EVENT_IDX_START) { throw new IllegalArgumentException("invalid event tag :" + tag); } if (id >= 0x0100000000000000L) { throw new IllegalArgumentException("invalid event id :" + id + ", must be less than 0x0100000000000000L"); } id |= (tag << 56); if (Thread.currentThread() == NGINX_MAIN_THREAD) { int rt = (int)ngx_http_clojure_mem_broadcast_event(id, null, 0, 0); if (rt == 0) { return handlePostEvent(id, null, 0); }else { rt = handlePostEvent(id, null, 0); } return rt; }else { return (int)ngx_http_clojure_mem_broadcast_event(id, null, 0, 1); } } /** * broadcast event to all nginx workers, message length must be less than PIPE_BUF - 8, generally on Linux/Windows is 4088, on MacosX is 504 * message will be truncated if its length exceeds this limitation. * @param tag must be greater than POST_EVENT_TYPE_COMPLEX_EVENT_IDX_START and less than POST_EVENT_TYPE_COMPLEX_EVENT_IDX_END * @param body * @param offset * @param len */ public static int broadcastEvent(long tag, byte[] body, long offset, long len) { if (tag >= 0xff) { throw new IllegalArgumentException("invalid event tag :" + tag); } if (tag < POST_EVENT_TYPE_COMPLEX_EVENT_IDX_START) { throw new IllegalArgumentException("invalid event tag :" + tag + ", must be greater than POST_EVENT_TYPE_COMPLEX_EVENT_IDX_START"); } long event = (tag << 56) | len; if (log.isDebugEnabled()) { log.debug("broadcast event tag=%d, body=%s", tag, new String(body, (int)offset, (int)len), DEFAULT_ENCODING); } if (Thread.currentThread() == NGINX_MAIN_THREAD) { int rt = (int)ngx_http_clojure_mem_broadcast_event(event, body, BYTE_ARRAY_OFFSET + offset, 0); if (rt == 0) { rt = (int)handlePostEvent(event, body, offset); }else { handlePostEvent(event, body, offset); } return rt; }else { return (int)ngx_http_clojure_mem_broadcast_event(event, body, BYTE_ARRAY_OFFSET + offset, 1); } } /** * broadcast event to all nginx workers, message length must be less than PIPE_BUF - 8, generally on Linux/Windows is 4088, on MacosX is 504 * message will be truncated if its length exceeds this limitation. * it is identical to * <pre> * broadcastEvent(POST_EVENT_TYPE_COMPLEX_EVENT_IDX_START, body, offset, len); * </pre> */ public static int broadcastEvent(byte[] message, long offset, long len) { return broadcastEvent(POST_EVENT_TYPE_COMPLEX_EVENT_IDX_START, message, offset, len); } /** * broadcast event to all nginx workers, message length, viz. message.getBytes("utf-8").length, must be less than PIPE_BUF - 8, * generally on Linux/Windows is 4088, on MacosX is 504 * message will be truncated if its length exceeds this limitation. * it is identical to * <pre> * byte[] buf = message.getBytes(DEFAULT_ENCODING); * return broadcastEvent(tag, buf, 0, buf.length); * </pre> */ public static int broadcastEvent(long tag, String message) { byte[] buf = message.getBytes(DEFAULT_ENCODING); return broadcastEvent(tag, buf, 0, buf.length); } /** * broadcast event to all nginx workers, message length, viz. message.getBytes("utf-8").length, must be less than PIPE_BUF - 8, * generally on Linux/Windows is 4088, on MacosX is 504 * message will be truncated if its length exceeds this limitation. * it is identical to * <pre> * byte[] buf = message.getBytes(DEFAULT_ENCODING); * return broadcastEvent(POST_EVENT_TYPE_COMPLEX_EVENT_IDX_START, buf, 0, buf.length); * </pre> */ public static int broadcastEvent(String message) { byte[] buf = message.getBytes(DEFAULT_ENCODING); return broadcastEvent(POST_EVENT_TYPE_COMPLEX_EVENT_IDX_START, buf, 0, buf.length); } public static final class BatchCallRunner implements Runnable { Coroutine parent; int[] counter; Callable handler; int order; Object[] results; public BatchCallRunner(Coroutine parent, int[] counter, Callable handler, int order, Object[] results) { super(); this.parent = parent; this.counter = counter; this.handler = handler; this.order = order; this.results = results; } @Override public void run() throws SuspendExecution { try { results[order] = handler.call(); }catch(Throwable e) { log.error("error in sub coroutine", e); } if ( --counter[0] == 0 && parent != null && parent.getState() == Coroutine.State.SUSPENDED) { parent.resume(); } } } public static final Object[] coBatchCall(Callable<Object> ...calls) { int c = calls.length; int[] counter = new int[] {c}; Object[] results = new Object[c]; Coroutine parent = Coroutine.getActiveCoroutine(); if (parent == null && (JavaAgent.db == null || !JavaAgent.db.isRunTool())) { log.warn("we are not in coroutine enabled context, so we turn to use thread for only testing usage!"); Future[] futures = new Future[c]; for (int i = 0; i < c ; i++) { BatchCallRunner bcr = new BatchCallRunner(parent, counter, calls[i], i, results); if (threadPoolOnlyForTestingUsage == null) { initThreadPoolOnlyForTestingUsage(); } futures[i] = threadPoolOnlyForTestingUsage.submit(bcr); } for (Future f : futures) { try { f.get(); } catch (Throwable e) { log.error("do future failed", e); } } }else { boolean shouldYieldParent = false; for (int i = 0; i < c ; i++) { Coroutine co = new Coroutine(new BatchCallRunner(parent, counter, calls[i], i, results)); co.resume(); if (co.getState() != Coroutine.State.FINISHED) { shouldYieldParent = true; } } if (parent != null && shouldYieldParent) { Coroutine.yield(); } } return results; } public static ByteBuffer pickByteBuffer() { // if (defaultByteBuffer != null) { // defaultByteBuffer.clear(); // return defaultByteBuffer; // } ByteBuffer bb = threadLocalByteBuffers.get(); if (bb == null) { threadLocalByteBuffers.set(bb = ByteBuffer.allocate(NGINX_CLOJURE_CORE_CLIENT_HEADER_MAX_SIZE)); }else { bb.clear(); } return bb; } public static CharBuffer pickCharBuffer() { // if (defaultCharBuffer != null) { // defaultCharBuffer.clear(); // return defaultCharBuffer; // } CharBuffer cb = threadLocalCharBuffers.get(); if (cb == null) { threadLocalCharBuffers.set(cb = CharBuffer.allocate(NGINX_CLOJURE_CORE_CLIENT_HEADER_MAX_SIZE)); }else { cb.clear(); } return cb; } }