iodine 0.1.21 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -2
  3. data/.travis.yml +23 -2
  4. data/CHANGELOG.md +9 -2
  5. data/README.md +232 -179
  6. data/Rakefile +13 -1
  7. data/bin/config.ru +63 -0
  8. data/bin/console +6 -0
  9. data/bin/echo +42 -32
  10. data/bin/http-hello +62 -0
  11. data/bin/http-playground +124 -0
  12. data/bin/playground +62 -0
  13. data/bin/poc/Gemfile.lock +23 -0
  14. data/bin/poc/README.md +37 -0
  15. data/bin/poc/config.ru +66 -0
  16. data/bin/poc/gemfile +1 -0
  17. data/bin/poc/www/index.html +57 -0
  18. data/bin/raw-rbhttp +35 -0
  19. data/bin/raw_broadcast +66 -0
  20. data/bin/test_with_faye +40 -0
  21. data/bin/ws-broadcast +108 -0
  22. data/bin/ws-echo +108 -0
  23. data/exe/iodine +59 -0
  24. data/ext/iodine/base64.c +264 -0
  25. data/ext/iodine/base64.h +72 -0
  26. data/ext/iodine/bscrypt-common.h +109 -0
  27. data/ext/iodine/bscrypt.h +49 -0
  28. data/ext/iodine/extconf.rb +41 -0
  29. data/ext/iodine/hex.c +123 -0
  30. data/ext/iodine/hex.h +70 -0
  31. data/ext/iodine/http.c +200 -0
  32. data/ext/iodine/http.h +128 -0
  33. data/ext/iodine/http1.c +402 -0
  34. data/ext/iodine/http1.h +56 -0
  35. data/ext/iodine/http1_simple_parser.c +473 -0
  36. data/ext/iodine/http1_simple_parser.h +59 -0
  37. data/ext/iodine/http_request.h +128 -0
  38. data/ext/iodine/http_response.c +1606 -0
  39. data/ext/iodine/http_response.h +393 -0
  40. data/ext/iodine/http_response_http1.h +374 -0
  41. data/ext/iodine/iodine_core.c +641 -0
  42. data/ext/iodine/iodine_core.h +70 -0
  43. data/ext/iodine/iodine_http.c +615 -0
  44. data/ext/iodine/iodine_http.h +19 -0
  45. data/ext/iodine/iodine_websocket.c +430 -0
  46. data/ext/iodine/iodine_websocket.h +21 -0
  47. data/ext/iodine/libasync.c +552 -0
  48. data/ext/iodine/libasync.h +117 -0
  49. data/ext/iodine/libreact.c +347 -0
  50. data/ext/iodine/libreact.h +244 -0
  51. data/ext/iodine/libserver.c +912 -0
  52. data/ext/iodine/libserver.h +435 -0
  53. data/ext/iodine/libsock.c +950 -0
  54. data/ext/iodine/libsock.h +478 -0
  55. data/ext/iodine/misc.c +181 -0
  56. data/ext/iodine/misc.h +76 -0
  57. data/ext/iodine/random.c +193 -0
  58. data/ext/iodine/random.h +48 -0
  59. data/ext/iodine/rb-call.c +127 -0
  60. data/ext/iodine/rb-call.h +60 -0
  61. data/ext/iodine/rb-libasync.h +79 -0
  62. data/ext/iodine/rb-rack-io.c +389 -0
  63. data/ext/iodine/rb-rack-io.h +17 -0
  64. data/ext/iodine/rb-registry.c +213 -0
  65. data/ext/iodine/rb-registry.h +33 -0
  66. data/ext/iodine/sha1.c +359 -0
  67. data/ext/iodine/sha1.h +85 -0
  68. data/ext/iodine/sha2.c +825 -0
  69. data/ext/iodine/sha2.h +138 -0
  70. data/ext/iodine/siphash.c +136 -0
  71. data/ext/iodine/siphash.h +15 -0
  72. data/ext/iodine/spnlock.h +235 -0
  73. data/ext/iodine/websockets.c +696 -0
  74. data/ext/iodine/websockets.h +120 -0
  75. data/ext/iodine/xor-crypt.c +189 -0
  76. data/ext/iodine/xor-crypt.h +107 -0
  77. data/iodine.gemspec +25 -18
  78. data/lib/iodine.rb +57 -58
  79. data/lib/iodine/http.rb +0 -189
  80. data/lib/iodine/protocol.rb +36 -245
  81. data/lib/iodine/version.rb +1 -1
  82. data/lib/rack/handler/iodine.rb +145 -2
  83. metadata +115 -37
  84. data/bin/core_http_test +0 -51
  85. data/bin/em playground +0 -56
  86. data/bin/hello_world +0 -75
  87. data/bin/setup +0 -7
  88. data/lib/iodine/client.rb +0 -5
  89. data/lib/iodine/core.rb +0 -102
  90. data/lib/iodine/core_init.rb +0 -143
  91. data/lib/iodine/http/hpack.rb +0 -553
  92. data/lib/iodine/http/http1.rb +0 -251
  93. data/lib/iodine/http/http2.rb +0 -507
  94. data/lib/iodine/http/rack_support.rb +0 -108
  95. data/lib/iodine/http/request.rb +0 -462
  96. data/lib/iodine/http/response.rb +0 -474
  97. data/lib/iodine/http/session.rb +0 -143
  98. data/lib/iodine/http/websocket_client.rb +0 -335
  99. data/lib/iodine/http/websocket_handler.rb +0 -101
  100. data/lib/iodine/http/websockets.rb +0 -336
  101. data/lib/iodine/io.rb +0 -56
  102. data/lib/iodine/logging.rb +0 -46
  103. data/lib/iodine/settings.rb +0 -158
  104. data/lib/iodine/ssl_connector.rb +0 -48
  105. data/lib/iodine/timers.rb +0 -95
