Yaws反向代理场景下HTTP 500内部服务器错误的调试问询
我使用Yaws作为反向代理,Yaws部署在名为yaws的Docker容器中,调用名为wsm的NGINX容器。极少情况下,从Opera或Edge调用时会出现500 Internal Server Error,但在Yaws内部调用则无此问题。
该如何调试此问题?
在Linux中使用curl调用时也会出现同样情况:经过一段时间后返回HTTP/1.1 500 Internal Server Error,显然Yaws在此过程中参与了处理。
CS-1 16:33:06 :/tmp# curl -v 'https://wp:my_secret@sm.myurl.biz/equivoxx/2181/sitemap/?rrr=1&del=1&ren=1&rss=1&bak=1&x=1&sfp=1&lg=en&del_sm=1' * Trying 123.456.240.136:443... * TCP_NODELAY set * Connected to sm.myurl.biz (123.456.240.136) port 443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /etc/ssl/certs/ca-certificates.crt [...] * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN, server did not agree to a protocol * Server certificate: * subject: CN=myurl.biz * start date: Jun 15 00:00:00 2023 GMT * expire date: Sep 13 23:59:59 2023 GMT * subjectAltName: host "sm.myurl.biz" matched cert's "sm.myurl.biz" * issuer: C=AT; O=ZeroSSL; CN=ZeroSSL RSA Domain Secure Site CA * SSL certificate verify ok. * Server auth using Basic with user 'wp' > GET /equivoxx/2181/sitemap/?rrr=1&del=1&ren=1&rss=1&bak=1&x=1&sfp=1&lg=en&del_sm=1 HTTP/1.1 > Host: sm.myurl.biz > Authorization: Basic d3A6Y2hlZiw0OA== > User-Agent: curl/7.68.0 > Accept: */* > [working] * Mark bundle as not supporting multiuse < HTTP/1.1 500 Internal Server Error < Connection: close < Server: Yaws 2.0.9 < Date: Wed, 26 Jul 2023 14:34:28 GMT < Content-Length: 47 < Content-Type: text/html < Vary: Accept-Encoding < * Closing connection 0 * TLSv1.3 (OUT), TLS alert, close notify (256): <html><h1>500 Internal Server Error</h1></html>
消息Mark bundle as not supporting multiuse并非错误信息,而是无影响的调试跟踪消息,与HTTP/1.1 500 Internal Server Error无关。
例如,URL:
https://sm.myurl.biz/equimyurl/2181/sitemap/
在所有环境下运行正常,但带多个参数的URL:
https://sm.myurl.biz/equimyurl/2181/sitemap/?rrr=1&del=1&ren=1&rss=1&bak=1&x=1&sfp=1&lg=en&del_sm=1
在浏览器或curl中返回错误。这些参数会触发数据库处理。
检查参数后发现,仅del_sm会触发500错误,该参数会通过删除部分表来清理旧数据,之后重建表以完成更新。此操作与Yaws无关,按理说无需担心,对吗?
wsm中运行正常 在Yaws容器yaws内部,直接调用NGINX容器wsm的带参URL(即curl调用wsm)时运行正常:
curl -v 'wsm/equivoxx/2181/sitemap?rrr=1&del=1&ren=1&rss=1&bak=1&x=1&sfp=1&lg=en&del_sm=1' | tee /tmp/2181-wsm1.htm
curl输出符合预期,用浏览器打开输出文件2181-wsm1.htm也无问题,数据更新正常。Yaws不应修改输出,但仍返回500错误。
我设置了日志文件/tmp/tmp_'.$this->id_ex.'__hunt_500.log记录消息,以追踪源代码中可能出错的位置。不出所料,最后一条日志如下:
2023-07-24 19:56:26 elapsed_secs :14: -- L: 1189 M: Imp::_die HUNT_500 this->lg :en: this->id_ex :2181:
该消息表明,它是在Imp模块的_die方法中触发的,这是程序退出前执行的最后操作:
function _die() { $this->dba->close(); $this->output->_display(); wp_str_to_file('L: '.__LINE__.' M: '.__METHOD__." this->lg :$this->lg: this->id_ex :$this->id_ex:", '/tmp/tmp_'.$this->id_ex.'__hunt_500.log'); exit; } # _die
由此可见,程序已完成预期操作,更新的数据也能证明这一点。那为何Yaws仍对输出有问题?
我在Yaws中大量使用mnesia进行数据缓存,当然部分URL不应被缓存。参数rrr=1会禁用缓存,因此问题URL的输出不会被Yaws处理,而是直接透传。
相关的appmod函数如下:
out(A) -> S = check_intrusion(A#arg.querydata), % out(A) -> if S -> out_hello(); % avoid bad Urls true -> out_check_log(A) % out, real content, filter for cache end. check_intrusion(U) -> % avoid bad Urls case U of undefined -> 0; _ -> L = ["/solr", "HelloThink", "XDEBUG_SESSION", "phpstorm", "invokefunction", "/TP/public"], [string:find(U, N) || N <- L, string:find(U, N) /= nomatch] /= [] end. out_check_log(A) -> R = check_log(A), if R -> % logging turned on, pass through Url = get_uri(A) ++ get_cookies_string(A), pass_through(Url); true -> out_myurl(A) % real work, eventually saved by mnesia end.
check_log(A) -> Url = get_uri_string(A), R = string:str(Url, "log=1") > 0, R. out_myurl(A) -> Del = yaws_api:queryvar(A, "del"), Lg = get_lang(A), % Lg is important Request_path = get_request_path(A), Lg_url = prepend_lg(Lg, Request_path), if Del /= undefined -> url:delete_record(myurl, Lg), % we have at least lg=... will delete if we have no GET param url:delete_record(myurl, Lg_url); % we have at least lg=... delete the record true -> % out_myurl Del ok end, case skip_this_url(Request_path) of false -> % we want to save or retrieve this Start = deb:get_timestamp(), % timestamp -- a bit late, we miss some cycles url:init(), % mnesia Html = select_or_record(A, Request_path, Start, Lg), % should replace elapsed time with real data Html; % we get cached data with fresh timestamp true -> pass_through(Request_path) % out_myurl skip_this_url end.
skip_this_url(Url) -> % determine URLs not to be cached R = string:str(Url, ".ico") > 0 orelse string:str(Url, "search=") > 0 % search is so fast and should be fresh % [...] % lots of conditions orelse string:str(Url, "rrr=1") > 0 % param to not cache orelse string:str(Url, ".well-known") > 0 % letsencrypt orelse string:str(Url, "uploads/") > 0 orelse string:str(Url, "xframe/") > 0, R. pass_through(Request_path) -> Url = check_scheme(Request_path), inets:start(), ssl:start(), Url_2 = string:replace(Url, " ", "%2B"), Http_result = httpc:request(Url_2), {ok, {{V, S, R}, _, _}} = Http_result, % we get the results case Http_result of {ok, {{_Version, 200, _ReasonPhrase}, _Headers, Html}} -> {html, Html}; % we get the results, display them {ok, {{_Version, 401, _ReasonPhrase}, _Headers, Html}} -> {html, Html}; % auth mechanism _ -> {ok, {{_, Status, ReasonPhrase}, _, _}} = Http_result, not_200 % some kind of error end.
据我观察,rrr=1会调用pass_through函数,该函数仅执行httpc:request,并在收到200响应时透传结果。
目前,该错误仅影响更新机制的输出,但未影响数据本身,数据更新正常。虽然输出无关紧要,但仍想解决此问题。
该如何调试?我未在yaws.conf中使用容易引发500错误的interception module。有哪些方法可用于排查此500错误?
wsm运行正常,不清楚为何会导致Yaws返回错误。
我已在Yaws容器中执行yaws --stop停止服务,然后通过以下命令重启以开启调试:
rm -f /tmp/myurl*.log yaws -i
但未发现任何差异。
查看myurl.log时,发现大量如下类型的日志:
------2023-07-26 12:57:57--- 139 url:delete_record RECORD NOT FOUND, mnesia:abort(not_exist) Key "de"
这只是函数delete_record(Table, Key)的通知消息,其中Key会遍历涉及的5种语言。
查看Yaws的跟踪日志,未发现差异:两种情况的日志均以以下内容结尾:
Mod:myurl line:575 'select_or_record(A)=SELECT_OR_RECORD==SELECT_NOT_FOUND=> INSERT_WEB_PAGE Cookie' ["<!-- : -->"]
请问还有哪些排查方向?
内容的提问来源于stack exchange,提问作者kklepper

