unicorn-simon 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. checksums.yaml +7 -0
  2. data/.CHANGELOG.old +25 -0
  3. data/.document +28 -0
  4. data/.gitattributes +5 -0
  5. data/.gitignore +25 -0
  6. data/.mailmap +26 -0
  7. data/.manifest +156 -0
  8. data/.olddoc.yml +18 -0
  9. data/Application_Timeouts +77 -0
  10. data/CONTRIBUTORS +35 -0
  11. data/COPYING +674 -0
  12. data/DESIGN +95 -0
  13. data/Documentation/.gitignore +5 -0
  14. data/Documentation/GNUmakefile +30 -0
  15. data/Documentation/unicorn.1.txt +187 -0
  16. data/Documentation/unicorn_rails.1.txt +175 -0
  17. data/FAQ +70 -0
  18. data/GIT-VERSION-FILE +1 -0
  19. data/GIT-VERSION-GEN +39 -0
  20. data/GNUmakefile +253 -0
  21. data/HACKING +120 -0
  22. data/ISSUES +90 -0
  23. data/KNOWN_ISSUES +79 -0
  24. data/LATEST +30 -0
  25. data/LICENSE +67 -0
  26. data/Links +56 -0
  27. data/NEWS +2465 -0
  28. data/PHILOSOPHY +139 -0
  29. data/README +138 -0
  30. data/Rakefile +16 -0
  31. data/SIGNALS +123 -0
  32. data/Sandbox +104 -0
  33. data/TODO +3 -0
  34. data/TUNING +119 -0
  35. data/archive/.gitignore +3 -0
  36. data/archive/slrnpull.conf +4 -0
  37. data/bin/unicorn +126 -0
  38. data/bin/unicorn_rails +209 -0
  39. data/examples/big_app_gc.rb +2 -0
  40. data/examples/echo.ru +27 -0
  41. data/examples/init.sh +102 -0
  42. data/examples/logger_mp_safe.rb +25 -0
  43. data/examples/logrotate.conf +44 -0
  44. data/examples/nginx.conf +155 -0
  45. data/examples/unicorn.conf.minimal.rb +13 -0
  46. data/examples/unicorn.conf.rb +110 -0
  47. data/examples/unicorn.socket +11 -0
  48. data/examples/unicorn@.service +33 -0
  49. data/ext/unicorn_http/CFLAGS +13 -0
  50. data/ext/unicorn_http/c_util.h +124 -0
  51. data/ext/unicorn_http/common_field_optimization.h +111 -0
  52. data/ext/unicorn_http/ext_help.h +62 -0
  53. data/ext/unicorn_http/extconf.rb +11 -0
  54. data/ext/unicorn_http/global_variables.h +97 -0
  55. data/ext/unicorn_http/httpdate.c +78 -0
  56. data/ext/unicorn_http/unicorn_http.c +4274 -0
  57. data/ext/unicorn_http/unicorn_http.rl +980 -0
  58. data/ext/unicorn_http/unicorn_http_common.rl +76 -0
  59. data/lib/unicorn/app/old_rails/static.rb +59 -0
  60. data/lib/unicorn/app/old_rails.rb +35 -0
  61. data/lib/unicorn/cgi_wrapper.rb +147 -0
  62. data/lib/unicorn/configurator.rb +664 -0
  63. data/lib/unicorn/const.rb +21 -0
  64. data/lib/unicorn/http_request.rb +122 -0
  65. data/lib/unicorn/http_response.rb +60 -0
  66. data/lib/unicorn/http_server.rb +824 -0
  67. data/lib/unicorn/launcher.rb +62 -0
  68. data/lib/unicorn/oob_gc.rb +82 -0
  69. data/lib/unicorn/preread_input.rb +33 -0
  70. data/lib/unicorn/socket_helper.rb +195 -0
  71. data/lib/unicorn/stream_input.rb +146 -0
  72. data/lib/unicorn/tee_input.rb +133 -0
  73. data/lib/unicorn/tmpio.rb +27 -0
  74. data/lib/unicorn/util.rb +90 -0
  75. data/lib/unicorn/version.rb +1 -0
  76. data/lib/unicorn/worker.rb +140 -0
  77. data/lib/unicorn.rb +123 -0
  78. data/man/man1/unicorn.1 +221 -0
  79. data/man/man1/unicorn_rails.1 +212 -0
  80. data/setup.rb +1586 -0
  81. data/t/.gitignore +4 -0
  82. data/t/GNUmakefile +74 -0
  83. data/t/README +42 -0
  84. data/t/bin/content-md5-put +36 -0
  85. data/t/bin/sha1sum.rb +17 -0
  86. data/t/bin/unused_listen +40 -0
  87. data/t/broken-app.ru +12 -0
  88. data/t/detach.ru +11 -0
  89. data/t/env.ru +3 -0
  90. data/t/fails-rack-lint.ru +5 -0
  91. data/t/heartbeat-timeout.ru +12 -0
  92. data/t/hijack.ru +43 -0
  93. data/t/listener_names.ru +4 -0
  94. data/t/my-tap-lib.sh +201 -0
  95. data/t/oob_gc.ru +20 -0
  96. data/t/oob_gc_path.ru +20 -0
  97. data/t/pid.ru +3 -0
  98. data/t/preread_input.ru +17 -0
  99. data/t/rack-input-tests.ru +21 -0
  100. data/t/t0000-http-basic.sh +50 -0
  101. data/t/t0001-reload-bad-config.sh +53 -0
  102. data/t/t0002-config-conflict.sh +49 -0
  103. data/t/t0002-parser-error.sh +94 -0
  104. data/t/t0003-working_directory.sh +51 -0
  105. data/t/t0004-heartbeat-timeout.sh +69 -0
  106. data/t/t0004-working_directory_broken.sh +24 -0
  107. data/t/t0005-working_directory_app.rb.sh +40 -0
  108. data/t/t0006-reopen-logs.sh +83 -0
  109. data/t/t0006.ru +13 -0
  110. data/t/t0007-working_directory_no_embed_cli.sh +44 -0
  111. data/t/t0008-back_out_of_upgrade.sh +110 -0
  112. data/t/t0009-broken-app.sh +56 -0
  113. data/t/t0009-winch_ttin.sh +59 -0
  114. data/t/t0010-reap-logging.sh +55 -0
  115. data/t/t0011-active-unix-socket.sh +79 -0
  116. data/t/t0012-reload-empty-config.sh +85 -0
  117. data/t/t0013-rewindable-input-false.sh +24 -0
  118. data/t/t0013.ru +12 -0
  119. data/t/t0014-rewindable-input-true.sh +24 -0
  120. data/t/t0014.ru +12 -0
  121. data/t/t0015-configurator-internals.sh +25 -0
  122. data/t/t0018-write-on-close.sh +23 -0
  123. data/t/t0019-max_header_len.sh +49 -0
  124. data/t/t0020-at_exit-handler.sh +49 -0
  125. data/t/t0021-process_detach.sh +29 -0
  126. data/t/t0022-listener_names-preload_app.sh +32 -0
  127. data/t/t0100-rack-input-tests.sh +124 -0
  128. data/t/t0116-client_body_buffer_size.sh +80 -0
  129. data/t/t0116.ru +16 -0
  130. data/t/t0200-rack-hijack.sh +30 -0
  131. data/t/t0300-no-default-middleware.sh +20 -0
  132. data/t/t9000-preread-input.sh +48 -0
  133. data/t/t9001-oob_gc.sh +47 -0
  134. data/t/t9002-oob_gc-path.sh +75 -0
  135. data/t/test-lib.sh +128 -0
  136. data/t/write-on-close.ru +11 -0
  137. data/test/aggregate.rb +15 -0
  138. data/test/benchmark/README +50 -0
  139. data/test/benchmark/dd.ru +18 -0
  140. data/test/benchmark/stack.ru +8 -0
  141. data/test/exec/README +5 -0
  142. data/test/exec/test_exec.rb +1099 -0
  143. data/test/test_helper.rb +298 -0
  144. data/test/unit/test_configurator.rb +175 -0
  145. data/test/unit/test_droplet.rb +28 -0
  146. data/test/unit/test_http_parser.rb +886 -0
  147. data/test/unit/test_http_parser_ng.rb +633 -0
  148. data/test/unit/test_request.rb +182 -0
  149. data/test/unit/test_response.rb +111 -0
  150. data/test/unit/test_server.rb +268 -0
  151. data/test/unit/test_signals.rb +188 -0
  152. data/test/unit/test_socket_helper.rb +197 -0
  153. data/test/unit/test_stream_input.rb +203 -0
  154. data/test/unit/test_tee_input.rb +304 -0
  155. data/test/unit/test_upload.rb +306 -0
  156. data/test/unit/test_util.rb +105 -0
  157. data/unicorn.gemspec +50 -0
  158. metadata +310 -0
