unicorn.git  about / heads / tags
Rack HTTP server for Unix and fast clients
blob f69b618d522903970666a56a6328ced672907cc6 2381 bytes (raw)
$ git show gperf:ext/unicorn_http/common_field_optimization.h	# shows this blob on the CLI

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
 
#ifndef common_field_optimization
#define common_field_optimization
#include "ruby.h"
#include "c_util.h"

/*
 * A list of common HTTP headers we expect to receive.
 * This allows us to avoid repeatedly creating identical string
 * objects to be used with rb_hash_aset().
 */
#include "common_fields.h"

#define HTTP_PREFIX "HTTP_"
#define HTTP_PREFIX_LEN (sizeof(HTTP_PREFIX) - 1)
static ID id_uminus;

/* this dedupes under Ruby 2.5+ (December 2017) */
static VALUE str_dd_freeze(VALUE str)
{
  if (STR_UMINUS_DEDUPE)
    return rb_funcall(str, id_uminus, 0);

  /* freeze,since it speeds up older MRI slightly */
  OBJ_FREEZE(str);
  return str;
}

static VALUE str_new_dd_freeze(const char *ptr, long len)
{
  return str_dd_freeze(rb_str_new(ptr, len));
}

/* this function is not performance-critical, called only at load time */
static void init_common_fields(void)
{
  size_t i;
  char tmp[64];

  id_uminus = rb_intern("-@");
  memcpy(tmp, HTTP_PREFIX, HTTP_PREFIX_LEN);

  for (i = 0; i < ARRAY_SIZE(cf_wordlist); i++) {
    long len = (long)cf_lengthtable[i];
    struct common_field *cf = &cf_wordlist[i];
    const char *s;

    if (!len)
      continue;

    s = cf->name + cf_stringpool;
    /* Rack doesn't like certain headers prefixed with "HTTP_" */
    if (!strcmp("CONTENT_LENGTH", s) || !strcmp("CONTENT_TYPE", s)) {
      cf->value = str_new_dd_freeze(s, len);
    } else {
      memcpy(tmp + HTTP_PREFIX_LEN, s, len + 1);
      cf->value = str_new_dd_freeze(tmp, HTTP_PREFIX_LEN + len);
    }
    rb_gc_register_mark_object(cf->value);
  }
}

/* this function is called for every header set */
static VALUE find_common_field(const char *field, size_t flen)
{
  struct common_field *cf = cf_lookup(field, flen);

  if (cf) {
    assert(cf->value);
    return cf->value;
  }
  return Qnil;
}

/*
 * We got a strange header that we don't have a memoized value for.
 * Fallback to creating a new string to use as a hash key.
 */
static VALUE uncommon_field(const char *field, size_t flen)
{
  VALUE f = rb_str_new(NULL, HTTP_PREFIX_LEN + flen);
  memcpy(RSTRING_PTR(f), HTTP_PREFIX, HTTP_PREFIX_LEN);
  memcpy(RSTRING_PTR(f) + HTTP_PREFIX_LEN, field, flen);
  assert(*(RSTRING_PTR(f) + RSTRING_LEN(f)) == '\0' &&
         "string didn't end with \\0"); /* paranoia */
  return HASH_ASET_DEDUPE ? f : str_dd_freeze(f);
}

#endif /* common_field_optimization_h */

git clone https://yhbt.net/unicorn.git