@@ -0,0 +1,49 @@
1
+ /*
2
+ (un)copyright: Boaz segev, 2016
3
+ License: Public Domain except for any non-public-domain algorithms, which are
4
+ subject to their own licenses.
5
+
6
+ Feel free to copy, use and enjoy in accordance with to the license(s).
7
+ */
8
+ #ifndef BSCRYPT
9
+ /**
10
+ The bscrypt library supplies some **basic** cryptographic functions.
11
+
12
+ Read the README file for more details.
13
+
14
+ All functions will be available using the prefix `bscrypt_`, i.e.:
15
+
16
+ char buffer[13] = {0};
17
+ bscrypt_base64_encode(buffer, "My String", 9);
18
+
19
+
20
+ */
21
+ #define BSCRYPT "0.0.1"
22
+
23
+ #include "bscrypt-common.h"
24
+ #include "base64.h"
25
+ #include "hex.h"
26
+ #include "misc.h"
27
+ #include "random.h"
28
+ #include "sha1.h"
29
+ #include "sha2.h"
30
+ #include "siphash.h"
31
+ #include "xor-crypt.h"
32
+
33
+ #if defined(DEBUG) && DEBUG == 1
34
+ #define bscrypt_test() \
35
+ { \
36
+ bscrypt_test_sha1(); \
37
+ bscrypt_test_sha2(); \
38
+ bscrypt_test_base64(); \
39
+ bscrypt_test_random(); \
40
+ bscrypt_test_siphash(); \
41
+ }
42
+ #else
43
+ #define bscrypt_test() \
44
+ fprintf(stderr, \
45
+ "Debug mode not enabled, define DEBUG as 1 in the compiler.\n");
46
+ #endif
47
+
48
+ /* end include gate */
49
+ #endif
@@ -0,0 +1,41 @@
1
+ require 'mkmf'
2
+
3
+ def check_for_stdatomics
4
+ RbConfig::MAKEFILE_CONFIG['CC'] = $CC = ENV['CC'] if ENV['CC']
5
+ RbConfig::MAKEFILE_CONFIG['CPP'] = $CPP = ENV['CPP'] if ENV['CPP']
6
+ puts 'Missing support for atomic operations (support for C11) - is your compiler updated?' unless have_header('stdatomic.h')
7
+ end
8
+
9
+ abort 'Missing a Linux/Unix OS evented API (epoll/kqueue).' unless have_func('kevent') || have_func('epoll_ctl')
10
+
11
+ if ENV['CC']
12
+ ENV['CPP'] ||= ENV['CC']
13
+ puts "detected user prefered compiler (#{ENV['CC']})."
14
+ check_for_stdatomics
15
+ elsif find_executable('clang') && puts('testing clang for stdatomic support...').nil? && system("printf \"\#include <stdatomic.h>\nint main(void) {}\" | clang -include stdatomic.h -xc -o /dev/null -", out: '/dev/null')
16
+ $CC = ENV['CC'] = 'clang'
17
+ $CPP = ENV['CPP'] = 'clang'
18
+ puts "using clang compiler v. #{`clang -dumpversion`}."
19
+ elsif find_executable('gcc-6')
20
+ $CC = ENV['CC'] = 'gcc-6'
21
+ $CPP = ENV['CPP'] = find_executable('g++-6') ? 'g++-6' : 'gcc-6'
22
+ puts 'using gcc-6 compiler.'
23
+ elsif find_executable('gcc-5')
24
+ $CC = ENV['CC'] = 'gcc-5'
25
+ $CPP = ENV['CPP'] = find_executable('g++-5') ? 'g++-5' : 'gcc-5'
26
+ puts 'using gcc-5 compiler.'
27
+ elsif find_executable('gcc-4.9')
28
+ $CC = ENV['CC'] = 'gcc-4.9'
29
+ $CPP = ENV['CPP'] = find_executable('g++-4.9') ? 'g++-4.9' : 'gcc-4.9'
30
+ puts 'using gcc-4.9 compiler.'
31
+ else
32
+ check_for_stdatomics
33
+ end
34
+
35
+ $CFLAGS = '-std=c11 -O3 -Wall'
36
+ RbConfig::MAKEFILE_CONFIG['CC'] = $CC = ENV['CC'] if ENV['CC']
37
+ RbConfig::MAKEFILE_CONFIG['CPP'] = $CPP = ENV['CPP'] if ENV['CPP']
38
+
39
+ # abort "Missing OpenSSL." unless have_library("ssl")
40
+
41
+ create_makefile 'iodine/iodine'
data/ext/iodine/hex.c ADDED
@@ -0,0 +1,123 @@
1
+ /*
2
+ (un)copyright: Boaz segev, 2016
3
+ License: Public Domain except for any non-public-domain algorithms, which are
4
+ subject to their own licenses.
5
+
6
+ Feel free to copy, use and enjoy in accordance with to the license(s).
7
+ */
8
+ #ifndef _GNU_SOURCE
9
+ #define _GNU_SOURCE
10
+ #endif
11
+ #include "hex.h"
12
+ #include <ctype.h>
13
+
14
+ /* ***************************************************************************
15
+ Hex Conversion
16
+ */
17
+
18
+ /*
19
+ #define hex2i(h) \
20
+ (((h) >= '0' && (h) <= '9') ? ((h) - '0') : (((h) | 32) - 'a' + 10))
21
+ */
22
+
23
+ #define i2hex(hi) (((hi) < 10) ? ('0' + (hi)) : ('A' + ((hi)-10)))
24
+
25
+ /* Credit to Jonathan Leffler for the idea */
26
+ #define hex2i(c) \
27
+ (((c) >= '0' && (c) <= '9') ? ((c)-48) : (((c) >= 'a' && (c) <= 'f') || \
28
+ ((c) >= 'A' && (c) <= 'F')) \
29
+ ? (((c) | 32) - 87) \
30
+ : ({ \
31
+ return -1; \
32
+ 0; \
33
+ }))
34
+
35
+ /**
36
+ Returns 1 if the string is HEX encoded (no non-valid hex values). Returns 0 if
37
+ it isn't.
38
+ */
39
+ int bscrypt_is_hex(const char *string, size_t length) {
40
+ // for (size_t i = 0; i < length; i++) {
41
+ // if (isxdigit(string[i]) == 0)
42
+ // return 0;
43
+ char c;
44
+ for (size_t i = 0; i < length; i++) {
45
+ c = string[i];
46
+ if ((!isspace(c)) &&
47
+ (c < '0' || c > 'z' ||
48
+ !((c >= 'a') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))))
49
+ return 0;
50
+ }
51
+ return 1;
52
+ }
53
+ /**
54
+ This will convert the string (byte stream) to a Hex string. This is not
55
+ cryptography, just conversion for pretty print.
56
+
57
+ The target buffer MUST have enough room for the expected data. The expected
58
+ data is double the length of the string + 1 byte for the NULL terminator
59
+ byte.
60
+
61
+ A NULL byte will be appended to the target buffer. The function will return
62
+ the number of bytes written to the target buffer.
63
+
64
+ Returns the number of bytes actually written to the target buffer (excluding
65
+ the NULL terminator byte).
66
+ */
67
+ int bscrypt_str2hex(char *target, const char *string, size_t length) {
68
+ if (!target)
69
+ return -1;
70
+ size_t i = length;
71
+ target[(length << 1) + 1] = 0;
72
+ // go in reverse, so that target could be same as string.
73
+ while (i) {
74
+ --i;
75
+ target[(i << 1) + 1] = i2hex(string[i] & 0x0F);
76
+ target[(i << 1)] = i2hex(((uint8_t *)string)[i] >> 4);
77
+ }
78
+ return (length << 1);
79
+ }
80
+
81
+ /**
82
+ This will convert a Hex string to a byte string. This is not cryptography,
83
+ just conversion for pretty print.
84
+
85
+ The target buffer MUST have enough room for the expected data. The expected
86
+ data is half the length of the Hex string + 1 byte for the NULL terminator
87
+ byte.
88
+
89
+ A NULL byte will be appended to the target buffer. The function will return
90
+ the number of bytes written to the target buffer.
91
+
92
+ If the target buffer is NULL, the encoded string will be destructively
93
+ edited
94
+ and the decoded data will be placed in the original string's buffer.
95
+
96
+ Returns the number of bytes actually written to the target buffer (excluding
97
+ the NULL terminator byte).
98
+ */
99
+ int bscrypt_hex2str(char *target, char *hex, size_t length) {
100
+ if (!target)
101
+ target = hex;
102
+ size_t i = 0;
103
+ size_t written = 0;
104
+ while (i + 1 < length) {
105
+ if (isspace(hex[i])) {
106
+ ++i;
107
+ continue;
108
+ }
109
+ target[written] = (hex2i(hex[i]) << 4) | hex2i(hex[i + 1]);
110
+ ++written;
111
+ i += 2;
112
+ }
113
+ if (i < length && !isspace(hex[i])) {
114
+ target[written] = hex2i(hex[i + 1]);
115
+ ++written;
116
+ }
117
+
118
+ target[written] = 0;
119
+ return written;
120
+ }
121
+
122
+ #undef hex2i
123
+ #undef i2hex
data/ext/iodine/hex.h ADDED
@@ -0,0 +1,70 @@
1
+ /*
2
+ (un)copyright: Boaz segev, 2016
3
+ License: Public Domain except for any non-public-domain algorithms, which are
4
+ subject to their own licenses.
5
+
6
+ Feel free to copy, use and enjoy in accordance with to the license(s).
7
+ */
8
+ #ifndef bscrypt_HEX_H
9
+ #define bscrypt_HEX_H
10
+ #include "bscrypt-common.h"
11
+ /* *****************************************************************************
12
+ C++ extern
13
+ */
14
+ #if defined(__cplusplus)
15
+ extern "C" {
16
+ #endif
17
+
18
+ /* ***************************************************************************
19
+ Hex Conversion
20
+ */
21
+
22
+ /**
23
+ Returns 1 if the string is HEX encoded (no non-valid hex values). Returns 0 if
24
+ it isn't.
25
+ */
26
+ int bscrypt_is_hex(const char *string, size_t length);
27
+ /**
28
+ This will convert the string (byte stream) to a Hex string. This is not
29
+ cryptography, just conversion for pretty print.
30
+
31
+ The target buffer MUST have enough room for the expected data. The expected
32
+ data is double the length of the string + 1 byte for the NULL terminator
33
+ byte.
34
+
35
+ A NULL byte will be appended to the target buffer. The function will return
36
+ the number of bytes written to the target buffer.
37
+
38
+ Returns the number of bytes actually written to the target buffer (excluding
39
+ the NULL terminator byte).
40
+ */
41
+ int bscrypt_str2hex(char *target, const char *string, size_t length);
42
+
43
+ /**
44
+ This will convert a Hex string to a byte string. This is not cryptography,
45
+ just conversion for pretty print.
46
+
47
+ The target buffer MUST have enough room for the expected data. The expected
48
+ data is half the length of the Hex string + 1 byte for the NULL terminator
49
+ byte.
50
+
51
+ A NULL byte will be appended to the target buffer. The function will return
52
+ the number of bytes written to the target buffer.
53
+
54
+ If the target buffer is NULL, the encoded string will be destructively
55
+ edited
56
+ and the decoded data will be placed in the original string's buffer.
57
+
58
+ Returns the number of bytes actually written to the target buffer (excluding
59
+ the NULL terminator byte).
60
+ */
61
+ int bscrypt_hex2str(char *target, char *hex, size_t length);
62
+
63
+ /* *****************************************************************************
64
+ C++ extern finish
65
+ */
66
+ #if defined(__cplusplus)
67
+ }
68
+ #endif
69
+
70
+ #endif
data/ext/iodine/http.c ADDED
@@ -0,0 +1,200 @@
1
+ #include "http.h"
2
+
3
+ /**
4
+ A faster (yet less localized) alternative to `gmtime_r`.
5
+
6
+ See the libc `gmtime_r` documentation for details.
7
+
8
+ Falls back to `gmtime_r` for dates before epoch.
9
+ */
10
+ struct tm* http_gmtime(const time_t* timer, struct tm* tmbuf) {
11
+ // static char* DAYS[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
12
+ // static char * Months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
13
+ // "Jul",
14
+ // "Aug", "Sep", "Oct", "Nov", "Dec"};
15
+ static uint8_t month_len[] = {
16
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, // nonleap year
17
+ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 // leap year
18
+ };
19
+ if (*timer < 0)
20
+ return gmtime_r(timer, tmbuf);
21
+ ssize_t tmp;
22
+ tmbuf->tm_gmtoff = 0;
23
+ tmbuf->tm_zone = "UTC";
24
+ tmbuf->tm_isdst = 0;
25
+ tmbuf->tm_year = 70; // tm_year == The number of years since 1900
26
+ tmbuf->tm_mon = 0;
27
+ // for seconds up to weekdays, we build up, as small values clean up larger
28
+ // values.
29
+ tmp = ((ssize_t)*timer);
30
+ tmbuf->tm_sec = tmp % 60;
31
+ tmp = tmp / 60;
32
+ tmbuf->tm_min = tmp % 60;
33
+ tmp = tmp / 60;
34
+ tmbuf->tm_hour = tmp % 24;
35
+ tmp = tmp / 24;
36
+ // day of epoch was a thursday. Add + 3 so sunday == 0...
37
+ tmbuf->tm_wday = (tmp + 3) % 7;
38
+ // tmp == number of days since epoch
39
+ #define DAYS_PER_400_YEARS ((400 * 365) + 97)
40
+ while (tmp >= DAYS_PER_400_YEARS) {
41
+ tmbuf->tm_year += 400;
42
+ tmp -= DAYS_PER_400_YEARS;
43
+ }
44
+ #undef DAYS_PER_400_YEARS
45
+ #define DAYS_PER_100_YEARS ((100 * 365) + 24)
46
+ while (tmp >= DAYS_PER_100_YEARS) {
47
+ tmbuf->tm_year += 100;
48
+ tmp -= DAYS_PER_100_YEARS;
49
+ if (((tmbuf->tm_year / 100) & 3) ==
50
+ 0) // leap century divisable by 400 => add leap
51
+ --tmp;
52
+ }
53
+ #undef DAYS_PER_100_YEARS
54
+ #define DAYS_PER_32_YEARS ((32 * 365) + 8)
55
+ while (tmp >= DAYS_PER_32_YEARS) {
56
+ tmbuf->tm_year += 32;
57
+ tmp -= DAYS_PER_32_YEARS;
58
+ }
59
+ #undef DAYS_PER_32_YEARS
60
+ #define DAYS_PER_8_YEARS ((8 * 365) + 2)
61
+ while (tmp >= DAYS_PER_8_YEARS) {
62
+ tmbuf->tm_year += 8;
63
+ tmp -= DAYS_PER_8_YEARS;
64
+ }
65
+ #undef DAYS_PER_8_YEARS
66
+ #define DAYS_PER_4_YEARS ((4 * 365) + 1)
67
+ while (tmp >= DAYS_PER_4_YEARS) {
68
+ tmbuf->tm_year += 4;
69
+ tmp -= DAYS_PER_4_YEARS;
70
+ }
71
+ #undef DAYS_PER_4_YEARS
72
+ while (tmp >= 365) {
73
+ tmbuf->tm_year += 1;
74
+ tmp -= 365;
75
+ if ((tmbuf->tm_year & 3) == 0) { // leap year
76
+ if (tmp > 0) {
77
+ --tmp;
78
+ continue;
79
+ } else {
80
+ tmp += 365;
81
+ --tmbuf->tm_year;
82
+ break;
83
+ }
84
+ }
85
+ }
86
+ tmbuf->tm_yday = tmp;
87
+ if ((tmbuf->tm_year & 3) == 1) {
88
+ // regular year
89
+ for (size_t i = 0; i < 12; i++) {
90
+ if (tmp < month_len[i])
91
+ break;
92
+ tmp -= month_len[i];
93
+ ++tmbuf->tm_mon;
94
+ }
95
+ } else {
96
+ // leap year
97
+ for (size_t i = 12; i < 24; i++) {
98
+ if (tmp < month_len[i])
99
+ break;
100
+ tmp -= month_len[i];
101
+ ++tmbuf->tm_mon;
102
+ }
103
+ }
104
+ tmbuf->tm_mday = tmp;
105
+ return tmbuf;
106
+ }
107
+
108
+ static char* DAY_NAMES[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
109
+ static char* MONTH_NAMES[] = {"Jan ", "Feb ", "Mar ", "Apr ", "May ", "Jun ",
110
+ "Jul ", "Aug ", "Sep ", "Oct ", "Nov ", "Dec "};
111
+ static const char* GMT_STR = "GMT";
112
+
113
+ size_t http_date2str(char* target, struct tm* tmbuf) {
114
+ char* pos = target;
115
+ uint16_t tmp;
116
+ *(uint32_t*)pos = *((uint32_t*)DAY_NAMES[tmbuf->tm_wday]);
117
+ pos[3] = ',';
118
+ pos[4] = ' ';
119
+ pos += 5;
120
+ if (tmbuf->tm_mday < 10) {
121
+ *pos = '0' + tmbuf->tm_mday;
122
+ ++pos;
123
+ } else {
124
+ tmp = tmbuf->tm_mday / 10;
125
+ pos[0] = '0' + tmp;
126
+ pos[1] = '0' + (tmbuf->tm_mday - (tmp * 10));
127
+ pos += 2;
128
+ }
129
+ *(pos++) = ' ';
130
+ *(uint32_t*)pos = *((uint32_t*)MONTH_NAMES[tmbuf->tm_mon]);
131
+ pos += 4;
132
+ // write year.
133
+ pos += http_ul2a(pos, tmbuf->tm_year + 1900);
134
+ *(pos++) = ' ';
135
+ tmp = tmbuf->tm_hour / 10;
136
+ pos[0] = '0' + tmp;
137
+ pos[1] = '0' + (tmbuf->tm_hour - (tmp * 10));
138
+ pos[2] = ':';
139
+ tmp = tmbuf->tm_min / 10;
140
+ pos[3] = '0' + tmp;
141
+ pos[4] = '0' + (tmbuf->tm_min - (tmp * 10));
142
+ pos[5] = ':';
143
+ tmp = tmbuf->tm_sec / 10;
144
+ pos[6] = '0' + tmp;
145
+ pos[7] = '0' + (tmbuf->tm_sec - (tmp * 10));
146
+ pos += 8;
147
+ pos[0] = ' ';
148
+ *((uint32_t*)(pos + 1)) = *((uint32_t*)GMT_STR);
149
+ pos += 4;
150
+ return pos - target;
151
+ }
152
+
153
+ /* Credit to Jonathan Leffler for the idea of a unified conditional */
154
+ #define hex_val(c) \
155
+ (((c) >= '0' && (c) <= '9') ? ((c)-48) : (((c) >= 'a' && (c) <= 'f') || \
156
+ ((c) >= 'A' && (c) <= 'F')) \
157
+ ? (((c) | 32) - 87) \
158
+ : ({ \
159
+ return -1; \
160
+ 0; \
161
+ }))
162
+ ssize_t http_decode_url(char* dest, const char* url_data, size_t length) {
163
+ char* pos = dest;
164
+ const char* end = url_data + length;
165
+ while (url_data < end) {
166
+ if (*url_data == '+') {
167
+ // decode space
168
+ *(pos++) = ' ';
169
+ ++url_data;
170
+ } else if (*url_data == '%') {
171
+ // decode hex value
172
+ // this is a percent encoded value.
173
+ *(pos++) = (hex_val(url_data[1]) << 4) | hex_val(url_data[2]);
174
+ url_data += 3;
175
+ } else
176
+ *(pos++) = *(url_data++);
177
+ }
178
+ *pos = 0;
179
+ return pos - dest;
180
+ }
181
+
182
+ ssize_t http_decode_url_unsafe(char* dest, const char* url_data) {
183
+ char* pos = dest;
184
+ while (*url_data) {
185
+ if (*url_data == '+') {
186
+ // decode space
187
+ *(pos++) = ' ';
188
+ ++url_data;
189
+ } else if (*url_data == '%') {
190
+ // decode hex value
191
+ // this is a percent encoded value.
192
+ *(pos++) = (hex_val(url_data[1]) << 4) | hex_val(url_data[2]);
193
+ url_data += 3;
194
+ } else
195
+ *(pos++) = *(url_data++);
196
+ }
197
+ *pos = 0;
198
+ return pos - dest;
199
+ }
200
+ #undef hex_val