@@ -0,0 +1,13 @@
1
+ # Minimal sample configuration file for Unicorn (not Rack) when used
2
+ # with daemonization (unicorn -D) started in your working directory.
3
+ #
4
+ # See https://bogomips.org/unicorn/Unicorn/Configurator.html for complete
5
+ # documentation.
6
+ # See also https://bogomips.org/unicorn/examples/unicorn.conf.rb for
7
+ # a more verbose configuration using more features.
8
+
9
+ listen 2007 # by default Unicorn listens on port 8080
10
+ worker_processes 2 # this should be >= nr_cpus
11
+ pid "/path/to/app/shared/pids/unicorn.pid"
12
+ stderr_path "/path/to/app/shared/log/unicorn.log"
13
+ stdout_path "/path/to/app/shared/log/unicorn.log"
@@ -0,0 +1,110 @@
1
+ # Sample verbose configuration file for Unicorn (not Rack)
2
+ #
3
+ # This configuration file documents many features of Unicorn
4
+ # that may not be needed for some applications. See
5
+ # https://bogomips.org/unicorn/examples/unicorn.conf.minimal.rb
6
+ # for a much simpler configuration file.
7
+ #
8
+ # See https://bogomips.org/unicorn/Unicorn/Configurator.html for complete
9
+ # documentation.
10
+
11
+ # Use at least one worker per core if you're on a dedicated server,
12
+ # more will usually help for _short_ waits on databases/caches.
13
+ worker_processes 4
14
+
15
+ # Since Unicorn is never exposed to outside clients, it does not need to
16
+ # run on the standard HTTP port (80), there is no reason to start Unicorn
17
+ # as root unless it's from system init scripts.
18
+ # If running the master process as root and the workers as an unprivileged
19
+ # user, do this to switch euid/egid in the workers (also chowns logs):
20
+ # user "unprivileged_user", "unprivileged_group"
21
+
22
+ # Help ensure your application will always spawn in the symlinked
23
+ # "current" directory that Capistrano sets up.
24
+ working_directory "/path/to/app/current" # available in 0.94.0+
25
+
26
+ # listen on both a Unix domain socket and a TCP port,
27
+ # we use a shorter backlog for quicker failover when busy
28
+ listen "/path/to/.unicorn.sock", :backlog => 64
29
+ listen 8080, :tcp_nopush => true
30
+
31
+ # nuke workers after 30 seconds instead of 60 seconds (the default)
32
+ timeout 30
33
+
34
+ # feel free to point this anywhere accessible on the filesystem
35
+ pid "/path/to/app/shared/pids/unicorn.pid"
36
+
37
+ # By default, the Unicorn logger will write to stderr.
38
+ # Additionally, ome applications/frameworks log to stderr or stdout,
39
+ # so prevent them from going to /dev/null when daemonized here:
40
+ stderr_path "/path/to/app/shared/log/unicorn.stderr.log"
41
+ stdout_path "/path/to/app/shared/log/unicorn.stdout.log"
42
+
43
+ # combine Ruby 2.0.0+ with "preload_app true" for memory savings
44
+ preload_app true
45
+
46
+ # Enable this flag to have unicorn test client connections by writing the
47
+ # beginning of the HTTP headers before calling the application. This
48
+ # prevents calling the application for connections that have disconnected
49
+ # while queued. This is only guaranteed to detect clients on the same
50
+ # host unicorn runs on, and unlikely to detect disconnects even on a
51
+ # fast LAN.
52
+ check_client_connection false
53
+
54
+ # local variable to guard against running a hook multiple times
55
+ run_once = true
56
+
57
+ before_fork do |server, worker|
58
+ # the following is highly recomended for Rails + "preload_app true"
59
+ # as there's no need for the master process to hold a connection
60
+ defined?(ActiveRecord::Base) and
61
+ ActiveRecord::Base.connection.disconnect!
62
+
63
+ # Occasionally, it may be necessary to run non-idempotent code in the
64
+ # master before forking. Keep in mind the above disconnect! example
65
+ # is idempotent and does not need a guard.
66
+ if run_once
67
+ # do_something_once_here ...
68
+ run_once = false # prevent from firing again
69
+ end
70
+
71
+ # The following is only recommended for memory/DB-constrained
72
+ # installations. It is not needed if your system can house
73
+ # twice as many worker_processes as you have configured.
74
+ #
75
+ # # This allows a new master process to incrementally
76
+ # # phase out the old master process with SIGTTOU to avoid a
77
+ # # thundering herd (especially in the "preload_app false" case)
78
+ # # when doing a transparent upgrade. The last worker spawned
79
+ # # will then kill off the old master process with a SIGQUIT.
80
+ # old_pid = "#{server.config[:pid]}.oldbin"
81
+ # if old_pid != server.pid
82
+ # begin
83
+ # sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
84
+ # Process.kill(sig, File.read(old_pid).to_i)
85
+ # rescue Errno::ENOENT, Errno::ESRCH
86
+ # end
87
+ # end
88
+ #
89
+ # Throttle the master from forking too quickly by sleeping. Due
90
+ # to the implementation of standard Unix signal handlers, this
91
+ # helps (but does not completely) prevent identical, repeated signals
92
+ # from being lost when the receiving process is busy.
93
+ # sleep 1
94
+ end
95
+
96
+ after_fork do |server, worker|
97
+ # per-process listener ports for debugging/admin/migrations
98
+ # addr = "127.0.0.1:#{9293 + worker.nr}"
99
+ # server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
100
+
101
+ # the following is *required* for Rails + "preload_app true",
102
+ defined?(ActiveRecord::Base) and
103
+ ActiveRecord::Base.establish_connection
104
+
105
+ # if preload_app is true, then you may also want to check and
106
+ # restart any other shared sockets/descriptors such as Memcached,
107
+ # and Redis. TokyoCabinet file handles are safe to reuse
108
+ # between any number of forked children (assuming your kernel
109
+ # correctly implements pread()/pwrite() system calls)
110
+ end
@@ -0,0 +1,11 @@
1
+ # ==> /etc/systemd/system/unicorn.socket <==
2
+ [Unit]
3
+ Description = unicorn sockets
4
+
5
+ [Socket]
6
+ ListenStream = 127.0.0.1:8080
7
+ ListenStream = /tmp/path/to/.unicorn.sock
8
+ Service = unicorn@1.service
9
+
10
+ [Install]
11
+ WantedBy = sockets.target
@@ -0,0 +1,33 @@
1
+ # ==> /etc/systemd/system/unicorn@.service <==
2
+ # Since SIGUSR2 upgrades do not work under systemd, this service file
3
+ # allows starting two simultaneous services during upgrade time
4
+ # (e.g. unicorn@1 unicorn@2) with the intention that they take
5
+ # turns running in-between upgrades. This should allow upgrading
6
+ # without downtime.
7
+
8
+ [Unit]
9
+ Description = unicorn Rack application server %i
10
+ Wants = unicorn.socket
11
+ After = unicorn.socket
12
+
13
+ [Service]
14
+ # bundler users must use the "--keep-file-descriptors" switch, here:
15
+ # ExecStart = bundle exec --keep-file-descriptors unicorn -c ...
16
+ ExecStart = /usr/bin/unicorn -c /path/to/unicorn.conf.rb /path/to/config.ru
17
+ Sockets = unicorn.socket
18
+ KillSignal = SIGQUIT
19
+ User = nobody
20
+ Group = nogroup
21
+ ExecReload = /bin/kill -HUP $MAINPID
22
+
23
+ # This is based on the Unicorn::Configurator#timeout directive,
24
+ # adding a few seconds for scheduling differences:
25
+ TimeoutStopSec = 62
26
+
27
+ # Only kill the master process, it may be harmful to signal
28
+ # workers via default "control-group" setting since some
29
+ # Ruby extensions and applications misbehave on interrupts
30
+ KillMode = process
31
+
32
+ [Install]
33
+ WantedBy = multi-user.target
@@ -0,0 +1,13 @@
1
+ # CFLAGS used for development (gcc-dependent)
2
+ # source this file if you want/need them
3
+ CFLAGS=
4
+ CFLAGS="$CFLAGS -Wall"
5
+ CFLAGS="$CFLAGS -Wwrite-strings"
6
+ CFLAGS="$CFLAGS -Wdeclaration-after-statement"
7
+ CFLAGS="$CFLAGS -Wcast-qual"
8
+ CFLAGS="$CFLAGS -Wstrict-prototypes"
9
+ CFLAGS="$CFLAGS -Wshadow"
10
+ CFLAGS="$CFLAGS -Wextra"
11
+ CFLAGS="$CFLAGS -Wno-deprecated-declarations"
12
+ CFLAGS="$CFLAGS -Waggregate-return"
13
+ CFLAGS="$CFLAGS -Wchar-subscripts"
@@ -0,0 +1,124 @@
1
+ /*
2
+ * Generic C functions and macros go here, there are no dependencies
3
+ * on Unicorn internal structures or the Ruby C API in here.
4
+ */
5
+
6
+ #ifndef UH_util_h
7
+ #define UH_util_h
8
+
9
+ #include <unistd.h>
10
+ #include <assert.h>
11
+
12
+ #define MIN(a,b) (a < b ? a : b)
13
+ #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
14
+
15
+ #ifndef SIZEOF_OFF_T
16
+ # define SIZEOF_OFF_T 4
17
+ # warning SIZEOF_OFF_T not defined, guessing 4. Did you run extconf.rb?
18
+ #endif
19
+
20
+ #if SIZEOF_OFF_T == 4
21
+ # define UH_OFF_T_MAX 0x7fffffff
22
+ #elif SIZEOF_OFF_T == 8
23
+ # if SIZEOF_LONG == 4
24
+ # define UH_OFF_T_MAX 0x7fffffffffffffffLL
25
+ # else
26
+ # define UH_OFF_T_MAX 0x7fffffffffffffff
27
+ # endif
28
+ #else
29
+ # error off_t size unknown for this platform!
30
+ #endif /* SIZEOF_OFF_T check */
31
+
32
+ /*
33
+ * ragel enforces fpc as a const, and merely casting can make picky
34
+ * compilers unhappy, so we have this little helper do our dirty work
35
+ */
36
+ static inline void *deconst(const void *in)
37
+ {
38
+ union { const void *in; void *out; } tmp;
39
+
40
+ tmp.in = in;
41
+
42
+ return tmp.out;
43
+ }
44
+
45
+ /*
46
+ * capitalizes all lower-case ASCII characters and converts dashes
47
+ * to underscores for HTTP headers. Locale-agnostic.
48
+ */
49
+ static void snake_upcase_char(char *c)
50
+ {
51
+ if (*c >= 'a' && *c <= 'z')
52
+ *c &= ~0x20;
53
+ else if (*c == '-')
54
+ *c = '_';
55
+ }
56
+
57
+ /* Downcases a single ASCII character. Locale-agnostic. */
58
+ static void downcase_char(char *c)
59
+ {
60
+ if (*c >= 'A' && *c <= 'Z')
61
+ *c |= 0x20;
62
+ }
63
+
64
+ static int hexchar2int(int xdigit)
65
+ {
66
+ if (xdigit >= 'A' && xdigit <= 'F')
67
+ return xdigit - 'A' + 10;
68
+ if (xdigit >= 'a' && xdigit <= 'f')
69
+ return xdigit - 'a' + 10;
70
+
71
+ /* Ragel already does runtime range checking for us in Unicorn: */
72
+ assert(xdigit >= '0' && xdigit <= '9' && "invalid digit character");
73
+
74
+ return xdigit - '0';
75
+ }
76
+
77
+ /*
78
+ * multiplies +i+ by +base+ and increments the result by the parsed
79
+ * integer value of +xdigit+. +xdigit+ is a character byte
80
+ * representing a number the range of 0..(base-1)
81
+ * returns the new value of +i+ on success
82
+ * returns -1 on errors (including overflow)
83
+ */
84
+ static off_t step_incr(off_t i, int xdigit, const int base)
85
+ {
86
+ static const off_t max = UH_OFF_T_MAX;
87
+ const off_t next_max = (max - (max % base)) / base;
88
+ off_t offset = hexchar2int(xdigit);
89
+
90
+ if (offset > (base - 1))
91
+ return -1;
92
+ if (i > next_max)
93
+ return -1;
94
+ i *= base;
95
+
96
+ if ((offset > (base - 1)) || ((max - i) < offset))
97
+ return -1;
98
+
99
+ return i + offset;
100
+ }
101
+
102
+ /*
103
+ * parses a non-negative length according to base-10 and
104
+ * returns it as an off_t value. Returns -1 on errors
105
+ * (including overflow).
106
+ */
107
+ static off_t parse_length(const char *value, size_t length)
108
+ {
109
+ off_t rv;
110
+
111
+ for (rv = 0; length-- && rv >= 0; ++value) {
112
+ if (*value >= '0' && *value <= '9')
113
+ rv = step_incr(rv, *value, 10);
114
+ else
115
+ return -1;
116
+ }
117
+
118
+ return rv;
119
+ }
120
+
121
+ #define CONST_MEM_EQ(const_p, buf, len) \
122
+ ((sizeof(const_p) - 1) == len && !memcmp(const_p, buf, sizeof(const_p) - 1))
123
+
124
+ #endif /* UH_util_h */
@@ -0,0 +1,111 @@
1
+ #ifndef common_field_optimization
2
+ #define common_field_optimization
3
+ #include "ruby.h"
4
+ #include "c_util.h"
5
+
6
+ struct common_field {
7
+ const signed long len;
8
+ const char *name;
9
+ VALUE value;
10
+ };
11
+
12
+ /*
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().
16
+ */
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
57
+ };
58
+
59
+ #define HTTP_PREFIX "HTTP_"
60
+ #define HTTP_PREFIX_LEN (sizeof(HTTP_PREFIX) - 1)
61
+
62
+ /* this function is not performance-critical, called only at load time */
63
+ static void init_common_fields(void)
64
+ {
65
+ int i;
66
+ struct common_field *cf = common_http_fields;
67
+ char tmp[64];
68
+ memcpy(tmp, HTTP_PREFIX, HTTP_PREFIX_LEN);
69
+
70
+ for(i = ARRAY_SIZE(common_http_fields); --i >= 0; cf++) {
71
+ /* Rack doesn't like certain headers prefixed with "HTTP_" */
72
+ if (!strcmp("CONTENT_LENGTH", cf->name) ||
73
+ !strcmp("CONTENT_TYPE", cf->name)) {
74
+ cf->value = rb_str_new(cf->name, cf->len);
75
+ } else {
76
+ memcpy(tmp + HTTP_PREFIX_LEN, cf->name, cf->len + 1);
77
+ cf->value = rb_str_new(tmp, HTTP_PREFIX_LEN + cf->len);
78
+ }
79
+ cf->value = rb_obj_freeze(cf->value);
80
+ rb_global_variable(&cf->value);
81
+ }
82
+ }
83
+
84
+ /* this function is called for every header set */
85
+ static VALUE find_common_field(const char *field, size_t flen)
86
+ {
87
+ int i;
88
+ struct common_field *cf = common_http_fields;
89
+
90
+ for(i = ARRAY_SIZE(common_http_fields); --i >= 0; cf++) {
91
+ if (cf->len == (long)flen && !memcmp(cf->name, field, flen))
92
+ return cf->value;
93
+ }
94
+ return Qnil;
95
+ }
96
+
97
+ /*
98
+ * We got a strange header that we don't have a memoized value for.
99
+ * Fallback to creating a new string to use as a hash key.
100
+ */
101
+ static VALUE uncommon_field(const char *field, size_t flen)
102
+ {
103
+ VALUE f = rb_str_new(NULL, HTTP_PREFIX_LEN + flen);
104
+ memcpy(RSTRING_PTR(f), HTTP_PREFIX, HTTP_PREFIX_LEN);
105
+ memcpy(RSTRING_PTR(f) + HTTP_PREFIX_LEN, field, flen);
106
+ assert(*(RSTRING_PTR(f) + RSTRING_LEN(f)) == '\0' &&
107
+ "string didn't end with \\0"); /* paranoia */
108
+ return rb_obj_freeze(f);
109
+ }
110
+
111
+ #endif /* common_field_optimization_h */
@@ -0,0 +1,62 @@
1
+ #ifndef ext_help_h
2
+ #define ext_help_h
3
+
4
+ /* not all Ruby implementations support frozen objects (Rubinius does not) */
5
+ #if defined(OBJ_FROZEN)
6
+ # define assert_frozen(f) assert(OBJ_FROZEN(f) && "unfrozen object")
7
+ #else
8
+ # define assert_frozen(f) do {} while (0)
9
+ #endif /* !defined(OBJ_FROZEN) */
10
+
11
+ #if !defined(OFFT2NUM)
12
+ # if SIZEOF_OFF_T == SIZEOF_LONG
13
+ # define OFFT2NUM(n) LONG2NUM(n)
14
+ # else
15
+ # define OFFT2NUM(n) LL2NUM(n)
16
+ # endif
17
+ #endif /* ! defined(OFFT2NUM) */
18
+
19
+ #if !defined(SIZET2NUM)
20
+ # if SIZEOF_SIZE_T == SIZEOF_LONG
21
+ # define SIZET2NUM(n) ULONG2NUM(n)
22
+ # else
23
+ # define SIZET2NUM(n) ULL2NUM(n)
24
+ # endif
25
+ #endif /* ! defined(SIZET2NUM) */
26
+
27
+ #if !defined(NUM2SIZET)
28
+ # if SIZEOF_SIZE_T == SIZEOF_LONG
29
+ # define NUM2SIZET(n) ((size_t)NUM2ULONG(n))
30
+ # else
31
+ # define NUM2SIZET(n) ((size_t)NUM2ULL(n))
32
+ # endif
33
+ #endif /* ! defined(NUM2SIZET) */
34
+
35
+ static inline int str_cstr_eq(VALUE val, const char *ptr, long len)
36
+ {
37
+ return (RSTRING_LEN(val) == len && !memcmp(ptr, RSTRING_PTR(val), len));
38
+ }
39
+
40
+ #define STR_CSTR_EQ(val, const_str) \
41
+ str_cstr_eq(val, const_str, sizeof(const_str) - 1)
42
+
43
+ /* strcasecmp isn't locale independent */
44
+ static int str_cstr_case_eq(VALUE val, const char *ptr, long len)
45
+ {
46
+ if (RSTRING_LEN(val) == len) {
47
+ const char *v = RSTRING_PTR(val);
48
+
49
+ for (; len--; ++ptr, ++v) {
50
+ if ((*ptr == *v) || (*v >= 'A' && *v <= 'Z' && (*v | 0x20) == *ptr))
51
+ continue;
52
+ return 0;
53
+ }
54
+ return 1;
55
+ }
56
+ return 0;
57
+ }
58
+
59
+ #define STR_CSTR_CASE_EQ(val, const_str) \
60
+ str_cstr_case_eq(val, const_str, sizeof(const_str) - 1)
61
+
62
+ #endif /* ext_help_h */
@@ -0,0 +1,11 @@
1
+ # -*- encoding: binary -*-
2
+ require 'mkmf'
3
+
4
+ have_macro("SIZEOF_OFF_T", "ruby.h") or check_sizeof("off_t", "sys/types.h")
5
+ have_macro("SIZEOF_SIZE_T", "ruby.h") or check_sizeof("size_t", "sys/types.h")
6
+ have_macro("SIZEOF_LONG", "ruby.h") or check_sizeof("long", "sys/types.h")
7
+ have_func("rb_str_set_len", "ruby.h") or abort 'Ruby 1.9.3+ required'
8
+ have_func("rb_hash_clear", "ruby.h") # Ruby 2.0+
9
+ have_func("gmtime_r", "time.h")
10
+
11
+ create_makefile("unicorn_http")
@@ -0,0 +1,97 @@
1
+ #ifndef global_variables_h
2
+ #define global_variables_h
3
+ static VALUE eHttpParserError;
4
+ static VALUE e413;
5
+ static VALUE e414;
6
+
7
+ static VALUE g_rack_url_scheme;
8
+ static VALUE g_request_method;
9
+ static VALUE g_request_uri;
10
+ static VALUE g_fragment;
11
+ static VALUE g_query_string;
12
+ static VALUE g_http_version;
13
+ static VALUE g_request_path;
14
+ static VALUE g_path_info;
15
+ static VALUE g_server_name;
16
+ static VALUE g_server_port;
17
+ static VALUE g_server_protocol;
18
+ static VALUE g_http_host;
19
+ static VALUE g_http_x_forwarded_proto;
20
+ static VALUE g_http_x_forwarded_ssl;
21
+ static VALUE g_http_transfer_encoding;
22
+ static VALUE g_content_length;
23
+ static VALUE g_http_trailer;
24
+ static VALUE g_http_connection;
25
+ static VALUE g_port_80;
26
+ static VALUE g_port_443;
27
+ static VALUE g_localhost;
28
+ static VALUE g_http;
29
+ static VALUE g_https;
30
+ static VALUE g_http_09;
31
+ static VALUE g_http_10;
32
+ static VALUE g_http_11;
33
+
34
+ /** Defines common length and error messages for input length validation. */
35
+ #define DEF_MAX_LENGTH(N, length) \
36
+ static const size_t MAX_##N##_LENGTH = length; \
37
+ static const char * const MAX_##N##_LENGTH_ERR = \
38
+ "HTTP element " # N " is longer than the " # length " allowed length."
39
+
40
+ NORETURN(static void parser_raise(VALUE klass, const char *));
41
+
42
+ /**
43
+ * Validates the max length of given input and throws an HttpParserError
44
+ * exception if over.
45
+ */
46
+ #define VALIDATE_MAX_LENGTH(len, N) do { \
47
+ if (len > MAX_##N##_LENGTH) \
48
+ parser_raise(eHttpParserError, MAX_##N##_LENGTH_ERR); \
49
+ } while (0)
50
+
51
+ #define VALIDATE_MAX_URI_LENGTH(len, N) do { \
52
+ if (len > MAX_##N##_LENGTH) \
53
+ parser_raise(e414, MAX_##N##_LENGTH_ERR); \
54
+ } while (0)
55
+
56
+ /** Defines global strings in the init method. */
57
+ #define DEF_GLOBAL(N, val) do { \
58
+ g_##N = rb_obj_freeze(rb_str_new(val, sizeof(val) - 1)); \
59
+ rb_global_variable(&g_##N); \
60
+ } while (0)
61
+
62
+ /* Defines the maximum allowed lengths for various input elements.*/
63
+ DEF_MAX_LENGTH(FIELD_NAME, 256);
64
+ DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
65
+ DEF_MAX_LENGTH(REQUEST_URI, 1024 * 15);
66
+ DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
67
+ DEF_MAX_LENGTH(REQUEST_PATH, 4096); /* common PATH_MAX on modern systems */
68
+ DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
69
+
70
+ static void init_globals(void)
71
+ {
72
+ DEF_GLOBAL(rack_url_scheme, "rack.url_scheme");
73
+ DEF_GLOBAL(request_method, "REQUEST_METHOD");
74
+ DEF_GLOBAL(request_uri, "REQUEST_URI");
75
+ DEF_GLOBAL(fragment, "FRAGMENT");
76
+ DEF_GLOBAL(query_string, "QUERY_STRING");
77
+ DEF_GLOBAL(http_version, "HTTP_VERSION");
78
+ DEF_GLOBAL(request_path, "REQUEST_PATH");
79
+ DEF_GLOBAL(path_info, "PATH_INFO");
80
+ DEF_GLOBAL(server_name, "SERVER_NAME");
81
+ DEF_GLOBAL(server_port, "SERVER_PORT");
82
+ DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
83
+ DEF_GLOBAL(http_x_forwarded_proto, "HTTP_X_FORWARDED_PROTO");
84
+ DEF_GLOBAL(http_x_forwarded_ssl, "HTTP_X_FORWARDED_SSL");
85
+ DEF_GLOBAL(port_80, "80");
86
+ DEF_GLOBAL(port_443, "443");
87
+ DEF_GLOBAL(localhost, "localhost");
88
+ DEF_GLOBAL(http, "http");
89
+ DEF_GLOBAL(https, "https");
90
+ DEF_GLOBAL(http_11, "HTTP/1.1");
91
+ DEF_GLOBAL(http_10, "HTTP/1.0");
92
+ DEF_GLOBAL(http_09, "HTTP/0.9");
93
+ }
94
+
95
+ #undef DEF_GLOBAL
96
+
97
+ #endif /* global_variables_h */
@@ -0,0 +1,78 @@
1
+ #include <ruby.h>
2
+ #include <time.h>
3
+ #include <stdio.h>
4
+
5
+ static const size_t buf_capa = sizeof("Thu, 01 Jan 1970 00:00:00 GMT");
6
+ static VALUE buf;
7
+ static char *buf_ptr;
8
+ static const char week[] = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
9
+ static const char months[] = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
10
+ "Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
11
+
12
+ /* for people on wonky systems only */
13
+ #ifndef HAVE_GMTIME_R
14
+ static struct tm * my_gmtime_r(time_t *now, struct tm *tm)
15
+ {
16
+ struct tm *global = gmtime(now);
17
+ if (global)
18
+ *tm = *global;
19
+ return tm;
20
+ }
21
+ # define gmtime_r my_gmtime_r
22
+ #endif
23
+
24
+
25
+ /*
26
+ * Returns a string which represents the time as rfc1123-date of HTTP-date
27
+ * defined by RFC 2616:
28
+ *
29
+ * day-of-week, DD month-name CCYY hh:mm:ss GMT
30
+ *
31
+ * Note that the result is always GMT.
32
+ *
33
+ * This method is identical to Time#httpdate in the Ruby standard library,
34
+ * except it is implemented in C for performance. We always saw
35
+ * Time#httpdate at or near the top of the profiler output so we
36
+ * decided to rewrite this in C.
37
+ *
38
+ * Caveats: it relies on a Ruby implementation with the global VM lock,
39
+ * a thread-safe version will be provided when a Unix-only, GVL-free Ruby
40
+ * implementation becomes viable.
41
+ */
42
+ static VALUE httpdate(VALUE self)
43
+ {
44
+ static time_t last;
45
+ time_t now = time(NULL); /* not a syscall on modern 64-bit systems */
46
+ struct tm tm;
47
+
48
+ if (last == now)
49
+ return buf;
50
+ last = now;
51
+ gmtime_r(&now, &tm);
52
+
53
+ /* we can make this thread-safe later if our Ruby loses the GVL */
54
+ snprintf(buf_ptr, buf_capa,
55
+ "%s, %02d %s %4d %02d:%02d:%02d GMT",
56
+ week + (tm.tm_wday * 4),
57
+ tm.tm_mday,
58
+ months + (tm.tm_mon * 4),
59
+ tm.tm_year + 1900,
60
+ tm.tm_hour,
61
+ tm.tm_min,
62
+ tm.tm_sec);
63
+
64
+ return buf;
65
+ }
66
+
67
+ void init_unicorn_httpdate(void)
68
+ {
69
+ VALUE mod = rb_define_module("Unicorn");
70
+ mod = rb_define_module_under(mod, "HttpResponse");
71
+
72
+ buf = rb_str_new(0, buf_capa - 1);
73
+ rb_global_variable(&buf);
74
+ buf_ptr = RSTRING_PTR(buf);
75
+ httpdate(Qnil);
76
+
77
+ rb_define_method(mod, "httpdate", httpdate, 0);
78
+ }