All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.firenio.baseio.codec.http11.HttpCodec Maven / Gradle / Ivy

There is a newer version: 3.2.9.beta11
Show newest version
/*
 * Copyright 2015 The Baseio Project
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.firenio.baseio.codec.http11;

import static com.firenio.baseio.codec.http11.HttpHeader.Content_Length;
import static com.firenio.baseio.codec.http11.HttpHeader.Content_Type;
import static com.firenio.baseio.codec.http11.HttpHeader.Cookie;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.firenio.baseio.Develop;
import com.firenio.baseio.buffer.ByteBuf;
import com.firenio.baseio.collection.IntMap;
import com.firenio.baseio.common.ByteUtil;
import com.firenio.baseio.common.Util;
import com.firenio.baseio.component.Channel;
import com.firenio.baseio.component.FastThreadLocal;
import com.firenio.baseio.component.Frame;
import com.firenio.baseio.component.NioEventLoop;
import com.firenio.baseio.component.ProtocolCodec;

/**
 * @author wangkai
 *
 */
public class HttpCodec extends ProtocolCodec {

    static final byte[]             CONNECTION_CLOSE          = b("\r\nConnection: close");
    static final byte[]             CONNECTION_KEEP           = b("\r\nConnection: keep-alive");
    static final byte[]             CONNECTIONS               = b("\r\nConnection: keep-alive");
    static final byte[]             CONTENT_LENGTH_MATCH      = b("Content-Length:");
    static final byte[]             CONTENT_TYPE              = b("\r\nContent-Type: ");
    static final byte[]             DATE                      = b("\r\nDate: ");
    static final int                decode_state_body         = 2;
    static final int                decode_state_complate     = 3;
    static final int                decode_state_header       = 1;
    static final int                decode_state_line_one     = 0;
    static final int                encode_bytes_arrays_index = nextIndexedVariablesIndex();
    static final int                content_len_index         = nextIndexedVariablesIndex();
    static final String             FRAME_BUFFER_KEY          = "_HTTP_FRAME_BUFFER_KEY";
    static final String             FRAME_DECODE_KEY          = "_HTTP_FRAME_DECODE_KEY";
    static final KMPUtil            KMP_BOUNDARY              = new KMPUtil("boundary=");
    static final byte               N                         = '\n';
    public static final IOException OVER_LIMIT                = EXCEPTION("over limit");
    static final byte               R                         = '\r';
    static final byte[]             SET_COOKIE                = b("Set-Cookie:");
    static final byte               SPACE                     = ' ';

    private int                     bodyLimit                 = 1024 * 512;
    private final byte[][]          CONTENT_LENGTHS           = new byte[1024][];
    private int                     headerLimit               = 1024 * 8;
    private int                     httpFrameBuffer           = 0;
    private boolean                 lite;
    private ByteBuffer              contentLenBuf;

    public HttpCodec() {
        this(0);
    }

    public HttpCodec(int httpFrameBuffer) {
        this(null, httpFrameBuffer);
    }

    public HttpCodec(String server) {
        this(server, 0);
    }

    public HttpCodec(String server, int httpFrameBuffer) {
        this(server, httpFrameBuffer, false);
    }

    public HttpCodec(String server, int httpFrameBuffer, boolean lite) {
        this.lite = lite;
        this.httpFrameBuffer = httpFrameBuffer;
        ByteBuffer temp = ByteBuffer.allocate(128);
        if (server == null) {
            temp.put(b("\r\nContent-Length: "));
        } else {
            temp.put(b("\r\nServer: " + server + "\r\nContent-Length: "));
        }
        contentLenBuf = temp.duplicate();
        contentLenBuf.flip();
        int p = temp.position();
        for (int i = 0; i < CONTENT_LENGTHS.length; i++) {
            temp.clear().position(p);
            temp.put(String.valueOf(i).getBytes());
            temp.flip();
            CONTENT_LENGTHS[i] = new byte[temp.limit()];
            temp.get(CONTENT_LENGTHS[i]);
        }
    }

