jruby-http-kit 0.0.1
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 +7 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +72 -0
- data/Rakefile +1 -0
- data/example/config.ru +9 -0
- data/example/hello_world.rb +17 -0
- data/example/rack.rb +11 -0
- data/example/ring.rb +31 -0
- data/example/sinatra/app.rb +9 -0
- data/example/sinatra/config.ru +8 -0
- data/example/webmachine/Gemfile +4 -0
- data/example/webmachine/Gemfile.lock +16 -0
- data/example/webmachine/app.rb +37 -0
- data/jruby-http-kit.gemspec +25 -0
- data/lib/http_kit/rack_handler.rb +139 -0
- data/lib/http_kit/server.rb +62 -0
- data/lib/http_kit/version.rb +3 -0
- data/lib/http_kit.rb +6 -0
- data/lib/java/clojure-1.5.1.jar +0 -0
- data/lib/java/http-kit.jar +0 -0
- data/lib/rack/handler/http_kit.rb +3 -0
- data/lib/rack/http_kit.rb +3 -0
- data/lib/webmachine/adapters/ring.rb +135 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/support/test_resource.rb +75 -0
- data/spec/webmachine/ring_adapter_spec.rb +127 -0
- data/src/.classpath +7 -0
- data/src/.project +17 -0
- data/src/org/httpkit/BytesInputStream.class +0 -0
- data/src/org/httpkit/BytesInputStream.java +83 -0
- data/src/org/httpkit/DateFormatter.class +0 -0
- data/src/org/httpkit/DynamicBytes.class +0 -0
- data/src/org/httpkit/DynamicBytes.java +76 -0
- data/src/org/httpkit/HTTPException.class +0 -0
- data/src/org/httpkit/HTTPException.java +10 -0
- data/src/org/httpkit/HeaderMap.class +0 -0
- data/src/org/httpkit/HeaderMap.java +98 -0
- data/src/org/httpkit/HttpMethod.class +0 -0
- data/src/org/httpkit/HttpMethod.java +28 -0
- data/src/org/httpkit/HttpStatus.class +0 -0
- data/src/org/httpkit/HttpStatus.java +416 -0
- data/src/org/httpkit/HttpUtils.class +0 -0
- data/src/org/httpkit/HttpUtils.java +484 -0
- data/src/org/httpkit/HttpVersion.class +0 -0
- data/src/org/httpkit/HttpVersion.java +5 -0
- data/src/org/httpkit/LineReader.class +0 -0
- data/src/org/httpkit/LineReader.java +52 -0
- data/src/org/httpkit/LineTooLargeException.class +0 -0
- data/src/org/httpkit/LineTooLargeException.java +13 -0
- data/src/org/httpkit/PrefixThreadFactory.class +0 -0
- data/src/org/httpkit/PrefixThreadFactory.java +20 -0
- data/src/org/httpkit/PriorityQueue.class +0 -0
- data/src/org/httpkit/PriorityQueue.java +235 -0
- data/src/org/httpkit/ProtocolException.class +0 -0
- data/src/org/httpkit/ProtocolException.java +10 -0
- data/src/org/httpkit/RequestTooLargeException.class +0 -0
- data/src/org/httpkit/RequestTooLargeException.java +10 -0
- data/src/org/httpkit/client/AbortException.class +0 -0
- data/src/org/httpkit/client/AbortException.java +13 -0
- data/src/org/httpkit/client/Decoder.class +0 -0
- data/src/org/httpkit/client/Decoder.java +182 -0
- data/src/org/httpkit/client/Handler.class +0 -0
- data/src/org/httpkit/client/HttpClient.class +0 -0
- data/src/org/httpkit/client/HttpClient.java +393 -0
- data/src/org/httpkit/client/HttpsRequest.class +0 -0
- data/src/org/httpkit/client/HttpsRequest.java +141 -0
- data/src/org/httpkit/client/IFilter$1.class +0 -0
- data/src/org/httpkit/client/IFilter$MaxBodyFilter.class +0 -0
- data/src/org/httpkit/client/IFilter.class +0 -0
- data/src/org/httpkit/client/IFilter.java +53 -0
- data/src/org/httpkit/client/IRespListener.class +0 -0
- data/src/org/httpkit/client/IRespListener.java +30 -0
- data/src/org/httpkit/client/IResponseHandler.class +0 -0
- data/src/org/httpkit/client/IResponseHandler.java +18 -0
- data/src/org/httpkit/client/PersistentConn.class +0 -0
- data/src/org/httpkit/client/PersistentConn.java +33 -0
- data/src/org/httpkit/client/Request.class +0 -0
- data/src/org/httpkit/client/Request.java +74 -0
- data/src/org/httpkit/client/RequestConfig.class +0 -0
- data/src/org/httpkit/client/RequestConfig.java +28 -0
- data/src/org/httpkit/client/RespListener.class +0 -0
- data/src/org/httpkit/client/RespListener.java +160 -0
- data/src/org/httpkit/client/SslContextFactory.class +0 -0
- data/src/org/httpkit/client/SslContextFactory.java +79 -0
- data/src/org/httpkit/client/State.class +0 -0
- data/src/org/httpkit/client/TimeoutException.class +0 -0
- data/src/org/httpkit/client/TimeoutException.java +12 -0
- data/src/org/httpkit/client/TrustManagerFactory$1.class +0 -0
- data/src/org/httpkit/client/TrustManagerFactory.class +0 -0
- data/src/org/httpkit/server/AsyncChannel.class +0 -0
- data/src/org/httpkit/server/AsyncChannel.java +286 -0
- data/src/org/httpkit/server/ClojureRing.class +0 -0
- data/src/org/httpkit/server/Frame$BinaryFrame.class +0 -0
- data/src/org/httpkit/server/Frame$CloseFrame.class +0 -0
- data/src/org/httpkit/server/Frame$PingFrame.class +0 -0
- data/src/org/httpkit/server/Frame$TextFrame.class +0 -0
- data/src/org/httpkit/server/Frame.class +0 -0
- data/src/org/httpkit/server/Frame.java +73 -0
- data/src/org/httpkit/server/HttpAtta.class +0 -0
- data/src/org/httpkit/server/HttpAtta.java +10 -0
- data/src/org/httpkit/server/HttpDecoder$State.class +0 -0
- data/src/org/httpkit/server/HttpDecoder.class +0 -0
- data/src/org/httpkit/server/HttpDecoder.java +202 -0
- data/src/org/httpkit/server/HttpHandler.class +0 -0
- data/src/org/httpkit/server/HttpRequest.class +0 -0
- data/src/org/httpkit/server/HttpRequest.java +109 -0
- data/src/org/httpkit/server/HttpServer.class +0 -0
- data/src/org/httpkit/server/HttpServer.java +281 -0
- data/src/org/httpkit/server/IHandler.class +0 -0
- data/src/org/httpkit/server/IHandler.java +12 -0
- data/src/org/httpkit/server/LinkingRunnable.class +0 -0
- data/src/org/httpkit/server/Rack.class +0 -0
- data/src/org/httpkit/server/RackApplication.class +0 -0
- data/src/org/httpkit/server/RackApplication.java +10 -0
- data/src/org/httpkit/server/RackHandler$1.class +0 -0
- data/src/org/httpkit/server/RackHandler.class +0 -0
- data/src/org/httpkit/server/RackHandler.java +259 -0
- data/src/org/httpkit/server/RackHttpHandler.class +0 -0
- data/src/org/httpkit/server/RackHttpHandler.java +20 -0
- data/src/org/httpkit/server/RespCallback.class +0 -0
- data/src/org/httpkit/server/RespCallback.java +19 -0
- data/src/org/httpkit/server/RingHandler$1.class +0 -0
- data/src/org/httpkit/server/RingHandler.class +0 -0
- data/src/org/httpkit/server/RingHandler.java +222 -0
- data/src/org/httpkit/server/ServerAtta.class +0 -0
- data/src/org/httpkit/server/ServerAtta.java +22 -0
- data/src/org/httpkit/server/WSDecoder$State.class +0 -0
- data/src/org/httpkit/server/WSDecoder.class +0 -0
- data/src/org/httpkit/server/WSDecoder.java +181 -0
- data/src/org/httpkit/server/WSHandler.class +0 -0
- data/src/org/httpkit/server/WsAtta.class +0 -0
- data/src/org/httpkit/server/WsAtta.java +11 -0
- data/src/org/httpkit/timer/CancelableFutureTask.class +0 -0
- data/src/org/httpkit/timer/CancelableFutureTask.java +52 -0
- data/src/org/httpkit/timer/TimerService.class +0 -0
- data/src/org/httpkit/timer/TimerService.java +89 -0
- metadata +241 -0
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
|
4
|
+
# loaded once.
|
|
5
|
+
#
|
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
|
7
|
+
|
|
8
|
+
require 'rubygems'
|
|
9
|
+
require 'webmachine'
|
|
10
|
+
require 'http_kit'
|
|
11
|
+
require 'rspec'
|
|
12
|
+
require 'logger'
|
|
13
|
+
|
|
14
|
+
RSpec.configure do |config|
|
|
15
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
|
16
|
+
config.run_all_when_everything_filtered = true
|
|
17
|
+
config.filter_run :focus
|
|
18
|
+
|
|
19
|
+
# Run specs in random order to surface order dependencies. If you find an
|
|
20
|
+
# order dependency and want to debug it, you can fix the order by providing
|
|
21
|
+
# the seed, which is printed after each run.
|
|
22
|
+
# --seed 1234
|
|
23
|
+
config.order = 'random'
|
|
24
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
require "webmachine"
|
|
2
|
+
|
|
3
|
+
module Test
|
|
4
|
+
class Resource < ::Webmachine::Resource
|
|
5
|
+
def allowed_methods
|
|
6
|
+
["GET", "PUT", "POST"]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def content_types_accepted
|
|
10
|
+
[
|
|
11
|
+
["test/request.stringbody", :from_string],
|
|
12
|
+
["test/request.enumbody", :from_enum]
|
|
13
|
+
]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def content_types_provided
|
|
17
|
+
[
|
|
18
|
+
["test/response.stringbody", :to_string],
|
|
19
|
+
["test/response.enumbody", :to_enum],
|
|
20
|
+
["test/response.procbody", :to_proc],
|
|
21
|
+
["test/response.fiberbody", :to_fiber],
|
|
22
|
+
["test/response.iobody", :to_io],
|
|
23
|
+
["test/response.cookies", :to_cookies]
|
|
24
|
+
]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def from_string
|
|
28
|
+
response.body = "String: #{request.body.to_s}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def from_enum
|
|
32
|
+
response.body = "Enum: "
|
|
33
|
+
request.body.each do |part|
|
|
34
|
+
response.body += part
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Response intentionally left blank to test 204 support
|
|
39
|
+
def process_post
|
|
40
|
+
true
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def to_string
|
|
44
|
+
"String response body"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_enum
|
|
48
|
+
["Enumerable ", "response " "body"]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_proc
|
|
52
|
+
Proc.new { "Proc response body" }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def to_fiber
|
|
56
|
+
Fiber.new do
|
|
57
|
+
Fiber.yield "Fiber "
|
|
58
|
+
Fiber.yield "response "
|
|
59
|
+
"body"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def to_io
|
|
64
|
+
StringIO.new("IO response body")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def to_cookies
|
|
68
|
+
response.set_cookie("cookie", "monster")
|
|
69
|
+
response.set_cookie("rodeo", "clown")
|
|
70
|
+
# FIXME: Mongrel/WEBrick fail if this method returns nil
|
|
71
|
+
# Might be a net/http issue. Is this a bug?
|
|
72
|
+
request.cookies["echo"] || ""
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "webmachine/adapters/ring"
|
|
3
|
+
require "support/test_resource"
|
|
4
|
+
require "net/http"
|
|
5
|
+
|
|
6
|
+
describe "Webmachine::Adapters::Ring" do
|
|
7
|
+
attr_accessor :client
|
|
8
|
+
|
|
9
|
+
before(:all) do
|
|
10
|
+
configuration = Webmachine::Configuration.default
|
|
11
|
+
dispatcher = Webmachine::Dispatcher.new
|
|
12
|
+
dispatcher.add_route ["test"], Test::Resource
|
|
13
|
+
|
|
14
|
+
@adapter = Webmachine::Adapters::Ring.new(configuration, dispatcher)
|
|
15
|
+
@client = Net::HTTP.new(configuration.ip, configuration.port)
|
|
16
|
+
|
|
17
|
+
Thread.abort_on_exception = true
|
|
18
|
+
@server_thread = Thread.new { @adapter.run }
|
|
19
|
+
|
|
20
|
+
# Wait until the server is responsive
|
|
21
|
+
timeout(5) do
|
|
22
|
+
begin
|
|
23
|
+
client.start
|
|
24
|
+
rescue Errno::ECONNREFUSED
|
|
25
|
+
sleep(0.1)
|
|
26
|
+
retry
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
after(:all) do
|
|
32
|
+
@adapter.shutdown
|
|
33
|
+
@client.finish
|
|
34
|
+
@server_thread.join
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "provides a string-like request body" do
|
|
38
|
+
request = Net::HTTP::Put.new("/test")
|
|
39
|
+
request.body = "Hello, World!"
|
|
40
|
+
request["Content-Type"] = "test/request.stringbody"
|
|
41
|
+
response = client.request(request)
|
|
42
|
+
response["Content-Length"].should eq("21")
|
|
43
|
+
response.body.should eq("String: Hello, World!")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "provides an enumerable request body" do
|
|
47
|
+
request = Net::HTTP::Put.new("/test")
|
|
48
|
+
request.body = "Hello, World!"
|
|
49
|
+
request["Content-Type"] = "test/request.enumbody"
|
|
50
|
+
response = client.request(request)
|
|
51
|
+
response["Content-Length"].should eq("19")
|
|
52
|
+
response.body.should eq("Enum: Hello, World!")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "handles missing pages" do
|
|
56
|
+
request = Net::HTTP::Get.new("/missing")
|
|
57
|
+
response = client.request(request)
|
|
58
|
+
response.code.should eq("404")
|
|
59
|
+
response["Content-Type"].should eq("text/html")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "handles empty response bodies" do
|
|
63
|
+
request = Net::HTTP::Post.new("/test")
|
|
64
|
+
request.body = ""
|
|
65
|
+
response = client.request(request)
|
|
66
|
+
response.code.should eq("204")
|
|
67
|
+
# FIXME: Mongrel/WEBrick fail this test. Is there a bug?
|
|
68
|
+
#response["Content-Type"].should be_nil
|
|
69
|
+
response["Content-Length"].should be_nil
|
|
70
|
+
response.body.should be_nil
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "handles string response bodies" do
|
|
74
|
+
request = Net::HTTP::Get.new("/test")
|
|
75
|
+
request["Accept"] = "test/response.stringbody"
|
|
76
|
+
response = client.request(request)
|
|
77
|
+
response["Content-Length"].should eq("20")
|
|
78
|
+
response.body.should eq("String response body")
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "handles enumerable response bodies" do
|
|
82
|
+
request = Net::HTTP::Get.new("/test")
|
|
83
|
+
request["Accept"] = "test/response.enumbody"
|
|
84
|
+
response = client.request(request)
|
|
85
|
+
response["Transfer-Encoding"].should eq("chunked")
|
|
86
|
+
response.body.should eq("Enumerable response body")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "handles proc response bodies" do
|
|
90
|
+
request = Net::HTTP::Get.new("/test")
|
|
91
|
+
request["Accept"] = "test/response.procbody"
|
|
92
|
+
response = client.request(request)
|
|
93
|
+
response["Transfer-Encoding"].should eq("chunked")
|
|
94
|
+
response.body.should eq("Proc response body")
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "handles fiber response bodies" do
|
|
98
|
+
request = Net::HTTP::Get.new("/test")
|
|
99
|
+
request["Accept"] = "test/response.fiberbody"
|
|
100
|
+
response = client.request(request)
|
|
101
|
+
response["Transfer-Encoding"].should eq("chunked")
|
|
102
|
+
response.body.should eq("Fiber response body")
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "handles io response bodies" do
|
|
106
|
+
request = Net::HTTP::Get.new("/test")
|
|
107
|
+
request["Accept"] = "test/response.iobody"
|
|
108
|
+
response = client.request(request)
|
|
109
|
+
response["Content-Length"].should eq("16")
|
|
110
|
+
response.body.should eq("IO response body")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "handles request cookies" do
|
|
114
|
+
request = Net::HTTP::Get.new("/test")
|
|
115
|
+
request["Accept"] = "test/response.cookies"
|
|
116
|
+
request["Cookie"] = "echo=echocookie"
|
|
117
|
+
response = client.request(request)
|
|
118
|
+
response.body.should eq("echocookie")
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it "handles response cookies" do
|
|
122
|
+
request = Net::HTTP::Get.new("/test")
|
|
123
|
+
request["Accept"] = "test/response.cookies"
|
|
124
|
+
response = client.request(request)
|
|
125
|
+
response["Set-Cookie"].should eq("cookie=monster, rodeo=clown")
|
|
126
|
+
end
|
|
127
|
+
end
|
data/src/.classpath
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<classpath>
|
|
3
|
+
<classpathentry kind="src" path=""/>
|
|
4
|
+
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
|
5
|
+
<classpathentry kind="lib" path="/Users/drozhkov/Downloads/clojure-1.5.1/clojure-1.5.1.jar"/>
|
|
6
|
+
<classpathentry kind="output" path=""/>
|
|
7
|
+
</classpath>
|
data/src/.project
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<projectDescription>
|
|
3
|
+
<name>httpkit</name>
|
|
4
|
+
<comment></comment>
|
|
5
|
+
<projects>
|
|
6
|
+
</projects>
|
|
7
|
+
<buildSpec>
|
|
8
|
+
<buildCommand>
|
|
9
|
+
<name>org.eclipse.jdt.core.javabuilder</name>
|
|
10
|
+
<arguments>
|
|
11
|
+
</arguments>
|
|
12
|
+
</buildCommand>
|
|
13
|
+
</buildSpec>
|
|
14
|
+
<natures>
|
|
15
|
+
<nature>org.eclipse.jdt.core.javanature</nature>
|
|
16
|
+
</natures>
|
|
17
|
+
</projectDescription>
|
|
Binary file
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
package org.httpkit;
|
|
2
|
+
|
|
3
|
+
import java.io.IOException;
|
|
4
|
+
import java.io.InputStream;
|
|
5
|
+
import java.util.Arrays;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* No synchronization, better toString
|
|
9
|
+
*/
|
|
10
|
+
public class BytesInputStream extends InputStream {
|
|
11
|
+
private final byte[] buf;
|
|
12
|
+
private final int count;
|
|
13
|
+
private int mark = 0;
|
|
14
|
+
|
|
15
|
+
private int pos;
|
|
16
|
+
|
|
17
|
+
public BytesInputStream(byte[] data, int length) {
|
|
18
|
+
this.buf = data;
|
|
19
|
+
this.count = length;
|
|
20
|
+
this.pos = 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* get the underlying bytes, copied
|
|
25
|
+
*
|
|
26
|
+
* @return
|
|
27
|
+
*/
|
|
28
|
+
public byte[] bytes() {
|
|
29
|
+
return Arrays.copyOf(buf, count);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public int read() throws IOException {
|
|
33
|
+
return (pos < count) ? (buf[pos++] & 0xff) : -1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public int read(byte[] b) throws IOException {
|
|
37
|
+
return read(b, 0, b.length);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public int read(byte[] b, int off, int len) throws IOException {
|
|
41
|
+
if (pos >= count) {
|
|
42
|
+
return -1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
int avail = count - pos;
|
|
46
|
+
if (len > avail) {
|
|
47
|
+
len = avail;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
System.arraycopy(buf, pos, b, off, len);
|
|
51
|
+
pos += len;
|
|
52
|
+
return len;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public String toString() {
|
|
56
|
+
return "BytesInputStream[len=" + count + "]";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public long skip(long n) throws IOException {
|
|
60
|
+
long k = count - pos;
|
|
61
|
+
if (n < k) {
|
|
62
|
+
k = n < 0 ? 0 : n;
|
|
63
|
+
}
|
|
64
|
+
pos += k;
|
|
65
|
+
return k;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public int available() {
|
|
69
|
+
return count - pos;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public boolean markSupported() {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public void mark(int readAheadLimit) {
|
|
77
|
+
mark = pos;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public void reset() {
|
|
81
|
+
pos = mark;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
package org.httpkit;
|
|
2
|
+
|
|
3
|
+
import java.nio.charset.Charset;
|
|
4
|
+
import java.util.Arrays;
|
|
5
|
+
|
|
6
|
+
public class DynamicBytes {
|
|
7
|
+
private byte[] data;
|
|
8
|
+
private int idx = 0;
|
|
9
|
+
|
|
10
|
+
public DynamicBytes(int size) {
|
|
11
|
+
data = new byte[size];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
private void expandIfNeeded(int more) {
|
|
15
|
+
if (idx + more > data.length) {
|
|
16
|
+
int after = (int) ((idx + more) * 1.33);
|
|
17
|
+
// String msg = "expand memory, from " + data.length + " to " +
|
|
18
|
+
// after + "; need " + more;
|
|
19
|
+
// System.out.println(msg);
|
|
20
|
+
data = Arrays.copyOf(data, after);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public byte[] get() {
|
|
25
|
+
return data;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public int length() {
|
|
29
|
+
return idx;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public DynamicBytes append(byte b) {
|
|
33
|
+
expandIfNeeded(1);
|
|
34
|
+
data[idx++] = b;
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public void append(byte b1, byte b2) {
|
|
39
|
+
expandIfNeeded(2);
|
|
40
|
+
data[idx++] = b1;
|
|
41
|
+
data[idx++] = b2;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@Override
|
|
45
|
+
public String toString() {
|
|
46
|
+
return "DynamicBytes[len=" + idx + ", cap=" + data.length + ']';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public DynamicBytes append(byte[] d, int length) {
|
|
50
|
+
expandIfNeeded(length);
|
|
51
|
+
System.arraycopy(d, 0, data, idx, length);
|
|
52
|
+
idx += length;
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public DynamicBytes append(String str) {
|
|
57
|
+
// ISO-8859-1. much faster than String.getBytes("ISO-8859-1")
|
|
58
|
+
// less copy. 620ms vs 190ms
|
|
59
|
+
int length = str.length();
|
|
60
|
+
expandIfNeeded(length);
|
|
61
|
+
for (int i = 0; i < length; i++) {
|
|
62
|
+
char c = str.charAt(i);
|
|
63
|
+
if (c < 128) {
|
|
64
|
+
data[idx++] = (byte) c;
|
|
65
|
+
} else {
|
|
66
|
+
data[idx++] = (byte) '?'; // JDK uses ? to represent non ASCII
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public DynamicBytes append(String str, Charset c) {
|
|
73
|
+
byte[] bs = str.getBytes(c);
|
|
74
|
+
return append(bs, bs.length);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
package org.httpkit;
|
|
2
|
+
|
|
3
|
+
import clojure.lang.ISeq;
|
|
4
|
+
import clojure.lang.Seqable;
|
|
5
|
+
|
|
6
|
+
import java.util.Arrays;
|
|
7
|
+
import java.util.Map;
|
|
8
|
+
|
|
9
|
+
import static org.httpkit.HttpUtils.*;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Save a few bytes of memory => No Map.Entry, and a little faster
|
|
13
|
+
* Date: 4/23/13
|
|
14
|
+
*
|
|
15
|
+
* @author feng <shenedu@gmail.com>
|
|
16
|
+
* @since 2.0.2
|
|
17
|
+
*/
|
|
18
|
+
public class HeaderMap {
|
|
19
|
+
public boolean isEmpty() {
|
|
20
|
+
return size == 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public final int INIT_SIZE = 8;
|
|
24
|
+
|
|
25
|
+
private int size = 0;
|
|
26
|
+
private Object arrays[] = new Object[INIT_SIZE * 2];
|
|
27
|
+
|
|
28
|
+
public void put(String key, Object obj) {
|
|
29
|
+
final int total = size << 1;
|
|
30
|
+
if (total == arrays.length) {
|
|
31
|
+
arrays = Arrays.copyOf(arrays, arrays.length * 2);
|
|
32
|
+
}
|
|
33
|
+
arrays[total] = key;
|
|
34
|
+
arrays[total + 1] = obj;
|
|
35
|
+
size += 1;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public Object get(String key) {
|
|
39
|
+
final int total = size << 1; // * 2
|
|
40
|
+
for (int i = 0; i < total; i += 2) {
|
|
41
|
+
if (key.equals(arrays[i])) {
|
|
42
|
+
return arrays[i + 1];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public boolean containsKey(String key) {
|
|
49
|
+
final int total = size << 1; // * 2
|
|
50
|
+
for (int i = 0; i < total; i += 2) {
|
|
51
|
+
if (key.equals(arrays[i])) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public void clear() {
|
|
59
|
+
this.size = 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public static HeaderMap camelCase(Map<String, Object> map) {
|
|
63
|
+
HeaderMap tmp = new HeaderMap();
|
|
64
|
+
if (map != null) {
|
|
65
|
+
for (Map.Entry<String, Object> e : map.entrySet()) {
|
|
66
|
+
tmp.put(HttpUtils.camelCase(e.getKey()), e.getValue());
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return tmp;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public void encodeHeaders(DynamicBytes bytes) {
|
|
73
|
+
final int total = size << 1;
|
|
74
|
+
for (int i = 0; i < total; i += 2) {
|
|
75
|
+
String k = (String) arrays[i];
|
|
76
|
+
Object v = arrays[i + 1];
|
|
77
|
+
if (v instanceof String) {
|
|
78
|
+
bytes.append(k);
|
|
79
|
+
bytes.append(COLON, SP);
|
|
80
|
+
// supposed to be ISO-8859-1, but utf-8 is compatible.
|
|
81
|
+
// filename in Content-Disposition can be utf8
|
|
82
|
+
bytes.append((String) v, HttpUtils.UTF_8);
|
|
83
|
+
bytes.append(CR, LF);
|
|
84
|
+
// ring spec says it could be a seq
|
|
85
|
+
} else if (v instanceof Seqable) {
|
|
86
|
+
ISeq seq = ((Seqable) v).seq();
|
|
87
|
+
while (seq != null) {
|
|
88
|
+
bytes.append(k);
|
|
89
|
+
bytes.append(COLON, SP);
|
|
90
|
+
bytes.append(seq.first().toString(), HttpUtils.UTF_8);
|
|
91
|
+
bytes.append(CR, LF);
|
|
92
|
+
seq = seq.next();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
bytes.append(CR, LF);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
package org.httpkit;
|
|
2
|
+
|
|
3
|
+
import clojure.lang.Keyword;
|
|
4
|
+
|
|
5
|
+
import static clojure.lang.Keyword.intern;
|
|
6
|
+
|
|
7
|
+
public enum HttpMethod {
|
|
8
|
+
|
|
9
|
+
GET(intern("get")), HEAD(intern("head")), POST(intern("post")), PUT(intern("put")),
|
|
10
|
+
DELETE(intern("delete")), TRACE(intern("trace")), OPTIONS(intern("options")),
|
|
11
|
+
CONNECT(intern("connect")), PATCH(intern("patch"));
|
|
12
|
+
|
|
13
|
+
public final Keyword KEY;
|
|
14
|
+
|
|
15
|
+
private HttpMethod(Keyword key) {
|
|
16
|
+
this.KEY = key;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public static HttpMethod fromKeyword(Keyword k) {
|
|
20
|
+
HttpMethod[] values = HttpMethod.values();
|
|
21
|
+
for (HttpMethod m : values) {
|
|
22
|
+
if (m.KEY.equals(k)) {
|
|
23
|
+
return m;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
throw new RuntimeException("unsupported method " + k);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
Binary file
|