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.
Files changed (140) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +72 -0
  7. data/Rakefile +1 -0
  8. data/example/config.ru +9 -0
  9. data/example/hello_world.rb +17 -0
  10. data/example/rack.rb +11 -0
  11. data/example/ring.rb +31 -0
  12. data/example/sinatra/app.rb +9 -0
  13. data/example/sinatra/config.ru +8 -0
  14. data/example/webmachine/Gemfile +4 -0
  15. data/example/webmachine/Gemfile.lock +16 -0
  16. data/example/webmachine/app.rb +37 -0
  17. data/jruby-http-kit.gemspec +25 -0
  18. data/lib/http_kit/rack_handler.rb +139 -0
  19. data/lib/http_kit/server.rb +62 -0
  20. data/lib/http_kit/version.rb +3 -0
  21. data/lib/http_kit.rb +6 -0
  22. data/lib/java/clojure-1.5.1.jar +0 -0
  23. data/lib/java/http-kit.jar +0 -0
  24. data/lib/rack/handler/http_kit.rb +3 -0
  25. data/lib/rack/http_kit.rb +3 -0
  26. data/lib/webmachine/adapters/ring.rb +135 -0
  27. data/spec/spec_helper.rb +24 -0
  28. data/spec/support/test_resource.rb +75 -0
  29. data/spec/webmachine/ring_adapter_spec.rb +127 -0
  30. data/src/.classpath +7 -0
  31. data/src/.project +17 -0
  32. data/src/org/httpkit/BytesInputStream.class +0 -0
  33. data/src/org/httpkit/BytesInputStream.java +83 -0
  34. data/src/org/httpkit/DateFormatter.class +0 -0
  35. data/src/org/httpkit/DynamicBytes.class +0 -0
  36. data/src/org/httpkit/DynamicBytes.java +76 -0
  37. data/src/org/httpkit/HTTPException.class +0 -0
  38. data/src/org/httpkit/HTTPException.java +10 -0
  39. data/src/org/httpkit/HeaderMap.class +0 -0
  40. data/src/org/httpkit/HeaderMap.java +98 -0
  41. data/src/org/httpkit/HttpMethod.class +0 -0
  42. data/src/org/httpkit/HttpMethod.java +28 -0
  43. data/src/org/httpkit/HttpStatus.class +0 -0
  44. data/src/org/httpkit/HttpStatus.java +416 -0
  45. data/src/org/httpkit/HttpUtils.class +0 -0
  46. data/src/org/httpkit/HttpUtils.java +484 -0
  47. data/src/org/httpkit/HttpVersion.class +0 -0
  48. data/src/org/httpkit/HttpVersion.java +5 -0
  49. data/src/org/httpkit/LineReader.class +0 -0
  50. data/src/org/httpkit/LineReader.java +52 -0
  51. data/src/org/httpkit/LineTooLargeException.class +0 -0
  52. data/src/org/httpkit/LineTooLargeException.java +13 -0
  53. data/src/org/httpkit/PrefixThreadFactory.class +0 -0
  54. data/src/org/httpkit/PrefixThreadFactory.java +20 -0
  55. data/src/org/httpkit/PriorityQueue.class +0 -0
  56. data/src/org/httpkit/PriorityQueue.java +235 -0
  57. data/src/org/httpkit/ProtocolException.class +0 -0
  58. data/src/org/httpkit/ProtocolException.java +10 -0
  59. data/src/org/httpkit/RequestTooLargeException.class +0 -0
  60. data/src/org/httpkit/RequestTooLargeException.java +10 -0
  61. data/src/org/httpkit/client/AbortException.class +0 -0
  62. data/src/org/httpkit/client/AbortException.java +13 -0
  63. data/src/org/httpkit/client/Decoder.class +0 -0
  64. data/src/org/httpkit/client/Decoder.java +182 -0
  65. data/src/org/httpkit/client/Handler.class +0 -0
  66. data/src/org/httpkit/client/HttpClient.class +0 -0
  67. data/src/org/httpkit/client/HttpClient.java +393 -0
  68. data/src/org/httpkit/client/HttpsRequest.class +0 -0
  69. data/src/org/httpkit/client/HttpsRequest.java +141 -0
  70. data/src/org/httpkit/client/IFilter$1.class +0 -0
  71. data/src/org/httpkit/client/IFilter$MaxBodyFilter.class +0 -0
  72. data/src/org/httpkit/client/IFilter.class +0 -0
  73. data/src/org/httpkit/client/IFilter.java +53 -0
  74. data/src/org/httpkit/client/IRespListener.class +0 -0
  75. data/src/org/httpkit/client/IRespListener.java +30 -0
  76. data/src/org/httpkit/client/IResponseHandler.class +0 -0
  77. data/src/org/httpkit/client/IResponseHandler.java +18 -0
  78. data/src/org/httpkit/client/PersistentConn.class +0 -0
  79. data/src/org/httpkit/client/PersistentConn.java +33 -0
  80. data/src/org/httpkit/client/Request.class +0 -0
  81. data/src/org/httpkit/client/Request.java +74 -0
  82. data/src/org/httpkit/client/RequestConfig.class +0 -0
  83. data/src/org/httpkit/client/RequestConfig.java +28 -0
  84. data/src/org/httpkit/client/RespListener.class +0 -0
  85. data/src/org/httpkit/client/RespListener.java +160 -0
  86. data/src/org/httpkit/client/SslContextFactory.class +0 -0
  87. data/src/org/httpkit/client/SslContextFactory.java +79 -0
  88. data/src/org/httpkit/client/State.class +0 -0
  89. data/src/org/httpkit/client/TimeoutException.class +0 -0
  90. data/src/org/httpkit/client/TimeoutException.java +12 -0
  91. data/src/org/httpkit/client/TrustManagerFactory$1.class +0 -0
  92. data/src/org/httpkit/client/TrustManagerFactory.class +0 -0
  93. data/src/org/httpkit/server/AsyncChannel.class +0 -0
  94. data/src/org/httpkit/server/AsyncChannel.java +286 -0
  95. data/src/org/httpkit/server/ClojureRing.class +0 -0
  96. data/src/org/httpkit/server/Frame$BinaryFrame.class +0 -0
  97. data/src/org/httpkit/server/Frame$CloseFrame.class +0 -0
  98. data/src/org/httpkit/server/Frame$PingFrame.class +0 -0
  99. data/src/org/httpkit/server/Frame$TextFrame.class +0 -0
  100. data/src/org/httpkit/server/Frame.class +0 -0
  101. data/src/org/httpkit/server/Frame.java +73 -0
  102. data/src/org/httpkit/server/HttpAtta.class +0 -0
  103. data/src/org/httpkit/server/HttpAtta.java +10 -0
  104. data/src/org/httpkit/server/HttpDecoder$State.class +0 -0
  105. data/src/org/httpkit/server/HttpDecoder.class +0 -0
  106. data/src/org/httpkit/server/HttpDecoder.java +202 -0
  107. data/src/org/httpkit/server/HttpHandler.class +0 -0
  108. data/src/org/httpkit/server/HttpRequest.class +0 -0
  109. data/src/org/httpkit/server/HttpRequest.java +109 -0
  110. data/src/org/httpkit/server/HttpServer.class +0 -0
  111. data/src/org/httpkit/server/HttpServer.java +281 -0
  112. data/src/org/httpkit/server/IHandler.class +0 -0
  113. data/src/org/httpkit/server/IHandler.java +12 -0
  114. data/src/org/httpkit/server/LinkingRunnable.class +0 -0
  115. data/src/org/httpkit/server/Rack.class +0 -0
  116. data/src/org/httpkit/server/RackApplication.class +0 -0
  117. data/src/org/httpkit/server/RackApplication.java +10 -0
  118. data/src/org/httpkit/server/RackHandler$1.class +0 -0
  119. data/src/org/httpkit/server/RackHandler.class +0 -0
  120. data/src/org/httpkit/server/RackHandler.java +259 -0
  121. data/src/org/httpkit/server/RackHttpHandler.class +0 -0
  122. data/src/org/httpkit/server/RackHttpHandler.java +20 -0
  123. data/src/org/httpkit/server/RespCallback.class +0 -0
  124. data/src/org/httpkit/server/RespCallback.java +19 -0
  125. data/src/org/httpkit/server/RingHandler$1.class +0 -0
  126. data/src/org/httpkit/server/RingHandler.class +0 -0
  127. data/src/org/httpkit/server/RingHandler.java +222 -0
  128. data/src/org/httpkit/server/ServerAtta.class +0 -0
  129. data/src/org/httpkit/server/ServerAtta.java +22 -0
  130. data/src/org/httpkit/server/WSDecoder$State.class +0 -0
  131. data/src/org/httpkit/server/WSDecoder.class +0 -0
  132. data/src/org/httpkit/server/WSDecoder.java +181 -0
  133. data/src/org/httpkit/server/WSHandler.class +0 -0
  134. data/src/org/httpkit/server/WsAtta.class +0 -0
  135. data/src/org/httpkit/server/WsAtta.java +11 -0
  136. data/src/org/httpkit/timer/CancelableFutureTask.class +0 -0
  137. data/src/org/httpkit/timer/CancelableFutureTask.java +52 -0
  138. data/src/org/httpkit/timer/TimerService.class +0 -0
  139. data/src/org/httpkit/timer/TimerService.java +89 -0
  140. metadata +241 -0