    HttpFrame allocHttpFrame(Channel ch) {
        HttpFrame f = (HttpFrame) ch.getAttribute(FRAME_DECODE_KEY);
        if (f == null) {
            if (httpFrameBuffer > 0) {
                NioEventLoop el = ch.getEventLoop();
                Frame res = el.getFrameFromBuffer(ch, FRAME_BUFFER_KEY, httpFrameBuffer);
                if (res == null) {
                    return new HttpFrame();
                } else {
                    return (HttpFrame) res.reset();
                }
            }
            return new HttpFrame();
        }
        return f;
    }

    @Override
    public Frame decode(Channel ch, ByteBuf src) throws Exception {
        HttpFrame f = allocHttpFrame(ch);
        int decode_state = f.getDecodeState();
        if (lite) {
            if (decode_state == decode_state_line_one) {
                int ln = src.indexOf(N);
                if (ln != -1) {
                    int p = src.absPos();
                    f.incrementHeaderLength(ln - p);
                    decode_state = decode_state_header;
                    ln = findN(src, ln);
                    int skip;
                    if (src.absByte(p) == 'G') {
                        f.setMethod(HttpMethod.GET);
                        skip = 4;
                    } else {
                        f.setMethod(HttpMethod.POST);
                        skip = 5;
                    }
                    StringBuilder line = FastThreadLocal.get().getStringBuilder();
                    int count = ln - 9;
                    for (int i = p + skip; i < count; i++) {
                        line.append((char) (src.absByte(i) & 0xff));
                    }
                    int qmask = Util.indexOf(line, '?');
                    if (qmask > -1) {
                        parse_kv(f.getRequestParams(), line, qmask + 1, line.length(), '=', '&');
                        f.setRequestURL((String) line.subSequence(0, qmask));
                    } else {
                        f.setRequestURL(line.toString());
                    }
                    src.absPos(ln + 1);
                }
            }
            if (decode_state == decode_state_header) {
                for (;;) {
                    int ps = src.absPos();
                    int pe = read_line_range(src, f.getHeaderLength(), headerLimit);
                    if (pe == -1) {
                        break;
                    }
                    int size = pe - ps;
                    f.incrementHeaderLength(size);
                    if (size == 0) {
                        if (f.getContentLength() < 1) {
                            decode_state = decode_state_complate;
                        } else {
                            if (f.getContentLength() > bodyLimit) {
                                throw OVER_LIMIT;
                            }
                            decode_state = decode_state_body;
                        }
                        break;
                    } else {
                        if (!f.isGet()) {
                            if (startWith(src, ps, pe, CONTENT_LENGTH_MATCH)) {
                                int cp = ps + CONTENT_LENGTH_MATCH.length;
                                int cps = ByteUtil.skip(src, cp, pe, SPACE);
                                if (cps == -1) {
                                    throw OVER_LIMIT;
                                }
                                int ctLen = 0;
                                for (int i = cps; i < pe; i++) {
                                    ctLen = (src.absByte(i) - '0') + ctLen * 10;
                                }
                                f.setContentLength(ctLen);
                            }
                        }
                    }
                }
            }
            if (decode_state == decode_state_body) {
                int contentLength = f.getContentLength();
                int remain = src.remaining();
                if (remain > contentLength) {
                    src.markL();
                    src.limit(src.position() + contentLength);
                    f.setContent(src.getBytes());
                    src.resetL();
                    decode_state = decode_state_complate;
                } else if (remain == contentLength) {
                    f.setContent(src.getBytes());
                    decode_state = decode_state_complate;
                }
            }
            if (decode_state == decode_state_complate) {
                ch.removeAttribute(FRAME_DECODE_KEY);
                return f;
            } else {
                f.setDecodeState(decode_state);
                ch.setAttribute(FRAME_DECODE_KEY, f);
                return null;
            }

        } else {
            StringBuilder line = FastThreadLocal.get().getStringBuilder();
            if (decode_state == decode_state_line_one) {
                if (read_line(line, src, 0, headerLimit)) {
                    f.incrementHeaderLength(line.length());
                    decode_state = decode_state_header;
                    parse_line_one(f, line);
                }
            }
            if (decode_state == decode_state_header) {
                for (;;) {
                    line.setLength(0);
                    if (!read_line(line, src, f.getHeaderLength(), headerLimit)) {
                        break;
                    }
                    f.incrementHeaderLength(line.length());
                    if (line.length() == 0) {
                        decode_state = onHeaderReadComplete(f);
                        break;
                    } else {
                        int p = Util.indexOf(line, ':');
                        if (p == -1) {
                            continue;
                        }
                        int rp = Util.skip(line, ' ', p + 1);
                        String name = line.substring(0, p);
                        String value = line.substring(rp);
                        f.setReadHeader(name, value);
                    }
                }
            }
            if (decode_state == decode_state_body) {
                decode_state = decodeRemainBody(ch, src, f);
            }
            if (decode_state == decode_state_complate) {
                ch.removeAttribute(FRAME_DECODE_KEY);
                return f;
            } else {
                f.setDecodeState(decode_state);
                ch.setAttribute(FRAME_DECODE_KEY, f);
                return null;
            }
        }
    }

