escape_utils 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -1
- data/CHANGELOG.md +3 -0
- data/README.rdoc +17 -5
- data/VERSION +1 -1
- data/benchmark/{escape.rb → html_escape.rb} +0 -0
- data/benchmark/{unescape.rb → html_unescape.rb} +0 -0
- data/benchmark/javascript_escape.rb +36 -0
- data/escape_utils.gemspec +6 -3
- data/ext/escape_utils.c +63 -0
- data/lib/escape_utils.rb +1 -1
- data/spec/html/escape_spec.rb +12 -14
- data/spec/html/unescape_spec.rb +12 -14
- data/spec/javascript/escape_spec.rb +25 -0
- metadata +7 -4
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
data/README.rdoc
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Being as though we're all html escaping everything these days, why not make it faster?
|
4
4
|
|
5
|
-
At the moment escape_utils supports escaping and unescaping of HTML, but I wanna add URL encoding soon
|
5
|
+
At the moment escape_utils supports escaping and unescaping of HTML, and Javascript but I wanna add URL encoding soon
|
6
6
|
|
7
7
|
It has monkey-patches for Rack::Utils, CGI, ERB::Util and Haml
|
8
8
|
|
@@ -23,6 +23,11 @@ It has monkey-patches for Rack::Utils, CGI, ERB::Util and Haml
|
|
23
23
|
escaped_html = EscapeUtils.escape_html(html)
|
24
24
|
html = EscapeUtils.unescape_html(escaped_html)
|
25
25
|
|
26
|
+
=== Escaping Javascript
|
27
|
+
|
28
|
+
javascript = `curl -s http://code.jquery.com/jquery-1.4.2.js`
|
29
|
+
escaped_javascript = EscapeUtils.escape_javascript(javascript)
|
30
|
+
|
26
31
|
=== Monkey Patches
|
27
32
|
|
28
33
|
require 'escape_utils/rack' # to patch Rack::Utils
|
@@ -33,11 +38,11 @@ It has monkey-patches for Rack::Utils, CGI, ERB::Util and Haml
|
|
33
38
|
== Benchmarks
|
34
39
|
|
35
40
|
In my testing, escaping is around 10-20x faster than the pure ruby implementations in wide use today.
|
36
|
-
While unescaping is around
|
41
|
+
While unescaping is around 20-40x faster than CGI.unescapeHTML - also pure ruby.
|
37
42
|
|
38
43
|
This output is from my laptop using the benchmark scripts in the benchmarks folder.
|
39
44
|
|
40
|
-
=== Escaping
|
45
|
+
=== HTML Escaping
|
41
46
|
|
42
47
|
Rack::Utils.escape_html
|
43
48
|
0.560000 0.040000 0.600000 ( 0.589475)
|
@@ -50,9 +55,16 @@ This output is from my laptop using the benchmark scripts in the benchmarks fold
|
|
50
55
|
EscapeUtils.escape_html
|
51
56
|
0.050000 0.010000 0.060000 ( 0.054799)
|
52
57
|
|
53
|
-
=== Unescaping
|
58
|
+
=== HTML Unescaping
|
54
59
|
|
55
60
|
CGI.unescapeHTML
|
56
61
|
1.140000 0.010000 1.150000 ( 1.148470)
|
57
62
|
EscapeUtils.unescape_html
|
58
|
-
0.040000 0.000000 0.040000 ( 0.046166)
|
63
|
+
0.040000 0.000000 0.040000 ( 0.046166)
|
64
|
+
|
65
|
+
=== Javascript Escaping
|
66
|
+
|
67
|
+
ActionView::Helpers::JavaScriptHelper#escape_javascript
|
68
|
+
2.000000 0.020000 2.020000 ( 2.023047)
|
69
|
+
EscapeUtils.escape_javascript
|
70
|
+
0.110000 0.010000 0.120000 ( 0.121761)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.1
|
File without changes
|
File without changes
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/..')
|
3
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'benchmark'
|
7
|
+
|
8
|
+
require 'action_view'
|
9
|
+
require 'escape_utils'
|
10
|
+
|
11
|
+
class ActionPackBench
|
12
|
+
extend ActionView::Helpers::JavaScriptHelper
|
13
|
+
end
|
14
|
+
|
15
|
+
times = 100
|
16
|
+
url = "http://code.jquery.com/jquery-1.4.2.js"
|
17
|
+
javascript = `curl -s #{url}`
|
18
|
+
puts "Escaping #{javascript.bytesize} bytes of javascript from #{url}"
|
19
|
+
|
20
|
+
puts ActionPackBench.escape_javascript(javascript).eql?(EscapeUtils.escape_javascript(javascript))
|
21
|
+
|
22
|
+
Benchmark.bmbm do |x|
|
23
|
+
x.report do
|
24
|
+
puts "ActionView::Helpers::JavaScriptHelper#escape_javascript"
|
25
|
+
times.times do
|
26
|
+
ActionPackBench.escape_javascript(javascript)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
x.report do
|
31
|
+
puts "EscapeUtils.escape_javascript"
|
32
|
+
times.times do
|
33
|
+
EscapeUtils.escape_javascript(javascript)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/escape_utils.gemspec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{escape_utils}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Brian Lopez"]
|
@@ -22,8 +22,9 @@ Gem::Specification.new do |s|
|
|
22
22
|
"README.rdoc",
|
23
23
|
"Rakefile",
|
24
24
|
"VERSION",
|
25
|
-
"benchmark/
|
26
|
-
"benchmark/
|
25
|
+
"benchmark/html_escape.rb",
|
26
|
+
"benchmark/html_unescape.rb",
|
27
|
+
"benchmark/javascript_escape.rb",
|
27
28
|
"escape_utils.gemspec",
|
28
29
|
"ext/escape_utils.c",
|
29
30
|
"ext/extconf.rb",
|
@@ -34,6 +35,7 @@ Gem::Specification.new do |s|
|
|
34
35
|
"lib/escape_utils/rack.rb",
|
35
36
|
"spec/html/escape_spec.rb",
|
36
37
|
"spec/html/unescape_spec.rb",
|
38
|
+
"spec/javascript/escape_spec.rb",
|
37
39
|
"spec/rcov.opts",
|
38
40
|
"spec/spec.opts",
|
39
41
|
"spec/spec_helper.rb"
|
@@ -46,6 +48,7 @@ Gem::Specification.new do |s|
|
|
46
48
|
s.test_files = [
|
47
49
|
"spec/html/escape_spec.rb",
|
48
50
|
"spec/html/unescape_spec.rb",
|
51
|
+
"spec/javascript/escape_spec.rb",
|
49
52
|
"spec/spec_helper.rb"
|
50
53
|
]
|
51
54
|
|
data/ext/escape_utils.c
CHANGED
@@ -72,7 +72,39 @@ static size_t unescape_html(unsigned char *out, const unsigned char *in, size_t
|
|
72
72
|
return total + (i-offset);
|
73
73
|
}
|
74
74
|
|
75
|
+
static size_t escape_javascript(unsigned char *out, const unsigned char *in, size_t in_len) {
|
76
|
+
size_t i = 0, offset = 0, total = 0;
|
77
|
+
|
78
|
+
for(;i<in_len;i++) {
|
79
|
+
switch(in[i]) {
|
80
|
+
case '\\': APPEND_BUFFER("\\\\", 2, 1);
|
81
|
+
case '<':
|
82
|
+
if (i+1 <= in_len && in[i+1] == '/') {
|
83
|
+
APPEND_BUFFER("<\\/", 3, 2);
|
84
|
+
}
|
85
|
+
break;
|
86
|
+
case '\r':
|
87
|
+
if (i+1 <= in_len && in[i+1] == '\n') {
|
88
|
+
APPEND_BUFFER("\\n", 2, 1);
|
89
|
+
} else {
|
90
|
+
APPEND_BUFFER("\\n", 2, 1);
|
91
|
+
}
|
92
|
+
break;
|
93
|
+
case '\n': APPEND_BUFFER("\\n", 2, 1);
|
94
|
+
case '\"': APPEND_BUFFER("\\\"", 2, 1);
|
95
|
+
case '\'': APPEND_BUFFER("\\'", 2, 1);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
// append the rest of the buffer
|
100
|
+
memcpy(&out[total], &in[offset], i-offset);
|
101
|
+
|
102
|
+
return total + (i-offset);
|
103
|
+
}
|
104
|
+
|
75
105
|
static VALUE rb_escape_html(VALUE self, VALUE str) {
|
106
|
+
Check_Type(str, T_STRING);
|
107
|
+
|
76
108
|
VALUE rb_output_buf;
|
77
109
|
unsigned char *inBuf = (unsigned char*)RSTRING_PTR(str);
|
78
110
|
size_t len = RSTRING_LEN(str), new_len = 0;
|
@@ -94,6 +126,8 @@ static VALUE rb_escape_html(VALUE self, VALUE str) {
|
|
94
126
|
}
|
95
127
|
|
96
128
|
static VALUE rb_unescape_html(VALUE self, VALUE str) {
|
129
|
+
Check_Type(str, T_STRING);
|
130
|
+
|
97
131
|
VALUE rb_output_buf;
|
98
132
|
unsigned char *inBuf = (unsigned char*)RSTRING_PTR(str);
|
99
133
|
size_t len = RSTRING_LEN(str), new_len = 0;
|
@@ -114,6 +148,33 @@ static VALUE rb_unescape_html(VALUE self, VALUE str) {
|
|
114
148
|
return rb_output_buf;
|
115
149
|
}
|
116
150
|
|
151
|
+
static VALUE rb_escape_javascript(VALUE self, VALUE str) {
|
152
|
+
if (str == Qnil) {
|
153
|
+
return rb_str_new2("");
|
154
|
+
}
|
155
|
+
|
156
|
+
Check_Type(str, T_STRING);
|
157
|
+
|
158
|
+
VALUE rb_output_buf;
|
159
|
+
unsigned char *inBuf = (unsigned char*)RSTRING_PTR(str);
|
160
|
+
size_t len = RSTRING_LEN(str), new_len = 0;
|
161
|
+
|
162
|
+
// this is the max size the string could be
|
163
|
+
// TODO: we should try to be more intelligent about this
|
164
|
+
unsigned char *outBuf = (unsigned char *)malloc(sizeof(unsigned char *)*(len*2));
|
165
|
+
|
166
|
+
// perform our escape, returning the new string's length
|
167
|
+
new_len = escape_javascript(outBuf, inBuf, len);
|
168
|
+
|
169
|
+
// create our new ruby string
|
170
|
+
rb_output_buf = rb_str_new((char *)outBuf, new_len);
|
171
|
+
|
172
|
+
// free the temporary C string
|
173
|
+
free(outBuf);
|
174
|
+
|
175
|
+
return rb_output_buf;
|
176
|
+
}
|
177
|
+
|
117
178
|
/* Ruby Extension initializer */
|
118
179
|
void Init_escape_utils_ext() {
|
119
180
|
VALUE mEscape = rb_define_module("EscapeUtils");
|
@@ -121,4 +182,6 @@ void Init_escape_utils_ext() {
|
|
121
182
|
rb_define_module_function(mEscape, "escape_html", rb_escape_html, 1);
|
122
183
|
rb_define_method(mEscape, "unescape_html", rb_unescape_html, 1);
|
123
184
|
rb_define_module_function(mEscape, "unescape_html", rb_unescape_html, 1);
|
185
|
+
rb_define_method(mEscape, "escape_javascript", rb_escape_javascript, 1);
|
186
|
+
rb_define_module_function(mEscape, "escape_javascript", rb_escape_javascript, 1);
|
124
187
|
}
|
data/lib/escape_utils.rb
CHANGED
data/spec/html/escape_spec.rb
CHANGED
@@ -1,26 +1,24 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
|
3
3
|
|
4
|
-
describe EscapeUtils do
|
4
|
+
describe EscapeUtils, "escape_html" do
|
5
5
|
it "should respond to escape_html" do
|
6
6
|
EscapeUtils.should respond_to(:escape_html)
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
9
|
+
it "should escape a basic html tag" do
|
10
|
+
EscapeUtils.escape_html("<some_tag/>").should eql("<some_tag/>")
|
11
|
+
end
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
it "should escape double-quotes" do
|
14
|
+
EscapeUtils.escape_html("<some_tag some_attr=\"some value\"/>").should eql("<some_tag some_attr="some value"/>")
|
15
|
+
end
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
it "should escape single-quotes" do
|
18
|
+
EscapeUtils.escape_html("<some_tag some_attr='some value'/>").should eql("<some_tag some_attr='some value'/>")
|
19
|
+
end
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
end
|
21
|
+
it "should escape the & character" do
|
22
|
+
EscapeUtils.escape_html("<b>Bourbon & Branch</b>").should eql("<b>Bourbon & Branch</b>")
|
25
23
|
end
|
26
24
|
end
|
data/spec/html/unescape_spec.rb
CHANGED
@@ -1,26 +1,24 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
|
3
3
|
|
4
|
-
describe EscapeUtils do
|
4
|
+
describe EscapeUtils, "unescape_html" do
|
5
5
|
it "should respond to unescape_html" do
|
6
6
|
EscapeUtils.should respond_to(:unescape_html)
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
9
|
+
it "should unescape a basic html tag" do
|
10
|
+
EscapeUtils.unescape_html("<some_tag/>").should eql("<some_tag/>")
|
11
|
+
end
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
it "should unescape double-quotes" do
|
14
|
+
EscapeUtils.unescape_html("<some_tag some_attr="some value"/>").should eql("<some_tag some_attr=\"some value\"/>")
|
15
|
+
end
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
it "should unescape single-quotes" do
|
18
|
+
EscapeUtils.unescape_html("<some_tag some_attr='some value'/>").should eql("<some_tag some_attr='some value'/>")
|
19
|
+
end
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
end
|
21
|
+
it "should unescape the & character" do
|
22
|
+
EscapeUtils.unescape_html("<b>Bourbon & Branch</b>").should eql("<b>Bourbon & Branch</b>")
|
25
23
|
end
|
26
24
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
|
3
|
+
|
4
|
+
describe EscapeUtils, "escape_javascript" do
|
5
|
+
it "should respond to escape_javascript" do
|
6
|
+
EscapeUtils.should respond_to(:escape_javascript)
|
7
|
+
end
|
8
|
+
|
9
|
+
# these are from the ActionView tests
|
10
|
+
it "should return an empty string if passed nil" do
|
11
|
+
EscapeUtils.escape_javascript(nil).should eql("")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should escape quotes and newlines" do
|
15
|
+
EscapeUtils.escape_javascript(%(This "thing" is really\n netos')).should eql(%(This \\"thing\\" is really\\n netos\\'))
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should escape backslashes" do
|
19
|
+
EscapeUtils.escape_javascript(%(backslash\\test)).should eql(%(backslash\\\\test))
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should escape closed html tags" do
|
23
|
+
EscapeUtils.escape_javascript(%(dont </close> tags)).should eql(%(dont <\\/close> tags))
|
24
|
+
end
|
25
|
+
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 1
|
9
|
+
version: 0.1.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Brian Lopez
|
@@ -33,8 +33,9 @@ files:
|
|
33
33
|
- README.rdoc
|
34
34
|
- Rakefile
|
35
35
|
- VERSION
|
36
|
-
- benchmark/
|
37
|
-
- benchmark/
|
36
|
+
- benchmark/html_escape.rb
|
37
|
+
- benchmark/html_unescape.rb
|
38
|
+
- benchmark/javascript_escape.rb
|
38
39
|
- escape_utils.gemspec
|
39
40
|
- ext/escape_utils.c
|
40
41
|
- ext/extconf.rb
|
@@ -45,6 +46,7 @@ files:
|
|
45
46
|
- lib/escape_utils/rack.rb
|
46
47
|
- spec/html/escape_spec.rb
|
47
48
|
- spec/html/unescape_spec.rb
|
49
|
+
- spec/javascript/escape_spec.rb
|
48
50
|
- spec/rcov.opts
|
49
51
|
- spec/spec.opts
|
50
52
|
- spec/spec_helper.rb
|
@@ -82,4 +84,5 @@ summary: Faster string escaping routines for your web apps
|
|
82
84
|
test_files:
|
83
85
|
- spec/html/escape_spec.rb
|
84
86
|
- spec/html/unescape_spec.rb
|
87
|
+
- spec/javascript/escape_spec.rb
|
85
88
|
- spec/spec_helper.rb
|