From 58e7c2a350503c5163970f2dd7c56178bf6681ea Mon Sep 17 00:00:00 2001 From: Daniel Gruno Date: Sun, 26 Aug 2012 18:39:58 +0000 Subject: [PATCH] Add new directives, LuaInputFilter/LuaOutputFilter for creating content filters using Lua. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1377475 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 3 + docs/manual/mod/mod_lua.xml | 155 ++++++++++++ docs/manual/style/scripts/prettify.js | 2 +- modules/lua/lua_vmprep.h | 11 + modules/lua/mod_lua.c | 343 ++++++++++++++++++++++++++ modules/lua/mod_lua.h | 4 +- 6 files changed, 516 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index b7f8734e4e..fb085d0d01 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.0 + *) mod_lua: Add LuaInputFilter/LuaOutputFilter for creating content + filters in Lua [Daniel Gruno] + *) core: Apply length limit when logging Status header values. [Jeff Trawick, Chris Darroch] diff --git a/docs/manual/mod/mod_lua.xml b/docs/manual/mod/mod_lua.xml index 869b0a0e17..a9ddfb9abb 100644 --- a/docs/manual/mod/mod_lua.xml +++ b/docs/manual/mod/mod_lua.xml @@ -1186,4 +1186,159 @@ end + +LuaInputFilter +Provide a Lua function for content input filtering +LuaInputFilter filter_name /path/to/lua/script.lua function_name +server config +2.5.0 and later + + +

Provides a means of adding a Lua function as an input filter. +As with output filters, input filters work as coroutines, +first yielding before buffers are sent, then yielding whenever +a bucket needs to be passed down the chain, and finally (optionally) +yielding anything that needs to be appended to the input data. The +global variable bucket holds the buckets as they are passed +onto the Lua script: +

+ + +LuaInputFilter myInputFilter /www/filter.lua input_filter +<FilesMatch "\.lua> + SetInputFilter myInputFilter +</FilesMatch> + + +--[[ + Example input filter that converts all POST data to uppercase. +]]-- +function input_filter(r) + print("luaInputFilter called") -- debug print + coroutine.yield() -- Yield and wait for buckets + while bucket do -- For each bucket, do... + local output = string.upper(bucket) -- Convert all POST data to uppercase + coroutine.yield(output) -- Send converted data down the chain + end + -- No more buckets available. + coroutine.yield("&filterSignature=1234") -- Append signature at the end +end + +

+The input filter supports denying/skipping a filter if it is deemed unwanted: +

+ +function input_filter(r) + if not good then + return -- Simply deny filtering, passing on the original content instead + end + coroutine.yield() -- wait for buckets + ... -- insert filter stuff here +end + +

+See "Modifying contents with Lua +filters" for more information. +

+
+
+ + +LuaOutputFilter +Provide a Lua function for content output filtering +LuaOutputFilter filter_name /path/to/lua/script.lua function_name +server config +2.5.0 and later + + +

Provides a means of adding a Lua function as an output filter. +As with input filters, output filters work as coroutines, +first yielding before buffers are sent, then yielding whenever +a bucket needs to be passed down the chain, and finally (optionally) +yielding anything that needs to be appended to the input data. The +global variable bucket holds the buckets as they are passed +onto the Lua script: +

+ + +LuaOutputFilter myOutputFilter /www/filter.lua output_filter +<FilesMatch "\.lua> + SetOutputFilter myOutputFilter +</FilesMatch> + + +--[[ + Example output filter that escapes all HTML entities in the output +]]-- +function output_filter(r) + coroutine.yield("(Handled by myOutputFilter)<br/>\n") -- Prepend some data to the output, + -- yield and wait for buckets. + while bucket do -- For each bucket, do... + local output = r:escape_html(bucket) -- Escape all output + coroutine.yield(output) -- Send converted data down the chain + end + -- No more buckets available. +end + +

+As with the input filter, the output filter supports denying/skipping a filter +if it is deemed unwanted: +

+ +function output_filter(r) + if not r.content_type:match("text/html") then + return -- Simply deny filtering, passing on the original content instead + end + coroutine.yield() -- wait for buckets + ... -- insert filter stuff here +end + +

+See "Modifying contents with Lua filters" for more +information. +

+
+
+ +
+ Modifying contents with Lua filters +

+ Filter functions implemented via LuaInputFilter + or LuaOutputFilter are designed as + three-stage non-blocking functions using coroutines to suspend and resume a + function as buckets are sent down the filter chain. The core structure of + such a function is: +