    int decodeRemainBody(Channel ch, ByteBuf src, HttpFrame f) {
        int contentLength = f.getContentLength();
        int remain = src.remaining();
        byte[] ontent = null;
        if (remain < contentLength) {
            return decode_state_body;
        } else if (remain == contentLength) {
            ontent = src.getBytes();
        } else {
            src.markL();
            src.limit(src.position() + contentLength);
            ontent = src.getBytes();
            src.resetL();
        }
        if (!f.isForm()) {
            String param = new String(ontent, ch.getCharset());
            parse_kv(f.getRequestParams(), param, 0, param.length(), '=', '&');
        } else {
            f.setContent(ontent);
        }
        return decode_state_complate;
    }

    private byte[] getContentLenBuf(FastThreadLocal l) {
        byte[] bb = (byte[]) l.getIndexedVariable(content_len_index);
        if (bb == null) {
            int limit = contentLenBuf.limit();
            bb = new byte[contentLenBuf.limit() + 16];
            contentLenBuf.get(bb, 0, limit);
            contentLenBuf.clear().limit(limit);
            l.setIndexedVariable(content_len_index, bb);
        }
        return bb;
    }

    @Override
    public ByteBuf encode(final Channel ch, Frame frame) throws IOException {
        HttpFrame f = (HttpFrame) frame;
        FastThreadLocal l = FastThreadLocal.get();
        List encode_bytes_array = getEncodeBytesArray(l);
        Object content = f.getContent();
        ByteBuf contentBuf = null;
        byte[] contentArray = null;
        byte[] head_bytes = f.getStatus().getLine();
        byte[] conn_bytes = f.getConnection().getLine();
        byte[] type_bytes = f.getContentType().getLine();
        byte[] date_bytes = f.getDate();
        boolean isArray = false;
        int write_size = 0;
        if (content instanceof ByteBuf) {
            contentBuf = ((ByteBuf) content).flip();
            write_size = contentBuf.limit();
        } else if (content instanceof byte[]) {
            isArray = true;
            contentArray = (byte[]) content;
            write_size = contentArray.length;
        }
        byte[] clen_bytes;
        int clen;
        if (write_size < 1024) {
            clen_bytes = CONTENT_LENGTHS[write_size];
            clen = clen_bytes.length;
        } else {
            clen_bytes = getContentLenBuf(l);
            int tmp_len = contentLenBuf.limit();
            int len_idx = Util.valueOf(write_size, clen_bytes);
            int num_len = clen_bytes.length - len_idx;
            System.arraycopy(clen_bytes, len_idx, clen_bytes, tmp_len, num_len);
            clen = tmp_len + num_len;
        }
        int hlen = head_bytes.length;
        int tlen = type_bytes == null ? 0 : type_bytes.length;
        int conn_len = conn_bytes == null ? 0 : conn_bytes.length;
        int dlen = date_bytes == null ? 0 : date_bytes.length + DATE.length;
        int len = hlen + clen + dlen + 2 + conn_len + tlen;
        int header_size = 0;
        int cookie_size = 0;
        IntMap headers = f.getResponseHeaders();
        if (headers != null) {
            for (headers.scan(); headers.hasNext();) {
                byte[] k = HttpHeader.get(headers.nextKey()).getBytes();
                byte[] v = headers.value();
                header_size++;
                encode_bytes_array.add(k);
                encode_bytes_array.add(v);
                len += 4;
                len += k.length;
                len += v.length;
            }
        }
        List cookieList = f.getCookieList();
        if (cookieList != null) {
            for (Cookie c : cookieList) {
                byte[] bytes = c.toString().getBytes();
                cookie_size++;
                encode_bytes_array.add(bytes);
                len += SET_COOKIE.length + 2;
                len += bytes.length;
            }
        }
        len += 2;
        if (isArray) {
            len += write_size;
        }
        ByteBuf buf;
        if (Develop.BUF_DEBUG) {
            buf = ch.allocate();
        } else {
            buf = ch.alloc().allocate(len);
        }
        buf.put(head_bytes);
        buf.put(clen_bytes, 0, clen);
        if (conn_bytes != null) {
            buf.put(conn_bytes);
        }
        if (type_bytes != null) {
            buf.put(type_bytes);
        }
        if (date_bytes != null) {
            buf.put(DATE);
            buf.put(date_bytes);
        }
        buf.putByte(R);
        buf.putByte(N);
        int j = 0;
        if (header_size > 0) {
            for (int i = 0; i < header_size; i++) {
                buf.put(encode_bytes_array.get(j++));
                buf.putByte((byte) ':');
                buf.putByte(SPACE);
                buf.put(encode_bytes_array.get(j++));
                buf.putByte(R);
                buf.putByte(N);
            }
        }
        if (cookie_size > 0) {
            for (int i = 0; i < cookie_size; i++) {
                buf.put(SET_COOKIE);
                buf.put(encode_bytes_array.get(j++));
                buf.putByte(R);
                buf.putByte(N);
            }
        }
        buf.putByte(R);
        buf.putByte(N);
        if (write_size > 0) {
            if (isArray) {
                buf.put(contentArray);
            } else {
                ch.write(buf.flip());
                ch.write(contentBuf);
                return null;
            }
        }
        return buf.flip();
    }

