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,59 @@
1
+ #ifndef HTTP1_SIMPLE_PARSER_H
2
+ #define HTTP1_SIMPLE_PARSER_H
3
+ #include "http_request.h"
4
+
5
+ #ifndef HTTP_HEADERS_LOWERCASE
6
+ /** when defined, HTTP headers will be converted to lowercase and header
7
+ * searches will be case sensitive. This improves the parser performance in some
8
+ * instances (which surprised me.) */
9
+ #define HTTP_HEADERS_LOWERCASE 1
10
+ #endif
11
+
12
+ #ifndef HTTP_PARSER_TEST
13
+ /* a debugging flag that adds the test function and decleration */
14
+ #define HTTP_PARSER_TEST 0
15
+ #endif
16
+
17
+ /**
18
+ Parses HTTP request headers. This allows review of the expected content length
19
+ before accepting any content (server resource management).
20
+
21
+ Returns the number of bytes consumed before the full request was accepted.
22
+
23
+ Returns -1 on fatal error (i.e. protocol error).
24
+ Returns -2 when the request parsing didn't complete.
25
+
26
+ Incomplete request parsing updates the content in the buffer. The same buffer
27
+ and the same `http_request_s` should be returned to the parsed on the next
28
+ attempt, only the `len` argument is expected to grow.
29
+
30
+ The buffer should be kept intact for the life of the request object, as the
31
+ HTTP/1.1 parser does NOT copy any data.
32
+ */
33
+ ssize_t http1_parse_request_headers(void* buffer,
34
+ size_t len,
35
+ http_request_s* request);
36
+
37
+ /**
38
+ Parses HTTP request body content (if any).
39
+
40
+ Returns the number of bytes consumed before the body consumption was complete.
41
+
42
+ Returns -1 on fatal error (i.e. protocol error).
43
+ Returns -2 when the request parsing didn't complete (all the data was consumed).
44
+
45
+ Incomplete body parsing doesn't require the buffer to remain static (it can be
46
+ recycled).
47
+
48
+ It is expected that the next attempt will contain fresh data in the `buffer`
49
+ argument.
50
+ */
51
+ ssize_t http1_parse_request_body(void* buffer,
52
+ size_t len,
53
+ http_request_s* request);
54
+
55
+ #if defined(DEBUG) && DEBUG == 1
56
+ void http_parser_test(void);
57
+ #endif
58
+
59
+ #endif
@@ -0,0 +1,128 @@
1
+ #ifndef HTTP_REQUEST_H
2
+ #define HTTP_REQUEST_H
3
+
4
+ #ifndef _GNU_SOURCE
5
+ #define _GNU_SOURCE
6
+ #endif
7
+
8
+ #include <stdlib.h>
9
+ #include <stdio.h>
10
+ #include <stdint.h>
11
+ #include <unistd.h>
12
+ #include <string.h>
13
+
14
+ #ifndef __unused
15
+ #define __unused __attribute__((unused))
16
+ #endif
17
+
18
+ typedef struct {
19
+ const char* name;
20
+ const char* value;
21
+ uint16_t name_length;
22
+ uint16_t value_length;
23
+ } http_headers_s;
24
+
25
+ typedef struct {
26
+ /** points to the HTTP method name. */
27
+ const char* method;
28
+ /** The portion of the request URL that comes before the '?', if any. */
29
+ const char* path;
30
+ /** The string length of the path (editing the path requires update). */
31
+ /** The portion of the request URL that follows the ?, if any. */
32
+ const char* query;
33
+ /** Points to a version string. */
34
+ const char* version;
35
+ /** Points to the body's host header value (a required header). */
36
+ const char* host;
37
+ /** points to the body's content type header, if any. */
38
+ const char* content_type;
39
+ /** points to the Upgrade header, if any. */
40
+ const char* upgrade;
41
+ /** points to the Connection header, if any. */
42
+ const char* connection;
43
+
44
+ /** the body's content's length, in bytes (can be 0). */
45
+ size_t content_length;
46
+
47
+ /* string lengths */
48
+
49
+ uint16_t method_len;
50
+ uint16_t path_len;
51
+ uint16_t query_len;
52
+ uint16_t host_len;
53
+ uint16_t content_type_len;
54
+ uint16_t upgrade_len;
55
+
56
+ /** `version_len` is signed, to allow negative values (SPDY/HTTP2 etc). */
57
+ int16_t version_len;
58
+
59
+ /**
60
+ Points the body of the request, if the body exists and is stored in memory.
61
+ Otherwise, NULL. */
62
+ const char* body_str;
63
+ /** points a tmpfile file descriptor containing the body of the request. */
64
+ int body_file;
65
+ /** semi-private information. */
66
+ struct {
67
+ /**
68
+ When pooling request objects, this points to the next object.
69
+ In other times it may contain arbitrary data that can be used by the parser
70
+ or implementation.
71
+ */
72
+ void* next;
73
+ /**
74
+ Implementation specific. This, conceptually, holds information about the
75
+ "owner" of this request. */
76
+ void* owner;
77
+ /**
78
+ Implementation specific. This, conceptually, holds the connection that
79
+ "owns" this request, or an implementation identifier. */
80
+ intptr_t fd;
81
+ /** the current header position, for API or parser states. */
82
+ uint16_t headers_pos;
83
+ /** the maximum number of header space availble. */
84
+ uint16_t max_headers;
85
+ } metadata;
86
+
87
+ uint16_t connection_len;
88
+ uint16_t headers_count;
89
+
90
+ http_headers_s headers[];
91
+ } http_request_s;
92
+
93
+ __unused static inline void http_request_clear(http_request_s* request) {
94
+ if (request->body_file > 0) /* assumes no tempfile with fd 0 */
95
+ close(request->body_file);
96
+ *request = (http_request_s){
97
+ .metadata.owner = request->metadata.owner,
98
+ .metadata.fd = request->metadata.fd,
99
+ .metadata.max_headers = request->metadata.max_headers,
100
+ };
101
+ }
102
+
103
+ /** searches for a header in the header array, both reaturnning it's value and
104
+ * setting it's position in the `request->metadata.header_pos` variable.*/
105
+ __unused static inline const char* http_request_find_header(
106
+ http_request_s* request,
107
+ const char* header,
108
+ size_t header_len) {
109
+ if (header == NULL || request == NULL)
110
+ return NULL;
111
+ if (header_len == 0)
112
+ header_len = strlen(header);
113
+ request->metadata.headers_pos = 0;
114
+ while (request->metadata.headers_pos < request->headers_count) {
115
+ if (header_len ==
116
+ request->headers[request->metadata.headers_pos].name_length &&
117
+ strncasecmp(request->headers[request->metadata.headers_pos].name,
118
+ header, header_len) == 0)
119
+ return request->headers[request->metadata.headers_pos].value;
120
+ request->metadata.headers_pos += 1;
121
+ }
122
+ return NULL;
123
+ }
124
+
125
+ #define HTTP_REQUEST_SIZE(header_count) \
126
+ (sizeof(http_request_s) + ((header_count) * sizeof(http_headers_s)))
127
+
128
+ #endif
@@ -0,0 +1,1606 @@
1
+ #include "http.h"
2
+ #include "http_response_http1.h"
3
+ #include <netinet/in.h>
4
+ #include <netinet/ip.h>
5
+ #include <arpa/inet.h>
6
+ #include <fcntl.h>
7
+ #include <sys/stat.h>
8
+ #include <sys/types.h>
9
+ #include <sys/socket.h>
10
+ #include <time.h>
11
+ #include <sys/resource.h>
12
+
13
+ /* *****************************************************************************
14
+ Helpers
15
+ ***************************************************************************** */
16
+
17
+ #define forward_func(response, func, ...) \
18
+ if ((response)->metadata.version == 1) { \
19
+ return h1p_##func(__VA_ARGS__); \
20
+ } else \
21
+ return -1;
22
+ #define perform_func(response, func, ...) \
23
+ if ((response)->metadata.version == 1) { \
24
+ h1p_##func(__VA_ARGS__); \
25
+ }
26
+
27
+ // static char* MONTH_NAMES[] = {"Jan ", "Feb ", "Mar ", "Apr ", "May ", "Jun ",
28
+ // "Jul ", "Aug ", "Sep ", "Oct ", "Nov ", "Dec
29
+ // "};
30
+ //
31
+ #define is_num(c) ((c) >= '0' && (c) <= '9')
32
+ #define num_val(c) ((c)-48)
33
+
34
+ /* *****************************************************************************
35
+ API implementation
36
+ ***************************************************************************** */
37
+
38
+ /**
39
+ Initializes a response object with the request object. This function assumes the
40
+ response object memory is garbage and might have been stack-allocated.
41
+
42
+ Notice that the `http_request_s` pointer must point at a valid request object
43
+ and that the request object must remain valid until the response had been
44
+ completed.
45
+
46
+ Hangs on failuer (waits for available resources).
47
+ */
48
+ http_response_s http_response_init(http_request_s *request) {
49
+ protocol_s *http = request->metadata.owner;
50
+ time_t date = server_last_tick();
51
+ return (http_response_s){
52
+ .metadata.request = request,
53
+ .metadata.fd = request->metadata.fd,
54
+ .metadata.packet = sock_checkout_packet(),
55
+ .status = 200,
56
+ .date = date,
57
+ .last_modified = date,
58
+ .metadata.version = (http->service == HTTP1 ? 1 : 0),
59
+ .metadata.should_close =
60
+ (request && request->connection &&
61
+ request->connection_len ==
62
+ 5), // don't check header value, length is unique enough.
63
+ };
64
+ }
65
+ /**
66
+ Releases any resources held by the response object (doesn't release the response
67
+ object itself, which might have been allocated on the stack).
68
+
69
+ This function assumes the response object might have been stack-allocated.
70
+ */
71
+ void http_response_destroy(http_response_s *response) {
72
+ if (response->metadata.packet) {
73
+ sock_free_packet(response->metadata.packet);
74
+ response->metadata.packet = NULL;
75
+ }
76
+ }
77
+ /**
78
+ Writes a header to the response. This function writes only the requested
79
+ number of bytes from the header name and the requested number of bytes from
80
+ the header value. It can be used even when the header name and value don't
81
+ contain NULL terminating bytes by passing the `.name_len` or `.value_len` data
82
+ in the `http_headers_s` structure.
83
+
84
+ If the header buffer is full or the headers were already sent (new headers
85
+ cannot be sent), the function will return -1.
86
+
87
+ On success, the function returns 0.
88
+ */
89
+ #undef http_response_write_header
90
+ int http_response_write_header(http_response_s *response,
91
+ http_headers_s header) {
92
+ // check if the header is a reserved header
93
+ if ((header.name_length == 4 || header.name_length == 0) &&
94
+ strncasecmp(header.name, "date", 4) == 0)
95
+ response->metadata.date_written = 1;
96
+ else if ((header.name_length == 10 || header.name_length == 0) &&
97
+ strncasecmp(header.name, "connection", 10) == 0) {
98
+ response->metadata.connection_written = 1;
99
+ if (header.value && header.value[0] == 'c')
100
+ response->metadata.should_close = 1;
101
+ } else if ((header.name_length == 14 || header.name_length == 0) &&
102
+ strncasecmp(header.name, "content-length", 14) == 0)
103
+ response->metadata.content_length_written = 1;
104
+
105
+ // write the header to the protocol
106
+ forward_func(response, response_write_header, response, header);
107
+ }
108
+ #define http_response_write_header(response, ...) \
109
+ http_response_write_header(response, (http_headers_s){__VA_ARGS__})
110
+
111
+ /**
112
+ Set / Delete a cookie using this helper function.
113
+
114
+ To set a cookie, use (in this example, a session cookie):
115
+
116
+ http_response_set_cookie(response,
117
+ .name = "my_cookie",
118
+ .value = "data");
119
+
120
+ To delete a cookie, use:
121
+
122
+ http_response_set_cookie(response,
123
+ .name = "my_cookie",
124
+ .value = NULL);
125
+
126
+ This function writes a cookie header to the response. Only the requested
127
+ number of bytes from the cookie value and name are written (if none are
128
+ provided, a terminating NULL byte is assumed).
129
+
130
+ Both the name and the value of the cookie are checked for validity (legal
131
+ characters), but other properties aren't reviewed (domain/path) - please make
132
+ sure to use only valid data, as HTTP imposes restrictions on these things.
133
+
134
+ If the header buffer is full or the headers were already sent (new headers
135
+ cannot be sent), the function will return -1.
136
+
137
+ On success, the function returns 0.
138
+ */
139
+ #undef http_response_set_cookie
140
+ int http_response_set_cookie(http_response_s *response, http_cookie_s cookie) {
141
+ forward_func(response, response_set_cookie, response, cookie);
142
+ };
143
+
144
+ /**
145
+ Indicates that any pending data (i.e. unsent headers) should be sent and that no
146
+ more use of the response object will be made.
147
+ */
148
+ void http_response_finish(http_response_s *response) {
149
+ perform_func(response, response_finish, response);
150
+ if (response->metadata.logged)
151
+ http_response_log_finish(response);
152
+ http_response_destroy(response);
153
+ }
154
+ /**
155
+ Sends the headers (if they weren't previously sent) and writes the data to the
156
+ underlying socket.
157
+
158
+ The body will be copied to the server's outgoing buffer.
159
+
160
+ If the connection was already closed, the function will return -1. On success,
161
+ the function returns 0.
162
+ */
163
+ int http_response_write_body(http_response_s *response, const char *body,
164
+ size_t length) {
165
+ forward_func(response, response_write_body, response, body, length);
166
+ }
167
+ /**
168
+ Sends the headers (if they weren't previously sent) and writes the data to the
169
+ underlying socket.
170
+
171
+ The server's outgoing buffer will take ownership of the file and close it
172
+ using `fclose` once the data was sent.
173
+
174
+ If the connection was already closed, the function will return -1. On success,
175
+ the function returns 0.
176
+ */
177
+ int http_response_sendfile(http_response_s *response, int source_fd,
178
+ off_t offset, size_t length) {
179
+ forward_func(response, response_sendfile, response, source_fd, offset,
180
+ length);
181
+ }
182
+ /**
183
+ Attempts to send the file requested using an **optional** response object (if no
184
+ response object is pointed to, a temporary response object will be created).
185
+
186
+ If a `file_path_unsafe` is provided, it will be appended to the `file_path_safe`
187
+ (if any) and URL decoded before attempting to locate and open the file. Any
188
+ insecure path manipulations in the `file_path_unsafe` (i.e. `..` or `//`) will
189
+ cause the function to fail.
190
+
191
+ `file_path_unsafe` MUST begine with a `/`, or it will be appended to
192
+ `file_path_safe` as part of the last folder's name. if `file_path_safe` ends
193
+ with a `/`, it will be trancated.
194
+
195
+ If the `log` flag is set, response logging will be performed.
196
+
197
+ On failure, the function will return -1 (no response will be sent).
198
+
199
+ On success, the function returns 0.
200
+ */
201
+ int http_response_sendfile2(http_response_s *response, http_request_s *request,
202
+ const char *file_path_safe, size_t path_safe_len,
203
+ const char *file_path_unsafe,
204
+ size_t path_unsafe_len, uint8_t log) {
205
+ static char *HEAD = "HEAD";
206
+ if (request == NULL || (file_path_safe == NULL && file_path_unsafe == NULL))
207
+ return -1;
208
+ http_response_s tmp_response;
209
+
210
+ if (file_path_safe && path_safe_len == 0)
211
+ path_safe_len = strlen(file_path_safe);
212
+
213
+ if (file_path_unsafe && path_unsafe_len == 0)
214
+ path_unsafe_len = strlen(file_path_unsafe);
215
+
216
+ const char *mime = NULL;
217
+ const char *ext = NULL;
218
+ struct stat file_data = {};
219
+ // fprintf(stderr, "\n\noriginal request path: %s\n", req->path);
220
+ char *fname = malloc(path_safe_len + path_unsafe_len + 1 + 11);
221
+ if (fname == NULL)
222
+ return -1;
223
+ if (file_path_safe)
224
+ memcpy(fname, file_path_safe, path_safe_len);
225
+ fname[path_safe_len] = 0;
226
+ // if the last character is a '/', step back.
227
+ if (file_path_unsafe) {
228
+ if (fname[path_safe_len - 1] == '/')
229
+ path_safe_len--;
230
+ ssize_t tmp = http_decode_url(fname + path_safe_len, file_path_unsafe,
231
+ path_unsafe_len);
232
+ if (tmp < 0)
233
+ goto no_file;
234
+ path_safe_len += tmp;
235
+ if (fname[path_safe_len - 1] == '/') {
236
+ memcpy(fname + path_safe_len, "index.html", 10);
237
+ fname[path_safe_len += 10] = 0;
238
+ }
239
+
240
+ // scan path string for double dots (security - prevent path manipulation)
241
+ // set the extention point value, while were doing so.
242
+ tmp = 0;
243
+ while (fname[tmp]) {
244
+ if (fname[tmp] == '.')
245
+ ext = fname + tmp;
246
+ // return false if we found a "/.." or "/" in our string.
247
+ if (fname[tmp++] == '/' &&
248
+ ((fname[tmp++] == '.' && fname[tmp++] == '.') || fname[tmp] == '/'))
249
+ goto no_file;
250
+ }
251
+ }
252
+ // fprintf(stderr, "file name: %s\noriginal request path: %s\n", fname,
253
+ // req->path);
254
+ // get file data (prevent folder access and get modification date)
255
+ if (stat(fname, &file_data))
256
+ goto no_file;
257
+ // check that we have a file and not something else
258
+ if (!S_ISREG(file_data.st_mode) && !S_ISLNK(file_data.st_mode))
259
+ goto no_file;
260
+
261
+ if (response == NULL) {
262
+ response = &tmp_response;
263
+ tmp_response = http_response_init(request);
264
+ if (log)
265
+ http_response_log_start(response);
266
+ }
267
+
268
+ if (*((uint32_t *)request->method) == *((uint32_t *)HEAD)) {
269
+ forward_func(response, response_finish, response);
270
+ }
271
+
272
+ // we have a file, time to handle response details.
273
+ int file = open(fname, O_RDONLY);
274
+ if (file == -1) {
275
+ goto no_file;
276
+ }
277
+ // free the allocated fname memory
278
+ free(fname);
279
+ fname = NULL;
280
+
281
+ // get the mime type (we have an ext pointer and the string isn't empty)
282
+ if (ext && ext[1]) {
283
+ mime = http_response_ext2mime(ext + 1);
284
+ if (mime) {
285
+ http_response_write_header(response, .name = "Content-Type",
286
+ .name_length = 12, .value = mime);
287
+ }
288
+ }
289
+
290
+ response->last_modified = file_data.st_mtime;
291
+ http_response_write_header(response, .name = "Cache-Control",
292
+ .name_length = 13, .value = "public, max-age=3600",
293
+ .value_length = 20);
294
+
295
+ // Range handling
296
+ if ((ext = http_request_find_header(request, "range", 5)) &&
297
+ (ext[0] | 32) == 'b' && (ext[1] | 32) == 'y' && (ext[2] | 32) == 't' &&
298
+ (ext[3] | 32) == 'e' && (ext[4] | 32) == 's' && (ext[5] | 32) == '=') {
299
+ // ext holds the first range, starting on index 6 i.e. RANGE: bytes=0-1
300
+ // "HTTP/1.1 206 Partial content\r\n"
301
+ // "Accept-Ranges: bytes\r\n"
302
+ // "Content-Range: bytes %lu-%lu/%lu\r\n"
303
+ // fprintf(stderr, "Got a range request %s\n", ext);
304
+ size_t start = 0, finish = 0;
305
+ ext = ext + 6;
306
+ while (is_num(*ext)) {
307
+ start = start * 10;
308
+ start += num_val(*ext);
309
+ ext++;
310
+ }
311
+ // fprintf(stderr, "Start: %lu / %lld\n", start, file_data.st_size);
312
+ if (start >= file_data.st_size - 1)
313
+ goto invalid_range;
314
+ ext++;
315
+ while (is_num(*ext)) {
316
+ finish = finish * 10;
317
+ finish += num_val(*ext);
318
+ ext++;
319
+ }
320
+ // going to the EOF (big chunk or EOL requested) - send as file
321
+ if (finish >= file_data.st_size)
322
+ finish = file_data.st_size;
323
+ char buffer[64];
324
+ char *pos = buffer + 6;
325
+ memcpy(buffer, "bytes ", 6);
326
+ pos += http_ul2a(pos, start);
327
+ *(pos++) = '-';
328
+ pos += http_ul2a(pos, finish);
329
+ *(pos++) = '/';
330
+ pos += http_ul2a(pos, file_data.st_size);
331
+ http_response_write_header(response, .name = "Content-Range",
332
+ .name_length = 13, .value = buffer,
333
+ .value_length = pos - buffer);
334
+ response->status = 206;
335
+ http_response_write_header(response, .name = "Accept-Ranges",
336
+ .name_length = 13, .value = "bytes",
337
+ .value_length = 5);
338
+
339
+ http_response_sendfile(response, file, start, finish - start + 1);
340
+ if (log)
341
+ http_response_log_finish(response);
342
+ return 0;
343
+ }
344
+
345
+ invalid_range:
346
+ http_response_write_header(response, .name = "Accept-Ranges",
347
+ .name_length = 13, .value = "none",
348
+ .value_length = 4);
349
+
350
+ http_response_sendfile(response, file, 0, file_data.st_size);
351
+ if (log)
352
+ http_response_log_finish(response);
353
+ return 0;
354
+ no_file:
355
+ free(fname);
356
+ return -1;
357
+ }
358
+
359
+ #ifdef RUSAGE_SELF
360
+ const static size_t CLOCK_RESOLUTION = 1000; /* in miliseconds */
361
+ static size_t get_clock_mili(void) {
362
+ struct rusage rusage;
363
+ getrusage(RUSAGE_SELF, &rusage);
364
+ return ((rusage.ru_utime.tv_sec + rusage.ru_stime.tv_sec) * 1000000) +
365
+ (rusage.ru_utime.tv_usec + rusage.ru_stime.tv_usec);
366
+ }
367
+ #elif defined CLOCKS_PER_SEC
368
+ #define get_clock_mili() (size_t) clock()
369
+ #define CLOCK_RESOLUTION (CLOCKS_PER_SEC / 1000)
370
+ #else
371
+ #define get_clock_mili() 0
372
+ #define CLOCK_RESOLUTION 1
373
+ #endif
374
+
375
+ /**
376
+ Starts counting miliseconds for log results.
377
+ */
378
+ void http_response_log_start(http_response_s *response) {
379
+ response->metadata.clock_start = get_clock_mili();
380
+ response->metadata.logged = 1;
381
+ }
382
+ /**
383
+ prints out the log to stderr.
384
+ */
385
+ void http_response_log_finish(http_response_s *response) {
386
+ // TODO optimize using fwrite
387
+ http_request_s *request = response->metadata.request;
388
+ uintptr_t bytes_sent = (uintptr_t)response->metadata.headers_pos;
389
+
390
+ size_t mili = response->metadata.logged
391
+ ? ((get_clock_mili() - response->metadata.clock_start) /
392
+ CLOCK_RESOLUTION)
393
+ : 0;
394
+ struct tm tm;
395
+ struct sockaddr_in addrinfo;
396
+ socklen_t addrlen = sizeof(addrinfo);
397
+ time_t last_tick = server_last_tick();
398
+ http_gmtime(&last_tick, &tm);
399
+ int got_add = getpeername(sock_uuid2fd(request->metadata.fd),
400
+ (struct sockaddr *)&addrinfo, &addrlen);
401
+ #define HTTP_REQUEST_LOG_LIMIT 128
402
+ char buffer[HTTP_REQUEST_LOG_LIMIT];
403
+ char *tmp;
404
+ size_t pos;
405
+ if (got_add == 0) {
406
+ tmp = inet_ntoa(addrinfo.sin_addr);
407
+ pos = strlen(tmp);
408
+ memcpy(buffer, tmp, pos);
409
+ } else {
410
+ memcpy(buffer, "[unknown]", 9);
411
+ pos = 9;
412
+ }
413
+ memcpy(buffer + pos, " - - [", 6);
414
+ pos += 6;
415
+ pos += http_date2str(buffer + pos, &tm);
416
+ buffer[pos++] = ']';
417
+ buffer[pos++] = ' ';
418
+ buffer[pos++] = '"';
419
+ // limit method to 10 chars
420
+ if (request->method_len <= 10) {
421
+ memcpy(buffer + pos, request->method, request->method_len);
422
+ pos += request->method_len;
423
+ } else {
424
+ const char *j = request->method;
425
+ // copy first 7 chars
426
+ while (j < request->method + 7)
427
+ buffer[pos++] = *(j++);
428
+ // add three dots.
429
+ buffer[pos++] = '.';
430
+ buffer[pos++] = '.';
431
+ buffer[pos++] = '.';
432
+ }
433
+ buffer[pos++] = ' ';
434
+ // limit path to 24 chars
435
+ if (request->path_len <= 24) {
436
+ memcpy(buffer + pos, request->path, request->path_len);
437
+ pos += request->path_len;
438
+ } else {
439
+ const char *j = request->path;
440
+ // copy first 7 chars
441
+ while (j < request->path + 21)
442
+ buffer[pos++] = *(j++);
443
+ // add three dots.
444
+ buffer[pos++] = '.';
445
+ buffer[pos++] = '.';
446
+ buffer[pos++] = '.';
447
+ }
448
+ buffer[pos++] = ' ';
449
+ // limit version to 10 chars
450
+ if (request->version_len <= 10) {
451
+ memcpy(buffer + pos, request->version, request->version_len);
452
+ pos += request->version_len;
453
+ } else {
454
+ const char *j = request->version;
455
+ // copy first 7 chars
456
+ while (j < request->version + 7)
457
+ buffer[pos++] = *(j++);
458
+ // add three dots.
459
+ buffer[pos++] = '.';
460
+ buffer[pos++] = '.';
461
+ buffer[pos++] = '.';
462
+ }
463
+ buffer[pos++] = '"';
464
+ buffer[pos++] = ' ';
465
+ pos += http_ul2a(buffer + pos, response->status > 0 && response->status < 1000
466
+ ? response->status
467
+ : 0);
468
+
469
+ buffer[pos++] = ' ';
470
+ pos += http_ul2a(buffer + pos, bytes_sent);
471
+ if (response->metadata.logged) {
472
+ buffer[pos++] = ' ';
473
+ pos += http_ul2a(buffer + pos, mili);
474
+ buffer[pos++] = 'm';
475
+ buffer[pos++] = 's';
476
+ }
477
+ buffer[pos++] = '\n';
478
+ response->metadata.logged = 0;
479
+ fwrite(buffer, 1, pos, stderr);
480
+ }
481
+
482
+ /* *****************************************************************************
483
+ Hardcded lists / matching
484
+ *****************************************************************************
485
+ */
486
+
487
+ /** Gets a response status, as a string */
488
+ const char *http_response_status_str(uint16_t status) {
489
+ static struct {
490
+ int i_status;
491
+ char *s_status;
492
+ } List[] = {{200, "OK"},
493
+ {301, "Moved Permanently"},
494
+ {302, "Found"},
495
+ {100, "Continue"},
496
+ {101, "Switching Protocols"},
497
+ {403, "Forbidden"},
498
+ {404, "Not Found"},
499
+ {400, "Bad Request"},
500
+ {500, "Internal Server Error"},
501
+ {501, "Not Implemented"},
502
+ {502, "Bad Gateway"},
503
+ {503, "Service Unavailable"},
504
+ {102, "Processing"},
505
+ {201, "Created"},
506
+ {202, "Accepted"},
507
+ {203, "Non-Authoritative Information"},
508
+ {204, "No Content"},
509
+ {205, "Reset Content"},
510
+ {206, "Partial Content"},
511
+ {207, "Multi-Status"},
512
+ {208, "Already Reported"},
513
+ {226, "IM Used"},
514
+ {300, "Multiple Choices"},
515
+ {303, "See Other"},
516
+ {304, "Not Modified"},
517
+ {305, "Use Proxy"},
518
+ {306, "(Unused) "},
519
+ {307, "Temporary Redirect"},
520
+ {308, "Permanent Redirect"},
521
+ {401, "Unauthorized"},
522
+ {402, "Payment Required"},
523
+ {405, "Method Not Allowed"},
524
+ {406, "Not Acceptable"},
525
+ {407, "Proxy Authentication Required"},
526
+ {408, "Request Timeout"},
527
+ {409, "Conflict"},
528
+ {410, "Gone"},
529
+ {411, "Length Required"},
530
+ {412, "Precondition Failed"},
531
+ {413, "Payload Too Large"},
532
+ {414, "URI Too Long"},
533
+ {415, "Unsupported Media Type"},
534
+ {416, "Range Not Satisfiable"},
535
+ {417, "Expectation Failed"},
536
+ {421, "Misdirected Request"},
537
+ {422, "Unprocessable Entity"},
538
+ {423, "Locked"},
539
+ {424, "Failed Dependency"},
540
+ {425, "Unassigned"},
541
+ {426, "Upgrade Required"},
542
+ {427, "Unassigned"},
543
+ {428, "Precondition Required"},
544
+ {429, "Too Many Requests"},
545
+ {430, "Unassigned"},
546
+ {431, "Request Header Fields Too Large"},
547
+ {504, "Gateway Timeout"},
548
+ {505, "HTTP Version Not Supported"},
549
+ {506, "Variant Also Negotiates"},
550
+ {507, "Insufficient Storage"},
551
+ {508, "Loop Detected"},
552
+ {509, "Unassigned"},
553
+ {510, "Not Extended"},
554
+ {511, "Network Authentication Required"},
555
+ {0, 0}};
556
+ int pos = 0;
557
+ while (List[pos].i_status) {
558
+ if (List[pos].i_status == status)
559
+ return List[pos].s_status;
560
+ pos++;
561
+ }
562
+ return NULL;
563
+ }
564
+
565
+ /** Gets the mime-type string (C string) associated with the file extension.
566
+ */
567
+ const char *http_response_ext2mime(const char *ext) {
568
+ static struct {
569
+ char ext[12];
570
+ char *mime;
571
+ } List[] = {
572
+ {"123", "application/vnd.lotus-1-2-3"},
573
+ {"3dml", "text/vnd.in3d.3dml"},
574
+ {"3ds", "image/x-3ds"},
575
+ {"3g2", "video/3gpp2"},
576
+ {"3gp", "video/3gpp"},
577
+ {"7z", "application/x-7z-compressed"},
578
+ {"aab", "application/x-authorware-bin"},
579
+ {"aac", "audio/x-aac"},
580
+ {"aam", "application/x-authorware-map"},
581
+ {"aas", "application/x-authorware-seg"},
582
+ {"abw", "application/x-abiword"},
583
+ {"ac", "application/pkix-attr-cert"},
584
+ {"acc", "application/vnd.americandynamics.acc"},
585
+ {"ace", "application/x-ace-compressed"},
586
+ {"acu", "application/vnd.acucobol"},
587
+ {"acutc", "application/vnd.acucorp"},
588
+ {"adp", "audio/adpcm"},
589
+ {"aep", "application/vnd.audiograph"},
590
+ {"afm", "application/x-font-type1"},
591
+ {"afp", "application/vnd.ibm.modcap"},
592
+ {"ahead", "application/vnd.ahead.space"},
593
+ {"ai", "application/postscript"},
594
+ {"aif", "audio/x-aiff"},
595
+ {"aifc", "audio/x-aiff"},
596
+ {"aiff", "audio/x-aiff"},
597
+ {"air", "application/vnd.adobe.air-application-installer-package+zip"},
598
+ {"ait", "application/vnd.dvb.ait"},
599
+ {"ami", "application/vnd.amiga.ami"},
600
+ {"apk", "application/vnd.android.package-archive"},
601
+ {"appcache", "text/cache-manifest"},
602
+ {"application", "application/x-ms-application"},
603
+ {
604
+ "pptx",
605
+ "application/"
606
+ "vnd.openxmlformats-officedocument.presentationml.presentation",
607
+ },
608
+ {"apr", "application/vnd.lotus-approach"},
609
+ {"arc", "application/x-freearc"},
610
+ {"asc", "application/pgp-signature"},
611
+ {"asf", "video/x-ms-asf"},
612
+ {"asm", "text/x-asm"},
613
+ {"aso", "application/vnd.accpac.simply.aso"},
614
+ {"asx", "video/x-ms-asf"},
615
+ {"atc", "application/vnd.acucorp"},
616
+ {"atom", "application/atom+xml"},
617
+ {"atomcat", "application/atomcat+xml"},
618
+ {"atomsvc", "application/atomsvc+xml"},
619
+ {"atx", "application/vnd.antix.game-component"},
620
+ {"au", "audio/basic"},
621
+ {"avi", "video/x-msvideo"},
622
+ {"aw", "application/applixware"},
623
+ {"azf", "application/vnd.airzip.filesecure.azf"},
624
+ {"azs", "application/vnd.airzip.filesecure.azs"},
625
+ {"azw", "application/vnd.amazon.ebook"},
626
+ {"bat", "application/x-msdownload"},
627
+ {"bcpio", "application/x-bcpio"},
628
+ {"bdf", "application/x-font-bdf"},
629
+ {"bdm", "application/vnd.syncml.dm+wbxml"},
630
+ {"bed", "application/vnd.realvnc.bed"},
631
+ {"bh2", "application/vnd.fujitsu.oasysprs"},
632
+ {"bin", "application/octet-stream"},
633
+ {"blb", "application/x-blorb"},
634
+ {"blorb", "application/x-blorb"},
635
+ {"bmi", "application/vnd.bmi"},
636
+ {"bmp", "image/bmp"},
637
+ {"book", "application/vnd.framemaker"},
638
+ {"box", "application/vnd.previewsystems.box"},
639
+ {"boz", "application/x-bzip2"},
640
+ {"bpk", "application/octet-stream"},
641
+ {"btif", "image/prs.btif"},
642
+ {"bz", "application/x-bzip"},
643
+ {"bz2", "application/x-bzip2"},
644
+ {"c", "text/x-c"},
645
+ {"c11amc", "application/vnd.cluetrust.cartomobile-config"},
646
+ {"c11amz", "application/vnd.cluetrust.cartomobile-config-pkg"},
647
+ {"c4d", "application/vnd.clonk.c4group"},
648
+ {"c4f", "application/vnd.clonk.c4group"},
649
+ {"c4g", "application/vnd.clonk.c4group"},
650
+ {"c4p", "application/vnd.clonk.c4group"},
651
+ {"c4u", "application/vnd.clonk.c4group"},
652
+ {"cab", "application/vnd.ms-cab-compressed"},
653
+ {"caf", "audio/x-caf"},
654
+ {"cap", "application/vnd.tcpdump.pcap"},
655
+ {"car", "application/vnd.curl.car"},
656
+ {"cat", "application/vnd.ms-pki.seccat"},
657
+ {"cb7", "application/x-cbr"},
658
+ {"cba", "application/x-cbr"},
659
+ {"cbr", "application/x-cbr"},
660
+ {"cbt", "application/x-cbr"},
661
+ {"cbz", "application/x-cbr"},
662
+ {"cc", "text/x-c"},
663
+ {"cct", "application/x-director"},
664
+ {"ccxml", "application/ccxml+xml"},
665
+ {"cdbcmsg", "application/vnd.contact.cmsg"},
666
+ {"cdf", "application/x-netcdf"},
667
+ {"cdkey", "application/vnd.mediastation.cdkey"},
668
+ {"cdmia", "application/cdmi-capability"},
669
+ {"cdmic", "application/cdmi-container"},
670
+ {"cdmid", "application/cdmi-domain"},
671
+ {"cdmio", "application/cdmi-object"},
672
+ {"cdmiq", "application/cdmi-queue"},
673
+ {"cdx", "chemical/x-cdx"},
674
+ {"cdxml", "application/vnd.chemdraw+xml"},
675
+ {"cdy", "application/vnd.cinderella"},
676
+ {"cer", "application/pkix-cert"},
677
+ {"cfs", "application/x-cfs-compressed"},
678
+ {"cgm", "image/cgm"},
679
+ {"chat", "application/x-chat"},
680
+ {"chm", "application/vnd.ms-htmlhelp"},
681
+ {"chrt", "application/vnd.kde.kchart"},
682
+ {"cif", "chemical/x-cif"},
683
+ {"cii", "application/vnd.anser-web-certificate-issue-initiation"},
684
+ {"cil", "application/vnd.ms-artgalry"},
685
+ {"cla", "application/vnd.claymore"},
686
+ {"class", "application/java-vm"},
687
+ {"clkk", "application/vnd.crick.clicker.keyboard"},
688
+ {"clkp", "application/vnd.crick.clicker.palette"},
689
+ {"clkt", "application/vnd.crick.clicker.template"},
690
+ {"clkw", "application/vnd.crick.clicker.wordbank"},
691
+ {"clkx", "application/vnd.crick.clicker"},
692
+ {"clp", "application/x-msclip"},
693
+ {"cmc", "application/vnd.cosmocaller"},
694
+ {"cmdf", "chemical/x-cmdf"},
695
+ {"cml", "chemical/x-cml"},
696
+ {"cmp", "application/vnd.yellowriver-custom-menu"},
697
+ {"cmx", "image/x-cmx"},
698
+ {"cod", "application/vnd.rim.cod"},
699
+ {"com", "application/x-msdownload"},
700
+ {"conf", "text/plain"},
701
+ {"cpio", "application/x-cpio"},
702
+ {"cpp", "text/x-c"},
703
+ {"cpt", "application/mac-compactpro"},
704
+ {"crd", "application/x-mscardfile"},
705
+ {"crl", "application/pkix-crl"},
706
+ {"crt", "application/x-x509-ca-cert"},
707
+ {"cryptonote", "application/vnd.rig.cryptonote"},
708
+ {"csh", "application/x-csh"},
709
+ {"csml", "chemical/x-csml"},
710
+ {"csp", "application/vnd.commonspace"},
711
+ {"css", "text/css"},
712
+ {"cst", "application/x-director"},
713
+ {"csv", "text/csv"},
714
+ {"cu", "application/cu-seeme"},
715
+ {"curl", "text/vnd.curl"},
716
+ {"cww", "application/prs.cww"},
717
+ {"cxt", "application/x-director"},
718
+ {"cxx", "text/x-c"},
719
+ {"dae", "model/vnd.collada+xml"},
720
+ {"daf", "application/vnd.mobius.daf"},
721
+ {"dart", "application/vnd.dart"},
722
+ {"dataless", "application/vnd.fdsn.seed"},
723
+ {"davmount", "application/davmount+xml"},
724
+ {"dbk", "application/docbook+xml"},
725
+ {"dcr", "application/x-director"},
726
+ {"dcurl", "text/vnd.curl.dcurl"},
727
+ {"dd2", "application/vnd.oma.dd2+xml"},
728
+ {"ddd", "application/vnd.fujixerox.ddd"},
729
+ {"deb", "application/x-debian-package"},
730
+ {"def", "text/plain"},
731
+ {"deploy", "application/octet-stream"},
732
+ {"der", "application/x-x509-ca-cert"},
733
+ {"dfac", "application/vnd.dreamfactory"},
734
+ {"dgc", "application/x-dgc-compressed"},
735
+ {"dic", "text/x-c"},
736
+ {"dir", "application/x-director"},
737
+ {"dis", "application/vnd.mobius.dis"},
738
+ {"dist", "application/octet-stream"},
739
+ {"distz", "application/octet-stream"},
740
+ {"djv", "image/vnd.djvu"},
741
+ {"djvu", "image/vnd.djvu"},
742
+ {"dll", "application/x-msdownload"},
743
+ {"dmg", "application/x-apple-diskimage"},
744
+ {"dmp", "application/vnd.tcpdump.pcap"},
745
+ {"dms", "application/octet-stream"},
746
+ {"dna", "application/vnd.dna"},
747
+ {"doc", "application/msword"},
748
+ {"docm", "application/vnd.ms-word.document.macroenabled.12"},
749
+ {"docx", "application/"
750
+ "vnd.openxmlformats-officedocument.wordprocessingml.document"},
751
+ {"dot", "application/msword"},
752
+ {"dotm", "application/vnd.ms-word.template.macroenabled.12"},
753
+ {"dotx", "application/"
754
+ "vnd.openxmlformats-officedocument.wordprocessingml.template"},
755
+ {"dp", "application/vnd.osgi.dp"},
756
+ {"dpg", "application/vnd.dpgraph"},
757
+ {"dra", "audio/vnd.dra"},
758
+ {"dsc", "text/prs.lines.tag"},
759
+ {"dssc", "application/dssc+der"},
760
+ {"dtb", "application/x-dtbook+xml"},
761
+ {"dtd", "application/xml-dtd"},
762
+ {"dts", "audio/vnd.dts"},
763
+ {"dtshd", "audio/vnd.dts.hd"},
764
+ {"dump", "application/octet-stream"},
765
+ {"dvb", "video/vnd.dvb.file"},
766
+ {"dvi", "application/x-dvi"},
767
+ {"dwf", "model/vnd.dwf"},
768
+ {"dwg", "image/vnd.dwg"},
769
+ {"dxf", "image/vnd.dxf"},
770
+ {"dxp", "application/vnd.spotfire.dxp"},
771
+ {"dxr", "application/x-director"},
772
+ {"ecelp4800", "audio/vnd.nuera.ecelp4800"},
773
+ {"ecelp7470", "audio/vnd.nuera.ecelp7470"},
774
+ {"ecelp9600", "audio/vnd.nuera.ecelp9600"},
775
+ {"ecma", "application/ecmascript"},
776
+ {"edm", "application/vnd.novadigm.edm"},
777
+ {"edx", "application/vnd.novadigm.edx"},
778
+ {"efif", "application/vnd.picsel"},
779
+ {"ei6", "application/vnd.pg.osasli"},
780
+ {"elc", "application/octet-stream"},
781
+ {"emf", "application/x-msmetafile"},
782
+ {"eml", "message/rfc822"},
783
+ {"emma", "application/emma+xml"},
784
+ {"emz", "application/x-msmetafile"},
785
+ {"eol", "audio/vnd.digital-winds"},
786
+ {"eot", "application/vnd.ms-fontobject"},
787
+ {"eps", "application/postscript"},
788
+ {"epub", "application/epub+zip"},
789
+ {"es3", "application/vnd.eszigno3+xml"},
790
+ {"esa", "application/vnd.osgi.subsystem"},
791
+ {"esf", "application/vnd.epson.esf"},
792
+ {"et3", "application/vnd.eszigno3+xml"},
793
+ {"etx", "text/x-setext"},
794
+ {"eva", "application/x-eva"},
795
+ {"evy", "application/x-envoy"},
796
+ {"exe", "application/x-msdownload"},
797
+ {"exi", "application/exi"},
798
+ {"ext", "application/vnd.novadigm.ext"},
799
+ {"ez", "application/andrew-inset"},
800
+ {"ez2", "application/vnd.ezpix-album"},
801
+ {"ez3", "application/vnd.ezpix-package"},
802
+ {"f", "text/x-fortran"},
803
+ {"f4v", "video/x-f4v"},
804
+ {"f77", "text/x-fortran"},
805
+ {"f90", "text/x-fortran"},
806
+ {"fbs", "image/vnd.fastbidsheet"},
807
+ {"fcdt", "application/vnd.adobe.formscentral.fcdt"},
808
+ {"fcs", "application/vnd.isac.fcs"},
809
+ {"fdf", "application/vnd.fdf"},
810
+ {"fe_launch", "application/vnd.denovo.fcselayout-link"},
811
+ {"fg5", "application/vnd.fujitsu.oasysgp"},
812
+ {"fgd", "application/x-director"},
813
+ {"fh", "image/x-freehand"},
814
+ {"fh4", "image/x-freehand"},
815
+ {"fh5", "image/x-freehand"},
816
+ {"fh7", "image/x-freehand"},
817
+ {"fhc", "image/x-freehand"},
818
+ {"fig", "application/x-xfig"},
819
+ {"flac", "audio/x-flac"},
820
+ {"fli", "video/x-fli"},
821
+ {"flo", "application/vnd.micrografx.flo"},
822
+ {"flv", "video/x-flv"},
823
+ {"flw", "application/vnd.kde.kivio"},
824
+ {"flx", "text/vnd.fmi.flexstor"},
825
+ {"fly", "text/vnd.fly"},
826
+ {"fm", "application/vnd.framemaker"},
827
+ {"fnc", "application/vnd.frogans.fnc"},
828
+ {"for", "text/x-fortran"},
829
+ {"fpx", "image/vnd.fpx"},
830
+ {"frame", "application/vnd.framemaker"},
831
+ {"fsc", "application/vnd.fsc.weblaunch"},
832
+ {"fst", "image/vnd.fst"},
833
+ {"ftc", "application/vnd.fluxtime.clip"},
834
+ {"fti", "application/vnd.anser-web-funds-transfer-initiation"},
835
+ {"fvt", "video/vnd.fvt"},
836
+ {"fxp", "application/vnd.adobe.fxp"},
837
+ {"fxpl", "application/vnd.adobe.fxp"},
838
+ {"fzs", "application/vnd.fuzzysheet"},
839
+ {"g2w", "application/vnd.geoplan"},
840
+ {"g3", "image/g3fax"},
841
+ {"g3w", "application/vnd.geospace"},
842
+ {"gac", "application/vnd.groove-account"},
843
+ {"gam", "application/x-tads"},
844
+ {"gbr", "application/rpki-ghostbusters"},
845
+ {"gca", "application/x-gca-compressed"},
846
+ {"gdl", "model/vnd.gdl"},
847
+ {"geo", "application/vnd.dynageo"},
848
+ {"gex", "application/vnd.geometry-explorer"},
849
+ {"ggb", "application/vnd.geogebra.file"},
850
+ {"ggt", "application/vnd.geogebra.tool"},
851
+ {"ghf", "application/vnd.groove-help"},
852
+ {"gif", "image/gif"},
853
+ {"gim", "application/vnd.groove-identity-message"},
854
+ {"gml", "application/gml+xml"},
855
+ {"gmx", "application/vnd.gmx"},
856
+ {"gnumeric", "application/x-gnumeric"},
857
+ {"gph", "application/vnd.flographit"},
858
+ {"gpx", "application/gpx+xml"},
859
+ {"gqf", "application/vnd.grafeq"},
860
+ {"gqs", "application/vnd.grafeq"},
861
+ {"gram", "application/srgs"},
862
+ {"gramps", "application/x-gramps-xml"},
863
+ {"gre", "application/vnd.geometry-explorer"},
864
+ {"grv", "application/vnd.groove-injector"},
865
+ {"grxml", "application/srgs+xml"},
866
+ {"gsf", "application/x-font-ghostscript"},
867
+ {"gtar", "application/x-gtar"},
868
+ {"gtm", "application/vnd.groove-tool-message"},
869
+ {"gtw", "model/vnd.gtw"},
870
+ {"gv", "text/vnd.graphviz"},
871
+ {"gxf", "application/gxf"},
872
+ {"gxt", "application/vnd.geonext"},
873
+ {"h", "text/x-c"},
874
+ {"h261", "video/h261"},
875
+ {"h263", "video/h263"},
876
+ {"h264", "video/h264"},
877
+ {"hal", "application/vnd.hal+xml"},
878
+ {"hbci", "application/vnd.hbci"},
879
+ {"hdf", "application/x-hdf"},
880
+ {"hh", "text/x-c"},
881
+ {"hlp", "application/winhlp"},
882
+ {"hpgl", "application/vnd.hp-hpgl"},
883
+ {"hpid", "application/vnd.hp-hpid"},
884
+ {"hps", "application/vnd.hp-hps"},
885
+ {"hqx", "application/mac-binhex40"},
886
+ {"htke", "application/vnd.kenameaapp"},
887
+ {"htm", "text/html"},
888
+ {"html", "text/html"},
889
+ {"hvd", "application/vnd.yamaha.hv-dic"},
890
+ {"hvp", "application/vnd.yamaha.hv-voice"},
891
+ {"hvs", "application/vnd.yamaha.hv-script"},
892
+ {"i2g", "application/vnd.intergeo"},
893
+ {"icc", "application/vnd.iccprofile"},
894
+ {"ice", "x-conference/x-cooltalk"},
895
+ {"icm", "application/vnd.iccprofile"},
896
+ {"ico", "image/x-icon"},
897
+ {"ics", "text/calendar"},
898
+ {"ief", "image/ief"},
899
+ {"ifb", "text/calendar"},
900
+ {"ifm", "application/vnd.shana.informed.formdata"},
901
+ {"iges", "model/iges"},
902
+ {"igl", "application/vnd.igloader"},
903
+ {"igm", "application/vnd.insors.igm"},
904
+ {"igs", "model/iges"},
905
+ {"igx", "application/vnd.micrografx.igx"},
906
+ {"iif", "application/vnd.shana.informed.interchange"},
907
+ {"imp", "application/vnd.accpac.simply.imp"},
908
+ {"ims", "application/vnd.ms-ims"},
909
+ {"in", "text/plain"},
910
+ {"ink", "application/inkml+xml"},
911
+ {"inkml", "application/inkml+xml"},
912
+ {"install", "application/x-install-instructions"},
913
+ {"iota", "application/vnd.astraea-software.iota"},
914
+ {"ipfix", "application/ipfix"},
915
+ {"ipk", "application/vnd.shana.informed.package"},
916
+ {"irm", "application/vnd.ibm.rights-management"},
917
+ {"irp", "application/vnd.irepository.package+xml"},
918
+ {"iso", "application/x-iso9660-image"},
919
+ {"itp", "application/vnd.shana.informed.formtemplate"},
920
+ {"ivp", "application/vnd.immervision-ivp"},
921
+ {"ivu", "application/vnd.immervision-ivu"},
922
+ {"jad", "text/vnd.sun.j2me.app-descriptor"},
923
+ {"jam", "application/vnd.jam"},
924
+ {"jar", "application/java-archive"},
925
+ {"java", "text/x-java-source"},
926
+ {"jisp", "application/vnd.jisp"},
927
+ {"jlt", "application/vnd.hp-jlyt"},
928
+ {"jnlp", "application/x-java-jnlp-file"},
929
+ {"joda", "application/vnd.joost.joda-archive"},
930
+ {"jpe", "image/jpeg"},
931
+ {"jpeg", "image/jpeg"},
932
+ {"jpg", "image/jpeg"},
933
+ {"jpgm", "video/jpm"},
934
+ {"jpgv", "video/jpeg"},
935
+ {"jpm", "video/jpm"},
936
+ {"js", "application/javascript"},
937
+ {"json", "application/json"},
938
+ {"jsonml", "application/jsonml+json"},
939
+ {"kar", "audio/midi"},
940
+ {"karbon", "application/vnd.kde.karbon"},
941
+ {"kfo", "application/vnd.kde.kformula"},
942
+ {"kia", "application/vnd.kidspiration"},
943
+ {"kml", "application/vnd.google-earth.kml+xml"},
944
+ {"kmz", "application/vnd.google-earth.kmz"},
945
+ {"kne", "application/vnd.kinar"},
946
+ {"knp", "application/vnd.kinar"},
947
+ {"kon", "application/vnd.kde.kontour"},
948
+ {"kpr", "application/vnd.kde.kpresenter"},
949
+ {"kpt", "application/vnd.kde.kpresenter"},
950
+ {"kpxx", "application/vnd.ds-keypoint"},
951
+ {"ksp", "application/vnd.kde.kspread"},
952
+ {"ktr", "application/vnd.kahootz"},
953
+ {"ktx", "image/ktx"},
954
+ {"ktz", "application/vnd.kahootz"},
955
+ {"kwd", "application/vnd.kde.kword"},
956
+ {"kwt", "application/vnd.kde.kword"},
957
+ {"lasxml", "application/vnd.las.las+xml"},
958
+ {"latex", "application/x-latex"},
959
+ {"lbd", "application/vnd.llamagraphics.life-balance.desktop"},
960
+ {"lbe", "application/vnd.llamagraphics.life-balance.exchange+xml"},
961
+ {"les", "application/vnd.hhe.lesson-player"},
962
+ {"lha", "application/x-lzh-compressed"},
963
+ {"link66", "application/vnd.route66.link66+xml"},
964
+ {"list", "text/plain"},
965
+ {"list3820", "application/vnd.ibm.modcap"},
966
+ {"listafp", "application/vnd.ibm.modcap"},
967
+ {"lnk", "application/x-ms-shortcut"},
968
+ {"log", "text/plain"},
969
+ {"lostxml", "application/lost+xml"},
970
+ {"lrf", "application/octet-stream"},
971
+ {"lrm", "application/vnd.ms-lrm"},
972
+ {"ltf", "application/vnd.frogans.ltf"},
973
+ {"lvp", "audio/vnd.lucent.voice"},
974
+ {"lwp", "application/vnd.lotus-wordpro"},
975
+ {"lzh", "application/x-lzh-compressed"},
976
+ {"m13", "application/x-msmediaview"},
977
+ {"m14", "application/x-msmediaview"},
978
+ {"m1v", "video/mpeg"},
979
+ {"m21", "application/mp21"},
980
+ {"m2a", "audio/mpeg"},
981
+ {"m2v", "video/mpeg"},
982
+ {"m3a", "audio/mpeg"},
983
+ {"m3u", "audio/x-mpegurl"},
984
+ {"m3u8", "application/vnd.apple.mpegurl"},
985
+ {"m4a", "audio/mp4"},
986
+ {"m4u", "video/vnd.mpegurl"},
987
+ {"m4v", "video/x-m4v"},
988
+ {"ma", "application/mathematica"},
989
+ {"mads", "application/mads+xml"},
990
+ {"mag", "application/vnd.ecowin.chart"},
991
+ {"maker", "application/vnd.framemaker"},
992
+ {"man", "text/troff"},
993
+ {"mar", "application/octet-stream"},
994
+ {"mathml", "application/mathml+xml"},
995
+ {"mb", "application/mathematica"},
996
+ {"mbk", "application/vnd.mobius.mbk"},
997
+ {"mbox", "application/mbox"},
998
+ {"mc1", "application/vnd.medcalcdata"},
999
+ {"mcd", "application/vnd.mcd"},
1000
+ {"mcurl", "text/vnd.curl.mcurl"},
1001
+ {"mdb", "application/x-msaccess"},
1002
+ {"mdi", "image/vnd.ms-modi"},
1003
+ {"me", "text/troff"},
1004
+ {"mesh", "model/mesh"},
1005
+ {"meta4", "application/metalink4+xml"},
1006
+ {"metalink", "application/metalink+xml"},
1007
+ {"mets", "application/mets+xml"},
1008
+ {"mfm", "application/vnd.mfmp"},
1009
+ {"mft", "application/rpki-manifest"},
1010
+ {"mgp", "application/vnd.osgeo.mapguide.package"},
1011
+ {"mgz", "application/vnd.proteus.magazine"},
1012
+ {"mid", "audio/midi"},
1013
+ {"midi", "audio/midi"},
1014
+ {"mie", "application/x-mie"},
1015
+ {"mif", "application/vnd.mif"},
1016
+ {"mime", "message/rfc822"},
1017
+ {"mj2", "video/mj2"},
1018
+ {"mjp2", "video/mj2"},
1019
+ {"mk3d", "video/x-matroska"},
1020
+ {"mka", "audio/x-matroska"},
1021
+ {"mks", "video/x-matroska"},
1022
+ {"mkv", "video/x-matroska"},
1023
+ {"mlp", "application/vnd.dolby.mlp"},
1024
+ {"mmd", "application/vnd.chipnuts.karaoke-mmd"},
1025
+ {"mmf", "application/vnd.smaf"},
1026
+ {"mmr", "image/vnd.fujixerox.edmics-mmr"},
1027
+ {"mng", "video/x-mng"},
1028
+ {"mny", "application/x-msmoney"},
1029
+ {"mobi", "application/x-mobipocket-ebook"},
1030
+ {"mods", "application/mods+xml"},
1031
+ {"mov", "video/quicktime"},
1032
+ {"movie", "video/x-sgi-movie"},
1033
+ {"mp2", "audio/mpeg"},
1034
+ {"mp21", "application/mp21"},
1035
+ {"mp2a", "audio/mpeg"},
1036
+ {"mp3", "audio/mpeg"},
1037
+ {"mp4", "video/mp4"},
1038
+ {"mp4a", "audio/mp4"},
1039
+ {"mp4s", "application/mp4"},
1040
+ {"mp4v", "video/mp4"},
1041
+ {"mpc", "application/vnd.mophun.certificate"},
1042
+ {"mpe", "video/mpeg"},
1043
+ {"mpeg", "video/mpeg"},
1044
+ {"mpg", "video/mpeg"},
1045
+ {"mpg4", "video/mp4"},
1046
+ {"mpga", "audio/mpeg"},
1047
+ {"mpkg", "application/vnd.apple.installer+xml"},
1048
+ {"mpm", "application/vnd.blueice.multipass"},
1049
+ {"mpn", "application/vnd.mophun.application"},
1050
+ {"mpp", "application/vnd.ms-project"},
1051
+ {"mpt", "application/vnd.ms-project"},
1052
+ {"mpy", "application/vnd.ibm.minipay"},
1053
+ {"mqy", "application/vnd.mobius.mqy"},
1054
+ {"mrc", "application/marc"},
1055
+ {"mrcx", "application/marcxml+xml"},
1056
+ {"ms", "text/troff"},
1057
+ {"mscml", "application/mediaservercontrol+xml"},
1058
+ {"mseed", "application/vnd.fdsn.mseed"},
1059
+ {"mseq", "application/vnd.mseq"},
1060
+ {"msf", "application/vnd.epson.msf"},
1061
+ {"msh", "model/mesh"},
1062
+ {"msi", "application/x-msdownload"},
1063
+ {"msl", "application/vnd.mobius.msl"},
1064
+ {"msty", "application/vnd.muvee.style"},
1065
+ {"mts", "model/vnd.mts"},
1066
+ {"mus", "application/vnd.musician"},
1067
+ {"musicxml", "application/vnd.recordare.musicxml+xml"},
1068
+ {"mvb", "application/x-msmediaview"},
1069
+ {"mwf", "application/vnd.mfer"},
1070
+ {"mxf", "application/mxf"},
1071
+ {"mxl", "application/vnd.recordare.musicxml"},
1072
+ {"mxml", "application/xv+xml"},
1073
+ {"mxs", "application/vnd.triscape.mxs"},
1074
+ {"mxu", "video/vnd.mpegurl"},
1075
+ {"n-gage", "application/vnd.nokia.n-gage.symbian.install"},
1076
+ {"n3", "text/n3"},
1077
+ {"nb", "application/mathematica"},
1078
+ {"nbp", "application/vnd.wolfram.player"},
1079
+ {"nc", "application/x-netcdf"},
1080
+ {"ncx", "application/x-dtbncx+xml"},
1081
+ {"nfo", "text/x-nfo"},
1082
+ {"ngdat", "application/vnd.nokia.n-gage.data"},
1083
+ {"nitf", "application/vnd.nitf"},
1084
+ {"nlu", "application/vnd.neurolanguage.nlu"},
1085
+ {"nml", "application/vnd.enliven"},
1086
+ {"nnd", "application/vnd.noblenet-directory"},
1087
+ {"nns", "application/vnd.noblenet-sealer"},
1088
+ {"nnw", "application/vnd.noblenet-web"},
1089
+ {"npx", "image/vnd.net-fpx"},
1090
+ {"nsc", "application/x-conference"},
1091
+ {"nsf", "application/vnd.lotus-notes"},
1092
+ {"ntf", "application/vnd.nitf"},
1093
+ {"nzb", "application/x-nzb"},
1094
+ {"oa2", "application/vnd.fujitsu.oasys2"},
1095
+ {"oa3", "application/vnd.fujitsu.oasys3"},
1096
+ {"oas", "application/vnd.fujitsu.oasys"},
1097
+ {"obd", "application/x-msbinder"},
1098
+ {"obj", "application/x-tgif"},
1099
+ {"oda", "application/oda"},
1100
+ {"odb", "application/vnd.oasis.opendocument.database"},
1101
+ {"odc", "application/vnd.oasis.opendocument.chart"},
1102
+ {"odf", "application/vnd.oasis.opendocument.formula"},
1103
+ {"odft", "application/vnd.oasis.opendocument.formula-template"},
1104
+ {"odg", "application/vnd.oasis.opendocument.graphics"},
1105
+ {"odi", "application/vnd.oasis.opendocument.image"},
1106
+ {"odm", "application/vnd.oasis.opendocument.text-master"},
1107
+ {"odp", "application/vnd.oasis.opendocument.presentation"},
1108
+ {"ods", "application/vnd.oasis.opendocument.spreadsheet"},
1109
+ {"odt", "application/vnd.oasis.opendocument.text"},
1110
+ {"oga", "audio/ogg"},
1111
+ {"ogg", "audio/ogg"},
1112
+ {"ogv", "video/ogg"},
1113
+ {"ogx", "application/ogg"},
1114
+ {"omdoc", "application/omdoc+xml"},
1115
+ {"onepkg", "application/onenote"},
1116
+ {"onetmp", "application/onenote"},
1117
+ {"onetoc", "application/onenote"},
1118
+ {"onetoc2", "application/onenote"},
1119
+ {"opf", "application/oebps-package+xml"},
1120
+ {"opml", "text/x-opml"},
1121
+ {"oprc", "application/vnd.palm"},
1122
+ {"org", "application/vnd.lotus-organizer"},
1123
+ {"osf", "application/vnd.yamaha.openscoreformat"},
1124
+ {"osfpvg", "application/vnd.yamaha.openscoreformat.osfpvg+xml"},
1125
+ {"otc", "application/vnd.oasis.opendocument.chart-template"},
1126
+ {"otf", "application/x-font-otf"},
1127
+ {"otg", "application/vnd.oasis.opendocument.graphics-template"},
1128
+ {"oth", "application/vnd.oasis.opendocument.text-web"},
1129
+ {"oti", "application/vnd.oasis.opendocument.image-template"},
1130
+ {"otp", "application/vnd.oasis.opendocument.presentation-template"},
1131
+ {"ots", "application/vnd.oasis.opendocument.spreadsheet-template"},
1132
+ {"ott", "application/vnd.oasis.opendocument.text-template"},
1133
+ {"oxps", "application/oxps"},
1134
+ {"oxt", "application/vnd.openofficeorg.extension"},
1135
+ {"p", "text/x-pascal"},
1136
+ {"p10", "application/pkcs10"},
1137
+ {"p12", "application/x-pkcs12"},
1138
+ {"p7b", "application/x-pkcs7-certificates"},
1139
+ {"p7c", "application/pkcs7-mime"},
1140
+ {"p7m", "application/pkcs7-mime"},
1141
+ {"p7r", "application/x-pkcs7-certreqresp"},
1142
+ {"p7s", "application/pkcs7-signature"},
1143
+ {"p8", "application/pkcs8"},
1144
+ {"pas", "text/x-pascal"},
1145
+ {"paw", "application/vnd.pawaafile"},
1146
+ {"pbd", "application/vnd.powerbuilder6"},
1147
+ {"pbm", "image/x-portable-bitmap"},
1148
+ {"pcap", "application/vnd.tcpdump.pcap"},
1149
+ {"pcf", "application/x-font-pcf"},
1150
+ {"pcl", "application/vnd.hp-pcl"},
1151
+ {"pclxl", "application/vnd.hp-pclxl"},
1152
+ {"pct", "image/x-pict"},
1153
+ {"pcurl", "application/vnd.curl.pcurl"},
1154
+ {"pcx", "image/x-pcx"},
1155
+ {"pdb", "application/vnd.palm"},
1156
+ {"pdf", "application/pdf"},
1157
+ {"pfa", "application/x-font-type1"},
1158
+ {"pfb", "application/x-font-type1"},
1159
+ {"pfm", "application/x-font-type1"},
1160
+ {"pfr", "application/font-tdpfr"},
1161
+ {"pfx", "application/x-pkcs12"},
1162
+ {"pgm", "image/x-portable-graymap"},
1163
+ {"pgn", "application/x-chess-pgn"},
1164
+ {"pgp", "application/pgp-encrypted"},
1165
+ {"pic", "image/x-pict"},
1166
+ {"pkg", "application/octet-stream"},
1167
+ {"pki", "application/pkixcmp"},
1168
+ {"pkipath", "application/pkix-pkipath"},
1169
+ {"plb", "application/vnd.3gpp.pic-bw-large"},
1170
+ {"plc", "application/vnd.mobius.plc"},
1171
+ {"plf", "application/vnd.pocketlearn"},
1172
+ {"pls", "application/pls+xml"},
1173
+ {"pml", "application/vnd.ctc-posml"},
1174
+ {"png", "image/png"},
1175
+ {"pnm", "image/x-portable-anymap"},
1176
+ {"portpkg", "application/vnd.macports.portpkg"},
1177
+ {"pot", "application/vnd.ms-powerpoint"},
1178
+ {"potm", "application/vnd.ms-powerpoint.template.macroenabled.12"},
1179
+ {"potx", "application/"
1180
+ "vnd.openxmlformats-officedocument.presentationml.template"},
1181
+ {"ppam", "application/vnd.ms-powerpoint.addin.macroenabled.12"},
1182
+ {"ppd", "application/vnd.cups-ppd"},
1183
+ {"ppm", "image/x-portable-pixmap"},
1184
+ {"pps", "application/vnd.ms-powerpoint"},
1185
+ {"ppsm", "application/vnd.ms-powerpoint.slideshow.macroenabled.12"},
1186
+ {"ppsx", "application/"
1187
+ "vnd.openxmlformats-officedocument.presentationml.slideshow"},
1188
+ {"ppt", "application/vnd.ms-powerpoint"},
1189
+ {"pptm", "application/vnd.ms-powerpoint.presentation.macroenabled.12"},
1190
+ {"pqa", "application/vnd.palm"},
1191
+ {"prc", "application/x-mobipocket-ebook"},
1192
+ {"pre", "application/vnd.lotus-freelance"},
1193
+ {"prf", "application/pics-rules"},
1194
+ {"ps", "application/postscript"},
1195
+ {"psb", "application/vnd.3gpp.pic-bw-small"},
1196
+ {"psd", "image/vnd.adobe.photoshop"},
1197
+ {"psf", "application/x-font-linux-psf"},
1198
+ {"pskcxml", "application/pskc+xml"},
1199
+ {"ptid", "application/vnd.pvi.ptid1"},
1200
+ {"pub", "application/x-mspublisher"},
1201
+ {"pvb", "application/vnd.3gpp.pic-bw-var"},
1202
+ {"pwn", "application/vnd.3m.post-it-notes"},
1203
+ {"pya", "audio/vnd.ms-playready.media.pya"},
1204
+ {"pyv", "video/vnd.ms-playready.media.pyv"},
1205
+ {"qam", "application/vnd.epson.quickanime"},
1206
+ {"qbo", "application/vnd.intu.qbo"},
1207
+ {"qfx", "application/vnd.intu.qfx"},
1208
+ {"qps", "application/vnd.publishare-delta-tree"},
1209
+ {"qt", "video/quicktime"},
1210
+ {"qwd", "application/vnd.quark.quarkxpress"},
1211
+ {"qwt", "application/vnd.quark.quarkxpress"},
1212
+ {"qxb", "application/vnd.quark.quarkxpress"},
1213
+ {"qxd", "application/vnd.quark.quarkxpress"},
1214
+ {"qxl", "application/vnd.quark.quarkxpress"},
1215
+ {"qxt", "application/vnd.quark.quarkxpress"},
1216
+ {"ra", "audio/x-pn-realaudio"},
1217
+ {"ram", "audio/x-pn-realaudio"},
1218
+ {"rar", "application/x-rar-compressed"},
1219
+ {"ras", "image/x-cmu-raster"},
1220
+ {"rcprofile", "application/vnd.ipunplugged.rcprofile"},
1221
+ {"rdf", "application/rdf+xml"},
1222
+ {"rdz", "application/vnd.data-vision.rdz"},
1223
+ {"rep", "application/vnd.businessobjects"},
1224
+ {"res", "application/x-dtbresource+xml"},
1225
+ {"rgb", "image/x-rgb"},
1226
+ {"rif", "application/reginfo+xml"},
1227
+ {"rip", "audio/vnd.rip"},
1228
+ {"ris", "application/x-research-info-systems"},
1229
+ {"rl", "application/resource-lists+xml"},
1230
+ {"rlc", "image/vnd.fujixerox.edmics-rlc"},
1231
+ {"rld", "application/resource-lists-diff+xml"},
1232
+ {"rm", "application/vnd.rn-realmedia"},
1233
+ {"rmi", "audio/midi"},
1234
+ {"rmp", "audio/x-pn-realaudio-plugin"},
1235
+ {"rms", "application/vnd.jcp.javame.midlet-rms"},
1236
+ {"rmvb", "application/vnd.rn-realmedia-vbr"},
1237
+ {"rnc", "application/relax-ng-compact-syntax"},
1238
+ {"roa", "application/rpki-roa"},
1239
+ {"roff", "text/troff"},
1240
+ {"rp9", "application/vnd.cloanto.rp9"},
1241
+ {"rpss", "application/vnd.nokia.radio-presets"},
1242
+ {"rpst", "application/vnd.nokia.radio-preset"},
1243
+ {"rq", "application/sparql-query"},
1244
+ {"rs", "application/rls-services+xml"},
1245
+ {"rsd", "application/rsd+xml"},
1246
+ {"rss", "application/rss+xml"},
1247
+ {"rtf", "application/rtf"},
1248
+ {"rtx", "text/richtext"},
1249
+ {"s", "text/x-asm"},
1250
+ {"s3m", "audio/s3m"},
1251
+ {"saf", "application/vnd.yamaha.smaf-audio"},
1252
+ {"sbml", "application/sbml+xml"},
1253
+ {"sc", "application/vnd.ibm.secure-container"},
1254
+ {"scd", "application/x-msschedule"},
1255
+ {"scm", "application/vnd.lotus-screencam"},
1256
+ {"scq", "application/scvp-cv-request"},
1257
+ {"scs", "application/scvp-cv-response"},
1258
+ {"scurl", "text/vnd.curl.scurl"},
1259
+ {"sda", "application/vnd.stardivision.draw"},
1260
+ {"sdc", "application/vnd.stardivision.calc"},
1261
+ {"sdd", "application/vnd.stardivision.impress"},
1262
+ {"sdkd", "application/vnd.solent.sdkm+xml"},
1263
+ {"sdkm", "application/vnd.solent.sdkm+xml"},
1264
+ {"sdp", "application/sdp"},
1265
+ {"sdw", "application/vnd.stardivision.writer"},
1266
+ {"see", "application/vnd.seemail"},
1267
+ {"seed", "application/vnd.fdsn.seed"},
1268
+ {"sema", "application/vnd.sema"},
1269
+ {"semd", "application/vnd.semd"},
1270
+ {"semf", "application/vnd.semf"},
1271
+ {"ser", "application/java-serialized-object"},
1272
+ {"setpay", "application/set-payment-initiation"},
1273
+ {"setreg", "application/set-registration-initiation"},
1274
+ {"sfd-hdstx", "application/vnd.hydrostatix.sof-data"},
1275
+ {"sfs", "application/vnd.spotfire.sfs"},
1276
+ {"sfv", "text/x-sfv"},
1277
+ {"sgi", "image/sgi"},
1278
+ {"sgl", "application/vnd.stardivision.writer-global"},
1279
+ {"sgm", "text/sgml"},
1280
+ {"sgml", "text/sgml"},
1281
+ {"sh", "application/x-sh"},
1282
+ {"shar", "application/x-shar"},
1283
+ {"shf", "application/shf+xml"},
1284
+ {"sid", "image/x-mrsid-image"},
1285
+ {"sig", "application/pgp-signature"},
1286
+ {"sil", "audio/silk"},
1287
+ {"silo", "model/mesh"},
1288
+ {"sis", "application/vnd.symbian.install"},
1289
+ {"sisx", "application/vnd.symbian.install"},
1290
+ {"sit", "application/x-stuffit"},
1291
+ {"sitx", "application/x-stuffitx"},
1292
+ {"skd", "application/vnd.koan"},
1293
+ {"skm", "application/vnd.koan"},
1294
+ {"skp", "application/vnd.koan"},
1295
+ {"skt", "application/vnd.koan"},
1296
+ {"sldm", "application/vnd.ms-powerpoint.slide.macroenabled.12"},
1297
+ {"sldx",
1298
+ "application/vnd.openxmlformats-officedocument.presentationml.slide"},
1299
+ {"slt", "application/vnd.epson.salt"},
1300
+ {"sm", "application/vnd.stepmania.stepchart"},
1301
+ {"smf", "application/vnd.stardivision.math"},
1302
+ {"smi", "application/smil+xml"},
1303
+ {"smil", "application/smil+xml"},
1304
+ {"smv", "video/x-smv"},
1305
+ {"smzip", "application/vnd.stepmania.package"},
1306
+ {"snd", "audio/basic"},
1307
+ {"snf", "application/x-font-snf"},
1308
+ {"so", "application/octet-stream"},
1309
+ {"spc", "application/x-pkcs7-certificates"},
1310
+ {"spf", "application/vnd.yamaha.smaf-phrase"},
1311
+ {"spl", "application/x-futuresplash"},
1312
+ {"spot", "text/vnd.in3d.spot"},
1313
+ {"spp", "application/scvp-vp-response"},
1314
+ {"spq", "application/scvp-vp-request"},
1315
+ {"spx", "audio/ogg"},
1316
+ {"sql", "application/x-sql"},
1317
+ {"src", "application/x-wais-source"},
1318
+ {"srt", "application/x-subrip"},
1319
+ {"sru", "application/sru+xml"},
1320
+ {"srx", "application/sparql-results+xml"},
1321
+ {"ssdl", "application/ssdl+xml"},
1322
+ {"sse", "application/vnd.kodak-descriptor"},
1323
+ {"ssf", "application/vnd.epson.ssf"},
1324
+ {"ssml", "application/ssml+xml"},
1325
+ {"st", "application/vnd.sailingtracker.track"},
1326
+ {"stc", "application/vnd.sun.xml.calc.template"},
1327
+ {"std", "application/vnd.sun.xml.draw.template"},
1328
+ {"stf", "application/vnd.wt.stf"},
1329
+ {"sti", "application/vnd.sun.xml.impress.template"},
1330
+ {"stk", "application/hyperstudio"},
1331
+ {"stl", "application/vnd.ms-pki.stl"},
1332
+ {"str", "application/vnd.pg.format"},
1333
+ {"stw", "application/vnd.sun.xml.writer.template"},
1334
+ {"sub", "image/vnd.dvb.subtitle"},
1335
+ {"sub", "text/vnd.dvb.subtitle"},
1336
+ {"sus", "application/vnd.sus-calendar"},
1337
+ {"susp", "application/vnd.sus-calendar"},
1338
+ {"sv4cpio", "application/x-sv4cpio"},
1339
+ {"sv4crc", "application/x-sv4crc"},
1340
+ {"svc", "application/vnd.dvb.service"},
1341
+ {"svd", "application/vnd.svd"},
1342
+ {"svg", "image/svg+xml"},
1343
+ {"svgz", "image/svg+xml"},
1344
+ {"swa", "application/x-director"},
1345
+ {"swf", "application/x-shockwave-flash"},
1346
+ {"swi", "application/vnd.aristanetworks.swi"},
1347
+ {"sxc", "application/vnd.sun.xml.calc"},
1348
+ {"sxd", "application/vnd.sun.xml.draw"},
1349
+ {"sxg", "application/vnd.sun.xml.writer.global"},
1350
+ {"sxi", "application/vnd.sun.xml.impress"},
1351
+ {"sxm", "application/vnd.sun.xml.math"},
1352
+ {"sxw", "application/vnd.sun.xml.writer"},
1353
+ {"t", "text/troff"},
1354
+ {"t3", "application/x-t3vm-image"},
1355
+ {"taglet", "application/vnd.mynfc"},
1356
+ {"tao", "application/vnd.tao.intent-module-archive"},
1357
+ {"tar", "application/x-tar"},
1358
+ {"tcap", "application/vnd.3gpp2.tcap"},
1359
+ {"tcl", "application/x-tcl"},
1360
+ {"teacher", "application/vnd.smart.teacher"},
1361
+ {"tei", "application/tei+xml"},
1362
+ {"teicorpus", "application/tei+xml"},
1363
+ {"tex", "application/x-tex"},
1364
+ {"texi", "application/x-texinfo"},
1365
+ {"texinfo", "application/x-texinfo"},
1366
+ {"text", "text/plain"},
1367
+ {"tfi", "application/thraud+xml"},
1368
+ {"tfm", "application/x-tex-tfm"},
1369
+ {"tga", "image/x-tga"},
1370
+ {"thmx", "application/vnd.ms-officetheme"},
1371
+ {"tif", "image/tiff"},
1372
+ {"tiff", "image/tiff"},
1373
+ {"tmo", "application/vnd.tmobile-livetv"},
1374
+ {"torrent", "application/x-bittorrent"},
1375
+ {"tpl", "application/vnd.groove-tool-template"},
1376
+ {"tpt", "application/vnd.trid.tpt"},
1377
+ {"tr", "text/troff"},
1378
+ {"tra", "application/vnd.trueapp"},
1379
+ {"trm", "application/x-msterminal"},
1380
+ {"tsd", "application/timestamped-data"},
1381
+ {"tsv", "text/tab-separated-values"},
1382
+ {"ttc", "application/x-font-ttf"},
1383
+ {"ttf", "application/x-font-ttf"},
1384
+ {"ttl", "text/turtle"},
1385
+ {"twd", "application/vnd.simtech-mindmapper"},
1386
+ {"twds", "application/vnd.simtech-mindmapper"},
1387
+ {"txd", "application/vnd.genomatix.tuxedo"},
1388
+ {"txf", "application/vnd.mobius.txf"},
1389
+ {"txt", "text/plain"},
1390
+ {"u32", "application/x-authorware-bin"},
1391
+ {"udeb", "application/x-debian-package"},
1392
+ {"ufd", "application/vnd.ufdl"},
1393
+ {"ufdl", "application/vnd.ufdl"},
1394
+ {"ulx", "application/x-glulx"},
1395
+ {"umj", "application/vnd.umajin"},
1396
+ {"unityweb", "application/vnd.unity"},
1397
+ {"uoml", "application/vnd.uoml+xml"},
1398
+ {"uri", "text/uri-list"},
1399
+ {"uris", "text/uri-list"},
1400
+ {"urls", "text/uri-list"},
1401
+ {"ustar", "application/x-ustar"},
1402
+ {"utz", "application/vnd.uiq.theme"},
1403
+ {"uu", "text/x-uuencode"},
1404
+ {"uva", "audio/vnd.dece.audio"},
1405
+ {"uvd", "application/vnd.dece.data"},
1406
+ {"uvf", "application/vnd.dece.data"},
1407
+ {"uvg", "image/vnd.dece.graphic"},
1408
+ {"uvh", "video/vnd.dece.hd"},
1409
+ {"uvi", "image/vnd.dece.graphic"},
1410
+ {"uvm", "video/vnd.dece.mobile"},
1411
+ {"uvp", "video/vnd.dece.pd"},
1412
+ {"uvs", "video/vnd.dece.sd"},
1413
+ {"uvt", "application/vnd.dece.ttml+xml"},
1414
+ {"uvu", "video/vnd.uvvu.mp4"},
1415
+ {"uvv", "video/vnd.dece.video"},
1416
+ {"uvva", "audio/vnd.dece.audio"},
1417
+ {"uvvd", "application/vnd.dece.data"},
1418
+ {"uvvf", "application/vnd.dece.data"},
1419
+ {"uvvg", "image/vnd.dece.graphic"},
1420
+ {"uvvh", "video/vnd.dece.hd"},
1421
+ {"uvvi", "image/vnd.dece.graphic"},
1422
+ {"uvvm", "video/vnd.dece.mobile"},
1423
+ {"uvvp", "video/vnd.dece.pd"},
1424
+ {"uvvs", "video/vnd.dece.sd"},
1425
+ {"uvvt", "application/vnd.dece.ttml+xml"},
1426
+ {"uvvu", "video/vnd.uvvu.mp4"},
1427
+ {"uvvv", "video/vnd.dece.video"},
1428
+ {"uvvx", "application/vnd.dece.unspecified"},
1429
+ {"uvvz", "application/vnd.dece.zip"},
1430
+ {"uvx", "application/vnd.dece.unspecified"},
1431
+ {"uvz", "application/vnd.dece.zip"},
1432
+ {"vcard", "text/vcard"},
1433
+ {"vcd", "application/x-cdlink"},
1434
+ {"vcf", "text/x-vcard"},
1435
+ {"vcg", "application/vnd.groove-vcard"},
1436
+ {"vcs", "text/x-vcalendar"},
1437
+ {"vcx", "application/vnd.vcx"},
1438
+ {"vis", "application/vnd.visionary"},
1439
+ {"viv", "video/vnd.vivo"},
1440
+ {"vob", "video/x-ms-vob"},
1441
+ {"vor", "application/vnd.stardivision.writer"},
1442
+ {"vox", "application/x-authorware-bin"},
1443
+ {"vrml", "model/vrml"},
1444
+ {"vsd", "application/vnd.visio"},
1445
+ {"vsf", "application/vnd.vsf"},
1446
+ {"vss", "application/vnd.visio"},
1447
+ {"vst", "application/vnd.visio"},
1448
+ {"vsw", "application/vnd.visio"},
1449
+ {"vtu", "model/vnd.vtu"},
1450
+ {"vxml", "application/voicexml+xml"},
1451
+ {"w3d", "application/x-director"},
1452
+ {"wad", "application/x-doom"},
1453
+ {"wav", "audio/x-wav"},
1454
+ {"wax", "audio/x-ms-wax"},
1455
+ {"wbmp", "image/vnd.wap.wbmp"},
1456
+ {"wbs", "application/vnd.criticaltools.wbs+xml"},
1457
+ {"wbxml", "application/vnd.wap.wbxml"},
1458
+ {"wcm", "application/vnd.ms-works"},
1459
+ {"wdb", "application/vnd.ms-works"},
1460
+ {"wdp", "image/vnd.ms-photo"},
1461
+ {"weba", "audio/webm"},
1462
+ {"webm", "video/webm"},
1463
+ {"webp", "image/webp"},
1464
+ {"wg", "application/vnd.pmi.widget"},
1465
+ {"wgt", "application/widget"},
1466
+ {"wks", "application/vnd.ms-works"},
1467
+ {"wm", "video/x-ms-wm"},
1468
+ {"wma", "audio/x-ms-wma"},
1469
+ {"wmd", "application/x-ms-wmd"},
1470
+ {"wmf", "application/x-msmetafile"},
1471
+ {"wml", "text/vnd.wap.wml"},
1472
+ {"wmlc", "application/vnd.wap.wmlc"},
1473
+ {"wmls", "text/vnd.wap.wmlscript"},
1474
+ {"wmlsc", "application/vnd.wap.wmlscriptc"},
1475
+ {"wmv", "video/x-ms-wmv"},
1476
+ {"wmx", "video/x-ms-wmx"},
1477
+ {"wmz", "application/x-ms-wmz"},
1478
+ {"wmz", "application/x-msmetafile"},
1479
+ {"woff", "application/font-woff"},
1480
+ {"wpd", "application/vnd.wordperfect"},
1481
+ {"wpl", "application/vnd.ms-wpl"},
1482
+ {"wps", "application/vnd.ms-works"},
1483
+ {"wqd", "application/vnd.wqd"},
1484
+ {"wri", "application/x-mswrite"},
1485
+ {"wrl", "model/vrml"},
1486
+ {"wsdl", "application/wsdl+xml"},
1487
+ {"wspolicy", "application/wspolicy+xml"},
1488
+ {"wtb", "application/vnd.webturbo"},
1489
+ {"wvx", "video/x-ms-wvx"},
1490
+ {"x32", "application/x-authorware-bin"},
1491
+ {"x3d", "model/x3d+xml"},
1492
+ {"x3db", "model/x3d+binary"},
1493
+ {"x3dbz", "model/x3d+binary"},
1494
+ {"x3dv", "model/x3d+vrml"},
1495
+ {"x3dvz", "model/x3d+vrml"},
1496
+ {"x3dz", "model/x3d+xml"},
1497
+ {"xaml", "application/xaml+xml"},
1498
+ {"xap", "application/x-silverlight-app"},
1499
+ {"xar", "application/vnd.xara"},
1500
+ {"xbap", "application/x-ms-xbap"},
1501
+ {"xbd", "application/vnd.fujixerox.docuworks.binder"},
1502
+ {"xbm", "image/x-xbitmap"},
1503
+ {"xdf", "application/xcap-diff+xml"},
1504
+ {"xdm", "application/vnd.syncml.dm+xml"},
1505
+ {"xdp", "application/vnd.adobe.xdp+xml"},
1506
+ {"xdssc", "application/dssc+xml"},
1507
+ {"xdw", "application/vnd.fujixerox.docuworks"},
1508
+ {"xenc", "application/xenc+xml"},
1509
+ {"xer", "application/patch-ops-error+xml"},
1510
+ {"xfdf", "application/vnd.adobe.xfdf"},
1511
+ {"xfdl", "application/vnd.xfdl"},
1512
+ {"xht", "application/xhtml+xml"},
1513
+ {"xhtml", "application/xhtml+xml"},
1514
+ {"xhvml", "application/xv+xml"},
1515
+ {"xif", "image/vnd.xiff"},
1516
+ {"xla", "application/vnd.ms-excel"},
1517
+ {"xlam", "application/vnd.ms-excel.addin.macroenabled.12"},
1518
+ {"xlc", "application/vnd.ms-excel"},
1519
+ {"xlf", "application/x-xliff+xml"},
1520
+ {"xlm", "application/vnd.ms-excel"},
1521
+ {"xls", "application/vnd.ms-excel"},
1522
+ {"xlsb", "application/vnd.ms-excel.sheet.binary.macroenabled.12"},
1523
+ {"xlsm", "application/vnd.ms-excel.sheet.macroenabled.12"},
1524
+ {"xlsx",
1525
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
1526
+ {"xlt", "application/vnd.ms-excel"},
1527
+ {"xltm", "application/vnd.ms-excel.template.macroenabled.12"},
1528
+ {"xltx", "application/"
1529
+ "vnd.openxmlformats-officedocument.spreadsheetml.template"},
1530
+ {"xlw", "application/vnd.ms-excel"},
1531
+ {"xm", "audio/xm"},
1532
+ {"xml", "application/xml"},
1533
+ {"xo", "application/vnd.olpc-sugar"},
1534
+ {"xop", "application/xop+xml"},
1535
+ {"xpi", "application/x-xpinstall"},
1536
+ {"xpl", "application/xproc+xml"},
1537
+ {"xpm", "image/x-xpixmap"},
1538
+ {"xpr", "application/vnd.is-xpr"},
1539
+ {"xps", "application/vnd.ms-xpsdocument"},
1540
+ {"xpw", "application/vnd.intercon.formnet"},
1541
+ {"xpx", "application/vnd.intercon.formnet"},
1542
+ {"xsl", "application/xml"},
1543
+ {"xslt", "application/xslt+xml"},
1544
+ {"xsm", "application/vnd.syncml+xml"},
1545
+ {"xspf", "application/xspf+xml"},
1546
+ {"xul", "application/vnd.mozilla.xul+xml"},
1547
+ {"xvm", "application/xv+xml"},
1548
+ {"xvml", "application/xv+xml"},
1549
+ {"xwd", "image/x-xwindowdump"},
1550
+ {"xyz", "chemical/x-xyz"},
1551
+ {"xz", "application/x-xz"},
1552
+ {"yang", "application/yang"},
1553
+ {"yin", "application/yin+xml"},
1554
+ {"z1", "application/x-zmachine"},
1555
+ {"z2", "application/x-zmachine"},
1556
+ {"z3", "application/x-zmachine"},
1557
+ {"z4", "application/x-zmachine"},
1558
+ {"z5", "application/x-zmachine"},
1559
+ {"z6", "application/x-zmachine"},
1560
+ {"z7", "application/x-zmachine"},
1561
+ {"z8", "application/x-zmachine"},
1562
+ {"zaz", "application/vnd.zzazz.deck+xml"},
1563
+ {"zip", "application/zip"},
1564
+ {"zir", "application/vnd.zul"},
1565
+ {"zirz", "application/vnd.zul"},
1566
+ {"zmm", "application/vnd.handheld-entertainment+xml"},
1567
+ {{0}, NULL},
1568
+ };
1569
+ // Copy 8 bytes of the requested extension.
1570
+ // (8 byte comparison in enough to avoid collisions)
1571
+ uint64_t ext8byte = 0;
1572
+ char *extlow = (void *)(&ext8byte);
1573
+ // change the copy to lowercase
1574
+ size_t pos = 0;
1575
+ while (ext[pos] && pos < 8) {
1576
+ extlow[pos] =
1577
+ (ext[pos] >= 'A' && ext[pos] <= 'Z') ? (ext[pos] | 32) : ext[pos];
1578
+ ++pos;
1579
+ }
1580
+ // optimize starting position
1581
+ uint8_t start = (uint8_t)extlow[0];
1582
+ // skip unnecessary reviews
1583
+ if (start >= 'u')
1584
+ pos = 800;
1585
+ else if (start >= 'r')
1586
+ pos = 640;
1587
+ else if (start >= 'n')
1588
+ pos = 499;
1589
+ else if (start >= 'm')
1590
+ pos = 400;
1591
+ else if (start >= 'i')
1592
+ pos = 300;
1593
+ else if (start >= 'e')
1594
+ pos = 190;
1595
+ else
1596
+ pos = 0;
1597
+ // check for 8 byte comparison - should be fast on 64 bit systems.
1598
+ uint64_t *lstext;
1599
+ while (List[pos].ext[0]) {
1600
+ lstext = (uint64_t *)List[pos].ext;
1601
+ if (ext8byte == *lstext)
1602
+ return List[pos].mime;
1603
+ pos++;
1604
+ }
1605
+ return NULL;
1606
+ }