+ +function filter(r) + -- Our first yield is to signal that we are ready to receive buckets. + -- Before this yield, we can set up our environment, check for conditions, + -- and, if we deem it necessary, decline filtering a request alltogether: + if something_bad then + return -- This would skip this filter. + end + -- Regardless of whether we have data to prepend, a yield MUST be called here. + -- Note that only output filters can prepend data. Input filters must use the + -- final stage to append data to the content. + coroutine.yield([optional header to be prepended to the content]) + + -- After we have yielded, buckets will be sent to us, one by one, and we can + -- do whatever we want with them and then pass on the result. + -- Buckets are stored in the global variable 'bucket', so we create a loop + -- that checks if 'bucket' is not nil: + while bucket ~= nil do + local output = mangle(bucket) -- Do some stuff to the content + coroutine.yield(output) -- Return our new content to the filter chain + end + + -- Once the buckets are gone, 'bucket' is set to nil, which will exit the + -- loop and land us here. Anything extra we want to append to the content + -- can be done by doing a final yield here. Both input and output filters + -- can append data to the content in this phase. + coroutine.yield([optional footer to be appended to the content]) +end + +
+ + diff --git a/docs/manual/style/scripts/prettify.js b/docs/manual/style/scripts/prettify.js index 49d134f652..57b6e5b245 100644 --- a/docs/manual/style/scripts/prettify.js +++ b/docs/manual/style/scripts/prettify.js @@ -132,7 +132,7 @@ var prettyPrint; var SH_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "case,done,elif,esac,eval,fi," + "function,in,local,set,then,until,echo"]; var CONFIG_ENVS = ["User-Agent,HTTP_USER_AGENT,HTTP_REFERER,HTTP_COOKIE,HTTP_FORWARDED,HTTP_HOST,HTTP_PROXY_CONNECTION,HTTP_ACCEPT,REMOTE_ADDR,REMOTE_HOST,REMOTE_PORT,REMOTE_USER,REMOTE_IDENT,REQUEST_METHOD,SCRIPT_FILENAME,PATH_INFO,QUERY_STRING,AUTH_TYPE,DOCUMENT_ROOT,SERVER_ADMIN,SERVER_NAME,SERVER_ADDR,SERVER_PORT,SERVER_PROTOCOL,SERVER_SOFTWARE,TIME_YEAR,TIME_MON,TIME_DAY,TIME_HOUR,TIME_MIN,TIME_SEC,TIME_WDAY,TIME,API_VERSION,THE_REQUEST,REQUEST_URI,REQUEST_FILENAME,IS_SUBREQ,HTTPS,REQUEST_SCHEME"]; - var CONFIG_KEYWORDS = ["AuthLDAPURL,AcceptFilter,AcceptPathInfo,AccessFileName,Action,AddAlt,AddAltByEncoding,AddAltByType,AddCharset,AddDefaultCharset,AddDescription,AddEncoding,AddHandler,AddIcon,AddIconByEncoding,AddIconByType,AddInputFilter,AddLanguage,AddModuleInfo,AddOutputFilter,AddOutputFilterByType,AddType,Alias,AliasMatch,Allow,AllowCONNECT,AllowEncodedSlashes,AllowMethods,AllowOverride,AllowOverrideList,Anonymous,Anonymous_LogEmail,Anonymous_MustGiveEmail,Anonymous_NoUserID,Anonymous_VerifyEmail,AsyncRequestWorkerFactor,AuthBasicAuthoritative,AuthBasicProvider,AuthDBDUserPWQuery,AuthDBDUserRealmQuery,AuthDBMGroupFile,AuthDBMType,AuthDBMUserFile,AuthDigestAlgorithm,AuthDigestDomain,AuthDigestNcCheck,AuthDigestNonceFormat,AuthDigestNonceLifetime,AuthDigestProvider,AuthDigestQop,AuthDigestShmemSize,AuthFormAuthoritative,AuthFormBody,AuthFormDisableNoStore,AuthFormFakeBasicAuth,AuthFormLocation,AuthFormLoginRequiredLocation,AuthFormLoginSuccessLocation,AuthFormLogoutLocation,AuthFormMethod,AuthFormMimetype,AuthFormPassword,AuthFormProvider,AuthFormSitePassphrase,AuthFormSize,AuthFormUsername,AuthGroupFile,AuthLDAPAuthorizePrefix,AuthLDAPBindAuthoritative,AuthLDAPBindDN,AuthLDAPBindPassword,AuthLDAPCharsetConfig,AuthLDAPCompareAsUser,AuthLDAPCompareDNOnServer,AuthLDAPDereferenceAliases,AuthLDAPGroupAttribute,AuthLDAPGroupAttributeIsDN,AuthLDAPInitialBindAsUser,AuthLDAPInitialBindPattern,AuthLDAPMaxSubGroupDepth,AuthLDAPRemoteUserAttribute,AuthLDAPRemoteUserIsDN,AuthLDAPSearchAsUser,AuthLDAPSubGroupAttribute,AuthLDAPSubGroupClass,AuthLDAPUrl,AuthMerging,AuthName,AuthnCacheContext,AuthnCacheEnable,AuthnCacheProvideFor,AuthnCacheSOCache,AuthnCacheTimeout,,AuthType,AuthUserFile,AuthzDBDLoginToReferer,AuthzDBDQuery,AuthzDBDRedirectQuery,AuthzDBMType,,AuthzSendForbiddenOnFailure,BalancerGrowth,BalancerMember,BrowserMatch,BrowserMatchNoCase,BufferedLogs,BufferSize,CacheDefaultExpire,CacheDetailHeader,CacheDirLength,CacheDirLevels,CacheDisable,CacheEnable,CacheFile,CacheHeader,CacheIgnoreCacheControl,CacheIgnoreHeaders,CacheIgnoreNoLastMod,CacheIgnoreQueryString,CacheIgnoreURLSessionIdentifiers,CacheKeyBaseURL,CacheLastModifiedFactor,CacheLock,CacheLockMaxAge,CacheLockPath,CacheMaxExpire,CacheMaxFileSize,CacheMinExpire,CacheMinFileSize,CacheNegotiatedDocs,CacheQuickHandler,CacheReadSize,CacheReadTime,CacheRoot,CacheStaleOnError,CacheStoreExpired,CacheStoreNoStore,CacheStorePrivate,CGIMapExtension,CharsetDefault,CharsetOptions,CharsetSourceEnc,CheckCaseOnly,CheckSpelling,ChrootDir,ContentDigest,CookieDomain,CookieExpires,CookieName,CookieStyle,CookieTracking,CoreDumpDirectory,CustomLog,Dav,DavDepthInfinity,DavGenericLockDB,DavLockDB,DavMinTimeout,DBDExptime,DBDInitSQL,DBDKeep,DBDMax,DBDMin,DBDParams,DBDPersist,DBDPrepareSQL,DBDriver,DefaultIcon,DefaultLanguage,DefaultRuntimeDir,DefaultType,Define,DeflateBufferSize,DeflateCompressionLevel,DeflateFilterNote,DeflateMemLevel,DeflateWindowSize,Deny,,DirectoryIndex,DirectoryIndexRedirect,,DirectorySlash,DocumentRoot,DTracePrivileges,DumpIOInput,DumpIOOutput,,,EnableExceptionHook,EnableMMAP,EnableSendfile,Error,ErrorDocument,ErrorLog,ErrorLogFormat,Example,ExpiresActive,ExpiresByType,ExpiresDefault,ExtendedStatus,ExtFilterDefine,ExtFilterOptions,FallbackResource,FileETag,,,FilterChain,FilterDeclare,FilterProtocol,FilterProvider,FilterTrace,ForceLanguagePriority,ForceType,ForensicLog,GprofDir,GracefulShutdownTimeout,Group,Header,HeaderName,HeartbeatAddress,HeartbeatListen,HeartbeatMaxServers,HeartbeatStorage,HeartbeatStorage,HostnameLookups,IdentityCheck,IdentityCheckTimeout,,,,,ImapBase,ImapDefault,ImapMenu,Include,IncludeOptional,IndexHeadInsert,IndexIgnore,IndexIgnoreReset,IndexOptions,IndexOrderDefault,IndexStyleSheet,InputSed,ISAPIAppendLogToErrors,ISAPIAppendLogToQuery,ISAPICacheFile,ISAPIFakeAsync,ISAPILogNotSupported,ISAPIReadAheadBuffer,KeepAlive,KeepAliveTimeout,KeptBodySize,LanguagePriority,LDAPCacheEntries,LDAPCacheTTL,LDAPConnectionPoolTTL,LDAPConnectionTimeout,LDAPLibraryDebug,LDAPOpCacheEntries,LDAPOpCacheTTL,LDAPReferralHopLimit,LDAPReferrals,LDAPRetries,LDAPRetryDelay,LDAPSharedCacheFile,LDAPSharedCacheSize,LDAPTimeout,LDAPTrustedClientCert,LDAPTrustedGlobalCert,LDAPTrustedMode,LDAPVerifyServerCert,,,LimitInternalRecursion,LimitRequestBody,LimitRequestFields,LimitRequestFieldSize,LimitRequestLine,LimitXMLRequestBody,Listen,ListenBackLog,LoadFile,LoadModule,,,LogFormat,LogLevel,LogMessage,LuaCodeCache,LuaHookAccessChecker,LuaHookAuthChecker,LuaAuthzProvider,LuaHookCheckUserID,LuaHookFixups,LuaHookInsertFilter,LuaHookMapToStorage,LuaHookTranslateName,LuaHookTypeChecker,LuaInherit,LuaMapHandler,LuaPackageCPath,LuaPackagePath,LuaQuickHandler,LuaRoot,LuaScope,MaxConnectionsPerChild,MaxKeepAliveRequests,MaxMemFree,MaxRangeOverlaps,MaxRangeReversals,MaxRanges,MaxRequestWorkers,MaxSpareServers,MaxSpareThreads,MaxThreads,MetaDir,MetaFiles,MetaSuffix,MimeMagicFile,MinSpareServers,MinSpareThreads,MMapFile,ModemStandard,ModMimeUsePathInfo,MultiviewsMatch,Mutex,NameVirtualHost,NoProxy,NWSSLTrustedCerts,NWSSLUpgradeable,Options,Order,OutputSed,PassEnv,PidFile,PrivilegesMode,Protocol,ProtocolEcho,,ProxyAddHeaders,ProxyBadHeader,ProxyBlock,ProxyDomain,ProxyErrorOverride,ProxyExpressDBMFile,ProxyExpressDBMType,ProxyExpressEnable,ProxyFtpDirCharset,ProxyFtpEscapeWildcards,ProxyFtpListOnWildcard,ProxyHTMLBufSize,ProxyHTMLCharsetOut,ProxyHTMLDocType,ProxyHTMLEnable,ProxyHTMLEvents,ProxyHTMLExtended,ProxyHTMLFixups,ProxyHTMLInterp,ProxyHTMLLinks,ProxyHTMLStripComments,ProxyHTMLURLMap,ProxyIOBufferSize,,ProxyMaxForwards,ProxyPass,ProxyPassInterpolateEnv,ProxyPassMatch,ProxyPassReverse,ProxyPassReverseCookieDomain,ProxyPassReverseCookiePath,ProxyPreserveHost,ProxyReceiveBufferSize,ProxyRemote,ProxyRemoteMatch,ProxyRequests,ProxySCGIInternalRedirect,ProxySCGISendfile,ProxySet,ProxySourceAddress,ProxyStatus,ProxyTimeout,ProxyVia,ReadmeName,ReceiveBufferSize,Redirect,RedirectMatch,RedirectPermanent,RedirectTemp,ReflectorHeader,RemoteIPHeader,RemoteIPInternalProxy,RemoteIPInternalProxyList,RemoteIPProxiesHeader,RemoteIPTrustedProxy,RemoteIPTrustedProxyList,RemoveCharset,RemoveEncoding,RemoveHandler,RemoveInputFilter,RemoveLanguage,RemoveOutputFilter,RemoveType,RequestHeader,RequestReadTimeout,Require,,,,RewriteBase,RewriteCond,RewriteEngine,RewriteMap,RewriteOptions,RewriteRule,RLimitCPU,RLimitMEM,RLimitNPROC,Satisfy,ScoreBoardFile,Script,ScriptAlias,ScriptAliasMatch,ScriptInterpreterSource,ScriptLog,ScriptLogBuffer,ScriptLogLength,ScriptSock,SecureListen,SeeRequestTail,SendBufferSize,ServerAdmin,ServerAlias,ServerLimit,ServerName,ServerPath,ServerRoot,ServerSignature,ServerTokens,Session,SessionCookieName,SessionCookieName2,SessionCookieRemove,SessionCryptoCipher,SessionCryptoDriver,SessionCryptoPassphrase,SessionCryptoPassphraseFile,SessionDBDCookieName,SessionDBDCookieName2,SessionDBDCookieRemove,SessionDBDDeleteLabel,SessionDBDInsertLabel,SessionDBDPerUser,SessionDBDSelectLabel,SessionDBDUpdateLabel,SessionEnv,SessionExclude,SessionHeader,SessionInclude,SessionMaxAge,SetEnv,SetEnvIf,SetEnvIfExpr,SetEnvIfNoCase,SetHandler,SetInputFilter,SetOutputFilter,SSIEndTag,SSIErrorMsg,SSIETag,SSILastModified,SSILegacyExprParser,SSIStartTag,SSITimeFormat,SSIUndefinedEcho,SSLCACertificateFile,SSLCACertificatePath,SSLCADNRequestFile,SSLCADNRequestPath,SSLCARevocationCheck,SSLCARevocationFile,SSLCARevocationPath,SSLCertificateChainFile,SSLCertificateFile,SSLCertificateKeyFile,SSLCipherSuite,SSLCryptoDevice,SSLEngine,SSLFIPS,SSLHonorCipherOrder,SSLInsecureRenegotiation,SSLOCSPDefaultResponder,SSLOCSPEnable,SSLOCSPOverrideResponder,SSLOCSPResponderTimeout,SSLOCSPResponseMaxAge,SSLOCSPResponseTimeSkew,SSLOptions,SSLPassPhraseDialog,SSLProtocol,SSLProxyCACertificateFile,SSLProxyCACertificatePath,SSLProxyCARevocationCheck,SSLProxyCARevocationFile,SSLProxyCARevocationPath,SSLProxyCheckPeerCN,SSLProxyCheckPeerExpire,SSLProxyCipherSuite,SSLProxyEngine,SSLProxyMachineCertificateChainFile,SSLProxyMachineCertificateFile,SSLProxyMachineCertificatePath,SSLProxyProtocol,SSLProxyVerify,SSLProxyVerifyDepth,SSLRandomSeed,SSLRenegBufferSize,SSLRequire,SSLRequireSSL,SSLSessionCache,SSLSessionCacheTimeout,SSLSessionTicketKeyFile,SSLStaplingCache,SSLStaplingErrorCacheTimeout,SSLStaplingFakeTryLater,SSLStaplingForceURL,SSLStaplingResponderTimeout,SSLStaplingResponseMaxAge,SSLStaplingResponseTimeSkew,SSLStaplingReturnResponderErrors,SSLStaplingStandardCacheTimeout,SSLStrictSNIVHostCheck,SSLUserName,SSLUseStapling,SSLVerifyClient,SSLVerifyDepth,StartServers,StartThreads,Substitute,Suexec,SuexecUserGroup,ThreadLimit,ThreadsPerChild,ThreadStackSize,TimeOut,TraceEnable,TransferLog,TypesConfig,UnDefine,UnsetEnv,UseCanonicalName,UseCanonicalPhysicalPort,User,UserDir,VHostCGIMode,VHostCGIPrivs,VHostGroup,VHostPrivs,VHostSecure,VHostUser,VirtualDocumentRoot,VirtualDocumentRootIP,,VirtualScriptAlias,VirtualScriptAliasIP,WatchdogInterval,XBitHack,xml2EncAlias,xml2EncDefault,xml2StartParse,RewriteLog,RewriteLogLevel"]; + var CONFIG_KEYWORDS = ["AuthLDAPURL,AcceptFilter,AcceptPathInfo,AccessFileName,Action,AddAlt,AddAltByEncoding,AddAltByType,AddCharset,AddDefaultCharset,AddDescription,AddEncoding,AddHandler,AddIcon,AddIconByEncoding,AddIconByType,AddInputFilter,AddLanguage,AddModuleInfo,AddOutputFilter,AddOutputFilterByType,AddType,Alias,AliasMatch,Allow,AllowCONNECT,AllowEncodedSlashes,AllowMethods,AllowOverride,AllowOverrideList,Anonymous,Anonymous_LogEmail,Anonymous_MustGiveEmail,Anonymous_NoUserID,Anonymous_VerifyEmail,AsyncRequestWorkerFactor,AuthBasicAuthoritative,AuthBasicProvider,AuthDBDUserPWQuery,AuthDBDUserRealmQuery,AuthDBMGroupFile,AuthDBMType,AuthDBMUserFile,AuthDigestAlgorithm,AuthDigestDomain,AuthDigestNcCheck,AuthDigestNonceFormat,AuthDigestNonceLifetime,AuthDigestProvider,AuthDigestQop,AuthDigestShmemSize,AuthFormAuthoritative,AuthFormBody,AuthFormDisableNoStore,AuthFormFakeBasicAuth,AuthFormLocation,AuthFormLoginRequiredLocation,AuthFormLoginSuccessLocation,AuthFormLogoutLocation,AuthFormMethod,AuthFormMimetype,AuthFormPassword,AuthFormProvider,AuthFormSitePassphrase,AuthFormSize,AuthFormUsername,AuthGroupFile,AuthLDAPAuthorizePrefix,AuthLDAPBindAuthoritative,AuthLDAPBindDN,AuthLDAPBindPassword,AuthLDAPCharsetConfig,AuthLDAPCompareAsUser,AuthLDAPCompareDNOnServer,AuthLDAPDereferenceAliases,AuthLDAPGroupAttribute,AuthLDAPGroupAttributeIsDN,AuthLDAPInitialBindAsUser,AuthLDAPInitialBindPattern,AuthLDAPMaxSubGroupDepth,AuthLDAPRemoteUserAttribute,AuthLDAPRemoteUserIsDN,AuthLDAPSearchAsUser,AuthLDAPSubGroupAttribute,AuthLDAPSubGroupClass,AuthLDAPUrl,AuthMerging,AuthName,AuthnCacheContext,AuthnCacheEnable,AuthnCacheProvideFor,AuthnCacheSOCache,AuthnCacheTimeout,,AuthType,AuthUserFile,AuthzDBDLoginToReferer,AuthzDBDQuery,AuthzDBDRedirectQuery,AuthzDBMType,,AuthzSendForbiddenOnFailure,BalancerGrowth,BalancerMember,BrowserMatch,BrowserMatchNoCase,BufferedLogs,BufferSize,CacheDefaultExpire,CacheDetailHeader,CacheDirLength,CacheDirLevels,CacheDisable,CacheEnable,CacheFile,CacheHeader,CacheIgnoreCacheControl,CacheIgnoreHeaders,CacheIgnoreNoLastMod,CacheIgnoreQueryString,CacheIgnoreURLSessionIdentifiers,CacheKeyBaseURL,CacheLastModifiedFactor,CacheLock,CacheLockMaxAge,CacheLockPath,CacheMaxExpire,CacheMaxFileSize,CacheMinExpire,CacheMinFileSize,CacheNegotiatedDocs,CacheQuickHandler,CacheReadSize,CacheReadTime,CacheRoot,CacheStaleOnError,CacheStoreExpired,CacheStoreNoStore,CacheStorePrivate,CGIMapExtension,CharsetDefault,CharsetOptions,CharsetSourceEnc,CheckCaseOnly,CheckSpelling,ChrootDir,ContentDigest,CookieDomain,CookieExpires,CookieName,CookieStyle,CookieTracking,CoreDumpDirectory,CustomLog,Dav,DavDepthInfinity,DavGenericLockDB,DavLockDB,DavMinTimeout,DBDExptime,DBDInitSQL,DBDKeep,DBDMax,DBDMin,DBDParams,DBDPersist,DBDPrepareSQL,DBDriver,DefaultIcon,DefaultLanguage,DefaultRuntimeDir,DefaultType,Define,DeflateBufferSize,DeflateCompressionLevel,DeflateFilterNote,DeflateMemLevel,DeflateWindowSize,Deny,,DirectoryIndex,DirectoryIndexRedirect,,DirectorySlash,DocumentRoot,DTracePrivileges,DumpIOInput,DumpIOOutput,,,EnableExceptionHook,EnableMMAP,EnableSendfile,Error,ErrorDocument,ErrorLog,ErrorLogFormat,Example,ExpiresActive,ExpiresByType,ExpiresDefault,ExtendedStatus,ExtFilterDefine,ExtFilterOptions,FallbackResource,FileETag,,,FilterChain,FilterDeclare,FilterProtocol,FilterProvider,FilterTrace,ForceLanguagePriority,ForceType,ForensicLog,GprofDir,GracefulShutdownTimeout,Group,Header,HeaderName,HeartbeatAddress,HeartbeatListen,HeartbeatMaxServers,HeartbeatStorage,HeartbeatStorage,HostnameLookups,IdentityCheck,IdentityCheckTimeout,,,,,ImapBase,ImapDefault,ImapMenu,Include,IncludeOptional,IndexHeadInsert,IndexIgnore,IndexIgnoreReset,IndexOptions,IndexOrderDefault,IndexStyleSheet,InputSed,ISAPIAppendLogToErrors,ISAPIAppendLogToQuery,ISAPICacheFile,ISAPIFakeAsync,ISAPILogNotSupported,ISAPIReadAheadBuffer,KeepAlive,KeepAliveTimeout,KeptBodySize,LanguagePriority,LDAPCacheEntries,LDAPCacheTTL,LDAPConnectionPoolTTL,LDAPConnectionTimeout,LDAPLibraryDebug,LDAPOpCacheEntries,LDAPOpCacheTTL,LDAPReferralHopLimit,LDAPReferrals,LDAPRetries,LDAPRetryDelay,LDAPSharedCacheFile,LDAPSharedCacheSize,LDAPTimeout,LDAPTrustedClientCert,LDAPTrustedGlobalCert,LDAPTrustedMode,LDAPVerifyServerCert,,,LimitInternalRecursion,LimitRequestBody,LimitRequestFields,LimitRequestFieldSize,LimitRequestLine,LimitXMLRequestBody,Listen,ListenBackLog,LoadFile,LoadModule,,,LogFormat,LogLevel,LogMessage,LuaCodeCache,LuaHookAccessChecker,LuaHookAuthChecker,LuaAuthzProvider,LuaHookCheckUserID,LuaHookFixups,LuaHookInsertFilter,LuaHookMapToStorage,LuaHookTranslateName,LuaHookTypeChecker,LuaInherit,LuaInputFilter,LuaMapHandler,LuaOutputFilter,LuaPackageCPath,LuaPackagePath,LuaQuickHandler,LuaRoot,LuaScope,MaxConnectionsPerChild,MaxKeepAliveRequests,MaxMemFree,MaxRangeOverlaps,MaxRangeReversals,MaxRanges,MaxRequestWorkers,MaxSpareServers,MaxSpareThreads,MaxThreads,MetaDir,MetaFiles,MetaSuffix,MimeMagicFile,MinSpareServers,MinSpareThreads,MMapFile,ModemStandard,ModMimeUsePathInfo,MultiviewsMatch,Mutex,NameVirtualHost,NoProxy,NWSSLTrustedCerts,NWSSLUpgradeable,Options,Order,OutputSed,PassEnv,PidFile,PrivilegesMode,Protocol,ProtocolEcho,,ProxyAddHeaders,ProxyBadHeader,ProxyBlock,ProxyDomain,ProxyErrorOverride,ProxyExpressDBMFile,ProxyExpressDBMType,ProxyExpressEnable,ProxyFtpDirCharset,ProxyFtpEscapeWildcards,ProxyFtpListOnWildcard,ProxyHTMLBufSize,ProxyHTMLCharsetOut,ProxyHTMLDocType,ProxyHTMLEnable,ProxyHTMLEvents,ProxyHTMLExtended,ProxyHTMLFixups,ProxyHTMLInterp,ProxyHTMLLinks,ProxyHTMLStripComments,ProxyHTMLURLMap,ProxyIOBufferSize,,ProxyMaxForwards,ProxyPass,ProxyPassInterpolateEnv,ProxyPassMatch,ProxyPassReverse,ProxyPassReverseCookieDomain,ProxyPassReverseCookiePath,ProxyPreserveHost,ProxyReceiveBufferSize,ProxyRemote,ProxyRemoteMatch,ProxyRequests,ProxySCGIInternalRedirect,ProxySCGISendfile,ProxySet,ProxySourceAddress,ProxyStatus,ProxyTimeout,ProxyVia,ReadmeName,ReceiveBufferSize,Redirect,RedirectMatch,RedirectPermanent,RedirectTemp,ReflectorHeader,RemoteIPHeader,RemoteIPInternalProxy,RemoteIPInternalProxyList,RemoteIPProxiesHeader,RemoteIPTrustedProxy,RemoteIPTrustedProxyList,RemoveCharset,RemoveEncoding,RemoveHandler,RemoveInputFilter,RemoveLanguage,RemoveOutputFilter,RemoveType,RequestHeader,RequestReadTimeout,Require,,,,RewriteBase,RewriteCond,RewriteEngine,RewriteMap,RewriteOptions,RewriteRule,RLimitCPU,RLimitMEM,RLimitNPROC,Satisfy,ScoreBoardFile,Script,ScriptAlias,ScriptAliasMatch,ScriptInterpreterSource,ScriptLog,ScriptLogBuffer,ScriptLogLength,ScriptSock,SecureListen,SeeRequestTail,SendBufferSize,ServerAdmin,ServerAlias,ServerLimit,ServerName,ServerPath,ServerRoot,ServerSignature,ServerTokens,Session,SessionCookieName,SessionCookieName2,SessionCookieRemove,SessionCryptoCipher,SessionCryptoDriver,SessionCryptoPassphrase,SessionCryptoPassphraseFile,SessionDBDCookieName,SessionDBDCookieName2,SessionDBDCookieRemove,SessionDBDDeleteLabel,SessionDBDInsertLabel,SessionDBDPerUser,SessionDBDSelectLabel,SessionDBDUpdateLabel,SessionEnv,SessionExclude,SessionHeader,SessionInclude,SessionMaxAge,SetEnv,SetEnvIf,SetEnvIfExpr,SetEnvIfNoCase,SetHandler,SetInputFilter,SetOutputFilter,SSIEndTag,SSIErrorMsg,SSIETag,SSILastModified,SSILegacyExprParser,SSIStartTag,SSITimeFormat,SSIUndefinedEcho,SSLCACertificateFile,SSLCACertificatePath,SSLCADNRequestFile,SSLCADNRequestPath,SSLCARevocationCheck,SSLCARevocationFile,SSLCARevocationPath,SSLCertificateChainFile,SSLCertificateFile,SSLCertificateKeyFile,SSLCipherSuite,SSLCryptoDevice,SSLEngine,SSLFIPS,SSLHonorCipherOrder,SSLInsecureRenegotiation,SSLOCSPDefaultResponder,SSLOCSPEnable,SSLOCSPOverrideResponder,SSLOCSPResponderTimeout,SSLOCSPResponseMaxAge,SSLOCSPResponseTimeSkew,SSLOptions,SSLPassPhraseDialog,SSLProtocol,SSLProxyCACertificateFile,SSLProxyCACertificatePath,SSLProxyCARevocationCheck,SSLProxyCARevocationFile,SSLProxyCARevocationPath,SSLProxyCheckPeerCN,SSLProxyCheckPeerExpire,SSLProxyCipherSuite,SSLProxyEngine,SSLProxyMachineCertificateChainFile,SSLProxyMachineCertificateFile,SSLProxyMachineCertificatePath,SSLProxyProtocol,SSLProxyVerify,SSLProxyVerifyDepth,SSLRandomSeed,SSLRenegBufferSize,SSLRequire,SSLRequireSSL,SSLSessionCache,SSLSessionCacheTimeout,SSLSessionTicketKeyFile,SSLStaplingCache,SSLStaplingErrorCacheTimeout,SSLStaplingFakeTryLater,SSLStaplingForceURL,SSLStaplingResponderTimeout,SSLStaplingResponseMaxAge,SSLStaplingResponseTimeSkew,SSLStaplingReturnResponderErrors,SSLStaplingStandardCacheTimeout,SSLStrictSNIVHostCheck,SSLUserName,SSLUseStapling,SSLVerifyClient,SSLVerifyDepth,StartServers,StartThreads,Substitute,Suexec,SuexecUserGroup,ThreadLimit,ThreadsPerChild,ThreadStackSize,TimeOut,TraceEnable,TransferLog,TypesConfig,UnDefine,UnsetEnv,UseCanonicalName,UseCanonicalPhysicalPort,User,UserDir,VHostCGIMode,VHostCGIPrivs,VHostGroup,VHostPrivs,VHostSecure,VHostUser,VirtualDocumentRoot,VirtualDocumentRootIP,,VirtualScriptAlias,VirtualScriptAliasIP,WatchdogInterval,XBitHack,xml2EncAlias,xml2EncDefault,xml2StartParse,RewriteLog,RewriteLogLevel"]; var CONFIG_OPTIONS = /^[\\+\\-]?(AuthConfig|IncludesNOEXEC|ExecCGI|FollowSymLinks|MultiViews|Includes|Indexes|SymLinksIfOwnerMatch)\b/i; var ALL_KEYWORDS = [ CPP_KEYWORDS, CSHARP_KEYWORDS, JSCRIPT_KEYWORDS, PERL_KEYWORDS + diff --git a/modules/lua/lua_vmprep.h b/modules/lua/lua_vmprep.h index 374c62b37a..5ec199f58c 100644 --- a/modules/lua/lua_vmprep.h +++ b/modules/lua/lua_vmprep.h @@ -47,6 +47,9 @@ #define AP_LUA_CACHE_STAT 2 #define AP_LUA_CACHE_FOREVER 3 +#define AP_LUA_FILTER_INPUT 1 +#define AP_LUA_FILTER_OUTPUT 2 + typedef void (*ap_lua_state_open_callback) (lua_State *L, apr_pool_t *p, void *ctx); /** @@ -93,6 +96,14 @@ typedef struct int codecache; } ap_lua_mapped_handler_spec; +typedef struct +{ + const char *function_name; + const char *file_name; + const char* filter_name; + int direction; /* AP_LUA_FILTER_INPUT | AP_LUA_FILTER_OUTPUT */ +} ap_lua_filter_handler_spec; + typedef struct { apr_size_t runs; apr_time_t modified; diff --git a/modules/lua/mod_lua.c b/modules/lua/mod_lua.c index 39b2fdc7b2..ca3869ff20 100644 --- a/modules/lua/mod_lua.c +++ b/modules/lua/mod_lua.c @@ -55,6 +55,14 @@ typedef struct { apr_hash_t *lua_authz_providers; +typedef struct +{ + apr_bucket_brigade *tmpBucket; + lua_State *L; + ap_lua_vm_spec *spec; + int broken; +} lua_filter_ctx; + /** * error reporting if lua has an error. @@ -290,6 +298,281 @@ static int lua_handler(request_rec *r) } +/* ------------------- Input/output content filters ------------------- */ + + +static apr_status_t lua_setup_filter_ctx(ap_filter_t* f, request_rec* r, lua_filter_ctx** c) { + apr_pool_t *pool; + ap_lua_vm_spec *spec; + int n, rc; + lua_State *L; + lua_filter_ctx *ctx; + ap_lua_server_cfg *server_cfg = ap_get_module_config(r->server->module_config, + &lua_module); + const ap_lua_dir_cfg *cfg = ap_get_module_config(r->per_dir_config, + &lua_module); + + ctx = apr_pcalloc(r->pool, sizeof(lua_filter_ctx)); + ctx->broken = 0; + *c = ctx; + /* Find the filter that was called */ + for (n = 0; n < cfg->mapped_filters->nelts; n++) { + ap_lua_filter_handler_spec *hook_spec = + ((ap_lua_filter_handler_spec **) cfg->mapped_filters->elts)[n]; + + if (hook_spec == NULL) { + continue; + } + if (!strcasecmp(hook_spec->filter_name, f->frec->name)) { + spec = create_vm_spec(&pool, r, cfg, server_cfg, + hook_spec->file_name, + NULL, + 0, + hook_spec->function_name, + "filter"); + L = ap_lua_get_lua_state(pool, spec, r); + if (L) { + L = lua_newthread(L); + } + + if (!L) { + ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01477) + "lua: Failed to obtain lua interpreter for %s %s", + hook_spec->function_name, hook_spec->file_name); + ap_lua_release_state(L, spec, r); + return APR_EGENERAL; + } + if (hook_spec->function_name != NULL) { + lua_getglobal(L, hook_spec->function_name); + if (!lua_isfunction(L, -1)) { + ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01478) + "lua: Unable to find function %s in %s", + hook_spec->function_name, + hook_spec->file_name); + ap_lua_release_state(L, spec, r); + return APR_EGENERAL; + } + + ap_lua_run_lua_request(L, r); + } + else { + int t; + ap_lua_run_lua_request(L, r); + + t = lua_gettop(L); + lua_setglobal(L, "r"); + lua_settop(L, t); + } + ctx->L = L; + ctx->spec = spec; + + /* If a Lua filter is interested in filtering a request, it must first do a yield, + * otherwise we'll assume that it's not interested and pretend we didn't find it. + */ + rc = lua_resume(L, 1); + if (rc == LUA_YIELD) { + return OK; + } + else { + ap_lua_release_state(L, spec, r); + return APR_ENOENT; + } + } + } + return APR_ENOENT; +} + +static apr_status_t lua_output_filter_handle(ap_filter_t *f, apr_bucket_brigade *pbbIn) { + apr_bucket *e; + request_rec *r = f->r; + int rc; + lua_State *L; + lua_filter_ctx* ctx; + conn_rec *c = r->connection; + apr_bucket *pbktIn; + apr_bucket_brigade *pbbOut = NULL; + + /* Set up the initial filter context and acquire the function. + * The corresponding Lua function should yield here. + */ + if (!f->ctx) { + rc = lua_setup_filter_ctx(f,r,&ctx); + if (rc == APR_EGENERAL) { + return HTTP_INTERNAL_SERVER_ERROR; + } + if (rc == APR_ENOENT) { + /* No filter entry found (or the script declined to filter), just pass on the buckets */ + return ap_pass_brigade(f->next,pbbIn); + } + f->ctx = ctx; + } + ctx = (lua_filter_ctx*) f->ctx; + L = ctx->L; + /* While the Lua function is still yielding, pass in buckets to the coroutine */ + if (!ctx->broken) { + pbbOut=apr_brigade_create(r->pool, c->bucket_alloc); + for (pbktIn = APR_BRIGADE_FIRST(pbbIn); + pbktIn != APR_BRIGADE_SENTINEL(pbbIn); + pbktIn = APR_BUCKET_NEXT(pbktIn)) + { + const char *data; + apr_size_t len; + apr_bucket *pbktOut; + + /* read the bucket */ + apr_bucket_read(pbktIn,&data,&len,APR_BLOCK_READ); + + /* Push the bucket onto the Lua stack as a global var */ + lua_pushlstring(L, data, len); + lua_setglobal(L, "bucket"); + + /* If Lua yielded, it means we have something to pass on */ + if (lua_resume(L, 0) == LUA_YIELD) { + size_t olen; + const char* output = lua_tolstring(L, 1, &olen); + pbktOut = apr_bucket_heap_create(output, olen, NULL, + c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(pbbOut,pbktOut); + } + else { + ctx->broken = 1; + ap_lua_release_state(L, ctx->spec, r); + return HTTP_INTERNAL_SERVER_ERROR; + } + } + /* If we've safely reached the end, do a final call to Lua to allow for any + finishing moves by the script, such as appending a tail. */ + if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(pbbIn))) { + apr_bucket *pbktEOS; + lua_pushnil(L); + lua_setglobal(L, "bucket"); + if (lua_resume(L, 0) == LUA_YIELD) { + apr_bucket *pbktOut; + size_t olen; + const char* output = lua_tolstring(L, 1, &olen); + pbktOut = apr_bucket_heap_create(output, olen, NULL, + c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(pbbOut,pbktOut); + } + pbktEOS=apr_bucket_eos_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(pbbOut,pbktEOS); + ap_lua_release_state(L, ctx->spec, r); + } + } + /* Clean up and pass on the brigade to the next filter in the chain */ + apr_brigade_cleanup(pbbIn); + if (pbbOut) { + return ap_pass_brigade(f->next,pbbOut); + } + else { + return ap_pass_brigade(f->next,pbbIn); + } +} + + + +static apr_status_t lua_input_filter_handle(ap_filter_t *f, + apr_bucket_brigade *pbbOut, + ap_input_mode_t eMode, + apr_read_type_e eBlock, + apr_off_t nBytes) +{ + request_rec *r = f->r; + int rc, lastCall = 0; + lua_State *L; + lua_filter_ctx* ctx; + conn_rec *c = r->connection; + apr_status_t ret; + + /* Set up the initial filter context and acquire the function. + * The corresponding Lua function should yield here. + */ + if (!f->ctx) { + rc = lua_setup_filter_ctx(f,r,&ctx); + f->ctx = ctx; + ctx->tmpBucket = apr_brigade_create(r->pool, c->bucket_alloc); + if (rc == APR_EGENERAL) { + ctx->broken = 1; + return HTTP_INTERNAL_SERVER_ERROR; + } + if (rc == APR_ENOENT ) { + ctx->broken = 1; + } + } + ctx = (lua_filter_ctx*) f->ctx; + L = ctx->L; + /* If the Lua script broke or denied serving the request, just pass the buckets through */ + if (ctx->broken) { + return ap_get_brigade(f->next, pbbOut, eMode, eBlock, nBytes); + } + + if (APR_BRIGADE_EMPTY(ctx->tmpBucket)) { + ret = ap_get_brigade(f->next, ctx->tmpBucket, eMode, eBlock, nBytes); + + if (eMode == AP_MODE_EATCRLF || ret != APR_SUCCESS) + return ret; + } + + /* While the Lua function is still yielding, pass buckets to the coroutine */ + if (!ctx->broken) { + lastCall = 0; + while(!APR_BRIGADE_EMPTY(ctx->tmpBucket)) { + apr_bucket *pbktIn = APR_BRIGADE_FIRST(ctx->tmpBucket); + apr_bucket *pbktOut; + const char *data; + apr_size_t len; + + if(APR_BUCKET_IS_EOS(pbktIn)) { + APR_BUCKET_REMOVE(pbktIn); + break; + } + + /* read the bucket */ + ret=apr_bucket_read(pbktIn, &data, &len, eBlock); + if(ret != APR_SUCCESS) { + return ret; + } + + /* Push the bucket onto the Lua stack as a global var */ + lastCall++; + lua_pushlstring(L, data, len); + lua_setglobal(L, "bucket"); + + /* If Lua yielded, it means we have something to pass on */ + if (lua_resume(L, 0) == LUA_YIELD) { + size_t olen; + const char* output = lua_tolstring(L, 1, &olen); + pbktOut = apr_bucket_heap_create(output, olen, 0, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(pbbOut, pbktOut); + apr_bucket_delete(pbktIn); + } + else { + ctx->broken = 1; + ap_lua_release_state(L, ctx->spec, r); + return HTTP_INTERNAL_SERVER_ERROR; + } + } + /* If we've safely reached the end, do a final call to Lua to allow for any + finishing moves by the script, such as appending a tail. */ + if (lastCall == 0) { + apr_bucket *pbktEOS = apr_bucket_eos_create(c->bucket_alloc); + lua_pushnil(L); + lua_setglobal(L, "bucket"); + if (lua_resume(L, 0) == LUA_YIELD) { + apr_bucket *pbktOut; + size_t olen; + const char* output = lua_tolstring(L, 1, &olen); + pbktOut = apr_bucket_heap_create(output, olen, 0, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(pbbOut, pbktOut); + } + APR_BRIGADE_INSERT_TAIL(pbbOut,pbktEOS); + ap_lua_release_state(L, ctx->spec, r); + } + } + /* Clean up and pass on the brigade to the next filter in the chain */ + return APR_SUCCESS; +} + /* ---------------- Configury stuff --------------- */ @@ -753,6 +1036,33 @@ static const char *register_mapped_file_function_hook(const char *pattern, *(ap_lua_mapped_handler_spec **) apr_array_push(cfg->mapped_handlers) = spec; return NULL; } +static const char *register_filter_function_hook(const char *filter, + cmd_parms *cmd, + void *_cfg, + const char *file, + const char *function, + int direction) +{ + ap_lua_filter_handler_spec *spec; + ap_lua_dir_cfg *cfg = (ap_lua_dir_cfg *) _cfg; + + spec = apr_pcalloc(cmd->pool, sizeof(ap_lua_filter_handler_spec)); + spec->file_name = apr_pstrdup(cmd->pool, file); + spec->function_name = apr_pstrdup(cmd->pool, function); + spec->filter_name = filter; + + *(ap_lua_filter_handler_spec **) apr_array_push(cfg->mapped_filters) = spec; + /* TODO: Make it work on other types than just AP_FTYPE_RESOURCE? */ + if (direction == AP_LUA_FILTER_OUTPUT) { + spec->direction = AP_LUA_FILTER_OUTPUT; + ap_register_output_filter(filter, lua_output_filter_handle, NULL, AP_FTYPE_RESOURCE); + } + else { + spec->direction = AP_LUA_FILTER_INPUT; + ap_register_input_filter(filter, lua_input_filter_handle, NULL, AP_FTYPE_RESOURCE); + } + return NULL; +} static int lua_check_user_id_harness_first(request_rec *r) { return lua_request_rec_hook_harness(r, "check_user_id", AP_LUA_HOOK_FIRST); @@ -1027,6 +1337,30 @@ static const char *register_map_handler(cmd_parms *cmd, void *_cfg, return register_mapped_file_function_hook(match, cmd, _cfg, file, function); } +static const char *register_output_filter(cmd_parms *cmd, void *_cfg, + const char* filter, const char *file, const char *function) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES| + NOT_IN_HTACCESS); + if (err) { + return err; + } + if (!function) function = "handle"; + return register_filter_function_hook(filter, cmd, _cfg, file, + function, AP_LUA_FILTER_OUTPUT); +} +static const char *register_input_filter(cmd_parms *cmd, void *_cfg, + const char* filter, const char *file, const char *function) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES| + NOT_IN_HTACCESS); + if (err) { + return err; + } + if (!function) function = "handle"; + return register_filter_function_hook(filter, cmd, _cfg, file, + function, AP_LUA_FILTER_INPUT); +} static const char *register_quick_block(cmd_parms *cmd, void *_cfg, const char *line) { @@ -1448,6 +1782,10 @@ command_rec lua_commands[] = { "(internal) Byte code handler"), AP_INIT_TAKE23("LuaMapHandler", register_map_handler, NULL, OR_ALL, "Maps a path to a lua handler"), + AP_INIT_TAKE3("LuaOutputFilter", register_output_filter, NULL, OR_ALL, + "Registers a Lua function as an output filter"), + AP_INIT_TAKE3("LuaInputFilter", register_input_filter, NULL, OR_ALL, + "Registers a Lua function as an input filter"), {NULL} }; @@ -1459,6 +1797,8 @@ static void *create_dir_config(apr_pool_t *p, char *dir) cfg->package_cpaths = apr_array_make(p, 2, sizeof(char *)); cfg->mapped_handlers = apr_array_make(p, 1, sizeof(ap_lua_mapped_handler_spec *)); + cfg->mapped_filters = + apr_array_make(p, 1, sizeof(ap_lua_filter_handler_spec *)); cfg->pool = p; cfg->hooks = apr_hash_make(p); cfg->dir = apr_pstrdup(p, dir); @@ -1537,18 +1877,21 @@ static void *merge_dir_config(apr_pool_t *p, void *basev, void *overridesv) a->package_paths = apr_array_append(p, base->package_paths, overrides->package_paths); a->package_cpaths = apr_array_append(p, base->package_cpaths, overrides->package_cpaths); a->mapped_handlers = apr_array_append(p, base->mapped_handlers, overrides->mapped_handlers); + a->mapped_filters = apr_array_append(p, base->mapped_filters, overrides->mapped_filters); a->hooks = apr_hash_merge(p, overrides->hooks, base->hooks, overlay_hook_specs, NULL); } else if (a->inherit == AP_LUA_INHERIT_PARENT_LAST) { a->package_paths = apr_array_append(p, overrides->package_paths, base->package_paths); a->package_cpaths = apr_array_append(p, overrides->package_cpaths, base->package_cpaths); a->mapped_handlers = apr_array_append(p, overrides->mapped_handlers, base->mapped_handlers); + a->mapped_filters = apr_array_append(p, overrides->mapped_filters, base->mapped_filters); a->hooks = apr_hash_merge(p, base->hooks, overrides->hooks, overlay_hook_specs, NULL); } else { a->package_paths = overrides->package_paths; a->package_cpaths = overrides->package_cpaths; a->mapped_handlers= overrides->mapped_handlers; + a->mapped_filters= overrides->mapped_filters; a->hooks= overrides->hooks; } diff --git a/modules/lua/mod_lua.h b/modules/lua/mod_lua.h index c51c352399..14935275da 100644 --- a/modules/lua/mod_lua.h +++ b/modules/lua/mod_lua.h @@ -51,6 +51,7 @@ #if LUA_VERSION_NUM > 501 /* Load mode for lua_load() */ #define lua_load(a,b,c,d) lua_load(a,b,c,d,NULL) +#define lua_resume(a,b) lua_resume(a, NULL, b) #endif /* Create a set of AP_LUA_DECLARE(type), AP_LUA_DECLARE_NONSTD(type) and @@ -102,9 +103,10 @@ typedef struct apr_array_header_t *package_cpaths; /** - * mapped handlers + * mapped handlers/filters */ apr_array_header_t *mapped_handlers; + apr_array_header_t *mapped_filters; apr_pool_t *pool; -- 2.40.0