Skip to content

Commit a3e0c16

Browse files
authored
Merge pull request #831 from byroot/reduce-buffering-do-write-overhead
Reduce OpenSSL::Buffering#do_write overhead
2 parents 2733bea + 28f2901 commit a3e0c16

File tree

3 files changed

+63
-30
lines changed

3 files changed

+63
-30
lines changed

ext/openssl/ossl_ssl.c

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2054,28 +2054,32 @@ ossl_ssl_read_nonblock(int argc, VALUE *argv, VALUE self)
20542054
}
20552055

20562056
static VALUE
2057-
ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
2057+
ossl_ssl_write_internal_safe(VALUE _args)
20582058
{
2059+
VALUE *args = (VALUE*)_args;
2060+
VALUE self = args[0];
2061+
VALUE str = args[1];
2062+
VALUE opts = args[2];
2063+
20592064
SSL *ssl;
20602065
rb_io_t *fptr;
20612066
int num, nonblock = opts != Qfalse;
2062-
VALUE tmp, cb_state;
2067+
VALUE cb_state;
20632068

20642069
GetSSL(self, ssl);
20652070
if (!ssl_started(ssl))
20662071
rb_raise(eSSLError, "SSL session is not started yet");
20672072

2068-
tmp = rb_str_new_frozen(StringValue(str));
20692073
VALUE io = rb_attr_get(self, id_i_io);
20702074
GetOpenFile(io, fptr);
20712075

20722076
/* SSL_write(3ssl) manpage states num == 0 is undefined */
2073-
num = RSTRING_LENINT(tmp);
2077+
num = RSTRING_LENINT(str);
20742078
if (num == 0)
20752079
return INT2FIX(0);
20762080

20772081
for (;;) {
2078-
int nwritten = SSL_write(ssl, RSTRING_PTR(tmp), num);
2082+
int nwritten = SSL_write(ssl, RSTRING_PTR(str), num);
20792083

20802084
cb_state = rb_attr_get(self, ID_callback_state);
20812085
if (!NIL_P(cb_state)) {
@@ -2116,6 +2120,29 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
21162120
}
21172121
}
21182122

2123+
2124+
static VALUE
2125+
ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
2126+
{
2127+
VALUE args[3] = {self, str, opts};
2128+
int state;
2129+
str = StringValue(str);
2130+
2131+
int frozen = RB_OBJ_FROZEN(str);
2132+
if (!frozen) {
2133+
str = rb_str_locktmp(str);
2134+
}
2135+
VALUE result = rb_protect(ossl_ssl_write_internal_safe, (VALUE)args, &state);
2136+
if (!frozen) {
2137+
rb_str_unlocktmp(str);
2138+
}
2139+
2140+
if (state) {
2141+
rb_jump_tag(state);
2142+
}
2143+
return result;
2144+
}
2145+
21192146
/*
21202147
* call-seq:
21212148
* ssl.syswrite(string) => Integer

lib/openssl/buffering.rb

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,21 @@ module OpenSSL::Buffering
2424

2525
# A buffer which will retain binary encoding.
2626
class Buffer < String
27-
BINARY = Encoding::BINARY
28-
29-
def initialize
30-
super
31-
32-
force_encoding(BINARY)
33-
end
27+
unless String.method_defined?(:append_as_bytes)
28+
alias_method :_append, :<<
29+
def append_as_bytes(string)
30+
if string.encoding == Encoding::BINARY
31+
_append(string)
32+
else
33+
_append(string.b)
34+
end
3435

35-
def << string
36-
if string.encoding == BINARY
37-
super(string)
38-
else
39-
super(string.b)
36+
self
4037
end
41-
42-
return self
4338
end
4439

45-
alias concat <<
40+
undef_method :concat
41+
undef_method :<<
4642
end
4743

4844
##
@@ -77,7 +73,7 @@ def initialize(*)
7773

7874
def fill_rbuff
7975
begin
80-
@rbuffer << self.sysread(BLOCK_SIZE)
76+
@rbuffer.append_as_bytes(self.sysread(BLOCK_SIZE))
8177
rescue Errno::EAGAIN
8278
retry
8379
rescue EOFError
@@ -352,22 +348,32 @@ def eof?
352348

353349
def do_write(s)
354350
@wbuffer = Buffer.new unless defined? @wbuffer
355-
@wbuffer << s
356-
@wbuffer.force_encoding(Encoding::BINARY)
351+
@wbuffer.append_as_bytes(s)
352+
357353
@sync ||= false
358-
buffer_size = @wbuffer.size
354+
buffer_size = @wbuffer.bytesize
359355
if @sync or buffer_size > BLOCK_SIZE
360356
nwrote = 0
361357
begin
362358
while nwrote < buffer_size do
363359
begin
364-
nwrote += syswrite(@wbuffer[nwrote, buffer_size - nwrote])
360+
chunk = if nwrote > 0
361+
@wbuffer.byteslice(nwrote, @wbuffer.bytesize)
362+
else
363+
@wbuffer
364+
end
365+
366+
nwrote += syswrite(chunk)
365367
rescue Errno::EAGAIN
366368
retry
367369
end
368370
end
369371
ensure
370-
@wbuffer[0, nwrote] = ""
372+
if nwrote < @wbuffer.bytesize
373+
@wbuffer[0, nwrote] = ""
374+
else
375+
@wbuffer.clear
376+
end
371377
end
372378
end
373379
end
@@ -444,10 +450,10 @@ def <<(s)
444450
def puts(*args)
445451
s = Buffer.new
446452
if args.empty?
447-
s << "\n"
453+
s.append_as_bytes("\n")
448454
end
449455
args.each{|arg|
450-
s << arg.to_s
456+
s.append_as_bytes(arg.to_s)
451457
s.sub!(/(?<!\n)\z/, "\n")
452458
}
453459
do_write(s)
@@ -461,7 +467,7 @@ def puts(*args)
461467

462468
def print(*args)
463469
s = Buffer.new
464-
args.each{ |arg| s << arg.to_s }
470+
args.each{ |arg| s.append_as_bytes(arg.to_s) }
465471
do_write(s)
466472
nil
467473
end

test/openssl/test_buffering.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def sysread(size)
3131
end
3232

3333
def syswrite(str)
34-
@io << str
34+
@io.append_as_bytes(str)
3535
str.size
3636
end
3737
end

0 commit comments

Comments
 (0)