@@ -16,7 +16,7 @@ local errno = require 'errno'
16
16
local DETACHED = 101
17
17
18
18
local function errorf (fmt , ...)
19
- error (string.format (fmt , ... ))
19
+ error (string.format (fmt , ... ), 3 )
20
20
end
21
21
22
22
local function sprintf (fmt , ...)
@@ -28,11 +28,23 @@ local function assertf(ok, fmt, ...)
28
28
if select (' #' , ... ) > 0 then
29
29
fmt = tostring (fmt ):format (... )
30
30
end
31
- error (fmt , 2 )
31
+ error (fmt , 3 )
32
32
end
33
33
return ok
34
34
end
35
35
36
+ local function is_callable (obj )
37
+ local t_obj = type (obj )
38
+ if t_obj == ' function' then
39
+ return true
40
+ end
41
+ if t_obj == ' table' then
42
+ local mt = getmetatable (obj )
43
+ return (type (mt ) == ' table' and type (mt .__call ) == ' function' )
44
+ end
45
+ return false
46
+ end
47
+
36
48
local function uri_escape (str )
37
49
local res = {}
38
50
if type (str ) == ' table' then
@@ -924,7 +936,7 @@ local function url_for_httpd(httpd, name, args, query)
924
936
end
925
937
end
926
938
927
- local function httpd_http11_parse_request ( session , request_raw )
939
+ local function httpd_parse_request ( request_raw )
928
940
local request_parsed = lib ._parse_request (request_raw )
929
941
if request_parsed .error then
930
942
return nil , request_parsed .error
@@ -934,6 +946,14 @@ local function httpd_http11_parse_request(session, request_raw)
934
946
request_parsed .path :find (" ./" , nil , true ) ~= nil then
935
947
return nil , " invalid uri"
936
948
end
949
+ return request_parsed
950
+ end
951
+
952
+ local function httpd_http11_parse_request (session , request_raw )
953
+ local request_parsed , err = httpd_parse_request (request_raw )
954
+ if not request_parsed then
955
+ return nil , err
956
+ end
937
957
request_parsed .httpd = session .server
938
958
request_parsed .s = session .socket
939
959
request_parsed .peer = session .peer
@@ -970,6 +990,30 @@ local function httpd_http11_handler(session)
970
990
return
971
991
end
972
992
993
+ if p .headers [' upgrade' ] then
994
+ local proto_name = p .headers [' upgrade' ]:lower ()
995
+ local proto = session .server .upgrades [proto_name ]
996
+ if not proto then
997
+ session :write (' HTTP/1.1 400 Bad Request\r\n\r\n ' )
998
+ return false
999
+ else
1000
+ local ok , upgrade_ok = pcall (proto .upgrade , session , p )
1001
+ if not ok then
1002
+ log .error (" Failed to upgrade to '%s': %s" , p .headers [' upgrade' ],
1003
+ upgrade_ok )
1004
+ session :write (' HTTP/1.1 500 Internal Error\r\n\r\n ' )
1005
+ return false
1006
+ elseif not upgrade_ok then
1007
+ -- TODO: should we close connection, or we should retry again
1008
+ return false
1009
+ end
1010
+
1011
+ session .ctx .proto = proto .name
1012
+ session .ctx .handler = proto .handler
1013
+ return true
1014
+ end
1015
+ end
1016
+
973
1017
if p .headers [' expect' ] == ' 100-continue' then
974
1018
session :write (' HTTP/1.1 100 Continue\r\n\r\n ' )
975
1019
elseif p .headers [' expect' ] then
@@ -1174,14 +1218,32 @@ local function httpd_start(self)
1174
1218
return self
1175
1219
end
1176
1220
1221
+ local function httpd_register_extension (self , ext_type , opts )
1222
+ if ext_type :lower () == ' upgrade' then
1223
+ assertf (type (opts ) == ' table' ,
1224
+ " Upgrade extension argument should be table" )
1225
+ assertf (type (opts .name ) == ' string' ,
1226
+ " Upgrade extension name should be %s" , ' options.name' , ' string' )
1227
+ assertf (is_callable (opts .upgrade ),
1228
+ " Upgrade extension callback should be callable" )
1229
+ assertf (is_callable (opts .handler ),
1230
+ " Upgrade extension handler should be callable" )
1231
+
1232
+ self .upgrades [opts .name :lower ()] = table .copy (opts )
1233
+ else
1234
+ errorf (' Unknown extension type: %s' , ext_type )
1235
+ end
1236
+ end
1237
+
1177
1238
local httpd_methods = {
1178
- stop = httpd_stop ,
1179
- start = httpd_start ,
1180
- route = add_route ,
1181
- match = match_route ,
1182
- helper = set_helper ,
1183
- hook = set_hook ,
1184
- url_for = url_for_httpd ,
1239
+ stop = httpd_stop ,
1240
+ start = httpd_start ,
1241
+ route = add_route ,
1242
+ match = match_route ,
1243
+ helper = set_helper ,
1244
+ hook = set_hook ,
1245
+ url_for = url_for_httpd ,
1246
+ register_extension = httpd_register_extension ,
1185
1247
}
1186
1248
1187
1249
local httpd_mt = {
@@ -1216,10 +1278,11 @@ local function httpd_new(host, port, options)
1216
1278
is_run = false ,
1217
1279
options = options ,
1218
1280
1219
- routes = { },
1220
- iroutes = { },
1221
- helpers = { url_for = url_for_helper , },
1222
- hooks = { },
1281
+ routes = { },
1282
+ iroutes = { },
1283
+ helpers = { url_for = url_for_helper , },
1284
+ hooks = { },
1285
+ upgrades = { },
1223
1286
1224
1287
-- caches
1225
1288
cache = { tpl = {}, ctx = {}, static = {}, },
@@ -1229,6 +1292,9 @@ local function httpd_new(host, port, options)
1229
1292
end
1230
1293
1231
1294
return {
1232
- DETACHED = DETACHED ,
1233
- new = httpd_new
1295
+ DETACHED = DETACHED ,
1296
+ new = httpd_new ,
1297
+ parse_headers = httpd_parse_request ,
1298
+ uri_escape = uri_escape ,
1299
+ uri_unescape = uri_unescape ,
1234
1300
}
0 commit comments