    public int getBodyLimit() {
        return bodyLimit;
    }

    public int getHeaderLimit() {
        return headerLimit;
    }

    public int getHttpFrameStackSize() {
        return httpFrameBuffer;
    }

    @Override
    public String getProtocolId() {
        return "HTTP1.1";
    }

    @Override
    public int headerLength() {
        return 0;
    }

    int onHeaderReadComplete(HttpFrame f) throws IOException {
        int contentLength = 0;
        String clength = f.getRequestHeader(Content_Length);
        String ctype = f.getRequestHeader(Content_Type);
        String cookie = f.getRequestHeader(Cookie);
        f.setForm(ctype == null ? false : ctype.startsWith("multipart/form-data;"));
        if (!Util.isNullOrBlank(clength)) {
            contentLength = Integer.parseInt(clength);
            f.setContentLength(contentLength);
        }
        if (!Util.isNullOrBlank(cookie)) {
            parse_cookies(f, cookie);
        }
        if (contentLength < 1) {
            return decode_state_complate;
        } else {
            if (contentLength > bodyLimit) {
                throw OVER_LIMIT;
            }
            return decode_state_body;
        }
    }

    private void parse_cookies(HttpFrame f, String line) {
        Map cookies = f.getCookies();
        if (cookies == null) {
            cookies = new HashMap<>();
            f.setCookies(cookies);
        }
        parse_kv(cookies, line, 0, line.length(), '=', ';');
    }

    void parse_kv(Map map, CharSequence line, int start, int end, char kvSplitor,
            char eSplitor) {
        int state_findKey = 0;
        int state_findValue = 1;
        int state = state_findKey;
        int count = end;
        int i = start;
        int ks = start;
        int vs = 0;
        CharSequence key = null;
        CharSequence value = null;
        for (; i != count;) {
            char c = line.charAt(i++);
            if (state == state_findKey) {
                if (c == kvSplitor) {
                    key = line.subSequence(ks, i - 1);
                    state = state_findValue;
                    vs = i;
                    continue;
                }
            } else if (state == state_findValue) {
                if (c == eSplitor) {
                    value = line.subSequence(vs, i - 1);
                    state = state_findKey;
                    ks = i;
                    map.put((String) key, (String) value);
                    continue;
                }
            }
        }
        if (state == state_findValue && end > vs) {
            map.put((String) key, (String) line.subSequence(vs, end));
        }
    }