@@ -0,0 +1,484 @@
1
+ package org.httpkit;
2
+
3
+ import clojure.lang.ISeq;
4
+ import clojure.lang.PersistentList;
5
+ import clojure.lang.Seqable;
6
+
7
+ import java.io.*;
8
+ import java.net.*;
9
+ import java.nio.ByteBuffer;
10
+ import java.nio.MappedByteBuffer;
11
+ import java.nio.channels.FileChannel.MapMode;
12
+ import java.nio.charset.Charset;
13
+ import java.text.SimpleDateFormat;
14
+ import java.util.*;
15
+ import java.util.regex.Matcher;
16
+ import java.util.regex.Pattern;
17
+
18
+ import static java.lang.Character.isWhitespace;
19
+ import static java.lang.Math.min;
20
+ import static java.net.InetAddress.getByName;
21
+
22
+ // SimpleDateFormat is not thread safe
23
+ class DateFormatter extends ThreadLocal<SimpleDateFormat> {
24
+ protected SimpleDateFormat initialValue() {
25
+ // Formats into HTTP date format (RFC 822/1123).
26
+ SimpleDateFormat f = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
27
+ f.setTimeZone(TimeZone.getTimeZone("GMT"));
28
+ return f;
29
+ }
30
+
31
+ private static final DateFormatter FORMATTER = new DateFormatter();
32
+
33
+ public static String getDate() {
34
+ return FORMATTER.get().format(new Date());
35
+ }
36
+ }
37
+
38
+ public class HttpUtils {
39
+
40
+ public static final Charset ASCII = Charset.forName("US-ASCII");
41
+ public static final Charset UTF_8 = Charset.forName("utf8");
42
+
43
+ public static final String CHARSET = "charset=";
44
+ // Colon ':'
45
+ public static final byte COLON = 58;
46
+
47
+ public static final byte CR = 13; // \r
48
+
49
+ public static final byte LF = 10; // \n
50
+
51
+ // public static final int ABORT_PROCESSING = -1;
52
+
53
+ // public static final String USER_AGENT = "user-agent";
54
+
55
+ // public static final String ACCEPT = "Accept";
56
+
57
+ // public static final String ETAG = "ETag";
58
+
59
+ // public static final String ACCEPT_ENCODING = "accept-encoding";
60
+
61
+ public static final String TRANSFER_ENCODING = "transfer-encoding";
62
+
63
+ public static final String CONTENT_ENCODING = "content-encoding";
64
+
65
+ public static final String CONTENT_TYPE = "content-type";
66
+
67
+ public static final String CHUNKED = "chunked";
68
+
69
+ public static final String CONNECTION = "connection";
70
+
71
+ // public static final String LOCATION = "location";
72
+
73
+ // public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
74
+
75
+ // public static final String IF_NONE_MATCH = "If-None-Match";
76
+
77
+ // public static final String LAST_MODIFIED = "Last-Modified";
78
+
79
+ public static final String X_FORWARDED_FOR = "x-forwarded-for";
80
+
81
+ public static final String CONTENT_LENGTH = "content-length";
82
+
83
+ // public static final String CACHE_CONTROL = "Cache-Control";
84
+
85
+ // space ' '
86
+ public static final byte SP = 32;
87
+
88
+ public static ByteBuffer bodyBuffer(Object body) throws IOException {
89
+ if (body == null) {
90
+ return null;
91
+ } else if (body instanceof String) {
92
+ byte[] b = ((String) body).getBytes(UTF_8);
93
+ return ByteBuffer.wrap(b);
94
+ } else if (body instanceof InputStream) {
95
+ DynamicBytes b = readAll((InputStream) body);
96
+ return ByteBuffer.wrap(b.get(), 0, b.length());
97
+ } else if (body instanceof File) {
98
+ // serving file is better be done by Nginx
99
+ return readAll((File) body);
100
+ } else if (body instanceof Seqable) {
101
+ ISeq seq = ((Seqable) body).seq();
102
+ DynamicBytes b = new DynamicBytes(seq.count() * 512);
103
+ while (seq != null) {
104
+ b.append(seq.first().toString(), UTF_8);
105
+ seq = seq.next();
106
+ }
107
+ return ByteBuffer.wrap(b.get(), 0, b.length());
108
+ // makes ultimate optimization possible: no copy
109
+ } else if (body instanceof ByteBuffer) {
110
+ return (ByteBuffer) body;
111
+ } else {
112
+ throw new RuntimeException(body.getClass() + " is not understandable");
113
+ }
114
+ }
115
+
116
+ private static final byte[] ALPHAS = "0123456789ABCDEF".getBytes();
117
+
118
+ // like javascript's encodeURI
119
+ // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURI
120
+ public static String encodeURI(String url) {
121
+ byte[] bytes = url.getBytes(UTF_8);
122
+ DynamicBytes buffer = new DynamicBytes(bytes.length * 2);
123
+ boolean e = true;
124
+ for (byte b : bytes) {
125
+ int c = b < 0 ? b + 256 : b;
126
+ if (c < '!' || c > '~') {
127
+ e = true;
128
+ } else {
129
+ switch (c) {
130
+ case '"':
131
+ // https://github.com/http-kit/http-kit/issues/70
132
+ // case '%':
133
+ case '<':
134
+ case '>':
135
+ case '\\':
136
+ case '^':
137
+ case '`':
138
+ case '{':
139
+ case '}':
140
+ case '|':
141
+ e = true;
142
+ break;
143
+ default:
144
+ e = false;
145
+ }
146
+ }
147
+ if (e) {
148
+ buffer.append((byte) '%');
149
+ buffer.append(ALPHAS[c / 16]);
150
+ buffer.append(ALPHAS[c % 16]);
151
+ } else {
152
+ buffer.append(b);
153
+ }
154
+ }
155
+ return new String(buffer.get(), 0, buffer.length(), UTF_8);
156
+ }
157
+
158
+ public static int findEndOfString(String sb) {
159
+ int result;
160
+ for (result = sb.length(); result > 0; result--) {
161
+ if (!isWhitespace(sb.charAt(result - 1))) {
162
+ break;
163
+ }
164
+ }
165
+ return result;
166
+ }
167
+
168
+ public static int findNonWhitespace(String sb, int offset) {
169
+ int result;
170
+ for (result = offset; result < sb.length(); result++) {
171
+ if (!isWhitespace(sb.charAt(result))) {
172
+ break;
173
+ }
174
+ }
175
+ return result;
176
+ }
177
+
178
+ public static int findWhitespace(String sb, int offset) {
179
+ int result;
180
+ for (result = offset; result < sb.length(); result++) {
181
+ if (isWhitespace(sb.charAt(result))) {
182
+ break;
183
+ }
184
+ }
185
+ return result;
186
+ }
187
+
188
+ public static int getChunkSize(String hex) throws ProtocolException {
189
+ hex = hex.trim();
190
+ for (int i = 0; i < hex.length(); i++) {
191
+ char c = hex.charAt(i);
192
+ if (c == ';' || Character.isWhitespace(c) || Character.isISOControl(c)) {
193
+ hex = hex.substring(0, i);
194
+ break;
195
+ }
196
+ }
197
+ try {
198
+ return Integer.parseInt(hex, 16);
199
+ } catch (Exception e) {
200
+ throw new ProtocolException("Expect chunk size to be a number, get" + hex);
201
+ }
202
+ }
203
+
204
+ // content-type => Content-Type
205
+ public static String camelCase(String key) {
206
+ StringBuilder sb = new StringBuilder(key.length());
207
+ boolean upper = true;
208
+ for (int i = 0; i < key.length(); i++) {
209
+ char c = key.charAt(i);
210
+ if (upper) {
211
+ sb.append(Character.toUpperCase(c));
212
+ } else {
213
+ sb.append(Character.toLowerCase(c));
214
+ }
215
+ upper = c == '-';
216
+ }
217
+ return sb.toString();
218
+ }
219
+
220
+ public static String getPath(URI uri) {
221
+ String path = encodeURI(uri.getRawPath());
222
+ String query = uri.getRawQuery();
223
+ if ("".equals(path))
224
+ path = "/";
225
+ if (query == null)
226
+ return path;
227
+ else
228
+ return path + "?" + query;
229
+ }
230
+
231
+ public static int getPort(URI uri) {
232
+ int port = uri.getPort();
233
+ if (port == -1) {
234
+ if ("https".equals(uri.getScheme()))
235
+ port = 443;
236
+ else
237
+ port = 80;
238
+ }
239
+ return port;
240
+ }
241
+
242
+ public static String getHost(URI uri) {
243
+ String host = uri.getHost();
244
+ int port = uri.getPort();
245
+
246
+ if (port != -1) {
247
+ host += ":" + port;
248
+ }
249
+ return host;
250
+ }
251
+
252
+ public static InetSocketAddress getServerAddr(URI uri) throws UnknownHostException {
253
+ InetAddress host = getByName(uri.getHost());
254
+ return new InetSocketAddress(host, getPort(uri));
255
+ }
256
+
257
+ public static byte[] readContent(File f, int length) throws IOException {
258
+ byte[] bytes = new byte[length];
259
+ FileInputStream fs = null;
260
+ try {
261
+ fs = new FileInputStream(f);
262
+ int offset = 0;
263
+ while (offset < length) {
264
+ offset += fs.read(bytes, offset, length - offset);
265
+ }
266
+ } finally {
267
+ if (fs != null) {
268
+ try {
269
+ fs.close();
270
+ } catch (Exception ignore) {
271
+ }
272
+ }
273
+ }
274
+ return bytes;
275
+ }
276
+
277
+ public static ByteBuffer readAll(File f) throws IOException {
278
+ int length = (int) f.length();
279
+ if (length >= 1024 * 1024 * 2) { // 2M
280
+ FileInputStream fs = new FileInputStream(f);
281
+ MappedByteBuffer buffer = fs.getChannel().map(MapMode.READ_ONLY, 0, length);
282
+ fs.close();
283
+ return buffer;
284
+ } else {
285
+ return ByteBuffer.wrap(readContent(f, length));
286
+ }
287
+ }
288
+
289
+ public static DynamicBytes readAll(InputStream is) throws IOException {
290
+ DynamicBytes bytes = new DynamicBytes(32768); // init 32k
291
+ byte[] buffer = new byte[16384];
292
+ int read;
293
+ while ((read = is.read(buffer)) != -1) {
294
+ bytes.append(buffer, read);
295
+ }
296
+ is.close();
297
+ return bytes;
298
+ }
299
+
300
+ public static String getStringValue(Map<String, Object> headers, String key) {
301
+ Object o = headers.get(key);
302
+ if(o instanceof String) {
303
+ return (String)o;
304
+ }
305
+ return null;
306
+ }
307
+
308
+ public static void printError(String msg, Throwable t) {
309
+ String error = String.format("%s [%s] ERROR - %s", new Date(), Thread.currentThread()
310
+ .getName(), msg);
311
+ StringWriter str = new StringWriter();
312
+ PrintWriter pw = new PrintWriter(str, false);
313
+ pw.println(error);
314
+ t.printStackTrace(pw);
315
+ System.err.print(str.getBuffer().toString());
316
+ }
317
+
318
+ public static void splitAndAddHeader(String sb, Map<String, Object> headers) {
319
+ final int length = sb.length();
320
+ int nameStart;
321
+ int nameEnd;
322
+ int colonEnd;
323
+ int valueStart;
324
+ int valueEnd;
325
+
326
+ nameStart = findNonWhitespace(sb, 0);
327
+ for (nameEnd = nameStart; nameEnd < length; nameEnd++) {
328
+ char ch = sb.charAt(nameEnd);
329
+ if (ch == ':' || Character.isWhitespace(ch)) {
330
+ break;
331
+ }
332
+ }
333
+
334
+ for (colonEnd = nameEnd; colonEnd < length; colonEnd++) {
335
+ if (sb.charAt(colonEnd) == ':') {
336
+ colonEnd++;
337
+ break;
338
+ }
339
+ }
340
+
341
+ valueStart = findNonWhitespace(sb, colonEnd);
342
+ valueEnd = findEndOfString(sb);
343
+
344
+ String key = sb.substring(nameStart, nameEnd);
345
+ if (valueStart > valueEnd) { // ignore
346
+ // logger.warn("header error: " + sb);
347
+ } else {
348
+ String value = sb.substring(valueStart, valueEnd);
349
+ key = key.toLowerCase();
350
+ Object v = headers.get(key);
351
+ if (v == null) {
352
+ headers.put(key, value);
353
+ } else {
354
+ if (v instanceof String) {
355
+ headers.put(key, PersistentList.create(Arrays.asList((String) v, value)));
356
+ } else {
357
+ headers.put(key, ((ISeq)v).cons(value));
358
+ }
359
+ }
360
+ }
361
+ }
362
+
363
+ /*----------------charset--------------------*/
364
+
365
+ public static Charset parseCharset(String type) {
366
+ if (type != null) {
367
+ try {
368
+ type = type.toLowerCase();
369
+ int i = type.indexOf(CHARSET);
370
+ if (i != -1) {
371
+ String charset = type.substring(i + CHARSET.length()).trim();
372
+ return Charset.forName(charset);
373
+ }
374
+ } catch (Exception ignore) {
375
+ }
376
+ }
377
+ return null;
378
+ }
379
+
380
+ // <?xml version='1.0' encoding='GBK'?>
381
+ // <?xml version="1.0" encoding="UTF-8"?>
382
+ static final Pattern ENCODING = Pattern.compile("encoding=('|\")([\\w|-]+)('|\")",
383
+ Pattern.CASE_INSENSITIVE);
384
+
385
+ private static Charset guess(String html, String patten) {
386
+ int idx = html.indexOf(patten);
387
+ if (idx != -1) {
388
+ int start = idx + patten.length();
389
+ int end = html.indexOf('"', start);
390
+ if (end != -1) {
391
+ try {
392
+ return Charset.forName(html.substring(start, end));
393
+ } catch (Exception ignore) {
394
+ }
395
+ }
396
+ }
397
+ return null;
398
+ }
399
+
400
+ // unit test in utils-test.clj
401
+ public static Charset detectCharset(Map<String, Object> headers, DynamicBytes body) {
402
+ // 1. first from http header: Content-Type: text/html; charset=utf8
403
+ Charset result = parseCharset(getStringValue(headers, CONTENT_TYPE));
404
+ if (result == null) {
405
+ // 2. decode a little to find charset=???
406
+ String s = new String(body.get(), 0, min(512, body.length()), ASCII);
407
+ // content="text/html;charset=gb2312"
408
+ result = guess(s, CHARSET);
409
+ if (result == null) {
410
+ // for xml
411
+ Matcher matcher = ENCODING.matcher(s);
412
+ if (matcher.find()) {
413
+ try {
414
+ result = Charset.forName(matcher.group(2));
415
+ } catch (Exception ignore) {
416
+ }
417
+ }
418
+ }
419
+ }
420
+ // default utf8
421
+ return result == null ? UTF_8 : result;
422
+ }
423
+
424
+ public static final String CL = "Content-Length";
425
+
426
+ public static ByteBuffer[] HttpEncode(int status, HeaderMap headers, Object body) {
427
+ ByteBuffer bodyBuffer;
428
+ try {
429
+ bodyBuffer = bodyBuffer(body);
430
+ // only write length if not chunked
431
+ if (!CHUNKED.equals(headers.get("Transfer-Encoding"))) {
432
+ if (bodyBuffer != null) {
433
+ headers.put(CL, Integer.toString(bodyBuffer.remaining()));
434
+ } else {
435
+ headers.put(CL, "0");
436
+ }
437
+ }
438
+ } catch (IOException e) {
439
+ byte[] b = e.getMessage().getBytes(ASCII);
440
+ status = 500;
441
+ headers.clear();
442
+ headers.put(CL, Integer.toString(b.length));
443
+ bodyBuffer = ByteBuffer.wrap(b);
444
+ }
445
+ headers.put("Server", "http-kit");
446
+ headers.put("Date", DateFormatter.getDate()); // rfc says the Date is needed
447
+
448
+ DynamicBytes bytes = new DynamicBytes(196);
449
+ byte[] bs = HttpStatus.valueOf(status).getInitialLineBytes();
450
+ bytes.append(bs, bs.length);
451
+ headers.encodeHeaders(bytes);
452
+ ByteBuffer headBuffer = ByteBuffer.wrap(bytes.get(), 0, bytes.length());
453
+
454
+ if (bodyBuffer != null)
455
+ return new ByteBuffer[]{headBuffer, bodyBuffer};
456
+ else
457
+ return new ByteBuffer[]{headBuffer};
458
+ }
459
+
460
+ public static ByteBuffer WsEncode(byte opcode, byte[] data, int length) {
461
+ byte b0 = 0;
462
+ b0 |= 1 << 7; // FIN
463
+ b0 |= opcode;
464
+ ByteBuffer buffer = ByteBuffer.allocate(length + 10); // max
465
+ buffer.put(b0);
466
+
467
+ if (length <= 125) {
468
+ buffer.put((byte) (length));
469
+ } else if (length <= 0xFFFF) {
470
+ buffer.put((byte) 126);
471
+ buffer.putShort((short) length);
472
+ } else {
473
+ buffer.put((byte) 127);
474
+ buffer.putLong(length);
475
+ }
476
+ buffer.put(data, 0, length);
477
+ buffer.flip();
478
+ return buffer;
479
+ }
480
+
481
+ public static ByteBuffer WsEncode(byte opcode, byte[] data) {
482
+ return WsEncode(opcode, data, data.length);
483
+ }
484
+ }
Binary file
@@ -0,0 +1,5 @@
1
+ package org.httpkit;
2
+
3
+ public enum HttpVersion {
4
+ HTTP_1_0, HTTP_1_1
5
+ }
Binary file
@@ -0,0 +1,52 @@
1
+ package org.httpkit;
2
+
3
+ import java.nio.ByteBuffer;
4
+ import java.util.Arrays;
5
+
6
+ import static org.httpkit.HttpUtils.CR;
7
+ import static org.httpkit.HttpUtils.LF;
8
+
9
+ public class LineReader {
10
+ // 1k buffer, increase as necessary;
11
+ byte[] lineBuffer = new byte[1024];
12
+ int lineBufferIdx = 0;
13
+ private final int maxLine;
14
+
15
+ public LineReader(int maxLine) {
16
+ this.maxLine = maxLine;
17
+ }
18
+
19
+ public String readLine(ByteBuffer buffer) throws LineTooLargeException {
20
+ byte b;
21
+ boolean more = true;
22
+ while (buffer.hasRemaining() && more) {
23
+ b = buffer.get();
24
+ if (b == CR) {
25
+ if (buffer.hasRemaining() && buffer.get() == LF) {
26
+ more = false;
27
+ }
28
+ } else if (b == LF) {
29
+ more = false;
30
+ } else {
31
+ if (lineBufferIdx == maxLine - 2) {
32
+ throw new LineTooLargeException("exceed max line " + maxLine);
33
+ }
34
+ if (lineBufferIdx == lineBuffer.length) {
35
+ lineBuffer = Arrays.copyOf(lineBuffer, lineBuffer.length * 2);
36
+ }
37
+ lineBuffer[lineBufferIdx] = b;
38
+ ++lineBufferIdx;
39
+ }
40
+ }
41
+ String line = null;
42
+ if (!more) {
43
+ line = new String(lineBuffer, 0, lineBufferIdx);
44
+ lineBufferIdx = 0;
45
+ }
46
+ return line;
47
+ }
48
+
49
+ public final void reset() {
50
+ this.lineBufferIdx = 0;
51
+ }
52
+ }
@@ -0,0 +1,13 @@
1
+ package org.httpkit;
2
+
3
+ // TODO 431 Request Header Fields Too Large
4
+ // http://www.rooftopsolutions.nl/blog/new-http-status-codes
5
+
6
+ public class LineTooLargeException extends HTTPException {
7
+
8
+ private static final long serialVersionUID = 1L;
9
+
10
+ public LineTooLargeException(String msg) {
11
+ super(msg);
12
+ }
13
+ }
@@ -0,0 +1,20 @@
1
+ package org.httpkit;
2
+
3
+ import java.util.concurrent.ThreadFactory;
4
+ import java.util.concurrent.atomic.AtomicInteger;
5
+
6
+ public class PrefixThreadFactory implements ThreadFactory {
7
+ final AtomicInteger id = new AtomicInteger(0);
8
+ private final String prefix;
9
+
10
+ public PrefixThreadFactory(String prefix) {
11
+ this.prefix = prefix;
12
+ }
13
+
14
+ public Thread newThread(Runnable r) {
15
+ int i = id.incrementAndGet();
16
+ Thread t = new Thread(r, prefix + i);
17
+ t.setDaemon(true);
18
+ return t;
19
+ }
20
+ }
Binary file