@@ -16,7 +16,7 @@ local errno = require 'errno'
1616local DETACHED = 101
1717
1818local function errorf (fmt , ...)
19- error (string.format (fmt , ... ))
19+ error (string.format (fmt , ... ), 3 )
2020end
2121
2222local function sprintf (fmt , ...)
@@ -35,6 +35,18 @@ local function is_callable(obj)
3535 return false
3636end
3737
38+ local function is_callable (obj )
39+ local t_obj = type (obj )
40+ if t_obj == ' function' then
41+ return true
42+ end
43+ if t_obj == ' table' then
44+ local mt = getmetatable (obj )
45+ return (type (mt ) == ' table' and type (mt .__call ) == ' function' )
46+ end
47+ return false
48+ end
49+
3850local function uri_escape (str )
3951 local res = {}
4052 if type (str ) == ' table' then
@@ -930,7 +942,7 @@ local function url_for_httpd(httpd, name, args, query)
930942 end
931943end
932944
933- local function httpd_http11_parse_request ( session , request_raw )
945+ local function httpd_parse_request ( request_raw )
934946 local request_parsed = lib ._parse_request (request_raw )
935947 if request_parsed .error then
936948 return nil , request_parsed .error
@@ -940,6 +952,14 @@ local function httpd_http11_parse_request(session, request_raw)
940952 request_parsed .path :find (" ./" , nil , true ) ~= nil then
941953 return nil , " invalid uri"
942954 end
955+ return request_parsed
956+ end
957+
958+ local function httpd_http11_parse_request (session , request_raw )
959+ local request_parsed , err = httpd_parse_request (request_raw )
960+ if not request_parsed then
961+ return nil , err
962+ end
943963 request_parsed .httpd = session .server
944964 request_parsed .s = session .socket
945965 request_parsed .peer = session .peer
@@ -976,6 +996,30 @@ local function httpd_http11_handler(session)
976996 return
977997 end
978998
999+ if p .headers [' upgrade' ] then
1000+ local proto_name = p .headers [' upgrade' ]:lower ()
1001+ local proto = session .server .upgrades [proto_name ]
1002+ if not proto then
1003+ session :write (' HTTP/1.1 400 Bad Request\r\n\r\n ' )
1004+ return false
1005+ else
1006+ local ok , upgrade_ok = pcall (proto .upgrade , session , p )
1007+ if not ok then
1008+ log .error (" Failed to upgrade to '%s': %s" , p .headers [' upgrade' ],
1009+ upgrade_ok )
1010+ session :write (' HTTP/1.1 500 Internal Error\r\n\r\n ' )
1011+ return false
1012+ elseif not upgrade_ok then
1013+ -- TODO: should we close connection, or we should retry again
1014+ return false
1015+ end
1016+
1017+ session .ctx .proto = proto .name
1018+ session .ctx .handler = proto .handler
1019+ return true
1020+ end
1021+ end
1022+
9791023 if p .headers [' expect' ] == ' 100-continue' then
9801024 session :write (' HTTP/1.1 100 Continue\r\n\r\n ' )
9811025 elseif p .headers [' expect' ] then
@@ -1180,14 +1224,36 @@ local function httpd_start(self)
11801224 return self
11811225end
11821226
1227+ local function httpd_register_extension (self , ext_type , opts )
1228+ if ext_type :lower () == ' upgrade' then
1229+ if not (type (opts ) == ' table' ) then
1230+ errorf (" Upgrade extension argument should be table" )
1231+ end
1232+ if not (type (opts .name ) == ' string' ) then
1233+ errorf (" Upgrade extension name should be %s" , ' options.name' , ' string' )
1234+ end
1235+ if not is_callable (opts .upgrade ) then
1236+ errorf (" Upgrade extension callback should be callable" )
1237+ end
1238+ if not is_callable (opts .handler ) then
1239+ errorf (" Upgrade extension handler should be callable" )
1240+ end
1241+
1242+ self .upgrades [opts .name :lower ()] = table .copy (opts )
1243+ else
1244+ errorf (' Unknown extension type: %s' , ext_type )
1245+ end
1246+ end
1247+
11831248local httpd_methods = {
1184- stop = httpd_stop ,
1185- start = httpd_start ,
1186- route = add_route ,
1187- match = match_route ,
1188- helper = set_helper ,
1189- hook = set_hook ,
1190- url_for = url_for_httpd ,
1249+ stop = httpd_stop ,
1250+ start = httpd_start ,
1251+ route = add_route ,
1252+ match = match_route ,
1253+ helper = set_helper ,
1254+ hook = set_hook ,
1255+ url_for = url_for_httpd ,
1256+ register_extension = httpd_register_extension ,
11911257}
11921258
11931259local httpd_mt = {
@@ -1223,10 +1289,11 @@ local function httpd_new(host, port, options)
12231289 is_run = false ,
12241290 options = options ,
12251291
1226- routes = { },
1227- iroutes = { },
1228- helpers = { url_for = url_for_helper , },
1229- hooks = { },
1292+ routes = { },
1293+ iroutes = { },
1294+ helpers = { url_for = url_for_helper , },
1295+ hooks = { },
1296+ upgrades = { },
12301297
12311298 -- caches
12321299 cache = { tpl = {}, ctx = {}, static = {}, },
@@ -1236,6 +1303,9 @@ local function httpd_new(host, port, options)
12361303end
12371304
12381305return {
1239- DETACHED = DETACHED ,
1240- new = httpd_new
1306+ DETACHED = DETACHED ,
1307+ new = httpd_new ,
1308+ parse_headers = httpd_parse_request ,
1309+ uri_escape = uri_escape ,
1310+ uri_unescape = uri_unescape ,
12411311}
0 commit comments