    protected void parse_line_one(HttpFrame f, CharSequence line) {
        if (line.charAt(0) == 'G' && line.charAt(1) == 'E' && line.charAt(2) == 'T') {
            f.setMethod(HttpMethod.GET);
            parseRequestURL(f, 4, line);
        } else {
            f.setMethod(HttpMethod.POST);
            parseRequestURL(f, 5, line);
        }
    }

    protected void parseRequestURL(HttpFrame f, int skip, CharSequence line) {
        int index = Util.indexOf(line, '?');
        int lastSpace = Util.lastIndexOf(line, ' ');
        if (index > -1) {
            parse_kv(f.getRequestParams(), line, index + 1, lastSpace, '=', '&');
            f.setRequestURL((String) line.subSequence(skip, index));
        } else {
            f.setRequestURL((String) line.subSequence(skip, lastSpace));
        }
    }

    @Override
    public void release(NioEventLoop eventLoop, Frame frame) {
        eventLoop.releaseFrame(FRAME_BUFFER_KEY, frame);
    }

    class DBsH {
        long   time;
        byte[] value;
    }

    static class HttpDateTimeClock implements Runnable {

        volatile Thread  owner;

        volatile boolean running;

        volatile long    time;

        @Override
        public void run() {
            owner = Thread.currentThread();
            running = true;
            for (; running;) {
                time = System.currentTimeMillis();
                Util.sleep(1000);
            }
        }

        void stop() {
            running = false;
            Thread t = owner;
            if (t != null) {
                t.interrupt();
            }
        }

    }

    static byte[] b(String s) {
        return s.getBytes();
    }

    private static int findN(ByteBuf src, int p) {
        src.absPos(p + 1);
        p--;
        if (src.absByte(p) == R) {
            return p;
        } else {
            return ++p;
        }
    }

    @SuppressWarnings("unchecked")
    static List getEncodeBytesArray(FastThreadLocal l) {
        return (List) l.getList(encode_bytes_arrays_index);
    }

    protected static String parseBoundary(String contentType) {
        int index = KMP_BOUNDARY.match(contentType);
        if (index != -1) {
            return contentType.substring(index + 9);
        }
        return null;
    }

    private static boolean read_line(StringBuilder line, ByteBuf src, int length, int limit)
            throws IOException {
        src.markP();
        int maybeRead = limit - length;
        if (src.remaining() > maybeRead) {
            int count = src.absPos() + maybeRead;
            for (int i = src.absPos(); i < count; i++) {
                byte b = src.absByte(i);
                if (b == N) {
                    int p = line.length() - 1;
                    if (line.charAt(p) == R) {
                        line.setLength(p);
                    }
                    src.absPos(i + 1);
                    return true;
                } else {
                    line.append((char) (b & 0xff));
                }
            }
            throw OVER_LIMIT;
        } else {
            int count = src.absPos() + src.remaining();
            for (int i = src.absPos(); i < count; i++) {
                byte b = src.absByte(i);
                if (b == N) {
                    int p = line.length() - 1;
                    if (line.charAt(p) == R) {
                        line.setLength(p);
                    }
                    src.absPos(i + 1);
                    return true;
                } else {
                    line.append((char) (b & 0xff));
                }
            }
        }
        src.resetP();
        return false;
    }

    private static int read_line_range(ByteBuf src, int length, int limit) throws IOException {
        src.markP();
        int p;
        int maybeRead = limit - length;
        if (src.remaining() > maybeRead) {
            p = src.indexOf(N, src.absPos(), maybeRead);
            if (p == -1) {
                throw OVER_LIMIT;
            }
        } else {
            p = src.indexOf(N);
            if (p == -1) {
                src.resetP();
                return -1;
            }
        }
        return findN(src, p);
    }

    private static boolean startWith(ByteBuf src, int ps, int pe, byte[] match) {
        if (pe - ps < match.length) {
            return false;
        }
        for (int i = 0; i < match.length; i++) {
            if (src.absByte(ps + i) != match[i]) {
                return false;
            }
        }
        return true;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy