Package org.jruby.jubilee

Source Code of org.jruby.jubilee.RackEnvironment

package org.jruby.jubilee;

import io.netty.handler.codec.http.HttpHeaders;
import org.jruby.*;
import org.jruby.jubilee.utils.RubyHelper;
import org.jruby.runtime.*;
import org.jruby.runtime.builtin.IRubyObject;
import org.vertx.java.core.MultiMap;
import org.vertx.java.core.http.HttpServerRequest;
import org.vertx.java.core.http.HttpVersion;
import org.vertx.java.core.net.NetSocket;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;

public class RackEnvironment {

    // When adding a key to the enum be sure to add its RubyString equivalent
    // to populateRackKeyMap below
    static enum RACK_KEY {
        RACK_INPUT, RACK_ERRORS, REQUEST_METHOD, SCRIPT_NAME,
        PATH_INFO, QUERY_STRING, SERVER_NAME, SERVER_PORT,
        CONTENT_TYPE, REQUEST_URI, REMOTE_ADDR, URL_SCHEME,
        VERSION, MULTITHREAD, MULTIPROCESS, RUN_ONCE, CONTENT_LENGTH,
        HTTPS, HTTP_VERSION, HIJACK_P, HIJACK//, HIJACK_IO
    }

    static final int NUM_RACK_KEYS = RACK_KEY.values().length;

    public RackEnvironment(final Ruby runtime) throws IOException {
        this.runtime = runtime;
        this.netSocketClass = (RubyClass) runtime.getClassFromPath("Jubilee::NetSocket");
        rackVersion = RubyArray.newArray(runtime, RubyFixnum.one(runtime), RubyFixnum.four(runtime));
        errors = new RubyIO(runtime, runtime.getErr());
        errors.setAutoclose(false);

        populateRackKeyMap();
    }

    private void populateRackKeyMap() {
        putRack("rack.input", RACK_KEY.RACK_INPUT);
        putRack("rack.errors", RACK_KEY.RACK_ERRORS);
        putRack("REQUEST_METHOD", RACK_KEY.REQUEST_METHOD);
        putRack("SCRIPT_NAME", RACK_KEY.SCRIPT_NAME);
        putRack("PATH_INFO", RACK_KEY.PATH_INFO);
        putRack("QUERY_STRING", RACK_KEY.QUERY_STRING);
        putRack("SERVER_NAME", RACK_KEY.SERVER_NAME);
        putRack("SERVER_PORT", RACK_KEY.SERVER_PORT);
        putRack("HTTP_VERSION", RACK_KEY.HTTP_VERSION);
        putRack("CONTENT_TYPE", RACK_KEY.CONTENT_TYPE);
        putRack("REQUEST_URI", RACK_KEY.REQUEST_URI);
        putRack("REMOTE_ADDR", RACK_KEY.REMOTE_ADDR);
        putRack("rack.url_scheme", RACK_KEY.URL_SCHEME);
        putRack("rack.version", RACK_KEY.VERSION);
        putRack("rack.multithread", RACK_KEY.MULTITHREAD);
        putRack("rack.multiprocess", RACK_KEY.MULTIPROCESS);
        putRack("rack.run_once", RACK_KEY.RUN_ONCE);
        putRack("rack.hijack?", RACK_KEY.HIJACK_P);
        putRack("rack.hijack", RACK_KEY.HIJACK);
        // putRack("rack.hijack_io", RACK_KEY.HIJACK_IO); Don't have to be lazy, since once rack.hijack is called, the io
        // object has to be required by the caller.
        putRack("CONTENT_LENGTH", RACK_KEY.CONTENT_LENGTH);
        putRack("HTTPS", RACK_KEY.HTTPS);
    }

    private void putRack(String key, RACK_KEY value) {
        rackKeyMap.put(RubyHelper.toUsAsciiRubyString(runtime, key), value);
    }

