view asyncdreactor/protocol/http11_parser.rl @ 11:5836613d16ac

reorg! reorg!
author rick@minifunk
date Tue, 12 Aug 2008 16:59:56 -0400
parents
children
line wrap: on
line source

/**
 * Copyright (c) 2005 Zed A. Shaw, 
 * Modified for D/DReactor by Rick Richardson, 2008
 * You can redistribute it and/or modify it under the same terms as Ruby.
 */

alias void delegate (void *data, const char *at, size_t length) element_cb;
alias void delegate (void *data, const char *field, size_t flen, const char *value, size_t vlen) field_cb;

class Http11Parser
{
  int cs;
  size_t body_start;
  int content_len;
  size_t nread;
  size_t mark;
  size_t field_start;
  size_t field_len;
  size_t query_start;

  void *data;

  field_cb http_field;
  element_cb request_method;
  element_cb request_uri;
  element_cb fragment;
  element_cb request_path;
  element_cb query_string;
  element_cb http_version;
  element_cb header_done;
  
/*
 * capitalizes all lower-case ASCII characters,
 * converts dashes to underscores.
 */
private void snake_upcase_char(char *c)
{
    if (*c >= 'a' && *c <= 'z')
      *c &= ~0x20;
    else if (*c == '-')
      *c = '_';
}

//#define LEN(AT, FPC) (FPC - buffer - parser->AT)
private int LEN(char* at, char* fpc)
{
    return (fpc - buffer.ptr - at);
}

//#define MARK(M,FPC) (parser->M = (FPC) - buffer)
private void MARK(size_t* item, char* fpc)
{
    *item = fpc - buffer.ptr;
}

//#define PTR_TO(F) (buffer + parser->F)
private char* PTR_TO(size_t F)
{
    return (buffer.ptr + F);
}
/** Machine **/

%%{
  
  machine http_parser;

  action mark {MARK(mark, fpc); }


  action start_field { MARK(field_start, fpc); }
  action snake_upcase_field { snake_upcase_char((char *)fpc); }
  action write_field { 
    field_len = LEN(field_start, fpc);
  }

  action start_value { MARK(mark, fpc); }
  action write_value { 
    if(http_field) 
      http_field(data, 
                 PTR_TO(field_start), 
                 field_len, 
                 PTR_TO(mark), 
                 LEN(mark, fpc));
  }
  action request_method { 
    if(request_method) 
      request_method(data, PTR_TO(mark), LEN(mark, fpc));
  }

  action request_uri { 
    if(request_uri)
      request_uri(data, PTR_TO(mark), LEN(mark, fpc));
  }

  action fragment { 
    if(fragment)
      fragment(data, PTR_TO(mark), LEN(mark, fpc));
  }

  action start_query {MARK(query_start, fpc); }

  action query_string { 
    if(query_string)
      query_string(data, PTR_TO(query_start), LEN(query_start, fpc));
  }

  action http_version {	
    if(http_version)
      http_version(data, PTR_TO(mark), LEN(mark, fpc));
  }

  action request_path {
    if(request_path)
      request_path(data, PTR_TO(mark), LEN(mark,fpc));
  }

  action done { 
    body_start = fpc - buffer + 1; 
    if(header_done)
      header_done(data, fpc + 1, pe - fpc - 1);
    fbreak;
  }

  include http_parser_common "http11_parser_common.rl";

}%%

/** Data **/
%% write data;

this ()  
{
  cs = 0;
  %% write init;
  
  body_start = 0;
  content_len = 0;
  mark = 0;
  nread = 0;
  field_len = 0;
  field_start = 0;    
}


/** exec **/
size_t execute(const char[] buffer, size_t off)  {
  const char *p, *pe;
  int len = buffer.length;

  assert(off <= len && "offset past end of buffer");

  p = buffer.ptr+off;
  pe = buffer.ptr+len;

  /* assert(*pe == '\0' && "pointer does not end on NUL"); */
  assert(pe - p == len - off && "pointers aren't same distance");

  %% write exec;

  //parser->cs = cs;
  nread += p - (buffer.ptr + off);

  assert(p <= pe && "buffer overflow after parsing execute");
  assert(nread <= len && "nread longer than length");
  assert(body_start <= len && "body starts after buffer end");
  assert(mark < len && "mark is after buffer end");
  assert(field_len <= len && "field has length longer than whole buffer");
  assert(field_start < len && "field starts after buffer end");

  return(nread);
}

int finish()
{
  if (has_error() ) {
    return -1;
  } else if (is_finished() ) {
    return 1;
  } else {
    return 0;
  }
}

bool has_error() {
  return cs == http_parser_error;
}

bool is_finished() {
  return cs >= http_parser_first_final;
}

}