allow Ruby to deduplicate remaining globals
[unicorn.git] / ext / unicorn_http / common_field_optimization.h
blob250e43efa06500b9bdeaa1554425a656d8645a9d
1 #ifndef common_field_optimization
2 #define common_field_optimization
3 #include "ruby.h"
4 #include "c_util.h"
6 struct common_field {
7 const signed long len;
8 const char *name;
9 VALUE value;
13 * A list of common HTTP headers we expect to receive.
14 * This allows us to avoid repeatedly creating identical string
15 * objects to be used with rb_hash_aset().
17 static struct common_field common_http_fields[] = {
18 # define f(N) { (sizeof(N) - 1), N, Qnil }
19 f("ACCEPT"),
20 f("ACCEPT_CHARSET"),
21 f("ACCEPT_ENCODING"),
22 f("ACCEPT_LANGUAGE"),
23 f("ALLOW"),
24 f("AUTHORIZATION"),
25 f("CACHE_CONTROL"),
26 f("CONNECTION"),
27 f("CONTENT_ENCODING"),
28 f("CONTENT_LENGTH"),
29 f("CONTENT_TYPE"),
30 f("COOKIE"),
31 f("DATE"),
32 f("EXPECT"),
33 f("FROM"),
34 f("HOST"),
35 f("IF_MATCH"),
36 f("IF_MODIFIED_SINCE"),
37 f("IF_NONE_MATCH"),
38 f("IF_RANGE"),
39 f("IF_UNMODIFIED_SINCE"),
40 f("KEEP_ALIVE"), /* Firefox sends this */
41 f("MAX_FORWARDS"),
42 f("PRAGMA"),
43 f("PROXY_AUTHORIZATION"),
44 f("RANGE"),
45 f("REFERER"),
46 f("TE"),
47 f("TRAILER"),
48 f("TRANSFER_ENCODING"),
49 f("UPGRADE"),
50 f("USER_AGENT"),
51 f("VIA"),
52 f("X_FORWARDED_FOR"), /* common for proxies */
53 f("X_FORWARDED_PROTO"), /* common for proxies */
54 f("X_REAL_IP"), /* common for proxies */
55 f("WARNING")
56 # undef f
59 #define HTTP_PREFIX "HTTP_"
60 #define HTTP_PREFIX_LEN (sizeof(HTTP_PREFIX) - 1)
61 static ID id_uminus;
63 /* this dedupes under Ruby 2.5+ (December 2017) */
64 static VALUE str_dd_freeze(VALUE str)
66 if (STR_UMINUS_DEDUPE)
67 return rb_funcall(str, id_uminus, 0);
69 /* freeze,since it speeds up older MRI slightly */
70 OBJ_FREEZE(str);
71 return str;
74 static VALUE str_new_dd_freeze(const char *ptr, long len)
76 return str_dd_freeze(rb_str_new(ptr, len));
79 /* this function is not performance-critical, called only at load time */
80 static void init_common_fields(void)
82 int i;
83 struct common_field *cf = common_http_fields;
84 char tmp[64];
86 memcpy(tmp, HTTP_PREFIX, HTTP_PREFIX_LEN);
88 for(i = ARRAY_SIZE(common_http_fields); --i >= 0; cf++) {
89 /* Rack doesn't like certain headers prefixed with "HTTP_" */
90 if (!strcmp("CONTENT_LENGTH", cf->name) ||
91 !strcmp("CONTENT_TYPE", cf->name)) {
92 cf->value = str_new_dd_freeze(cf->name, cf->len);
93 } else {
94 memcpy(tmp + HTTP_PREFIX_LEN, cf->name, cf->len + 1);
95 cf->value = str_new_dd_freeze(tmp, HTTP_PREFIX_LEN + cf->len);
97 rb_gc_register_mark_object(cf->value);
101 /* this function is called for every header set */
102 static VALUE find_common_field(const char *field, size_t flen)
104 int i;
105 struct common_field *cf = common_http_fields;
107 for(i = ARRAY_SIZE(common_http_fields); --i >= 0; cf++) {
108 if (cf->len == (long)flen && !memcmp(cf->name, field, flen))
109 return cf->value;
111 return Qnil;
115 * We got a strange header that we don't have a memoized value for.
116 * Fallback to creating a new string to use as a hash key.
118 static VALUE uncommon_field(const char *field, size_t flen)
120 VALUE f = rb_str_new(NULL, HTTP_PREFIX_LEN + flen);
121 memcpy(RSTRING_PTR(f), HTTP_PREFIX, HTTP_PREFIX_LEN);
122 memcpy(RSTRING_PTR(f) + HTTP_PREFIX_LEN, field, flen);
123 assert(*(RSTRING_PTR(f) + RSTRING_LEN(f)) == '\0' &&
124 "string didn't end with \\0"); /* paranoia */
125 return HASH_ASET_DEDUPE ? f : str_dd_freeze(f);
128 #endif /* common_field_optimization_h */