rack-tctp 0.9.4 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/engine/engine.c +216 -0
- data/ext/engine/extconf.rb +6 -0
- data/lib/rack/tctp/halec.rb +75 -42
- data/lib/rack/tctp.rb +29 -24
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b6b91b55890f0a473f132a0e6c2eec7fe5aa3254
|
4
|
+
data.tar.gz: 8ac625d2bb5a6576a5cc164fed0b7a928419d129
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d93b73d45ac80ad26c8c6f5d1b10964cf6f07d47a7a4a4fde519d96d7206c74613cae47301658d6e2d50ae44ed658666c8eb6bc5323b4c9d3eba15f8543577a8
|
7
|
+
data.tar.gz: b68e836d5e51dd6cfbc10ff89b7b67f98ed3adeee36f34faabab4dc117d7bcdbe61bae9bd4a36f1177f51b80ce40b61335ce60927381d6636de71dbe4375330c
|
data/ext/engine/engine.c
ADDED
@@ -0,0 +1,216 @@
|
|
1
|
+
//Modified from: https://github.com/puma/puma/blob/master/ext/puma_http11/mini_ssl.c
|
2
|
+
#define RSTRING_NOT_MODIFIED 1
|
3
|
+
#include <assert.h>
|
4
|
+
#include <ruby.h>
|
5
|
+
#include <rubyio.h>
|
6
|
+
#include <openssl/bio.h>
|
7
|
+
#include <openssl/ssl.h>
|
8
|
+
#include <openssl/err.h>
|
9
|
+
|
10
|
+
typedef struct {
|
11
|
+
BIO* read;
|
12
|
+
BIO* write;
|
13
|
+
SSL* ssl;
|
14
|
+
SSL_CTX* ctx;
|
15
|
+
} ms_conn;
|
16
|
+
|
17
|
+
void engine_free(ms_conn* conn) {
|
18
|
+
BIO_free(conn->read);
|
19
|
+
BIO_free(conn->write);
|
20
|
+
|
21
|
+
free(conn);
|
22
|
+
}
|
23
|
+
|
24
|
+
static VALUE eError;
|
25
|
+
|
26
|
+
void raise_error(SSL* ssl, int result) {
|
27
|
+
char buf[256];
|
28
|
+
u_long err;
|
29
|
+
|
30
|
+
while ((err = ERR_get_error()) != 0) {
|
31
|
+
ERR_error_string_n(err, buf, sizeof(buf));
|
32
|
+
printf("*** %s\n", buf);
|
33
|
+
}
|
34
|
+
|
35
|
+
ERR_clear_error();
|
36
|
+
rb_raise(eError, "OpenSSL error");
|
37
|
+
}
|
38
|
+
|
39
|
+
ms_conn* engine_alloc(VALUE klass, VALUE* obj) {
|
40
|
+
ms_conn* conn;
|
41
|
+
|
42
|
+
*obj = Data_Make_Struct(klass, ms_conn, 0, engine_free, conn);
|
43
|
+
|
44
|
+
conn->read = BIO_new(BIO_s_mem());
|
45
|
+
BIO_set_nbio(conn->read, 1);
|
46
|
+
|
47
|
+
conn->write = BIO_new(BIO_s_mem());
|
48
|
+
BIO_set_nbio(conn->write, 1);
|
49
|
+
|
50
|
+
conn->ssl = 0;
|
51
|
+
conn->ctx = 0;
|
52
|
+
|
53
|
+
return conn;
|
54
|
+
}
|
55
|
+
|
56
|
+
VALUE engine_init_server(VALUE self, VALUE key, VALUE cert) {
|
57
|
+
VALUE obj;
|
58
|
+
SSL_CTX* ctx;
|
59
|
+
SSL* ssl;
|
60
|
+
int use_certificate_file_ret, use_pk_file_ret;
|
61
|
+
|
62
|
+
ms_conn* conn = engine_alloc(self, &obj);
|
63
|
+
|
64
|
+
StringValue(key);
|
65
|
+
StringValue(cert);
|
66
|
+
|
67
|
+
ctx = SSL_CTX_new(TLSv1_server_method());
|
68
|
+
conn->ctx = ctx;
|
69
|
+
|
70
|
+
use_certificate_file_ret = SSL_CTX_use_certificate_file(ctx, RSTRING_PTR(cert), SSL_FILETYPE_PEM);
|
71
|
+
if(use_certificate_file_ret != 1) {
|
72
|
+
raise_error(conn->ssl, 0);
|
73
|
+
}
|
74
|
+
|
75
|
+
use_pk_file_ret = SSL_CTX_use_PrivateKey_file(ctx, RSTRING_PTR(key), SSL_FILETYPE_PEM);
|
76
|
+
if(use_pk_file_ret != 1) {
|
77
|
+
raise_error(conn->ssl, 0);
|
78
|
+
}
|
79
|
+
|
80
|
+
SSL_CTX_set_cipher_list(ctx, "ALL");
|
81
|
+
SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);
|
82
|
+
|
83
|
+
ssl = SSL_new(ctx);
|
84
|
+
conn->ssl = ssl;
|
85
|
+
|
86
|
+
SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
|
87
|
+
|
88
|
+
SSL_set_bio(conn->ssl, conn->read, conn->write);
|
89
|
+
|
90
|
+
SSL_set_accept_state(ssl);
|
91
|
+
return obj;
|
92
|
+
}
|
93
|
+
|
94
|
+
VALUE engine_init_client(VALUE klass) {
|
95
|
+
VALUE obj;
|
96
|
+
ms_conn* conn = engine_alloc(klass, &obj);
|
97
|
+
|
98
|
+
conn->ctx = SSL_CTX_new(TLSv1_client_method());
|
99
|
+
SSL_CTX_set_cipher_list(conn->ctx, "ALL");
|
100
|
+
|
101
|
+
conn->ssl = SSL_new(conn->ctx);
|
102
|
+
|
103
|
+
SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
|
104
|
+
|
105
|
+
SSL_set_bio(conn->ssl, conn->read, conn->write);
|
106
|
+
|
107
|
+
SSL_set_connect_state(conn->ssl);
|
108
|
+
return obj;
|
109
|
+
}
|
110
|
+
|
111
|
+
VALUE engine_inject(VALUE self, VALUE str) {
|
112
|
+
ms_conn* conn;
|
113
|
+
long used;
|
114
|
+
|
115
|
+
Data_Get_Struct(self, ms_conn, conn);
|
116
|
+
|
117
|
+
StringValue(str);
|
118
|
+
|
119
|
+
used = BIO_write(conn->read, RSTRING_PTR(str), (int)RSTRING_LEN(str));
|
120
|
+
|
121
|
+
if(used == 0 || used == -1) {
|
122
|
+
return Qfalse;
|
123
|
+
}
|
124
|
+
|
125
|
+
return INT2FIX(used);
|
126
|
+
}
|
127
|
+
|
128
|
+
VALUE engine_read(VALUE self) {
|
129
|
+
ms_conn* conn;
|
130
|
+
char buf[512];
|
131
|
+
int bytes, n;
|
132
|
+
|
133
|
+
Data_Get_Struct(self, ms_conn, conn);
|
134
|
+
|
135
|
+
bytes = SSL_read(conn->ssl, (void*)buf, sizeof(buf));
|
136
|
+
|
137
|
+
if(bytes > 0) {
|
138
|
+
return rb_str_new(buf, bytes);
|
139
|
+
}
|
140
|
+
|
141
|
+
if(SSL_want_read(conn->ssl)) return Qnil;
|
142
|
+
|
143
|
+
if(SSL_get_error(conn->ssl, bytes) == SSL_ERROR_ZERO_RETURN) {
|
144
|
+
rb_eof_error();
|
145
|
+
}
|
146
|
+
|
147
|
+
raise_error(conn->ssl, bytes);
|
148
|
+
|
149
|
+
return Qnil;
|
150
|
+
}
|
151
|
+
|
152
|
+
VALUE engine_write(VALUE self, VALUE str) {
|
153
|
+
ms_conn* conn;
|
154
|
+
char buf[512];
|
155
|
+
int bytes;
|
156
|
+
|
157
|
+
Data_Get_Struct(self, ms_conn, conn);
|
158
|
+
|
159
|
+
StringValue(str);
|
160
|
+
|
161
|
+
bytes = SSL_write(conn->ssl, (void*)RSTRING_PTR(str), (int)RSTRING_LEN(str));
|
162
|
+
if(bytes > 0) {
|
163
|
+
return INT2FIX(bytes);
|
164
|
+
}
|
165
|
+
|
166
|
+
if(SSL_want_write(conn->ssl)) return Qnil;
|
167
|
+
|
168
|
+
raise_error(conn->ssl, bytes);
|
169
|
+
|
170
|
+
return Qnil;
|
171
|
+
}
|
172
|
+
|
173
|
+
VALUE engine_extract(VALUE self) {
|
174
|
+
ms_conn* conn;
|
175
|
+
int bytes;
|
176
|
+
size_t pending;
|
177
|
+
char buf[512];
|
178
|
+
|
179
|
+
Data_Get_Struct(self, ms_conn, conn);
|
180
|
+
|
181
|
+
pending = BIO_pending(conn->write);
|
182
|
+
if(pending > 0) {
|
183
|
+
bytes = BIO_read(conn->write, buf, sizeof(buf));
|
184
|
+
if(bytes > 0) {
|
185
|
+
return rb_str_new(buf, bytes);
|
186
|
+
} else if(!BIO_should_retry(conn->write)) {
|
187
|
+
raise_error(conn->ssl, bytes);
|
188
|
+
}
|
189
|
+
}
|
190
|
+
|
191
|
+
return Qnil;
|
192
|
+
}
|
193
|
+
|
194
|
+
void Init_engine() {
|
195
|
+
VALUE mod, eng, rack;
|
196
|
+
|
197
|
+
SSL_library_init();
|
198
|
+
OpenSSL_add_ssl_algorithms();
|
199
|
+
SSL_load_error_strings();
|
200
|
+
ERR_load_crypto_strings();
|
201
|
+
|
202
|
+
rack = rb_define_module("Rack");
|
203
|
+
mod = rb_define_class_under(rack, "TCTP", rb_cObject);
|
204
|
+
eng = rb_define_class_under(mod, "Engine", rb_cObject);
|
205
|
+
|
206
|
+
eError = rb_define_class_under(mod, "SSLError", rb_eStandardError);
|
207
|
+
|
208
|
+
rb_define_singleton_method(eng, "server", engine_init_server, 2);
|
209
|
+
rb_define_singleton_method(eng, "client", engine_init_client, 0);
|
210
|
+
|
211
|
+
rb_define_method(eng, "inject", engine_inject, 1);
|
212
|
+
rb_define_method(eng, "read", engine_read, 0);
|
213
|
+
|
214
|
+
rb_define_method(eng, "write", engine_write, 1);
|
215
|
+
rb_define_method(eng, "extract", engine_extract, 0);
|
216
|
+
}
|
data/lib/rack/tctp/halec.rb
CHANGED
@@ -1,19 +1,16 @@
|
|
1
1
|
require 'openssl'
|
2
2
|
require 'socket'
|
3
|
+
require 'radix'
|
3
4
|
|
4
|
-
|
5
|
-
class HALEC
|
6
|
-
# The URL of this HALEC
|
7
|
-
attr_reader :url
|
8
|
-
|
9
|
-
# The plaintext socket
|
10
|
-
attr_reader :socket_here
|
5
|
+
require 'rack/tctp/engine'
|
11
6
|
|
12
|
-
|
13
|
-
|
7
|
+
# HTTP application layer encryption channel. Used for the Trusted Cloud Transfer Protocol (TCTP)
|
8
|
+
class Rack::TCTP::HALEC
|
9
|
+
# The SSL engine
|
10
|
+
attr_reader :engine
|
14
11
|
|
15
|
-
# The
|
16
|
-
|
12
|
+
# The URL of this HALEC
|
13
|
+
attr_accessor :url
|
17
14
|
|
18
15
|
# The private key for a certificate (if any)
|
19
16
|
attr_reader :private_key
|
@@ -21,40 +18,68 @@ class HALEC
|
|
21
18
|
# A server or client certificate (if any)
|
22
19
|
attr_reader :certificate
|
23
20
|
|
24
|
-
# The TLS context
|
25
|
-
attr_reader :ctx
|
26
|
-
|
27
21
|
def initialize(options = {})
|
28
|
-
@url = options[:url] ||
|
29
|
-
|
30
|
-
|
31
|
-
@ctx.ssl_version = :TLSv1
|
22
|
+
@url = options[:url] || nil
|
23
|
+
end
|
32
24
|
|
33
|
-
|
34
|
-
|
35
|
-
|
25
|
+
# Encrypts +plaintext+ data and either returns the encrypted data or calls a block with it.
|
26
|
+
# @param [String] plaintext The plaintext
|
27
|
+
# @return [String] The encrypted data
|
28
|
+
# @yield Gives the encrypted data to the block
|
29
|
+
# @yieldparam [String] The encrypted data
|
30
|
+
def encrypt_data(plaintext, &encrypted)
|
31
|
+
@engine.write plaintext
|
32
|
+
|
33
|
+
while(read_chunk = @engine.extract)
|
34
|
+
if(block_given?)
|
35
|
+
encrypted.call read_chunk
|
36
|
+
else
|
37
|
+
if read_data
|
38
|
+
read_data.write read_chunk
|
39
|
+
else
|
40
|
+
read_data = StringIO.new(read_chunk)
|
41
|
+
end
|
42
|
+
end
|
36
43
|
end
|
44
|
+
|
45
|
+
read_data.string unless block_given?
|
37
46
|
end
|
38
47
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
48
|
+
# Decrypts +encrypted+ data and either returns the plaintext or calls a block with it.
|
49
|
+
# @param [String] encrypted The encrypted data
|
50
|
+
# @return [String] The plaintext
|
51
|
+
# @yield Gives the plaintext to the block
|
52
|
+
# @yieldparam [String] The plaintext
|
53
|
+
def decrypt_data(encrypted, &decrypted)
|
54
|
+
@engine.inject encrypted
|
55
|
+
|
56
|
+
while(read_chunk = @engine.read)
|
57
|
+
if(block_given?)
|
58
|
+
decrypted.call read_chunk
|
59
|
+
else
|
60
|
+
if read_data
|
61
|
+
read_data.write read_chunk
|
62
|
+
else
|
63
|
+
read_data = StringIO.new(read_chunk)
|
64
|
+
end
|
65
|
+
end
|
44
66
|
end
|
67
|
+
|
68
|
+
read_data.string unless block_given?
|
69
|
+
end
|
45
70
|
end
|
46
71
|
|
47
72
|
# The Client end of an HALEC
|
48
|
-
class ClientHALEC < HALEC
|
73
|
+
class Rack::TCTP::ClientHALEC < Rack::TCTP::HALEC
|
49
74
|
def initialize(options = {})
|
50
75
|
super(options)
|
51
76
|
|
52
|
-
@
|
77
|
+
@engine = Rack::TCTP::Engine.client
|
53
78
|
end
|
54
79
|
end
|
55
80
|
|
56
81
|
# The Server end of an HALEC
|
57
|
-
class ServerHALEC < HALEC
|
82
|
+
class Rack::TCTP::ServerHALEC < Rack::TCTP::HALEC
|
58
83
|
def initialize(options = {})
|
59
84
|
super(options)
|
60
85
|
|
@@ -62,21 +87,19 @@ class ServerHALEC < HALEC
|
|
62
87
|
@private_key = options[:private_key]
|
63
88
|
@certificate = options[:certificate]
|
64
89
|
else
|
65
|
-
@private_key =
|
66
|
-
@certificate =
|
90
|
+
@private_key = self.class.default_key
|
91
|
+
@certificate = self.class.default_self_signed_certificate
|
67
92
|
end
|
68
93
|
|
69
|
-
@
|
70
|
-
@
|
94
|
+
@private_key_file = Tempfile.new('rack_tctp_pk')
|
95
|
+
@private_key_file.write @private_key.to_s
|
96
|
+
@private_key_file.close
|
71
97
|
|
72
|
-
@
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
puts e
|
78
|
-
end
|
79
|
-
}
|
98
|
+
@certificate_file = Tempfile.new('rack_tctp_cert')
|
99
|
+
@certificate_file.write @certificate.to_s
|
100
|
+
@certificate_file.close
|
101
|
+
|
102
|
+
@engine = Rack::TCTP::Engine.server(@private_key_file.path, @certificate_file.path)
|
80
103
|
end
|
81
104
|
|
82
105
|
class << self
|
@@ -112,5 +135,15 @@ class ServerHALEC < HALEC
|
|
112
135
|
|
113
136
|
cert
|
114
137
|
end
|
138
|
+
|
139
|
+
# The slug URI can contain any HTTP compatible characters
|
140
|
+
def slug_base
|
141
|
+
Radix::Base.new(Radix::BASE::B62 + ['-', '_'])
|
142
|
+
end
|
143
|
+
|
144
|
+
# Generate a new random slug (2^64 possibilities)
|
145
|
+
def new_slug
|
146
|
+
slug_base.convert(rand(2**64), 10)
|
147
|
+
end
|
115
148
|
end
|
116
|
-
end
|
149
|
+
end
|
data/lib/rack/tctp.rb
CHANGED
@@ -26,8 +26,8 @@ module Rack
|
|
26
26
|
# Initializes TCTP middleware
|
27
27
|
def initialize(app, logger = nil)
|
28
28
|
unless logger
|
29
|
-
@logger =
|
30
|
-
@logger.level = Logger::FATAL
|
29
|
+
@logger = ::Logger.new(STDOUT)
|
30
|
+
@logger.level = ::Logger::FATAL
|
31
31
|
else
|
32
32
|
@logger = logger
|
33
33
|
end
|
@@ -52,21 +52,22 @@ module Rack
|
|
52
52
|
when is_tctp_discovery?(req)
|
53
53
|
# TCTP discovery
|
54
54
|
# TODO Parameterize discovery information
|
55
|
-
[200, {"Content-Type" => TCTP_DISCOVERY_MEDIA_TYPE, "Content-Length" => DEFAULT_TCTP_DISCOVERY_INFORMATION.length.to_s}, DEFAULT_TCTP_DISCOVERY_INFORMATION]
|
55
|
+
[200, {"Content-Type" => TCTP_DISCOVERY_MEDIA_TYPE, "Content-Length" => DEFAULT_TCTP_DISCOVERY_INFORMATION.length.to_s}, [DEFAULT_TCTP_DISCOVERY_INFORMATION]]
|
56
56
|
when is_halec_creation?(req)
|
57
57
|
# HALEC creation
|
58
|
-
halec = ServerHALEC.new(url:
|
58
|
+
halec = ServerHALEC.new(url: halec_uri(req.env, "/halecs/#{TCTP::new_slug}"))
|
59
59
|
|
60
60
|
# TODO Allow creation using predefined cookie
|
61
61
|
session = TCTPSession.new
|
62
62
|
|
63
63
|
# Send client_hello to server HALEC and read handshake_response
|
64
64
|
client_hello = req.body.read
|
65
|
-
halec.
|
66
|
-
|
65
|
+
halec.engine.inject client_hello
|
66
|
+
halec.engine.read
|
67
|
+
handshake_response = [halec.engine.extract]
|
67
68
|
|
68
69
|
# Set location header and content-length
|
69
|
-
header = {'Location' => halec.url, 'Content-Length' => handshake_response[0].length.to_s}
|
70
|
+
header = {'Location' => halec.url.to_s, 'Content-Length' => handshake_response[0].length.to_s}
|
70
71
|
|
71
72
|
# Set the TCTP session cookie header
|
72
73
|
Rack::Utils.set_cookie_header!(header, "tctp_session_cookie", {:value => session.session_id, :path => '/', :expires => Time.now+24*60*60})
|
@@ -78,13 +79,14 @@ module Rack
|
|
78
79
|
[201, header, handshake_response]
|
79
80
|
when is_halec_handshake?(req)
|
80
81
|
# Get persisted server HALEC
|
81
|
-
halec = @sessions[req.cookies['tctp_session_cookie']].halecs[req.path_info]
|
82
|
+
halec = @sessions[req.cookies['tctp_session_cookie']].halecs[halec_uri(req.env, req.path_info)]
|
82
83
|
|
83
84
|
# Write handshake message to server HALEC
|
84
|
-
halec.
|
85
|
+
halec.engine.inject req.body.read
|
85
86
|
|
86
87
|
# Receive handshake response
|
87
|
-
|
88
|
+
halec.engine.read
|
89
|
+
handshake_response = halec.engine.extract
|
88
90
|
|
89
91
|
# Send back server HALEC response
|
90
92
|
[200, {'Content-Length' => handshake_response.length.to_s}, [handshake_response]]
|
@@ -96,10 +98,11 @@ module Rack
|
|
96
98
|
halec_url = req.body.readline.chomp
|
97
99
|
|
98
100
|
# Gets the HALEC
|
99
|
-
halec = @sessions[req.cookies['tctp_session_cookie']].halecs[halec_url]
|
101
|
+
halec = @sessions[req.cookies['tctp_session_cookie']].halecs[URI(halec_url)]
|
100
102
|
|
101
|
-
|
102
|
-
|
103
|
+
read_body = req.body.read
|
104
|
+
|
105
|
+
decrypted_body.write halec.decrypt_data(read_body)
|
103
106
|
|
104
107
|
req.body.string = decrypted_body.string
|
105
108
|
end
|
@@ -125,7 +128,7 @@ module Rack
|
|
125
128
|
content_body_length = 0
|
126
129
|
|
127
130
|
# The first line
|
128
|
-
first_line = halec.url + "\r\n"
|
131
|
+
first_line = halec.url.to_s + "\r\n"
|
129
132
|
content_body_length += first_line.length
|
130
133
|
|
131
134
|
# Encrypt the body. The first line of the response specifies the used HALEC
|
@@ -134,15 +137,9 @@ module Rack
|
|
134
137
|
|
135
138
|
# Encrypt each body fragment
|
136
139
|
body.each do |fragment|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
chunk = bodyio.read(16 * 1024)
|
141
|
-
halec.ssl_socket.write(chunk)
|
142
|
-
encrypted_chunk = halec.socket_there.readpartial(32 * 1024)
|
143
|
-
encrypted_body << encrypted_chunk
|
144
|
-
content_body_length += encrypted_chunk.length
|
145
|
-
end
|
140
|
+
encrypted_fragment = halec.encrypt_data fragment
|
141
|
+
encrypted_body << encrypted_fragment
|
142
|
+
content_body_length += encrypted_fragment.length
|
146
143
|
end
|
147
144
|
|
148
145
|
# Finding this bug took waaaay too long ...
|
@@ -190,7 +187,7 @@ module Rack
|
|
190
187
|
!req.cookies.nil? &&
|
191
188
|
req.cookies.has_key?('tctp_session_cookie') &&
|
192
189
|
sessions.has_key?(req.cookies['tctp_session_cookie']) &&
|
193
|
-
sessions[req.cookies['tctp_session_cookie']].halecs.has_key?(req.path_info)
|
190
|
+
sessions[req.cookies['tctp_session_cookie']].halecs.has_key?(halec_uri(req.env, req.path_info))
|
194
191
|
end
|
195
192
|
|
196
193
|
def is_tctp_response_requested? (req)
|
@@ -200,6 +197,14 @@ module Rack
|
|
200
197
|
def is_tctp_encrypted_body? (req)
|
201
198
|
req.env['HTTP_CONTENT_ENCODING'].eql?('encrypted')
|
202
199
|
end
|
200
|
+
|
201
|
+
# Builds an URI object to be used as a HALEC +uri+
|
202
|
+
# @param [Hash] env A Rack environment hash
|
203
|
+
# @param [String] path A path
|
204
|
+
# @return [URI] An HALEC +uri+
|
205
|
+
def halec_uri(env, path)
|
206
|
+
URI("#{env['rack.url_scheme']}://#{env['HTTP_HOST']}:#{env['SERVER_PORT']}#{path}")
|
207
|
+
end
|
203
208
|
end
|
204
209
|
|
205
210
|
class TCTPSession
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-tctp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mathias Slawik
|
@@ -97,12 +97,15 @@ dependencies:
|
|
97
97
|
description: Rack middleware for end-to-end security through TCTP
|
98
98
|
email: mathias.slawik@tu-berlin.de
|
99
99
|
executables: []
|
100
|
-
extensions:
|
100
|
+
extensions:
|
101
|
+
- ext/engine/extconf.rb
|
101
102
|
extra_rdoc_files: []
|
102
103
|
files:
|
103
104
|
- lib/rack-tctp.rb
|
104
105
|
- lib/rack/tctp.rb
|
105
106
|
- lib/rack/tctp/halec.rb
|
107
|
+
- ext/engine/engine.c
|
108
|
+
- ext/engine/extconf.rb
|
106
109
|
homepage: https://github.com/mathiasslawik/rack-tctp
|
107
110
|
licenses:
|
108
111
|
- Apache-2.0
|
@@ -128,3 +131,4 @@ signing_key:
|
|
128
131
|
specification_version: 4
|
129
132
|
summary: Rack TCTP middleware
|
130
133
|
test_files: []
|
134
|
+
has_rdoc:
|