fishwife 1.7.1-java → 1.8.0-java
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/History.rdoc +8 -0
- data/Manifest.txt +3 -1
- data/README.rdoc +10 -2
- data/example/ssl.ru +10 -0
- data/lib/fishwife/base.rb +1 -1
- data/lib/fishwife/{fishwife-1.7.1.jar → fishwife-1.8.0.jar} +0 -0
- data/lib/fishwife/http_server.rb +31 -14
- data/pom.xml +1 -1
- data/spec/data/localhost.keystore +0 -0
- data/spec/fishwife_spec.rb +195 -170
- data/spec/spec_helper.rb +1 -0
- metadata +9 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dde09a0a87fbd2aa589d44a02616cb08993cca24
|
|
4
|
+
data.tar.gz: b88680fc0b5dfa5ab36e111310fec7a087ead6c1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1bdd3ba4110e324a8a5b678102c25edab31872633f29ffab82fc523f1f964eb3c9d3d750ed09fda8832edbcb3f6aa82018a7485824c22f4420b9bff55307fb68
|
|
7
|
+
data.tar.gz: df9ce253c9096bf6db08b5f058a1c377b408dd4f8b781a42d73ac7014b15f4a8eec920d71ee480ff3b66b7753cbb04d88634a29086ef5aef3db8d31a8cc89266
|
data/History.rdoc
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
=== 1.8.0 (2015-5-3)
|
|
2
|
+
* Added Fishwife::HttpServer::new :connections configuration, allowing
|
|
3
|
+
multiple connectors and including SSL support. (#10 with Gregory
|
|
4
|
+
Huczynski)
|
|
5
|
+
* Upgrade/broaden to rjack-jetty [9.2.11, 9.4), for SSL/connections
|
|
6
|
+
support. Tested with 9.3.0.0 (M2 prerelease). Note that 9.2.11 is
|
|
7
|
+
awaiting upstream release and backport of the same features.
|
|
8
|
+
|
|
1
9
|
=== 1.7.1 (2015-3-30)
|
|
2
10
|
* Add missing StringIO require from 1.7.0 rewind feature
|
|
3
11
|
changes (Theo Hultberg, #8)
|
data/Manifest.txt
CHANGED
|
@@ -7,6 +7,7 @@ pom.xml
|
|
|
7
7
|
bin/fishwife
|
|
8
8
|
example/config.ru
|
|
9
9
|
example/giant.ru
|
|
10
|
+
example/ssl.ru
|
|
10
11
|
lib/fishwife/base.rb
|
|
11
12
|
lib/fishwife.rb
|
|
12
13
|
lib/fishwife/http_server.rb
|
|
@@ -16,5 +17,6 @@ spec/fishwife_spec.rb
|
|
|
16
17
|
spec/spec_helper.rb
|
|
17
18
|
spec/test_app.rb
|
|
18
19
|
spec/data/hello.txt
|
|
20
|
+
spec/data/localhost.keystore
|
|
19
21
|
spec/data/reddit-icon.png
|
|
20
|
-
lib/fishwife/fishwife-1.
|
|
22
|
+
lib/fishwife/fishwife-1.8.0.jar
|
data/README.rdoc
CHANGED
|
@@ -40,15 +40,21 @@ unique features:
|
|
|
40
40
|
To use Fishwife with your Rack app and config.ru:
|
|
41
41
|
|
|
42
42
|
gem install fishwife
|
|
43
|
-
|
|
44
43
|
cd /path/to/my/rack/app
|
|
44
|
+
|
|
45
|
+
Then:
|
|
46
|
+
|
|
45
47
|
fishwife
|
|
46
48
|
|
|
47
49
|
-or-
|
|
48
50
|
|
|
49
51
|
rackup -s Fishwife
|
|
50
52
|
|
|
51
|
-
See
|
|
53
|
+
See also:
|
|
54
|
+
|
|
55
|
+
* Fishwife::HttpServer::new supported options
|
|
56
|
+
* example/config.ru
|
|
57
|
+
* Notes below on Logging and Process Monitoring
|
|
52
58
|
|
|
53
59
|
== Logging
|
|
54
60
|
|
|
@@ -82,6 +88,8 @@ using:
|
|
|
82
88
|
desired. For an example see:
|
|
83
89
|
{boxed-geminabox}[https://github.com/dekellum/boxed-geminabox#readme].
|
|
84
90
|
|
|
91
|
+
* Linux systemd
|
|
92
|
+
|
|
85
93
|
All options also support redirecting log output (including any
|
|
86
94
|
JVM-level errors) to a file. Iyyov supports log rotation internally.
|
|
87
95
|
Unix-like logrotate(8) may be used with the former process monitors.
|
data/example/ssl.ru
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# -*- ruby -*-
|
|
2
|
+
#\ -s Fishwife -O connections=tcp://127.0.0.1:9292|ssl://0.0.0.0:9433?key_store_path=spec/data/localhost.keystore&key_store_password=password
|
|
3
|
+
|
|
4
|
+
# We don't assume SLF4J log output handler when launching via
|
|
5
|
+
# 'rackup', so you should do something like this here.
|
|
6
|
+
# If launching via bin/fishwife, this isn't required.
|
|
7
|
+
require 'rjack-logback'
|
|
8
|
+
RJack::Logback.config_console( :stderr => true, :thread => true )
|
|
9
|
+
|
|
10
|
+
run proc { [ 200, {"Content-Type" => "text/plain"}, ["Hello", " world!\n"] ] }
|
data/lib/fishwife/base.rb
CHANGED
|
Binary file
|
data/lib/fishwife/http_server.rb
CHANGED
|
@@ -17,17 +17,20 @@
|
|
|
17
17
|
module Fishwife
|
|
18
18
|
class HttpServer < RJack::Jetty::ServerFactory
|
|
19
19
|
|
|
20
|
-
attr_accessor :host
|
|
21
|
-
|
|
22
20
|
# Create the server with specified options:
|
|
23
21
|
#
|
|
24
22
|
# :host::
|
|
25
|
-
#
|
|
23
|
+
# The interface to bind to (default: 0.0.0.0 -> all)
|
|
26
24
|
#
|
|
27
25
|
# :port::
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
# from start.)
|
|
26
|
+
# The local port to bind to, for the first connection. Jetty
|
|
27
|
+
# picks if given port 0, and first connection port can be read
|
|
28
|
+
# on return from start. (default: 9292)
|
|
29
|
+
#
|
|
30
|
+
# :connections::
|
|
31
|
+
# An array, or a string that will be split on '|', where each
|
|
32
|
+
# element is a connection URI String or a hash of
|
|
33
|
+
# parameters. See details below.
|
|
31
34
|
#
|
|
32
35
|
# :min_threads::
|
|
33
36
|
# Minimum number of threads to keep in pool (default: 5)
|
|
@@ -54,11 +57,29 @@ module Fishwife
|
|
|
54
57
|
# request will be rejected with status 413. This limit is
|
|
55
58
|
# provided to avoid pathologic resource exhaustion. (default: 8 MiB)
|
|
56
59
|
#
|
|
60
|
+
# === Options in connections
|
|
61
|
+
#
|
|
62
|
+
# Each member of the connections array is either a hash with
|
|
63
|
+
# the following properties or an equivalent URI string:
|
|
64
|
+
#
|
|
65
|
+
# :scheme:: Values 'tcp' or 'ssl'
|
|
66
|
+
# :host:: The local interface to bind
|
|
67
|
+
# (default: top level #host or 0.0.0.0)
|
|
68
|
+
# :port:: Port number or 0 to select an available port
|
|
69
|
+
# (default: top level #port for first connection or 0)
|
|
70
|
+
# :max_idle_time_ms:: See above
|
|
71
|
+
# :key_store_path:: For ssl, the path to the (Java JKS) keystore
|
|
72
|
+
# :key_store_password:: For ssl, the password to the keystore
|
|
73
|
+
#
|
|
74
|
+
# URI examples:
|
|
75
|
+
#
|
|
76
|
+
# tcp://127.0.0.1
|
|
77
|
+
# ssl://0.0.0.0:8443?key_store_path=keystore&key_store_password=399as8d9
|
|
78
|
+
#
|
|
57
79
|
def initialize( options = {} )
|
|
58
80
|
super()
|
|
59
81
|
|
|
60
82
|
@server = nil
|
|
61
|
-
@host = nil
|
|
62
83
|
|
|
63
84
|
self.min_threads = 5
|
|
64
85
|
self.max_threads = 50
|
|
@@ -76,6 +97,9 @@ module Fishwife
|
|
|
76
97
|
v = options[ :request_log_file ]
|
|
77
98
|
options[ :request_log_file ] = v.to_sym if v == 'stderr'
|
|
78
99
|
|
|
100
|
+
v = options[ :connections ]
|
|
101
|
+
options[ :connections ] = v.split('|') if v.is_a?( String )
|
|
102
|
+
|
|
79
103
|
# Split out servlet options.
|
|
80
104
|
@servlet_options = {}
|
|
81
105
|
[ :request_body_ram, :request_body_tmpdir, :request_body_max ].each do |k|
|
|
@@ -111,12 +135,5 @@ module Fishwife
|
|
|
111
135
|
def stop
|
|
112
136
|
@server.stop if @server
|
|
113
137
|
end
|
|
114
|
-
|
|
115
|
-
def create_connectors( *args )
|
|
116
|
-
super.tap do |ctrs|
|
|
117
|
-
ctrs.first.host = @host if ctrs.first
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
|
|
121
138
|
end
|
|
122
139
|
end
|
data/pom.xml
CHANGED
|
Binary file
|
data/spec/fishwife_spec.rb
CHANGED
|
@@ -23,203 +23,228 @@ require 'base64'
|
|
|
23
23
|
require 'json'
|
|
24
24
|
|
|
25
25
|
describe Fishwife do
|
|
26
|
-
def get(path, headers = {})
|
|
27
|
-
Net::HTTP.start(@options[:host], @options[:port]) do |http|
|
|
28
|
-
request = Net::HTTP::Get.new(path, headers)
|
|
29
|
-
http.request(request)
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
request
|
|
27
|
+
[:http, :https].each do |scheme|
|
|
28
|
+
|
|
29
|
+
describe "for #{scheme.to_s} scheme" do
|
|
30
|
+
|
|
31
|
+
def get(path, headers = {})
|
|
32
|
+
Net::HTTP.start(@options[:host], @options[:port],
|
|
33
|
+
nil, nil, nil, nil, #Irrelevant http proxy parameters
|
|
34
|
+
@https_client_opts) do |http|
|
|
35
|
+
request = Net::HTTP::Get.new(path, headers)
|
|
36
|
+
http.request(request)
|
|
42
37
|
end
|
|
43
38
|
end
|
|
44
|
-
http.request(request)
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
39
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
40
|
+
def post(path, params = nil, headers = {}, body = nil)
|
|
41
|
+
Net::HTTP.start(@options[:host], @options[:port],
|
|
42
|
+
nil, nil, nil, nil, #Irrelevant http proxy parameters
|
|
43
|
+
@https_client_opts) do |http|
|
|
44
|
+
request = Net::HTTP::Post.new(path, headers)
|
|
45
|
+
request.form_data = params if params
|
|
46
|
+
if body
|
|
47
|
+
if body.respond_to?( :read )
|
|
48
|
+
request.body_stream = body
|
|
49
|
+
else
|
|
50
|
+
request.body = body
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
http.request(request)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
60
56
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
before(:all) do
|
|
58
|
+
@scheme = scheme
|
|
59
|
+
@options = { :host => '127.0.0.1',
|
|
60
|
+
:port => 9201,
|
|
61
|
+
:request_body_ram => 256,
|
|
62
|
+
:request_body_max => 96 * 1024,
|
|
63
|
+
:request_body_tmpdir => File.dirname( __FILE__ ) }
|
|
64
|
+
@https_client_opts = {}
|
|
65
|
+
if @scheme == :https
|
|
66
|
+
@https_client_opts = {
|
|
67
|
+
:use_ssl => true,
|
|
68
|
+
:verify_mode => OpenSSL::SSL::VERIFY_NONE }
|
|
69
|
+
@options[:connections] = [ {
|
|
70
|
+
:scheme => 'ssl',
|
|
71
|
+
:key_store_path => 'spec/data/localhost.keystore',
|
|
72
|
+
:key_store_password => 'password' } ]
|
|
73
|
+
end
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
75
|
+
@lock = Mutex.new
|
|
76
|
+
@app = Rack::Lint.new(TestApp.new)
|
|
77
|
+
Net::HTTP.version_1_2
|
|
78
|
+
@server = Fishwife::HttpServer.new(@options)
|
|
79
|
+
@server.start(@app)
|
|
80
|
+
end
|
|
70
81
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
82
|
+
after(:all) do
|
|
83
|
+
@server.stop
|
|
84
|
+
@server.join
|
|
85
|
+
end
|
|
75
86
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
87
|
+
it "returns 200 OK" do
|
|
88
|
+
response = get("/ping")
|
|
89
|
+
response.code.should == "200"
|
|
90
|
+
end
|
|
80
91
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
content["rack.multithread"].should be_true
|
|
86
|
-
content["rack.multiprocess"].should be_false
|
|
87
|
-
content["rack.run_once"].should be_false
|
|
88
|
-
end
|
|
92
|
+
it "returns 403 FORBIDDEN" do
|
|
93
|
+
response = get("/error/403")
|
|
94
|
+
response.code.should == "403"
|
|
95
|
+
end
|
|
89
96
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
content['request.params']['answer'].should == '42'
|
|
95
|
-
end
|
|
97
|
+
it "returns 404 NOT FOUND" do
|
|
98
|
+
response = get("/jimmy/hoffa")
|
|
99
|
+
response.code.should == "404"
|
|
100
|
+
end
|
|
96
101
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
it "sets Rack headers" do
|
|
103
|
+
response = get("/echo")
|
|
104
|
+
response.code.should == "200"
|
|
105
|
+
content = JSON.parse(response.body)
|
|
106
|
+
content["rack.multithread"].should be_true
|
|
107
|
+
content["rack.multiprocess"].should be_false
|
|
108
|
+
content["rack.run_once"].should be_false
|
|
109
|
+
end
|
|
104
110
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
end
|
|
111
|
+
it "passes form variables via GET" do
|
|
112
|
+
response = get("/echo?answer=42")
|
|
113
|
+
response.code.should == "200"
|
|
114
|
+
content = JSON.parse(response.body)
|
|
115
|
+
content['request.params']['answer'].should == '42'
|
|
116
|
+
end
|
|
112
117
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
it "Rejects request body larger than maximum" do
|
|
123
|
-
body = '<' + "f" * (100*1024) + '>'
|
|
124
|
-
headers = { "Content-Type" => "text/plain" }
|
|
125
|
-
response = post("/count", nil, headers, body)
|
|
126
|
-
response.code.should == "413"
|
|
127
|
-
end
|
|
118
|
+
it "passes form variables via POST" do
|
|
119
|
+
question = "What is the answer to life, the universe, and everything?"
|
|
120
|
+
response = post("/echo", 'question' => question)
|
|
121
|
+
response.code.should == "200"
|
|
122
|
+
content = JSON.parse(response.body)
|
|
123
|
+
content['request.params']['question'].should == question
|
|
124
|
+
end
|
|
128
125
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
126
|
+
it "Passes along larger non-form POST body" do
|
|
127
|
+
body = '<' + "f" * (93*1024) + '>'
|
|
128
|
+
headers = { "Content-Type" => "text/plain" }
|
|
129
|
+
response = post("/dcount", nil, headers, body)
|
|
130
|
+
response.code.should == "200"
|
|
131
|
+
response.body.to_i.should == body.size * 2
|
|
132
|
+
end
|
|
136
133
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
134
|
+
it "Passes along larger non-form POST body when chunked" do
|
|
135
|
+
body = '<' + "f" * (93*1024) + '>'
|
|
136
|
+
headers = { "Content-Type" => "text/plain",
|
|
137
|
+
"Transfer-Encoding" => "chunked" }
|
|
138
|
+
response = post("/dcount", nil, headers, StringIO.new( body ) )
|
|
139
|
+
response.code.should == "200"
|
|
140
|
+
response.body.to_i.should == body.size * 2
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it "Rejects request body larger than maximum" do
|
|
144
|
+
body = '<' + "f" * (100*1024) + '>'
|
|
145
|
+
headers = { "Content-Type" => "text/plain" }
|
|
146
|
+
response = post("/count", nil, headers, body)
|
|
147
|
+
response.code.should == "413"
|
|
148
|
+
end
|
|
143
149
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
150
|
+
it "Rejects request body larger than maximum in chunked request" do
|
|
151
|
+
body = '<' + "f" * (100*1024) + '>'
|
|
152
|
+
headers = { "Content-Type" => "text/plain",
|
|
153
|
+
"Transfer-Encoding" => "chunked" }
|
|
154
|
+
response = post("/count", nil, headers, StringIO.new( body ) )
|
|
155
|
+
response.code.should == "413"
|
|
156
|
+
end
|
|
149
157
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
158
|
+
it "passes custom headers" do
|
|
159
|
+
response = get("/echo", "X-My-Header" => "Pancakes")
|
|
160
|
+
response.code.should == "200"
|
|
161
|
+
content = JSON.parse(response.body)
|
|
162
|
+
content["HTTP_X_MY_HEADER"].should == "Pancakes"
|
|
163
|
+
end
|
|
156
164
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
165
|
+
it "returns multiple values of the same header" do
|
|
166
|
+
response = get("/multi_headers")
|
|
167
|
+
response['Warning'].should == "warn-1, warn-2"
|
|
168
|
+
# Net::HTTP handle multiple headers with join( ", " )
|
|
169
|
+
end
|
|
161
170
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
171
|
+
it "lets the Rack app know it's running as a servlet" do
|
|
172
|
+
response = get("/echo", 'answer' => '42')
|
|
173
|
+
response.code.should == "200"
|
|
174
|
+
content = JSON.parse(response.body)
|
|
175
|
+
content['rack.java.servlet'].should be_true
|
|
176
|
+
end
|
|
168
177
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
end
|
|
178
|
+
it "is clearly Jetty" do
|
|
179
|
+
response = get("/ping")
|
|
180
|
+
response['server'].should =~ /jetty/i
|
|
181
|
+
end
|
|
174
182
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
end
|
|
183
|
+
it "sets the server port and hostname" do
|
|
184
|
+
response = get("/echo")
|
|
185
|
+
content = JSON.parse(response.body)
|
|
186
|
+
content["SERVER_PORT"].should == "9201"
|
|
187
|
+
content["SERVER_NAME"].should == "127.0.0.1"
|
|
188
|
+
end
|
|
182
189
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
'filename="reddit-icon.png"'
|
|
189
|
-
content << 'Content-Type: image/png'
|
|
190
|
-
content << 'Content-Transfer-Encoding: base64'
|
|
191
|
-
content << ''
|
|
192
|
-
content <<
|
|
193
|
-
Base64.encode64( File.read('spec/data/reddit-icon.png') ).strip
|
|
194
|
-
content << "--#{boundary}--"
|
|
195
|
-
body = content.map { |l| l + "\r\n" }.join('')
|
|
196
|
-
headers = { "Content-Type" =>
|
|
197
|
-
"multipart/form-data; boundary=#{boundary}" }
|
|
198
|
-
response = post("/upload", nil, headers, body)
|
|
199
|
-
response.code.should == "200"
|
|
200
|
-
response.body.should == '8da4b60a9bbe205d4d3699985470627e'
|
|
201
|
-
end
|
|
190
|
+
it "passes the URI scheme" do
|
|
191
|
+
response = get("/echo")
|
|
192
|
+
content = JSON.parse(response.body)
|
|
193
|
+
content['rack.url_scheme'].should == @scheme.to_s
|
|
194
|
+
end
|
|
202
195
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
196
|
+
it "supports file downloads" do
|
|
197
|
+
response = get("/download")
|
|
198
|
+
response.code.should == "200"
|
|
199
|
+
response['Content-Type'].should == 'image/png'
|
|
200
|
+
checksum = Digest::MD5.hexdigest(response.body)
|
|
201
|
+
checksum.should == '8da4b60a9bbe205d4d3699985470627e'
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it "supports file uploads" do
|
|
205
|
+
boundary = '349832898984244898448024464570528145'
|
|
206
|
+
content = []
|
|
207
|
+
content << "--#{boundary}"
|
|
208
|
+
content << 'Content-Disposition: form-data; name="file"; ' +
|
|
209
|
+
'filename="reddit-icon.png"'
|
|
210
|
+
content << 'Content-Type: image/png'
|
|
211
|
+
content << 'Content-Transfer-Encoding: base64'
|
|
212
|
+
content << ''
|
|
213
|
+
content <<
|
|
214
|
+
Base64.encode64( File.read('spec/data/reddit-icon.png') ).strip
|
|
215
|
+
content << "--#{boundary}--"
|
|
216
|
+
body = content.map { |l| l + "\r\n" }.join('')
|
|
217
|
+
headers = { "Content-Type" =>
|
|
218
|
+
"multipart/form-data; boundary=#{boundary}" }
|
|
219
|
+
response = post("/upload", nil, headers, body)
|
|
220
|
+
response.code.should == "200"
|
|
221
|
+
response.body.should == '8da4b60a9bbe205d4d3699985470627e'
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
it "handles async requests" do
|
|
225
|
+
pending "Causes intermittent 30s pauses, TestApp.push/pull is sketchy"
|
|
226
|
+
lock = Mutex.new
|
|
227
|
+
buffer = Array.new
|
|
228
|
+
|
|
229
|
+
clients = 10.times.map do |index|
|
|
230
|
+
Thread.new do
|
|
231
|
+
Net::HTTP.start(@options[:host], @options[:port]) do |http|
|
|
232
|
+
response = http.get("/pull")
|
|
233
|
+
lock.synchronize {
|
|
234
|
+
buffer << "#{index}: #{response.body}" }
|
|
235
|
+
end
|
|
236
|
+
end
|
|
214
237
|
end
|
|
238
|
+
|
|
239
|
+
lock.synchronize { buffer.should be_empty }
|
|
240
|
+
post("/push", 'message' => "one")
|
|
241
|
+
clients.each { |c| c.join }
|
|
242
|
+
lock.synchronize { buffer.should_not be_empty }
|
|
243
|
+
lock.synchronize { buffer.count.should == 10 }
|
|
215
244
|
end
|
|
245
|
+
|
|
216
246
|
end
|
|
217
247
|
|
|
218
|
-
lock.synchronize { buffer.should be_empty }
|
|
219
|
-
post("/push", 'message' => "one")
|
|
220
|
-
clients.each { |c| c.join }
|
|
221
|
-
lock.synchronize { buffer.should_not be_empty }
|
|
222
|
-
lock.synchronize { buffer.count.should == 10 }
|
|
223
248
|
end
|
|
224
249
|
|
|
225
250
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fishwife
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.8.0
|
|
5
5
|
platform: java
|
|
6
6
|
authors:
|
|
7
7
|
- David Kellum
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2015-03
|
|
11
|
+
date: 2015-05-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -35,10 +35,10 @@ dependencies:
|
|
|
35
35
|
requirements:
|
|
36
36
|
- - '>='
|
|
37
37
|
- !ruby/object:Gem::Version
|
|
38
|
-
version: 9.
|
|
38
|
+
version: 9.2.11
|
|
39
39
|
- - <
|
|
40
40
|
- !ruby/object:Gem::Version
|
|
41
|
-
version: '9.
|
|
41
|
+
version: '9.4'
|
|
42
42
|
name: rjack-jetty
|
|
43
43
|
prerelease: false
|
|
44
44
|
type: :runtime
|
|
@@ -46,10 +46,10 @@ dependencies:
|
|
|
46
46
|
requirements:
|
|
47
47
|
- - '>='
|
|
48
48
|
- !ruby/object:Gem::Version
|
|
49
|
-
version: 9.
|
|
49
|
+
version: 9.2.11
|
|
50
50
|
- - <
|
|
51
51
|
- !ruby/object:Gem::Version
|
|
52
|
-
version: '9.
|
|
52
|
+
version: '9.4'
|
|
53
53
|
- !ruby/object:Gem::Dependency
|
|
54
54
|
requirement: !ruby/object:Gem::Requirement
|
|
55
55
|
requirements:
|
|
@@ -138,14 +138,16 @@ files:
|
|
|
138
138
|
- bin/fishwife
|
|
139
139
|
- example/config.ru
|
|
140
140
|
- example/giant.ru
|
|
141
|
+
- example/ssl.ru
|
|
141
142
|
- lib/fishwife.rb
|
|
142
143
|
- lib/fishwife/base.rb
|
|
143
|
-
- lib/fishwife/fishwife-1.
|
|
144
|
+
- lib/fishwife/fishwife-1.8.0.jar
|
|
144
145
|
- lib/fishwife/http_server.rb
|
|
145
146
|
- lib/fishwife/rack_servlet.rb
|
|
146
147
|
- lib/rack/handler/fishwife.rb
|
|
147
148
|
- pom.xml
|
|
148
149
|
- spec/data/hello.txt
|
|
150
|
+
- spec/data/localhost.keystore
|
|
149
151
|
- spec/data/reddit-icon.png
|
|
150
152
|
- spec/fishwife_spec.rb
|
|
151
153
|
- spec/spec_helper.rb
|