puma 2.0.0.b5 → 5.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +1598 -0
  3. data/LICENSE +23 -20
  4. data/README.md +222 -62
  5. data/bin/puma-wild +31 -0
  6. data/bin/pumactl +1 -1
  7. data/docs/architecture.md +37 -0
  8. data/docs/deployment.md +113 -0
  9. data/docs/fork_worker.md +31 -0
  10. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  11. data/docs/images/puma-connection-flow.png +0 -0
  12. data/docs/images/puma-general-arch.png +0 -0
  13. data/docs/jungle/README.md +13 -0
  14. data/docs/jungle/rc.d/README.md +74 -0
  15. data/docs/jungle/rc.d/puma +61 -0
  16. data/docs/jungle/rc.d/puma.conf +10 -0
  17. data/docs/jungle/upstart/README.md +61 -0
  18. data/docs/jungle/upstart/puma-manager.conf +31 -0
  19. data/docs/jungle/upstart/puma.conf +69 -0
  20. data/docs/nginx.md +5 -10
  21. data/docs/plugins.md +38 -0
  22. data/docs/restart.md +41 -0
  23. data/docs/signals.md +97 -0
  24. data/docs/systemd.md +228 -0
  25. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  26. data/ext/puma_http11/extconf.rb +23 -2
  27. data/ext/puma_http11/http11_parser.c +301 -482
  28. data/ext/puma_http11/http11_parser.h +13 -11
  29. data/ext/puma_http11/http11_parser.java.rl +26 -42
  30. data/ext/puma_http11/http11_parser.rl +22 -21
  31. data/ext/puma_http11/http11_parser_common.rl +5 -5
  32. data/ext/puma_http11/mini_ssl.c +377 -18
  33. data/ext/puma_http11/org/jruby/puma/Http11.java +108 -107
  34. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +137 -170
  35. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +265 -191
  36. data/ext/puma_http11/puma_http11.c +57 -81
  37. data/lib/puma.rb +25 -4
  38. data/lib/puma/accept_nonblock.rb +7 -1
  39. data/lib/puma/app/status.rb +61 -24
  40. data/lib/puma/binder.rb +212 -78
  41. data/lib/puma/cli.rb +149 -644
  42. data/lib/puma/client.rb +316 -65
  43. data/lib/puma/cluster.rb +659 -0
  44. data/lib/puma/commonlogger.rb +108 -0
  45. data/lib/puma/configuration.rb +279 -180
  46. data/lib/puma/const.rb +126 -39
  47. data/lib/puma/control_cli.rb +183 -96
  48. data/lib/puma/detect.rb +20 -1
  49. data/lib/puma/dsl.rb +776 -0
  50. data/lib/puma/events.rb +91 -23
  51. data/lib/puma/io_buffer.rb +9 -5
  52. data/lib/puma/jruby_restart.rb +9 -5
  53. data/lib/puma/launcher.rb +487 -0
  54. data/lib/puma/minissl.rb +239 -93
  55. data/lib/puma/minissl/context_builder.rb +76 -0
  56. data/lib/puma/null_io.rb +22 -12
  57. data/lib/puma/plugin.rb +111 -0
  58. data/lib/puma/plugin/tmp_restart.rb +36 -0
  59. data/lib/puma/rack/builder.rb +297 -0
  60. data/lib/puma/rack/urlmap.rb +93 -0
  61. data/lib/puma/rack_default.rb +9 -0
  62. data/lib/puma/reactor.rb +290 -43
  63. data/lib/puma/runner.rb +163 -0
  64. data/lib/puma/server.rb +493 -126
  65. data/lib/puma/single.rb +66 -0
  66. data/lib/puma/state_file.rb +34 -0
  67. data/lib/puma/thread_pool.rb +228 -47
  68. data/lib/puma/util.rb +115 -0
  69. data/lib/rack/handler/puma.rb +78 -31
  70. data/tools/Dockerfile +16 -0
  71. data/tools/trickletest.rb +44 -0
  72. metadata +60 -155
  73. data/COPYING +0 -55
  74. data/Gemfile +0 -8
  75. data/History.txt +0 -196
  76. data/Manifest.txt +0 -56
  77. data/Rakefile +0 -121
  78. data/TODO +0 -5
  79. data/docs/config.md +0 -0
  80. data/ext/puma_http11/io_buffer.c +0 -154
  81. data/lib/puma/capistrano.rb +0 -26
  82. data/lib/puma/compat.rb +0 -11
  83. data/lib/puma/daemon_ext.rb +0 -20
  84. data/lib/puma/delegation.rb +0 -11
  85. data/lib/puma/java_io_buffer.rb +0 -45
  86. data/lib/puma/rack_patch.rb +0 -25
  87. data/puma.gemspec +0 -45
  88. data/test/test_app_status.rb +0 -88
  89. data/test/test_cli.rb +0 -171
  90. data/test/test_config.rb +0 -16
  91. data/test/test_http10.rb +0 -27
  92. data/test/test_http11.rb +0 -126
  93. data/test/test_integration.rb +0 -150
  94. data/test/test_iobuffer.rb +0 -38
  95. data/test/test_minissl.rb +0 -22
  96. data/test/test_null_io.rb +0 -31
  97. data/test/test_persistent.rb +0 -238
  98. data/test/test_puma_server.rb +0 -128
  99. data/test/test_rack_handler.rb +0 -10
  100. data/test/test_rack_server.rb +0 -141
  101. data/test/test_thread_pool.rb +0 -146
  102. data/test/test_unix_socket.rb +0 -39
  103. data/test/test_ws.rb +0 -89
  104. data/tools/jungle/README.md +0 -54
  105. data/tools/jungle/puma +0 -332
  106. data/tools/jungle/run-puma +0 -3