    public RubyHash getEnv(final HttpServerRequest request,
                           final RackInput input,
                           final boolean isSSL) throws IOException {
        MultiMap headers = request.headers();
        final RackEnvironmentHash env = new RackEnvironmentHash(runtime, headers, rackKeyMap);
        env.lazyPut(RACK_KEY.RACK_INPUT, input, false);
        env.lazyPut(RACK_KEY.RACK_ERRORS, errors, false);

        String pathInfo = request.path();

        String scriptName = "";
        String[] hostInfo = getHostInfo(request.headers().get(Const.HOST));

        env.lazyPut(RACK_KEY.REQUEST_METHOD, request.method(), true);
        env.lazyPut(RACK_KEY.SCRIPT_NAME, scriptName, false);
        env.lazyPut(RACK_KEY.PATH_INFO, pathInfo, false);
        env.lazyPut(RACK_KEY.QUERY_STRING, orEmpty(request.query()), false);
        env.lazyPut(RACK_KEY.SERVER_NAME, hostInfo[0], false);
        env.lazyPut(RACK_KEY.SERVER_PORT, hostInfo[1], true);
        env.lazyPut(RACK_KEY.HTTP_VERSION,
                request.version() == HttpVersion.HTTP_1_1 ? Const.HTTP_11 : Const.HTTP_10, true);
        env.lazyPut(RACK_KEY.CONTENT_TYPE, headers.get(HttpHeaders.Names.CONTENT_TYPE), true);
        env.lazyPut(RACK_KEY.REQUEST_URI, request.uri(), false);
        env.lazyPut(RACK_KEY.REMOTE_ADDR, getRemoteAddr(request), true);
        env.lazyPut(RACK_KEY.URL_SCHEME, isSSL ? Const.HTTPS : Const.HTTP, true);
        env.lazyPut(RACK_KEY.VERSION, rackVersion, false);
        env.lazyPut(RACK_KEY.MULTITHREAD, runtime.getTrue(), false);
        env.lazyPut(RACK_KEY.MULTIPROCESS, runtime.getFalse(), false);
        env.lazyPut(RACK_KEY.RUN_ONCE, runtime.getFalse(), false);

        // Hijack handling
        env.lazyPut(RACK_KEY.HIJACK_P, runtime.getFalse(), false);
//        env.lazyPut(RACK_KEY.HIJACK, hijackProc(env, request), false);


        final int contentLength = getContentLength(headers);
        if (contentLength >= 0) {
            env.lazyPut(RACK_KEY.CONTENT_LENGTH, contentLength + "", true);
        }

        if (isSSL) {
            env.lazyPut(RACK_KEY.HTTPS, "on", true);
        }

        return env;
    }

    private IRubyObject hijackProc(final RackEnvironmentHash env, final HttpServerRequest req) {
        CompiledBlockCallback19 callback = new CompiledBlockCallback19() {
            @Override
            public IRubyObject call(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) {
                RubyNetSocket rubyNetSocket = new RubyNetSocket(context.runtime, netSocketClass, req.netSocket());
                env.put("rack.hijack_io", rubyNetSocket);
                return rubyNetSocket;
            }

            @Override
            public String getFile() {
                return null;
            }

            @Override
            public int getLine() {
                return 0;
            }
        };
        BlockBody body = CompiledBlockLight19.newCompiledBlockLight(Arity.NO_ARGUMENTS, runtime.getStaticScopeFactory().getDummyScope(), callback, false, 0, new String[]{});
        Block b = new Block(body, runtime.newBinding().getBinding());

        return RubyProc.newProc(runtime, b, Block.Type.LAMBDA);
    }

    public String[] getHostInfo(String host) {
        String[] hostInfo;
        if (host != null) {
            int colon = host.indexOf(":");
            if (colon > 0)
                hostInfo = new String[]{host.substring(0, colon), host.substring(colon + 1)};
            else
                hostInfo = new String[]{host, Const.PORT_80};

        } else {
            hostInfo = new String[]{Const.LOCALHOST, Const.PORT_80};
        }
        return hostInfo;
    }

    private static String getRemoteAddr(final HttpServerRequest request) {
        InetSocketAddress sourceAddress = request.remoteAddress();
        if (sourceAddress == null) {
            return "";
        }
        return sourceAddress.getHostString();
    }

    private static int getContentLength(final MultiMap headers) {
        final String contentLengthStr = headers.get(HttpHeaders.Names.CONTENT_LENGTH);
        if (contentLengthStr == null || contentLengthStr.isEmpty()) {
            return -1;
        }
        return Integer.parseInt(contentLengthStr);
    }

    private String orEmpty(String val) {
        return val == null ? "" : val;
    }

    private final Ruby runtime;
    private final RubyArray rackVersion;
    private final RubyIO errors;
    private final Map<RubyString, RACK_KEY> rackKeyMap = new HashMap<>();
    private final RubyClass netSocketClass;
}
TOP

Related Classes of org.jruby.jubilee.RackEnvironment

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.