Package erjang.driver.tcp_inet

Source Code of erjang.driver.tcp_inet.Packet$HTTPAtom

/**
* This file is part of Erjang - A JVM-based Erlang VM
*
* Copyright (c) 2010 by Trifork
*
* 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 erjang.driver.tcp_inet;

import java.nio.ByteBuffer;

import kilim.Pausable;

import erjang.EAtom;
import erjang.NotImplemented;
import erjang.driver.IO;
import erjang.driver.tcp_inet.PacketHttpURI.URIType;

public class Packet {

  private static final int HTTP_HDR_HASH_SIZE = 53;
  private static final int HTTP_METH_HASH_SIZE = 13;
  private static final int HTTP_MAX_NAME_LEN = 20;

  public static void get_body(PacketParseType htype, ByteBuffer out) {

    switch (htype) {
    case TCP_PB_1:
      out.position(out.position() + 1);
      break;
    case TCP_PB_2:
      out.position(out.position() + 2);
      break;
    case TCP_PB_4:
      out.position(out.position() + 4);
      break;
    case TCP_PB_FCGI:
      out.limit(out.limit() - (0xff & out.get(6)));
      break;
    default:
      ;/* Return other packets "as is" */
    }

  }

  public static <T> int parse(PacketParseType htype, byte[] data, int buf,
      int len, IntCell statep, PacketCallbacks<T> pcb, T arg) throws Pausable {

    switch (htype) {
    case TCP_PB_HTTP:
    case TCP_PB_HTTPH:
    case TCP_PB_HTTP_BIN:
    case TCP_PB_HTTPH_BIN:
      if (parse_http(data, buf, len, statep, pcb, arg) < 0)
        pcb.http_error(arg, data, buf, len);
      return 1;
    case TCP_PB_SSL_TLS:
      return parse_ssl(data, buf, len, pcb, arg);
    default:
    }
    return 0;

  }

  public static <T> int parse_http(byte[] data, int buf, int len,
      IntCell statep, PacketCallbacks<T> pcb, T arg) throws Pausable {

    int ptr = buf;
    int p0;
    int n = len;

    /* remove trailing CRNL (accept NL as well) */
    if ((n >= 2) && (data[buf + n - 2] == '\r'))
      n -= 2;
    else if ((n >= 1) && (data[buf + n - 1] == '\n'))
      n -= 1;

    if (statep.get() == 0) {
      /* start-line = Request-Line | Status-Line */

      if (n >= 5 && (strncmp(data, buf, "HTTP/", 5) == 0)) {
        int major = 0;
        int minor = 0;
        int status = 0;
        /*
         * Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase
         * CRNL HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
         */
        ptr += 5;
        n -= 5;
        p0 = ptr;
        while (n != 0 && isdigit(data, ptr)) {
          major = 10 * major + (data[ptr] - '0');
          ptr++;
          n--;
        }
        if (ptr == p0 || n == 0 || (data[ptr] != '.'))
          return -1;
        ptr++;
        n--;
        p0 = ptr;
        while (n != 0 && isdigit((int) data[ptr])) {
          minor = 10 * minor + (data[ptr] - '0');
          ptr++;
          n--;
        }
        if (ptr == p0)
          return -1;
        p0 = ptr;
        while (n != 0 && SP(data, ptr)) {
          ptr++;
          n--;
        }
        if (ptr == p0)
          return -1;

        while (n != 0 && isdigit((int) data[ptr])) {
          status = 10 * status + (data[ptr] - '0');
          ptr++;
          n--;
        }
        p0 = ptr;
        while (n != 0 && SP(data, ptr)) {
          ptr++;
          n--;
        }
        if (ptr == p0)
          return -1;

        /* NOTE: the syntax allows empty reason phrases */
        statep.set(~0);

        return pcb.http_response(arg, major, minor, status, data, ptr,
            n);
      } else {
        /* Request-Line = Method SP Request-URI SP HTTP-Version CRLF */
        HTTPAtom meth;
        int meth_ptr = buf;
        int meth_len;
        PacketHttpURI uri;
        int uri_ptr;
        int uri_len;
        int major = 0;
        int minor = 0;
        int h = 0;

        while (n != 0 && !is_tspecial(data[ptr])) {
          h = hash_update(h, data[ptr]);
          ptr++;
          n--;
        }
        meth_len = ptr - meth_ptr;
        if (n == 0 || meth_len == 0 || !SP(data, ptr))
          return -1;

        meth = http_hash_lookup(data, meth_ptr, meth_len, h,
            http_meth_hash);

        while (n != 0 && SP(data, ptr)) {
          ptr++;
          n--;
        }
        uri_ptr = ptr;
        while (n != 0 && !SP(data, ptr)) {
          ptr++;
          n--;
        }
        if ((uri_len = (ptr - uri_ptr)) == 0)
          return -1;
        while (n != 0 && SP(data, ptr)) {
          ptr++;
          n--;
        }
        if (n == 0) {
          statep.set(~0);
          uri = http_parse_uri(data, uri_ptr, uri_len);
          return pcb.http_request(arg, meth, data, meth_ptr,
              meth_len, uri, 0, 9);
        }
        if (n < 8)
          return -1;
        if (strncmp(data, ptr, "HTTP/", 5) != 0)
          return -1;
        ptr += 5;
        n -= 5;

        p0 = ptr;
        while (n != 0 && isdigit((int) data[ptr])) {
          major = 10 * major + (data[ptr] - '0');
          ptr++;
          n--;
        }
        if (ptr == p0 || n == 0 || (data[ptr] != '.'))
          return -1;
        ptr++;
        n--;
        p0 = ptr;
        while (n != 0 && isdigit((int) data[ptr])) {
          minor = 10 * minor + (data[ptr] - '0');
          ptr++;
          n--;
        }
        if (ptr == p0)
          return -1;

        statep.set(~0);
        uri = http_parse_uri(data, uri_ptr, uri_len);
        return pcb.http_request(arg, meth, data, meth_ptr, meth_len,
            uri, major, minor);
      }
    } else {
      int up = 1; /* make next char uppercase */
      HTTPAtom name;
      byte[] name_buf = new byte[HTTP_MAX_NAME_LEN];
      int name_ptr = 0;
      int name_len;
      int h;

      if (n == 0) {
        /* end of headers */
        statep.set(0); /* reset state (for next request) */
        return pcb.http_eoh(arg);
      }
      h = 0;
      name_len = 0;
      while (!is_tspecial(data[ptr])) {
        if (name_len < HTTP_MAX_NAME_LEN) {
          byte c = data[ptr];
          if (up != 0) {
            if (islower(c)) {
              c = toupper(c);
            }
            up = 0;
          } else {
            if (isupper(c))
              c = tolower(c);
            else if (c == '-')
              up = 1;
          }
          name_buf[name_len] = c;
          h = hash_update(h, c);
        }
        name_len++;
        ptr++;
        if (--n == 0)
          return -1;
      }
      while (n != 0 && SP(data, ptr)) { /* Skip white space before ':' */
        ptr++;
        n--;
      }
      if (data[ptr] != ':') {
        return -1;
      }
      if (name_len <= HTTP_MAX_NAME_LEN) {
        name = http_hash_lookup(name_buf, 0, name_len, h, http_hdr_hash);
      } else {
        /* Is it ok to return original name without case adjustments? */
        name_ptr = buf;
        name = null;
      }
      ptr++;
      n--;
      /* Skip white space after ':' */
      while (n != 0 && SP(data, ptr)) {
        ptr++;
        n--;
      }
      return pcb.http_header(arg, name, name_buf, name_ptr, name_len,
          data, ptr, n);
    }

    // unreachable:
    // return -1;

  }

  private static byte tolower(byte c) {
    return (byte) Character.toLowerCase(c);
  }

  private static boolean isupper(byte c) {
    return Character.isUpperCase(c);
  }

  private static byte toupper(byte c) {
    return (byte) Character.toUpperCase(c);
  }

  private static boolean islower(byte c) {
    return Character.isLowerCase(c);
  }

  private static HTTPAtom http_hash_lookup(byte[] data, int ptr, int len,
      int h, HTTPAtom[] hash) {

    int ix = Math.abs(h) % hash.length;
    for (HTTPAtom entry = hash[ix]; entry != null; entry = entry.next) {

      if (h == entry.h && len == entry.len
          && strncmp(data, ptr, entry.name, 0, len) == 0) {
        return entry;
      }
    }

    return null;
  }

  /** return 0 on success
   * @param string_ptr TODO*/
  private static int strncmp(byte[] data, int ptr, byte[] string, int string_ptr, int i) {
    for (int p = 0; p < i; p++) {
      int val = string[string_ptr+p] - data[ptr+p];
      if (val != 0)
        return val;
    }

    return 0;
  }

  /** return 0 on success */
  private static int strncmp(byte[] data, int ptr, String string, int i) {
    for (int p = 0; p < i; p++) {
      int val = string.charAt(p) - data[ptr+p];
      if (val != 0)
        return val;
    }

    return 0;
  }

  /*
   * * Handle URI syntax:** Request-URI = "*" | absoluteURI | abs_path*
   * absoluteURI = scheme ":" *( uchar | reserved )* net_path = "//" net_loc [
   * abs_path ]* abs_path = "/" rel_path* rel_path = [ path ] [ ";" params ] [
   * "?" query ]* path = fsegment *( "/" segment )* fsegment = 1*pchar*
   * segment = *pchar* params = param *( ";" param )* param = *( pchar | "/" )
   * * query = *( uchar | reserved )** http_URL = "http:" "//" host [ ":" port
   * ] [ abs_path ]** host = <A legal Internet host domain name* or IP address
   * (in dotted-decimal form),* as defined by Section 2.1 of RFC 1123>* port =
   * *DIGIT** {absoluteURI, <scheme>, <host>, <port>, <path+params+query>}*
   * when <scheme> = http | https* {scheme, <scheme>, <chars>}* wheb <scheme>
   * is something else then http or https* {abs_path, <path>}** <string>
   * (unknown form)*
   */

  private static PacketHttpURI http_parse_uri(byte[] data, int uri_ptr,
      int uri_len) {

    PacketHttpURI uri = new PacketHttpURI();

    if ((uri_len == 1) && (data[uri_ptr + 0] == '*'))
      uri.type = URIType.URI_STAR;
    else if ((uri_len <= 1) || (data[uri_ptr + 0] == '/')) {
      uri.type = URIType.URI_ABS_PATH;
      uri.s1_data = data;
      uri.s1_ptr = uri_ptr;
      uri.s1_len = uri_len;
    } else if ((uri_len >= 7)
        && (STRNCASECMP(data, uri_ptr, "http://", 7) == 0)) {
      uri_len -= 7;
      uri_ptr += 7;
      uri.type = URIType.URI_HTTP;
      http_parse_absoluteURI(uri, data, uri_ptr, uri_len);
    } else if ((uri_len >= 8)
        && (STRNCASECMP(data, uri_ptr, "https://", 8) == 0)) {
      uri_len -= 8;
      uri_ptr += 8;
      uri.type = URIType.URI_HTTPS;
      http_parse_absoluteURI(uri, data, uri_ptr, uri_len);
    } else {
      int ptr;
      if ((ptr = memchr(data, uri_ptr, ':', uri_len)) == -1) {
        uri.type = URIType.URI_STRING;
        uri.s1_ptr = uri_ptr;
        uri.s1_len = uri_len;
      } else {
        int slen = ptr - uri_ptr;
        uri.type = URIType.URI_SCHEME;
        uri.s1_ptr = uri_ptr;
        uri.s1_len = slen;
        uri.s2_data = data;
        uri.s2_ptr = uri_ptr + (slen + 1);
        uri.s2_len = uri_len - (slen + 1);
      }
    }

    return uri;
  }

  static final byte[] SLASH = new byte[] { '/' };
 
  private static void http_parse_absoluteURI(PacketHttpURI uri, byte[] data,
      int uri_ptr, int uri_len) {

      int p;
     
      if ((p = memchr(data, uri_ptr, '/', uri_len)) == -1) {
          /* host [":" port] */
       
        uri.s2_data = SLASH;
          uri.s2_ptr = 0;
          uri.s2_len = 1;
      }
      else {
          int n = (p - uri_ptr);
          uri.s2_data = data;
          uri.s2_ptr = p;
          uri.s2_len = uri_len - n;
          uri_len = n;
      }

      uri.s1_data = data;
      uri.s1_ptr = uri_ptr;
      uri.port = 0; /* undefined */
      /* host[:port]  */
      if ((p = memchr(data, uri_ptr, ':', uri_len)) == -1) {
          uri.s1_len = uri_len;
      }
      else {
          int n = (p - uri_ptr);
          int port = 0;       
          uri.s1_len = n;
          n = uri_len - (n+1);
          p++;
          while(n!=0 && isdigit(data[p])) {
              port = port*10 + (data[p] - '0');
              n--;
              p++;
          }
          if (n==0 && port!=0)
              uri.port = port;
    }
   
  }

  private static int STRNCASECMP(byte[] data, int s1, String s2, int n) {

    int i;

    for (i = 0; i < n - 1 && data[s1 + i] != 0 && i < s2.length()
        && toupper(data[s1 + i]) == toupper((byte) s2.charAt(i)); ++i)
      ;
    return (toupper(data[s1 + i]) - toupper((byte) s2.charAt(i)));

  }

  static class HTTPAtom {
    private final HTTPAtom next;
    private final int h;
    private final byte[] name;
    private final int len;
    final EAtom atom;
    final int index;

    public HTTPAtom(String am, int index, HTTPAtom[] hash) {
      this.index = index;
      this.atom = EAtom.intern(am);
      this.name = am.getBytes(IO.ISO_LATIN_1);

      int ptr = 0, len = 0, h = 0;
      while (ptr != name.length) {
        h = hash_update(h, name[ptr]);
        ptr++;
        len++;
      }
      int ix = h % hash.length;

      this.len = len;
      this.h = h;

      this.next = hash[ix];
      hash[ix] = this;

    }
  }

  static boolean[] tspecial = new boolean[128];
  static HTTPAtom[] http_hdr_hash = new HTTPAtom[HTTP_HDR_HASH_SIZE];
  static HTTPAtom[] http_meth_hash = new HTTPAtom[HTTP_METH_HASH_SIZE];

  static String http_hdr_strings[] = { "Cache-Control", "Connection", "Date",
      "Pragma", "Transfer-Encoding", "Upgrade", "Via", "Accept",
      "Accept-Charset", "Accept-Encoding", "Accept-Language",
      "Authorization", "From", "Host", "If-Modified-Since", "If-Match",
      "If-None-Match", "If-Range", "If-Unmodified-Since", "Max-Forwards",
      "Proxy-Authorization", "Range", "Referer", "User-Agent", "Age",
      "Location", "Proxy-Authenticate", "Public", "Retry-After",
      "Server", "Vary", "Warning", "Www-Authenticate", "Allow",
      "Content-Base", "Content-Encoding", "Content-Language",
      "Content-Length", "Content-Location", "Content-Md5",
      "Content-Range", "Content-Type", "Etag", "Expires",
      "Last-Modified", "Accept-Ranges", "Set-Cookie", "Set-Cookie2",
      "X-Forwarded-For", "Cookie", "Keep-Alive", "Proxy-Connection", null };

  static String http_meth_strings[] = { "OPTIONS", "GET", "HEAD", "POST",
      "PUT", "DELETE", "TRACE", null };

  static {
    int i;

    for (i = 0; i < 33; i++)
      tspecial[i] = true;
    String specials = "()<>@,;:\\\"/[]?={} \t";
    for (i = 0; i < specials.length(); i++) {
      char val = specials.charAt(i);
      tspecial[val] = true;
    }

    for (i = 0; i < HTTP_HDR_HASH_SIZE; i++)
      http_hdr_hash[i] = null;
    for (i = 0; http_hdr_strings[i] != null; i++) {
      assert (http_hdr_strings[i].length() <= HTTP_MAX_NAME_LEN);
      new HTTPAtom(http_hdr_strings[i], i, http_hdr_hash);
    }

    for (i = 0; i < HTTP_METH_HASH_SIZE; i++)
      http_hdr_hash[i] = null;
    for (i = 0; http_meth_strings[i] != null; i++) {
      new HTTPAtom(http_meth_strings[i], i, http_meth_hash);
    }
  }

  private static boolean is_tspecial(byte x) {
    if (x < 0) return false;
    return tspecial[x];
  }

  static final int hash_update(int h, int c) {
    c &= 0xff;
    int __g;
    (h) = ((h) << 4) + (c);
    if ((__g = (h) & 0xf0000000) != 0) {
      (h) ^= (__g >>> 24);
      (h) ^= __g;
    }

    return h;
  }

  private static boolean isdigit(byte[] data, int ptr) {
    byte ch = data[ptr];
    return (ch >= '0' && ch <= '9');
  }

  private static boolean isdigit(int data) {
    return (data >= '0' && data <= '9');
  }

  public static <T> int parse_ssl(byte[] buf, int start, int len,
      PacketCallbacks<T> pcb, T arg) {
    throw new NotImplemented();
  }

  /*
   * Return > 0 Total packet length.in bytes = 0 Length unknown, need more
   * data. < 0 Error, invalid format.
   */
  public static int get_length(PacketParseType htype, byte[] data, int ptr,
      int n, int max_plen, int trunc_len, IntCell statep) {

    int hlen, plen;

    switch (htype) {
    case TCP_PB_RAW:
      if (n == 0)
        return 0;
      else
        return n;

    case TCP_PB_1:
      hlen = 1;
      if (n < hlen)
        return 0;
      plen = data[ptr] & 0xff;
      break;

    case TCP_PB_2:
      hlen = 2;
      if (n < hlen)
        return 0;

      plen = ((data[ptr] & 0xff) << 8) | (data[ptr + 1] & 0xff);
      break;

    case TCP_PB_4:
      hlen = 4;
      if (n < hlen)
        return 0;

      plen = ((data[ptr] & 0xff) << 24) | ((data[ptr + 1] & 0xff) << 16)
          | ((data[ptr + 2] & 0xff) << 8) | (data[ptr + 3] & 0xff);
      break;

    case TCP_PB_LINE_LF: {
      int ptr2;
      if ((ptr2 = memchr(data, ptr, '\n', n)) == -1) {
        if (n >= trunc_len && trunc_len != 0) {
          return trunc_len;
        }
        return 0;
      }

      int len = (ptr2 - ptr) + 1;
      if (len > trunc_len && trunc_len != 0) {
        return trunc_len;
      }
      return len;
    }

    case TCP_PB_HTTPH:
    case TCP_PB_HTTPH_BIN:
      statep.set(~0);
    case TCP_PB_HTTP:
    case TCP_PB_HTTP_BIN:
      /* TCP_PB_HTTP: data \r\n(SP data\r\n)* */
      plen = n;
      if (((plen == 1) && NL(data, ptr))
          || ((plen == 2) && CRNL(data, ptr)))
        return plen;
      else {
        int ptr1 = ptr;
        int len = plen;

        while (true) {
          int ptr2 = memchr(data, ptr1, '\n', len);

          if (ptr2 == -1) {
            if (n >= trunc_len && trunc_len != 0) { /* buffer full */
              plen = trunc_len;
              return plen;
            }
            return 0;
          } else {
            plen = (ptr2 - ptr) + 1;

            if (statep.get() == 0)
              return plen;

            if (plen < n) {
              if (SP(data, ptr2 + 1) && plen > 2) {
                /* header field value continue on next line */
                ptr1 = ptr2 + 1;
                len = n - plen;
              } else
                return plen;
            } else
              return 0;
          }
        }
      }

    default:
      throw new NotImplemented("packet parser for " + htype);
    }

    // remain case...
    int tlen = hlen + plen;
    if ((max_plen != 0 && plen > max_plen) || tlen < hlen) {
      return -1;
    }
    return tlen;

  }

  private static boolean SP(byte[] data, int ptr) {
    return data[ptr] == ' ' || data[ptr] == '\t';
  }

  private static boolean CRNL(byte[] data, int ptr) {
    return data[ptr] == '\r' && data[ptr + 1] == '\n';
  }

  private static boolean NL(byte[] data, int ptr) {
    return data[ptr] == '\n';
  }

  private static int memchr(byte[] data, int offset, char c, int n) {
    for (int i = 0; i < n; i++) {
      if (data[offset + i] == c)
        return offset + i;
    }
    return -1;
  }

}
TOP

Related Classes of erjang.driver.tcp_inet.Packet$HTTPAtom

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.