@@ -1,11 +1,13 @@
1
1
  /**
2
2
  * Copyright (c) 2005 Zed A. Shaw
3
3
  * You can redistribute it and/or modify it under the same terms as Ruby.
4
+ * License 3-clause BSD
4
5
  */
5
6
 
6
7
  #ifndef http11_parser_h
7
8
  #define http11_parser_h
8
9
 
10
+ #define RSTRING_NOT_MODIFIED 1
9
11
  #include "ruby.h"
10
12
 
11
13
  #include <sys/types.h>
@@ -16,16 +18,16 @@
16
18
 
17
19
  #define BUFFER_LEN 1024
18
20
 
19
- struct http_parser;
21
+ struct puma_parser;
20
22
 
21
- typedef void (*element_cb)(struct http_parser* hp,
23
+ typedef void (*element_cb)(struct puma_parser* hp,
22
24
  const char *at, size_t length);
23
25
 
24
- typedef void (*field_cb)(struct http_parser* hp,
26
+ typedef void (*field_cb)(struct puma_parser* hp,
25
27
  const char *field, size_t flen,
26
28
  const char *value, size_t vlen);
27
29
 
28
- typedef struct http_parser {
30
+ typedef struct puma_parser {
29
31
  int cs;
30
32
  size_t body_start;
31
33
  int content_len;
@@ -49,15 +51,15 @@ typedef struct http_parser {
49
51
 
50
52
  char buf[BUFFER_LEN];
51
53
 
52
- } http_parser;
54
+ } puma_parser;
53
55
 
54
- int http_parser_init(http_parser *parser);
55
- int http_parser_finish(http_parser *parser);
56
- size_t http_parser_execute(http_parser *parser, const char *data,
56
+ int puma_parser_init(puma_parser *parser);
57
+ int puma_parser_finish(puma_parser *parser);
58
+ size_t puma_parser_execute(puma_parser *parser, const char *data,
57
59
  size_t len, size_t off);
58
- int http_parser_has_error(http_parser *parser);
59
- int http_parser_is_finished(http_parser *parser);
60
+ int puma_parser_has_error(puma_parser *parser);
61
+ int puma_parser_is_finished(puma_parser *parser);
60
62
 
61
- #define http_parser_nread(parser) (parser)->nread
63
+ #define puma_parser_nread(parser) (parser)->nread
62
64
 
63
65
  #endif
@@ -1,5 +1,7 @@
1
1
  package org.jruby.puma;
2
2
 
3
+ import org.jruby.Ruby;
4
+ import org.jruby.RubyHash;
3
5
  import org.jruby.util.ByteList;
4
6
 
5
7
  public class Http11Parser {
@@ -7,8 +9,8 @@ public class Http11Parser {
7
9
  /** Machine **/
8
10
 
9
11
  %%{
10
-
11
- machine http_parser;
12
+
13
+ machine puma_parser;
12
14
 
13
15
  action mark {parser.mark = fpc; }
14
16
 
@@ -19,48 +21,39 @@ public class Http11Parser {
19
21
  }
20
22
 
21
23
  action start_value { parser.mark = fpc; }
22
- action write_value {
23
- if(parser.http_field != null) {
24
- parser.http_field.call(parser.data, parser.field_start, parser.field_len, parser.mark, fpc-parser.mark);
25
- }
24
+ action write_value {
25
+ Http11.http_field(runtime, parser.data, parser.buffer, parser.field_start, parser.field_len, parser.mark, fpc-parser.mark);
26
26
  }
27
- action request_method {
28
- if(parser.request_method != null)
29
- parser.request_method.call(parser.data, parser.mark, fpc-parser.mark);
27
+ action request_method {
28
+ Http11.request_method(runtime, parser.data, parser.buffer, parser.mark, fpc-parser.mark);
30
29
  }
31
- action request_uri {
32
- if(parser.request_uri != null)
33
- parser.request_uri.call(parser.data, parser.mark, fpc-parser.mark);
30
+ action request_uri {
31
+ Http11.request_uri(runtime, parser.data, parser.buffer, parser.mark, fpc-parser.mark);
34
32
  }
35
- action fragment {
36
- if(parser.fragment != null)
37
- parser.fragment.call(parser.data, parser.mark, fpc-parser.mark);
33
+ action fragment {
34
+ Http11.fragment(runtime, parser.data, parser.buffer, parser.mark, fpc-parser.mark);
38
35
  }
39
36
 
40
37
  action start_query {parser.query_start = fpc; }
41
- action query_string {
42
- if(parser.query_string != null)
43
- parser.query_string.call(parser.data, parser.query_start, fpc-parser.query_start);
38
+ action query_string {
39
+ Http11.query_string(runtime, parser.data, parser.buffer, parser.query_start, fpc-parser.query_start);
44
40
  }
45
41
 
46
- action http_version {
47
- if(parser.http_version != null)
48
- parser.http_version.call(parser.data, parser.mark, fpc-parser.mark);
42
+ action http_version {
43
+ Http11.http_version(runtime, parser.data, parser.buffer, parser.mark, fpc-parser.mark);
49
44
  }
50
45
 
51
46
  action request_path {
52
- if(parser.request_path != null)
53
- parser.request_path.call(parser.data, parser.mark, fpc-parser.mark);
47
+ Http11.request_path(runtime, parser.data, parser.buffer, parser.mark, fpc-parser.mark);
54
48
  }
55
49
 
56
50
  action done {
57
- parser.body_start = fpc + 1;
58
- if(parser.header_done != null)
59
- parser.header_done.call(parser.data, fpc + 1, pe - fpc - 1);
51
+ parser.body_start = fpc + 1;
52
+ http.header_done(runtime, parser.data, parser.buffer, fpc + 1, pe - fpc - 1);
60
53
  fbreak;
61
54
  }
62
55
 
63
- include http_parser_common "http11_parser_common.rl";
56
+ include puma_parser_common "http11_parser_common.rl";
64
57
 
65
58
  }%%
66
59
 
@@ -68,11 +61,11 @@ public class Http11Parser {
68
61
  %% write data;
69
62
 
70
63
  public static interface ElementCB {
71
- public void call(Object data, int at, int length);
64
+ public void call(Ruby runtime, RubyHash data, ByteList buffer, int at, int length);
72
65
  }
73
66
 
74
67
  public static interface FieldCB {
75
- public void call(Object data, int field, int flen, int value, int vlen);
68
+ public void call(Ruby runtime, RubyHash data, ByteList buffer, int field, int flen, int value, int vlen);
76
69
  }
77
70
 
78
71
  public static class HttpParser {
@@ -85,18 +78,9 @@ public class Http11Parser {
85
78
  int field_len;
86
79
  int query_start;
87
80
 
88
- Object data;
81
+ RubyHash data;
89
82
  ByteList buffer;
90
83
 
91
- public FieldCB http_field;
92
- public ElementCB request_method;
93
- public ElementCB request_uri;
94
- public ElementCB fragment;
95
- public ElementCB request_path;
96
- public ElementCB query_string;
97
- public ElementCB http_version;
98
- public ElementCB header_done;
99
-
100
84
  public void init() {
101
85
  cs = 0;
102
86
 
@@ -113,7 +97,7 @@ public class Http11Parser {
113
97
 
114
98
  public final HttpParser parser = new HttpParser();
115
99
 
116
- public int execute(ByteList buffer, int off) {
100
+ public int execute(Ruby runtime, Http11 http, ByteList buffer, int off) {
117
101
  int p, pe;
118
102
  int cs = parser.cs;
119
103
  int len = buffer.length();
@@ -152,10 +136,10 @@ public class Http11Parser {
152
136
  }
153
137
 
154
138
  public boolean has_error() {
155
- return parser.cs == http_parser_error;
139
+ return parser.cs == puma_parser_error;
156
140
  }
157
141
 
158
142
  public boolean is_finished() {
159
- return parser.cs == http_parser_first_final;
143
+ return parser.cs == puma_parser_first_final;
160
144
  }
161
145
  }
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Copyright (c) 2005 Zed A. Shaw
3
3
  * You can redistribute it and/or modify it under the same terms as Ruby.
4
+ * License 3-clause BSD
4
5
  */
5
6
  #include "http11_parser.h"
6
7
  #include <stdio.h>
@@ -28,15 +29,15 @@ static void snake_upcase_char(char *c)
28
29
  /** Machine **/
29
30
 
30
31
  %%{
31
-
32
- machine http_parser;
32
+
33
+ machine puma_parser;
33
34
 
34
35
  action mark { MARK(mark, fpc); }
35
36
 
36
37
 
37
38
  action start_field { MARK(field_start, fpc); }
38
39
  action snake_upcase_field { snake_upcase_char((char *)fpc); }
39
- action write_field {
40
+ action write_field {
40
41
  parser->field_len = LEN(field_start, fpc);
41
42
  }
42
43
 
@@ -44,10 +45,10 @@ static void snake_upcase_char(char *c)
44
45
  action write_value {
45
46
  parser->http_field(parser, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
46
47
  }
47
- action request_method {
48
+ action request_method {
48
49
  parser->request_method(parser, PTR_TO(mark), LEN(mark, fpc));
49
50
  }
50
- action request_uri {
51
+ action request_uri {
51
52
  parser->request_uri(parser, PTR_TO(mark), LEN(mark, fpc));
52
53
  }
53
54
  action fragment {
@@ -55,11 +56,11 @@ static void snake_upcase_char(char *c)
55
56
  }
56
57
 
57
58
  action start_query { MARK(query_start, fpc); }
58
- action query_string {
59
+ action query_string {
59
60
  parser->query_string(parser, PTR_TO(query_start), LEN(query_start, fpc));
60
61
  }
61
62
 
62
- action http_version {
63
+ action http_version {
63
64
  parser->http_version(parser, PTR_TO(mark), LEN(mark, fpc));
64
65
  }
65
66
 
@@ -67,20 +68,20 @@ static void snake_upcase_char(char *c)
67
68
  parser->request_path(parser, PTR_TO(mark), LEN(mark,fpc));
68
69
  }
69
70
 
70
- action done {
71
- parser->body_start = fpc - buffer + 1;
71
+ action done {
72
+ parser->body_start = fpc - buffer + 1;
72
73
  parser->header_done(parser, fpc + 1, pe - fpc - 1);
73
74
  fbreak;
74
75
  }
75
76
 
76
- include http_parser_common "http11_parser_common.rl";
77
+ include puma_parser_common "http11_parser_common.rl";
77
78
 
78
79
  }%%
79
80
 
80
81
  /** Data **/
81
82
  %% write data;
82
83
 
83
- int http_parser_init(http_parser *parser) {
84
+ int puma_parser_init(puma_parser *parser) {
84
85
  int cs = 0;
85
86
  %% write init;
86
87
  parser->cs = cs;
@@ -98,7 +99,7 @@ int http_parser_init(http_parser *parser) {
98
99
 
99
100
 
100
101
  /** exec **/
101
- size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off) {
102
+ size_t puma_parser_execute(puma_parser *parser, const char *buffer, size_t len, size_t off) {
102
103
  const char *p, *pe;
103
104
  int cs = parser->cs;
104
105
 
@@ -108,11 +109,11 @@ size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len,
108
109
  pe = buffer+len;
109
110
 
110
111
  /* assert(*pe == '\0' && "pointer does not end on NUL"); */
111
- assert(pe - p == len - off && "pointers aren't same distance");
112
+ assert((size_t) (pe - p) == len - off && "pointers aren't same distance");
112
113
 
113
114
  %% write exec;
114
115
 
115
- if (!http_parser_has_error(parser))
116
+ if (!puma_parser_has_error(parser))
116
117
  parser->cs = cs;
117
118
  parser->nread += p - (buffer + off);
118
119
 
@@ -126,21 +127,21 @@ size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len,
126
127
  return(parser->nread);
127
128
  }
128
129
 
129
- int http_parser_finish(http_parser *parser)
130
+ int puma_parser_finish(puma_parser *parser)
130
131
  {
131
- if (http_parser_has_error(parser) ) {
132
+ if (puma_parser_has_error(parser) ) {
132
133
  return -1;
133
- } else if (http_parser_is_finished(parser) ) {
134
+ } else if (puma_parser_is_finished(parser) ) {
134
135
  return 1;
135
136
  } else {
136
137
  return 0;
137
138
  }
138
139
  }
139
140
 
140
- int http_parser_has_error(http_parser *parser) {
141
- return parser->cs == http_parser_error;
141
+ int puma_parser_has_error(puma_parser *parser) {
142
+ return parser->cs == puma_parser_error;
142
143
  }
143
144
 
144
- int http_parser_is_finished(http_parser *parser) {
145
- return parser->cs >= http_parser_first_final;
145
+ int puma_parser_is_finished(puma_parser *parser) {
146
+ return parser->cs >= puma_parser_first_final;
146
147
  }
@@ -1,6 +1,6 @@
1
1
  %%{
2
-
3
- machine http_parser_common;
2
+
3
+ machine puma_parser_common;
4
4
 
5
5
  #### HTTP PROTOCOL GRAMMAR
6
6
  # line endings
@@ -15,8 +15,8 @@
15
15
  national = any -- (alpha | digit | reserved | extra | safe | unsafe);
16
16
  unreserved = (alpha | digit | safe | extra | national);
17
17
  escape = ("%" xdigit xdigit);
18
- uchar = (unreserved | escape);
19
- pchar = (uchar | ":" | "@" | "&" | "=" | "+");
18
+ uchar = (unreserved | escape | "%");
19
+ pchar = (uchar | ":" | "@" | "&" | "=" | "+" | ";");
20
20
  tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
21
21
 
22
22
  # elements
@@ -30,7 +30,7 @@
30
30
  query = ( uchar | reserved )* %query_string ;
31
31
  param = ( pchar | "/" )* ;
32
32
  params = ( param ( ";" param )* ) ;
33
- rel_path = ( path? %request_path (";" params)? ) ("?" %start_query query)?;
33
+ rel_path = ( path? %request_path ) ("?" %start_query query)?;
34
34
  absolute_path = ( "/"+ rel_path );
35
35
 
36
36
  Request_URI = ( "*" | absolute_uri | absolute_path ) >mark %request_uri;
@@ -1,8 +1,25 @@
1
+ #define RSTRING_NOT_MODIFIED 1
2
+
1
3
  #include <ruby.h>
4
+ #include <ruby/version.h>
5
+
6
+ #if RUBY_API_VERSION_MAJOR == 1
2
7
  #include <rubyio.h>
8
+ #else
9
+ #include <ruby/io.h>
10
+ #endif
11
+
12
+ #ifdef HAVE_OPENSSL_BIO_H
13
+
3
14
  #include <openssl/bio.h>
4
15
  #include <openssl/ssl.h>
16
+ #include <openssl/dh.h>
5
17
  #include <openssl/err.h>
18
+ #include <openssl/x509.h>
19
+
20
+ #ifndef SSL_OP_NO_COMPRESSION
21
+ #define SSL_OP_NO_COMPRESSION 0
22
+ #endif
6
23
 
7
24
  typedef struct {
8
25
  BIO* read;
@@ -11,9 +28,19 @@ typedef struct {
11
28
  SSL_CTX* ctx;
12
29
  } ms_conn;
13
30
 
31
+ typedef struct {
32
+ unsigned char* buf;
33
+ int bytes;
34
+ } ms_cert_buf;
35
+
14
36
  void engine_free(ms_conn* conn) {
15
- BIO_free(conn->read);
16
- BIO_free(conn->write);
37
+ ms_cert_buf* cert_buf = (ms_cert_buf*)SSL_get_app_data(conn->ssl);
38
+ if(cert_buf) {
39
+ OPENSSL_free(cert_buf->buf);
40
+ free(cert_buf);
41
+ }
42
+ SSL_free(conn->ssl);
43
+ SSL_CTX_free(conn->ctx);
17
44
 
18
45
  free(conn);
19
46
  }
@@ -35,27 +62,196 @@ ms_conn* engine_alloc(VALUE klass, VALUE* obj) {
35
62
  return conn;
36
63
  }
37
64
 
38
- VALUE engine_init_server(VALUE self, VALUE key, VALUE cert) {
65
+ DH *get_dh1024() {
66
+ /* `openssl dhparam 1024 -C`
67
+ * -----BEGIN DH PARAMETERS-----
68
+ * MIGHAoGBALPwcEv0OstmQCZdfHw0N5r+07lmXMxkpQacy1blwj0LUqC+Divp6pBk
69
+ * usTJ9W2/dOYr1X7zi6yXNLp4oLzc/31PUL3D9q8CpGS7vPz5gijKSw9BwCTT5z9+
70
+ * KF9v46qw8XqT5HHV87sWFlGQcVFq+pEkA2kPikkKZ/X/CCcpCAV7AgEC
71
+ * -----END DH PARAMETERS-----
72
+ */
73
+ static unsigned char dh1024_p[] = {
74
+ 0xB3,0xF0,0x70,0x4B,0xF4,0x3A,0xCB,0x66,0x40,0x26,0x5D,0x7C,
75
+ 0x7C,0x34,0x37,0x9A,0xFE,0xD3,0xB9,0x66,0x5C,0xCC,0x64,0xA5,
76
+ 0x06,0x9C,0xCB,0x56,0xE5,0xC2,0x3D,0x0B,0x52,0xA0,0xBE,0x0E,
77
+ 0x2B,0xE9,0xEA,0x90,0x64,0xBA,0xC4,0xC9,0xF5,0x6D,0xBF,0x74,
78
+ 0xE6,0x2B,0xD5,0x7E,0xF3,0x8B,0xAC,0x97,0x34,0xBA,0x78,0xA0,
79
+ 0xBC,0xDC,0xFF,0x7D,0x4F,0x50,0xBD,0xC3,0xF6,0xAF,0x02,0xA4,
80
+ 0x64,0xBB,0xBC,0xFC,0xF9,0x82,0x28,0xCA,0x4B,0x0F,0x41,0xC0,
81
+ 0x24,0xD3,0xE7,0x3F,0x7E,0x28,0x5F,0x6F,0xE3,0xAA,0xB0,0xF1,
82
+ 0x7A,0x93,0xE4,0x71,0xD5,0xF3,0xBB,0x16,0x16,0x51,0x90,0x71,
83
+ 0x51,0x6A,0xFA,0x91,0x24,0x03,0x69,0x0F,0x8A,0x49,0x0A,0x67,
84
+ 0xF5,0xFF,0x08,0x27,0x29,0x08,0x05,0x7B
85
+ };
86
+ static unsigned char dh1024_g[] = { 0x02 };
87
+
88
+ DH *dh;
89
+ dh = DH_new();
90
+
91
+ #if OPENSSL_VERSION_NUMBER < 0x10100005L || defined(LIBRESSL_VERSION_NUMBER)
92
+ dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL);
93
+ dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL);
94
+
95
+ if ((dh->p == NULL) || (dh->g == NULL)) {
96
+ DH_free(dh);
97
+ return NULL;
98
+ }
99
+ #else
100
+ BIGNUM *p, *g;
101
+ p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL);
102
+ g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL);
103
+
104
+ if (p == NULL || g == NULL || !DH_set0_pqg(dh, p, NULL, g)) {
105
+ DH_free(dh);
106
+ BN_free(p);
107
+ BN_free(g);
108
+ return NULL;
109
+ }
110
+ #endif
111
+
112
+ return dh;
113
+ }
114
+
115
+ static int engine_verify_callback(int preverify_ok, X509_STORE_CTX* ctx) {
116
+ X509* err_cert;
117
+ SSL* ssl;
118
+ int bytes;
119
+ unsigned char* buf = NULL;
120
+
121
+ if(!preverify_ok) {
122
+ err_cert = X509_STORE_CTX_get_current_cert(ctx);
123
+ if(err_cert) {
124
+ /*
125
+ * Save the failed certificate for inspection/logging.
126
+ */
127
+ bytes = i2d_X509(err_cert, &buf);
128
+ if(bytes > 0) {
129
+ ms_cert_buf* cert_buf = (ms_cert_buf*)malloc(sizeof(ms_cert_buf));
130
+ cert_buf->buf = buf;
131
+ cert_buf->bytes = bytes;
132
+ ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
133
+ SSL_set_app_data(ssl, cert_buf);
134
+ }
135
+ }
136
+ }
137
+
138
+ return preverify_ok;
139
+ }
140
+
141
+ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
39
142
  VALUE obj;
40
143
  SSL_CTX* ctx;
41
144
  SSL* ssl;
145
+ int min, ssl_options;
42
146
 
43
147
  ms_conn* conn = engine_alloc(self, &obj);
44
148
 
149
+ ID sym_key = rb_intern("key");
150
+ VALUE key = rb_funcall(mini_ssl_ctx, sym_key, 0);
151
+
45
152
  StringValue(key);
153
+
154
+ ID sym_cert = rb_intern("cert");
155
+ VALUE cert = rb_funcall(mini_ssl_ctx, sym_cert, 0);
156
+
46
157
  StringValue(cert);
47
158
 
159
+ ID sym_ca = rb_intern("ca");
160
+ VALUE ca = rb_funcall(mini_ssl_ctx, sym_ca, 0);
161
+
162
+ ID sym_verify_mode = rb_intern("verify_mode");
163
+ VALUE verify_mode = rb_funcall(mini_ssl_ctx, sym_verify_mode, 0);
164
+
165
+ ID sym_ssl_cipher_filter = rb_intern("ssl_cipher_filter");
166
+ VALUE ssl_cipher_filter = rb_funcall(mini_ssl_ctx, sym_ssl_cipher_filter, 0);
167
+
168
+ ID sym_no_tlsv1 = rb_intern("no_tlsv1");
169
+ VALUE no_tlsv1 = rb_funcall(mini_ssl_ctx, sym_no_tlsv1, 0);
170
+
171
+ ID sym_no_tlsv1_1 = rb_intern("no_tlsv1_1");
172
+ VALUE no_tlsv1_1 = rb_funcall(mini_ssl_ctx, sym_no_tlsv1_1, 0);
173
+
174
+ #ifdef HAVE_TLS_SERVER_METHOD
175
+ ctx = SSL_CTX_new(TLS_server_method());
176
+ #else
48
177
  ctx = SSL_CTX_new(SSLv23_server_method());
178
+ #endif
49
179
  conn->ctx = ctx;
50
180
 
51
- SSL_CTX_use_certificate_file(ctx, RSTRING_PTR(cert), SSL_FILETYPE_PEM);
181
+ SSL_CTX_use_certificate_chain_file(ctx, RSTRING_PTR(cert));
52
182
  SSL_CTX_use_PrivateKey_file(ctx, RSTRING_PTR(key), SSL_FILETYPE_PEM);
53
- /* SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE); */
54
183
 
55
- ssl = SSL_new(ctx);
184
+ if (!NIL_P(ca)) {
185
+ StringValue(ca);
186
+ SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL);
187
+ }
188
+
189
+ ssl_options = SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION;
190
+
191
+ #ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
192
+ if (RTEST(no_tlsv1_1)) {
193
+ min = TLS1_2_VERSION;
194
+ }
195
+ else if (RTEST(no_tlsv1)) {
196
+ min = TLS1_1_VERSION;
197
+ }
198
+ else {
199
+ min = TLS1_VERSION;
200
+ }
201
+
202
+ SSL_CTX_set_min_proto_version(ctx, min);
203
+
204
+ SSL_CTX_set_options(ctx, ssl_options);
205
+
206
+ #else
207
+ /* As of 1.0.2f, SSL_OP_SINGLE_DH_USE key use is always on */
208
+ ssl_options |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE;
209
+
210
+ if (RTEST(no_tlsv1)) {
211
+ ssl_options |= SSL_OP_NO_TLSv1;
212
+ }
213
+ if(RTEST(no_tlsv1_1)) {
214
+ ssl_options |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
215
+ }
216
+ SSL_CTX_set_options(ctx, ssl_options);
217
+ #endif
218
+
219
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
220
+
221
+ if (!NIL_P(ssl_cipher_filter)) {
222
+ StringValue(ssl_cipher_filter);
223
+ SSL_CTX_set_cipher_list(ctx, RSTRING_PTR(ssl_cipher_filter));
224
+ }
225
+ else {
226
+ SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH");
227
+ }
228
+
229
+ DH *dh = get_dh1024();
230
+ SSL_CTX_set_tmp_dh(ctx, dh);
231
+
232
+ #if OPENSSL_VERSION_NUMBER < 0x10002000L
233
+ // Remove this case if OpenSSL 1.0.1 (now EOL) support is no
234
+ // longer needed.
235
+ EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
236
+ if (ecdh) {
237
+ SSL_CTX_set_tmp_ecdh(ctx, ecdh);
238
+ EC_KEY_free(ecdh);
239
+ }
240
+ #elif OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
241
+ // Prior to OpenSSL 1.1.0, servers must manually enable server-side ECDH
242
+ // negotiation.
243
+ SSL_CTX_set_ecdh_auto(ctx, 1);
244
+ #endif
245
+
246
+ ssl = SSL_new(ctx);
56
247
  conn->ssl = ssl;
248
+ SSL_set_app_data(ssl, NULL);
57
249
 
58
- /* SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); */
250
+ if (NIL_P(verify_mode)) {
251
+ /* SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); */
252
+ } else {
253
+ SSL_set_verify(ssl, NUM2INT(verify_mode), engine_verify_callback);
254
+ }
59
255
 
60
256
  SSL_set_bio(ssl, conn->read, conn->write);
61
257
 
@@ -66,9 +262,13 @@ VALUE engine_init_server(VALUE self, VALUE key, VALUE cert) {
66
262
  VALUE engine_init_client(VALUE klass) {
67
263
  VALUE obj;
68
264
  ms_conn* conn = engine_alloc(klass, &obj);
69
-
265
+ #ifdef HAVE_DTLS_METHOD
266
+ conn->ctx = SSL_CTX_new(DTLS_method());
267
+ #else
70
268
  conn->ctx = SSL_CTX_new(DTLSv1_method());
269
+ #endif
71
270
  conn->ssl = SSL_new(conn->ctx);
271
+ SSL_set_app_data(conn->ssl, NULL);
72
272
  SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
73
273
 
74
274
  SSL_set_bio(conn->ssl, conn->read, conn->write);
@@ -97,20 +297,46 @@ VALUE engine_inject(VALUE self, VALUE str) {
97
297
  static VALUE eError;
98
298
 
99
299
  void raise_error(SSL* ssl, int result) {
100
- int error = SSL_get_error(ssl, result);
101
- char* msg = ERR_error_string(error, NULL);
300
+ char buf[512];
301
+ char msg[512];
302
+ const char* err_str;
303
+ int err = errno;
304
+ int ssl_err = SSL_get_error(ssl, result);
305
+ int verify_err = (int) SSL_get_verify_result(ssl);
306
+
307
+ if(SSL_ERROR_SYSCALL == ssl_err) {
308
+ snprintf(msg, sizeof(msg), "System error: %s - %d", strerror(err), err);
309
+
310
+ } else if(SSL_ERROR_SSL == ssl_err) {
311
+ if(X509_V_OK != verify_err) {
312
+ err_str = X509_verify_cert_error_string(verify_err);
313
+ snprintf(msg, sizeof(msg),
314
+ "OpenSSL certificate verification error: %s - %d",
315
+ err_str, verify_err);
316
+
317
+ } else {
318
+ err = (int) ERR_get_error();
319
+ ERR_error_string_n(err, buf, sizeof(buf));
320
+ snprintf(msg, sizeof(msg), "OpenSSL error: %s - %d", buf, err);
321
+
322
+ }
323
+ } else {
324
+ snprintf(msg, sizeof(msg), "Unknown OpenSSL error: %d", ssl_err);
325
+ }
102
326
 
103
327
  ERR_clear_error();
104
- rb_raise(eError, "OpenSSL error: %s - %d", msg, error);
328
+ rb_raise(eError, "%s", msg);
105
329
  }
106
330
 
107
331
  VALUE engine_read(VALUE self) {
108
332
  ms_conn* conn;
109
333
  char buf[512];
110
- int bytes, n;
334
+ int bytes, error;
111
335
 
112
336
  Data_Get_Struct(self, ms_conn, conn);
113
337
 
338
+ ERR_clear_error();
339
+
114
340
  bytes = SSL_read(conn->ssl, (void*)buf, sizeof(buf));
115
341
 
116
342
  if(bytes > 0) {
@@ -119,24 +345,27 @@ VALUE engine_read(VALUE self) {
119
345
 
120
346
  if(SSL_want_read(conn->ssl)) return Qnil;
121
347
 
122
- if(SSL_get_error(conn->ssl, bytes) == SSL_ERROR_ZERO_RETURN) {
348
+ error = SSL_get_error(conn->ssl, bytes);
349
+
350
+ if(error == SSL_ERROR_ZERO_RETURN) {
123
351
  rb_eof_error();
352
+ } else {
353
+ raise_error(conn->ssl, bytes);
124
354
  }
125
355
 
126
- raise_error(conn->ssl, bytes);
127
-
128
356
  return Qnil;
129
357
  }
130
358
 
131
359
  VALUE engine_write(VALUE self, VALUE str) {
132
360
  ms_conn* conn;
133
- char buf[512];
134
361
  int bytes;
135
362
 
136
363
  Data_Get_Struct(self, ms_conn, conn);
137
364
 
138
365
  StringValue(str);
139
366
 
367
+ ERR_clear_error();
368
+
140
369
  bytes = SSL_write(conn->ssl, (void*)RSTRING_PTR(str), (int)RSTRING_LEN(str));
141
370
  if(bytes > 0) {
142
371
  return INT2FIX(bytes);
@@ -170,20 +399,127 @@ VALUE engine_extract(VALUE self) {
170
399
  return Qnil;
171
400
  }
172
401
 
402
+ VALUE engine_shutdown(VALUE self) {
403
+ ms_conn* conn;
404
+ int ok;
405
+
406
+ Data_Get_Struct(self, ms_conn, conn);
407
+
408
+ ERR_clear_error();
409
+
410
+ ok = SSL_shutdown(conn->ssl);
411
+ if (ok == 0) {
412
+ return Qfalse;
413
+ }
414
+
415
+ return Qtrue;
416
+ }
417
+
418
+ VALUE engine_init(VALUE self) {
419
+ ms_conn* conn;
420
+
421
+ Data_Get_Struct(self, ms_conn, conn);
422
+
423
+ return SSL_in_init(conn->ssl) ? Qtrue : Qfalse;
424
+ }
425
+
426
+ VALUE engine_peercert(VALUE self) {
427
+ ms_conn* conn;
428
+ X509* cert;
429
+ int bytes;
430
+ unsigned char* buf = NULL;
431
+ ms_cert_buf* cert_buf = NULL;
432
+ VALUE rb_cert_buf;
433
+
434
+ Data_Get_Struct(self, ms_conn, conn);
435
+
436
+ cert = SSL_get_peer_certificate(conn->ssl);
437
+ if(!cert) {
438
+ /*
439
+ * See if there was a failed certificate associated with this client.
440
+ */
441
+ cert_buf = (ms_cert_buf*)SSL_get_app_data(conn->ssl);
442
+ if(!cert_buf) {
443
+ return Qnil;
444
+ }
445
+ buf = cert_buf->buf;
446
+ bytes = cert_buf->bytes;
447
+
448
+ } else {
449
+ bytes = i2d_X509(cert, &buf);
450
+ X509_free(cert);
451
+
452
+ if(bytes < 0) {
453
+ return Qnil;
454
+ }
455
+ }
456
+
457
+ rb_cert_buf = rb_str_new((const char*)(buf), bytes);
458
+ if(!cert_buf) {
459
+ OPENSSL_free(buf);
460
+ }
461
+
462
+ return rb_cert_buf;
463
+ }
464
+
465
+ VALUE noop(VALUE self) {
466
+ return Qnil;
467
+ }
468
+
173
469
  void Init_mini_ssl(VALUE puma) {
174
470
  VALUE mod, eng;
175
471
 
472
+ /* Fake operation for documentation (RDoc, YARD) */
473
+ #if 0 == 1
474
+ puma = rb_define_module("Puma");
475
+ #endif
476
+
176
477
  SSL_library_init();
177
478
  OpenSSL_add_ssl_algorithms();
178
479
  SSL_load_error_strings();
179
480
  ERR_load_crypto_strings();
180
-
481
+
181
482
  mod = rb_define_module_under(puma, "MiniSSL");
182
483
  eng = rb_define_class_under(mod, "Engine", rb_cObject);
183
484
 
485
+ // OpenSSL Build / Runtime/Load versions
486
+
487
+ /* Version of OpenSSL that Puma was compiled with */
488
+ rb_define_const(mod, "OPENSSL_VERSION", rb_str_new2(OPENSSL_VERSION_TEXT));
489
+
490
+ #if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000
491
+ /* Version of OpenSSL that Puma loaded with */
492
+ rb_define_const(mod, "OPENSSL_LIBRARY_VERSION", rb_str_new2(OpenSSL_version(OPENSSL_VERSION)));
493
+ #else
494
+ rb_define_const(mod, "OPENSSL_LIBRARY_VERSION", rb_str_new2(SSLeay_version(SSLEAY_VERSION)));
495
+ #endif
496
+
497
+ #if defined(OPENSSL_NO_SSL3) || defined(OPENSSL_NO_SSL3_METHOD)
498
+ /* True if SSL3 is not available */
499
+ rb_define_const(mod, "OPENSSL_NO_SSL3", Qtrue);
500
+ #else
501
+ rb_define_const(mod, "OPENSSL_NO_SSL3", Qfalse);
502
+ #endif
503
+
504
+ #if defined(OPENSSL_NO_TLS1) || defined(OPENSSL_NO_TLS1_METHOD)
505
+ /* True if TLS1 is not available */
506
+ rb_define_const(mod, "OPENSSL_NO_TLS1", Qtrue);
507
+ #else
508
+ rb_define_const(mod, "OPENSSL_NO_TLS1", Qfalse);
509
+ #endif
510
+
511
+ #if defined(OPENSSL_NO_TLS1_1) || defined(OPENSSL_NO_TLS1_1_METHOD)
512
+ /* True if TLS1_1 is not available */
513
+ rb_define_const(mod, "OPENSSL_NO_TLS1_1", Qtrue);
514
+ #else
515
+ rb_define_const(mod, "OPENSSL_NO_TLS1_1", Qfalse);
516
+ #endif
517
+
518
+ rb_define_singleton_method(mod, "check", noop, 0);
519
+
184
520
  eError = rb_define_class_under(mod, "SSLError", rb_eStandardError);
185
521
 
186
- rb_define_singleton_method(eng, "server", engine_init_server, 2);
522
+ rb_define_singleton_method(eng, "server", engine_init_server, 1);
187
523
  rb_define_singleton_method(eng, "client", engine_init_client, 0);
188
524
 
189
525
  rb_define_method(eng, "inject", engine_inject, 1);
@@ -191,4 +527,27 @@ void Init_mini_ssl(VALUE puma) {
191
527
 
192
528
  rb_define_method(eng, "write", engine_write, 1);
193
529
  rb_define_method(eng, "extract", engine_extract, 0);
530
+
531
+ rb_define_method(eng, "shutdown", engine_shutdown, 0);
532
+
533
+ rb_define_method(eng, "init?", engine_init, 0);
534
+
535
+ rb_define_method(eng, "peercert", engine_peercert, 0);
536
+ }
537
+
538
+ #else
539
+
540
+ VALUE raise_error(VALUE self) {
541
+ rb_raise(rb_eStandardError, "SSL not available in this build");
542
+ return Qnil;
543
+ }
544
+
545
+ void Init_mini_ssl(VALUE puma) {
546
+ VALUE mod;
547
+
548
+ mod = rb_define_module_under(puma, "MiniSSL");
549
+ rb_define_class_under(mod, "SSLError", rb_eStandardError);
550
+
551
+ rb_define_singleton_method(mod, "check", raise_error, 0);
194
552
  }
553
+ #endif